Hardening Ubuntu 24.04 за 30 минут: чек-лист для нового VPS
TL;DR
Hardening Ubuntu 24.04 для нового VPS — это набор быстрых мер, которые уменьшают поверхность атаки: обновления, отдельный sudo-пользователь, SSH-ключи, запрет root-входа, UFW, Fail2ban, автоматические security-обновления и резервные копии. Базовую защиту можно внедрить за 30 минут, не блокируя себе доступ к серверу.
- Сначала создайте отдельного пользователя с SSH-ключом и проверьте вход во втором терминале.
- Только после проверки запретите SSH-вход под root и отключите аутентификацию по паролю.
- Откройте в UFW только необходимые порты: обычно 22, 80 и 443.
- Установите Fail2ban, автоматические security-обновления и базовые инструменты диагностики.
- Не публикуйте Docker-порты, базы данных, панели администрирования и Redis в интернет без необходимости.
- Настройте внешний зашифрованный бэкап: hardening без возможности восстановления неполон.
Что мы настраиваем и зачем
Новый VPS с Ubuntu 24.04 обычно доступен по публичному IPv4-адресу сразу после провижининга. Интернет-сканеры находят такой адрес за минуты: они проверяют SSH, веб-порты, базы данных, Docker API, панели управления и известные уязвимые сервисы. Даже сервер без сайта и приложений начинает получать попытки перебора паролей почти сразу.
Цель этого руководства — создать безопасный базовый слой для Ubuntu 24.04 LTS. Это не заменяет аудит конкретного приложения, защиту кода, настройку IAM или DDoS-защиту, но закрывает типовые ошибки нового VPS: root-доступ по паролю, открытые лишние порты, отсутствие обновлений, неограниченные попытки входа и отсутствие резервных копий.
После выполнения чек-листа сервер будет иметь отдельного администратора, вход по SSH-ключам, ограниченный firewall, Fail2ban, включённые автоматические обновления безопасности, аудит открытых портов и основу для зашифрованных бэкапов. Этот набор подходит для VPS с сайтом, API, VPN, Git-сервисом, Minecraft-сервером, ботом, Docker Compose и внутренними инструментами.
Что не входит в базовый hardening
Hardening ОС не делает небезопасное приложение безопасным. Если вы публикуете WordPress, GitLab, Nextcloud, Grafana, Mattermost или собственный API, нужно отдельно обновлять само приложение, использовать сильные пароли и MFA, выключать тестовые endpoints, ограничивать административные URL и регулярно проверять логи.
Также не стоит воспринимать смену SSH-порта как защиту. Нестандартный порт снижает шум в логах, но не заменяет ключи, firewall и запрет паролей. Сканеры находят открытые порты быстро.
Правило безопасного порядка: сначала добавьте и протестируйте новый доступ, затем ужесточайте старый. Не закрывайте текущую SSH-сессию, пока не убедились, что новая сессия работает.
Cloud-managed и self-hosted: что выбрать
| Подход | Что делает провайдер | Что остаётся вашей задачей |
|---|---|---|
| Managed-платформа | Часть обновлений, балансировка, резервирование отдельных сервисов | Доступы, конфигурация приложения, данные, биллинг, vendor lock-in |
| Self-hosted на VPS | Сеть, виртуализация, физическая инфраструктура | ОС, SSH, firewall, обновления, бэкапы, мониторинг, приложение |
| Dedicated | Физический сервер и канал связи | Всё, включая ОС и часто аппаратный мониторинг |
Self-hosted VPS выбирают ради контроля: можно запускать нужные версии ПО, держать данные в выбранной юрисдикции, не платить за каждый managed-компонент и не зависеть от ограничений платформы. Цена контроля — необходимость выполнять регулярную эксплуатацию. Этот чек-лист сокращает начальный риск, но его нужно повторять после изменений инфраструктуры.
Какой VPS-конфиг нужен под эту задачу
Сам hardening почти не требует ресурсов. Ubuntu 24.04, OpenSSH, UFW, Fail2ban и unattended-upgrades нормально работают на небольшом сервере. Ресурсы определяет не защита, а ваше будущее приложение: база данных, Docker-контейнеры, игровой сервер, VPN, CI, файловое хранилище или веб-приложение.
| Сценарий | vCPU | RAM | Диск | Сеть |
|---|---|---|---|---|
| Только VPN, бот, статический сайт, bastion | 1 | 1 ГБ | 20–25 ГБ SSD | 100 Мбит/с |
| Docker Compose, небольшой API, Caddy, PostgreSQL | 2 | 2–4 ГБ | 40–80 ГБ NVMe | 100–1000 Мбит/с |
| Nextcloud, Mattermost, Minecraft, Git-сервис | 4 | 8 ГБ | 100 ГБ NVMe | 1 Гбит/с |
Для универсального нового сервера под небольшой проект разумный старт — 2 vCPU, 4 ГБ RAM, 60–80 ГБ NVMe и публичный IPv4. Такой запас позволяет использовать Docker, reverse proxy, логи, swap и небольшую базу данных без постоянного давления на память. Как один из нейтральных вариантов можно взять VPS с указанными характеристиками, но перед заказом сопоставьте объём диска с размером бэкапов и ростом данных.
Когда VPS достаточно
VPS подходит почти для всех одиночных сервисов и небольших команд. Изоляция виртуальной машины, регулярные snapshots и возможность быстро увеличить тариф удобны для старта. Для WireGuard, веб-приложения, мониторинга, пары контейнеров и нескольких десятков пользователей VPS обычно более чем достаточно.
Когда нужен dedicated
Выбирайте dedicated, если нужен гарантированный CPU без конкуренции с соседями, очень интенсивный I/O, много памяти, большие локальные массивы данных, высокая пропускная способность или предсказуемая производительность под постоянной нагрузкой. Типичные примеры: крупный игровой сервер, индексатор блокчейна, CI-раннеры, несколько тяжёлых виртуальных машин, медиа-транскодирование и база данных с сотнями гигабайт.
Как влияет локация
Локация влияет на задержку, законодательство о данных и скорость до вашей аудитории. Для VPN выбирайте регион рядом с пользователями. Для базы с персональными данными учитывайте требования вашей юрисдикции и договоров. Для администрирования важна не только география, но и стабильность маршрута: проверьте задержку командой ping и фактическую скорость после запуска.
Подготовка сервера
Ниже предполагается, что провайдер выдал IPv4-адрес и временный root-пароль либо добавил ваш публичный ключ. Работайте из обычного локального терминала. На Windows подойдут PowerShell, Windows Terminal или WSL. Все команды выполняются на сервере, если не указано иное.
Проверьте систему и обновите пакеты
Ubuntu 24.04 LTS использует ветку Linux 6.8 с HWE-обновлениями в зависимости от образа. Не ориентируйтесь только на номер версии: патчи безопасности часто бэкпортируются, поэтому пакет может иметь старый upstream-номер и при этом быть исправленным.
# Подключитесь к серверу по адресу, выданному после провижининга.
ssh root@SERVER_IP
# Проверьте релиз Ubuntu, ядро и текущего пользователя.
cat /etc/os-release
uname -r
whoami
# Обновите индекс пакетов и установленные пакеты.
apt update && apt full-upgrade -y
# Перезагрузите сервер, если обновилось ядро или systemd.
reboot
После перезагрузки подключитесь заново. Если SSH временно не отвечает, подождите одну-две минуты и проверьте web-консоль у провайдера. Не продолжайте настройку, пока не убедитесь, что система загрузилась.
Создайте отдельного администратора
Не используйте root как ежедневную учётную запись. Отдельный пользователь даёт понятный аудит действий, снижает риск случайной команды с полными правами и позволяет безопасно отключить root-login по SSH.
# Создайте пользователя admin и добавьте его в группу sudo.
adduser admin
usermod -aG sudo admin
# Создайте каталог для SSH-ключей с корректными правами.
install -d -m 700 -o admin -g admin /home/admin/.ssh
# Скопируйте уже работающий root authorized_keys новому пользователю.
cp /root/.ssh/authorized_keys /home/admin/.ssh/authorized_keys
chown admin:admin /home/admin/.ssh/authorized_keys
chmod 600 /home/admin/.ssh/authorized_keys
Если у root нет файла authorized_keys, создайте ключ на своём компьютере. Для 2026 года практичный выбор — Ed25519. Не передавайте приватный ключ на сервер и не отправляйте его в мессенджеры.
# Выполните на локальном компьютере: создайте ключ Ed25519.
ssh-keygen -t ed25519 -a 100 -C "admin@my-vps"
# Скопируйте публичный ключ на сервер; команда спросит текущий пароль root.
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@SERVER_IP
# На сервере повторите копирование ключа в учётную запись admin.
install -d -m 700 -o admin -g admin /home/admin/.ssh
cp /root/.ssh/authorized_keys /home/admin/.ssh/authorized_keys
chown admin:admin /home/admin/.ssh/authorized_keys
chmod 600 /home/admin/.ssh/authorized_keys
Откройте второй терминал и проверьте новый доступ. Старую root-сессию не закрывайте.
# Выполните локально: вход должен пройти по ключу без пароля пользователя.
ssh -i ~/.ssh/id_ed25519 admin@SERVER_IP
# Проверьте, что sudo работает в новой сессии.
sudo whoami
Ожидаемый результат последней команды — root. Только после этого переходите к настройке SSH. Если ключ не работает, сначала исправьте права на /home/admin/.ssh и authorized_keys.
Установка ПО — пошагово
В Ubuntu 24.04 используются пакеты OpenSSH 9.6p1 с патчами Ubuntu, UFW 0.36.x, Fail2ban 1.0.x и unattended-upgrades 2.9.x. Точную установленную версию всегда проверяйте через apt-cache policy: безопасность определяется доступными обновлениями конкретного репозитория, а не только номером upstream-версии.
# Подключитесь под новым пользователем и установите базовые инструменты.
ssh admin@SERVER_IP
sudo apt update
sudo apt install -y ufw fail2ban unattended-upgrades apt-listchanges \
curl ca-certificates gnupg jq vim-tiny lsof needrestart chrony
Пакет needrestart сообщает, какие службы требуют перезапуска после обновления библиотек. chrony поддерживает точное время, что важно для TLS, токенов, журналов и расследования инцидентов.
# Проверьте версии и статус основных служб после установки.
apt-cache policy openssh-server ufw fail2ban unattended-upgrades
systemctl status ssh --no-pager
systemctl status chrony --no-pager
timedatectl status
Настройте UFW до отключения слабых методов SSH
UFW управляет правилами Netfilter. Безопасная последовательность: разрешить SSH, включить firewall, проверить статус, затем открыть только необходимые сервисы. Если вы используете нестандартный SSH-порт, разрешите именно его до применения конфигурации.
# Задайте политику: входящие запрещены, исходящие разрешены.
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Разрешите SSH и включите firewall.
sudo ufw allow 22/tcp comment 'OpenSSH'
sudo ufw enable
# Проверьте активные правила и номера правил.
sudo ufw status numbered
Если сервер будет обслуживать обычный HTTPS-сайт, откройте HTTP и HTTPS. Порт 80 нужен Caddy или Certbot для выпуска сертификата по HTTP-01. После получения сертификата его обычно оставляют открытым для автоматического продления и редиректа на HTTPS.
# Откройте веб-порты только если на сервере действительно будет HTTP/HTTPS-сервис.
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# Убедитесь, что доступен только ожидаемый набор портов.
sudo ufw status verbose
sudo ss -tulpn
Установите и включите автоматические security-обновления
Автоматическое применение обновлений безопасности не отменяет ручные обновления. Оно закрывает критичный промежуток между выходом патча и вашим плановым окном обслуживания. Для production-сервисов, чувствительных к рестартам, заранее тестируйте обновления на staging-сервере.
# Запустите интерактивную настройку автоматических обновлений Ubuntu.
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Включите таймеры загрузки списков пакетов и установки обновлений.
sudo systemctl enable --now apt-daily.timer apt-daily-upgrade.timer
# Проверьте расписание системных таймеров.
systemctl list-timers --all | grep -E 'apt-daily|unattended'
Включите Fail2ban
Fail2ban читает журналы и временно блокирует IP-адреса после серии неудачных попыток. Это полезно против массового перебора, но не является заменой SSH-ключей. На Ubuntu 24.04 журнал SSH обычно доступен через systemd journal; ниже используется backend systemd.
# Создайте локальный конфигурационный каталог Fail2ban.
sudo install -d -m 755 /etc/fail2ban/jail.d
# Создайте отдельную локальную конфигурацию для SSH.
sudo tee /etc/fail2ban/jail.d/sshd.local > /dev/null <<'EOF'
[sshd]
enabled = true
backend = systemd
port = ssh
maxretry = 5
findtime = 10m
bantime = 1h
banaction = ufw
EOF
# Проверьте конфигурацию и запустите службу.
sudo fail2ban-client -d > /dev/null
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Не редактируйте файлы /etc/fail2ban/jail.conf и /etc/ssh/sshd_config, поставляемые пакетом, без необходимости: обновления могут изменить их. Для локальных настроек используйте jail.d/.local и sshd_config.d/.conf.
Конфигурация
Ужесточите SSH без потери доступа
Ubuntu 24.04 поддерживает каталог /etc/ssh/sshd_config.d/. Создадим файл с приоритетным локальным набором параметров. Перед перезапуском всегда запускайте проверку синтаксиса: одна ошибка в SSH-конфиге может оставить сервер без удалённого доступа.
# Создайте локальный файл с безопасными настройками OpenSSH.
sudo tee /etc/ssh/sshd_config.d/99-hardening.conf > /dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
UsePAM yes
X11Forwarding no
AllowUsers admin
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
LogLevel VERBOSE
EOF
# Проверьте синтаксис и примените изменения без полного рестарта.
sudo sshd -t
sudo systemctl reload ssh
# Проверьте эффективные параметры, учитывая все include-файлы.
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|allowusers|maxauthtries'
Теперь откройте ещё одно новое SSH-подключение как admin. Если подключение проходит, root-доступ по SSH отключён корректно. Если вы используете несколько администраторов, перечислите их в AllowUsers через пробел или удалите эту строку и управляйте доступом другими способами.
# Выполните локально: проверьте вход администратора после hardening SSH.
ssh -o PreferredAuthentications=publickey admin@SERVER_IP
# Выполните локально: root-вход должен быть отклонён.
ssh root@SERVER_IP
Добавьте параметры ядра через sysctl
Следующие параметры отключают неиспользуемую маршрутизацию IPv4 и уменьшают риск ряда сетевых подмен. Не применяйте их вслепую на VPS, который работает как маршрутизатор, WireGuard-шлюз, Kubernetes-нода или NAT-шлюз: там потребуется отдельная сетевая конфигурация.
# Создайте безопасные sysctl-настройки для обычного публичного сервера.
sudo tee /etc/sysctl.d/99-hardening.conf > /dev/null <<'EOF'
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.tcp_syncookies = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
EOF
# Примените sysctl-файлы и проверьте один из параметров.
sudo sysctl --system
sysctl net.ipv4.tcp_syncookies
Секреты храните вне кода и репозитория
Пароли базы данных, токены API, ключи S3 и SMTP-учётные данные не должны попадать в Git, Dockerfile, shell history или публичные конфиги Nginx/Caddy. Для небольшого сервиса храните переменные в файле .env с правами 600. Для systemd используйте EnvironmentFile. В более крупной инфраструктуре применяйте Vault, SOPS, cloud secret manager или аналогичный секрет-хранилище.
# Создайте защищённый каталог и файл переменных окружения для приложения.
sudo install -d -m 750 -o root -g root /etc/myapp
sudo tee /etc/myapp/app.env > /dev/null <<'EOF'
APP_ENV=production
DATABASE_URL=postgresql://app:[email protected]:5432/app
JWT_SECRET=CHANGE_ME_TO_A_LONG_RANDOM_VALUE
EOF
sudo chmod 600 /etc/myapp/app.env
# Сгенерируйте криптографически случайный секрет и замените placeholder вручную.
openssl rand -base64 48
TLS/HTTPS через Caddy
Если на VPS есть веб-сервис, публикуйте его через reverse proxy. Caddy автоматически получает и продлевает TLS-сертификаты, если DNS-запись домена указывает на IP сервера, а порты 80 и 443 доступны извне. Не устанавливайте Caddy только ради hardening: если веб-сервиса нет, держите 80 и 443 закрытыми.
# Добавьте официальный репозиторий Caddy для актуальной стабильной ветки 2.x.
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy
# Проверьте установленную версию Caddy.
caddy version
Предположим, приложение слушает только локальный адрес 127.0.0.1:3000. Это важное ограничение: сервис не должен дополнительно открывать порт 3000 в интернет. Замените домен и порт на свои значения.
example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3000
header {
-Server
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
Strict-Transport-Security "max-age=31536000; includeSubDomains"
}
log {
output file /var/log/caddy/example.com.access.log
format json
}
}
# Сохраните Caddyfile, проверьте его и перезагрузите Caddy.
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak
sudo vim /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl enable --now caddy
sudo systemctl reload caddy
# Проверьте HTTP-редирект, HTTPS и локальный health endpoint приложения.
curl -I http://example.com
curl -I https://example.com
curl -fsS http://127.0.0.1:3000/health || echo "Проверьте endpoint приложения"
Финальная проверка поверхности атаки
Проверяйте не только то, что вы настроили, но и то, что реально слушает сеть. Команда ss показывает локальные сокеты. Порты, привязанные к 127.0.0.1, не доступны напрямую из интернета; адреса 0.0.0.0 и [::] доступны на всех интерфейсах при разрешающем firewall.
# Покажите все слушающие TCP/UDP-порты и процессы-владельцы.
sudo ss -tulpn
# Проверьте firewall, Fail2ban, обновления и необходимость перезагрузки.
sudo ufw status numbered
sudo fail2ban-client status sshd
sudo unattended-upgrade --dry-run --debug
sudo needrestart -r l
# Проверьте доступность сервера и TLS с локального компьютера.
ping -c 4 SERVER_IP
curl -fsSI https://example.com
Для внешней проверки используйте второй сервер или мобильный интернет: убедитесь, что открыты только 22, 80 и 443, если это ваш план. Не открывайте PostgreSQL на 5432, MySQL на 3306, Redis на 6379, Docker API на 2375 и панели администрирования «на время»: такие временные решения часто остаются навсегда.
Бэкапы и обслуживание
Снимок VPS полезен, но не должен быть единственной резервной копией. Он может находиться в той же учётной записи, той же локации и даже на той же инфраструктуре, что и исходный сервер. Рабочая схема — правило 3-2-1: минимум три копии данных, на двух типах хранилища, одна копия вне основного сервера.
Что нужно бэкапить
- Конфигурацию:
/etc, Caddyfile, systemd units, Docker Compose-файлы, настройки firewall и скрипты. - Данные приложений: каталоги uploads, volumes Docker, файлы пользователей, медиа, ключи и сертификаты при необходимости.
- Базы данных: логический dump PostgreSQL или MySQL плюс проверка восстановления.
- Секреты: зашифрованные env-файлы, ключи доступа, recovery codes. Не складывайте секреты в обычный незашифрованный архив.
- Документацию: список доменов, пользователей, портов, порядок восстановления и дату последней проверки restore.
Для небольшого сервера удобен Restic 0.17.x или новее: он шифрует данные на стороне VPS, поддерживает S3-совместимые хранилища, дедупликацию и политики хранения. Репозиторий Restic не заменяет пароль: потеря пароля репозитория без отдельного безопасного хранения означает потерю доступа к копиям.
# Установите Restic и создайте каталог для защищённых переменных бэкапа.
sudo apt install -y restic
sudo install -d -m 700 /root/.config/restic
# Создайте файл окружения; замените все значения на реальные.
sudo tee /root/.config/restic/backup.env > /dev/null <<'EOF'
export RESTIC_REPOSITORY="s3:https://s3.example.net/my-vps-backup"
export RESTIC_PASSWORD="CHANGE_ME_TO_A_LONG_UNIQUE_PASSWORD"
export AWS_ACCESS_KEY_ID="CHANGE_ME"
export AWS_SECRET_ACCESS_KEY="CHANGE_ME"
EOF
sudo chmod 600 /root/.config/restic/backup.env
# Инициализируйте новый зашифрованный репозиторий только один раз.
sudo bash -c 'source /root/.config/restic/backup.env && restic init'
Для PostgreSQL сначала создавайте дамп, а затем архивируйте его Restic. Команда ниже рассчитана на локальную базу и пользователя с правами чтения всех нужных объектов. Для Docker-контейнера БД используйте docker exec либо официальный механизм dump внутри контейнера.
# Создайте каталог для дампов, недоступный обычным пользователям.
sudo install -d -m 700 /var/backups/postgresql
# Пример: создайте сжатый дамп базы appdb от имени системного пользователя postgres.
sudo -u postgres pg_dump -Fc appdb > /var/backups/postgresql/appdb.dump
sudo chmod 600 /var/backups/postgresql/appdb.dump
# Создайте ежедневный скрипт: дамп БД, backup Restic, проверка и политика хранения.
sudo tee /usr/local/sbin/backup-server.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
source /root/.config/restic/backup.env
DATE="$(date +%F)"
BACKUP_PATHS=(
/etc
/home
/opt
/srv
/var/lib/docker/volumes
/var/backups/postgresql
)
restic backup "${BACKUP_PATHS[@]}" \
--exclude-caches \
--exclude='/home//.cache' \
--tag "server" \
--tag "$DATE"
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check --read-data-subset=1/50
EOF
sudo chmod 700 /usr/local/sbin/backup-server.sh
# Запустите первый бэкап вручную и убедитесь, что он завершается без ошибок.
sudo /usr/local/sbin/backup-server.sh
sudo bash -c 'source /root/.config/restic/backup.env && restic snapshots'
Путь /var/lib/docker/volumes нельзя бездумно копировать во время активной записи базы данных: для PostgreSQL и MySQL используйте dump или штатный backup-инструмент. Для файловых данных можно архивировать volumes, но сначала зафиксируйте консистентность приложения.
# Добавьте запуск каждый день в 03:20 и запись лога в отдельный файл.
sudo tee /etc/cron.d/server-backup > /dev/null <<'EOF'
20 3 root /usr/local/sbin/backup-server.sh >> /var/log/server-backup.log 2>&1
EOF
# Проверьте, что cron видит задание, и посмотрите последние строки лога после запуска.
sudo systemctl status cron --no-pager
sudo tail -n 50 /var/log/server-backup.log
Проверка восстановления
Бэкап считается рабочим только после восстановления. Раз в месяц восстановите один файл и тестовый dump базы в отдельный каталог или на отдельный сервер. Не восстанавливайте базу поверх production-данных без остановки приложения и подтверждённого плана отката.
# Просмотрите содержимое последнего snapshot и восстановите его в тестовый каталог.
sudo bash -c 'source /root/.config/restic/backup.env && restic snapshots'
sudo mkdir -p /tmp/restic-restore
sudo bash -c 'source /root/.config/restic/backup.env && restic restore latest --target /tmp/restic-restore'
# Убедитесь, что нужные файлы и дамп действительно восстановились.
sudo find /tmp/restic-restore -maxdepth 4 -type f | head -n 30
План обновлений
Security-обновления можно применять автоматически, но обновления приложения, Docker-образов и крупных версий базы лучше планировать в maintenance window. Для stateless веб-приложения используйте rolling-обновление: запустите новую версию, проверьте healthcheck, затем переключите reverse proxy. Для одиночной базы данных обычно нужен короткий контролируемый простой.
- Раз в неделю просматривайте
journalctl -p warning..alert, статус диска и логи бэкапа. - Раз в месяц выполняйте
sudo apt update && sudo apt full-upgradeи перезагрузку при необходимости. - Раз в месяц проверяйте восстановление хотя бы одного файла и одного дампа БД.
- Раз в квартал пересматривайте пользователей, SSH-ключи, открытые порты и токены доступа.
Troubleshooting + FAQ
После настройки SSH я получаю Permission denied (publickey)
Сначала не закрывайте старую сессию или используйте web-консоль. Проверьте, что входите нужным пользователем: ssh admin@SERVER_IP. На сервере проверьте права: каталог ~/.ssh должен иметь режим 700, а authorized_keys — 600 и принадлежать пользователю. Посмотрите причину в sudo journalctl -u ssh -n 100. Частая ошибка — ключ скопирован в root, а в конфиге уже включён PermitRootLogin no.
UFW включён, но сайт или SSH недоступен
Подключитесь через консоль и выполните sudo ufw status numbered и sudo ss -tulpn. Firewall может разрешать порт, но приложение не слушает его либо привязано только к 127.0.0.1. Для SSH должна быть строка allow для фактического порта. Для сайта проверьте правила 80/tcp и 443/tcp, DNS-запись домена и статус Caddy. При ошибке правила можно удалить по номеру через sudo ufw delete НОМЕР.
Fail2ban не банит адреса или не видит SSH-попытки
Проверьте службу командой sudo systemctl status fail2ban, затем выполните sudo fail2ban-client status sshd. На Ubuntu 24.04 для SSH обычно правильный backend — systemd, поэтому он указан в локальном jail-файле. Посмотрите журналы через sudo journalctl -u fail2ban -n 100. Убедитесь, что в конфигурации указан banaction = ufw, если UFW используется как firewall.
Автоматические обновления включены, но сервер просит перезагрузку
Это нормальная ситуация после обновления ядра, некоторых библиотек или systemd. Проверьте наличие файла /var/run/reboot-required и вывод sudo needrestart -r l. Выберите окно обслуживания, предупредите пользователей и выполните sudo reboot. Автоматические security-обновления уменьшают окно уязвимости, но не могут безопасно перезапустить все приложения без учёта вашей нагрузки и зависимостей.
Какой VPS-конфиг минимально подойдёт?
Для самого hardening достаточно 1 vCPU, 1 ГБ RAM и 20 ГБ SSD, если сервер выполняет роль bastion-host, простого VPN или небольшого бота. Для практического универсального старта лучше 2 vCPU, 2–4 ГБ RAM и 40–80 ГБ NVMe: останется место для Docker, логов, обновлений и резервных дампов. Не рассчитывайте диск только под ОС: заранее заложите место под данные, временные файлы и локальные копии перед выгрузкой в внешний backup.
Что выбрать — VPS или dedicated для этой задачи?
Для hardening, небольшого сайта, VPN, API и нескольких контейнеров достаточно VPS. Он дешевле, быстрее разворачивается и проще масштабируется по памяти или диску. Dedicated нужен не из-за самого Ubuntu hardening, а при тяжёлой постоянной нагрузке: высоком I/O базы, большом игровом сервере, транскодировании, CI или требованиях к гарантированной производительности CPU. В обоих случаях правила SSH, firewall, обновления и бэкапы остаются обязанностью администратора.
Caddy не получает TLS-сертификат и выдаёт ошибку ACME
Проверьте, что A-запись домена указывает на публичный IPv4 VPS, а AAAA-запись либо корректна, либо отсутствует. Порты 80 и 443 должны быть открыты в UFW и во внешнем firewall провайдера. Выполните sudo journalctl -u caddy -n 100 --no-pager. Часто проблема в том, что другой веб-сервер уже занял порт 80 или домен указывает на старый IP. После исправления Caddy повторит попытку автоматически.
Как понять, что Docker случайно не открыл базу или панель в интернет?
Выполните sudo ss -tulpn и проверьте адреса привязки. Опасны сервисы на 0.0.0.0:5432, 0.0.0.0:3306, 0.0.0.0:6379 и похожих портах. В Docker Compose публикуйте внутренние сервисы как 127.0.0.1:5432:5432 либо вообще не используйте секцию ports, если контейнеры общаются по внутренней сети. Оставьте наружу только reverse proxy на 80/443 и SSH на выбранном порту.
Выводы и следующие шаги
Теперь новый VPS на Ubuntu 24.04 имеет базовую защиту: обновлённую систему, ключевой SSH-доступ, запрет root-входа, UFW, Fail2ban, sysctl-настройки, TLS для веб-сервиса и зашифрованный внешний бэкап. Это заметно снижает риск типовых автоматизированных атак и ошибок начальной настройки.
Следующим шагом добавьте мониторинг ресурсов и доступности, например проверку диска, памяти, срока TLS-сертификатов и статуса backup-задачи. Затем настройте отдельные политики hardening для вашего приложения: Docker, PostgreSQL, WireGuard, веб-фреймворка или панели управления.
После каждого нового сервиса повторяйте короткий аудит: нужен ли ему публичный порт, кто имеет доступ, где хранятся секреты, как выполняется обновление и можно ли восстановить данные из бэкапа. Именно этот цикл превращает разовую настройку VPS в поддерживаемую инфраструктуру.