Учится ли Rspamd, когда я переношу письма в спам
АйТи Фреш
Linux, Docker и DevOps

Переношу письма в «Спам» в Outlook — Rspamd в mailcow правда на этом учится?

Автор: , директор ООО «АйТи-Фреш» · · ~17 мин чтения
Перенос письма в папку Junk обучает статистическую модель Bayes на сервере mailcow — механика imapsieve
Обучение идёт не в почтовом клиенте, а на сервере — через Dovecot imapsieve и статистику Bayes в Redis.

Короткий ответ: да, но только если письмо попадает в серверную папку с именем Junk и обратно, а не в любую папку «для спама». В mailcow за это отвечает связка Dovecot imapsieve и статистика Bayes в Redis, и без проверки легко решить, что обучение «не работает», хотя оно просто не видит ваших действий. Разбираю механизм, как убедиться, что он реально включён, и когда стоит сбросить модель после накопленных ошибок.

Кейс: сотрудники «дрессируют» спам-фильтр вручную, а толку не видно

«Книжная гавань» — книжный магазин, 35 рабочих мест, склад и интернет-магазин, вся переписка с поставщиками и покупателями идёт через корпоративную почту на mailcow. Осенью в отдел закупок стало приходить много спама под видом рассылок от типографий — не откровенный мусор, а достаточно правдоподобные письма, которые Rspamd пропускал во «Входящие». Администратор дал сотрудникам простую инструкцию: видите спам — тащите в «Спам» в Outlook. Через две недели картина не изменилась: похожие письма продолжали идти мимо фильтра, а часть легитимных писем от нового поставщика, наоборот, стала падать в Junk без видимой причины.

Логичный вопрос, с которым к нам обратились: «мы же вручную помечаем спам каждый день, почему Rspamd как будто не учится?» Проверка началась с самого простого — а точно ли перенос письма в Outlook доходит до Dovecot как ожидаемое действие, а не как обычное перемещение файла между папками IMAP. Дело в том, что «учится на переносе в спам» — не магия и не встроенное поведение любого антиспам-движка по умолчанию: это конкретный механизм, который либо настроен и работает, либо нет, и по внешнему виду почтового клиента понять это невозможно.

Выяснилось: часть сотрудников пользовалась не стандартной папкой Junk, а создала свою папку «Спам-нежелательное» — так исторически повелось с прошлого почтового сервера. Rspamd её не видел вообще, потому что триггер обучения в mailcow завязан на серверную папку с точным именем Junk, а не на любую папку с похожим названием. Вторая часть проблемы — легитимные письма поставщика, которые начали залетать в Junk: их туда перенёс один сотрудник по ошибке в первый день эксперимента, и с этого момента фильтр честно обучился считать их спамом. Обе причины лежат не в самом Rspamd, а в том, как устроена связка Dovecot-Rspamd на уровне протокола IMAP.

Первый вопрос при жалобе «фильтр не учится»: откройте настройки учётной записи в почтовом клиенте и убедитесь, что папка для спама у пользователя — это серверная папка `Junk` (клиент может показывать её как «Спам» или «Нежелательная почта»), а не отдельная папка с другим именем на сервере.

Как на самом деле устроено обучение: imapsieve, а не магия клиента

Механизм обучения в mailcow работает не на стороне Outlook или веб-почты, а на стороне Dovecot — почтовый клиент здесь только выполняет обычную операцию IMAP MOVE, а дальше в дело вступает плагин imapsieve. Документация mailcow формулирует это прямо: перенос сообщения в папку junk обучает Rspamd как спам, а перенос ИЗ неё в любую папку, кроме Trash, обучает как ham — то есть как легитимное письмо. Перенос в Trash не обучает ничего, это просто удаление. Отдельно документация упоминает автообучение: письма с очень высокой или очень низкой оценкой Rspamd учит сам, без участия пользователей.

Технически это sieve_imapsieve — часть конфигурации Dovecot, которая слушает события перемещения сообщений между папками и по этим событиям запускает Sieve-скрипты report-spam.sieve и report-ham.sieve. Если открыть эталонный dovecot.conf, видно главное: правило imapsieve_mailbox1_name = Junk срабатывает на папку с именем именно Junk, а report-ham.sieve пропускает только перенос в папку с именем ровно Trash. Атрибут \Junk при этом ни при чём: в dovecot.folders.conf mailcow помечает им ещё и «Spam», «Junk E-Mail», «Нежелательная почта», «Спам», но imapsieve на них не реагирует. Скрипты через pipe отдают письмо Rspamd на обучение. Важная деталь: обучение происходит на уровне Dovecot, поэтому оно работает одинаково для любого почтового клиента, который умеет IMAP MOVE — Outlook, Thunderbird, веб-интерфейс SOGo или мобильное приложение. Специфики под конкретный клиент тут нет, разница может быть только в том, как клиент физически перемещает письмо: правило в mailcow настроено на событие COPY, а IMAP MOVE для imapsieve выглядит так же, поэтому и MOVE, и COPY+DELETE обучают одинаково. Проблемы начинаются, когда клиент кладёт «спам» не в серверную Junk, а в свою папку или вообще в локальный файл данных.

