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— перебір проти базової автентифікації. Потрібен, якщо адмінка чи тестовий сайт закриті паролем вебсервера;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 (у більшості систем керування вмістом для цього є плагін) або обмежити частоту запитів до сторінки входу. Друге — обмежувач, а не детектор, і про це краще знати заздалегідь.
Числа
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 по кожному джейлу, а зростання кількості банів помічають, коли щось уже зламалося. На демосторінках нижче ті самі дані лежать на одній сторінці: список джейлів, хто забанений просто зараз і звідки йдуть спроби.