Если у вас свой почтовый сервер на Postfix и Dovecot, то вы почти наверняка видите в логах попытки bruteforce: сотни неудачных входов на IMAP/POP3 и бесконечные пробные авторизации на submission/SMTPS. fail2ban отлично гасит такие волны, но у почты есть риск «перебанить лишнее»: слишком агрессивные фильтры могут блокировать внешние MX-сервера или клиентов с нестабильным соединением. Ниже — практическая схема точных jails и фильтров, которые ловят только реальный bruteforce авторизации и минимизируют ложные срабатывания.
Зачем разделять jails для почты
Почтовая подсистема состоит из разных ролей и портов:
- Postfix: публичный SMTP на 25 порту (входящая почта), а также клиентские submission/SMTPS на 587/465 для аутентифицированной отправки.
- Dovecot: IMAP/POP3 (обычно 143/110) и их TLS-варианты 993/995 для чтения почты пользователями.
Ошибка многих конфигураций — использовать один общий jail для всего Postfix и банить по любому reject или «lost connection». Это приводит к блокировке легитимных внешних хостов, пытавшихся доставить письмо (в это время MTA вполне корректно отвергает спам). Правильный подход — бить по точным признакам неудачной аутентификации и разделять jails:
- postfix-submission — неудачные SASL-авторизации на 587/465.
- dovecot-auth — неудачные логины на IMAP/POP3.
- recidive-mail — «долгоиграющий» бан для IP, который системно попадает под баны снова и снова.
Идея простая: не баним за отказ в приёме письма на 25 порту (там много легитимного шума), а баним только за постоянные провалы авторизации.
Логи и backend: systemd-journald vs файлы
Современные дистрибутивы всё чаще хранят почтовые логи в journald. Для fail2ban это означает удобный backend = systemd и возможность точно фильтровать по SYSLOG_IDENTIFIER (например, postfix/submission/smtpd) без хрупких регулярных выражений таймстампов.
Если у вас классическая схема с rsyslog, логи обычно в /var/log/mail.log (Debian/Ubuntu) или /var/log/maillog (RHEL/AlmaLinux/Rocky). Оба варианта рабочие, но с journald проще сделать «ювелирные» jails.
Установка и базовая структура
На большинстве систем установка стандартная. Примеры для Debian/Ubuntu и RHEL-подобных:
apt update
apt install fail2ban
yum install fail2ban
dnf install fail2ban
systemctl enable --now fail2ban
Конфигурацию удобнее вести в /etc/fail2ban/jail.d/ своими файлами, а фильтры — в /etc/fail2ban/filter.d/. Так вы не потеряете настройки при обновлении пакета. Если вы поднимаете почтовик на отдельном VDS, сразу продумайте файрвол на уровне хоста и провайдера.

Точный jail для Postfix: только submission/SMTPS
Наша цель — реагировать только на неудачные SASL-логины клиентов, а не на все события Postfix. Это достигается двумя вещами:
- Фильтр, ловящий именно «SASL authentication failed»;
journalmatch, ограничивающий источник логов процессамиpostfix/submission/smtpdиpostfix/smtps/smtpd.
Создайте фильтр /etc/fail2ban/filter.d/postfix-submission.conf:
[Definition]
failregex = ^(?:.+ )?postfix/(?:submission|smtps)/smtpd\[\d+\]: (?:warning|info): .*\[<HOST>\]: SASL (?:\S+ )?authentication (?:failed|failure): .*$
ignoreregex =
Комментарий по регулярке: она не пытается парсить таймстампы и хостнеймы (форматы отличаются между journald и rsyslog), а жёстко привязана к идентификаторам процессов Postfix для клиентских портов и к тексту про ошибку SASL. Токен <HOST> обязателен — так fail2ban извлекает IP.
Создайте jail /etc/fail2ban/jail.d/mail.conf с секцией для Postfix:
[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports
backend = systemd
[postfix-submission]
enabled = true
filter = postfix-submission
journalmatch = _SYSTEMD_UNIT=postfix.service + SYSLOG_IDENTIFIER=postfix/submission/smtpd
journalmatch = _SYSTEMD_UNIT=postfix.service + SYSLOG_IDENTIFIER=postfix/smtps/smtpd
port = 587,465,submission,smtps
findtime = 15m
maxretry = 5
bantime = 1h
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 12h
ignoreip = 127.0.0.0/8 ::1
Мы используем два journalmatch, чтобы ловить и submission (587), и smtps (465). На системах без journald замените backend на auto или polling и укажите logpath:
[postfix-submission]
backend = auto
logpath = /var/log/mail.log
В этом случае фильтр остаётся тем же, так как он проверяет наличие postfix/(submission|smtps)/smtpd прямо в тексте строки.
Точный jail для Dovecot: только провалы логина
Для Dovecot избегаем всего, что не связано с аутентификацией. Типичные нужные сообщения: Disconnected (auth failed), Aborted login (auth failed), Failed login. Сделаем фильтр /etc/fail2ban/filter.d/dovecot-auth.conf:
[Definition]
failregex = ^(?:.+ )?dovecot: (?:imap|pop3)-login: (?:Aborted login|Disconnected \(auth failed.*\)|Failed login).*rip=<HOST>.*$
ignoreregex = ^(?:.+ )?dovecot: (?:imap|pop3)-login: (?:Disconnected|Logged out).*$
И соответствующий jail:
[dovecot-auth]
enabled = true
filter = dovecot-auth
backend = systemd
journalmatch = _SYSTEMD_UNIT=dovecot.service
port = imap,imaps,pop3,pop3s,143,993,110,995
findtime = 15m
maxretry = 7
bantime = 1h
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 12h
Здесь maxretry можно сделать выше, чем у submission: мобильные клиенты чаще «мазают» паролем после смены или кэшированных автонастроек.
Recidive: бан на сутки для настойчивых
Даже с мягкими порогами полезно иметь «повторник»: если IP несколько раз попадал под бан за последние сутки — выносим длительный приговор, причём по всем портам. Это снижает шум и рескейлинг атак.
[recidive-mail]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
findtime = 1d
maxretry = 5
bantime = 24h
banaction = nftables-allports
Этот jail не зависит от отдельных сервисов: он анализирует сам лог fail2ban.
Тестирование фильтров и отладка
Перед запуском всегда проверяйте фильтр на живых логах:
fail2ban-regex /var/log/mail.log /etc/fail2ban/filter.d/postfix-submission.conf
fail2ban-regex /var/log/mail.log /etc/fail2ban/filter.d/dovecot-auth.conf
Для journald удобно сделать выгрузку примеров:
journalctl -u postfix -n 200 | tee /tmp/postfix.log
journalctl -u dovecot -n 200 | tee /tmp/dovecot.log
fail2ban-regex /tmp/postfix.log /etc/fail2ban/filter.d/postfix-submission.conf
fail2ban-regex /tmp/dovecot.log /etc/fail2ban/filter.d/dovecot-auth.conf
После включения jails:
systemctl reload fail2ban
fail2ban-client status
fail2ban-client status postfix-submission
fail2ban-client status dovecot-auth
Разовый добан/разбан для диагностики:
fail2ban-client set postfix-submission banip 203.0.113.10
fail2ban-client set dovecot-auth unbanip 203.0.113.10

Пороговые значения без ложнопозитивов
Рекомендуемые стартовые настройки:
findtime = 15m,maxretry = 5для submission/SMTPS,maxretry = 7для Dovecot.bantime = 1hсbantime.increment = true,bantime.factor = 2,bantime.maxtime = 12h.ignoreipдля локалхоста, VPN-админок и мониторингов.
Если среди пользователей есть абоненты за общим NAT (провайдеры, офисы), поднимите maxretry на Dovecot, иначе можно случайно банить целые офисные подсети.
Чего не должно быть в фильтрах для почты
Чтобы не банить внешние MX и нормальные почтовые диалоги, избегайте таких признаков:
- Любые NOQUEUE: reject без явной привязки к SASL-логину — это нормальная антиспамная работа вашего MTA.
- lost connection, timeout и другие сетевые особенности — у клиентов бывает плохой сигнал или TCP сбои.
- Срабатывания на одноразовые TLS/SMTP ошибки, не связанные с авторизацией.
Простое правило: баним только за повторные провалы аутентификации пользователя, а не за «плохие письма».
IPv6 и выбор banaction
Если у вас включён IPv6, используйте nftables-действия (nftables-multiport/nftables-allports) — они одинаково хорошо работают для IPv4 и IPv6. Для старых систем с iptables проверьте, что выбранный banaction поддерживает оба стека либо добавьте отдельное действие для v6.
Жизненный цикл: уведомления и отчётность
Для оперативного контроля можно включить email-уведомления в действиях fail2ban. Но помните, что на почтовом сервере важно не захламлять себя письмами о каждом бане. Разумнее уведомлять только о recidive или о количестве заблокированных за период в мониторинге.
Производительность и стабильность
Несколько практик для серверов с высокой нагрузкой:
- backend = systemd: меньше I/O, проще фильтрация по источнику.
- Не используйте чрезмерно тяжёлые регулярки с жадными квантификаторами на всю строку — ограничивайте контекст.
- Не делайте десятки перекрывающихся jails для одного и того же события — лучше один точный.
Для общей жёсткости периметра посмотрите базовую памятку по защите хоста и файрвола: безопасность VDS: SSH и firewall.
Типичные события в логах (для самопроверки)
Postfix (submission/SMTPS):
Jul 20 10:15:22 mx1 postfix/submission/smtpd[1234]: warning: unknown[198.51.100.20]: SASL LOGIN authentication failed: authentication failure
Jul 20 10:15:26 mx1 postfix/smtps/smtpd[1235]: warning: client.example[198.51.100.21]: SASL PLAIN authentication failed: UGFzc3dvcmQ6
Dovecot:
Jul 20 10:16:03 mx1 dovecot: imap-login: Disconnected (auth failed, 3 attempts): user=<user@example.com>, method=PLAIN, rip=203.0.113.4, lip=192.0.2.10, TLS
Jul 20 10:16:10 mx1 dovecot: pop3-login: Aborted login (auth failed, 2 attempts): user=<>, rip=203.0.113.5, lip=192.0.2.10
Если ваши строки заметно отличаются (кастомный формат логов), подправьте регулярные выражения точечно: обычно достаточно изменить часть между префиксом сервиса и полем rip=<HOST> или хвост после SASL ... failed. Для бесшовных переносов ящиков пригодится материал: миграция IMAP без простоя через imapsync.
Экстренные ситуации
Если легитимный клиент попал под бан:
- Разбаньте IP:
fail2ban-client set dovecot-auth unbanip 203.0.113.10. - Добавьте в
ignoreip, если это постоянный «шумный» адрес мониторинга/шлюза. - Проверьте, нет ли опечатки в логине у пользователя или устаревшего пароля в одном из его устройств.
Итог
Точные jails для почты — это прежде всего дисциплина: ловим только повторные провалы авторизации и жёстко ограничиваем источники логов. Postfix — нацеливаемся на submission/SMTPS, Dovecot — только на IMAP/POP3-логины. Добавляем «повторник» на сутки и не забываем тестировать фильтры на реальных логах. Такая схема надёжно останавливает bruteforce и почти не создаёт ложных блокировок, сохраняя доставляемость почты и спокойствие админа.


