Назад в блог/Fail2ban на VPS: закрыть SSH от брутфорса без блокировки себя

Fail2ban на VPS: закрыть SSH от брутфорса без блокировки себя

Как настроить Fail2ban для SSH на Ubuntu VPS: jail sshd, whitelist VPN, разбан себя и типичные ошибки без блокировки собственного доступа.

Fail2ban на VPS: закрыть SSH от брутфорса без блокировки себя

Пока dig/scan идёт по всему интернету, ваш :22 уже в чужом скрипте. Ключи вместо пароля и UFW — база. Fail2ban — следующий слой: он читает логи SSH и временно банит IP, который слишком часто ошибается. Не вместо ключей и firewall, а вместе с ними.

Ниже — рабочая настройка на Ubuntu 22.04/24.04: jail для sshd, whitelist своего VPN, разбан себя одной командой и типичные ошибки, из‑за которых админ сам себе закрывает вход.

О базовой безопасности Ubuntu (ключи, UFW, обновления) мы писали в гайде по настройке VPS. Как зайти на сервер с Windows, macOS и Linux — в статье про SSH. Здесь — только Fail2ban поверх уже живого SSH.

Зачем fail2ban, если уже есть ключи и UFW

UFW режет порты: закрыл лишнее, оставил 22 (или свой порт) и 80/443. Ключи убирают парольный брут в классическом виде — без PasswordAuthentication бот с словарём почти бесполезен.

Но порт SSH всё равно торчит в интернет. Боты долбят его сутками: Invalid user, Failed password, Connection closed. Даже при входе только по ключу это шумит в логах, грузит sshd и иногда маскирует настоящую попытку взлома.

Fail2ban делает простое:

  1. следит за логами (обычно journald или /var/log/auth.log);
  2. считает неудачные попытки с одного IP за окно времени (findtime);
  3. при превышении maxretry добавляет IP в ban на bantime через firewall (nftables/iptables/UFW-цепочку).

Это не IDS и не «полная защита». Это автомат, который отсекает шум и дешёвый брут. Ключи, отключение пароля, смена порта (по желанию) и UFW остаются обязательными. Fail2ban — страховочный слой, когда кто‑то всё же угадал слабый пароль или вы временно включили парольный вход для аварии.

Типичная картина на свежем VPS без fail2ban за сутки: сотни–тысячи строк в auth.log с Invalid user admin, root, ubuntu. С jail’ом через час–два большинство IP уже в бане, а лог читается без боли.

Установка на Ubuntu 22.04 и 24.04

Пакет есть в стандартных репозиториях. На чистой системе:

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

Проверьте версию и что демон жив:

BASH
fail2ban-client version
sudo fail2ban-client ping
# Ответ: pong

Конфиги лежат в /etc/fail2ban/. Править jail.conf напрямую не стоит — его перезапишут при обновлении пакета. Создайте jail.local (или файлы в jail.d/) — они перекрывают дефолты.

Минимальный скелет:

BASH
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# лучше не копировать целиком, а держать короткий jail.local —
# ниже рабочий вариант с нуля

Рекомендуемый подход: короткий /etc/fail2ban/jail.local только с тем, что меняете.

На Ubuntu 22.04/24.04 backend по умолчанию обычно systemd (чтение journal) или auto. Для SSH этого достаточно — отдельно монтировать auth.log не нужно, если rsyslog пишет в journal.

BASH
sudo fail2ban-client get sshd logpath
# или смотрите статус jail после включения

Если после установки jail sshd не активен — это нормально: в дефолтах он часто выключен или с мягкими параметрами. Включаем сами в следующем разделе.

jail.local для sshd: bantime, findtime, maxretry

Создайте файл:

BASH
sudo nano /etc/fail2ban/jail.local

Рабочий стартовый конфиг для VPS с одним SSH:

