Все уроки
Linux и серверыНачальный8 мин чтенияВ моём стеке

fail2ban: баним brute-force-атакующих по логам

Следим за логами, считаем неудачи, баним IP: простая защита от вторжений для SSH и nginx.

  • Jails
  • Filters
  • Bans
  • SSH / nginx
Шаг 1/6
Сервер с открытым SSH. Приходит хакер со списком паролей. fail2ban на посту (слегка сонный).

За 30 секунд

Каждый публичный сервер круглые сутки прощупывают боты, перебирающие пароли и сканирующие известные уязвимые URL. fail2ban читает ваши логи, замечает IP, который раз за разом терпит неудачу, и на время блокирует его в файрволе. Это дёшево, просто и эффективно против шумных атак с одного источника, но это лишь один слой из нескольких, а не полноценная защита.

Почему я это использую

  • Я запускаю fail2ban на своих серверах, потому что он тихо убирает большую часть brute-force-шума против SSH и веб-логинов.
  • Он входит в базовый набор безопасности, который я применяю к каждому серверу, рядом с SSH только по ключам, ufw и автоматическими обновлениями.
  • Он также снова делает логи читаемыми: меньше мусорных попыток — и реальные проблемы бросаются в глаза.

Как это работает

01Идея: охранник, который читает журнал посещений

Представьте охранника клуба, который каждые несколько секунд заглядывает в журнал. Если один и тот же человек пять раз за десять минут назвал неверный пароль, его не пускают час. Это и есть fail2ban. Технически это демон на Python, который следит за лог-файлами или журналом systemd, сопоставляет каждую новую строку с регулярными выражениями, считает совпадения по IP-адресам и при превышении порога выполняет действие — обычно добавляет правило файрвола (nftables, iptables или ufw), которое отбрасывает пакеты атакующего. По истечении времени бана он удаляет правило. Это называется защитой от вторжений на основе логов: сам он трафик не анализирует, а реагирует на то, что уже пишут в лог ваши сервисы. Поэтому он лёгкий и не зависит от конкретного сервиса: защитить можно всё, что логирует неудачи вместе с IP.

02Jail, фильтры и действия

