vps

Полная настройка и защита Ubuntu 24.04 LTS для VPS

Полная настройка и защита Ubuntu 24.04 LTS для VPS

Вступление

После установки Ubuntu на VPS хочется как можно быстрее перейти к полезной работе: поставить Docker, VPN, reverse proxy, DNS, панели или свои сервисы. Но именно на этом этапе чаще всего и появляются базовые проблемы: вход под root остаётся как есть, парольный SSH не отключён, firewall включён наугад, automatic security updates формально существуют, но реально не проверены, а на сервере уже лежат старые или провайдерские SSH-ключи.

Этот материал не про «идеально неуязвимый сервер». Его задача проще и практичнее: привести свежий VPS на Ubuntu 24.04 LTS в нормальное базовое боевое состояние. В конце у тебя будет отдельный администратор, вход по SSH только по ключу, включённый UFW, работающий Fail2ban, проверенный AppArmor, понятная схема automatic security updates и финальный чек-лист перед установкой любых следующих сервисов.

AppArmor в Ubuntu — штатный механизм ограничения привилегий, а cloud-init действительно умеет управлять SSH-ключами, root-политикой и начальными пользовательскими настройками, поэтому оба пункта в таком baseline не декоративные.

Перед началом

Нужны:

  • IP-адрес VPS
  • root-доступ от провайдера или веб-консоль / VNC
  • компьютер, с которого ты будешь входить по SSH
  • 10–20 минут на настройку
  • готовность к одной контролируемой перезагрузке сервера

Этот мануал рассчитан на чистый VPS на Ubuntu 24.04 LTS. Если на сервере уже стоят панели, Docker-стек, WG-Easy, AdGuard Home или чужая автоматизация, сначала разберись, что именно там уже изменено.

Что получится в конце

В конце у тебя будет:

  • обновлённая Ubuntu 24.04 LTS
  • отдельный пользователь-администратор вместо постоянной работы под root
  • SSH-вход только по ключу
  • закрытый root login по SSH
  • включённый UFW с минимально нужными портами
  • работающий Fail2ban для SSH
  • проверенный AppArmor
  • проверенный cloud-init и отсутствие неожиданных провайдерских SSH-ключей
  • проверенные automatic security updates
  • понятный финальный аудит перед дальнейшей установкой Docker, VPN, reverse proxy, DNS и других сервисов

Шаг 1. Проверяем базовое состояние VPS

Сначала подключись к серверу и посмотри, что именно тебе выдал провайдер.

hostnamectl
uname -r
cat /etc/os-release
whoami
id
ip a
ss -lntup

Эти команды покажут:

  • имя хоста
  • версию Ubuntu
  • текущее ядро
  • под каким пользователем ты вошёл
  • сетевые интерфейсы
  • какие порты уже слушает сервер

Что проверить после шага

Ты должен видеть Ubuntu 24.04 LTS, свой hostname, активный сетевой интерфейс и понимать, какие порты уже открыты наружу.

Ошибка новичка

Сразу начинать ставить Docker, VPN или панель управления, не разобравшись, под кем ты вошёл и что уже слушает сервер.

Шаг 2. Обновляем систему

После установки сразу приведи пакеты в актуальное состояние.

sudo apt update
sudo apt full-upgrade -y
sudo apt autoremove -y

Если после обновления система сообщает о новых пакетах или новом ядре, проверь это:

uname -r
sudo apt list --upgradable

Если увидишь сообщение о phased updates или об отложенных обновлениях, это не обязательно ошибка: часть пакетов Ubuntu может раскатывать поэтапно.

Сначала лучше обновить систему, а уже потом закреплять hardening. Так ты строишь baseline на актуальных пакетах, а не на том, что было в образе провайдера.

Шаг 3. Создаём отдельного администратора

Постоянно работать под root на VPS не нужно. Создай обычного администратора и добавь его в sudo.

Пример с пользователем your_admin_user:

sudo adduser your_admin_user
sudo usermod -aG sudo your_admin_user
id your_admin_user
getent group sudo

После этого можно заблокировать пароль root:

sudo passwd -l root
sudo passwd -S root

Если увидишь букву L, это значит, что пароль root заблокирован.

Что проверить после шага

  • пользователь существует
  • пользователь входит в группу sudo
  • пароль root заблокирован

Ошибка новичка

Думать, что одной блокировки root-пароля уже достаточно. Нет: это полезно, но SSH всё равно нужно доводить отдельно.