Соответственно, диагностика жалобы «не учится» всегда должна начинаться не с Rspamd, а с проверки, что письмо реально долетело до правильной папки на сервере IMAP, а не осело в локальном кэше клиента или в неправильной папке. Это и произошло в «Книжной гавани» — визуально в Outlook у сотрудника была папка «Спам», но фактически это была обычная пользовательская папка, не связанная с серверной папкой Junk.

Схема обучения Rspamd через Dovecot imapsieve: перенос в Junk даёт класс SPAM, перенос из Junk — класс HAM
Обучает не факт удаления письма, а именно перемещение между Junk и остальными папками.

Где физически хранится модель и как её увидеть

Статистика Bayes у Rspamd в mailcow не файл на диске, а данные в Redis. Документация прямо указывает: байесовская статистика пишется в Redis под ключами BAYES_HAM и BAYES_SPAM. Отдельно, для похожих, но не идентичных писем, используется локальное Fuzzy-хранилище — оно ловит повторяющиеся паттерны в тексте или изображениях, характерные для конкретной волны спама, и это отдельный механизм от Bayes, хотя оба участвуют в общей оценке письма.

Заглянуть в статистику Bayes из командной строки можно через redis-cli с паролем из mailcow.conf:

docker compose exec redis-mailcow redis-cli -a "$(grep -oP '(?<=REDISPASS=).*' mailcow.conf)" --scan --pattern 'BAYES_*'

Куда информативнее — веб-интерфейс Rspamd, доступный на mailcow по адресу /rspamd/ (пароль к нему задаётся отдельно в админке mailcow): там на вкладке статистики видно суммарное число токенов в классах spam и ham, а на вкладке истории — что конкретно движок посчитал причиной оценки письма (символ BAYES_SPAM или BAYES_HAM в списке сработавших правил означает, что статистическая модель поучаствовала в решении). Если у вас в истории по подозрительным письмам символ BAYES_SPAM не появляется вообще ни разу за последние дни — это самый надёжный признак, что обучение фактически не идёт, даже если сотрудники честно переносят письма в правильную папку.

Для ручного дообучения отдельных сообщений вне механизма imapsieve — например, если нужно скормить фильтру архив уже накопленного спама — mailcow (как и любая инсталляция Rspamd) поддерживает прямые команды через rspamc:

# с хоста, из каталога mailcow-dockerized; письма в несжатом виде
for file in /my/folder/.Junk/cur/*; do docker exec -i $(docker compose ps -q rspamd-mailcow) rspamc learn_spam < "$file"; done
for file in /my/folder/cur/*; do docker exec -i $(docker compose ps -q rspamd-mailcow) rspamc learn_ham < "$file"; done

Такой ручной прогон полезен разово, при переносе большого архива уже классифицированных писем, но не заменяет постоянный механизм imapsieve — он должен продолжать работать сам по себе на текущей переписке.

Если в истории Rspamd за неделю ни разу не встретился символ BAYES_SPAM или BAYES_HAM — не спешите винить сотрудников. Сначала проверьте, что спам у них уходит в серверную папку с именем Junk, а сам imapsieve вообще активен в конфигурации Dovecot.
Чек-лист проверки обучения Rspamd: история Rspamd, символы BAYES, папка с именем Junk, ключи в Redis
Четыре проверки за пять минут дают точный ответ вместо догадок по ощущениям.

Проверка на живом сервере: диагностика imapsieve по логам

Если внешне всё настроено правильно, а обучение по ощущениям не работает, следующий шаг — заглянуть в отладочные логи, а не гадать по косвенным признакам. Я включаю отладку конкретных модулей Rspamd (debug_modules — штатный параметр логирования Rspamd) через отдельный файл logging.custom.inc: сам override.d/logging.inc в mailcow лежит в git и подключает этот файл, если он есть, так что обновление правку не затрёт:

# на хосте, в каталоге mailcow-dockerized
# override.d/logging.inc — файл mailcow из git, он сам подключает logging.custom.inc
cat > data/conf/rspamd/override.d/logging.custom.inc <<'EOF'
debug_modules = ["fuzzy_backend", "bayes"];
EOF
docker compose restart rspamd-mailcow

После этого в логе контейнера появляются записи о каждом обращении к байесовскому и fuzzy-модулям — в том числе события обучения. Смотреть удобнее сразу с фильтром, потому что подробный лог быстро становится многословным:

docker compose logs -f --tail=200 rspamd-mailcow | grep -iE 'bayes|learn'

Затем в почтовом клиенте вручную переносите тестовое письмо в Junk и наблюдаете за логом в реальном времени: должна появиться запись об обучении с указанием класса (spam/ham) и токенов. Если события нет вообще — проблема на уровне Dovecot и imapsieve, стоит проверить конфигурацию sieve_imapsieve в data/conf/dovecot/dovecot.conf (в mailcow это эталонный файл, который поставляется с проектом, и вручную его лучше не редактировать, а искать отклонения от актуальной версии на GitHub). Если событие есть, но статистика Bayes в интерфейсе не растёт — уже вопрос к доступу Rspamd к Redis или к конфигурации классификатора в local.d/statistic.conf.

Не забудьте выключить расширенное логирование после диагностики — оно ощутимо увеличивает объём логов контейнера, и оставлять его постоянно включённым на боевом сервере смысла нет. Для «Книжной гавани» диагностика заняла один вечер: два сотрудника действительно писали в непривязанную папку, ещё у одного Outlook складывал спам в серверную папку «Нежелательная почта» — у неё есть атрибут \Junk, но имя не Junk, и imapsieve её игнорировал. После пересоздания профиля и перевода всех на серверную папку Junk обучение пошло штатно, и уже через десять дней доля ложных срабатываний по легитимным поставщикам заметно снизилась.

Зачем иногда рекомендуют сбросить Bayes и как не задеть остальные данные Redis

Отдельная ситуация — когда обучение технически работает годами, но фильтр всё равно ошибается чаще, чем должен. Причина может быть не в механизме доставки обучающих сигналов, а в самой модели: если в неё долго попадали противоречивые сигналы (сотрудники по-разному оценивали похожие письма, часть спама вручную не переносили, часть легитимных писем ошибочно улетала в Junk), статистика может накопить перекос, который проще сбросить и накопить заново, чем распутывать.

У этого есть и историческая точка отсчёта: в релизе mailcow 2024-08 обновление Rspamd сопровождалось рекомендацией разово сбросить данные Bayes — с явной оговоркой, что это не обязательный шаг, а мера на усмотрение администратора. Причина была в том, что core-токены модели могли продолжать влиять на оценку даже после истечения срока действия остальных токенов, что потенциально вело к переобучению. К сегодняшнему дню, 23 сентября 2026 года, mailcow ушёл далеко вперёд — актуальная ветка обновлений 2026-07 несёт Rspamd версии 4.1.x, и сама рекомендация 2024 года уже не актуальна как обязательная процедура, но сам приём сброса Bayes остаётся рабочим инструментом на случай, когда модель действительно перекошена.

Документация mailcow для этой операции требует аккуратности: сначала сделать копию dump.rdb Redis на случай отката, затем удалить именно байесовские ключи по шаблону, а не всю базу целиком — в том же Redis mailcow хранит настройки fail2ban (F2B_*), DKIM-ключи, лимиты и другие данные, которые трогать нельзя.

cd /opt/mailcow-dockerized && source mailcow.conf
# 1. Сбросить Redis на диск и скопировать dump.rdb — обязательно
docker compose exec redis-mailcow redis-cli -a "$REDISPASS" SAVE
cp /var/lib/docker/volumes/mailcowdockerized_redis-vol-1/_data/dump.rdb /root/dump.rdb.$(date +%F)

# 2. Удаление ключей Bayes и связанных RS*-структур точечно по шаблону (как в документации)
docker compose exec redis-mailcow sh -c \
  "redis-cli -a '$REDISPASS' --scan --pattern 'BAYES_*' | xargs -r redis-cli -a '$REDISPASS' del"
docker compose exec redis-mailcow sh -c \
  "redis-cli -a '$REDISPASS' --scan --pattern 'RS*' | xargs -r redis-cli -a '$REDISPASS' del"

Ключ -r у xargs я добавляю специально: в документации его нет, и если по шаблону ничего не нашлось, del запускается без аргументов и отвечает «wrong number of arguments for del». Это не сбой, а признак, что удалять было нечего. Имя тома mailcowdockerized_redis-vol-1 зависит от COMPOSE_PROJECT_NAME — сверьте его через docker volume ls. После сброса модель начинает учиться заново с нуля, поэтому в первые дни стоит быть внимательнее к ложным срабатываниям и, если нужно, ускорить накопление статистики ручным прогоном rspamc learn_spam/learn_ham по свежему архиву уже классифицированных писем.

Не сбрасывайте Bayes «на всякий случай» вслед за каждым релизом mailcow. Это разовая мера для реально перекошенной модели — если фильтр работает предсказуемо и ложных срабатываний немного, трогать статистику не нужно.
Пошаговая схема безопасного сброса статистики Bayes в Redis mailcow без потери остальных данных
Сброс — это точечное удаление двух шаблонов ключей, а не очистка Redis целиком.

Что я советую настроить сразу, чтобы не разбирать это по жалобам

Из этого и похожих разборов на других клиентах я вынес практику, которую теперь включаю в первичную настройку любого mailcow: убедиться, что у всех почтовых ящиков спам уходит именно в серверную папку Junk — mailcow создаёт её автоматически (auto = subscribe), а клиент подхватывает её как папку спама, если профиль настроен через автонастройку, а не собран вручную по старой памяти. Папки «Спам» или «Нежелательная почта», созданные клиентом рядом, обучение не запускают, даже с атрибутом \Junk.

Второе — короткая памятка для сотрудников без технических подробностей: «спам переносим строго в папку Спам/Junk, случайно попавшее туда легитимное письмо — переносим обратно во Входящие». Это закрывает оба сценария обучения одним предложением и не требует объяснять сотрудникам, что такое imapsieve или Bayes.

Третье — регулярный, но не навязчивый контроль: раз в квартал проверяю в веб-интерфейсе Rspamd долю писем с символом BAYES_SPAM/BAYES_HAM в общей истории. Если она стабильно близка к нулю при заметном объёме подозрительной почты — значит, где-то в цепочке снова сломалось обучение, и дешевле поймать это на плановой проверке, чем на жалобе клиента о пропущенном спаме или потерянном письме от поставщика.

И последнее: обучение через imapsieve — это дополнение к базовой антиспам-конфигурации, а не замена ей. Если у вас в принципе не настроены SPF, DKIM и DMARC на входящей и исходящей стороне, Bayes не вытянет качество фильтрации в одиночку — про базовую настройку у меня есть отдельный разбор DKIM, SPF и DMARC. А плановые проверки вроде этой я свёл в регламент эксплуатации mailcow.

Частые вопросы

Перенос письма в «Спам» в Outlook точно обучает Rspamd в mailcow?

Да, но только если письмо попадает в серверную папку с именем Junk. В эталонном dovecot.conf mailcow правило imapsieve привязано к имени папки (imapsieve_mailbox1_name = Junk), поэтому своя папка «Спам» или «Нежелательная почта» обучение не запускает, даже если у неё есть атрибут \Junk.

Где посмотреть, что обучение реально происходит?

Проще всего — веб-интерфейс Rspamd (/rspamd/), вкладка История: если в списке сработавших правил по подозрительным письмам регулярно встречается символ BAYES_SPAM или BAYES_HAM, статистическая модель участвует в оценке. Для точечной диагностики можно временно включить debug_modules = ["bayes"] в override.d/logging.custom.inc и смотреть логи rspamd-mailcow в реальном времени при тестовом переносе письма.

Перенос письма в Корзину тоже чему-то учит фильтр?

Нет, если папка называется ровно Trash. Перенос в Junk обучает как spam, перенос из Junk в любую другую папку — как ham; report-ham.sieve пропускает только папку с именем Trash, поэтому «Корзина» с другим серверным именем обучит письмо как легитимное.

Нужно ли сбрасывать Bayes после каждого крупного обновления mailcow?

Нет, это не постоянная практика. Разовая рекомендация была актуальна для релиза 2024-08 из-за конкретного изменения в Rspamd. Сбрасывать статистику стоит только тогда, когда модель реально накопила перекос от противоречивого обучения — например, после периода, когда сотрудники массово ошибались с папками.

Как безопасно сбросить только статистику Bayes, не затронув остальные данные Redis?

Сначала выполнить SAVE в Redis и скопировать dump.rdb из тома redis-vol-1, затем удалить ключи точечно по шаблонам BAYES_* и RS* через redis-cli --scan вместе с xargs del — не трогая остальную базу, где mailcow хранит, например, DKIM-ключи и настройки fail2ban.

Можно ли обучить фильтр сразу большим архивом старого спама, не дожидаясь ручной сортировки почты?

Да, через rspamc learn_spam и rspamc learn_ham с файлами .eml — это разовый способ ускорить накопление статистики, например сразу после переноса на mailcow. Но он не заменяет постоянно работающий imapsieve на текущей переписке.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи