SMTP AUTH только поверх TLS: жёсткая настройка submission и лимиты через postfwd
Порт 25 — это транспорт между серверами, а не форма логина. Разносим приём MTA и клиентский submission, делаем AUTH невозможным без TLS через smtpd_tls_auth_only и закрываем компрометацию учёток нативными лимитами Postfix и политиками postfwd. Только конфиги, которые копируются в прод.
EvilMail Team25 июля 2026 г.14 мин чтения
Порт 25 не для людей: где утекает пароль
На порт 25 стучатся чужие MTA, чтобы отдать вам почту. Живой пользователь с логином и паролем там не появляется никогда. Тем не менее на тысячах инсталляций Postfix smtpd_sasl_auth_enable включён глобально в main.cf, а значит SASL-механизмы предлагаются на всех сервисах разом — включая 25/tcp, где TLS всего лишь оппортунистический. Сервер честно отдаёт 250-AUTH PLAIN LOGIN в ответ на EHLOдо всякого шифрования. Клиент, настроенный чуть криво (или библиотека, которая «пробует 25-й, раз он открыт»), отправляет AUTH PLAIN в base64 по голому TCP. Пароль снимается пассивным сниффером в кафе одной командой tcpdump.
Проблема не в самом AUTH, а в том, что его предлагают там, где нет гарантированного канала. Правильная модель — три физически разных интерфейса с разными правилами, а не один «универсальный» smtpd, который пытается обслужить и MTA-релей, и почтовый клиент. Разнесём двери, сделаем AUTH невозможным без установленного TLS, а потом закроем второй фронт: украденную учётку, из-под которой уходит спам.
Три двери: 25, 587 и 465 — и почему их нельзя путать
Разница между портами не косметическая — она определяет весь набор правил:
25/tcp — relay между почтовыми серверами. TLS оппортунистический (may), потому что заставить весь интернет шифроваться нельзя: иначе половина легитимной почты отвалится. AUTH здесь не нужен вообще — чужие MTA не аутентифицируются, они просто доставляют письмо вашему домену. Единственная защита от опен-релея — reject_unauth_destination.
587/tcp submission — дверь для ваших клиентов. STARTTLS обязателен, AUTH обязателен, без валидного sasl_username письмо никуда не уходит.
465/tcp submissions — implicit TLS. Соединение шифруется на уровне сокета, до первой SMTP-команды. RFC 8314 (2018) реабилитировал этот порт после многолетнего статуса «deprecated» и прямо рекомендует его как основной для почтовых клиентов: implicit TLS не оставляет окна для STARTTLS-stripping атаки.
AUTH только поверх TLS: smtpd_tls_auth_only
Ключевой параметр — smtpd_tls_auth_only = yes. С ним Postfix физически не включает 250-AUTH в ответ на EHLO, пока клиент не выполнит STARTTLS. Механизмы SASL просто отсутствуют в списке возможностей до установки TLS-канала. Даже если где-то по недосмотру smtpd_sasl_auth_enable=yes протёк на 25-й порт, снять пароль в открытом виде уже не выйдет — предлагать нечего.
Держим глобальный дефолт в main.cf строгим, а AUTH включаем точечно через -o в master.cf:
ini
# main.cf — глобальные дефолты
smtpd_sasl_auth_enable = no
smtpd_tls_auth_only = yes
smtpd_tls_security_level = may # порт 25: оппортунистический TLS
# запрет древних протоколов на обоих направлениях
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_ciphers = high
tls_preempt_cipherlist = yes
TLSv1.2 — практический минимум в 2026, TLSv1.3 предпочтителен. tls_preempt_cipherlist = yes заставляет сервер навязывать свой порядок шифров, а не доверять выбору клиента.
Сами клиентские сервисы описываем в master.cf. Submission на 587 со STARTTLS:
Обратите внимание: smtpd_tls_security_level=encrypt на клиентских портах — это не may. Клиент без TLS туда просто не зайдёт. На 25-м оставляем may, иначе поломаем входящую почту от серверов без TLS.
Первая линия — нативные лимиты Postfix (anvil)
Демон anvil встроен в Postfix и собирает статистику по сессиям. Он даёт грубые, но бесплатные лимиты, которых хватает против тупого брутфорса и лавины соединений. Вешаем их на submission-инстанс:
ini
smtpd_client_connection_rate_limit = 30 # новых коннектов с одного IP за интервал
smtpd_client_message_rate_limit = 100 # сообщений с одного IP за интервал
smtpd_client_connection_count_limit = 20 # одновременных сессий с одного IP
smtpd_client_recipient_rate_limit = 200 # получателей с одного IP за интервал
anvil_rate_time_unit = 60s
Критично понимать ограничение: anvil считает по `client_address`, а не по `sasl_username`. За одним корпоративным NAT сидят сотни легитимных пользователей с общим внешним IP — в лимит они упрутся все вместе. А злоумышленник с ботнета обходит anvil, размазав отправку по тысяче адресов. Поэтому anvil — защита от шума на сетевом уровне, но не инструмент квотирования по учётке. Per-user квота требует policy-демона.
policyd / postfwd: политики отправки и суточные квоты
Здесь нужна честность. Классический policyd v2 (cluebringer) — тот самый, что многие до сих пор гуглят — не обновлялся примерно с 2015 года. MySQL-схема, PHP-морда и Perl-демон сгнили, на современном стеке он собирается с бубном и течёт. В 2026 практический стандарт per-user rate limiting в Postfix — это postfwd (postfix firewall daemon): один Perl-файл, конфиг без базы данных, живой и поддерживаемый.
Работает через policy delegation: в списке ограничений submission-инстанса Postfix встречает check_policy_service, открывает TCP-сокет к демону, отдаёт атрибуты запроса и ждёт вердикт. Критичен порядок: check_policy_service должен стоять передpermit_sasl_authenticated. Postfix обходит список ограничений до первого вердикта permit/reject; если permit_sasl_authenticated отработает раньше, обход прекратится — и до политики дело не дойдёт. Именно тот аутентифицированный отправитель, которого мы хотим квотировать, проскочит без проверки. Поэтому в submission-инстансе master.cf меняем строку recipient_restrictions:
ini
# master.cf, submission — политика ПЕРЕД permit_sasl_authenticated
-o smtpd_recipient_restrictions=check_policy_service inet:127.0.0.1:10040,permit_sasl_authenticated,reject
# main.cf — сколько ждать ответ демона, прежде чем отпустить сессию
policy_time_limit = 3600
sasl_username попадёт в запрос независимо от позиции в списке: RCPT идёт уже после AUTH, и Postfix передаёт демону все известные на этот момент атрибуты. Позиция важна только из-за короткого замыкания на permit/reject.
Конфиг postfwd с суточной квотой на письма и получателей:
encryption_protocol, encryption_cipher — можно строить правила «требую TLSv1.3 для этого класса отправителей»
Возможные ответы демона: DUNNO (не моё решение, идём дальше по цепочке), OK, DEFER/DEFER_IF_PERMIT (временный отказ), REJECT, HOLD (в карантин), DISCARD (тихо проглотить). Обмен можно эмулировать вручную через netcat — отдать блок атрибутов и посмотреть вердикт:
Фильтр цепляется за строки SASL LOGIN authentication failed. Пять неудач за 10 минут — час бана.
postfwd ловит аномалию уже после успешного логина: резкий всплеск разных получателей — классический признак угнанной учётки, из которой погнали спам-рассылку. Для подозрительного потока правильнее не REJECT, а HOLD: письма уходят в hold-очередь, откуда их можно разобрать руками:
Что лежит в карантине, видно в postqueue -p — held-письма помечены !. Дальше — либо освободить всё (postsuper -H ALL), либо удалить рассылку целиком (postsuper -d ALL hold). Грепнуть логи по факту компрометации:
smtpd_sasl_auth_enable = no в main.cf — AUTH включается только через -o на 587/465
smtpd_tls_auth_only = yes глобально — механизмы AUTH скрыты до STARTTLS
порт 25 (smtp inet) без smtpd_sasl_auth_enable=yes и с security_level = may
587 с security_level=encrypt, 465 с tls_wrappermode=yes
запрещены SSLv2/3, TLSv1.0/1.1; mandatory_ciphers = high
нативные лимиты anvil выставлены на submission-инстансе
check_policy_service стоит передpermit_sasl_authenticated
postfwd запущен с суточной квотой по sasl_username
джейл postfix-sasl в fail2ban активен на всех клиентских портах
правило HOLD на аномальный всплеск получателей
Проверяем боем. Баннер EHLO не должен содержать AUTH до STARTTLS:
bash
openssl s_client -starttls smtp -connect mail.evilmail.pro:587 -crlf
# в первом EHLO-ответе строки 250-AUTH быть НЕ должно
AUTH без TLS не должен предлагаться, с TLS — работать:
bash
# без TLS: AUTH не анонсируется, swaks завершится «no supported auth type»;
# принудительный AUTH получит 530 5.7.0 Must issue a STARTTLS command first
swaks --server mail.evilmail.pro:587 --auth LOGIN --auth-user [email protected]
# с TLS: аутентификация проходит
swaks --server mail.evilmail.pro:587 --auth LOGIN --auth-user [email protected] -tls
Сверка конфигов и счётчиков политики:
bash
postconf -Mf # форматированный master.cf, проверить -o
postconf -n | grep -E 'tls_auth_only|sasl_auth'
postfwd --summary # сколько раз сработали правила квот
Большую часть задач по защите submission закрывают встроенные счётчики Postfix и один параметр smtpd_tls_auth_only. Внешний policy-демон нужен ровно там, где anvil бессилен — per-user квоты и детект компрометации по sasl_username. Ставьте postfwd, а не мёртвый cluebringer, и не пускайте AUTH на порт, где нет гарантированного шифра.