Fail2ban: комплексная защита 50 VPS от брутфорса и сканирования

Задача клиента: 50 VPS под постоянной атакой

Веб-хостинг «ХостПлюс» предоставляет VPS для малого бизнеса: интернет-магазины на WordPress, корпоративные сайты на Bitrix, почтовые серверы. 50 серверов с белыми IP-адресами круглосуточно подвергались атакам: SSH-брутфорс генерировал до 10 000 попыток в час на каждый сервер, боты подбирали пароли к wp-login.php, сканеры перебирали пути к phpMyAdmin и .env-файлам.

Проблемы нарастали: несколько клиентов использовали слабые пароли, и их серверы были скомпрометированы. Логи auth.log и access.log разрастались до гигабайтов за сутки. Нагрузка от мусорных запросов доходила до 15% CPU на некоторых серверах.

Специалисты itfresh.ru развернули fail2ban — систему анализа логов и автоматической блокировки IP-адресов — на всех 50 серверах с единым набором правил и Telegram-оповещениями.

Архитектура fail2ban: jail, filter, action

Fail2ban работает по простому принципу: читает лог-файлы, ищет паттерны (фильтры), и при превышении порога срабатываний за период — выполняет действие (обычно блокировку IP).

Три ключевых компонента:

  • Jail — правило: какой лог читать, какой фильтр применять, порог и действие.
  • Filter — регулярное выражение для поиска вредоносных паттернов в логе.
  • Action — что делать при срабатывании: заблокировать в iptables/nftables, отправить уведомление.
# Установка
$ apt install fail2ban

# Структура конфигурации
/etc/fail2ban/
├── fail2ban.conf          # Глобальные настройки (не редактировать)
├── fail2ban.local          # Переопределение глобальных настроек
├── jail.conf               # Все jails по умолчанию (не редактировать)
├── jail.local              # Переопределение jail.conf
├── jail.d/                 # Дополнительные jail-файлы
│   ├── sshd.local
│   ├── nginx-4xx.local
│   └── wordpress.local
├── filter.d/               # Фильтры (регулярные выражения)
│   ├── sshd.conf
│   ├── nginx-4xx.conf
│   └── wordpress.conf
└── action.d/               # Действия
    ├── nftables.conf
    ├── telegram.conf
    └── cloudflare.conf

Правило номер один: никогда не редактировать jail.conf и fail2ban.conf — они перезаписываются при обновлении пакета. Все настройки — в *.local файлах, которые имеют приоритет.

# /etc/fail2ban/jail.local — базовые настройки для всех jails
[DEFAULT]
# Время бана по умолчанию: 1 час
bantime = 3600

# Период наблюдения: 10 минут
findtime = 600

# Порог: 5 попыток за findtime
maxretry = 5

# Действие по умолчанию: nftables + отправка в Telegram
banaction = nftables-multiport
action = %(action_)s
         telegram[name=%(__name__)s]

# Игнорируемые IP (внутренняя сеть + мониторинг)
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 192.168.0.0/16

SSH jail: тонкая настройка

SSH — главная цель атак. Стандартный jail [sshd] работает, но нуждается в тюнинге для production.

# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = ssh,2222
logpath = /var/log/auth.log
backend = systemd

# Агрессивный режим: распознаёт больше паттернов атак
mode = aggressive

# 3 неудачные попытки за 5 минут = бан на 12 часов
maxretry = 3
findtime = 300
bantime = 43200

# Использовать nftables вместо iptables
banaction = nftables-multiport

Режим aggressive добавляет распознавание паттернов, которых нет в normal: «Connection closed by authenticating user», «Unable to negotiate» и другие. Это ловит продвинутые сканеры, которые обрывают соединение до явного отказа.

Проверка работы:

# Статус jail
$ fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 12
|  |- Total failed:     847
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service
`- Actions
   |- Currently banned: 34
   |- Total banned:     523
   `- Banned IP list:   103.145.xx.xx 45.227.xx.xx ...

# Ручная разблокировка IP
$ fail2ban-client set sshd unbanip 103.145.xx.xx

# Ручная блокировка IP
$ fail2ban-client set sshd banip 185.234.xx.xx

# Проверка фильтра на логе
$ fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched

Кастомные jails: Nginx 4xx, WordPress, Postfix

Для «ХостПлюс» мы создали кастомные фильтры для трёх основных векторов атак.

Nginx 4xx — блокировка сканеров:

# /etc/fail2ban/filter.d/nginx-4xx.conf
[Definition]
failregex = ^ - .* "(GET|POST|HEAD|PUT|DELETE) .+" (400|401|403|404|405|444) .*$
ignoreregex = ^ - .* "GET /favicon\.ico" 404
              ^ - .* "GET /robots\.txt" 404

# /etc/fail2ban/jail.d/nginx-4xx.local
[nginx-4xx]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
filter = nginx-4xx
maxretry = 20
findtime = 60
bantime = 7200

WordPress wp-login.php — защита от подбора паролей:

# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^ - .* "POST /wp-login\.php HTTP/.*" (200|302) .*$
            ^ - .* "POST /xmlrpc\.php HTTP/.*" 200 .*$
ignoreregex =

# /etc/fail2ban/jail.d/wordpress.local
[wordpress]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
filter = wordpress
maxretry = 5
findtime = 120
bantime = 86400

# Отдельный jail для xmlrpc (более агрессивный)
[wordpress-xmlrpc]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
filter = wordpress[mode=xmlrpc]
maxretry = 2
findtime = 60
bantime = 604800

Postfix SASL — защита почтового сервера:

# /etc/fail2ban/filter.d/postfix-sasl.conf
[Definition]
failregex = ^.*warning: [-._\w]+\[\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed:.*$
            ^.*reject: RCPT from [-._\w]+\[\]: 554 5\.7\.1.*$
            ^.*reject: RCPT from [-._\w]+\[\]: 450 4\.7\.1.*Client host rejected.*$
ignoreregex =

# /etc/fail2ban/jail.d/postfix.local
[postfix-sasl]
enabled = true
port = smtp,465,587,submission
logpath = /var/log/mail.log
filter = postfix-sasl
maxretry = 3
findtime = 600
bantime = 86400

Recidive jail: наказание рецидивистов

Многие боты после разблокировки возвращаются через час. Jail recidive анализирует лог самого fail2ban и банит IP-адреса, которые были заблокированы повторно.

# /etc/fail2ban/jail.d/recidive.local
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
filter = recidive

# 3 бана за неделю = бан на 4 недели
maxretry = 3
findtime = 604800
bantime = 2419200

# Или перманентный бан
# bantime = -1

# Действие: блокировка + Telegram-уведомление
action = nftables-allports[name=recidive]
         telegram[name=recidive]

# /etc/fail2ban/filter.d/recidive.conf
[Definition]
failregex = ^.*\[.*\]\s+Ban\s+\s*$
ignoreregex =

[Init]
journalmatch =

Цепочка эскалации для «ХостПлюс»:

  1. Первый бан (SSH/Nginx/WordPress) — 1-24 часа в зависимости от jail.
  2. Третий бан за неделю (recidive) — 4 недели, все порты.
  3. Пятый бан за месяц — перманентная блокировка + добавление в общий blacklist.

Проверка recidive-банов:

$ fail2ban-client status recidive
Status for the jail: recidive
|- Filter
|  |- Currently failed: 8
|  |- Total failed:     156
|  `- File list:        /var/log/fail2ban.log
`- Actions
   |- Currently banned: 47
   |- Total banned:     892
   `- Banned IP list:   45.227.xx.xx 103.145.xx.xx ...

Telegram-алерты и интеграция с Cloudflare

Для оперативного реагирования мы настроили Telegram-уведомления о каждом бане и разблокировке.

# /etc/fail2ban/action.d/telegram.conf
[Definition]
norestored = true

actionstart = /usr/bin/curl -s -X POST \
    "https://api.telegram.org/bot/sendMessage" \
    -d chat_id="" \
    -d parse_mode="HTML" \
    -d text="🟢 Fail2ban запущен\nJail: \nServer: $(hostname)"

actionstop = /usr/bin/curl -s -X POST \
    "https://api.telegram.org/bot/sendMessage" \
    -d chat_id="" \
    -d parse_mode="HTML" \
    -d text="🔴 Fail2ban остановлен\nJail: \nServer: $(hostname)"

actionban = /usr/bin/curl -s -X POST \
    "https://api.telegram.org/bot/sendMessage" \
    -d chat_id="" \
    -d parse_mode="HTML" \
    -d text="🚫 BAN\nIP: \nJail: \nServer: $(hostname)\nПопыток: "

actionunban = /usr/bin/curl -s -X POST \
    "https://api.telegram.org/bot/sendMessage" \
    -d chat_id="" \
    -d parse_mode="HTML" \
    -d text="✅ UNBAN\nIP: \nJail: \nServer: $(hostname)"

[Init]
tg_token = 5551234567:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
tg_chatid = -100123456789

Для сайтов за Cloudflare блокировка через iptables бесполезна — реальный IP клиента в заголовке CF-Connecting-IP. Мы настроили action для Cloudflare API:

# /etc/fail2ban/action.d/cloudflare.conf
[Definition]
actionban = /usr/bin/curl -s -X POST \
    "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules" \
    -H "Authorization: Bearer " \
    -H "Content-Type: application/json" \
    -d '{"mode":"block","configuration":{"target":"ip","value":""},"notes":"fail2ban "}'

actionunban = /usr/bin/curl -s -X DELETE \
    "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$(\
    /usr/bin/curl -s \
    "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?configuration.value=" \
    -H "Authorization: Bearer " | /usr/bin/jq -r '.result[0].id'\
    )" \
    -H "Authorization: Bearer "

[Init]
cf_token = your_cloudflare_api_token

В jail.local для сайтов за Cloudflare:

[nginx-cloudflare]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
filter = nginx-4xx
maxretry = 20
findtime = 60
bantime = 7200
action = cloudflare[name=%(__name__)s]
         telegram[name=%(__name__)s]

Результаты и рекомендации по fail2ban на масштабе

После разворачивания fail2ban на 50 серверах «ХостПлюс» через Ansible мы собрали статистику за первый месяц работы.

МетрикаДо fail2banПосле fail2ban
SSH-попытки брутфорса в сутки~500 000~12 000 (остаток — новые IP)
Успешные компрометации за месяц30
Размер auth.log за сутки800 МБ - 1.5 ГБ15-30 МБ
Нагрузка CPU от мусорных запросов10-15%<1%
Уникальных забаненных IP за месяц42 847
Recidive-банов (рецидивисты)3 291

Рекомендации по production-эксплуатации:

  • Всегда добавляйте свои IP и IP мониторинга в ignoreip — иначе заблокируете себя при ошибке пароля.
  • Используйте backend = systemd вместо polling — меньше CPU на отслеживание файлов.
  • Для серверов с >10 000 забаненных IP используйте banaction = nftables-multiport — nftables с sets работает значительно быстрее iptables с цепочками.
  • Тестируйте фильтры перед деплоем: fail2ban-regex /path/to/log /path/to/filter покажет все совпадения.
  • Настройте recidive jail обязательно — без него ботнеты просто ждут окончания бана и возвращаются.

Если ваши серверы подвергаются атакам брутфорса и сканирования — обращайтесь к специалистам itfresh.ru. Мы настроим комплексную защиту с fail2ban, мониторингом и автоматическим реагированием.

Часто задаваемые вопросы

При использовании iptables с цепочками — да, 50 000+ правил заметно замедляют обработку пакетов. Решение — использовать nftables с sets (banaction = nftables-multiport): IP хранятся в hash-таблице, поиск за O(1) независимо от количества. Также можно использовать ipset с iptables.
Добавьте свои IP-адреса в ignoreip в секции [DEFAULT]. Держите запасной доступ к серверу: IPMI/KVM-консоль, второй SSH на нестандартном порту. При тестировании новых jails используйте bantime = 60 (1 минута) — даже при ошибке бан быстро снимется.
Да, но стандартная блокировка через iptables не работает — запросы приходят с IP Cloudflare, а не клиента. Нужен кастомный action, который через Cloudflare API добавляет IP в firewall rules. Дополнительно настройте Nginx для извлечения реального IP из заголовка CF-Connecting-IP.
Создайте файл в /etc/fail2ban/filter.d/myfilter.conf с секцией [Definition] и failregex, где — placeholder для IP-адреса. Протестируйте командой fail2ban-regex /path/to/logfile /etc/fail2ban/filter.d/myfilter.conf. Важно: регулярка должна содержать — без него fail2ban не сможет извлечь IP.
Nginx limit_req ограничивает частоту запросов, но не блокирует IP полностью и не анализирует другие сервисы (SSH, почта). Fail2ban анализирует логи любых приложений, блокирует на уровне файрвола (все порты) и поддерживает эскалацию (recidive). Лучший результат — комбинация обоих: limit_req как первая линия, fail2ban для полной блокировки.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#fail2ban#защита от брутфорса#jail.conf#fail2ban ssh#fail2ban nginx#fail2ban wordpress#fail2ban telegram#recidive
Комментарии 0

Оставить комментарий

загрузка...