Rspamd actions.conf не применяется к части ящиков mailcow
АйТи Фреш
Linux, Docker и DevOps

Изменил пороги спама в actions.conf, а отдельные ящики mailcow фильтруют по-старому: разбираем приоритеты Rspamd

Автор: , директор ООО «АйТи-Фреш» · · ~12 мин чтения
Глобальный порог спама Rspamd в mailcow изменён, но отдельные ящики продолжают работать по старым индивидуальным настройкам
Один общий регулятор, но часть ящиков давно переключена на собственный, никем не задокументированный.

Поднял порог reject в actions.conf, перезапустил rspamd-mailcow — и часть сотрудников продолжает терять письма или получать спам по старым правилам. Причина не в кэше и не в баге: у отдельных ящиков есть собственные пороги highspamlevel/lowspamlevel, которые глобальные настройки не трогают в принципе. Разбираю, откуда берётся приоритет и как привести все ящики к единому поведению.

Симптом: одни ящики видят новый порог, другие — нет

Типичная задача в корпоративной почте на mailcow: спам-фильтр либо слишком агрессивный (в спам улетают нормальные письма от контрагентов), либо слишком мягкий (спам доходит до входящих). Администратор открывает data/conf/rspamd/local.d/actions.conf, меняет пороги reject, add_header, greylist, перезапускает контейнер — и на тестовом ящике всё работает как задумано.

Но через день выясняется: у части сотрудников поведение фильтра не изменилось вообще. У кого-то спам как проходил во входящие, так и проходит; у кого-то, наоборот, письма от постоянных контрагентов продолжают падать в папку «Спам», хотя порог reject подняли специально, чтобы этого не происходило. Разница не связана с доменом, ролью или датой создания ящика — она случайная на первый взгляд, что сильнее всего сбивает с толку.

На деле дело не в самом Rspamd и не в том, что конфиг не применился — перезапуск rspamd-mailcow прошёл штатно, новые глобальные значения активны. Дело в том, что у части ящиков когда-то (часто сам пользователь, через собственный интерфейс) были заданы индивидуальные пороги, и они имеют приоритет выше глобальных настроек — по дизайну, а не по ошибке.

Где на самом деле живут пороги: actions.conf vs filterconf

Глобальные пороги спама в mailcow задаются файлом data/conf/rspamd/local.d/actions.conf — именно его официальная документация предлагает править. Учтите, что файл лежит в git-репозитории mailcow, поэтому свою правку фиксируйте в регламенте и сверяйте после каждого update.sh. Штатное содержимое такое:

# data/conf/rspamd/local.d/actions.conf
reject = 15;
add_header = 8;
greylist = 7;

После правки документация прямо указывает перезапустить именно Rspamd, а не пересобирать весь стек:

docker compose restart rspamd-mailcow

