Перехід зі SpamAssassin на rspamd: milter-інтеграція з Postfix і перші правила фільтрації
SpamAssassin у 2026-му впирається не в throughput, а в архітектуру: синхронний spamd, Bayes у Berkeley DB і зоопарк з amavisd, opendkim та postgrey. Показуємо, як під'єднати rspamd як milter поруч зі старим стеком, ганяти обидва в shadow-режимі на бойовому Postfix, а тоді вимкнути SpamAssassin без даунтайму.
EvilMail Team14 липня 2026 р.13 хв читання
SpamAssassin не «повільний». Він упирається в стелю, яку не пробити тюнінгом sa-update чи додаванням ядер. Perl-демон spamd форкає воркери й проганяє правила синхронно: кожен лист чекає, поки відпрацює весь rules-стек, перш ніж отримати вердикт. Bayes лежить у Berkeley DB, а BDB під навантаженням серіалізує записи через блокування на файл — два воркери, що одночасно вчаться, стають у чергу один за одним. А щойно вам потрібні greylisting, DKIM-перевірка й репутація відправника, ви зшиваєте amavisd-new + opendkim + postgrey у конструкцію, де кожен компонент — окремий демон зі своїм сокетом, своїм логом і своїм способом зламатися о третій ночі.
rspamd робить те саме одним подієвим C-демоном. Асинхронні DNS-запити, символи рахуються паралельно, Redis замість BDB, вбудований DKIM/ARC-signing, фаззі-хеші й нейромережа — усе «з коробки». Ця стаття — не есе про те, що «rspamd кращий». Це план міграції для інженера, який тримає бойовий MX і не може дозволити собі втратити жодного transactional-листа. Через інфраструктуру evilmail.pro проходить і транзакційна пошта, і temp-mail трафік, тож ціна false positive у нас висока — саме тому ми не вимикаємо SpamAssassin одним рухом, а спершу ганяємо обидва фільтри поруч.
Межа статті чітка: базове налаштування, milter-інтеграція з Postfix і перші правила. Кластеризацію, власний DNS-fuzzy-storage і тонке калібрування нейромережі залишаємо на потім.
Міграція зі SpamAssassin на rspamd: Postfix milter і перші правила — EvilMail Blog
Чому SpamAssassin впирається в стелю
Проблема не в кількості листів за секунду, а в тому, як пакет обробляє кожен окремий. Розкладемо біль по пунктах:
Синхронний rules-стек.spamd рахує правила послідовно в межах одного повідомлення. RBL-запит, що чекає відповіді DNS, блокує воркер, який цей час міг би рахувати regexp.
Bayes у BDB. Berkeley DB бере блокування на запис. За активного автонавчання воркери конкурують за файл, і latency стрибає непередбачувано.
Зоопарк демонів. SpamAssassin сам по собі не вміє ні greylisting, ні DKIM-підпис, ні rate-limit. Щоб зібрати повноцінний фільтр, ви тримаєте amavisd-new як обгортку, opendkim як окремий milter, postgrey як policy-демон. Чотири процеси, три сокети, чотири конфіги.
rspamd поєднує це в одному event loop. SPF, DKIM, DMARC, RBL, Bayes і fuzzy рахуються паралельно, кожен асинхронно б'є в DNS або Redis і не блокує решту. Один Redis обслуговує Bayes, greylist, ratelimit, fuzzy і replies. Ось як це виглядає поруч:
Модель оцінювання: symbols і actions, а не «score як у SA»
Найбільша концептуальна пастка для тих, хто йде зі SpamAssassin, — очікування єдиного порогу «spam/ham». У rspamd такого порогу немає. Замість нього є actions — градація рішень, кожне зі своїм score-порогом у метриці default:
no action — лист чистий, пропускаємо;
greylist — тимчасово відкидаємо з проханням повторити;
rewrite subject — дописуємо префікс на кшталт [SPAM];
reject — відмова на рівні SMTP.
Бали дають символи: SPF_ALLOW віднімає, DKIM_ALLOW віднімає більше, BAYES_SPAM, FUZZY_DENIED, DMARC_POLICY_REJECT додають. Сума символів визначає, який action спрацює.
Практичний висновок: не тягніть `score_set` зі SpamAssassin напряму. 90% ваших SA-правил rspamd уже покриває власними модулями, а сліпе перенесення балів ламає калібрування — ви подвоюєте ваги за те, що вже враховано. Мігруйте наміри, а не цифри.
Встановлення й перший запуск
Дистрибутивний пакет rspamd майже завжди застарілий. Ставимо з офіційного stable-репо (гілка 3.x станом на 2026):
Головне правило конфіг-гігієни: **ніколи не редагуйте файли /etc/rspamd/*.conf напряму** — вони перезапишуться при першому ж apt upgrade. Усі зміни йдуть у /etc/rspamd/local.d/ (додається й зливається з базовим конфігом) або /etc/rspamd/override.d/ (жорстко перекриває). Це не порада, а умова того, що ваш налаштований фільтр переживе апдейт.
Вебінтерфейс слухає на 127.0.0.1:11334 через worker-controller. Генеруємо хеш пароля й кладемо у local.d:
milter_default_action = accept означає fail-open: якщо rspamd упав або не відповідає, пошта йде далі, а не бавнситься. Для прод-MX це не опція, це вимога. Фільтр, який кладе вашу пошту, коли сам зламався, гірший за відсутність фільтра.
Shadow-режим. На цьому етапі rspamd висить milter'ом поруч зі старим opendkim/SpamAssassin, але actions обмежені зверху add_header — жодного reject. Postfix уже вставляє заголовок X-Spamd-Result у кожен лист, і кілька днів ви просто накопичуєте history в UI на реальному трафіку. Мета — побачити, як rspamd оцінює вашу власну пошту, перш ніж дати йому право її відхиляти. Заголовок читається так:
Число [2.10 / 15.00] — набраний бал і поріг reject. Для ручного тесту згодовуйте сирий лист напряму: rspamc < message.eml.
Redis і модулі, що вмикаються одразу
Один Redis обслуговує всі storage. /etc/rspamd/local.d/redis.conf:
conf
servers = "127.0.0.1:6379";
Після цього оживають без додаткової возні: greylisting (local.d/greylist.conf, TTL кілька хвилин, стан у Redis), rbl (Spamhaus SBL/XBL — але для обсягу потрібен платний data-feed, публічний DNS-мірор швидко вас зарейт-лімітить), spf/dkim/dmarc через відповідні .conf, і fuzzy_check проти публічного fuzzy-storage rspamd.com. Ключові символи, які ви бачитимете в history: SPF_ALLOW/SPF_FAIL, DKIM_ALLOW/R_DKIM_REJECT, DMARC_POLICY_ALLOW/DMARC_POLICY_REJECT, BAYES_SPAM/BAYES_HAM, FUZZY_DENIED, RCVD_IN_DNSWL.
DKIM-підпис теж переїжджає в rspamd — це дозволяє потім прибрати opendkim як окремий milter. local.d/dkim_signing.conf:
rspamadm dkim_keygen -s mail -d evilmail.pro \
-k /var/lib/rspamd/dkim/evilmail.pro.mail.key
# друкує готовий запис:
# mail._domainkey.evilmail.pro IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSq..."
Не вмикайте DKIM-signing у rspamd, поки opendkim ще активний — інакше кожен лист підпишеться двічі, і частина верифікаторів це відхилить. Спершу знімаємо opendkim зі smtpd_milters, потім вмикаємо підпис у rspamd.
Перші власні правила: multimap і Bayes
Замість перенесення SA-правил починайте з локальних чорних/білих списків через multimap. local.d/multimap.conf:
conf
local_bl_from {
type = "from";
map = "/etc/rspamd/maps.d/blacklist_from.map";
score = 12.0;
symbol = "LOCAL_BL_FROM";
}
Файл .map — простий список адрес чи доменів, по одному на рядок; rspamd підхоплює зміни без рестарту. Так само робляться whitelist по ip, url, helo.
Bayes стає надійним лише після навчання. Мінімум — приблизно 200 спам- і 200 ham-повідомлень, менше дасть галасливі BAYES_*. Спам беремо з Junk-теки, ham — з кореня INBOX Maildir:
bash
rspamc learn_spam /var/mail/vhosts/evilmail.pro/user/.Junk/cur/*
rspamc learn_ham /var/mail/vhosts/evilmail.pro/user/cur/*
rspamc stat # скільки навчено, розмір словника
Нейромережу (neural.conf) вмикайте тільки після того, як зібрали кілька днів history — їй потрібен реальний потік із розміченими вердиктами, інакше вона вчиться на шумі.
Перемикання з shadow у enforce
Коли history виглядає здорово, переходимо в бойовий режим без даунтайму, в такому порядку:
1.Підняти actions до бойових значень у local.d/actions.conf:
conf
greylist = 4;
add_header = 6;
reject = 15;
1.Прибрати opendkim і SpamAssassin/amavis із Postfix (master.cf + smtpd_milters), щоб не було подвійного підпису й подвійного сканування.
2.Стежити за rspamc stat і history в UI: розподіл листів по actions, топ-символи, і головне — false positive rate на власному ham-корпусі. Якщо ваша транзакційна пошта раптом набирає add_header — шукайте символ-винуватця й правте його вагу або додавайте у whitelist через multimap.
Чек-лист перед вимкненням SpamAssassin
[ ] rspamadm configtest проходить без помилок після кожної зміни в local.d/.
[ ] milter_default_action = accept стоїть у main.cf (fail-open перевірено: зупиніть rspamd на тестовому хості — пошта має йти).
[ ] Кілька днів shadow-history зібрано, топ-символи оглянуто, аномалій на власному ham немає.
[ ] Bayes навчений на ≥200 spam + ≥200 ham, rspamc stat це підтверджує.
[ ] DKIM-ключ згенеровано, TXT-запис mail._domainkey опубліковано й провалідовано, opendkim знято до ввімкнення підпису в rspamd.
[ ] Redis один на всі storage, персистентність увімкнено, пам'ять під контролем.
[ ] actions виставлено на бойові пороги, reject-поріг протестовано на завідомо спамному семплі через rspamc.
[ ] SpamAssassin/amavis прибрано з master.cf і smtpd_milters, старі демони зупинено й вимкнено з автозапуску.
[ ] Моніторинг /var/log/rspamd/rspamd.log і UI history налаштовано на перші 48 годин після enforce.
Порядок тут важливіший за швидкість. Shadow-режим коштує вам кількох днів чекання, але саме він відрізняє контрольовану міграцію від нічного інциденту, коли половина транзакційної пошти пішла в reject через одне неоткаліброване правило.