Шаг 4. Проверяем cloud-init и провайдерские SSH-ключи

На VPS начальный SSH-доступ иногда управляется через cloud-init или провайдерские механизмы. Через него можно задать disable_root, allow_public_ssh_keys, ssh_authorized_keys, ssh_import_id и другие параметры, связанные с доступом.

Поэтому перед окончательным ужесточением SSH полезно убедиться, что на сервере нет лишних root-ключей и неожиданных ключей от провайдера.

Проверь:

sudo cloud-init status --long 2>/dev/null || true
sudo grep -R -nE '^(users:|system_info:|disable_root:|ssh_authorized_keys:|ssh_import_id:|allow_public_ssh_keys:)' \
  /etc/cloud/cloud.cfg /etc/cloud/cloud.cfg.d 2>/dev/null || true
sudo ls -la /root/.ssh 2>/dev/null || true
sudo cat /root/.ssh/authorized_keys 2>/dev/null || true
ls -la ~/.ssh 2>/dev/null || true
cat ~/.ssh/authorized_keys 2>/dev/null || true

Если хочешь добить проверку:

sudo systemctl status cloud-init --no-pager
dpkg -l | grep cloud-init

Нормальная картина:

  • у root нет лишнего authorized_keys
  • у твоего администратора есть только нужный ключ
  • нет неожиданных cloud-init-настроек, которые снова откроют root-доступ или подложат чужой SSH-ключ

Если текущий доступ на сервер держится на провайдерском ключе, не удаляй ключи вслепую. Сначала создай своего администратора, добавь свой ключ и проверь новый вход в отдельной SSH-сессии.

Шаг 5. Настраиваем SSH-вход по ключу для администратора

В примерах ниже используется пользователь your_admin_user.

На этом этапе мы переводим SSH-доступ на более безопасную схему:

  • вход по SSH будет разрешён только пользователю your_admin_user
  • аутентификация будет выполняться по ключу
  • парольный вход будет отключён только после успешной проверки входа по ключу

Пока ты не убедился, что вход по ключу действительно работает, отключать парольный вход нельзя.

Шаг 5.1. Создаём SSH-ключ на своём компьютере

Если SSH-ключа у тебя ещё нет, сначала создай его на своём компьютере.

Windows PowerShell

ssh-keygen -t ed25519 -a 100 -C "your_admin_user-homelab"

Linux / macOS

ssh-keygen -t ed25519 -a 100 -C "your_admin_user-homelab"

При запросе пути просто нажми Enter, чтобы сохранить ключ в стандартное место.

Обычно будут созданы два файла:

  • id_ed25519 — приватный ключ
  • id_ed25519.pub — публичный ключ

Приватный ключ id_ed25519 должен оставаться только на твоём компьютере. На сервер копируется только публичный ключ id_ed25519.pub.

Ошибка новичка

Нельзя отправлять на сервер приватный ключ id_ed25519. Если это сделать, безопасность SSH будет фактически потеряна.

Что проверить после шага

  • файл id_ed25519 существует
  • файл id_ed25519.pub существует
  • ключ создан без ошибок

Шаг 5.2. Проверяем, что ключи действительно создались

Windows PowerShell

Get-ChildItem $HOME\.ssh
Test-Path $HOME\.ssh\id_ed25519
Test-Path $HOME\.ssh\id_ed25519.pub

Linux / macOS

ls -la ~/.ssh

Нужны именно два файла:

  • id_ed25519
  • id_ed25519.pub

Если их нет, дальше идти нельзя: сначала нужно корректно создать ключ.

Что проверить после шага

  • публичный ключ id_ed25519.pub есть
  • приватный ключ id_ed25519 есть
  • оба файла лежат в каталоге .ssh

Шаг 5.3. Смотрим содержимое публичного ключа

Теперь нужно открыть публичный ключ и скопировать его целиком одной строкой.

Windows PowerShell

Get-Content $HOME\.ssh\id_ed25519.pub

Linux / macOS

cat ~/.ssh/id_ed25519.pub

Ты увидишь строку примерно такого вида:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your_admin_user-homelab

Скопируй всю строку целиком.

Публичный ключ должен копироваться полностью: от ssh-ed25519 до комментария в конце строки.

Ошибка новичка

Если скопировать ключ не полностью или вставить его с переносами строк, SSH не сможет использовать его для входа.

Что проверить после шага

  • ключ начинается с ssh-ed25519
  • ключ скопирован целиком
  • строка не содержит лишних переносов

