Вступление
После установки 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_ed25519id_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: yesNTP 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_usersudo 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 или UDPSPT=— source portDPT=— 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, а с нормальной защищённой основы.