Типичная история: инженер ставит 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или спамассассиновский протокол. Сегодня отдельно его поднимать не нужно. - 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.
curl -fsSL https://rspamd.com/apt-stable/gpg.key | gpg --dearmor > /usr/share/keyrings/rspamd.gpg
echo "deb [signed-by=/usr/share/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ $(lsb_release -cs) main" \
> /etc/apt/sources.list.d/rspamd.list
apt update && apt install rspamdПервое правило, которое надо усвоить и никогда не нарушать: **стоковые конфиги в /etc/rspamd/*.conf не трогаем**. Никогда. Они перезаписываются при обновлении пакета. Все ваши изменения живут в двух каталогах:
/etc/rspamd/local.d/— файлы отсюда дополняют соответствующую секцию стокового конфига (мержатся)./etc/rspamd/override.d/— файлы отсюда жёстко перезаписывают секцию целиком.
В 95% случаев вам нужен local.d. Синтаксис проверяем перед каждым рестартом:
rspamadm configtestПодключение к Postfix
Настраиваем milter в main.cf:
smtpd_milters = inet:127.0.0.1:11332
non_smtpd_milters = inet:127.0.0.1:11332
milter_default_action = accept
milter_protocol = 6milter_default_action = accept — важная строка: если rspamd упал или не отвечает, письма продолжают ходить, а не отбиваются с tempfail. Для почтовой инфраструктуры доступность важнее, чем герметичность фильтра на те секунды, пока демон перезапускается.
Теперь включаем self-scan в proxy — /etc/rspamd/local.d/worker-proxy.inc:
milter = yes;
timeout = 120s;
upstream "local" {
default = yes;
self_scan = yes;
}Если у вас уже стоит OpenDKIM отдельным milter'ом — самое время его убрать. rspamd подписывает DKIM сам (модуль dkim_signing), и держать две подписывающие цепочки — верный способ получить двойную подпись или конфликт. Отдайте DKIM rspamd: /etc/rspamd/local.d/dkim_signing.conf с path, selector и domain, а OpenDKIM снимите из smtpd_milters.
После рестарта проверьте, что заголовки появляются. Отправьте тестовое письмо через swaks:
swaks --to [email protected] --server localhostВ заголовках принятого письма должны появиться X-Spamd-Result (итоговый score и список сработавших символов с весами) и X-Rspamd-Queue-Id.
Redis — единое состояние
Вот место, где рушится большинство инсталляций. Bayes, greylisting, fuzzy, ratelimit, neural, модуль replies — все они хранят состояние в Redis. Без него байес не переживает рестарт, greylisting не работает, а ratelimit молча ничего не делает.
apt install redis-serverКонфиг rspamd — один-единственный файл /etc/rspamd/local.d/redis.conf:
servers = "127.0.0.1:6379";
db = "0";
timeout = 1;Этот файл автоматически подхватывают все перечисленные модули — прописывать Redis в каждом конфиге отдельно не нужно. При наличии реплики чтение и запись разносятся через read_servers/write_servers.
Отдельно про maxmemory-policy в самом Redis. Байесовские токены — это не кэш. Отдадите инстанс под allkeys-lru — Redis начнёт под нагрузкой выселять токены, и обученный классификатор будет тихо деградировать без единой ошибки в логах. Держите noeviction, а лучше — вынесите Bayes на отдельный db или отдельный инстанс:
redis-cli CONFIG SET maxmemory-policy noevictionБайесовский классификатор
Теперь главное — /etc/rspamd/local.d/classifier-bayes.conf:
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 показывает, что классификатор стабилен.
Ручное обучение — два ключевых вызова:
rspamc learn_spam < /path/to/spam.eml
rspamc learn_ham < /path/to/ham.emlМассово — скормите целиком папку .Junk из Maildir реального пользователя:
rspamc learn_spam /var/mail/vhosts/evilmail.pro/user/.Junk/cur/
rspamc learn_ham /var/mail/vhosts/evilmail.pro/user/cur/Идеальный вариант — замкнуть цикл через Dovecot/Sieve: пользователь перетаскивает письмо в «Спам», а imap_sieve дёргает rspamc learn_spam на это письмо. Тогда классификатор учится на реальных решениях живых людей, а не на своих же вердиктах.
Пороги действий и заголовки
Действия задаются в /etc/rspamd/local.d/actions.conf. Значения — это суммарный score письма, а не проценты:
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-совпадение. Это принципиальная позиция: байес — это голос, а не приговор.
Веб-контроллер даёт удобный интерфейс для обучения и просмотра статистики. Сгенерируйте хеш пароля:
rspamadm pwКоманда спросит пароль и вернёт хеш вида $2$.... Кладём его в /etc/rspamd/local.d/worker-controller.inc:
password = "$2$....";
enable_password = "$2$...."; # нужен для learn и действий из UIКонтроллер слушает 127.0.0.1:11334 — наружу его отдаём только через nginx reverse-proxy с TLS и, желательно, дополнительной авторизацией. Никогда не выставляйте 11334 в интернет напрямую.
Проверка и эксплуатация
Смотрим, что байес вообще накапливает статистику:
rspamc statИщите строки вида BAYES_SPAM ... learns: N и BAYES_HAM ... learns: N. Пока оба N меньше 200 — байес молчит по дизайну. Классический тест на живость всей цепочки — GTUBE-строка (стандартный тестовый образец спама): письмо с ней должно поднять символ GTUBE и уйти в reject.
Что реально применилось из конфига (а не что вы написали) — смотрите через дамп секции классификатора:
rspamadm configdump classifierПроверить, что после обучения байесовские ключи легли в Redis:
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не тронуты. - Postfix:
smtpd_milters = inet:127.0.0.1:11332,milter_default_action = accept,milter_protocol = 6. - OpenDKIM снят с milter-цепочки, DKIM подписывает rspamd.
Такая конфигурация держит поток на evilmail.pro без ложных reject'ов и без того, чтобы rspamd жёг CPU впустую. Главное — не воспринимать байес как кнопку «включить и забыть»: это классификатор, ровно настолько хороший, насколько чист корпус, на котором вы его учите.