Шаг 5.4. Создаём каталог .ssh для пользователя your_admin_user на сервере

Сейчас ты работаешь на сервере под root, поэтому использовать ~/.ssh нельзя: в этом случае ~ указывает на каталог root.

Нужно создать каталог SSH именно для пользователя your_admin_user:

sudo mkdir -p /home/your_admin_user/.ssh
sudo chown your_admin_user:your_admin_user /home/your_admin_user/.ssh
sudo chmod 700 /home/your_admin_user/.ssh

Когда настройка выполняется из-под root, всегда используй явный путь /home/your_admin_user/.ssh, а не ~/.ssh.

Ошибка новичка

Если выполнить mkdir -p ~/.ssh под root, каталог будет создан в /root/.ssh, а не у пользователя your_admin_user.

Что проверить после шага

  • каталог /home/your_admin_user/.ssh существует
  • владелец каталога — your_admin_user:your_admin_user
  • права на каталог — 700

Шаг 5.5. Добавляем публичный ключ пользователя your_admin_user

Теперь создай файл authorized_keys:

sudo touch /home/your_admin_user/.ssh/authorized_keys
sudo chown your_admin_user:your_admin_user /home/your_admin_user/.ssh/authorized_keys
sudo chmod 600 /home/your_admin_user/.ssh/authorized_keys

Открой его:

sudo nano /home/your_admin_user/.ssh/authorized_keys

Вставь туда свой публичный ключ одной строкой, сохрани файл и выйди из редактора.

Проверь содержимое:

sudo cat /home/your_admin_user/.ssh/authorized_keys

В файл /home/your_admin_user/.ssh/authorized_keys вставляется содержимое публичного ключа id_ed25519.pub, а не приватного ключа.

Ошибка новичка

Частая ошибка — вставить ключ с лишним переносом строки или случайно вставить неполный ключ.

Что проверить после шага

  • файл /home/your_admin_user/.ssh/authorized_keys существует
  • права на файл — 600
  • ключ вставлен одной строкой
  • владелец файла — your_admin_user:your_admin_user

Шаг 5.6. Проверяем fingerprint сервера перед первым входом

Перед первым подтверждением yes на клиенте не принимай host key вслепую.

На сервере выполни:

sudo ssh-keygen -l -E sha256 -f /etc/ssh/ssh_host_ed25519_key.pub

Ты увидишь строку примерно такого вида:
256 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX root@homelab (ED25519)

Именно этот fingerprint нужно сравнить с тем, что покажет SSH-клиент при первом подключении.

Здесь проверяется host key сервера, а не пользовательский ключ your_admin_user. Эта проверка подтверждает, что ты подключаешься именно к своему серверу.

Ошибка новичка

Не путай публичный ключ пользователя в authorized_keys и host key сервера. Это два разных ключа с разным назначением.

Что проверить после шага

  • fingerprint сохранён или переписан
  • команда выполнилась без ошибок
  • в выводе указан ключ типа ED25519

Шаг 5.7. Проверяем первый вход по ключу

Очень важно: не закрывай текущую root-сессию, пока не убедишься, что новый вход под your_admin_user действительно работает.

Открой новое окно терминала на своём компьютере и попробуй войти.

Windows PowerShell

ssh -i $HOME\.ssh\id_ed25519 your_admin_user@YOUR_SERVER_IP

Linux / macOS

ssh -i ~/.ssh/id_ed25519 your_admin_user@YOUR_SERVER_IP

При первом подключении SSH покажет fingerprint сервера и попросит подтверждение.

Подтверждай подключение только если fingerprint совпадает с тем, что ты получил на сервере командой:

sudo ssh-keygen -l -E sha256 -f /etc/ssh/ssh_host_ed25519_key.pub

Если fingerprint совпал, ответь:
yes

После входа сразу проверь текущего пользователя:

whoami
id

Ты должен увидеть пользователя your_admin_user.

На этом этапе задача — не просто подключиться к серверу, а убедиться, что вход выполнен именно под your_admin_user и именно по ключу.

Ошибка новичка

Нельзя отключать парольный вход сразу после добавления ключа в authorized_keys. Сначала нужен реальный успешный вход новым SSH-сеансом.

Что проверить после шага

  • вход выполнен в новом окне терминала
  • fingerprint сервера совпал
  • whoami показывает your_admin_user
  • старая root-сессия по-прежнему остаётся открытой как страховка

Шаг 5.8. Проверяем, что вход был именно по ключу

После успешного входа посмотри журнал аутентификации на сервере:

sudo tail -n 50 /var/log/auth.log

Найди строку примерно такого вида:
Accepted publickey for your_admin_user from ...

Дополнительно можно посмотреть журнал SSH-службы:

sudo journalctl -u ssh -u ssh.socket -n 50 --no-pager

Но для подтверждения ключевой аутентификации обычно удобнее ориентироваться на /var/log/auth.log.

Строка Accepted publickey for your_admin_user подтверждает, что аутентификация действительно прошла по ключу.

Ошибка новичка

Если ты просто вошёл на сервер, это ещё не доказывает, что сработал именно ключ. Проверка по логам нужна обязательно.

Что проверить после шага

  • в /var/log/auth.log есть строка Accepted publickey for your_admin_user
  • вход выполнен именно по ключу
  • пользователь в логе указан правильный: your_admin_user

Когда отключать парольный вход

Парольный вход для your_admin_user отключают не после создания ключа, а только после того, как одновременно выполнены все условия:

  • каталог /home/your_admin_user/.ssh существует
  • файл /home/your_admin_user/.ssh/authorized_keys существует
  • публичный ключ добавлен туда одной строкой
  • права на .ssh и authorized_keys выставлены правильно
  • fingerprint сервера перед первым подключением проверен
  • новый вход под your_admin_user уже реально выполнен
  • в логах есть строка Accepted publickey for your_admin_user

Пока хотя бы один из этих пунктов не выполнен, отключать парольный вход нельзя.

Шаг 6. Переводим SSH в финальную безопасную схему

Когда вход по ключу уже точно работает, переведи SSH в финальную безопасную конфигурацию.

В Ubuntu 24.04 OpenSSH использует socket activation, поэтому после изменения конфигурации SSH нужно проверить и итоговый конфиг, и состояние ssh.service и ssh.socket.

Создай отдельный drop-in файл:

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Вставь:

PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
PermitEmptyPasswords no

DisableForwarding yes
PermitTunnel no
PermitUserEnvironment no
X11Forwarding no

LoginGraceTime 30
MaxAuthTries 3
MaxSessions 4
MaxStartups 10:30:60

ClientAliveInterval 300
ClientAliveCountMax 2

LogLevel VERBOSE

AllowUsers your_admin_user

Проверь синтаксис:

sudo sshd -t

Если вывода нет, конфиг валиден.

Теперь примени настройки и проверь оба юнита:

sudo systemctl restart ssh
sudo systemctl restart ssh.socket
sudo systemctl status ssh --no-pager
sudo systemctl status ssh.socket --no-pager

Посмотри итоговые реальные параметры:

sudo sshd -T | egrep 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|allowusers|disableforwarding|x11forwarding|loglevel'

В выводе должно быть:
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
x11forwarding no
disableforwarding yes
loglevel VERBOSE
allowusers your_admin_user

После этого открой ещё одно новое окно терминала и снова проверь вход.

Windows PowerShell

ssh -i $HOME\.ssh\id_ed25519 your_admin_user@YOUR_SERVER_IP

Linux / macOS

ssh -i ~/.ssh/id_ed25519 your_admin_user@YOUR_SERVER_IP

Если вход снова проходит, значит SSH переведён в нормальную безопасную схему.

Что проверить после шага

  • sshd -t не показывает ошибок
  • ssh.service и ssh.socket активны
  • effective config показывает passwordauthentication no
  • новый вход под your_admin_user снова проходит успешно

Ошибка новичка

Закрыть текущую рабочую SSH-сессию до проверки нового входа в отдельном окне.

Шаг 7. Включаем UFW

Если на сервере пока нужен только SSH, начни с минимального правила:

sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Для просмотра логов:

sudo ufw logging on
sudo tail -f /var/log/ufw.log

Полезные проверки:

sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | tail -n 50
sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | grep 'DPT=22' | tail -n 50

Сначала разреши SSH, и только потом включай UFW.

Если у провайдера есть внешний cloud firewall, security group или ACL, проверь, что 22/tcp разрешён не только в UFW, но и там тоже.

Что проверить после шага

UFW включён, правило для 22/tcp есть, новый SSH-сеанс продолжает работать.

Шаг 8. Подключаем Fail2ban для SSH

Установи и включи Fail2ban:

sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban --no-pager

Создай локальный override-файл:

sudo nano /etc/fail2ban/jail.local

Вставь:

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3

bantime.increment = true
bantime.maxtime = 24h
bantime.rndtime = 10m

