Почему триплетный greylisting теряет письма крупных отправителей
Сценарий, который вы уже видели в логах. Клиент ждёт письмо-подтверждение из Salesforce, оно уходит через Amazon SES. На вашем MX сидит postgrey, для этого триплета (client_ip, sender, recipient) записи нет, и Postfix отвечает:
450 4.2.0 <[email protected]>: Recipient address rejected: Greylisted, please try again laterОтправляющий MTA честно ставит письмо в очередь и ретраит через 12 минут — но уже с соседнего IP. 10.0.4.17 было в первой попытке, 10.0.7.203 во второй, и оба принадлежат одному пулу SES размером с целый /19. Триплет не совпал, postgrey снова отвечает 450, счётчик задержки обнуляется. Письмо, которое человек ждёт «мгновенно», болтается в очереди отправителя.
Тут три отдельные патологии, и их важно различать:
- Налог задержки на 100% первых контактов. postgrey по определению греет каждого нового отправителя. Дефолт
--delay=300— это пять минут задержки на любой первый разговор с новым корреспондентом, даже идеально чистым. - Ротация пулов ломает совпадение триплета. Крупные ESP — SES, SendGrid, Google, Outlook — отправляют с сотен исходящих адресов. Второй ретрай почти гарантированно приходит не с того IP, который вы уже «видели». Для postgrey это новый триплет, отсчёт начинается заново.
- Bulk-MTA, которые не ретраят вовсе. Часть транзакционных и рассыльных систем на
4xxпросто дропает сообщение — для них временный отказ неотличим от постоянного. Письмо теряется навсегда, и вы об этом не узнаете: в логе будет аккуратная строчка «greylisted», как будто всё в порядке.
Поведение ретраев различается и по самим MTA. Postfix стартует с minimal_backoff_time = 300 секунд, Exim обычно ждёт около 15 минут между попытками, ряд ESP использует экспоненциальный backoff и растягивает вторую попытку до 30–60 минут. То есть «задержка 5 минут» на практике легко превращается в полчаса тишины на стороне получателя.
Два движка greylisting: IP-триплет postgrey против score-band rspamd
Разница не в настройках, а в архитектуре — где именно в конвейере принимается решение и по какому ключу.
postgrey — это policy-демон на 127.0.0.1:10023, к которому Postfix обращается в smtpd_recipient_restrictions через check_policy_service. Решение принимается до контентной фильтрации, на этапе RCPT TO. Ключ — тот самый триплет (client_ip, envelope_from, envelope_to). Демон ничего не знает о теле письма, о SPF, о DKIM — он греет всё подряд по факту «нового IP-адреса».
Модуль greylist в rspamd работает принципиально иначе. Это пост-фильтр: он срабатывает уже после того, как письмо просканировано и получило итоговый score. Ключ — не IP, а хеш тела письма (первые max_data_len байт) плюс список получателей, и хранится он в Redis. Из этого следуют два вывода:
- Ротация пула больше не важна. Байт-в-байт тот же ретраенный мейл даёт тот же хеш — неважно, с какого IP из
/19он пришёл. Совпадение находится всегда. - Чистая почта вообще не доходит до greylist. Модуль включается, только если score попал в узкую полосу между порогом действия
greylistи порогомreject. Письмо с валидными SPF/DKIM/DMARC уходит в минус по score, не достаёт до нижней границы полосы и проходит без единой секунды задержки.
Базовый конфиг: /etc/rspamd/local.d/greylist.conf
Ничего не правьте в /etc/rspamd/modules.d/ — всё в local.d. Минимально осмысленный рабочий блок:
# /etc/rspamd/local.d/greylist.conf
servers = "127.0.0.1:6379"; # Redis, где живут метазаписи
expire = 1d; # TTL записи после первого контакта
timeout = 5min; # окно, в течение которого примем ретрай
key_prefix = "rg"; # префикс ключей в Redis
max_data_len = 10k; # сколько байт тела берём в хеш
ipv4_mask = 19; # группировка meta по блоку /19
ipv6_mask = 64;
message = "Greylisted, please try again later";
symbol = "GREYLIST";Ключевой параметр — timeout. Это окно, в течение которого повторная попытка будет принята. Меньше 1 минуты — рискуете отбить агрессивных ботов вместе с быстрыми легитимными ретраями; больше 5 минут — копите задержку без пользы, потому что честный MTA всё равно вернётся в свой интервал backoff.
Сам факт greylisting rspamd отдаёт как soft reject — это SMTP-код 451, временный отказ. Отдельно его включать в конфиге не нужно, но убедитесь, что дальше по цепочке (milter, транспорт, релей) 4xx нигде не переписывается в 5xx, иначе временный отказ превратится в постоянный и письмо потеряется.
Про маски. ipv4_mask = 19 означает, что метазапись «этот отправитель уже грелся» привязывается не к конкретному /32, а к блоку /19. Это компенсирует ротацию: ретрай с соседнего IP того же провайдера попадает в ту же группу. Не опускайте маску ниже `/16` — сгребёте в одну корзину целого провайдера и дадите спамеру бесплатный проход по чужой прогретой записи.
После правки — перезагрузка и проверка, что конфиг действительно применился:
systemctl reload rspamd
rspamadm configdump greylistГлавный рычаг: score-полоса в actions.conf
Вся «тонкость» настройки живёт не в greylist.conf, а здесь:
# /etc/rspamd/local.d/actions.conf
greylist = 4;
add_header = 6;
reject = 15;Логика по порогам:
- score < 4 — письмо уходит получателю без всякого greylisting;
- score 4–6 — попадает в полосу greylist и греется;
- score 6–15 — помечается как спам (заголовок / папка Junk);
- score > 15 — режется на
reject.
Теперь суть: почему легитимная почта физически не доходит до порога 4. Отрицательные символы честной аутентификации тянут score вниз. R_SPF_ALLOW даёт примерно −0.2, R_DKIM_ALLOW около −1.0, DMARC_POLICY_ALLOW порядка −0.5, плюс ARC_ALLOW и whitelist-модуль для крупных провайдеров (Google, Microsoft, Apple) от −1 до −3. Письмо с валидными SPF+DKIM+DMARC стартует в глубоком минусе и просто не может доползти до нижней границы полосы.
Отсюда практический вывод, который часто понимают наоборот. Поднять порог greylist с 4 до 7 — это не «сделать мягче». Это осознанно решить, какую полосу подозрительности вы готовы задерживать. greylist = 4 — строгий узел, греется всё пограничное. greylist = 6..7 разумно, если у вас много ложных грейлистов от мелких корпоративных доменов, которые вообще не подписывают почту DKIM и потому естественно набирают +1..+2 «ни за что».
Белые списки для пулов крупных ESP
Даже со score-band иногда нужно явно вытащить конкретного отправителя из-под подозрения. В rspamd для этого есть multimap.
# /etc/rspamd/local.d/multimap.conf
WHITELIST_DKIM_ESP {
type = "dkim";
map = "/etc/rspamd/maps.d/dkim_whitelist.map";
score = -2.0;
}# /etc/rspamd/maps.d/dkim_whitelist.map — по d= домену подписи, не по IP
amazonses.com
sendgrid.net
google.comПредостережение, которое отличает грамотную настройку от опасной: не делайте whitelist по IP shared-пула ESP. IP-адреса SES или SendGrid используют тысячи отправителей, и среди них есть спамеры. Whitelist по IP означает «доверяю всем на этом пуле». Whitelist по DKIM-домену (d=) привязан к конкретному подписанту и куда безопаснее.
Для тех, кто ещё на postgrey, эквивалент — файлы whitelist по PTR:
# /etc/postgrey/whitelist_clients
.google.com
.amazonses.com
sendgrid.net
.mailchimp.com
.protection.outlook.com# /etc/postgrey/whitelist_recipients
postmaster@
abuse@whitelist_recipients для postmaster@ и abuse@ — не роскошь, а требование RFC 5321: эти ящики обязаны принимать почту без искусственных задержек.
Наблюдение и отладка: почему это письмо погрелось
Не гадайте — смотрите. Проверить score и то, сработал ли символ на конкретном .eml:
rspamc symbols < message.eml | grep -E 'GREYLIST|SCORE|DKIM|SPF|DMARC'Посмотреть, что реально лежит в Redis, и сколько живёт запись:
redis-cli --scan --pattern 'rg*'
redis-cli TTL rg_body_<hash>Найти в логе все погретые письма за смену:
grep GREYLIST /var/log/rspamd/rspamd.logКак отличить «погрелось и доставилось» от «отправитель не вернулся»: в первом случае в логе будет пара событий по одному хешу — soft reject, затем через несколько минут no action / доставка. Если второго события нет, а запись в Redis дожила до expire без повторного попадания — отправитель не ретраит, и это ваш кандидат в whitelist.
Метрика для дашборда — доля писем с символом GREYLIST от общего потока. Норма меньше 3–5%. Если греется 15% потока, то либо порог greylist выставлен слишком низко, либо ломается whitelist (например, DKIM не проходит проверку из-за рассинхрона ключей) и легитимная почта валится в полосу.
Чеклист перед продакшеном
- Валидация SPF/DKIM/DMARC на приём работает — без этого отрицательные символы не начисляются и чистая почта попадает в полосу.
- Пороги в
actions.confвыставлены явно (greylist,add_header,reject), а не унаследованы от дефолтов. timeout ≤ 5min— иначе копите задержку впустую.ipv4_maskне ниже/16, чтобы не прогревать спамерам чужие записи.- Whitelist критичных ESP сделан по DKIM-домену, а не по IP shared-пула.
- Redis запущен с
appendonly yes— иначе после рестарта Redis весь поток греется заново. - Мониторинг доли символа GREYLIST настроен, порог тревоги — 5%.
Правильно настроенный greylisting в 2026-м невидим для реальных корреспондентов и жжёт ботам ресурс на ретраи, которых у них нет. Если пользователи жалуются на «пропавшую почту», проблема не в том, что greylisting включён, а в том, что он греет по IP-триплету вместо score-полосы.


