Настройка fail2ban и автоматическая блокировка брутфорс-атак

Опубликовано 13 мин. чтения

Как аккуратно настроить fail2ban через jail.local, запустить джейл sshd на всех четырёх актуальных дистрибутивах LTS, проверить блокировки и снять их, и какие ошибки чаще всего мешают.

Любой сервер с публичным IPv4-адресом уже через несколько минут после первой выдачи начинает получать попытки входа на порт 22. Это не целенаправленная атака, а фоновый шум. fail2ban читает строки журнала с этими неудачными попытками и заставляет файрвол блокировать IP-адрес источника на заданное время. Инструмент есть в пакетах всех рассматриваемых здесь дистрибутивов и настраивается за несколько минут. Подвохи кроются в другом: на Debian 12 fail2ban охотно стартует с ошибкой конфигурации, на некоторых системах он блокирует лишь на первый взгляд, хотя ни один пакет на самом деле не отбрасывается, а одна необдуманная строка в jail.local закроет доступ вам самим.

Эта статья разбирает именно такие места, для Debian 13, Debian 12, Ubuntu 24.04 LTS и Ubuntu 22.04 LTS.

Почему jail.local и никогда jail.conf

Файл /etc/fail2ban/jail.conf принадлежит пакету. При каждом обновлении он может быть перезаписан, и тогда dpkg в лучшем случае задаст вопрос, а в худшем, во время автоматического обновления без присмотра, не спросит вообще. Поэтому ваши изменения живут в /etc/fail2ban/jail.local. После установки этого файла нет, вы создаёте его сами, и в нём должны стоять только те значения, которые вы действительно меняете.

