Веб-интерфейс rspamd: защищённый доступ, чтение графиков символов и оперативная диагностика
Контроллер rspamd на порту 11334 — это не украшение, а самый быстрый способ ответить на два вопроса, которые задают каждый день: почему легитимное письмо ушло в reject и почему явный спам получил no action. Разбираем, как закрыть панель так, чтобы не подарить конфиг злоумышленнику, и как читать Symbols и History как сумму баллов, а не как картинку.
EvilMail Team29 июля 2026 г.11 мин чтения
Клиент пишет в поддержку: рассылка партнёра не доходит, партнёр клянётся, что у него всё настроено. Открываю панель rspamd, вкладку History, вбиваю адрес отправителя в фильтр — и через сорок секунд вижу строку: score 16.2 при пороге reject 15. Разворачиваю её, смотрю разбивку символов: BAYES_SPAM (5.1), FORGED_SENDER (3.0), R_DKIM_REJECT (1.0) и ещё горсть мелочи. Причина не в нашем фильтре и не в «магии»: у партнёра письмо подписано доменом, который не совпадает с From, а байесовский классификатор дообучили на похожем шаблоне. Диагноз поставлен без единого grep по логам.
Вот для чего на самом деле нужен веб-интерфейс rspamd. Не «посмотреть красивый график», а за минуту разложить конкретное письмо на баллы и понять, что чинить. Но у инструмента острый край: тот же контроллер, который рисует графики, умеет переписывать scores и actions, обучать fuzzy и писать в конфиг. Выставите его наружу неправильно — и подарите управление своим фильтром первому же сканеру, который найдёт открытый порт 11334.
Что такое контроллер и что на самом деле рисуют эти графики
WebUI — это не отдельный сервис. Его отдаёт worker-controller
Веб-интерфейс и статистика rspamd: защита, графики символов, диагностика — EvilMail Blog
, отдельный воркер rspamd, который слушает порт 11334 и обслуживает одновременно и HTTP API, и статику интерфейса. Всё, что вы кликаете в браузере, — это запросы к тому же API, которым пользуется
rspamc
.
Полезно держать в голове раскладку портов, потому что их постоянно путают:
11332 — proxy/milter, точка входа от MTA (Postfix/Exim);
11333 — scanner worker, который реально считает символы;
11334 — controller, то есть WebUI и управляющий API.
Вкладки интерфейса: Status, Throughput, Graphs, Symbols, Scan/Learn, History, Errors, Configuration. Важно понимать источник данных под каждой, иначе легко сделать ложные выводы:
Throughput и Graphs берутся из RRD-файла /var/lib/rspamd/rspamd.rrd, который контроллер обновляет сам. Удалили файл — графики обнулились, но фильтрация не пострадала.
Status собирает счётчики scanner-воркеров через сокет. В кластере это значит, что одна панель показывает статистику одного узла: если не настроена секция neighbours, вы смотрите на один сервер из трёх и делаете выводы обо всей ферме.
History по умолчанию — кольцевой буфер в памяти, примерно последние 1000 писем. Он теряется при рестарте rspamd и не общий между воркерами. Это первое, что нужно исправить перед прод-использованием, иначе диагностика «а что было полчаса назад» превращается в лотерею.
Закрываем доступ так, чтобы не подарить конфиг злоумышленнику
Здесь живёт главная историческая ловушка rspamd, на которой обжигаются до сих пор. У контроллера два пароля, и разница между ними не косметическая:
`password` — доступ только на чтение: смотреть графики, историю, символы.
`enable_password` — доступ на запись: менять scores и actions, обучать Bayes и fuzzy, редактировать конфиг во вкладке Configuration.
Ловушка в том, что если задан толькоpassword, он получает и права записи. То есть «я поставил один пароль, значит, защитился» — иллюзия: вы поставили пароль на полный доступ. Правило простое и не обсуждается: всегда задавайте оба пароля и делайте их разными.
Пароли хранятся не в открытом виде — генерируем хэш через rspamadm:
bash
# спросит пароль интерактивно (предпочтительно — не попадёт в history шелла)
rspamadm pw
# или сразу с паролем в аргументе (удобно для скриптов, но осторожно)
rspamadm pw -p 'ReadOnlySecret123'
Вывод вида $2$xxxxxxxx... — это catena-хэш (алгоритм по умолчанию), его и кладём в конфиг. Не трогаем rspamd.conf напрямую, всё пишем в override-файл /etc/rspamd/local.d/worker-controller.inc:
bind_socket = "127.0.0.1:11334" — ключевая строка. Контроллер не должен слушать 0.0.0.0. Наружу мы выставляем не rspamd, а nginx, который терминирует TLS и проксирует запрос на loopback, при желании добавляя второй слой basic_auth:
Получается двойная граница: сначала basic_auth на nginx, потом password/enable_password самого контроллера, и всё это только по TLS. А bind на loopback гарантирует, что даже если nginx упадёт или кто-то полезет напрямую по IP:11334 — соединения не будет.
Отдельно про secure_ip — параметр, который отключает проверку пароля для перечисленных адресов. Именно его чаще всего добавляют «чтобы rspamc не спрашивал пароль», вписывают туда 127.0.0.1 — и незаметно пробивают собственную защиту. Ведь nginx проксирует запросы как раз с loopback, а значит все обращения к панели начнут проходить в обход password/enable_password, оставив охрану только на basic_auth. Если вы фронтите контроллер через локальный nginx — не держите адрес прокси в `secure_ip`. Пусть пароли работают.
Кстати, вкладка Errors — ваш индикатор, что панель нашли в интернете. Всплеск строк с отказами авторизации и обращениями к API без валидного пароля означает, что по вам работает сканер. Если вы сделали всё по инструкции выше, ему нечего ловить, но сам факт стоит замечать.
Читаем Throughput и Symbols как инженер, а не как зритель
Throughput — это стек по действиям во времени: no action (зелёный), greylist, add header, rewrite subject и reject (красный) сверху. Ценность не в абсолютных числах, а в форме стека:
Резкий рост красной зоны (reject) на исходящем узле — почти всегда сломанный SPF или DKIM у вашего домена (протух ключ, съехала запись) либо атака на репутацию. Проверяю в первую очередь.
Рост greylist без роста reject — обычно временный источник или всплеск с нового IP; серый список делает свою работу, паниковать рано.
Плоское зелёное поле, которое вдруг просело в пользу add header — кто-то поменял пороги или подключил новый RBL.
Symbols показывает частоту срабатывания каждого символа и его вес. Здесь учишься отличать, что именно тянет score вверх. Есть «дорогие» символы, которые сразу задают тон письму:
BAYES_SPAM — байесовский классификатор уверен в спаме (до +5 и выше);
FORGED_SENDER — From не бьётся с фактическим отправителем;
R_DKIM_REJECT / DMARC_POLICY_REJECT — провал подписи или политики домена;
R_SPF_FAIL — отправитель вне разрешённых SPF;
FUZZY_DENIED, RBL_* — совпадение с fuzzy-хэшами или чёрными списками.
Быстрый навык: смотрите, из чего собран вес — из контентных символов (BAYES_*, тело письма) или из policy/auth (SPF, DKIM, DMARC). Это два разных диагноза. Контент чинит отправитель, аутентификацию — чаще администратор DNS.
History — основная рабочая лошадка
Практически вся диагностика живёт здесь. Каждая строка — одно письмо, и колонки читаются слева направо: Time, Message-ID, From/To, Score, Action, Symbols (с индивидуальными баллами), Size, Scan time. Клик по строке разворачивает полную разбивку: из каких символов сложился итог и какой порог он пересёк.
Пороги по умолчанию (задаются в /etc/rspamd/local.d/actions.conf) примерно такие: greylist ≈ 4, add_header ≈ 6, rewrite_subject ≈ 8–10, reject ≈ 15. Score письма — это просто сумма весов сработавших символов, а действие выбирается по самому высокому пересечённому порогу. Вот тот самый случай из открытия, разложенный по полочкам:
Диагноз по этой картинке очевиден: тянут BAYES_SPAM и FORGED_SENDER плюс провал аутентификации. Письмо честно похоже на спам, и чинить его нужно на стороне отправителя — выровнять From/DKIM, снять RBL-листинг, — а не крутить пороги у себя.
Чтобы History не исчезала при каждом рестарте, включаем персистентность в Redis. Для прода это обязательный шаг:
После этого история переживает рестарты, шарится между воркерами, и в панели работает фильтрация по получателю и по конкретному символу — та самая, которой я за сорок секунд нашёл проблемное письмо.
Связка с CLI: когда UI не хватает
Панель отвечает на «что случилось», но иногда нужно сверить счётчики или перепроверить письмо, которого уже нет в истории. Тут в дело идёт rspamc:
bash
# сводка: сколько просканировано, spam/ham, разбивка по действиям, обучение Bayes
rspamc stat
# статистика самих воркеров
rspamadm control stat
# перепроверить конкретное письмо и увидеть символы, даже если его нет в History
rspamc symbols < /tmp/message.eml
Если письмо реально должно было пройти, а Bayes уверенно топит его в спам, значит классификатор перекошен, и нужно осознанно дообучить его на правильных примерах — из вкладки Scan/Learn или командой:
Обучать надо с холодной головой: пара десятков ошибочно скормленных писем сдвигает BAYES_* для всего потока. После обучения полезно ещё раз прогнать rspamc stat и убедиться, что счётчик learned вырос, а fuzzy и Bayes действительно применяются к тестовому письму.
Чеклист перед тем, как открыть панель в прод
bind_socket строго на 127.0.0.1:11334, никогда не 0.0.0.0.
Заданы оба пароля — password и enable_password, и они разные.
Хэши сгенерированы через rspamadm pw, в конфиге нет паролей открытым текстом.
Доступ снаружи только через nginx с TLS; опционально второй слой basic_auth.
secure_ipне содержит адрес nginx (loopback), иначе он обнулит проверку паролей; в идеале его лучше вообще не задавать при фронтинге через прокси.
History переведена в Redis через history_redis.conf, буфер nrows достаточный.
RRD включён и /var/lib/rspamd/rspamd.rrd пишется — иначе Throughput пустой.
В кластере настроена секция neighbours, иначе одна панель = один узел.
Вкладка Errors просматривается регулярно как индикатор сканирования извне.
Пароли контроллера ротируются, а enable_password никогда не попадает в логи и историю шелла.
Панель rspamd полезна ровно настолько, насколько дисциплинированно вы держите её на loopback за TLS и читаете вкладку Symbols вместо любования графиком Throughput.