Настройка нового root-сервера: чек-лист на первые 30 минут

Опубликовано 14 мин. чтения

Первые 30 минут на новом root-сервере решают многое. Девять шагов в правильном порядке, вместе с ловушками Debian 13 и Ubuntu 24.04, которых нет в большинстве руководств.

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

Все команды предполагают, что вы работаете от имени root, то есть так, как сразу после выдачи сервера. Как только вы войдёте под своим новым пользователем, добавляйте к каждой команде sudo. Порядок шагов проверен на Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS и Ubuntu 22.04 LTS. Там, где эти четыре системы ведут себя по-разному, это отмечено отдельно.

Шаг 0: обеспечьте себе путь назад, прежде чем что-то менять

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

Кроме того, стоит заранее выяснить, где находится консольный доступ к серверу, ещё до того, как он понадобится. В KernelHost он открывается в личном кабинете, независимо от SSH и независимо от файрвола. Тот, кто начинает искать этот путь уже после того, как оказался заперт снаружи, теряет время.

Два сообщения об ошибке стоит научиться различать, потому что они указывают на совершенно разные причины:

  • ssh: connect to host 203.0.113.10 port 22: Connection refused означает, что пакет дошёл, но на этом порту никто не слушает. Служба SSH не запущена либо слушает другой порт.
  • ssh: connect to host 203.0.113.10 port 22: Connection timed out означает, что пакет был отброшен. Почти всегда это файрвол.
  • Permission denied (publickey) означает, что служба работает и файрвол вас пропускает, просто ваш ключ не подходит.

Шаг 1: обновление системы

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

cat /etc/os-release
apt update
apt full-upgrade -y

full-upgrade вместо upgrade выбран здесь осознанно: на свежей системе apt разрешено удалять пакеты, если этого требует зависимость. На работающей продуктивной системе вы бы сначала проверили, что именно предлагается удалить.

В Ubuntu 22.04 и 24.04 пакет needrestart предустановлен и прерывает обновление ярким полноэкранным диалогом с вопросом, какие службы перезапустить. Если это не нужно, например внутри скрипта:

NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive apt full-upgrade -y

После этого очистка и проверка, требуется ли перезагрузка:

apt autoremove --purge -y
apt list --upgradable
test -f /var/run/reboot-required && echo "требуется перезагрузка" || echo "перезагрузка не требуется"

Различие между дистрибутивами: файл /var/run/reboot-required надёжно создаёт только Ubuntu, он приходит из пакета update-notifier-common. Debian по умолчанию вообще не сообщает о необходимости перезагрузки. На Debian для этого доустанавливают needrestart, при каждом запуске он говорит, лежит ли наготове новое ядро. Кроме того, Debian 13 несёт новое поколение apt с цветным выводом, отформатированным по колонкам: это не ошибка, а просто непривычно.

Проверка результата: apt list --upgradable не выводит ничего, кроме заголовка Listing.... Если при обновлении появляется сообщение Release file for ... is not valid yet, значит часы сервера идут неверно: перейдите к шагу 5, а затем повторите шаг 1.

Подробнее об этом, в том числе о работе с удержанными пакетами и сторонними репозиториями: Обновление Linux-сервера через apt.

Шаг 2: создайте пользователя вместо работы под root

Под root не работают, потому что любая опечатка сразу задевает всю систему и потому что имя пользователя root известно каждому атакующему. На минимальных образах Debian пакет sudo часто даже не установлен:

apt install -y sudo
adduser --disabled-password --gecos "" kernel
usermod -aG sudo kernel

Ключ --disabled-password создаёт пользователя без пароля, и для входа исключительно по ключу это именно то, что нужно. Если пароль всё же нужен, например для sudo через консоль, задайте его командой passwd kernel.

Различие между дистрибутивами: в Debian и Ubuntu группа администраторов называется sudo. Группу wheel ищут только те, кто пришёл с систем семейства RHEL, здесь её нет.

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

mkdir -p /home/kernel/.ssh
chmod 700 /home/kernel/.ssh
touch /home/kernel/.ssh/authorized_keys
chmod 600 /home/kernel/.ssh/authorized_keys
chown -R kernel:kernel /home/kernel/.ssh

Содержимое своего публичного ключа впишите в authorized_keys. Удобнее сделать это со своей рабочей машины командой ssh-copy-id kernel@203.0.113.10.

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

id kernel
sudo -l -U kernel

