Фильтрация по аутентификации в rspamd: модули DKIM, DMARC и ARC и как они складываются в итоговый score
Аутентификация письма в rspamd — это не «прошло/не прошло», а набор символов с весами, которые суммируются в score и только потом становятся действием. Разбираем механику модулей dkim, dmarc и arc: где ломается SPF на пересылке, почему p=reject сам по себе ничего не реджектит, и как ARC чинит то, что рвут mailing-листы.
EvilMail Team22 июля 2026 г.12 мин чтения
Письмо от банка приходит клиенту не напрямую, а через корпоративный форвардер: адрес envelope-from подменяется на relay, и SPF честно возвращает fail — потому что IP форвардера в SPF-записи банка не значится. Но DKIM-подпись пережила пересылку, тело и заголовок From: никто не тронул, d=bank.example совпал с доменом в From. Вопрос на засыпку: пройдёт ли это письмо DMARC с политикой p=reject, и — что важнее — зарежектит ли его rspamd?
Ответ на первую часть: да, пройдёт, потому что DMARC требует, чтобы прошёл хотя бы один из механизмов (SPF или DKIM) и был выровнен с доменом отправителя. Ответ на вторую почти всегда обескураживает: даже если бы DMARC упал, rspamd по умолчанию не реджектит письмо по факту p=reject. Он всего лишь навешивает символ DMARC_POLICY_REJECT
rspamd: модули DKIM, DMARC, ARC и итоговый score письма — EvilMail Blog
с весом, и этот вес складывается с остальными. Решение принимает арифметика по шкале действий, а не запись в DNS.
Большинство админов настраивают DKIM и DMARC на уровне зоны, видят зелёные галочки в mail-tester и считают дело закрытым. Но реальный вердикт живёт внутри rspamd, в связке модулей spf → dkim → dmarc → arc, где выравнивание, доверие к upstream-подписи и порядок наложения политики значат больше, чем сам факт валидной подписи. Разберём эту механику по слоям.
Как rspamd раскладывает аутентификацию на символы
Ключевая идея, которую нужно усвоить раньше любого конфига: rspamd не оперирует булевыми вердиктами. Каждый модуль по итогам проверки выставляет символ — именованный признак с числовым весом. R_DKIM_ALLOW, R_SPF_FAIL, DMARC_POLICY_REJECT, ARC_ALLOW — это всё символы. Итоговый score письма есть сумма весов всех сработавших символов, и уже он сравнивается с порогами действий.
Модули работают конвейером, и порядок неслучаен. spf и dkim отрабатывают первыми и кладут результаты в контекст. dmarc запускается после них, потому что физически зависит от их вердиктов — ему нужно знать, прошёл ли SPF и совпал ли d= DKIM с From. arc смотрит на цепочку заголовков и может переопределить картину для пересланной почты.
Конфигурация лежит в предсказуемых местах:
/etc/rspamd/local.d/*.conf — ваши дополнения к дефолтным настройкам модулей (мержатся поверх).
/etc/rspamd/override.d/*.conf — жёсткое переопределение, вырезающее дефолт целиком. Нужно редко, но когда нужно — только оно и помогает.
/var/lib/rspamd/dkim/ — приватные ключи DKIM и ARC.
Символы объединяются в группы (groups.conf), у групп есть потолок суммарного веса, чтобы десяток мелких признаков не утащил письмо в reject случайно. Держите в голове эту трёхуровневую модель: символ → группа → итоговый score → действие.
Модуль dkim: проверка входящих и подпись исходящих
Здесь два разных модуля, которые часто путают. dkimпроверяет подписи на входящей почте. dkim_signingподписывает исходящую. Первый включён из коробки, второй нужно настраивать вручную.
Сначала генерируем ключ. Селектор — это просто метка, по которой получатель найдёт публичный ключ в DNS; заводите новый на каждую ротацию (по году удобно):
use_domain = "header" означает, что домен для подписи берётся из заголовка From:, а не из envelope — это то, что нужно для выравнивания с DMARC. allow_hdrfrom_mismatch = false не даёт подписать письмо, где From не совпадает с обслуживаемым доменом (защита от чужих отправок через ваш сервер).
Обратите внимание на масштаб: валидный DKIM даёт всего -0.2. Сам по себе он почти ничего не весит и заведомо не тащит письмо во «входящие». Его настоящая ценность — быть входом для DMARC: только выровненная валидная DKIM-подпись даёт DMARC_POLICY_ALLOW. Про канонизацию: используйте relaxed/relaxed — simple рвётся от малейшего изменения пробелов на транзитных серверах.
Модуль dmarc: выравнивание и превращение политики в символ
DMARC — это не отдельная криптографическая проверка, а логика поверх результатов SPF и DKIM плюс проверка выравнивания. Письмо проходит DMARC, если выполнено хотя бы одно:
SPF вернул passИ домен из MAIL FROM выровнен с доменом в From:, или
DKIM валиден Иd= подписи выровнен с доменом в From:.
Слово «выровнен» здесь ключевое. Организационный домен вычисляется через Public Suffix List: mail.evilmail.pro и evilmail.pro имеют один org-домен. Параметры adkim и aspf задают строгость: s (strict) требует точного совпадения FQDN, r (relaxed, дефолт) — совпадения на уровне org-домена. Именно поэтому пересылка из открытого примера проходит: SPF порвался, но DKIM выжил и выровнен — одного механизма достаточно.
Символы DMARC:
DMARC_POLICY_ALLOW-0.5
DMARC_POLICY_REJECT2.0
DMARC_POLICY_QUARANTINE1.5
DMARC_POLICY_SOFTFAIL0.1
DMARC_NA0 — у домена нет DMARC-записи
DMARC_BAD_POLICY0 — запись есть, но кривая
И вот главный сюрприз, из-за которого ломаются десятки инсталляций: символ `DMARC_POLICY_REJECT` не реджектит письмо. Его вес 2.0, порог reject — 15. Письмо с честным DMARC-провалом наберёт 2.0 плюс сколько-то спам-символов и почти наверняка останется ниже порога reject, попав максимум в add_header. Ваша политика p=reject в DNS объявляет намерение, но не исполняет его. Исполнение живёт в rspamd, и его нужно включать явно (см. раздел про enforcement).
Агрегатор копит счётчики в Redis и раз в сутки шлёт rua-отчёты. Без Redis репортинг молча не работает.
Модуль arc: почему пересылка перестаёт ломать аутентификацию
Вернёмся к сломанному кейсу. При проходе письма через mailing-лист (Mailman дописывает футер и [list] в тему) или через форвардер происходит двойная катастрофа: SPF рвётся, потому что меняется envelope-from, а DKIM рвётся, потому что мутирует тело или сабж. На выходе DMARC у получателя падает — хотя изначально письмо было полностью легитимным.
ARC (Authenticated Received Chain) решает это, фиксируя результат аутентификации на каждом хопе в криптографически подписанной цепочке. Промежуточный сервер, который вот-вот сломает подпись, сначала записывает: «на моём входе SPF и DKIM были pass». Три заголовка на каждый хоп:
ARC-Authentication-Results (AAR) — снимок Authentication-Results на этом узле.
ARC-Message-Signature (AMS) — подпись содержимого, как DKIM.
ARC-Seal (AS) — подпись самой цепочки с полем cv= (chain validation: none / pass / fail).
Инстансы нумеруются i=1, 2, 3.... Принимающий rspamd валидирует всю цепочку и, если она цела, доверяет AAR первого хопа — тому, где DKIM ещё был жив.
Символы валидатора: ARC_ALLOW-1.0 (цепочка цела и содержит успешную аутентификацию), ARC_REJECT2.0, ARC_INVALID1.0, ARC_NA/ARC_DNSFAIL0. Обратите внимание на вес -1.0 — вдвое больше, чем у DMARC_POLICY_ALLOW. Это осознанно: валидный ARC для пересланной почты сильнее склоняет решение в плюс.
Если ваш сервер сам выступает форвардером, включите ARC-подпись в /etc/rspamd/local.d/arc.conf — иначе вы порвёте DMARC для тех, кому пересылаете:
Ключ можно переиспользовать тот же, что для DKIM, — форматы совместимы.
От символов к действию: где реально живёт enforcement
Пороги действий задаются в /etc/rspamd/local.d/actions.conf:
greylist = 4;
add_header = 6;
reject = 15;
Score письма пробегает эту шкалу снизу вверх и останавливается на первом непревышенном пороге. Теперь понятно, почему DMARC_POLICY_REJECT с весом 2.0 бесполезен для жёсткого реджекта: до 15 ему как до луны.
Есть два способа сделать p=reject настоящим reject. Первый — грубо поднять вес символа через groups.conf или метрики. Плохая идея: сломаете пересылку без ARC и словите ложные срабатывания. Второй, правильный — привязать символ к действию напрямую в /etc/rspamd/local.d/force_actions.conf:
force_actions игнорирует арифметику score: символ есть — действие исполняется. Мощно и опасно. Расширяйте выражение, чтобы не реджектить свои же домены и оставить лазейку для валидного ARC:
expression = "DMARC_POLICY_REJECT & !ARC_ALLOW";
Так письмо с провалившимся «голым» DMARC, но с целой ARC-цепочкой пройдёт, а прямой спуфинг вашего домена улетит в reject. Именно это разделение — «объявляем в DNS, исполняем в rspamd» — и есть суть грамотной настройки.
Диагностика: читаем, что произошло
Прогоните конкретное письмо и посмотрите на символы:
bash
rspamc scan message.eml
В выводе (и в заголовке X-Spamd-Result: доставленного письма) вы увидите список сработавших символов с весами и итоговый score. Заголовок Authentication-Results: покажет вердикты spf/dkim/dmarc/arc в человекочитаемом виде. Полезные команды:
rspamadm configtest — проверить синтаксис перед reload
rspamadm configdump dmarc — увидеть эффективный конфиг модуля со всеми мержами
rspamc stat — статистика по действиям и обучению
веб-контроллер на 127.0.0.1:11334 — интерактивный scan с раскладкой символов и весов
Читая цепочку ARC глазами, идите от старшего i= к младшему: cv=pass в самом свежем ARC-Seal означает, что вся цепочка ниже валидна, и AAR с i=1 можно доверять.
Чеклист перед продакшеном
Ключи DKIM/ARC минимум 2048 бит; RSA-1024 уже отбраковывается частью провайдеров.
Селектор в DNS (2024._domainkey) байт в байт совпадает с selector в dkim_signing.conf.
DMARC запускать с `p=none` + `rua`, собрать неделю-две отчётов, убедиться, что своя же почта выровнена, и только потом переходить к quarantine/reject.
Репортинг включён и Redis жив — иначе reporting { enabled = true } молча ничего не делает.
ARC-подпись стоит на всех форвардинг-хопах, которые вы контролируете; иначе рвёте DMARC получателям.
Выравнивание по умолчанию relaxed — не ставьте adkim=s, пока не уверены, что подписываете точным FQDN из From.
Не полагайтесь на p=reject без force_actions.conf; и всегда добавляйте & !ARC_ALLOW, чтобы не убивать легитимную пересылку.
Прогоняйте тестовое письмо через rspamc scan после каждого изменения конфига.
Мониторьте DMARC_NA в логах: он означает, что у отправителя вообще нет DMARC-записи — частый сигнал молодого или брошенного домена.