Блог
DevOps · практикаDevOps 9 мин чтения

fail2ban на VPS: правильный конфиг для 2026

Стандартный fail2ban ловит только SSH-брут. Разбираем, как настроить jail-ы для nginx (401/403/scanner), для сайта (login-form), для почты и как не забанить себя.

fail2ban на большинстве production-серверов настроен по-минимуму — sshd jail, и всё. А между тем инструмент способен на большее: банить ботов, сканеры, WordPress-брут, почтовых спамеров. Разберёмся, как настроить полноценный фильтр за пару часов.

Плюс важный совет: как не заблокировать себя, свою CI, свой Cloudflare — самая частая проблема с fail2ban.

Как fail2ban устроен

Демон fail2ban мониторит логи (nginx, sshd, mail и т.д.) по регулярным выражениям. При превышении порога попыток за время-окно — вызывает action (обычно iptables/nftables drop на IP на N секунд).

Ключевые понятия:

  • Jail — правило: что мониторить + фильтр + action.
  • Filter — regex, определяющий «плохую» строку в логе.
  • Action — что делать (обычно ban через iptables).
  • Findtime — окно, за которое считаются попытки.
  • Maxretry — сколько попыток можно за findtime.
  • Bantime — насколько заблокировать.

Базовый конфиг: sshd

Файл — не редактируйте /etc/fail2ban/jail.conf. Создавайте /etc/fail2ban/jail.local:

# /etc/fail2ban/jail.local
[DEFAULT]
# Сколько банить (10 мин обычно; для сильно настырных увеличьте)
bantime = 1h
findtime = 10m
maxretry = 5

# Игнорируем свои IP и Cloudflare
ignoreip = 127.0.0.1/8 ::1 \
    203.0.113.42 \
    173.245.48.0/20 103.21.244.0/22 \
    103.22.200.0/22 103.31.4.0/22 \
    141.101.64.0/18 108.162.192.0/18 \
    190.93.240.0/20 188.114.96.0/20 \
    197.234.240.0/22 198.41.128.0/17 \
    162.158.0.0/15 104.16.0.0/13 \
    104.24.0.0/14 172.64.0.0/13 131.0.72.0/22

# Уведомления — куда шлём (при желании)
destemail = [email protected]
sender = [email protected]
action = %(action_)s

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = systemd
Осторожно

Список Cloudflare-подсетей обновляется — держите cloudflare.com/ips-v4 актуальным через cron. Иначе новый CF-диапазон приведёт к бану легитимного трафика.

Jail для nginx-401/403 (сканеры)

Стандартный nginx-http-auth — банит только Basic Auth брут. Дополним: любые массовые 401/403/404 (сканеры путей).

# /etc/fail2ban/jail.local — добавить:

[nginx-badbots]
enabled = true
port = http,https
filter = nginx-badbots
logpath = /var/log/nginx/access.log
maxretry = 3
findtime = 5m
bantime = 24h

[nginx-404-scanner]
enabled = true
port = http,https
filter = nginx-404-scanner
logpath = /var/log/nginx/access.log
maxretry = 20
findtime = 5m
bantime = 6h

# Файл /etc/fail2ban/filter.d/nginx-404-scanner.conf:
# [Definition]
# failregex = ^<HOST> .* "[A-Z]+ .*" 404 .*$
# ignoreregex = ^<HOST> .* "[A-Z]+ /(favicon\.ico|robots\.txt)" 404

Jail для WordPress брута

WordPress-логин (/wp-login.php, /xmlrpc.php) — типовая цель. Специальный jail:

# /etc/fail2ban/jail.local:

[wordpress-login]
enabled = true
port = http,https
filter = wordpress-login
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 5m
bantime = 6h

# /etc/fail2ban/filter.d/wordpress-login.conf:
# [Definition]
# failregex = ^<HOST> .* "POST /wp-login\.php HTTP.*" 200
#             ^<HOST> .* "POST /xmlrpc\.php HTTP.*" 200

Postfix / Dovecot

Если крутите свою почту — обязательные jail:

[postfix-sasl]
enabled = true
port = smtp,465,submission
filter = postfix-sasl
logpath = /var/log/mail.log
maxretry = 3
findtime = 10m
bantime = 24h

[dovecot]
enabled = true
port = imap,imaps,pop3,pop3s
filter = dovecot
logpath = /var/log/mail.log
maxretry = 5
findtime = 10m
bantime = 24h

Обход Cloudflare — реальный IP

Если фронт стоит за Cloudflare, все IP в логе — CF-адреса. Fail2ban будет банить CF (плохо!), и злоумышленники всё равно пройдут.

Решение: nginx должен писать реальный IP из X-Forwarded-For. Проверьте, что в конфиге nginx:

# /etc/nginx/nginx.conf в http {}
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ... остальные диапазоны Cloudflare
real_ip_header CF-Connecting-IP;
log_format combined_real '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent"';
access_log /var/log/nginx/access.log combined_real;

Мониторинг и не-забанить-себя

Проверить активные баны:

fail2ban-client status
fail2ban-client status sshd
fail2ban-client status nginx-404-scanner

# Разбанить IP
fail2ban-client set sshd unbanip 1.2.3.4

# Логи fail2ban
tail -f /var/log/fail2ban.log
Совет

Обязательно добавьте ignoreip для CI-сервисов (GitHub Actions), Uptimerobot IP, своего офиса. Иначе первая же нагрузочная проверка выкосит их.

Частые вопросы

fail2ban vs Cloudflare WAF — что выбрать?
Оба. CF WAF — на L7 для веба. fail2ban — для SSH, почты, сканеров. Дополняют друг друга.
IPv6 работает в fail2ban?
Да, но нужен nftables backend (не старый iptables). Проверьте `banaction = nftables-multiport` в jail.local.
fail2ban ест ресурсы?
Минимально: 20-30 MB RAM, 0.1% CPU при 10 jail-ах. На высоконагруженном сайте с сотнями jail-ов может расти.
Как забанить целую страну?
Не через fail2ban — он для реакции. Используйте GeoIP-модуль nginx или nftables с ipset. Или Cloudflare Firewall.