backend = systemd
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled = true

Этот вариант означает:

  • первый бан — на 1 час
  • если один и тот же IP повторяет атаки, срок бана будет расти
  • максимальный срок роста ограничен 24 часами
  • к бану добавляется небольшой случайный разброс
  • Fail2ban читает события из journald

Примени настройки:

sudo systemctl restart fail2ban
sudo systemctl status fail2ban --no-pager
sudo fail2ban-client status
sudo fail2ban-client status sshd

Полезная дополнительная проверка:

sudo fail2ban-client get sshd bantime
sudo fail2ban-client get sshd findtime
sudo fail2ban-client get sshd maxretry
sudo tail -n 50 /var/log/fail2ban.log

Редактировать лучше jail.local, а не jail.conf, чтобы твои настройки не потерялись при обновлении.

Что проверить после шага

fail2ban.service активен, jail sshd загружен, а fail2ban-client status sshd показывает рабочий jail и список банов.

Что делать, если ты сам себя заблокировал

Иногда после ужесточения SSH или включения Fail2ban можно случайно потерять доступ к серверу. Чаще всего это происходит в трёх случаях:

  • парольный вход уже отключён, а вход по ключу ещё не был нормально проверен
  • пользователь пытается снова входить под root, хотя AllowUsers разрешает только your_admin_user
  • Fail2ban воспринимает повторные неудачные входы как атаку и временно банит твой IP

Самое важное правило

Пока ты не убедился, что новый вход под your_admin_user по ключу реально работает, не закрывай текущую рабочую SSH-сессию и не выходи из веб-консоли провайдера.

Если вход под your_admin_user перестал работать

Сначала попробуй понять, что именно произошло:

  • если SSH отвечает, но пишет Permission denied (publickey), проблема обычно в ключе или в правах на ~/.ssh
  • если соединение вообще не устанавливается, проверь UFW и внешний firewall провайдера
  • если после нескольких попыток перестало пускать вообще, очень вероятно, что твой IP забанил Fail2ban

Как проверить, не забанил ли тебя Fail2ban

Зайди на сервер через веб-консоль / VNC от провайдера и выполни:

sudo fail2ban-client status sshd
sudo tail -n 50 /var/log/fail2ban.log

Если увидишь свой IP в бане, разблокируй его:

sudo fail2ban-client set sshd unbanip ТВОЙ_IP

Как временно снять проблему для восстановления доступа

Если нужно быстро вернуть себе вход и спокойно всё перепроверить, временно останови Fail2ban:

sudo systemctl stop fail2ban

После этого заново проверь вход под your_admin_user по ключу.

Когда убедишься, что всё работает, включи Fail2ban обратно:

sudo systemctl start fail2ban

Если проблема именно в SSH-ключе

Проверь права и владельцев:

sudo chown -R your_admin_user:your_admin_user /home/your_admin_user/.ssh
sudo chmod 755 /home/your_admin_user
sudo chmod 700 /home/your_admin_user/.ssh
sudo chmod 600 /home/your_admin_user/.ssh/authorized_keys

Проверь содержимое ключа:

sudo cat /home/your_admin_user/.ssh/authorized_keys

И смотри лог SSH во время новой попытки входа:

sudo tail -f /var/log/auth.log

Если хочешь временно вернуть парольный вход для восстановления доступа

Это аварийная мера, а не постоянная настройка.

Открой:

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Временно установи:
PasswordAuthentication yes

Проверь конфиг и перезапусти SSH:

sudo sshd -t
sudo systemctl restart ssh
sudo systemctl restart ssh.socket

После восстановления входа по ключу обязательно верни:
PasswordAuthentication no

Главное

Если ты сам себя заблокировал, это не означает, что сервер «сломался». Обычно проблема сводится к одной из трёх причин: неправильный ключ, бан от Fail2ban или слишком раннее отключение парольного входа.

Шаг 9. Проверяем AppArmor

Проверь состояние:

sudo systemctl status apparmor --no-pager
sudo aa-status

Нормальный результат:

  • модуль AppArmor загружен
  • часть профилей в enforce
  • сервис apparmor.service успешно завершился

Цель этого шага не «заставить все профили быть в enforce», а убедиться, что AppArmor вообще живой.

Шаг 10. Проверяем automatic security updates

На Ubuntu 24.04 LTS автоматическая установка security updates обычно уже включена, а unattended-upgrades по умолчанию запускается ежедневно. Но на VPS нельзя полагаться только на это предположение: после установки нужно отдельно проверить APT-конфиг, разрешённые origins, systemd timers и логи unattended-upgrades.

