Fail2ban ставят первым и на этом обычно останавливаются: пакет установлен, служба запущена, sshd вроде бы прикрыт. Через полгода выясняется, что джейл читает файл, которого на этой системе нет, а всё остальное — почта, панель, форма входа на сайте — не прикрывалось никогда.
Разберём, что стоит включить на обычном VPS с сайтом, какие числа туда ставить и как убедиться, что джейл работает, а не просто числится в конфиге.
Сначала проверьте, что sshd вообще ловит
Команда одна:
sudo fail2ban-client status sshd
В выводе есть строки Currently failed, Total failed и Total banned. Если Total failed равен нулю на сервере, который стоит в интернете хотя бы сутки, — джейл не работает. Сверьтесь с реальностью:
sudo lastb | wc -l
Тысячи неудачных попыток в lastb при нулях в Fail2ban означают ровно одно: фильтр смотрит не туда.
Самая частая причина — Debian 12. В нём rsyslog больше не ставится по умолчанию, файла /var/log/auth.log просто нет, а джейл sshd из коробки настроен на чтение файла. Сообщения об ошибке при этом не будет: служба запустится, статус покажется, счётчики останутся по нулям. Лечится переключением на журнал systemd:
[sshd]
enabled = true
backend = systemd
Второй вариант — вернуть rsyslog пакетом, если текстовый auth.log нужен другим инструментам. Ubuntu 24.04 ведёт себя так же.
Где писать настройки
/etc/fail2ban/jail.conf не трогаем: он перезаписывается при обновлении пакета, и все правки оттуда однажды тихо исчезнут. Своё идёт в /etc/fail2ban/jail.local — этот файл читается последним и перекрывает общий.
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.25
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
ignoreip со своим адресом — не роскошь, а страховка: заблокировать себя опечаткой в пароле проще, чем кажется. Если статического адреса нет, держите запасной вход помимо SSH — консоль или VNC у хостера.
Джейл, который меняет картину: recidive
Обычный джейл живёт короткой памятью: пять попыток за десять минут — час бана, и через час всё сначала. Бот с этим прекрасно уживается, он вернётся и завтра, и послезавтра. recidive закрывает именно этот зазор: он читает не системный лог, а собственный лог Fail2ban, то есть банит тех, кого Fail2ban уже банил.
[recidive]
enabled = true
bantime = 1w
findtime = 1d
maxretry = 5
Читается это так: «попал в бан пять раз за сутки — уходишь на неделю». Джейлу нужен файл /var/log/fail2ban.log, поэтому если вы перевели логирование Fail2ban в syslog, для recidive тоже понадобится backend = systemd.
Практический эффект от одного этого джейла обычно заметнее, чем от тонкой настройки всех остальных: постоянные посетители отваливаются, и в логах остаётся только свежий фон.
Что ещё включить, если на сервере сайт
Порядок по убыванию пользы:
nginx-http-authилиapache-auth— перебор basic-авторизации. Нужен, если админка или staging закрыты паролем веб-сервера;nginx-botsearch— сканирование по известным путям:/wp-login.php,/phpmyadmin,/.env. Это не взлом, а разведка, но именно она предшествует всему остальному;nginx-limit-req— работает только если в самом Nginx описанlimit_req_zone; без него джейл включён и бесполезен;postfix-saslиdovecot— обязательны, если почта своя. Подбор пароля к почтовому ящику идёт непрерывно и обычно вообще никем не смотрится.
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log
Отдельная история — форма входа самого сайта. У приложения нет своего лога неудачных входов, а в access.log неудачная попытка выглядит как обычный POST с кодом 200 или 302, и универсальным фильтром её не отличить от успешной. Варианта два: заставить приложение писать провалы в syslog (у большинства CMS для этого есть плагин) или ограничивать частоту запросов к странице входа. Второе — не детектор, а ограничитель, и лучше это понимать заранее.
Числа
bantime = 10m — настройка по умолчанию, из-за которой Fail2ban часто считают бесполезным: за десять минут бот успевает вернуться. Час на первый бан плюс recidive на неделю работают гораздо лучше вечных банов, которые со временем превращаются в километровый список правил.
maxretry = 3 для SSH — верный способ заблокировать самого себя. Пять попыток за десять минут отсекают перебор ничуть не хуже.
Медленный перебор — одна попытка в пять минут — под findtime не попадёт никогда. Это не повод растягивать окно до суток: получите ложные срабатывания на своих же сотрудниках. Против медленного перебора работает не порог, а отключённый парольный вход.
Убедитесь, что бан доходит до пакетов
Fail2ban только вызывает внешнюю команду. Если banaction не совпадает с тем, чем реально фильтруется трафик, в логе будут бодрые строки Ban 198.51.100.7, а пакеты продолжат приходить. На Debian 12 по умолчанию nftables, при включённом UFW правильнее banaction = ufw. Проверка:
sudo nft list ruleset | grep -c f2b
sudo iptables -S | grep f2b
Хотя бы одна из команд должна показать правила. А проверить сам фильтр, не дожидаясь атаки, можно так:
sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf
Внизу вывода — сколько строк совпало. Ноль означает, что фильтр и лог не сходятся, и дальше настраивать бессмысленно.
Чего Fail2ban не делает
Он не защищает от распределённого перебора: тысяча адресов по одной попытке не наберёт порога ни при каких настройках. Он не чинит причину — забаненный адрес не отменяет слабый пароль. И он ничего не знает о репутации: адрес, вчера ломавший чужие серверы, для него чист, пока не начнёт ломать ваш. Отсюда и связка на практике: ключи вместо паролей, Fail2ban как ограничитель шума и общий блоклист (CrowdSec) как знание о чужом опыте.
Отдельная беда — что всё это надо иногда смотреть. fail2ban-client status по каждому джейлу отдельно никто не набирает неделями, и рост числа банов замечают, когда уже что-то сломалось. На страницах демо ниже те же данные собраны на одну страницу: список джейлов, кто забанен прямо сейчас и откуда идут попытки.