servers-linux

VPS в Европе: покупка и настройка за 20 минут

Покупка учебного VPS, обновления, отдельный пользователь и sudo, независимые SSH-ключи Linux и Android, безопасный запрет root/password и Fail2ban с проверками и восстановлением.

Время
20 минут
Уровень
Начальный уровень
Обновлено
  • VPS
  • Linux
  • SSH
  • Безопасность

Параметры команд

203.0.113.10 — адрес из диапазона документации, не настоящий сервер. Замените его своим IPv4 или IPv6. Поддерживаются также частные адреса для VPN/LAN; доступность не проверяется. Примеры: operator, eu-vps, root. Первый пользователь может быть root или логином провайдера с sudo.

Сайт не выполняет команды. Проверьте контекст LOCAL / SERVER / MOBILE перед запуском. Без JavaScript замените примеры вручную и выделите нужный код для копирования.

Что понадобится

  • Новый VPS без важных данных, публичный IPv4 и доступ к панели провайдера, независимой web/VNC-консоли и rescue-режиму.
  • Первоначальный пользователь и способ входа из панели: root с паролем либо административный пользователь с sudo и уже зарегистрированным SSH-ключом.
  • Компьютер с OpenSSH: Терминал macOS/Linux или Ubuntu в WSL на Windows. Команды LOCAL рассчитаны на sh/bash, не PowerShell.
  • Телефон с Termux на Android либо SSH-клиентом с генерацией Ed25519 и экспортом публичного ключа; менеджер паролей для паролей и passphrase.

Что получится

  • Обновлённая Linux-система с понятным hostname и проверенным возвращением после перезагрузки.
  • Отдельный пользователь с sudo; независимые ключи компьютера и телефона проверены до запрета паролей.
  • SSH принимает ключи, но не root, пароль или keyboard-interactive; эффективная конфигурация проверена для обоих подключений.
  • Fail2ban читает журнал systemd; сохранена резервная копия SSH и есть команды восстановления из консоли.

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

15–20 минут — ориентир для активных действий, не гарантия. Покупка, обновления на 10 минут и дольше, настройка телефона и перезагрузки могут увеличить время. Инструкция рассчитана на Debian 12/13 и Ubuntu 24.04/26.04; сверена с документацией, но не выдаётся за испытание на реальном VPS каждой версии.

Укажите свой IP, имя нового пользователя, hostname и первоначального администратора в настройках команд. Примеры по умолчанию: 203.0.113.10, operator, eu-vps, root. Примерный IP не является вашим сервером. Имена пользователя и hostname — строчные латинские буквы, цифры и дефис, начинаются с буквы; новый пользователь должен отличаться от первоначального.

1. Купить VPS в Европе

  • Выберите европейский дата-центр с приемлемой задержкой и правилами использования. Для учебного сервера ориентир — KVM, 1–2 vCPU, 1–2 ГБ RAM и 20–30 ГБ SSD; для тяжёлых приложений ресурсов потребуется больше.
  • Проверьте полную цену с налогами, отдельную стоимость IPv4 и backups, лимит трафика, автопродление и способ окончательного удаления сервера. Выключенный VPS может продолжать тарифицироваться — это зависит от договора.
  • Нужны web/VNC-консоль и rescue-режим, доступные без SSH. Проверьте способ входа в консоль, восстановления пароля и подключения диска в rescue до запрета root по SSH.
  • Установите чистый Debian 12/13 либо Ubuntu 24.04/26.04 LTS без панели управления. Сохраните IP, первоначальный логин, пароль либо путь к приватному ключу, который соответствует публичному ключу в панели.
  • Включите 2FA для аккаунта провайдера. Уточните расписание, хранение и восстановление backups; snapshot перед изменениями полезен, но не заменяет отдельную резервную копию данных.

Конкретный провайдер, цена и партнёрская ссылка не нужны для этих шагов. Покупайте по своим требованиям; условия и доступность образов проверяйте в панели перед оплатой.

2. Проверить сервер и войти впервые

Сначала откройте независимую консоль в панели и войдите локально. Для root выполните следующий блок; для администратора с sudo добавьте sudo перед ssh-keygen. Запишите SHA256-отпечаток ключа хоста. Он удостоверяет сервер, а не ваш пользовательский ключ.[2]

В независимой консоли провайдера: отпечаток Ed25519 сервера

SERVER — SSH-сессия на сервереКоманда
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub -E sha256