Шаг 10.1. Проверяем конфигурацию APT и unattended-upgrades

Сначала посмотри, включены ли periodic update и unattended-upgrades:

sudo cat /etc/apt/apt.conf.d/20auto-upgrades
sudo grep -A20 'Allowed-Origins' /etc/apt/apt.conf.d/50unattended-upgrades
apt-config dump APT::Periodic::Update-Package-Lists
apt-config dump APT::Periodic::Unattended-Upgrade

Нормальный базовый результат должен показывать:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

Это означает, что сервер автоматически обновляет список пакетов и запускает unattended-upgrades.

Отдельно посмотри блок Allowed-Origins в файле 50unattended-upgrades. Именно он определяет, из каких источников серверу разрешено ставить обновления автоматически.

Что проверить после шага

Проверь, что:

  • APT::Periodic::Update-Package-Lists имеет значение "1"
  • APT::Periodic::Unattended-Upgrade имеет значение "1"
  • в Allowed-Origins присутствует как минимум security-origin для твоего релиза

Если в Allowed-Origins разрешены только security origins, то обычные обновления из noble-updates автоматически ставиться не будут. Это нормальная, более консервативная схема для боевого VPS.

Шаг 10.2. Проверяем systemd timers

Теперь проверь, что автоматический запуск действительно активен на уровне systemd:

sudo systemctl status apt-daily.timer apt-daily-upgrade.timer --no-pager
sudo systemctl list-timers --all | grep apt-daily

На исправно работающем сервере оба таймера должны быть активны и ждать следующего запуска.

Что проверить после шага

Проверь, что:

  • apt-daily.timer находится в состоянии active (waiting)
  • apt-daily-upgrade.timer находится в состоянии active (waiting)

Ошибка новичка

Увидеть строку APT::Periodic::Unattended-Upgrade "1"; и решить, что этого достаточно. На практике ещё нужно проверить, что timers действительно активны и не отключены.

Шаг 10.3. Если таймеры выключены или сервисы замаскированы

Если таймеры неактивны, а apt-daily.service или apt-daily-upgrade.service замаскированы, приведи systemd-часть в порядок:

sudo systemctl unmask apt-daily.service apt-daily-upgrade.service
sudo systemctl daemon-reload
sudo systemctl enable --now apt-daily.timer apt-daily-upgrade.timer

После этого снова проверь состояние таймеров:

sudo systemctl status apt-daily.timer apt-daily-upgrade.timer --no-pager

Что проверить после шага

Оба таймера должны перейти в состояние active (waiting).

Шаг 10.4. Если хочешь оставить reboot только под ручным контролем

Если ты не хочешь, чтобы сервер автоматически перезагружался после некоторых обновлений, создай отдельный override-файл:

sudo nano /etc/apt/apt.conf.d/52-homelab-unattended-upgrades

Вставь в него:

Unattended-Upgrade::Automatic-Reboot "false";

Этого достаточно, если 20auto-upgrades уже настроен правильно.

Что делает эта настройка

Она запрещает unattended-upgrades автоматически перезагружать сервер после установки обновлений. Для VPS это удобно, если ты хочешь сам контролировать момент reboot.

Ошибка новичка

Дублировать в override-файле настройки из 20auto-upgrades, если ты их не меняешь. Это не ломает систему, но делает конфигурацию менее чистой и хуже читаемой.

Шаг 10.5. Проверяем работу через dry-run

Теперь проверь, как unattended-upgrades отрабатывает в тестовом режиме:

sudo unattended-upgrade --dry-run --debug

Нормальный результат для уже обновлённого сервера может выглядеть так:
No packages found that can be upgraded unattended and no pending auto-removals

Это не ошибка. Это означает, что unattended-upgrades работает, но в данный момент нет подходящих пакетов для автоматической установки по текущей политике.

Что проверить после шага

Проверь, что:

  • команда выполняется без аварийных ошибок
  • в выводе нет явных проблем с конфигом
  • dry-run доходит до конца

Шаг 10.6. Проверяем лог unattended-upgrades

После dry-run или после очередного запуска посмотри лог:

sudo tail -n 50 /var/log/unattended-upgrades/unattended-upgrades.log

Лог поможет понять:

  • запускается ли unattended-upgrades вообще
  • какие origins разрешены
  • какие источники пакетов были отклонены
  • есть ли ошибки во время обработки обновлений

