Что делает и зачем
На любом публичном SSH порт 22 за сутки получает тысячи попыток
brute-force: словарные root/password, admin/admin, etc. Сильный пароль
- ключи защищают, но логи захламлены и тратится CPU на bcrypt-проверки.
fail2ban работает так:
/var/log/auth.log
↓ tail -F
fail2ban читает построчно
↓
применяет regex-фильтр (filter.d/sshd.conf)
↓
если IP X сделал N FAIL'ов за findtime секунд
↓
→ добавить правило REJECT для X в firewall на bantime секунд
↓
по истечении - удалить правило
Применимо ко всему что пишет логи: SSH, Apache/nginx (auth_basic), Postfix (smtp-auth), Dovecot, ProFTPD, Asterisk, named (DNS-amplification).
Установка
# Debian/Ubuntu
sudo apt install fail2ban
# RHEL/Fedora - нужен EPEL
sudo dnf install epel-release
sudo dnf install fail2ban
Демон fail2ban (через systemd: fail2ban.service).
CLI - fail2ban-client.
Архитектура конфигов
/etc/fail2ban/
├── jail.conf ← vendor, НЕ ТРОГАТЬ (перезапишется при apt upgrade)
├── jail.local ← твой override (создавать самому)
├── jail.d/ ← модульные override'ы (50-sshd.local и т.д.)
├── filter.d/ ← regex-фильтры под каждый сервис (sshd.conf, apache-auth.conf)
├── action.d/ ← действия (firewallcmd-ipset, iptables, ufw)
└── fail2ban.local ← глобальные настройки демона
Стандартный путь: скопировать jail.conf в jail.local, править local.
fail2ban читает оба, local перебивает vendor.
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Минимальный jail.local
[DEFAULT]
# Глобальные настройки для всех jails
bantime = 1h # на сколько банить (поддерживает s, m, h, d, w)
findtime = 10m # окно для подсчёта попыток
maxretry = 5 # порог попыток
backend = systemd # читать journal вместо файлов (важно!)
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/24 # whitelist
banaction = iptables-multiport # или firewallcmd-ipset, nftables-multiport
# SSH - почти всегда первое что включаем
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 3 # для SSH строже чем глобально
bantime = 24h # и баним на дольше
Без enabled = true jail не активируется даже если упомянут.
Опции %(sshd_log)s и %(sshd_backend)s - переменные из paths-*.conf,
они автоматически подставят правильный путь для дистрибутива.
bantime / findtime / maxretry
Семантика:
maxretry- сколько неудач достаточно для бана.findtime- окно в секундах, в котором считаем maxretry.bantime- на сколько банить.
Пример: findtime=600 maxretry=5 → 5 неудач за 10 минут = бан.
Слишком жёстко (maxretry=2) - банишь себя при опечатке. Слишком
мягко (maxretry=20) - boты успевают подобрать. Разумный SSH-default:
maxretry=3, bantime=24h, findtime=10m.
bantime.increment - экспоненциальный бан
Современный fail2ban умеет наращивать bantime для рецидивистов:
[DEFAULT]
bantime = 1h
bantime.increment = true # включить
bantime.factor = 2 # множитель
bantime.maxtime = 1w # потолок
Первый бан 1ч, второй 2ч, четвёртый 8ч, ..., потолок неделя. После такого боты уходят.
Какой backend выбрать
auto- пусть fail2ban сам решит (часто работает плохо).pyinotify- следит за файлом через inotify (быстро, для логов в файле).systemd- читает journald напрямую. Рекомендуется для современных дистрибутивов где sshd пишет в journal а не в auth.log.
Если backend не тот - fail2ban запустится но ничего не увидит. Признак:
fail2ban-client status sshd показывает Total failed: 0 хотя в журнале
миллион FAIL'ов.
Активация конкретных jails
По умолчанию (jail.conf) большинство jails имеют enabled = false. Включай
только те что нужны.
Самые популярные:
[sshd]
enabled = true
...
[apache-auth]
enabled = true
port = http,https
logpath = /var/log/apache2/error.log
[postfix]
enabled = true
port = smtp,ssmtp,submission
logpath = %(postfix_log)s
[recidive]
enabled = true # мета-jail: банит тех кто уже был забанен в других jails
bantime = 1w
findtime = 1d
maxretry = 5
recidive - кросс-jail рецидивист. Кого-то забанили в sshd, потом в
apache-auth - recidive забанит на неделю поверх обычного бана.
Управление через fail2ban-client
# обзор всех активных jails
sudo fail2ban-client status
▸Status
▸|- Number of jail: 2
▸`- Jail list: sshd, recidive
# детали по конкретному jail
sudo fail2ban-client status sshd
▸Status for the jail: sshd
▸|- Filter
▸| |- Currently failed: 3
▸| |- Total failed: 1247
▸| `- File list: /var/log/auth.log
▸`- Actions
▸|- Currently banned: 5
▸|- Total banned: 143
▸`- Banned IP list: 45.x.x.x 196.x.x.x ...
# ручной бан / разбан
sudo fail2ban-client set sshd banip 1.2.3.4
sudo fail2ban-client set sshd unbanip 1.2.3.4
# снять все баны во всех jails
sudo fail2ban-client unban --all
# перечитать конфиг без рестарта
sudo fail2ban-client reload
sudo fail2ban-client reload sshd # один jail
Как написать свой filter
Допустим у тебя своё API логирующее [AUTH-FAIL] from 1.2.3.4 invalid token.
Создаём /etc/fail2ban/filter.d/myapi.conf:
[Definition]
failregex = ^.*\[AUTH-FAIL\] from <HOST> .*$
ignoreregex =
<HOST> - специальный токен fail2ban для подстановки IP/hostname.
Подключаем jail в jail.local:
[myapi]
enabled = true
filter = myapi
port = http,https
logpath = /var/log/myapi/access.log
maxretry = 3
Проверить regex без перезапуска:
fail2ban-regex /var/log/myapi/access.log /etc/fail2ban/filter.d/myapi.conf
▸Lines: 1234 lines, 0 ignored, 17 matched, 1217 missed
Если matched: 0 - regex не работает.
Дебаг - почему не банит
sudo systemctl status fail2ban
sudo journalctl -u fail2ban -f # стрим логов демона
sudo tail -F /var/log/fail2ban.log # его собственный лог
# проверка что jail видит свой лог:
sudo fail2ban-client status sshd
# Total failed должно расти при попытках
# тест regex на реальном файле
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Типичные причины «не работает»:
- Backend mismatch: sshd пишет в journal, а jail настроен на файл.
Поставить
backend = systemd. - maxretry слишком высокий: успеваешь себя забанить сам при тестах, но боты ещё не накопили - поставь временно 2.
- ignoreip покрывает атакующего: проверь не входит ли его подсеть.
- firewall не применил правило: на nftables-системах с
iptables-legacyможет быть конфликт. Использоватьbanaction = nftables-multiport.
Сравнение с альтернативами
- CrowdSec - современная замена, шарит блок-листы между всеми инсталляциями (federated). Сложнее, но эффективнее против ботнетов.
- sshguard - лёгкий, только под SSH/SMTP/etc, на C, не Python.
- firewalld + ipset вручную - сам пишешь скрипт парсинга. Не делай так в проде.