На компьютере выберите одну ветку. Парольная: используйте пароль первоначального пользователя из панели. Ключевая: введите полный путь к уже зарегистрированному приватному ключу провайдера; путь не отправляется на сервер. Passphrase этого ключа и пароль сервера — разные вещи.

Ветка A: первоначальный вход по паролю

LOCAL — терминал вашего компьютераКоманда
ssh -o HostKeyAlgorithms=ssh-ed25519 root@203.0.113.10

Ветка B: первоначальный вход по существующему ключу

LOCAL — терминал вашего компьютераКоманда
printf "Полный путь к существующему ключу провайдера: "
IFS= read -r provider_key
ssh -i "$provider_key" -o IdentitiesOnly=yes -o HostKeyAlgorithms=ssh-ed25519 root@203.0.113.10

Если вошли как root, оставайтесь в этой сессии. Если вошли как ubuntu, debian или другой администратор с sudo, выполните sudo -i: дальше до конца шага 5 команды выполняются именно в полученной root-сессии. Если sudo недоступен, используйте root-консоль провайдера; не продолжайте без административного доступа.

Только для первоначального пользователя с sudo: перейти в root-сессию

SERVER — SSH-сессия на сервереКоманда
sudo -i

В root-сессии: проверить права и установленную систему

SERVER — SSH-сессия на сервереКоманда
id -u
cat /etc/os-release
uname -r

id -u должен вывести 0, а os-release — выбранную систему. Оставьте эту сессию открытой.

3. Задать имя сервера

Root-сессия: установить и прочитать hostname

SERVER — SSH-сессия на сервереКоманда
hostnamectl set-hostname eu-vps
hostnamectl --static
cat /etc/hosts

Проверьте, что новое имя выводится правильно. Если /etc/hosts содержит строку 127.0.1.1 со старым hostname, замените только его на новое имя, сохранив остальные записи. Следующий блок делает это для короткого имени; если строки нет, добавляет её. Он не меняет DNS и публичный IP.

Root-сессия: согласовать локальное имя в /etc/hosts

SERVER — SSH-сессия на сервереКоманда
python3 - <<'PY'
from pathlib import Path
p = Path("/etc/hosts")
lines = p.read_text().splitlines()
found = False
for i, line in enumerate(lines):
    fields = line.split()
    if fields and fields[0] == "127.0.1.1":
        lines[i] = "127.0.1.1 eu-vps"
        found = True
if not found:
    lines.append("127.0.1.1 eu-vps")
p.write_text("\n".join(lines) + "\n")
PY
getent hosts eu-vps

4. Обновить систему и проверить перезагрузку

apt update обновляет индекс, full-upgrade обновляет пакеты и при необходимости может удалить установленные пакеты. Читайте предлагаемый список до подтверждения: удаление openssh-server, sudo или критичных компонентов — повод остановиться. Это не переход Debian 12 → 13 или Ubuntu 24.04 → 26.04: источники пакетов не меняем.[7][8]

Root-сессия: обновления, затем минимальные административные инструменты

SERVER — SSH-сессия на сервереКоманда
apt update && apt full-upgrade
# Следующую строку выполняйте только после успешного обновления.
apt install sudo adduser python3 needrestart

Не используем -y: подтверждайте изменения осознанно. Если пакеты спрашивают про изменённый sshd_config, сохраните текущую рабочую конфигурацию и изучите diff. При занятой блокировке APT дождитесь завершения другого обновления; не удаляйте lock-файлы и не убивайте dpkg.[10]

Root-сессия: проверить пакеты и необходимость рестартов

SERVER — SSH-сессия на сервереКоманда
dpkg --audit
needrestart -r l
if [ -f /var/run/reboot-required ]; then
  cat /var/run/reboot-required
  if [ -f /var/run/reboot-required.pkgs ]; then cat /var/run/reboot-required.pkgs; fi
fi
uname -r

dpkg --audit не должен сообщать о незавершённых пакетах. needrestart -r l только перечисляет устаревшие процессы и проверяет ядро, не перезапускает службы.[11] Ubuntu может сигнализировать о перезагрузке через reboot-required; на Debian не считайте отсутствие этого файла доказательством, что перезагрузка не нужна. Для нового пустого VPS после успешного обновления здесь делаем одну плановую перезагрузку, чтобы проверить загрузку и доступ.

Root-сессия: только после завершения APT и с открытой консолью

SERVER — SSH-сессия на сервереКоманда
systemctl reboot

Разрыв SSH ожидаем. Дождитесь загрузки в панели и повторите свою команду первоначального входа из шага 2; отпечаток хоста не должен измениться. Снова получите root через sudo -i, если нужно, и выполните проверки. Если сервер не вернулся — переходите к восстановлению, не настраивайте защиту вслепую.

После повторного входа в root: проверить результат загрузки

SERVER — SSH-сессия на сервереКоманда
hostnamectl --static
uname -r
uptime
systemctl is-active ssh.service
systemctl --failed
needrestart -r l

Для ssh.service ожидается active. На Ubuntu может использоваться ssh.socket: это не повод менять socket activation или порт. Если служба не active, проверьте systemctl status ssh.service ssh.socket --no-pager и journalctl -u ssh.service -b --no-pager. Разберите failed units и предупреждения needrestart до следующего шага.

5. Создать отдельного пользователя с sudo

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

Root-сессия: создать только отсутствующего пользователя

SERVER — SSH-сессия на сервереКоманда
if getent passwd operator; then
  printf "Логин уже занят — остановитесь и выберите другой.\n"
else
  adduser operator && usermod -aG sudo operator
fi
id operator
sudo -l -U operator

В id должна присутствовать группа sudo, а sudo -l -U — разрешение выполнять административные команды. Это лишь проверка настройки: реальный sudo в новых SSH-сессиях проверим отдельно. Исходного администратора пока не удаляем.

6. Создать отдельный Ed25519-ключ на компьютере

В новом локальном терминале создайте ключ именно для этого VPS с именем id_ed25519_vps. Ed25519 рекомендован Ubuntu; ssh-keygen интерактивно запросит passphrase — задайте длинную непустую фразу.[3] Это защита приватного ключа на компьютере, не пароль Linux-пользователя.

Проверка существующих файлов без перезаписи и генерация нового ключа

LOCAL — терминал вашего компьютераКоманда
mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
key="$HOME/.ssh/id_ed25519_vps"
if [ -e "$key" ] || [ -L "$key" ] || [ -e "$key.pub" ] || [ -L "$key.pub" ]; then
  printf "Ключ или .pub уже существует; не перезаписываем. Проверьте, что это нужная пара.\n"
else
  ssh-keygen -t ed25519 -a 64 -f "$key" -C "vps-desktop"
fi
ssh-keygen -lf "$HOME/.ssh/id_ed25519_vps.pub" -E sha256
cat "$HOME/.ssh/id_ed25519_vps.pub"

Если оба файла уже существуют и это ваша известная пара, используйте её; не соглашайтесь на overwrite. Если остался только приватный файл, восстановите .pub следующим блоком. Если остался только .pub, приватный ключ из него восстановить нельзя: остановитесь и разберитесь с файлами, не удаляя их.

Только если приватный ключ есть, а .pub отсутствует: восстановить публичный

LOCAL — терминал вашего компьютераКоманда
if [ -f "$HOME/.ssh/id_ed25519_vps" ] && [ ! -e "$HOME/.ssh/id_ed25519_vps.pub" ] && [ ! -L "$HOME/.ssh/id_ed25519_vps.pub" ]; then
  (umask 077; set -C; ssh-keygen -y -f "$HOME/.ssh/id_ed25519_vps" > "$HOME/.ssh/id_ed25519_vps.pub")
fi

7. Установить ключ и проверить вход с компьютера

Выберите один способ установки. A: если сервер уже разрешает парольный SSH-вход нового пользователя, ssh-copy-id добавит только указанный .pub; введите пароль нового пользователя, не root.[5] Если ssh-copy-id не установлен или провайдер запрещает пароли, используйте B — не включайте пароли ради этой команды.

Способ A: при уже разрешённом парольном SSH-входе

LOCAL — терминал вашего компьютераКоманда
ssh-copy-id -i "$HOME/.ssh/id_ed25519_vps.pub" operator@203.0.113.10

Способ B: скопируйте одну полную строку из .pub. В исходной root-сессии администратора выполните блок ниже и вставьте её на запрос. Если вы остались в обычной sudo-сессии первоначального администратора, сначала выполните sudo -i. install здесь создаёт каталог с владельцем нового пользователя; authorized_keys дополняется, а не заменяется. Права 700/600 и отсутствие записи для группы/остальных нужны для безопасного использования ключей.[2]

Способ B: установка публичного ключа через исходного администратора, root-сессия

SERVER — SSH-сессия на сервереКоманда
(
  set -e
  home_dir=$(getent passwd operator | cut -d: -f6)
  user_group=$(id -gn operator)
  test -n "$home_dir" && test -d "$home_dir"
  printf "Вставьте одну полную строку публичного Ed25519-ключа: "
  IFS= read -r public_key
  case "$public_key" in
    "ssh-ed25519 "*) ;;
    *) printf "Это не публичный Ed25519-ключ — остановка.\n"; exit 1 ;;
  esac
  test ! -L "$home_dir/.ssh"
  test ! -L "$home_dir/.ssh/authorized_keys"
  install -d -m 700 -o operator -g "$user_group" "$home_dir/.ssh"
  touch "$home_dir/.ssh/authorized_keys"
  printf "\n%s\n" "$public_key" >> "$home_dir/.ssh/authorized_keys"
  chown operator:"$user_group" "$home_dir/.ssh/authorized_keys"
  chmod 600 "$home_dir/.ssh/authorized_keys"
  chmod go-w "$home_dir"
)

Теперь обязательно откройте отдельное новое соединение с указанным приватным ключом. -F /dev/null исключает неожиданные настройки клиента и multiplexing, IdentitiesOnly выбирает указанную идентичность, IdentityAgent=none исключает агент; PreferredAuthentications и оба запрета не дают незаметно перейти на пароль.[9] Passphrase локального ключа по-прежнему можно вводить.

Обязательный новый вход только по ключу компьютера

LOCAL — терминал вашего компьютераКоманда
ssh -F /dev/null -i "$HOME/.ssh/id_ed25519_vps" -o IdentitiesOnly=yes -o IdentityAgent=none -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no operator@203.0.113.10

В новой сессии пользователя с компьютера: настоящий sudo и права ключей

SERVER — SSH-сессия на сервереКоманда
whoami
id
sudo -k
sudo -v && sudo whoami
stat -c "%U %a %n" "$HOME" "$HOME/.ssh" "$HOME/.ssh/authorized_keys"
printf "%s\n" "$SSH_CONNECTION"

whoami должен показать нового пользователя, sudo whoami — root. Если sudo требует пароль, это пароль нового пользователя, не passphrase ключа; NOPASSWD в некоторых образах может не спрашивать его. У .ssh и authorized_keys ожидаются владелец — новый пользователь и права 700/600. Сохраните эту сессию.

8. Подключить телефон отдельным ключом

Android: установите Termux из источника, указанного в официальном README (например, F-Droid), не смешивайте приложение и плагины из разных источников.[14] Пакет openssh включает SSH и ssh-copy-id; SSH-сервер на самом телефоне запускать не требуется.[13]

Android / Termux: установить OpenSSH и создать неперезаписываемый отдельный ключ

MOBILE — терминал на телефонеКоманда
pkg update && pkg install openssh
# Далее — только после успешной установки.
mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
key="$HOME/.ssh/id_ed25519_vps_phone"
if [ -e "$key" ] || [ -L "$key" ] || [ -e "$key.pub" ] || [ -L "$key.pub" ]; then
  printf "Ключ телефона или .pub уже существует — не перезаписываем.\n"
else
  ssh-keygen -t ed25519 -a 64 -f "$key" -C "vps-phone"
fi
ssh-keygen -lf "$HOME/.ssh/id_ed25519_vps_phone.pub" -E sha256
cat "$HOME/.ssh/id_ed25519_vps_phone.pub"

Задайте непустую passphrase. При существующей известной паре используйте её; только приватный файл позволяет восстановить .pub следующим блоком. Только .pub без приватного — остановка, как на компьютере.

Termux: только восстановление отсутствующего .pub из своего приватного ключа

MOBILE — терминал на телефонеКоманда
if [ -f "$HOME/.ssh/id_ed25519_vps_phone" ] && [ ! -e "$HOME/.ssh/id_ed25519_vps_phone.pub" ] && [ ! -L "$HOME/.ssh/id_ed25519_vps_phone.pub" ]; then
  (umask 077; set -C; ssh-keygen -y -f "$HOME/.ssh/id_ed25519_vps_phone" > "$HOME/.ssh/id_ed25519_vps_phone.pub")
fi

Если парольный SSH уже разрешён, выполните ssh-copy-id ниже. Иначе скопируйте только строку публичного ключа телефона и повторите способ B из шага 7 в исходной root-сессии, вставляя новую строку. Первый fingerprint на телефоне сравните с той же независимой консолью, а не только с компьютером.

Termux: только при разрешённом парольном входе установить .pub

MOBILE — терминал на телефонеКоманда
ssh-copy-id -i "$HOME/.ssh/id_ed25519_vps_phone.pub" operator@203.0.113.10

Termux: новое соединение только по ключу телефона

MOBILE — терминал на телефонеКоманда
ssh -F /dev/null -i "$HOME/.ssh/id_ed25519_vps_phone" -o IdentitiesOnly=yes -o IdentityAgent=none -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no operator@203.0.113.10

В сессии нового пользователя с телефона: проверить sudo и сохранить контекст

SERVER — SSH-сессия на сервереКоманда
whoami
sudo -k
sudo -v && sudo whoami
printf "%s\n" "$SSH_CONNECTION"

9. Запретить root и парольный SSH-вход

Теперь работаем в новой sudo-сессии с компьютера, без sudo -i. У OpenSSH для большинства директив действует первое прочитанное значение; Include раскрывает файлы лексикографически, а не по принципу «последний победил».[1]

Debian и Ubuntu обычно подключают sshd_config.d в начале основного файла.[12][15][3] Поэтому 00-vps-first-20-minutes.conf обычно прочитается раньше 50-cloud-init.conf, но более ранний файл или параметры запуска службы могут изменить результат.

Новый пользователь с sudo: посмотреть реальные Include, Match и параметры службы

SERVER — SSH-сессия на сервереКоманда
sudo cat /etc/ssh/sshd_config
sudo find /etc/ssh/sshd_config.d -maxdepth 1 -name "*.conf" -type f -print -exec cat {} \;
sudo systemctl cat ssh.service
sudo cat /etc/default/ssh

Include должен действовать в глобальном контексте до нужных директив, не внутри Match. Match может отдельно разрешить пароли/root для определённых пользователей или адресов; глобального sshd -T недостаточно.[1][2] Следующий блок делает root-only копию всего /etc/ssh в стабильном месте, создаёт только новый 00-файл и добавляет явный Include этого файла первой строкой основного конфига. Это гарантирует глобальное чтение наших директив до cloud-init и других Include; существующие файлы провайдера не редактируются. Даже после этого Match обязательно проверяется.

Один раз: backup и отдельный глобальный конфиг без перезаписи

SERVER — SSH-сессия на сервереКоманда
sudo python3 - <<'PY'
from pathlib import Path
import os
import shutil
backup = Path("/root/vps-first-20-minutes-backup")
custom = Path("/etc/ssh/sshd_config.d/00-vps-first-20-minutes.conf")
main = Path("/etc/ssh/sshd_config")
if os.path.lexists(backup) or os.path.lexists(custom):
    raise SystemExit("STOP: backup или 00-файл уже существует; ничего не перезаписываем")
if main.is_symlink():
    raise SystemExit("STOP: основной конфиг — symlink; нужен отдельный разбор")
original = main.read_bytes()
backup.mkdir(mode=0o700)
shutil.copytree("/etc/ssh", backup / "ssh", symlinks=True)
custom.parent.mkdir(mode=0o755, exist_ok=True)
with custom.open("x") as f:
    f.write("PubkeyAuthentication yes\nPermitRootLogin no\nPasswordAuthentication no\nKbdInteractiveAuthentication no\nAuthenticationMethods publickey\n")
custom.chmod(0o644)
include = b"Include /etc/ssh/sshd_config.d/00-vps-first-20-minutes.conf\n"
main.write_bytes(include + original)
print("Backup:", backup / "ssh")
print("Created:", custom)
PY

PubkeyAuthentication оставляет ключи; PermitRootLogin no запрещает root даже с ключом; PasswordAuthentication no и KbdInteractiveAuthentication no закрывают оба парольных пути; AuthenticationMethods publickey требует ключ.[1] Не меняем UsePAM: он используется также для учётных записей и сессий. Формат этих директив проверен в manpages Ubuntu 24.04 и 26.04.[17][18]

Выполните следующий блок отдельно в обеих уже открытых новых сессиях — с компьютера и телефона. SSH_CONNECTION содержит реальный адрес клиента, его порт, адрес сервера и порт сервера. host в -C — имя клиента, не hostname VPS; при UseDNS no это адрес клиента. Если конфигурация использует UseDNS yes и Match Host, сначала определите подтверждённое прямым/обратным DNS имя клиента и замените client_host; без этого не продолжайте. Контекстный образец: user=operator,host=198.51.100.25,addr=198.51.100.25 — это только пример, не результат проверки.[2]

В каждой новой сессии: sshd -t и реальные контекстные значения для пользователя и root

SERVER — SSH-сессия на сервереКоманда
(
  set -e
  test -n "$SSH_CONNECTION"
  set -- $SSH_CONNECTION
  test "$#" -eq 4
  client_addr=$1
  server_addr=$3
  server_port=$4
  client_host=$client_addr
  sudo /usr/sbin/sshd -t
  for checked_user in operator root; do
    printf "Контекст user=%s host=%s addr=%s laddr=%s lport=%s\n" "$checked_user" "$client_host" "$client_addr" "$server_addr" "$server_port"
    effective=$(sudo /usr/sbin/sshd -t && sudo /usr/sbin/sshd -T -C "user=$checked_user,host=$client_host,addr=$client_addr,laddr=$server_addr,lport=$server_port")
    printf "%s\n" "$effective" | grep -E "^(pubkeyauthentication|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|authenticationmethods|usedns) "
    for expected in "pubkeyauthentication yes" "permitrootlogin no" "passwordauthentication no" "kbdinteractiveauthentication no" "authenticationmethods publickey"; do
      printf "%s\n" "$effective" | grep -Fx "$expected" >/dev/null || { printf "STOP: не получено %s\n" "$expected"; exit 1; }
    done
  done
)

Только после обеих контекстных проверок: применить без закрытия старых сессий

SERVER — SSH-сессия на сервереКоманда
sudo /usr/sbin/sshd -t && sudo systemctl reload ssh.service
sudo systemctl is-active ssh.service

После reload ещё раз выполните команды нового key-only входа из шагов 7 и 8 и sudo whoami на обоих устройствах. Не используйте для доказательства старое соединение. Если новый вход не работает, сохранённая сессия или консоль позволит восстановить конфигурацию. Reload обновляет демон для новых подключений; открытые сессии держите до конца.[2]

10. Включить Fail2ban для SSH

Fail2ban — дополнительный слой против повторных неудачных входов, не замена ключам и обновлениям. Настройки сохраняем в jail.d/*.local, а не в пакетном jail.conf.[4] backend=systemd читает journal и требует Python-модуль systemd; logpath для этого backend не задаём.[6]

Новый пользователь с sudo: установить Fail2ban и поддержку journal

SERVER — SSH-сессия на сервереКоманда
sudo apt update && sudo apt install fail2ban python3-systemd
sudo python3 -c "from systemd import journal; print('systemd journal module: OK')"

Создать только отсутствующий jail-файл: 5 ошибок за 10 минут, бан на 1 час

SERVER — SSH-сессия на сервереКоманда
sudo python3 - <<'PY'
from pathlib import Path
p = Path("/etc/fail2ban/jail.d/99-vps-first-20-minutes.local")
with p.open("x") as f:
    f.write("[sshd]\nenabled = true\nbackend = systemd\nport = ssh\nmaxretry = 5\nfindtime = 10m\nbantime = 1h\n")
p.chmod(0o644)
print("Created:", p)
PY

Если файл существует, не перезаписывайте его: изучите содержимое. В отличие от OpenSSH, у Fail2ban поздние .local могут переопределять ранние; итог проверяем клиентом.[4] port=ssh здесь означает стандартный SSH-порт 22, а не его смену. Общую политику firewall не настраиваем, но механизм банов Fail2ban сам использует правила firewall.[16]

Проверить конфигурацию, включить службу и прочитать фактический jail

SERVER — SSH-сессия на сервереКоманда
sudo fail2ban-client -t && sudo systemctl enable --now fail2ban
# Далее — только если тест и запуск успешны.
sudo fail2ban-client reload
sudo systemctl is-active fail2ban
sudo fail2ban-client ping
sudo fail2ban-client status sshd
sudo fail2ban-client get sshd maxretry
sudo fail2ban-client get sshd findtime
sudo fail2ban-client get sshd bantime
sudo fail2ban-client get sshd journalmatch

Ожидаются active, Server replied: pong, существующий jail sshd, maxretry=5, findtime=600 и bantime=3600 секунд; status для systemd должен показывать Journal matches. Команды test/reload/ping/status описаны upstream.[19] Нулевое число банов нормально и не доказывает работу реального бана. Не проверяйте блокировку перебором паролей со своего единственного адреса.

Если служба или jail не работают: диагностика без изменения firewall

SERVER — SSH-сессия на сервереКоманда
sudo systemctl status fail2ban --no-pager
sudo journalctl -u fail2ban -b -n 80 --no-pager
sudo journalctl -u ssh.service -b -n 40 --no-pager
sudo fail2ban-client -d

При ошибке запуска не считайте защиту включённой. Проверяйте наличие python3-systemd, конфликты других jail-файлов и ошибки выбранного пакетного ban action. Отсутствие /var/log/auth.log само по себе не ошибка при systemd backend. В минимальных образах состав пакетов и правила провайдера могут отличаться.

Финальная проверка

  • После reload и запуска Fail2ban заново войдите с компьютера и телефона командами key-only из шагов 7–8. В обеих новых сессиях выполните whoami и sudo -k; sudo -v && sudo whoami. Пользователь — новый логин, sudo — root.
  • В каждой новой сессии повторите контекстный блок sshd -t/-T из шага 9. Он должен подтвердить все пять значений для нового пользователя и root с реальными адресами обоих устройств.
  • hostnamectl --static показывает ваше имя; dpkg --audit не сообщает о незавершённой настройке; ssh.service и fail2ban — active; jail sshd существует, параметры совпадают, журнал читается.
  • Проверьте независимую консоль ещё раз. Сохраните путь /root/vps-first-20-minutes-backup/ssh, passphrase обоих ключей, пароль sudo и способ доступа к rescue. Backup содержит приватные ключи хоста — не публикуйте его.
  • Только после всех успешных проверок закройте исходную административную сессию. Не удаляйте bootstrap-пользователя провайдера в рамках этой инструкции; отдельные ключи можно отозвать позже после проверки резервного доступа.

Новая sudo-сессия: итоговая диагностика

SERVER — SSH-сессия на сервереКоманда
hostnamectl --static
sudo dpkg --audit
sudo systemctl is-active ssh.service fail2ban
sudo fail2ban-client status sshd
sudo journalctl -u ssh.service -u fail2ban -b -n 60 --no-pager

Необязательная отрицательная проверка с компьютера: следующий единственный запрос должен получить Permission denied без приглашения пароля. Он исключает ключи и keyboard-interactive и проверяет запрет SSH-пароля. Сам отказ не заменяет успешные входы и sshd -T: он мог бы быть вызван другой причиной. Не повторяйте его циклом — это не тест бана.

Один запрос без ключей: парольная аутентификация должна быть отвергнута

LOCAL — терминал вашего компьютераКоманда
ssh -F /dev/null -o PubkeyAuthentication=no -o PreferredAuthentications=password -o PasswordAuthentication=yes -o KbdInteractiveAuthentication=no -o BatchMode=yes operator@203.0.113.10

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

Если доступ пропал: восстановление

Не переустанавливайте VPS и не закрывайте рабочую сессию. Используйте оставшуюся sudo/root-сессию либо независимую консоль панели. Запрет PermitRootLogin относится к SSH, не к локальному входу в консоль. Для администратора с sudo выполните sudo -i; следующие команды восстановления — в root-сессии.

Откат ниже предназначен именно для изменений этой статьи: переносит наш единственный 00-файл за пределы Include и возвращает основной sshd_config из конкретного backup. Другие текущие snippets и ключи хоста не удаляет и не перезаписывает. Если после backup вы отдельно меняли другие файлы, сравните их с /root/vps-first-20-minutes-backup/ssh перед восстановлением; не заменяйте целиком /etc/ssh вслепую.

Root в сохранённой сессии или консоли: точечный откат SSH

SERVER — SSH-сессия на сервереКоманда
(
  set -e
  backup=/root/vps-first-20-minutes-backup
  custom=/etc/ssh/sshd_config.d/00-vps-first-20-minutes.conf
  disabled="$backup/00-vps-first-20-minutes.conf.disabled"
  test -f "$backup/ssh/sshd_config"
  test ! -L /etc/ssh/sshd_config
  if [ -e "$custom" ] || [ -L "$custom" ]; then
    test ! -e "$disabled" && test ! -L "$disabled"
    mv "$custom" "$disabled"
  fi
  cp -a "$backup/ssh/sshd_config" /etc/ssh/sshd_config
  /usr/sbin/sshd -t
  if systemctl is-active --quiet ssh.service; then
    systemctl reload ssh.service
  else
    systemctl start ssh.service
  fi
  systemctl status ssh.service --no-pager
)

Теперь повторите первоначальный способ входа из шага 2. Этот откат возвращает прежнюю политику аутентификации — она может снова разрешать root/пароли. Исправьте причину, заново проверьте оба ключа и sudo, затем повторите проверку эффективной политики. Не удаляйте backup ради быстрого перезапуска шага 9.

Если причина — бан Fail2ban, в консоли задайте реальный внешний IP заблокированного устройства (из сохранённого SSH_CONNECTION или списка ban, не IP VPS) и снимите только его блокировку.[19]

Root в консоли: прочитать бан и снять его с одного адреса

SERVER — SSH-сессия на сервереКоманда
fail2ban-client status sshd
printf "Внешний IP заблокированного клиента: "
IFS= read -r client_ip
fail2ban-client set sshd unbanip "$client_ip"

Root в консоли: только если Fail2ban не отвечает или блокирует восстановление

SERVER — SSH-сессия на сервереКоманда
systemctl stop fail2ban
journalctl -u fail2ban -b -n 80 --no-pager

Остановка временно убирает защиту Fail2ban. После исправления выполните fail2ban-client -t && systemctl start fail2ban, затем ping и status sshd из шага 10 и вновь проверьте оба входа. Не открывайте все порты и не очищайте firewall целиком.

Если система не загружается или консоль не позволяет войти, используйте rescue и инструкции именно вашего провайдера: имя диска и точка монтирования зависят от образа, универсальную опасную команду здесь не подставляем. На подключённом корневом диске backup расположен в /root/vps-first-20-minutes-backup/ssh; восстановите только sshd_config и уберите наш 00-файл, затем загрузитесь с обычного диска. При сбое диска/загрузчика восстановите проверенный snapshot/backup либо обратитесь к поддержке.

Только после независимой проверки нового fingerprint: удалить старую запись

LOCAL — терминал вашего компьютераКоманда
ssh-keygen -R 203.0.113.10

Источники

[1] OpenSSH: sshd_config(5): https://man.openbsd.org/sshd_config

[2] OpenSSH: sshd(8), проверка конфигурации и ключи: https://man.openbsd.org/sshd

[3] Ubuntu Server: OpenSSH server: https://documentation.ubuntu.com/server/how-to/security/openssh-server

[4] Debian 12 / Fail2ban: jail.conf(5): https://manpages.debian.org/bookworm/fail2ban/jail.conf.5.en.html

[5] Debian 12: ssh-copy-id(1): https://manpages.debian.org/bookworm/openssh-client/ssh-copy-id.1.en.html

[6] Fail2ban upstream: jail.conf, systemd backend: https://raw.githubusercontent.com/fail2ban/fail2ban/master/config/jail.conf

[7] Debian 13: apt(8): https://manpages.debian.org/trixie/apt/apt.8.en.html

[8] Ubuntu Server: управление пакетами: https://documentation.ubuntu.com/server/how-to/software/package-management

[9] OpenSSH: ssh_config(5), выбор идентичности: https://man.openbsd.org/ssh_config

[10] Ubuntu Server: automatic updates: https://documentation.ubuntu.com/server/how-to/software/automatic-updates

[11] Debian 12: needrestart(1): https://manpages.debian.org/bookworm/needrestart/needrestart.1.en.html

[12] Debian 13: sshd_config(5): https://manpages.debian.org/trixie/openssh-server/sshd_config.5.en.html

[13] Termux upstream: пакет openssh: https://raw.githubusercontent.com/termux/termux-packages/master/packages/openssh/build.sh

[14] Termux: официальная установка: https://github.com/termux/termux-app

[15] Debian 12: sshd_config(5): https://manpages.debian.org/bookworm/openssh-server/sshd_config.5.en.html

[16] Debian 12: fail2ban-client(1): https://manpages.debian.org/bookworm/fail2ban/fail2ban-client.1.en.html

[17] Ubuntu 24.04 LTS: sshd_config(5): https://manpages.ubuntu.com/manpages/noble/en/man5/sshd_config.5.html

[18] Ubuntu 26.04 LTS: sshd_config(5): https://manpages.ubuntu.com/manpages/resolute/man5/sshd_config.5.html

[19] Fail2ban upstream: fail2ban-client(1): https://raw.githubusercontent.com/fail2ban/fail2ban/master/man/fail2ban-client.1

← Все материалы KNOWLEDGE