Но у mailcow есть отдельный слой — пользовательские настройки спамфильтра, доступные каждому сотруднику через личный интерфейс (вкладка Spam filter), а администратор может поменять то же значение для ящиков, к которым у него есть доступ. Они хранятся не в файле конфигурации, а в базе данных mailcow, в таблице filterconf, в виде пары параметров highspamlevel и lowspamlevel на конкретный ящик. Из этой таблицы mailcow собирает для Rspamd карту настроек (settings.php, её Rspamd забирает у nginx по адресу http://nginx:8081/settings.php): для каждого такого ящика появляется правило с priority = 4, где reject = highspamlevel, "add header" = lowspamlevel, а greylist = lowspamlevel − 1. Это ровно тот индивидуальный порог, который перекрывает глобальный actions.conf для конкретного адреса — и именно поэтому правка общего файла не меняет поведение тех ящиков, где такая запись уже есть.

Важно не путать два похожих, но разных слоя настроек: вкладка Spam filter в интерфейсе mailcow отвечает не только за числовые пороги — там же настраиваются персональные blacklist и whitelist адресов, которые тоже могут объяснять, почему конкретное письмо ведёт себя иначе, чем ожидает администратор. Но именно за расхождение в поведении при изменении actions.conf почти всегда отвечает не black/whitelist (он работает по конкретным адресам отправителя), а именно числовые highspamlevel/lowspamlevel — они меняют саму границу, после которой Rspamd вообще считает письмо подозрительным, независимо от отправителя.

Схема двух источников порогов спама в mailcow: глобальный actions.conf и персональный filterconf с приоритетом
Персональная запись в filterconf всегда перекрывает глобальный actions.conf для конкретного ящика.

Почему глобальные настройки не перезаписывают личные — и это осознанно

В официальной документации mailcow по Rspamd Tweaks об этом сказано прямым текстом: изменение глобальных порогов не перезаписывает существующие настройки пользователей. Это не недоработка, а логичное поведение с точки зрения UX — если сотрудник один раз через свой личный интерфейс сознательно поднял порог чувствительности (например, потому что ему регулярно нужна рассылка, которую Rspamd маркирует пограничным баллом), администратор не должен иметь возможность тихо откатить это решение фоновой правкой общего конфига.

Проблема возникает не из-за самого механизма приоритета, а из-за того, что администраторы часто не знают о его существовании — особенно если персональные пороги были выставлены давно, другим сотрудником ИТ-поддержки, или самим пользователем через кнопку в интерфейсе, о которой все забыли. С точки зрения Rspamd это два совершенно независимых источника настроек: файл (local.d/actions.conf) задаёт действия по умолчанию, а правило из карты settings для конкретного получателя (rcpt) через apply "default" { actions { … } } подменяет пороги целиком — и reject, и «add header», и greylist. Поэтому у такого ящика глобальная правка не меняет вообще ничего, даже частично.

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

Как найти и сбросить персональные пороги

Первый шаг — проверить, у скольких ящиков вообще есть индивидуальные пороги, прежде чем менять что-либо массово. Это должно быть управляемое решение, а не случайный сброс всех персональных настроек компании одной командой без понимания масштаба. Если сервер разворачивался по нашему пошаговому регламенту установки mailcow, такой аудит проще — на старте персональных порогов обычно нет вообще, и любая найденная запись — заведомо результат более поздней ручной правки.

Официально задокументированный способ сбросить пользовательские пороги — прямой SQL-запрос к базе mailcow:

source mailcow.conf
docker compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow \
  -e "delete from filterconf where option = 'highspamlevel' or option = 'lowspamlevel';"

Это удаляет персональные пороги сразу у всех ящиков всех доменов на сервере — грубый, но предсказуемый инструмент. Если нужно вернуть к глобальным настройкам только один конкретный проблемный ящик, запрос сужают условием по адресу. Осторожно: в документации этот вариант напечатан без скобок (option = 'highspamlevel' or option = 'lowspamlevel' and object = …), а в SQL AND связывает сильнее OR, так что такой запрос снесёт highspamlevel у всех ящиков. Правильно — со скобками:

docker compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow \
  -e "delete from filterconf where (option = 'highspamlevel' or option = 'lowspamlevel') and object = 'only-this-mailbox@example.org';"

После удаления записей в filterconf ящик автоматически возвращается к глобальным порогам из actions.conf — отдельно перезапускать Rspamd для этого не требуется: карту settings.php Rspamd перечитывает сам при периодическом опросе, так что изменение вступает в силу с небольшой задержкой, без рестарта контейнера. Проверяю я это не по часам, а по заголовкам тестового письма — какое действие Rspamd реально применил.

Перед массовым DELETE предупредите сотрудников, у которых были осознанно настроены индивидуальные пороги — иначе почта, которую они специально разрешили себе получать, снова начнёт падать в спам.
Пошаговая инструкция сброса персонального порога спама одного ящика mailcow через SQL-запрос к filterconf
Точечный сброс безопаснее массового — не задевает осознанные настройки других сотрудников.

Кейс: «Мастер на час», 32 рабочих места

Клиент — сеть мастерских по бытовому ремонту «Мастер на час», 32 рабочих места. Проблема пришла как жалоба от бухгалтерии: письма с актами от поставщиков комплектующих стабильно улетали в спам, хотя администратор поднял add_header (порог перемещения в «Спам») и reject в actions.conf выше штатных 8 и 15, специально чтобы снизить ложные срабатывания. На тестовом ящике самого администратора всё заработало, у бухгалтерии — нет.

Проверили через docker compose exec mysql-mailcow mysql содержимое filterconf для ящика бухгалтерии и нашли там пару lowspamlevel/highspamlevel, где lowspamlevel стоял заметно ниже нового глобального add_header — судя по дате записи, его когда-то выставил предыдущий подрядчик, обслуживавший почту, видимо, в ответ на разовую волну спама, и с тех пор запись никто не трогал и не помнил о ней.

Решение заняло 15 минут: точечно удалили запись filterconf для конкретного ящика бухгалтерии тем же запросом с условием object = 'buh@domain.ru', не трогая остальные 31 ящик — чтобы не сбросить чьи-то осознанные персональные настройки. После этого ящик перешёл на новый глобальный порог, и письма от поставщиков начали доходить во входящие. Итог для клиента: ноль потерянных писем с актами за следующий отчётный период вместо двух-трёх пропущенных в месяц ранее.

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

Итоговые цифры кейса устранения расхождения порогов спама Rspamd для мастерской «Мастер на час»
Точечная правка одной записи в базе устранила регулярную потерю писем от поставщиков.

Как настроить процесс, чтобы это не повторялось

После разбора конкретного инцидента я обычно предлагаю клиенту простое правило из нашего регламента эксплуатации mailcow: любое изменение порогов документируется — кто, когда и для какого ящика поднял или опустил персональный порог, и по какой причине. Без этого через полгода-год никто не вспомнит, откуда взялась расходящаяся с общими настройками запись в filterconf, и каждая такая жалоба будет расследоваться заново с нуля.

Отдельно советую перед любой плановой правкой actions.conf сначала выяснить, сколько ящиков вообще затронет изменение, а не полагаться на предположение «у всех одинаковые настройки». На 32 ящиках «Мастер на час» ручная проверка заняла те же 15 минут, что и сама правка; на компании в 150+ ящиков я бы делал это через прямой SQL-запрос к filterconf, а не кликая по интерфейсу каждого сотрудника по очереди: docker compose exec mysql-mailcow mysql -umailcow -p$DBPASS mailcow -e "select object, option, value from filterconf where option in ('highspamlevel','lowspamlevel');" (предварительно source mailcow.conf).

Второе правило — не трогать индивидуальные настройки пользователей без необходимости просто потому, что «удобнее иметь один источник правды». Персональный порог — легитимный инструмент, которым сотрудник может решить свою конкретную проблему без похода к администратору; массовый сброс всех индивидуальных настроек ради унификации имеет смысл только как разовая мера при явном хаосе в конфигурации, а не как регулярная практика.

Третье, практическое: если вы регулярно меняете глобальные пороги в actions.conf (например, сезонно ужесточаете фильтр перед волной фишинга под отчётный период), заведите короткий скрипт-аудит, который раз в месяц выводит список ящиков с непустыми записями highspamlevel/lowspamlevel в filterconf — просто SELECT по таблице без сложной автоматизации. Это не решает проблему приоритета настроек — она остаётся частью логики mailcow и трогать её не нужно, — но избавляет от ситуации, когда о существовании десятка персональных порогов вспоминают только на разборе очередной жалобы.

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

Почему после docker compose restart rspamd-mailcow часть ящиков всё равно фильтрует по-старому?

Restart применяет новые глобальные пороги из actions.conf, но не трогает индивидуальные записи highspamlevel/lowspamlevel в таблице filterconf — они имеют приоритет для конкретного ящика и глобальным конфигом не перезаписываются.

Как узнать, есть ли у ящика персональные пороги, не лезя в базу?

Проще всего через собственный интерфейс пользователя (или от имени пользователя, если вы администратор домена) — вкладка Spam filter показывает текущие индивидуальные настройки, если они выставлены.

Можно ли сбросить пороги только одному ящику, не трогая остальные?

Да, в SQL-запрос на удаление добавляется условие and object = 'адрес@домен' — тогда удаляется запись только для этого конкретного ящика, остальные не затрагиваются.

Нужно ли перезапускать rspamd-mailcow после удаления записей из filterconf?

Нет, персональные пороги попадают в Rspamd через карту settings.php, которую он перечитывает сам при периодическом опросе, — изменение применяется с небольшой задержкой. Перезапуск нужен после правки самого файла actions.conf.

Актуален ли путь data/conf/rspamd/local.d/actions.conf для последних версий mailcow?

Да, это официально документированный путь глобальных порогов действий Rspamd; проверяйте актуальность своей версии перед правкой, если сервер давно не обновлялся.

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

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

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

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

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

Источники

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