INI
[DEFAULT]
# бан 1 час; для агрессивных ботов можно 12h или 1d
bantime  = 1h
findtime = 10m
maxretry = 5
# backend = systemd   # раскомментируйте, если auto ведёт себя странно
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled  = true
port     = ssh
filter   = sshd
# maxretry жёстче для sshd, чем глобальный DEFAULT
maxretry = 4
findtime = 10m
bantime  = 2h

Что означают параметры:

  • findtime — окно, в котором считаются провалы. 10m значит: за последние 10 минут.
  • maxretry — сколько провалов с одного IP за окно добана. 4 — разумный баланс: вы успеете опечататься в ключе/пользователе, бот — нет.
  • bantime — сколько держать бан. 2h для sshd обычно хватает; для «вечных» ботов ставят 12h или -1 (перманент — осторожно).
  • ignoreip — белый список (см. следующий раздел).
  • port = ssh — читает порт из /etc/services (22). Если SSH слушает нестандартный порт, укажите число: port = 2222.

После правки:

BASH
sudo fail2ban-client reload
# или полный рестарт:
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Ожидаемый кусок статуса:

TEXT
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     ...
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + ...
`- Actions
   |- Currently banned: N
   |- Total banned:     ...
   `- Banned IP list:   ...

Если sshd нет в списке fail2ban-client status, jail не включён — проверьте enabled = true и синтаксис ini (без лишних кавычек вокруг чисел в новых версиях обычно ок, но единообразие важно).

Ужесточение для публичного VPS, куда ходят только вы с VPN:

INI
[sshd]
enabled  = true
maxretry = 3
findtime = 5m
bantime  = 12h

Не ставьте maxretry = 1 без whitelist: один сбой сети или повторный ввод passphrase к ключу — и вы в бане.

Схема: бот бьёт в :22 → sshd пишет в journal → fail2ban банит IP в firewall
Fail2ban: от логов SSH к бану IP

Whitelist: свой IP и VPN

Самая частая боль — забанить себя. Лечится заранее через ignoreip.

Узнайте свой текущий выход в интернет (с рабочей машины, не с сервера):

BASH
curl -4 ifconfig.me
curl -6 ifconfig.me

Если сидите через домашний динамический IP — лучше поднять простой VPN/WireGuard на отдельном порту или использовать стабильный офисный IP. Динамика и fail2ban плохо дружат: сегодня белый список, завтра провайдер выдал другой адрес.

В jail.local:

INI
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.40 198.51.100.0/24
  • 203.0.113.40 — пример вашего домашнего/офисного IP;
  • 198.51.100.0/24 — пример подсети VPN или офиса;
  • 127.0.0.1/8 и ::1 — локалхост, чтобы не отстрелить себя тестами с сервера.

Несколько адресов — через пробел. После изменения:

BASH
sudo fail2ban-client reload
sudo fail2ban-client get sshd ignoreip

Если вход всегда через WireGuard на сам VPS, можно добавить адрес туннеля (10.0.0.2 и т.п.) в ignoreip — тогда даже при ошибках с «внешнего» интерфейса вы зайдёте по WG и разбаните себя.

Альтернатива whitelist’у на уровне UFW: разрешить SSH только с известных сетей и закрыть 22 для остальных. Тогда fail2ban почти не понадобится для брута с интернета — но это уже жёсткая модель доступа, не для каждого проекта.

Разбан себя: fail2ban-client set sshd unbanip

Забанили? Не паникуйте. Варианты по возрастанию боли.

1. Есть вторая сессия или консоль провайдера (VNC/serial/web-консоль). Зайдите и:

BASH
sudo fail2ban-client set sshd unbanip ВАШ.IP.АДРЕС
sudo fail2ban-client status sshd

Проверка, что IP ушёл из списка banned.

2. Временно остановить jail (если нужно много правок):

BASH
sudo fail2ban-client stop sshd
# ... правите ignoreip, ключи, sshd_config ...
sudo fail2ban-client start sshd

Или остановить весь сервис (откроет уже забаненные до рестарта firewall-правил — зависит от backend; обычно правила снимаются):

BASH
sudo systemctl stop fail2ban
# разбан / правка ignoreip
sudo systemctl start fail2ban

