Ubuntu 24.04: базовая настройка и hardening для production#
Ubuntu 24.04 LTS - production-ready дистрибутив с поддержкой до 2029 года (стандартная) и до 2034 года (расширенная через Ubuntu Pro). Из коробки он безопаснее голого Debian - UFW предустановлен, unattended-upgrades работает сразу - но настройки по умолчанию далеки от production-ready.
Статья покрывает всё что нужно для сервера без контейнеров. Docker - отдельная тема.
Что получишь на выходе:
- SSH доступен только по ключам с нестандартного порта
- Firewall блокирует всё лишнее
- Fail2ban режет брутфорс
- Swap настроен как файл (гибко, без переразбивки диска)
- Ядро защищено от базовых сетевых атак
- Автообновления безопасности без ручного вмешательства
- Ненужные сервисы отключены
- Логи ротируются, диск не забивается
- Конфиги забэкаплены
Исходные данные#
- Чистый Ubuntu 24.04 LTS (Noble Numbat)
- Root доступ или пользователь с sudo
- Статический IP
Проверь версию:
lsb_release -aОжидаемый вывод:
Distributor ID: Ubuntu
Release: 24.04
Codename: nobleПро пользователя: Ubuntu при установке создаёт непривилегированного пользователя с sudo. Если устанавливал с таким пользователем - используй его. Если получил root-only сервер (облако, VPS) - создадим пользователя в разделе SSH.
Все команды выполняются с sudo если не указано иное. Если работаешь от root - sudo можно опустить.
Обновление системы#
sudo apt update
sudo apt upgrade -y
sudo apt dist-upgrade -y
sudo apt autoremove -yUbuntu использует needrestart - после обновления сам сообщит какие сервисы нужно перезапустить. Если обновилось ядро, предложит перезагрузиться.
Проверь явно:
[ -f /var/run/reboot-required ] && echo "Reboot needed" || echo "No reboot needed"Если нужна:
sudo rebootSnap пакеты#
Ubuntu активно использует snap. Обнови snap пакеты отдельно:
sudo snap refreshПосмотри что установлено из snap:
snap listУдали ненужное - каждый snap это отдельный процесс и место на диске:
# Пример удаления
sudo snap remove lxd
sudo snap remove multipassНа минимальной серверной установке snap пакетов обычно нет или только core - это нормально.
Базовые утилиты#
sudo apt install -y \
curl wget git vim htop iotop iftop \
net-tools dnsutils tcpdump \
screen tmux \
rsync unzip \
ca-certificates gnupg2 lsb-release \
smartmontoolsНастройка системы#
Hostname#
sudo hostnamectl set-hostname srv01.example.comДобавь в /etc/hosts:
sudo nano /etc/hosts127.0.0.1 localhost
YOUR_IP srv01.example.com srv01
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allroutersЗамени YOUR_IP на реальный IP сервера.
Timezone#
sudo timedatectl set-timezone Europe/MoscowПроверь:
timedatectlUbuntu 24.04 использует systemd-timesyncd для синхронизации времени - он работает из коробки. Проверь:
timedatectl show-timesyncЕсли нужен ntpd вместо timesyncd - это отдельная история. Для большинства серверов timesyncd достаточен.
Локаль#
sudo apt install -y locales
sudo dpkg-reconfigure localesВыбери en_US.UTF-8 как основную. Добавь ru_RU.UTF-8 если нужна кириллица.
Проверь:
localeДолжно быть LANG=en_US.UTF-8.
DNS и systemd-resolved#
Ubuntu 24.04 использует systemd-resolved - DNS резолвер работает на 127.0.0.53. /etc/resolv.conf это симлинк.
Проверь:
resolvectl statusПосмотри на какой файл указывает симлинк:
ls -la /etc/resolv.confЭто нормально - не трогай если резолвинг работает. Если настраиваешь статический DNS:
sudo nano /etc/systemd/resolved.conf[Resolve]
DNS=8.8.8.8 8.8.4.4
FallbackDNS=1.1.1.1sudo systemctl restart systemd-resolvedВажно: Docker контейнеры не могут использовать 127.0.0.53 - это адрес loopback недоступный из контейнера. Для Docker-хоста это решается отдельно в следующей статье серии.
Пользователь и SSH#
Создание пользователя (если нет)#
Если получил сервер без непривилегированного пользователя:
adduser admin
usermod -aG sudo adminПроверь:
groups admin
# admin : admin sudoSSH ключи#
На своей рабочей машине сгенерируй ключ:
# Linux/macOS
ssh-keygen -t ed25519 -C "admin@srv01"# Windows
ssh-keygen -t ed25519Скопируй публичный ключ на сервер:
# Linux/macOS
ssh-copy-id admin@YOUR_IP# Windows
type $env:userprofile\.ssh\id_ed25519.pub | ssh admin@YOUR_IP "mkdir -m 700 -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"Проверь вход по ключу - не должен спрашивать пароль:
ssh admin@YOUR_IPSSH hardening#
Ubuntu 24.04 хранит конфигурацию SSH в /etc/ssh/sshd_config.d/ - основной файл не трогаем.
Создай файл с настройками:
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf# Нестандартный порт - отсекает большинство ботов
Port 2222
# Только ключи, без паролей
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
# Запретить root логин
PermitRootLogin no
# Ограничения сессий
MaxAuthTries 3
MaxSessions 3
LoginGraceTime 30
# Таймауты
ClientAliveInterval 300
ClientAliveCountMax 2
# Отключить лишнее
X11Forwarding no
PrintMotd no
AcceptEnv LANG LC_*
# Только IPv4 (убери если используешь IPv6)
AddressFamily inetВажно: Ubuntu 24.04 использует socket-активацию через ssh.socket - порт слушает не sshd напрямую, а сокет-юнит. Обычный systemctl restart ssh перезапускает только сервис, порт остаётся прежним. Для применения нового порта нужно перезапускать именно сокет.
КРИТИЧНО: Перед перезапуском открой вторую сессию и не закрывай её.
Проверь конфиг на ошибки:
sudo sshd -tНет вывода - нет ошибок. Примени:
sudo systemctl daemon-reload
sudo systemctl restart ssh.socketПроверь что сервис поднялся на новом порту:
sudo ss -tlnp | grep sshdОжидаемый вывод:
LISTEN 0 4096 0.0.0.0:2222Убедись что конфиг сокета подхватил изменение:
sudo systemctl cat ssh.socketСтрока ListenStream=0.0.0.0:2222 подтверждает что порт применился.
В новом терминале проверь подключение:
ssh -p 2222 admin@YOUR_IPЕсли работает - старые сессии можно закрывать.
SSH banner (опционально)#
sudo nano /etc/ssh/banner###############################################################################
# Доступ только для авторизованных пользователей. #
# Все действия логируются. #
# Несанкционированный доступ преследуется по понятиям! #
###############################################################################Добавь в /etc/ssh/sshd_config.d/99-hardening.conf:
Banner /etc/ssh/bannerПерезапусти:
sudo systemctl restart sshFirewall (UFW)#
UFW в Ubuntu предустановлен, но по умолчанию выключен.
Проверь статус:
sudo ufw statusСкорее всего увидишь Status: inactive.
Настройка правил#
Важно: сначала правила, потом включение. Иначе заблокируешь себя.
# Блокировать все входящие
sudo ufw default deny incoming
# Разрешить все исходящие
sudo ufw default allow outgoingРазрешаем SSH на новом порту:
# Замени 2222 на свой порт
sudo ufw allow 2222/tcp comment 'SSH'Rate limiting на SSH:
sudo ufw limit 2222/tcp comment 'SSH rate limit'Включаем:
sudo ufw enableПроверь:
sudo ufw status verboseОжидаемый вывод:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
2222/tcp LIMIT IN Anywhere # SSH rate limit
2222/tcp (v6) LIMIT IN Anywhere (v6) # SSH rate limitОткрытие дополнительных портов (опционально)#
# HTTP/HTTPS
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# PostgreSQL только с конкретного IP
sudo ufw allow from 192.168.1.10 to any port 5432 comment 'PostgreSQL'Проверь открытые порты на хосте:
ss -tulnpFail2ban#
sudo apt install -y fail2banСоздай локальный конфиг:
sudo nano /etc/fail2ban/jail.local[DEFAULT]
# Бан на 1 час
bantime = 3600
# За 10 минут
findtime = 600
# После 3 неудачных попыток
maxretry = 3
# Не банить свои IP
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600Замени 2222 на свой SSH порт.
Особенность Ubuntu 24.04: systemd-journald по умолчанию не пишет в /var/log/auth.log. Если файл отсутствует или пустой:
ls -la /var/log/auth.logВключи запись в файл:
sudo nano /etc/rsyslog.d/50-default.confУбедись что строка не закомментирована:
auth,authpriv.* /var/log/auth.logПерезапусти rsyslog:
sudo systemctl restart rsyslogИли используй journald backend в fail2ban - он умеет читать логи напрямую из journald без файлов:
sudo nano /etc/fail2ban/jail.localДобавь в секцию [sshd]:
[sshd]
enabled = true
port = 2222
filter = sshd
backend = systemd
maxretry = 3
bantime = 3600При backend = systemd параметр logpath не нужен - fail2ban читает journald напрямую.
Запусти:
sudo systemctl enable fail2ban
sudo systemctl start fail2banПроверь статус:
sudo fail2ban-client status
sudo fail2ban-client status sshdSwap#
Swap в виде файла - гибче раздела: можно увеличить или уменьшить без переразбивки диска.
Создание swap файла#
Проверь текущий swap:
free -h
swapon --showОжидаемый вывод если swap отсутствует:
Swap: 0B 0B 0BСоздай файл (пример - 2GB):
sudo fallocate -l 2G /swapfileЕсли fallocate не поддерживается файловой системой (например btrfs):
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progressУстанови права:
sudo chmod 600 /swapfileИнициализируй и активируй:
sudo mkswap /swapfile
sudo swapon /swapfileПроверь:
free -h
swapon --showОжидаемый вывод:
total used free shared buff/cache available
Mem: 1.9Gi 860Mi 240Mi 2.5Mi 1.0Gi 1.1Gi
Swap: 2.0Gi 0B 2.0Gi
NAME TYPE SIZE USED PRIO
/swapfile file 2G 0B -2Добавь в /etc/fstab для автомонтирования после перезагрузки:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabНастройка swappiness#
swappiness определяет агрессивность использования swap. Для production - снижаем:
cat /proc/sys/vm/swappiness
# по умолчанию 60Установи постоянно:
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.d/99-swap.conf
sudo sysctl -p /etc/sysctl.d/99-swap.confЗначение 10 - swap используется только когда RAM заполнена на 90%+.
Изменение размера swap#
Нельзя перезаписать активный swap файл - ядро блокирует его пока он используется:
$ sudo fallocate -l 4G /swapfile
fallocate: fallocate failed: Text file busy
$ sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress
dd: failed to open '/swapfile': Text file busyСначала отключи swap, затем меняй размер:
sudo swapoff /swapfilesudo fallocate -l 4G /swapfilesudo mkswap /swapfilesudo swapon /swapfileПроверь результат:
free -hОжидаемый вывод:
total used free shared buff/cache available
Mem: 1.9Gi 872Mi 227Mi 2.5Mi 1.0Gi 1.1Gi
Swap: 4.0Gi 0B 4.0GiЗапись в /etc/fstab менять не нужно - путь тот же.
Удаление swap#
sudo swapoff /swapfilesudo rm /swapfileУдали строку из /etc/fstab:
sudo nano /etc/fstab
# удали строку: /swapfile none swap sw 0 0Sysctl - защита ядра#
sudo nano /etc/sysctl.d/99-hardening.conf# Защита от SYN flood
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Игнорировать ICMP redirects (защита от MITM)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Не отправлять ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Игнорировать source routed packets
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Защита от IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Логировать подозрительные пакеты
net.ipv4.conf.all.log_martians = 1
# Защита от time-wait assassination
net.ipv4.tcp_rfc1337 = 1
# Отключить IPv6 если не используешь
# net.ipv6.conf.all.disable_ipv6 = 1
# net.ipv6.conf.default.disable_ipv6 = 1Примени:
sudo sysctl -p /etc/sysctl.d/99-hardening.confПроверь:
sudo sysctl net.ipv4.tcp_syncookies
# net.ipv4.tcp_syncookies = 1Автообновления безопасности#
Ubuntu 24.04 устанавливает unattended-upgrades автоматически. Проверь:
sudo systemctl status unattended-upgradesЕсли не установлен:
sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgradesНастрой конфиг:
sudo nano /etc/apt/apt.conf.d/50unattended-upgradesУбедись что раскомментированы (или добавь):
Unattended-Upgrade::Mail "root";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";Automatic-Reboot "false" - перезагрузку после обновления ядра делаешь вручную в удобное время.
Расписание:
sudo nano /etc/apt/apt.conf.d/10periodicAPT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";Отключение ненужных сервисов#
Посмотри что запущено:
systemctl list-units --type=service --state=runningUbuntu 24.04 по умолчанию запускает больше сервисов чем Debian. Кандидаты на отключение:
# Bluetooth - на сервере не нужен
sudo systemctl disable --now bluetooth.service
# avahi-daemon - mDNS/DNS-SD, нужен редко
sudo systemctl disable --now avahi-daemon.service
# ModemManager - управление модемами
sudo systemctl disable --now ModemManager.service
# snapd - если не используешь snap пакеты
# ОСТОРОЖНО: некоторые облачные образы зависят от snap
sudo systemctl disable --now snapd.service snapd.socketПо поводу snapd: если сервер облачный и ты не знаешь точно что использует snap - не отключай. Проверь сначала:
snap listЕсли кроме core и snapd ничего нет - можно отключать.
Проверь открытые порты:
ss -tulnpЛоги и ротация#
Ограничение journald#
sudo nano /etc/systemd/journald.confSystemMaxUse=500M
SystemMaxFileSize=100M
MaxRetentionSec=1monthПерезапусти:
sudo systemctl restart systemd-journaldПроверь размер:
sudo journalctl --disk-usageРотация rsyslog#
sudo nano /etc/logrotate.d/rsyslog/var/log/syslog
/var/log/mail.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/cron.log
{
su root syslog
rotate 7
daily
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}Директива su root syslog обязательна - без неё logrotate отказывается ротировать файлы в /var/log, потому что директория доступна на запись группе syslog, что считается небезопасным.
Проверь конфиг dry run:
sudo logrotate -d /etc/logrotate.d/rsyslogОжидаемый вывод:
...
rotating pattern: /var/log/syslog
/var/log/mail.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/cron.log
after 1 days (7 rotations)
empty log files are not rotated, old logs are removed
switching euid from 0 to 0 and egid from 0 to 104 (pid 76669)
...Мониторинг диска#
Определение имени диска#
Имя диска зависит от окружения - не хардкодь /dev/sda:
lsblkОжидаемый вывод (ориентировочный) на физическом сервере:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 500G 0 disk
├─sda1 8:1 0 1G 0 part /boot
└─sda2 8:2 0 499G 0 part /Ожидаемый вывод на виртуальной машине (KVM/Proxmox):
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 253:0 0 30G 0 disk
├─vda1 253:1 0 1M 0 part
└─vda2 253:2 0 30G 0 part /vda - virtio-диск на гипервизоре. sda - физический диск или SCSI-эмуляция.
Физический сервер - SMART#
На физическом сервере SMART даёт ранние признаки деградации диска:
sudo smartctl -H /dev/sdaОжидаемый вывод:
SMART overall-health self-assessment test result: PASSEDДетали:
sudo smartctl -a /dev/sdaОбращай внимание на три атрибута - если ненулевые, диск проблемный:
Reallocated_Sector_Ct- переназначенные секторыPending_Sector_Count- ожидающие переназначенияOffline_Uncorrectable- неисправимые ошибки
Виртуальная машина - SMART недоступен#
На VM smartctl не работает - гипервизор не пробрасывает SMART данные гостю:
/dev/vda: Unable to detect device type
Please specify device type with the -d option.Это нормально. SMART мониторинг на виртуальных дисках делается на уровне гипервизора - в Proxmox через Datacenter → Node → Disks. Внутри VM контролируй заполнение и I/O:
df -hiostat -x 1 3iostat входит в пакет sysstat:
sudo apt install -y sysstatОбращай внимание на %util в выводе iostat - значения близкие к 100% означают что диск перегружен.
Бэкап конфигов#
Скрипт архивирует /etc/, SSH ключи и список установленных пакетов.
sudo nano /root/backup-configs.sh#!/bin/bash
BACKUP_DIR="/root/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/config_backup_$DATE.tar.gz"
mkdir -p "$BACKUP_DIR"
dpkg --get-selections > /root/installed-packages.txt
tar -czf "$BACKUP_FILE" \
/etc/ \
/root/.ssh/ \
/home/*/.ssh/ \
/root/installed-packages.txt \
2>/dev/null
find "$BACKUP_DIR" -name "config_backup_*.tar.gz" -mtime +30 -delete
echo "Backup: $BACKUP_FILE"
ls -lh "$BACKUP_FILE"Установи права и запусти первый раз:
sudo chmod +x /root/backup-configs.sh
sudo /root/backup-configs.shОжидаемый вывод:
Backup: /root/backups/config_backup_20260707_145620.tar.gz
-rw-r--r-- 1 root root 660K Jul 7 14:56 /root/backups/config_backup_ДАТА.tar.gzАвтоматизация через cron (каждое воскресенье в 3:00):
sudo crontab -e0 3 * * 0 /root/backup-configs.sh >> /var/log/backup-configs.log 2>&1Важно: архив лежит на том же диске что и система. Это защита от случайных изменений конфигов, не от отказа диска. Копируй архивы на внешний сервер или S3-совместимое хранилище.
Восстановление конфигов#
Восстановить /etc/ и SSH ключи из архива:
sudo tar -xzf /root/backups/config_backup_ДАТА.tar.gz -C /Восстановить список пакетов:
sudo dpkg --set-selections < /root/installed-packages.txtsudo apt-get dselect-upgradeФинальная проверка#
# Открытые порты
ss -tulnp
# Статус firewall
sudo ufw status verbose
# Статус fail2ban
sudo fail2ban-client status sshd
# Состояние диска
sudo smartctl -H /dev/sda
# Swap
free -hЧто дальше#
Сервер готов к установке приложений.
- Docker хост - следующая статья серии
- Nginx/Traefik - reverse proxy поверх этой базы
- PostgreSQL/MySQL - базы данных
- Postfix/Dovecot - почтовый сервер
Стек этой статьи: Ubuntu 24.04 LTS (Noble Numbat) · UFW · Fail2ban · OpenSSH · unattended-upgrades · systemd · systemd-resolved · smartmontools
Послесловие: пока мы настраивали - они уже ломились#
Пока эта статья писалась и проверялась на реальном сервере, снаружи шла своя работа. Проверяем:
sudo fail2ban-client status sshdStatus for the jail: sshd
|- Filter
| |- Currently failed: 4
| |- Total failed: 22863
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
|- Currently banned: 3
|- Total banned: 18
`- Banned IP list: 167.99.72.161 104.236.83.40 47.77.182.5422 863 неудачных попытки. 18 забаненных IP. 3 в бане прямо сейчас.
Сколько времени сервер вообще стоит в интернете:
uptime -pup 4 days, 3 hours, 21 minutes4 дня. ~235 попыток в час, почти 4 в минуту - непрерывно, круглосуточно.
Посмотрим на что они надеялись:
sudo grep "Invalid user" /var/log/auth.log | grep -oP "Invalid user \K\S+" | sort | uniq -c | sort -rn | head -10 124 ubuntu
101 user
91 test
75 debian
59 ftpuser
55 postgres
50 oracle
34 git
34 deploy
25 deployerubuntu - 124 попытки, классика для облачных серверов. debian - 75, логично для нашего дистрибутива. ftpuser - 59, кто-то ищет забытый FTP из 2005 года. postgres, oracle - надеялись что база данных торчит наружу с дефолтным пользователем. git, deploy, deployer - охота на CI/CD пайплайны с предсказуемыми именами.
Все эти попытки получили одинаковый ответ: порт 22 закрыт, нестандартный порт принимает только ключи, после трёх ошибок - бан на час.
Это не повод расслабиться. Это повод убедиться что всё настроено именно так, как описано выше - потому что 22 863 попытки за 4 дня на одном небольшом сервере это не целевая атака, это обычный фоновый шум интернета. Целевая атака выглядит иначе.