Что проверить после шага

Если лог показывает, что неподходящие origins помечаются как not allowed, а подходящих обновлений сейчас просто нет, это нормальное поведение.

На VPS не надо слепо верить фразе «automatic security updates включены по умолчанию». Проверять нужно не только конфиги, но и таймеры, dry-run и логи.

Шаг 11. Проверяем, нужны ли перезапуски после обновлений

После установки обновлений полезно проверить, не осталось ли в системе сервисов или процессов, которые всё ещё работают на старых версиях библиотек или компонентов.

Для этого используется needrestart.

Сначала убедись, что пакет установлен:

sudo apt install -y needrestart

Теперь запусти проверку:

sudo needrestart

Нормальный результат для уже приведённого в порядок сервера может выглядеть так:

  • Running kernel seems to be up-to-date.
  • No services need to be restarted.
  • No containers need to be restarted.
  • No user sessions are running outdated binaries.

Это означает, что система после обновлений находится в согласованном состоянии: ядро актуально, сервисы не требуют дополнительного перезапуска, а пользовательские процессы не держат старые бинарники.

Что проверить после шага

Проверь, что:

  • needrestart запускается без ошибок
  • система не требует дополнительных обязательных перезапусков сервисов
  • нет сообщения о необходимости срочного reboot из-за уже установленных обновлений

Важно

needrestart не заменяет сами обновления и не делает hardening за тебя. Его задача — показать, не осталось ли после обновлений процессов и сервисов, которые всё ещё работают на старых версиях библиотек или компонентов.

Шаг 12. Проверяем время, NTP и часовой пояс

Для VPS корректное время важно не только для удобства, но и для нормальной работы логов, таймеров, Fail2ban, сертификатов и automatic updates.

Проверь текущее состояние времени:

timedatectl
sudo systemctl status systemd-timesyncd --no-pager

Нормальный результат:

  • System clock synchronized: yes
  • NTP service: active

Если сервер сейчас работает в UTC и тебе так удобно, можно оставить всё как есть.

Если хочешь, чтобы время в логах и systemd-таймерах сразу было в твоём часовом поясе, установи его явно.

Пример:

sudo timedatectl set-timezone Europe/Moscow

После этого снова проверь:

timedatectl

Что проверить после шага

Проверь, что:

  • синхронизация времени активна
  • NTP работает
  • часовой пояс выставлен так, как тебе нужно
  • локальное время выглядит ожидаемо

Важно

Часовой пояс — это вопрос удобства и читаемости логов. А вот работающий NTP и синхронизированное системное время — это уже нормальная техническая база для боевого VPS.

Шаг 13. Финальная проверка базовой защиты сервера

После завершения настройки полезно выполнить один итоговый блок команд и убедиться, что дальше ты работаешь уже не под root, а под своим администратором.

whoami
sudo -l
sudo id

echo '=== SSH EFFECTIVE CONFIG ==='
sudo sshd -T | egrep 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|allowusers|disableforwarding|x11forwarding|loglevel'

echo
echo '=== SSH SERVICES ==='
sudo systemctl status ssh --no-pager | sed -n '1,12p'
sudo systemctl status ssh.socket --no-pager | sed -n '1,12p'

echo
echo '=== LISTEN PORTS ==='
ss -lntup

echo
echo '=== UFW ==='
sudo ufw status verbose

echo
echo '=== FAIL2BAN ==='
sudo fail2ban-client status
sudo fail2ban-client status sshd

echo
echo '=== APPARMOR ==='
sudo aa-status

echo
echo '=== AUTO UPDATES ==='
sudo systemctl status apt-daily.timer apt-daily-upgrade.timer --no-pager
sudo tail -n 20 /var/log/unattended-upgrades/unattended-upgrades.log

echo
echo '=== TIME ==='
timedatectl
sudo systemctl status systemd-timesyncd --no-pager

echo
echo '=== NEEDRESTART ==='
sudo needrestart

Что должно быть в итоге

  • whoami показывает your_admin_user
  • sudo id показывает uid=0(root)
  • SSH принимает вход только по ключу
  • вход под root по SSH запрещён
  • парольная аутентификация по SSH отключена
  • вход по SSH разрешён только пользователю your_admin_user
  • ssh.service и ssh.socket активны
  • наружу открыт только 22/tcp
  • UFW включён и по умолчанию запрещает входящие подключения
  • Fail2ban активен и jail sshd работает
  • AppArmor загружен
  • apt-daily.timer и apt-daily-upgrade.timer активны
  • automatic security updates работают штатно
  • время синхронизируется нормально
  • needrestart не требует дополнительных перезапусков сервисов или системы