3. Нет консоли — только другое исходящее. Смените IP (мобильный интернет, другой VPN-выход) и зайдите с адреса, которого нет в бане. Сразу добавьте стабильный IP в ignoreip.

4. Посмотреть, кто в бане:

BASH
sudo fail2ban-client status sshd
sudo fail2ban-client get sshd banned

Разбанить пачку:

BASH
sudo fail2ban-client set sshd unbanip 198.51.100.10
sudo fail2ban-client set sshd unbanip 203.0.113.55

Не путайте с UFW: ufw status может не показывать fail2ban-цепочки явно так же, как iptables -L / nft list ruleset. Бан живёт в правилах, которые ставит action fail2ban (iptables-multiport, nftables, ufw — смотрите banaction в конфиге).

BASH
sudo fail2ban-client get sshd banaction

На свежих Ubuntu чаще nftables или iptables-multiport. Главное — разбанивать через fail2ban-client, а не руками вычищать цепочки вслепую.

Логи и проверка, что jail работает

Статус всех jail’ов:

BASH
sudo fail2ban-client status
sudo fail2ban-client status sshd

Логи самого fail2ban:

BASH
sudo journalctl -u fail2ban -n 50 --no-pager
sudo tail -n 50 /var/log/fail2ban.log

Сообщения вида Ban 203.0.113.8 и Unban ... — признак, что цикл «нашёл в логе → забанил» жив.

Сырые неудачи SSH (до бана):

BASH
sudo journalctl -u ssh -n 100 --no-pager
# или на системах с auth.log:
sudo grep -E 'Failed|Invalid user|Connection closed' /var/log/auth.log | tail -n 40

Смотрите, совпадает ли IP в логе sshd с тем, что потом появляется в Banned IP list. Если fail2ban «молчит», а брут идёт:

  • jail sshd выключен;
  • неверный backend / logpath — фильтр не видит строки;
  • SSH на нестандартном порту, а в jail port = ssh (22) — бан может ставиться не на тот порт (зависит от action);
  • кто‑то уже режет трафик до sshd (cloud firewall), и до логов попытки не доходят — тогда fail2ban просто нечего банить.

Тест на стенде (не на проде с единственным доступом): с другой машины несколько раз ошибитесь паролем/пользователем и смотрите status sshd. На проде с ключами безопаснее смотреть реальный шум ботов за сутки — Total banned должен расти.

Логи fail2ban и journal при активном бруте могут раздуваться. Если диск внезапно упрётся в 100%, сначала разберитесь с местом — кто съел диск: логи, Docker, journal — и ограничьте retention journald, иначе мониторинг банов тоже потеряете.

Частые ошибки: только пароль и динамический IP

Оставили парольный вход «на всякий случай»

Fail2ban снижает скорость брута, но не отменяет словарь. Если PasswordAuthentication yes и слабый пароль root/ubuntu — бот всё равно имеет шанс между банами (с разных IP ботнет обходит один banlist).

Сделайте как в базовом гайде:

BASH
sudo nano /etc/ssh/sshd_config
# PasswordAuthentication no
# PermitRootLogin no   # или prohibit-password
sudo sshd -t && sudo systemctl reload ssh

Fail2ban остаётся полезен против Invalid user и редких сбоев, но основной замок — ключи.

Забанили себя с динамического домашнего IP

Симптом: вечером всё ок, утром провайдер сменил адрес, вы не в ignoreip, ошиблись passphrase три раза — бан. Утром с «нового» IP вы уже другой клиент для fail2ban, зато вчерашний белый список бесполезен, а сегодняшний может улететь в бан снова.

Что делать:

  • whitelist стабильного VPN/офиса;
  • поднять WireGuard на VPS и ходить только через него;
  • не ставить maxretry в 2 и bantime в сутки без консоли провайдера;
  • хранить в панели хостинга доступ к web-консоли на случай лока.

Скопировали чужой jail.conf целиком и сломали filter

Длинный jail.local с устаревшими logpath = /var/log/auth.log на системе, где sshd пишет только в journal, даёт «пустой» jail. Держите короткий local-файл и проверяйте status sshd после каждого изменения.

Бан есть, а подключения всё равно идут

Иногда смотрят не тот jail (sshd vs sshd-ddos), или банится IPv4, а клиент приходит по IPv6. Добавьте оба семейства в мониторинг и в ignoreip при необходимости. Проверьте, что sshd слушает оба стека: ss -tlnp | grep sshd.

Связка с firewall, SSH и остальной базой

Порядок, который обычно не ломает прод:

  1. Ключи, отключение пароля, проверка входа со второй сессии — подключение по SSH.
  2. UFW: OpenSSH (или свой порт), 80/443 по необходимости — настройка Ubuntu на VPS.
  3. Fail2ban с ignoreip и умеренным maxretry.
  4. Обновления, отдельный пользователь с sudo, бэкапы.

Fail2ban не заменяет UFW: firewall режет порты целиком, fail2ban точечно банит адреса на уже открытом SSH. Не отключайте UFW «потому что есть fail2ban» — это разные слои.

Если меняете порт SSH — синхронно обновите UFW-правило и port= в jail sshd, иначе либо не зайдёте, либо бан будет вешаться мимо реального порта.

FAQ

Fail2ban обязателен, если вход только по ключу?

Не обязателен, но полезен. Боты всё равно долбят порт; jail снижает шум в логах и отсекает попытки подбора пользователей. Ключи — must-have; fail2ban — дешёвый второй слой.

Чем bantime отличается от findtime?

findtime — окно подсчёта ошибок. bantime — сколько IP сидит в бане после превышения maxretry. Ошибки старше findtime в счётчик уже не входят.

Как не забанить мониторинг и CI?

Добавьте IP раннеров и monitoring-probe в ignoreip, либо вынесите SSH за VPN и закройте 22 снаружи. Иначе health-check с неверным ключом за вечер сам себя вынесет.

fail2ban ест много RAM на маленьком тарифе?

Обычно нет: это лёгкий демон на Python. На минимальных VPS важнее не раздувать journal и не держать десятки тяжёлых jail’ов «на будущее». Один sshd — нормальная нагрузка.

Можно ли банить навсегда?

bantime = -1 в современных версиях даёт перманентный бан до ручного unban. Имеет смысл только при консоли провайдера и стабильном whitelist. Иначе один ошибочный бан своего динамического IP = квест на час.

Почему после reload IP снова в бане?

Reload не всегда чистит уже выданные баны так, как вы ждёте; часть action’ов держит правила до unbanip или истечения bantime. Явно разбаньте: fail2ban-client set sshd unbanip ....

Короткий чеклист перед уходом с сервера

  • PasswordAuthentication no, вход по ключу проверен со второй сессии
  • UFW разрешает SSH, лишнее закрыто
  • /etc/fail2ban/jail.local с [sshd] enabled = true
  • ignoreip содержит VPN/офис/стабильный IP
  • fail2ban-client status sshd показывает jail без ошибок
  • Знаете команду unbanip и где консоль провайдера

Fail2ban настраивается за пятнадцать минут и экономит часы на разбор auth.log. Главное — не выключить себя из сервера: whitelist, умеренный maxretry и запасной вход через консоль.

Если поднимаете новый сервер под проект и не хотите ждать выдачу полдня — на VPS Azerta машина обычно готова до 120 секунд после оплаты, тарифы от 360₽, поддержка 24/7, ДЦ в Москве (Нагатинский) и площадка в Германии. После 3 ТБ трафика действует шейпинг 1 Мбит/с — для SSH и админки этого более чем достаточно, а fail2ban как раз помогает не тратить нервы на чужие сканы в логах.

Нужен быстрый сервер под ваш проект?

Арендуйте производительный KVM VPS в Москве или Германии на скоростных NVMe дисках от 300 ₽/мес. Автовыдача за 60 секунд, выделенный IPv4 и защита от DDoS.

Выбрать тариф