Вторая строка должна содержать (ALL : ALL) ALL. Затем войдите в третьем окне как kernel и один раз выполните sudo -v. Дальше двигаемся только тогда, когда это сработало. Подробности: Создание пользователя и настройка sudo, а также Создание и загрузка SSH-ключей.

Шаг 3: защита SSH

На очень урезанных образах Debian сервер SSH даже не установлен, тогда сначала доустановите его:

apt install -y openssh-server

Все четыре рассматриваемые здесь системы читают дополнительную конфигурацию из /etc/ssh/sshd_config.d/. Поэтому правьте не большой sshd_config, а заведите собственный файл. Он переживает обновления пакетов без единого вопроса.

Деталь, которую почти все руководства излагают неверно: в конфигурации SSH побеждает первое найденное значение, а не последнее. Строка Include /etc/ssh/sshd_config.d/*.conf стоит у Debian и Ubuntu в самом начале, а файлы в этом каталоге читаются в алфавитном порядке. На образах Ubuntu там нередко уже лежит 50-cloud-init.conf с PasswordAuthentication yes. Файл с именем 99-... остался бы поэтому без всякого эффекта. Сначала посмотрите, что там уже есть:

ls -l /etc/ssh/sshd_config.d/
cat > /etc/ssh/sshd_config.d/10-kernelhost.conf <<'EOF'
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
EOF

prohibit-password вместо no — это осознанное решение: root по-прежнему может войти по ключу, но никогда по паролю. Это выручает, если с пользователем sudo что-то пойдёт не так. Кто хочет закрутить гайки сильнее, ставит no, но тогда консольный доступ действительно должен быть проверен заранее.

Перед каждым перезапуском службы проверяйте синтаксис:

ssh-keygen -A
sshd -t && echo "конфигурация в порядке"
sshd -T | grep -E "^(permitrootlogin|passwordauthentication|pubkeyauthentication|port) "

sshd -T показывает фактически действующие значения, уже после разбора всех включённых файлов. Это единственное надёжное доказательство того, что ваше изменение дошло до системы. Если команда сообщает sshd: no hostkeys available -- exiting, отсутствуют ключи хоста, их создаёт ssh-keygen -A. Если же проверка обрывается с Missing privilege separation directory: /run/sshd, значит служба ни разу не запускалась с момента загрузки системы и каталог времени выполнения отсутствует. Его создаёт mkdir -p /run/sshd или systemctl restart ssh, после чего sshd -t снова оценивает вашу конфигурацию.

Один фрагмент вывода регулярно сбивает с толку: для PermitRootLogin prohibit-password команда sshd -T печатает строку permitrootlogin without-password. Это то же самое значение под своим старым именем, а не признак того, что ваша настройка не применилась.

И только после этого:

systemctl restart ssh

Ловушка с сокетом в Ubuntu 24.04 и Debian 13

Начиная с Ubuntu 22.10, а значит и в 24.04, SSH запускается через активацию по сокету. Следствие: строка Port в конфигурации sshd игнорируется, порт берётся из ssh.socket. Кому нужно сменить порт, тому понадобится оверлей systemd:

systemctl edit ssh.socket

Туда вписывается следующее, причём пустая первая строка сбрасывает значение по умолчанию:

[Socket]
ListenStream=
ListenStream=0.0.0.0:2222
ListenStream=[::]:2222

Затем systemctl daemon-reload и systemctl restart ssh.socket. Ubuntu 22.04 такого механизма ещё не знает, там достаточно строки Port в конфигурации.

На Debian 13 встречается родственная странность. У части свежих образов ssh.socket активен, у систем, обновлённых с Debian 12, нет. Если активно и то и другое одновременно, перечитывание конфигурации падает с fatal: Cannot bind any address., потому что служба и сокет спорят за порт 22. Проверьте и при сомнении выберите что-то одно:

systemctl is-enabled ssh.socket
systemctl disable --now ssh.socket
systemctl enable --now ssh.service

Проверка результата: ss -tlnp | grep ssh показывает ожидаемый порт, и новая попытка подключения из свежего окна проходит. Подробно: Защита SSH и отключение входа под root и Смена порта SSH.

Шаг 4: включение файрвола

В Ubuntu ufw установлен, но неактивен. На минимальных образах Debian его нет вовсе. Порядок здесь жизненно важен: сначала разрешить SSH, потом включать.

apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Последняя команда самая важная во всём списке. Правила и политики по умолчанию сами по себе ничего не фильтруют, в боевой режим файрвол переводит только ufw enable. Кто её пропустит, получит в итоге полностью настроенный, но бездействующий файрвол.

При включении ufw спрашивает: Command may disrupt existing ssh connections. Proceed with operation (y|n)?. Это предупреждение сказано всерьёз, но если правило для порта 22 (или вашего изменённого порта) уже стоит выше, ничего не произойдёт, и именно поэтому ufw allow 22/tcp в списке идёт перед ufw enable. Если на шаге 3 вы меняли порт, здесь должно стоять ufw allow 2222/tcp, иначе вы запрёте себя снаружи. В скрипте или в неинтерактивной сессии используйте ufw --force enable, это пропускает вопрос.

Проверка результата:

ufw status verbose

Ожидается Status: active, ниже Default: deny (incoming), allow (outgoing), а в списке правил строка 22/tcp ALLOW IN для вашего порта SSH. Если там по-прежнему стоит Status: inactive, значит не хватает ufw enable и не защищено ничего, каким бы полным ни выглядел список правил. После этого откройте новое окно и подключитесь, прежде чем закрывать старое.

Дополнительно к этому нужен механизм блокировки перебора паролей:

apt install -y fail2ban python3-systemd
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
backend = systemd
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
EOF

Почему backend = systemd: начиная с версии 12 Debian больше не тянет за собой rsyslog, а значит нет и файла /var/log/auth.log. Значение по умолчанию backend = auto ищет именно этот файл, и fail2ban тогда вообще не стартует, сообщая Failed during configuration: Have not found any log file for sshd jail. Пакет python3-systemd нужен для того, чтобы доступ к журналу вообще заработал. В Ubuntu 22.04 и 24.04 файл ещё существует благодаря rsyslog, но вариант с systemd работает и там и остаётся выбором на перспективу. Посмотреть можно так:

test -f /var/log/auth.log && echo "auth.log есть" || echo "нет auth.log, нужен backend systemd"
fail2ban-client -t

Проверка результата: fail2ban-client status sshd показывает строку Currently banned. Если вместо этого приходит Sorry but the jail 'sshd' does not exist, конфигурация не была загружена. Больше в статьях Настройка файрвола UFW и Настройка Fail2ban.

Шаг 5: часовой пояс и время

Неверные часы обесценивают журналы, ломают проверку сертификатов и могут заблокировать apt сообщением Release file is not valid yet. На настоящем сервере:

timedatectl set-timezone Europe/Vienna
timedatectl status

В выводе должны сойтись две строки: Time zone: Europe/Vienna и System clock synchronized: yes, а также NTP service: active. Если там стоит NTP service: inactive, синхронизация времени не работает. Ubuntu по умолчанию несёт systemd-timesyncd, минимальные образы Debian часто нет:

DEBIAN_FRONTEND=noninteractive apt install -y systemd-timesyncd tzdata
date

Многие операторы сознательно держат серверы на UTC, чтобы журналы из разных локаций оставались сопоставимыми. Оба варианта приемлемы, важно лишь, чтобы вы знали, какой из них у вас. Если timedatectl в контейнерном окружении недоступен, годится и классический способ:

ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
dpkg-reconfigure -f noninteractive tzdata

Углубиться: Настройка часового пояса и синхронизации времени.

Шаг 6: имя хоста

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

hostnamectl set-hostname srv01.example.com
hostname -f

Дальше нужна соответствующая запись в /etc/hosts, иначе при каждом вызове sudo вас будет встречать сообщение sudo: unable to resolve host srv01: Name or service not known с заметной задержкой. Строка выглядит примерно так: 127.0.1.1 srv01.example.com srv01.

Ловушка: на образах с cloud-init, а в Ubuntu это правило, имя хоста при следующей перезагрузке сбрасывается. Средство против этого:

command -v cloud-init || echo "cloud-init не установлен"
mkdir -p /etc/cloud/cloud.cfg.d
printf 'preserve_hostname: true\n' > /etc/cloud/cloud.cfg.d/99_hostname.cfg

Проверка результата: после перезагрузки hostnamectl по-прежнему возвращает ваше имя. Подробности: Постоянная смена имени хоста в Linux.

Шаг 7: автоматические обновления безопасности

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

apt install -y unattended-upgrades
cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOF

Установки самого пакета хватает не везде, ежедневный запуск включает только этот файл. Собственные тонкие настройки кладут в файл с номером выше, чем у поставляемого 50unattended-upgrades, чтобы они побеждали и не перезаписывались при обновлении пакета:

cat > /etc/apt/apt.conf.d/52unattended-upgrades-local <<'EOF'
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::MinimalSteps "true";
EOF

По умолчанию на обоих дистрибутивах инструмент тянет только из репозитория безопасности, а не обычные обновления. Это сделано намеренно и для продуктивных систем обычно как раз правильно. Если вы ставите Automatic-Reboot "true", обязательно задайте и время, иначе сервер перезагрузится тогда, когда это будет удобно таймеру.

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

unattended-upgrade --dry-run --debug
apt-config dump | grep -iE "unattended|periodic"

В журнале под /var/log/unattended-upgrades/ после первого запуска должно что-то появиться. Если он остаётся пустым, конфигурация не действует. Подробно: Настройка автоматических обновлений безопасности.

Шаг 8: мониторинг

Мониторинг в первые 30 минут не означает поднять Grafana. Он означает, что вы узнаете, когда сервер встанет или заполнится накопитель.

apt install -y htop tmux curl
df -h /
free -m

Для начала хватит трёх вещей. Первое: внешняя проверка доступности, которая тестирует снаружи и уведомляет вас, потому что упавший сервер уже не пришлёт предупреждения о себе. Второе: оповещение о нехватке места на диске, потому что переполненная файловая система остаётся самой частой причиной сбоев, которых никто не ждал. Третье: взгляд в журнал, когда что-то выглядит странно:

journalctl -p 3 -b --no-pager | tail -n 30
systemctl --failed

systemctl --failed должен выводить 0 loaded units listed. Каждая строка там — это служба, которая не запускается и которую вы хотите починить сейчас, а не через три месяца. Подробнее: Настройка мониторинга сервера.

Шаг 9: резервная копия, пока терять ещё нечего

Лучший момент для первой резервной копии наступает до того, как появились данные. Тогда вы отрабатываете процедуру без давления. Две вещи стоит записать наружу сразу, потому что их восстановление отнимает больше всего времени: конфигурация в /etc и список установленных пакетов.

tar -czf /root/etc-backup-$(date +%F).tar.gz /etc
dpkg --get-selections > /root/packages.txt
tar -tzf /root/etc-backup-$(date +%F).tar.gz | wc -l

Это ещё не резервная копия, а лишь копия на том же накопителе. Резервная копия лежит на другой системе, в идеале в другом месте. Инструмент с шифрованием и дедупликацией одинаковых блоков окупается с первого дня:

apt install -y borgbackup
borg --version

Неудобная правда: резервная копия, из которой ещё ни разу ничего не восстанавливали, — это всего лишь предположение. Назначьте себе время на первое восстановление и верните один-единственный файл. Как это делается, описано в статье Стратегия резервного копирования для root-серверов.

Если вы заперли себя снаружи

Такое случается, чаще всего на шаге 3 или 4. Путь назад всегда один: войти через консоль в личном кабинете, там по имени пользователя и паролю, а не по ключу. Дальше по обстоятельствам:

  • Слишком строгий файрвол: ufw disable, исправить правило, ufw enable.
  • Сломанная конфигурация SSH: rm /etc/ssh/sshd_config.d/10-kernelhost.conf, затем sshd -t и systemctl restart ssh.
  • Неверный порт после правки сокета: systemctl revert ssh.socket сбрасывает оверлей, затем systemctl daemon-reload и systemctl restart ssh.socket.
  • Блокировка со стороны fail2ban: fail2ban-client set sshd unbanip 203.0.113.10. Чтобы это не повторилось, впишите свой постоянный адрес в ignoreip в файле jail.local.
  • Ключ отклоняется: почти всегда дело в правах. chmod 700 на каталог, chmod 600 на файл, и то и другое должно принадлежать пользователю, а не root.

Итоговая проверка

Команда не вернула ошибку, но это ещё не значит, что она подействовала. Эти шесть проверок показывают фактическое состояние:

  1. sshd -T | grep -E "^(permitrootlogin|passwordauthentication|port) " показывает действующие значения, а не желаемые.
  2. ufw status verbose сообщает Status: active и содержит правило для вашего порта SSH.
  3. timedatectl status сообщает System clock synchronized: yes.
  4. systemctl --failed не выводит ни одной строки.
  5. unattended-upgrade --dry-run --debug отрабатывает без сообщений об ошибках.
  6. Новое SSH-соединение из только что открытого окна проходит, по ключу и без запроса пароля.

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

Что дальше

Пара слов о выборе дистрибутива, потому что он определяет ближайшие годы. Debian 12 с июля 2026 года вышел из обычной поддержки, до середины 2028 года его будет сопровождать команда LTS, с ограниченным набором пакетов. Кто разворачивает систему сегодня, берёт Debian 13 или Ubuntu 24.04 LTS. Ubuntu 22.04 LTS остаётся в стандартной поддержке до 2027 года, но для сервера, которому предстоит работать годами, это уже не первый выбор. Debian 10 и Ubuntu 20.04 окончательно закрыты с июня 2024 и мая 2025 года соответственно, и на новом сервере им не место.

Практическое значение имеют и различия версий в репозиториях. Debian принципиально не поставляет mysql-server, стандартом там является MariaDB. Кому нужна определённая версия PHP, Node или Java, тому лучше заранее проверить, что несёт с собой дистрибутив, чем потом подключать сторонние источники. А если вы всё же подключаете сторонние репозитории: apt-key объявлен устаревшим, ключи кладут в /etc/apt/keyrings/ и ссылаются на них в описании источника через signed-by.

В итоге у вас стоит сервер, который обновлён, сам поддерживает себя в актуальном состоянии, впускает только вас и сообщает, когда что-то не так. Всё остальное, веб-сервер, база данных, сертификаты, строится поверх этого, а не рядом.

Частые вопросы

В каком порядке настраивать новый root-сервер?
Сначала обновить систему, затем создать пользователя с правами sudo и загруженным SSH-ключом, после этого защитить SSH и только потом включать файрвол. Дальше идут часовой пояс, имя хоста, автоматические обновления безопасности, мониторинг и резервное копирование. Важно только одно: пользователь и ключ должны работать до того, как вы отключите вход по паролю, а правило для SSH должно стоять в файрволе до того, как вы его включите.
Почему моё изменение в sshd_config не действует?
В Debian и Ubuntu строка Include /etc/ssh/sshd_config.d/*.conf стоит в самом начале sshd_config, а в конфигурации SSH побеждает первое найденное значение. Поэтому файл вроде 50-cloud-init.conf перебивает и основной файл, и ваш собственный файл с более высоким номером. Проверьте командой sshd -T, какие значения действуют на самом деле, и дайте своему файлу номер поменьше, например 10-kernelhost.conf.
Почему в Ubuntu 24.04 не меняется порт SSH?
Начиная с Ubuntu 22.10 SSH запускается через активацию по сокету. Порт тогда берётся из ssh.socket, а строка Port в конфигурации sshd игнорируется. Нужен оверлей systemd через systemctl edit ssh.socket: сначала пустая строка ListenStream= для сброса значения по умолчанию, а затем нужный вам порт. В Ubuntu 22.04 по-прежнему достаточно строки Port.
Fail2ban не запускается и сообщает, что не нашёл файл журнала для jail sshd. Что делать?
Начиная с версии 12 Debian больше не устанавливает rsyslog, поэтому файла /var/log/auth.log не существует. Задайте в /etc/fail2ban/jail.local в секции [DEFAULT] параметр backend = systemd и установите пакет python3-systemd. После этого fail2ban читает напрямую из журнала systemd. Проверить можно командой fail2ban-client -t и затем fail2ban-client status sshd.
Почему имя хоста после перезагрузки снова пропадает?
На образах с cloud-init, а в Ubuntu это правило, имя хоста задаётся заново при каждом запуске. Создайте файл /etc/cloud/cloud.cfg.d/99_hostname.cfg со строкой preserve_hostname: true, тогда ваша настройка сохранится. Кроме того, добавьте соответствующую запись в /etc/hosts, иначе sudo при каждом вызове будет сообщать, что не может разрешить имя хоста.
Достаточно ли apt upgrade или нужен apt full-upgrade?
На только что выданном сервере full-upgrade предпочтительнее, потому что он ставит и те обновления, ради которых нужно удалить или заменить пакеты. На работающей продуктивной системе сначала посмотрите через apt list --upgradable и пробный прогон, что именно произойдёт, и лишь затем запускайте full-upgrade.

root-сервер Настройка сервера Linux SSH Файрвол Debian Ubuntu Безопасность сервера Чек-лист