Контрольная перезагрузка

Когда базовая настройка завершена, сделай одну контролируемую перезагрузку:

sudo reboot

После повторного входа под your_admin_user выполни ещё раз:

whoami
ss -lntup
sudo ufw status verbose
sudo systemctl status ssh --no-pager
sudo systemctl status ssh.socket --no-pager
sudo systemctl status fail2ban --no-pager
sudo fail2ban-client status sshd
sudo systemctl status apparmor --no-pager
sudo systemctl status apt-daily.timer apt-daily-upgrade.timer --no-pager
timedatectl
sudo needrestart

Если всё поднялось штатно, значит базовая защита переживает reboot и baseline собран правильно.

Шпаргалка по UFW-логам

Смотреть лог вживую

sudo tail -f /var/log/ufw.log

Как выглядит запись

Пример:
[UFW BLOCK] IN=eth0 OUT= MAC=... SRC=87.251.64.144 DST=YOUR_SERVER_IP LEN=60 TOS=0x00 PREC=0x00 TTL=49 ID=54321 DF PROTO=TCP SPT=3204 DPT=22 WINDOW=64240 RES=0x00 SYN URGP=0

Что означает главное

  • [UFW BLOCK] — пакет заблокирован firewall
  • [UFW ALLOW] — пакет разрешён
  • IN=eth0 — трафик пришёл через интерфейс
  • SRC= — кто стучится
  • DST= — IP твоего сервера
  • PROTO= — TCP или UDP
  • SPT= — source port
  • DPT= — destination port на твоём сервере
  • SYN — попытка начать TCP-соединение

Как читать это на практике

Если видишь:
[UFW BLOCK] ... SRC=87.251.64.144 ... PROTO=TCP SPT=3204 DPT=22 ... SYN

это значит:

  • IP 87.251.64.144 пытался подключиться к твоему серверу
  • на 22 порт
  • по TCP
  • UFW это заблокировал

Самые полезные команды

Показать последние блокировки:

sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | tail -n 50

Показать попытки именно на SSH-порт:

sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | grep 'DPT=22' | tail -n 50

Показать попытки на конкретный порт, например 51820:

sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | grep 'DPT=51820' | tail -n 50

Показать разрешённый трафик:

sudo grep '\[UFW ALLOW\]' /var/log/ufw.log | tail -n 50

Показать, с каких IP чаще всего ломятся:

sudo grep '\[UFW BLOCK\]' /var/log/ufw.log | sed -n 's/.*SRC=\([^ ]*\).*/\1/p' | sort | uniq -c | sort -nr | head

Более удобный просмотр

sudo less /var/log/ufw.log

Внутри less можно искать:

  • /DPT=22
  • /UFW BLOCK
  • /87.251.64.144

Как смотреть логи Fail2ban

Самый надёжный способ

sudo journalctl -u fail2ban -n 50 --no-pager

Вживую:

sudo journalctl -u fail2ban -f

Если у тебя есть файл /var/log/fail2ban.log

sudo tail -n 50 /var/log/fail2ban.log

Вживую:

sudo tail -f /var/log/fail2ban.log

Самые полезные команды

Общий статус:

sudo fail2ban-client status

Статус SSH jail:

sudo fail2ban-client status sshd

Разбанить IP вручную:

sudo fail2ban-client set sshd unbanip IP_АДРЕС

Памятка: как выйти из просмотра логов

Если лог открыт так:

sudo tail -f /var/log/ufw.log

или так:

sudo journalctl -u fail2ban -f

выйти можно клавишами:
Ctrl + C

Если лог открыт через less:

sudo less /var/log/ufw.log

тогда для выхода нажми:
q

Что важно запомнить

Этот мануал не про «абсолютно неуязвимый VPS». Он про то, чтобы сразу после установки Ubuntu 24.04 LTS привести сервер в адекватное, предсказуемое и рабочее состояние:

  • не сидеть под root
  • не оставлять парольный SSH
  • не включать firewall наугад
  • не забыть Fail2ban
  • не игнорировать AppArmor
  • отдельно проверить automatic security updates
  • убедиться, что после reboot защита не разваливается

Если после этого базового слоя ты переходишь к установке Docker, VPN, reverse proxy, DNS или любых других сервисов, ты начинаешь уже не с сырого VPS, а с нормальной защищённой основы.