Полная настройка rspamd с нуля: Postfix через milter, Redis и живой байесовский фильтр
Ставим rspamd на прод так, как его задумали разработчики: Postfix через milter в rspamd_proxy (self-scan), Redis как единая точка состояния для Bayes, greylisting и ratelimit, и байесовский фильтр, который реально учится на потоке. Только команды, пути и пороги — без воды.
EvilMail Team20 июля 2026 г.13 мин чтения
Типичная история: инженер ставит rspamd по первому попавшемуся гайду, привязывает его к Postfix через content_filter, радуется первым дням — а через неделю приходит с двумя жалобами. Первая: rspamd молотит CPU на каждом письме. Вторая, куда хуже: спам всё равно проходит. Причина почти всегда одна и та же — байесовский классификатор не обучен, а его статистика не переживает рестарт демона, потому что Redis не подключён и токены живут в памяти процесса.
rspamd — это не «SpamAssassin, только быстрее». Это stateful-система фильтрации, где почти вся ценность прячется в Redis и в правильной топологии воркеров. Задача статьи — довести вас от чистой машины до конфигурации, которую я держу на проде evilmail.pro: postfix -> milter -> rspamd (self-scan) -> Redis -> вердикт, с живым байесом, который учится на потоке и не теряет память между перезапусками.
Почему milter, а не content_filter — и почему rspamd_proxy
У rspamd три воркера, и путаница между ними — источник половины кривых инсталляций:
normal (порт 11333) — классический воркер сканирования. Legacy. Использовался, когда rspamd дёргали через content_filter
Настройка rspamd + Postfix (milter) + Redis + Bayes с нуля | evilmail — EvilMail Blog
или спамассассиновский протокол. Сегодня отдельно его поднимать не нужно.
controller (порт 11334) — веб-интерфейс, learn API, статистика, обучение из UI.
rspamd_proxy (порт 11332) — milter-протокол и, что важнее, self-scan режим. Начиная с rspamd 1.7 это канон: Postfix разговаривает с proxy по milter-протоколу напрямую, а proxy сам сканирует письмо, не проксируя его в отдельный normal.
Почему milter, а не content_filter? Milter встраивается в SMTP-диалог до того, как письмо принято. Значит, при высоком спам-скоре мы отдаём reject прямо в SMTP-сессии — отправляющий сервер сам сообщит своему пользователю о недоставке. С content_filter письмо сначала принимается в очередь, а «отбивку» генерируем уже мы постфактум — это классический backscatter, за который IP прилетает в чёрные списки. Milter этой проблемы не создаёт по определению.
Ключевой посыл диаграммы: reject при score ≥ 15 срабатывает до того, как Postfix положит письмо в очередь. Никакого backscatter.
Установка из официального репозитория
Не ставьте rspamd из стокового Debian/Ubuntu — там древние версии, где половина модулей ведёт себя не так, как в документации. Подключаем официальный репозиторий rspamd.com. На середину 2026-го актуальная стабильная ветка — 3.9.x.
Первое правило, которое надо усвоить и никогда не нарушать: **стоковые конфиги в /etc/rspamd/*.conf не трогаем**. Никогда. Они перезаписываются при обновлении пакета. Все ваши изменения живут в двух каталогах:
milter_default_action = accept — важная строка: если rspamd упал или не отвечает, письма продолжают ходить, а не отбиваются с tempfail. Для почтовой инфраструктуры доступность важнее, чем герметичность фильтра на те секунды, пока демон перезапускается.
Теперь включаем self-scan в proxy — /etc/rspamd/local.d/worker-proxy.inc:
Если у вас уже стоит OpenDKIM отдельным milter'ом — самое время его убрать. rspamd подписывает DKIM сам (модуль dkim_signing), и держать две подписывающие цепочки — верный способ получить двойную подпись или конфликт. Отдайте DKIM rspamd: /etc/rspamd/local.d/dkim_signing.conf с path, selector и domain, а OpenDKIM снимите из smtpd_milters.
После рестарта проверьте, что заголовки появляются. Отправьте тестовое письмо через swaks:
В заголовках принятого письма должны появиться X-Spamd-Result (итоговый score и список сработавших символов с весами) и X-Rspamd-Queue-Id.
Redis — единое состояние
Вот место, где рушится большинство инсталляций. Bayes, greylisting, fuzzy, ratelimit, neural, модуль replies — все они хранят состояние в Redis. Без него байес не переживает рестарт, greylisting не работает, а ratelimit молча ничего не делает.
servers = "127.0.0.1:6379";
db = "0";
timeout = 1;
Этот файл автоматически подхватывают все перечисленные модули — прописывать Redis в каждом конфиге отдельно не нужно. При наличии реплики чтение и запись разносятся через read_servers/write_servers.
Отдельно про maxmemory-policy в самом Redis. Байесовские токены — это не кэш. Отдадите инстанс под allkeys-lru — Redis начнёт под нагрузкой выселять токены, и обученный классификатор будет тихо деградировать без единой ошибки в логах. Держите noeviction, а лучше — вынесите Bayes на отдельный db или отдельный инстанс:
bash
redis-cli CONFIG SET maxmemory-policy noeviction
Байесовский классификатор
Теперь главное — /etc/rspamd/local.d/classifier-bayes.conf:
nginx
backend = "redis";
new_schema = true;
expire = 8640000; # 100 дней жизни токена
min_learns = 200; # байес молчит, пока не обучен на 200 письмах каждого класса
autolearn = true; # осторожно — см. ниже
min_learns = 200 — это предохранитель. Пока классификатор не увидел хотя бы 200 спамов и 200 хамов, символ BAYES_SPAM в вердикт не попадает. Спасает от ситуации, когда байес на трёх обученных письмах уверенно записывает легитимную рассылку в спам.
Про autolearn — честно, как инженер инженеру. Автообучение означает, что rspamd учит байес на собственных вердиктах: письмо с высоким score уходит в спам-корпус, с низким — в ham. Это петля обратной связи. Если фильтр начал ошибаться в одну сторону, autolearn закрепляет ошибку и усиливает её. Первые недели обучайте руками. Включайте autolearn только когда накопился здоровый ручной корпус и rspamc stat показывает, что классификатор стабилен.
Идеальный вариант — замкнуть цикл через Dovecot/Sieve: пользователь перетаскивает письмо в «Спам», а imap_sieve дёргает rspamc learn_spam на это письмо. Тогда классификатор учится на реальных решениях живых людей, а не на своих же вердиктах.
Пороги действий и заголовки
Действия задаются в /etc/rspamd/local.d/actions.conf. Значения — это суммарный score письма, а не проценты:
nginx
greylist = 4;
add_header = 6;
reject = 15;
< 4 — принимаем молча.
4 — greylisting: временный отказ, легитимный сервер повторит, спам-бот обычно нет.
6 — добавляем заголовок X-Spam: Yes, письмо доходит, но клиент или Sieve может разложить его в «Спам».
>= 15 — reject прямо в SMTP.
Почему reject держим так высоко и не завязываем на один байес? Потому что BAYES_SPAM даёт максимум порядка +4–5 к score. В одиночку он никогда не должен приводить к отклонению письма — иначе одна ошибка обучения превращается в потерянную легитимную почту. reject должен собираться из нескольких независимых сигналов: байес + плохой SPF/DKIM + попадание в RBL + fuzzy-совпадение. Это принципиальная позиция: байес — это голос, а не приговор.
Веб-контроллер даёт удобный интерфейс для обучения и просмотра статистики. Сгенерируйте хеш пароля:
bash
rspamadm pw
Команда спросит пароль и вернёт хеш вида $2$.... Кладём его в /etc/rspamd/local.d/worker-controller.inc:
nginx
password = "$2$....";
enable_password = "$2$...."; # нужен для learn и действий из UI
Контроллер слушает 127.0.0.1:11334 — наружу его отдаём только через nginx reverse-proxy с TLS и, желательно, дополнительной авторизацией. Никогда не выставляйте 11334 в интернет напрямую.
Проверка и эксплуатация
Смотрим, что байес вообще накапливает статистику:
bash
rspamc stat
Ищите строки вида BAYES_SPAM ... learns: N и BAYES_HAM ... learns: N. Пока оба N меньше 200 — байес молчит по дизайну. Классический тест на живость всей цепочки — GTUBE-строка (стандартный тестовый образец спама): письмо с ней должно поднять символ GTUBE и уйти в reject.
Что реально применилось из конфига (а не что вы написали) — смотрите через дамп секции классификатора:
bash
rspamadm configdump classifier
Проверить, что после обучения байесовские ключи легли в Redis:
bash
redis-cli DBSIZE # число ключей растёт после каждого learn
redis-cli --scan | head # видны байесовские токены
Основной лог — /var/log/rspamd/rspamd.log. В заголовках писем читайте X-Spamd-Result: там весь разбор — итоговый score и каждый сработавший символ со своим весом. Это ваш главный инструмент диагностики «почему это письмо получило такой вердикт».
Чеклист запуска (перед тем как пускать трафик)
rspamadm configtest проходит без ошибок.
Redis запущен, maxmemory-policy = noeviction, Bayes на выделенном db.
В /etc/rspamd/local.d/ лежат redis.conf, classifier-bayes.conf, actions.conf, worker-proxy.inc, worker-controller.inc. Стоковые *.conf не тронуты.
OpenDKIM снят с milter-цепочки, DKIM подписывает rspamd.
swaks показывает X-Spamd-Result в заголовках принятого письма.
GTUBE-письмо уходит в reject.
rspamc stat показывает рост learns — байес обучается вручную минимум пару недель до включения autolearn.
Контроллер 11334 доступен только через reverse-proxy, пароль и enable_password заданы.
Такая конфигурация держит поток на evilmail.pro без ложных reject'ов и без того, чтобы rspamd жёг CPU впустую. Главное — не воспринимать байес как кнопку «включить и забыть»: это классификатор, ровно настолько хороший, насколько чист корпус, на котором вы его учите.