fail2ban состоит из трёх частей. Фильтр (filter.d/*.conf) — набор регулярных выражений, которые распознают неудачу в строке лога и захватывают хост тегом <HOST>. Действие (action.d/*.conf) говорит, что делать при бане и разбане: правила файрвола, вызов API Cloudflare, письмо. Jail связывает их вместе: какой лог читать, какой фильтр и действие использовать и какие пороги. Пороги — это три числа. maxretry — сколько неудач приводит к бану. findtime — окно, в которое эти неудачи должны произойти. bantime — сколько длится бан. С bantime.increment повторные нарушители каждый раз получают более долгий бан. Jail recidive идёт дальше: он следит за собственным логом fail2ban и на дни банит IP, которые раз за разом попадают в бан. Никогда не правьте jail.conf, его перезаписывают обновления; кладите свои настройки в jail.local или jail.d/*.local.

  • maxretry = 5, findtime = 10m, bantime = 1h — разумная отправная точка.
  • ignoreip гарантирует, что адреса вашего офиса или VPN никогда не будут забанены.
  • В Ubuntu 24.04 и новее jail sshd читает журнал systemd (backend = systemd), а не /var/log/auth.log.

03Практичный jail.local для SSH и nginx

Эта конфигурация защищает SSH и типичные случаи nginx с помощью фильтров, которые идут в комплекте с fail2ban. nginx-http-auth ловит неудачные входы через basic auth, nginx-botsearch ловит ботов, запрашивающих известные пути эксплойтов, которые возвращают 404, а nginx-limit-req банит клиентов, раз за разом упирающихся в rate limit nginx (работает, только если вы настроили limit_req). Recidive разбирается с настойчивыми атакующими. В Ubuntu устанавливайте через apt install fail2ban; пакет уже включает jail sshd через jail.d/defaults-debian.conf с действием nftables. После правок выполните fail2ban-client reload и проверьте статус. Если вы используете ufw и предпочитаете видеть баны в ufw status, задайте banaction = ufw.

ini
# /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
bantime.increment = true
ignoreip = 127.0.0.1/8 ::1 203.0.113.10
 
[sshd]
enabled  = true
backend  = systemd
maxretry = 3
 
[nginx-http-auth]
enabled = true
[nginx-botsearch]
enabled = true
[recidive]
enabled  = true
bantime  = 1w
findtime = 1d

04Пишем собственный фильтр

Защитить можно всё, что логирует неудачи. Частый пример: сайт на Laravel или Next.js, который постоянно прощупывают в поисках страниц входа WordPress и утёкших файлов .env. На сайте без WordPress такие запросы никогда не бывают легитимными, так что даже несколько штук — сильный сигнал. Фильтр ниже находит такие запросы в access-логе nginx (формат combined по умолчанию, где IP клиента — первое поле). Всегда проверяйте новый фильтр через fail2ban-regex на реальном логе, прежде чем включать его: он показывает, сколько строк совпало, и ловит ошибки в регулярках, из-за которых не забанили бы никого — или всех. Если сайт стоит за Cloudflare или другим прокси, сначала настройте модуль real_ip в nginx, иначе в логе будет IP прокси, и вы забаните сам прокси.

ini
# /etc/fail2ban/filter.d/nginx-probe.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD) /(wp-login\.php|xmlrpc\.php|\.env|\.git/)
ignoreregex =
 
# /etc/fail2ban/jail.d/nginx-probe.local
[nginx-probe]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 2
bantime  = 1d
 
# Test before enabling
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-probe.conf

05Проверка, разбан и отладка

fail2ban-client — инструмент управления. status показывает активные jail, status с именем jail — текущие неудачи и забаненные IP, а set jail unbanip снимает бан (например, когда коллега слишком часто ошибался в пароле). Лог fail2ban, /var/log/fail2ban.log, фиксирует каждый бан и разбан с именем jail — это первое место, куда смотреть, когда кто-то говорит, что его заблокировало. Если jail никогда никого не банит, проверьте три вещи: jail действительно включён (status его показывает), он читает правильный источник (путь к логу или backend systemd), и фильтр действительно совпадает со строками (fail2ban-regex). Если он банит, а трафик всё равно приходит, возможно, действие не соответствует вашему файрволу или трафик идёт через прокси, IP которого не совпадает с IP атакующего.

bash
sudo fail2ban-client status                 # list active jails
sudo fail2ban-client status sshd            # failures and banned IPs for one jail
sudo fail2ban-client banned                 # all banned IPs, per jail
sudo fail2ban-client set sshd unbanip 203.0.113.10
sudo fail2ban-client unban 203.0.113.10     # remove from every jail
sudo fail2ban-client reload                 # apply config changes
sudo tail -f /var/log/fail2ban.log
sudo nft list ruleset | grep -A3 f2b        # see the firewall rules it created

06Ограничения, и fail2ban против CrowdSec

fail2ban видит атаки только после того, как они появились в логе, и оценивает каждый IP отдельно. Распределённая атака с тысяч адресов, каждый из которых пробует один-два раза, никогда не превысит порог. Он ничего не делает против уязвимостей в вашем приложении, а бан в файрволе сервера не помогает, когда трафик приходит через прокси CDN, потому что соединение идёт от CDN; там нужно действие, блокирующее на краю (в fail2ban есть действия для Cloudflare), или собственные правила WAF у CDN. Поэтому относитесь к нему как к снижению шума поверх настоящих мер: SSH только по ключам, файрвол с запретом по умолчанию, rate limits в nginx и WAF вроде Cloudflare впереди. CrowdSec — современная альтернатива: он разбирает логи похожим образом, но отделяет обнаружение от блокировки (bouncers для файрвола, nginx или Cloudflare) и делится сигналами со своим сообществом, так что IP, атакующий других, может быть заблокирован ещё до того, как доберётся до вас. Он мощнее, но в нём больше движущихся частей и есть онлайн-компонент.

Когда применять

  • На любом сервере, смотрящем в интернет, с открытым SSH, даже при входе только по ключам, — чтобы срезать шум в логах и притормозить сканеры.
  • Перед формами входа, зонами с basic auth и админками, которые логируют неудачи с IP клиента.
  • Чтобы банить сканеры, прощупывающие пути, которых на вашем сайте никогда не было (.env, wp-login.php, .git).
  • Как лёгкий слой на небольших серверах, где полноценный WAF или SIEM были бы избыточны.

Когда не стоит

  • Как основная защита от распределённых атак или DDoS; используйте CDN/WAF и rate limiting.
  • За прокси или балансировщиком без настройки реального IP: вы забаните сам прокси.
  • Когда SSH доступен только через VPN или частную сеть: защищать там почти нечего.

Альтернативы

  • CrowdSec

    Обнаружение на основе логов с отдельными bouncers и общими блок-листами сообщества; больше возможностей, больше компонентов.

  • sshguard

    Меньший и более сфокусированный демон для SSH и нескольких сервисов; настраивается хуже, чем fail2ban.

  • Cloudflare WAF и rate limiting

    Блокирует атаки на краю, до того как они дойдут до сервера; покрывает только HTTP, не SSH.

  • nginx limit_req / limit_conn

    Встроенное ограничение частоты запросов по IP; предотвращает злоупотребление в моменте, а не банит постфактум.

Лучшие практики

  1. Кладите все настройки в jail.local или jail.d/*.local; никогда не правьте jail.conf.
  2. Добавьте свои статические IP в ignoreip, прежде чем включать строгие jail.
  3. Проверяйте каждый собственный фильтр через fail2ban-regex на реальных логах.
  4. Включите bantime.increment и jail recidive для настойчивых атакующих.
  5. За Cloudflare или балансировщиком настройте реальные IP клиентов и блокируйте на краю.
  6. Сохраняйте SSH только по ключам и файрвол с запретом по умолчанию: fail2ban их дополняет, а не заменяет.

Частые ошибки

  • Запирают себя, проверяя неверные пароли со своего единственного соединения без записи в ignoreip.
  • Указывают несуществующий путь к логу (auth.log в системах только с журналом), и jail молча ничего не делает.
  • Банят IP CDN или балансировщика, потому что nginx логирует адрес прокси.
  • Верят, что fail2ban останавливает распределённые атаки или защищает от уязвимостей приложения.

Глубже

07Внутри движка: backend, даты и база банов

У каждого jail есть backend, который доставляет новые строки лога. systemd читает журнал напрямую и фильтрует по юниту — именно это нужно современной Ubuntu для SSH. Для обычных файлов auto пробует pyinotify и при неудаче переходит на polling. Каждая строка должна содержать метку времени, которую fail2ban распознаёт; чтобы решить, попадает ли неудача в findtime, он использует время из лога, а не время чтения строки. Неверный datepattern или несовпадение часовых поясов делают неудачи «старыми», и jail молча никогда не банит. Баны сохраняются в базе SQLite /var/lib/fail2ban/fail2ban.sqlite3, поэтому восстанавливаются после рестарта или перезагрузки. dbpurgeage, по умолчанию один день, определяет, как долго хранится история банов. Это важно для recidive и инкрементальных банов: если findtime у recidive — неделя, а история чистится через день, повторных нарушителей никогда не распознают. Поднимите dbpurgeage в fail2ban.local выше самого длинного findtime.

ini
# /etc/fail2ban/fail2ban.local
[Definition]
dbpurgeage = 8d      # must be longer than recidive findtime
 
# Check which date format and lines a filter matches
sudo fail2ban-regex systemd-journal sshd
sudo fail2ban-regex --print-all-missed /var/log/nginx/access.log nginx-probe

08Инкрементальные баны: наказание повторных нарушителей

Фиксированный бан на час учит бота возвращаться через шестьдесят одну минуту. С bantime.increment fail2ban смотрит, сколько раз адрес уже банили, и умножает бан. Формула по умолчанию каждый раз удваивает его (время бана, умноженное на два в степени числа банов), так что 1h превращается в 2h, 4h, 8h и так далее; bantime.multipliers позволяет вместо этого задать явную последовательность. bantime.maxtime ограничивает рост, чтобы ошибка не заблокировала реального пользователя на месяцы, а bantime.rndtime добавляет случайную добавку, чтобы боты не могли точно рассчитать момент разбана. bantime.overalljails = true учитывает предыдущие баны из всех jail, а не только текущего: адрес, который вчера сканировал nginx, сегодня получает более длинный бан по SSH. Вместе с постоянной историей это часто делает отдельный jail recidive ненужным, хотя держать оба не вредно.

ini
# /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 1h
bantime.increment   = true
bantime.factor      = 1
bantime.maxtime     = 1w
bantime.rndtime     = 10m
bantime.overalljails = true
# alternative to the formula:
# bantime.multipliers = 1 2 4 8 16 32 64

09Action на масштабе: наборы nftables, IPv6 и edge

Старые action для iptables добавляли по правилу на каждый забаненный адрес, и каждый пакет проходил весь этот список, что при тысячах банов становится медленным. Action для nftables, которые используют Debian и Ubuntu, кладут адреса в set внутри inet f2b-table — это поиск по хэшу независимо от размера, и он охватывает IPv4 и IPv6 в одной таблице. Вариант allports блокирует все порты, что полезно для recidive, а multiport — только порты jail. IPv6 требует размышлений: fail2ban банит отдельные адреса, но один атакующий может контролировать целую /64 и просто менять адреса. Это ещё одна причина считать настоящей защитой rate limit и SSH только по ключам. Для HTTP за Cloudflare action cloudflare-token создаёт правило доступа в зоне через Cloudflare API с токеном ограниченных прав, так что блокировка происходит на edge, куда трафик на самом деле приходит. Комбинируйте его с локальным action, чтобы SSH и прямые обращения к origin по-прежнему блокировались на сервере.

ini
# See the nftables sets fail2ban maintains
sudo nft list table inet f2b-table
 
# jail.d/nginx-probe.local: block locally and at Cloudflare
[nginx-probe]
action = %(action_)s
         cloudflare-token[cfzone="your_zone_id", cftoken="your_scoped_token"]

10Фильтры, которыми нельзя злоупотребить

Фильтр превращает недоверенный текст в правила файрвола, так что это граница безопасности. Логи часто содержат поля под контролем атакующего: имя пользователя, URL, User-Agent. Если регулярное выражение не заякорено, атакующий может вписать в такое поле IP-адрес жертвы и заставить вас забанить её — например, вашего платёжного провайдера или сервис мониторинга. Из-за этого риска log injection штатные фильтры якорятся на фиксированной структуре строки и захватывают <HOST> только там, где адрес клиента пишет сам сервис. Правила для собственных фильтров: якорьте через ^ и сопоставляйте точный формат лога, захватывайте хост из поля, которое контролирует ваш сервер, делайте шаблон специфичным для одного смысла неудачи и избегайте вложенных жадных wildcard, которые медленны на длинных строках. Для заведомо нормального трафика, например health check, используйте ignoreregex. Перед включением протестируйте через fail2ban-regex на неделе реальных логов и посмотрите и совпавшие, и пропущенные строки.

Реальные ситуации

01

Коллегу заблокировало перед демо

Проблема

Новый деплой-скрипт разработчика постоянно повторял SSH с неверным именем пользователя с офисного подключения. Теперь его соединение отваливается по таймауту, а демо начинается через десять минут.

Как решить

Из другой сессии подтвердите причину через sudo fail2ban-client banned и найдите его адрес grep в /var/log/fail2ban.log, чтобы увидеть, какой jail его забанил. Снимите бан через sudo fail2ban-client set sshd unbanip 203.0.113.10 или через unban, чтобы убрать его из всех jail. Если включены инкрементальные баны, история всё равно учитывается, поэтому известный статический адрес офиса или VPN добавьте в ignoreip в jail.local и сделайте reload. Затем устраните первопричину: исправьте пользователя в скрипте и уберите слепые повторы. Если команда работает из дома с меняющимися IP, VPN с фиксированным выходным адресом чище, чем расширять ignoreip.

02

Jail sshd показывает ноль неудач на новом сервере

Проблема

В journalctl лог аутентификации полон попыток «Invalid user», но спустя сутки fail2ban-client status sshd показывает ноль failed и ноль banned.

Как решить

Jail читает не тот источник. В Ubuntu 24.04 и новее логи SSH идут в журнал, а /var/log/auth.log может отсутствовать или быть неполным, поэтому jail с файловым logpath ничего не видит. Задайте backend = systemd для sshd в jail.local, сделайте reload через fail2ban-client reload и убедитесь через fail2ban-client status sshd, что совпадения из журнала появились. Проверьте фильтр напрямую через fail2ban-regex systemd-journal sshd. Также убедитесь, что режим фильтра sshd соответствует подробности ваших логов и что часы и часовой пояс сервера верны: неудачи с меткой времени вне findtime игнорируются.

03

xmlrpc.php заваливает сайт за Cloudflare

Проблема

Сайт на WordPress за Cloudflare заваливают POST-запросами к xmlrpc.php. fail2ban сообщает о банах, но запросы продолжают приходить, а PHP-FPM перегружен.

Как решить

Баны никого полезного не задевают. Либо nginx пишет в лог адреса Cloudflare, и вы баните сам Cloudflare, либо реальный IP логируется, но трафик всё равно приходит из сети Cloudflare. Сначала настройте модуль realip с диапазонами Cloudflare и CF-Connecting-IP. Затем добавьте в jail action cloudflare-token, чтобы нарушителей блокировали на edge. А для этого случая ещё лучше: если xmlrpc.php не нужен, запретите его в nginx через location = /xmlrpc.php { deny all; } и добавьте правило Cloudflare WAF, блокирующее этот путь, чтобы запросы вообще не доходили до PHP. fail2ban остаётся для того, что проскочит.

Шпаргалка

Проверить конфигурацию
sudo fail2ban-client -t
Вывести действующую конфигурацию
sudo fail2ban-client -d | less
Показать текущие настройки jail
sudo fail2ban-client get sshd bantime && sudo fail2ban-client get sshd maxretry
Забанить адрес вручную
sudo fail2ban-client set sshd banip 203.0.113.10
Проверить фильтр на журнале
sudo fail2ban-regex systemd-journal sshd
Показать строки, пропущенные фильтром
sudo fail2ban-regex --print-all-missed /var/log/nginx/access.log nginx-probe
Посмотреть наборы nftables
sudo nft list table inet f2b-table
Хранить историю банов неделю
[Definition] dbpurgeage = 8d # in fail2ban.local
Посчитать баны за сегодня
sudo grep "$(date +%F)" /var/log/fail2ban.log | grep -c ' Ban '

В моих реальных проектах

  • fail2ban и ufw работают на продакшен-серверах, которые я веду.
  • Мой базовый набор объединяет fail2ban с unattended-upgrades, SSH только по ключам и файрволом, который открывает только 22, 80 и 443.
  • На сайтах за Cloudflare край берёт на себя DNS и базовую защиту от DDoS, а fail2ban покрывает то, что доходит до сервера.

Вопросы и ответы

Q1Что такое fail2ban и как он работает?

fail2ban — демон защиты от вторжений на основе логов. Он следит за лог-файлами или журналом systemd, применяет фильтры — регулярные выражения, которые обнаруживают неудачи и извлекают IP клиента, — и считает неудачи по каждому IP. Когда IP набирает maxretry неудач в пределах findtime, jail выполняет своё действие, обычно добавляя правило файрвола через nftables, iptables или ufw на время bantime. Затем он автоматически снимает бан. Jail объединяет источник логов, фильтр, действие и пороги, поэтому один и тот же механизм защищает SSH, basic auth в nginx, почтовые серверы или ваше собственное приложение — лишь бы оно логировало неудачи с адресом источника.

Q2Если SSH уже работает только по ключам, зачем вообще fail2ban?

При входе только по ключам brute force паролей не может увенчаться успехом, так что сам вход fail2ban больше не защищает. Но польза всё равно есть: он снижает шум в логах, чтобы реальные события были заметны, экономит немного CPU и трафика на бесконечные хендшейки, тормозит сканеры, ищущие слабые сервисы или ошибки конфигурации, и защищает другие сервисы, например веб-логины и админки. Он также служит страховкой, если кто-то временно снова включит пароли. Но я бы никогда не полагался на него вместо SSH только по ключам, файрвола с запретом по умолчанию и обновлений. Это дешёвый дополнительный слой, а не фундамент.

Q3Что такое bantime, findtime и maxretry?

Это пороги jail. maxretry — число неудач, которое запускает бан. findtime — скользящее окно, в которое эти неудачи должны произойти; пять неудач за неделю не засчитаются, если findtime — десять минут. bantime — сколько IP остаётся заблокированным; отрицательное значение означает навсегда. Типичные стартовые значения — пять неудач за десять минут для бана на час, для SSH строже. С включённым bantime.increment каждый повторный бан длится дольше, а jail recidive на дни банит IP, которые попадают в бан снова и снова. Слишком строгие значения блокируют реальных пользователей, слишком мягкие пропускают медленных атакующих.

Q4Наш сайт стоит за Cloudflare. Помогает ли fail2ban?

Только с дополнительной настройкой. При включённом проксировании каждое HTTP-соединение приходит на ваш сервер с адресов Cloudflare, поэтому nginx логирует IP Cloudflare, если вы не настроили модуль real_ip с диапазонами Cloudflare и заголовком CF-Connecting-IP. Но даже тогда бан в файрволе сервера блокирует IP атакующего, а его запросы всё равно приходят от Cloudflare, так что бан не действует. Нужно действие, которое создаёт правило блокировки в Cloudflare через его API, или, что лучше, напрямую использовать WAF и rate limiting Cloudflare. Для SSH, который не идёт через Cloudflare, fail2ban работает как обычно. Я также ограничиваю порты 80 и 443 диапазонами IP Cloudflare.

Q5Как бы вы сравнили fail2ban и CrowdSec?

Оба читают логи и банят IP, которые ведут себя плохо. fail2ban — единый зрелый демон: простой, работает офлайн, его легко понять, настраивается фильтрами на регулярках и действиями. CrowdSec отделяет обнаружение, которым занимается его агент с парсерами и сценариями, от реагирования, которым занимаются bouncers для файрвола, nginx, Cloudflare и других, так что один движок обнаружения может питать несколько точек применения. Его главный плюс — общий блок-лист сообщества: IP, о которых сообщили другие пользователи, можно заблокировать ещё до того, как они коснутся вашего сервера. Цена — больше компонентов, онлайн-консоль и больше того, что нужно изучить. Для одного-двух небольших серверов хватит fail2ban; для парка серверов CrowdSec масштабируется лучше.

Вопросы уровня senior

S1У вас пятнадцать серверов. Как вы организуете защиту от брутфорса на всём парке?

fail2ban на каждом сервере работает, но каждый сервер учится в одиночку: адрес, забаненный на одном, свеж для остальных четырнадцати. Варианты по возрастанию усилий: раскатывать одинаковую конфигурацию fail2ban через Ansible, чтобы хотя бы поведение было единым; добавить action, который отправляет баны в общий блоклист, например в файрвол облачного провайдера или Cloudflare, чтобы одно обнаружение защищало всех; или перейти на CrowdSec, чей локальный API позволяет нескольким агентам делиться решениями, а bouncers применяют их на файрволах, nginx и CDN. Больший выигрыш даёт архитектура: спрячьте SSH за VPN или bastion, чтобы наружу смотрел только один хост, а HTTP пусть обрабатывает edge. Тогда защита от брутфорса — забота двух компонентов, а не пятнадцати.

S2Какие риски несёт сам fail2ban?

Он работает от root и превращает содержимое логов в правила файрвола, поэтому немного расширяет поверхность атаки. Главный риск — log injection: плохо заякоренный собственный фильтр позволяет атакующему вписать адрес жертвы в имя пользователя или URL и добиться её бана, что может заблокировать вашего платёжного провайдера, мониторинг или офис. Второй — доступность: слишком агрессивные jail банят реальных клиентов за общим carrier-grade NAT, где тысячи пользователей делят один IPv4-адрес. Третий — ложное чувство защищённости: jail, который молча ничего не находит, выглядит как защита. Я снижаю риски заякоренными фильтрами, проверенными через fail2ban-regex, ignoreip для критичных партнёров, умеренными порогами в публичных веб-jail и алертами, когда обычно банящий jail затихает.

S3Rate limiting в nginx или бан через fail2ban: что вы используете для эндпоинта логина?

Оба, потому что они решают разные задачи. limit_req действует в реальном времени на каждый запрос: ограничивает, как быстро любой адрес может перебирать пароли, защищает приложение от перегрузки и сразу возвращает 429, не дожидаясь логов. Он быстро забывает, так что медленный атакующий может продолжать бесконечно. fail2ban действует постфактум по накопленным за минуты или дни уликам и полностью отсекает атакующего на файрволе, экономя TLS handshake и работу приложения. Приложению тоже нужны свои средства: ограничение по аккаунту, ведь атакующие меняют IP, но целятся в те же аккаунты, плюс многофакторная аутентификация. Порядок — эшелонированная защита: WAF на edge, rate limit в nginx, ограничения в приложении и затем fail2ban, банящий настойчивые источники.

Глоссарий

Jail
Единица fail2ban, объединяющая источник логов, фильтр, action и пороги для одного сервиса.
Filter
Набор регулярных выражений, которые распознают строки с неудачами в логе и извлекают адрес нарушителя.
Action
То, что fail2ban выполняет при бане и разбане, например добавляет адрес в набор nftables или вызывает API.
failregex
Регулярное выражение в фильтре, помечающее строку лога как неудачу; оно должно захватывать <HOST>.
<HOST>
Плейсхолдер fail2ban внутри failregex, который сопоставляет и захватывает IP-адрес или hostname.
Backend
Способ, которым jail получает строки лога: из журнала systemd, через inotify-отслеживание файлов или периодический polling.
Recidive
Jail, который следит за собственным логом fail2ban и надолго банит адреса, которые снова и снова попадают в бан.
ignoreip
Адреса или сети, которые никогда не банятся, например ваш офис, VPN или сервис мониторинга.
Log injection
Обман парсера логов: атакующий вставляет специально составленный текст, например IP жертвы, в контролируемые им поля.

Есть идея продукта?Давайте создадим его.

Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.