# fail2ban - автобан по логам _Безопасность · LinuxLab Knowledge Base_ **TL;DR:** fail2ban читает логи (sshd, nginx, postfix), регэкспом ловит N неудачных попыток за окно, добавляет IP в правила firewall'а на bantime. Главный инструмент против brute-force на SSH. ## Что делает и зачем На любом публичном 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). ## Установка ```bash # 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. ```bash sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local sudo nano /etc/fail2ban/jail.local ``` ## Минимальный jail.local ```ini [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 для рецидивистов: ```ini [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`. Включай только те что нужны. Самые популярные: ```ini [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 ```bash # обзор всех активных 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`: ```ini [Definition] failregex = ^.*\[AUTH-FAIL\] from .*$ ignoreregex = ``` `` - специальный токен fail2ban для подстановки IP/hostname. Подключаем jail в `jail.local`: ```ini [myapi] enabled = true filter = myapi port = http,https logpath = /var/log/myapi/access.log maxretry = 3 ``` Проверить regex без перезапуска: ```bash 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 не работает. ## Дебаг - почему не банит ```bash 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** вручную - сам пишешь скрипт парсинга. Не делай так в проде. ## Команды ```bash sudo fail2ban-client status sshd ``` Сколько IP забанено сейчас в SSH-jail и сколько было всего - главный обзор ```bash sudo fail2ban-client set sshd unbanip 1.2.3.4 ``` Разбанить конкретный IP - когда сам случайно попал в свой бан ```bash sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf ``` Тест regex'а фильтра на реальном логе - увидеть сколько строк матчатся ```bash sudo fail2ban-client reload sshd ``` Применить изменения jail.local без полного рестарта демона ```bash sudo journalctl -u fail2ban -f ``` Live-стрим логов fail2ban - увидеть в реальном времени баны/анбаны ## См. также - [SSH - secure shell](/kb/ssh.md) - [Apache httpd - веб-сервер](/kb/apache-httpd.md) - [BIND - авторитативный/кеширующий DNS-сервер](/kb/bind-dns-server.md) - [PAM - Pluggable Authentication Modules](/kb/pam.md)