Важен порядок, в котором fail2ban читает файлы. Страница руководства jail.conf(5) называет его явно:

  1. jail.conf
  2. jail.d/*.conf в алфавитном порядке
  3. jail.local
  4. jail.d/*.local в алфавитном порядке

Побеждают файлы, прочитанные позже. Конкретно это значит: ваш jail.local перекрывает и настройки из /etc/fail2ban/jail.d/defaults-debian.conf, которые приносит с собой пакет дистрибутива. Многие руководства утверждают обратное и советуют поэтому отдельный файл в jail.d/. Необходимости в этом нет. jail.d/ имеет смысл только тогда, когда вы хотите раскатывать джейлы по отдельности через систему управления конфигурацией.

Что уже задаёт ваш дистрибутив

Самое большое различие между четырьмя системами кроется не в самом fail2ban, а в одном файле, который пакет кладёт в /etc/fail2ban/jail.d/defaults-debian.conf. Посмотрите на него в первую очередь:

cat /etc/fail2ban/jail.d/defaults-debian.conf
Системаfail2banСодержимое defaults-debian.conf
Debian 131.1.0banaction = nftables, banaction_allports = nftables[type=allports], для [sshd] дополнительно backend = systemd, journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd и enabled = true
Debian 121.0.2только [sshd] и enabled = true
Ubuntu 24.041.0.2banaction = nftables, banaction_allports = nftables[type=allports], backend = systemd, плюс [sshd] с enabled = true
Ubuntu 22.040.11.2только [sshd] и enabled = true

Отсюда следует почти всё остальное. Debian 13 и Ubuntu 24.04 с завода читают журнал systemd и блокируют через nftables. Debian 12 и Ubuntu 22.04 откатываются к значениям из jail.conf, то есть к banaction = iptables-multiport и backend = auto. А auto означает явно не «выбери подходящее сам». Комментарий в jail.conf говорит, что auto по очереди пробует pyinotify и polling. Оба способа работают с файлами. Значение auto никогда не выбирает журнал. Без файла /var/log/auth.log sshd-джейл там работает вхолостую.

Установка и первая проверка

apt update
apt install -y fail2ban

На Debian 12 и Ubuntu 22.04 добавляется ещё один пакет, если вы хотите разбирать журнал вместо файла лога:

apt install -y python3-systemd

На Debian 13 python3-systemd входит в жёсткие зависимости пакета и уже присутствует. На Debian 12 он лишь рекомендован, и это самый частый источник ошибок на этой системе. Затем проверьте версию и состояние:

apt-cache policy fail2ban
fail2ban-client --version
systemctl status fail2ban --no-pager

Если здесь уже стоит active (running), половина дела сделана. Если там failed, переходите сразу к разделу про сообщения об ошибках.

Собираем собственный jail.local

Создайте /etc/fail2ban/jail.local. Эта версия работает на всех четырёх системах при условии, что установлен python3-systemd:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.7
bantime  = 1h
findtime = 10m
maxretry = 5

bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 7d

banaction = nftables
banaction_allports = nftables[type=allports]

[sshd]
enabled  = true
backend  = systemd
port     = ssh
maxretry = 4
bantime  = 2h

Значение параметров простыми словами: findtime — это скользящее временное окно, maxretry число неудачных попыток внутри него, bantime длительность блокировки. Четыре неудачные попытки за десять минут дают здесь два часа блокировки. Значения времени понимают суффиксы m, h, d и w, а чистое число означает секунды. bantime со значением -1 блокирует навсегда.

Самое интересное здесь bantime.increment. С ним fail2ban удваивает срок блокировки при каждом повторном срабатывании того же адреса, с потолком из bantime.maxtime. Однократная опечатка реального пользователя стоит двух часов, а настойчивый бот через несколько кругов доходит до недели. Эта возможность существует начиная с fail2ban 0.11, то есть доступна и на Ubuntu 22.04. Чтобы она работала и после перезагрузок, база данных должна сохранять историю, а это регулируется параметром dbpurgeage в /etc/fail2ban/fail2ban.conf, по умолчанию одни сутки. Если вы задаёте bantime.maxtime = 7d, поднимите заодно dbpurgeage в файле fail2ban.local.

Два подводных камня в этом блоке. Во-первых, port = ssh: это имя разрешается через /etc/services в порт 22. Если ваша служба SSH слушает на 2222, там должно стоять port = 2222, иначе появится правило файрвола для порта, в который никто не стучится. Во-вторых, ignoreip: впишите туда свой постоянный офисный адрес, но никогда не целую сеть провайдера. И не полагайтесь только на него. Параметр ignoreself и так по умолчанию стоит в true и защищает собственные IP-адреса сервера.

Отличия в зависимости от системы

На Debian 12 у вас есть два равноценных пути. Либо вы остаётесь при backend = systemd и ставите python3-systemd, либо ставите rsyslog, опускаете backend и работаете с /var/log/auth.log. Кроме того, на Debian 12 стоит осознанно задать banaction. Пакет рекомендует nftables или iptables, и apt, как правило, ставит из них только nftables. А значение по умолчанию из jail.confiptables-multiport. На минимальной установке fail2ban тогда обращается к бинарнику, которого просто нет.

На Ubuntu 22.04 rsyslog входит в серверный стандарт, файл /var/log/auth.log существует, и джейл работает без вмешательства. Если вы там ничего не хотите перенастраивать, просто не пишите backend и banaction в своём jail.local. fail2ban 0.11.2 хотя и знает действие nftables, но путь через iptables-multiport на этой системе более протоптанный.

На Debian 13 и Ubuntu 24.04 ваш jail.local совпадает с настройками пакета. Вреда от этого нет, конфигурация просто становится явной и потому читаемой.

Применяем и проверяем, что это действительно работает

Перечитать конфигурацию, не теряя действующие блокировки:

fail2ban-client reload

На Debian 12 и Ubuntu 24.04 fail2ban 1.0.2 при запуске и при перечитывании конфигурации выдаёт предупреждение 'allowipv6' not defined in 'Definition'. Оно безобидно, служба продолжает работать нормально.

Команда systemctl restart fail2ban нужна редко и сбрасывает действующие блокировки. Дальше идут запросы состояния:

fail2ban-client ping
fail2ban-client status
fail2ban-client status sshd

Показателен последний вывод. Он показывает Currently failed, Total failed, число блокировок и список заблокированных адресов. Именно здесь заканчивается большинство руководств, и именно здесь начинается проблема: джейл, который чисто стартует и сообщает «0 banned», может быть полностью слепым. Джейл без единого срабатывания выглядит точно так же, как джейл, читающий не тот источник логов.

Три проверки, которые делают эту разницу видимой. Первая: видит ли фильтр эти строки вообще?

fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf

При файловом бэкенде вместо этого:

fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

В конце появляется строка вида Lines: 4211 lines, 0 ignored, 137 matched, 4074 missed. Если там стоит 0 matched, хотя в логе явно видны неудачные попытки, значит не подходит либо фильтр, либо выборка из журнала.

Вторая: какой фильтр журнала джейл использует на самом деле?

fail2ban-client get sshd journalmatch

Ответ различается в зависимости от системы, и это различие важно. Debian 13 сообщает _SYSTEMD_UNIT=ssh.service + _COMM=sshd, а Ubuntu 24.04 наоборот _SYSTEMD_UNIT=sshd.service + _COMM=sshd. То есть там юнит называется sshd.service, а на Debian ssh.service. На Debian 12 и Ubuntu 22.04 в состоянии из коробки возвращается No journal match filter set, потому что там бэкенд systemd не выставлен по умолчанию и джейл читает файл лога. Это не ошибка, а подтверждение того, что фильтра журнала попросту нет. Только с backend = systemd в вашем jail.local на этих системах он становится действующим.

Отдельные условия разделены знаком + и связываются по «или». _SYSTEMD_UNIT=ssh.service + _COMM=sshd подходит, значит, ко всему из юнита ssh.service или к любому процессу с именем sshd. Для встречной проверки в журнале возьмите ровно то имя юнита, которое выдала команда выше:

journalctl -u ssh --no-pager -n 50
journalctl _COMM=sshd --no-pager -n 50

На Ubuntu 24.04 первая строка выглядит соответственно как journalctl -u sshd --no-pager -n 50. Кто возьмёт здесь неверное имя, увидит пустой вывод и будет потом искать не с того конца.

Если здесь появляются строки с Failed password for invalid user, а fail2ban при этом ничего не считает, дело в выборке из журнала. Это становится актуальным, если вы используете OpenSSH из backports или переходите на активацию через сокет. Начиная с OpenSSH 9.8 дочерний процесс называется sshd-session, а не sshd, а при активации через сокет юнит уже не называется ssh.service. Тогда допишите в свой jail.local (на Ubuntu 24.04 с _SYSTEMD_UNIT=sshd.service):

[sshd]
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd + _COMM=sshd-session

Третья проверка самая жёсткая. Заблокируйте вручную адрес из диапазона, зарезервированного для документации, и посмотрите, доходит ли он до файрвола:

fail2ban-client set sshd banip 203.0.113.10

Команда отвечает 1, то есть одним заблокированным адресом. Каким инструментом вы подтвердите эту блокировку, зависит от действующего banaction, а он по умолчанию разный в разных дистрибутивах:

Системаbanaction с заводаПроверка активной блокировки
Debian 13nftablesnft list table inet f2b-table
Ubuntu 24.04nftablesnft list table inet f2b-table
Debian 12iptables-multiportiptables -n -L f2b-sshd
Ubuntu 22.04iptables-multiportiptables -n -L f2b-sshd

Более старый Debian 11 ведёт себя при этом как Debian 12. Откуда берётся разница, видно в /etc/fail2ban/jail.d/defaults-debian.conf: только Debian 13 и Ubuntu 24.04 приносят там banaction = nftables, остальные остаются при значении iptables-multiport из jail.conf. Кто не хочет держать это в голове для каждого сервера отдельно, тот прописывает banaction = nftables в собственный jail.local, как в примере выше, и потом везде проверяет через nft.

При действии nftables появляется таблица f2b-table в семействе inet, в ней цепочка f2b-chain с приоритетом filter - 1 и набор с именем addr-set-sshd. Там и должен стоять элемент 203.0.113.10. Цепочки f2b-sshd на этих системах не существует, iptables -n -L f2b-sshd отвечает там No chain/target/match by that name. И наоборот, nft list table inet f2b-table на Debian 12 и Ubuntu 22.04 сообщает Error: No such file or directory, пока там работает iptables-multiport. Оба сообщения не доказывают сломанную конфигурацию, они говорят лишь о неверной команде для выбранного действия.

При действии iptables адрес 203.0.113.10 должен соответственно оказаться в цепочке f2b-sshd. Только когда адрес появляется в файрволе, цепочка от строки лога до отброшенного пакета замкнута. Прибраться за собой не забудьте.

Как снять блокировку

Освободить один адрес из конкретного джейла:

fail2ban-client set sshd unbanip 203.0.113.10

Адрес сразу из всех джейлов:

fail2ban-client unban 203.0.113.10

И в экстренном случае всё в ноль:

fail2ban-client unban --all

Последняя команда — это то, что вам нужно, если вы закрыли доступ самому себе и сидите за аварийной консолью сервера. Она снимает все блокировки без перезапуска службы. Если до сервера уже совсем не достучаться, поможет только путь через консоль в личном кабинете. На KVM root-серверах и выделенных серверах KernelHost вы получаете там сессию VNC, которая работает независимо от SSH. Это же и причина, по которой bantime = -1 не стоит ставить на сервере без второго пути доступа.

Блокировка, которую вы сняли, немедленно вернётся, если неудачные попытки в текущем окне ещё учитываются. Поэтому unban снимает заодно и связанные с адресом неудачные попытки. Кто должен оставаться свободным постоянно, тому место в ignoreip, а следом fail2ban-client reload.

Взаимодействие с UFW и nftables

Здесь возникает ущерб, который заметить труднее всего, ведь fail2ban бодро продолжает сообщать о блокировках, пока пакеты идут насквозь.

UFW — это фронтенд для iptables. При запуске или перечитывании он заново пишет таблицу filter через iptables-restore. При этом созданные fail2ban цепочки исчезают без замены, а сам fail2ban их обратно не создаёт. Поэтому после каждого ufw enable, ufw disable или ufw reload действует правило: systemctl restart fail2ban. До этого момента в fail2ban-client status sshd будет длинный список заблокированных адресов, из которых на деле не блокируется ни один.

Чище будет позволить fail2ban блокировать напрямую через UFW. Действие лежит в /etc/fail2ban/action.d/ufw.conf и входит во все четыре дистрибутива. В jail.local:

[DEFAULT]
banaction = ufw
banaction_allports = ufw

Тогда блокировки попадают правилами в сам UFW и переживают его перезагрузку. Проверка:

ufw status numbered

Заблокированные адреса появляются там как DENY IN в самом верху списка. Важно, чтобы правила блокировки стояли перед вашим разрешением для порта 22, иначе первым сработает разрешение. Поэтому штатное действие вставляет их в начало через insert.

У тех, кто не использует UFW и вместо этого ведёт собственный набор правил в /etc/nftables.conf, есть родственная проблема. Строка flush ruleset в начале этого файла, которую предлагает стандартный шаблон, при перечитывании удаляет и f2b-table. Либо вы отказываетесь от глобального flush и очищаете только свою таблицу, либо привязываете перезапуск fail2ban к службе nftables. Преимущество собственной таблицы fail2ban в том, что с приоритетом filter - 1 она срабатывает раньше обычной таблицы filter, а значит и раньше правил из iptables-nft.

Не стоит смешивать banaction = nftables и собственный набор правил iptables в расчёте на то, что iptables -L покажет блокировки. Оба пути работают параллельно, но каждый инструмент показывает только свой набор правил. Проверяйте всегда тем инструментом, который соответствует настроенному banaction.

Сообщения об ошибках дословно и что за ними стоит

Логи находятся, в зависимости от системы, в двух местах:

journalctl -u fail2ban --no-pager -n 100
tail -n 100 /var/log/fail2ban.log

Failed during configuration: Have not found any log file for sshd jail

Фирменное сообщение Debian 12. Джейл работает с backend = auto, ищет /var/log/auth.log, а файла нет, потому что rsyslog не установлен и всё попадает в журнал. Служба вообще не стартует, systemctl status fail2ban показывает Failed with result 'exit-code'. Два решения: либо apt install -y rsyslog и один перезапуск, либо строка backend = systemd в jail.local под [sshd]. Второй путь современнее, но требует следующего пакета.

Backend 'systemd' failed to initialize due to No module named 'systemd'

Вы задали backend = systemd, но не хватает привязки Python к журналу. На Debian 12 python3-systemd только рекомендован и при установке без рекомендованных пакетов не подтягивается. Решение:

apt install -y python3-systemd
systemctl restart fail2ban

Failed to access socket path: /var/run/fail2ban/fail2ban.sock. Is fail2ban running?

Клиент не достучался до сервера. В подавляющем большинстве случаев служба просто не работает, потому что упала на ошибке конфигурации. Сначала читайте systemctl status fail2ban, потом журнал. Реже после жёсткого обрыва остаётся осиротевший файл сокета, и тогда при запуске сервер сообщает Server already running. В этом случае удалите файл сокета и запустите заново.

NOK: ('sshd',)

Ответ на fail2ban-client status sshd, когда такого джейла во время работы нет. Либо не хватает enabled = true, либо вы опечатались в имени, либо синтаксическая ошибка выше в jail.local проглотила эту секцию. Какие джейлы действительно работают, показывает fail2ban-client status. А что fail2ban собрал из всех файлов, показывает выгрузка конфигурации:

fail2ban-client -d

Error banning и iptables not found

Джейл считает корректно, но блокировка спотыкается о действие. Типично для минимальных установок Debian 12, где присутствует только nftables, тогда как по умолчанию задано iptables-multiport. Либо доставьте пакет через apt install -y iptables, либо переключитесь в jail.local на banaction = nftables. Второй путь — тот, которым Debian 13 и Ubuntu 24.04 и так идут сами.

Если ничего не помогает: упорядоченный откат

Поскольку все изменения лежат в jail.local, путь назад короткий. Отложить файл в сторону, перезапустить службу, и вы снова при настройках пакета:

mv /etc/fail2ban/jail.local /root/jail.local.bak
systemctl restart fail2ban
fail2ban-client status

Если так всё работает, ошибка сидит в вашей конфигурации, и вы возвращаете блоки по одному. Если и так не работает, дело в окружении, то есть в отсутствующем источнике логов или в отсутствующем инструменте файрвола. Именно поэтому jail.conf должен оставаться нетронутым: этот откат за десять секунд возможен только до тех пор, пока файл пакета находится в исходном состоянии.

Напоследок оценка места. fail2ban снижает шум в логах и останавливает медленные повторяющиеся попытки входа. Серьёзная атака из крупного ботнета использует каждый адрес лишь однажды и проходит мимо любого ограничения частоты. Более действенным шагом против подбора паролей по SSH остаётся отключение входа по паролю в пользу ключей. А против объёмных атак в любом случае помогает только фильтрация в сети, у нас это 3,2 Тбит/с фильтрации Arbor в реальном времени прямо в дата-центре во Франкфурте-на-Майне и до 17 Тбит/с глобальной мощности фильтрации в тарифах Professional. fail2ban — это слой ниже, на самом хосте, и там он надёжно выполняет свою задачу, как только сходятся три вещи: правильный источник логов, правильное действие файрвола и jail.local, который вы в случае сомнений уберёте одним движением.

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

Действительно ли нужно оставлять jail.conf нетронутым?
Да. Файл принадлежит пакету и может быть заменён при обновлениях. Все изменения относятся в /etc/fail2ban/jail.local. Этот файл читается после jail.conf и после jail.d/*.conf, а потому перекрывает и настройки из defaults-debian.conf.
Почему fail2ban на Debian 12 не стартует после установки?
Потому что sshd-джейл работает с backend = auto и ожидает /var/log/auth.log, а без установленного rsyslog этого файла не существует. В журнале тогда стоит «Failed during configuration: Have not found any log file for sshd jail». Решение: либо установить rsyslog, либо задать backend = systemd и доустановить python3-systemd.
Что именно означает backend = auto?
auto по очереди пробует pyinotify и polling. Оба способа работают с файлами. auto никогда не выбирает журнал systemd. Кто хочет разбирать журнал, должен явно задать backend = systemd.
Как снять блокировку?
По одному адресу командой fail2ban-client set sshd unbanip IP-адрес, сразу во всех джейлах через fail2ban-client unban IP-адрес, а в экстренном случае всё разом через fail2ban-client unban --all. Кто должен оставаться свободным постоянно, тому место в ignoreip, а следом fail2ban-client reload.
Почему fail2ban блокирует, а атакующий всё равно проходит?
Чаще всего UFW тем временем заново переписал таблицу filter через iptables-restore и удалил при этом цепочки fail2ban. После каждого ufw enable, disable или reload fail2ban нужно перезапускать. Чище вариант banaction = ufw, тогда блокировки живут в самом UFW.
По чему видно, что fail2ban действительно работает?
Не по тому, что служба запущена. Проверьте через fail2ban-regex, попадает ли фильтр в строки лога, и заблокируйте для пробы вручную адрес из 203.0.113.0/24. После этого он должен появиться в файрволе, причём соответственно настроенному banaction: на Debian 13 и Ubuntu 24.04 через nft list table inet f2b-table в наборе addr-set-sshd, на Debian 12 и Ubuntu 22.04 через iptables -n -L f2b-sshd в одноимённой цепочке. Цепочки f2b-sshd на системах с nftables не существует.
Заменяет ли fail2ban защиту от DDoS?
Нет. fail2ban работает на хосте против повторяющихся попыток входа с отдельных адресов. Объёмные атаки нужно фильтровать в сети, до того как они дойдут до сервера.

fail2ban SSH Безопасность серверов Брутфорс Debian Ubuntu nftables UFW