ClamAV в mailcow: ложные срабатывания и как их снять
АйТи Фреш
Информационная безопасность

ClamAV в mailcow блокирует чистые письма: сторонние базы, ложные срабатывания и как их снять

Автор: , директор ООО «АйТи-Фреш» · · ~13 мин чтения
Иллюстрация: несогласованные веса антивируса в mailcow блокируют безобидное письмо и пропускают лишний риск
Без согласованных весов в composites.conf антивирус путает безобидную рассылку с угрозой

Сторонние базы SecuriteInfo резко повышают детект ClamAV в mailcow, но без правильной настройки оценок в Rspamd легитимные письма с вложениями начинают блокироваться наравне с реальными угрозами. Разбираю, как согласовать antivirus.conf и composites.conf, чем whitelist.ign2 отличается от allow-list по хешу и почему после исключения сигнатуры письмо всё равно может блокироваться из кэша.

Симптом: чистый PDF или обычная рассылка получают VIRUS_FOUND

Типичный сценарий, с которым ко мне обращаются после самостоятельной настройки антивируса для корпоративной почты на mailcow: администратор подключает дополнительные базы сигнатур SecuriteInfo к штатному ClamAV, чтобы ловить больше спама и вредоносных вложений — и почти сразу получает жалобы, что обычные счета в PDF, коммерческие предложения или рассылка с картинками начинают отклоняться с пометкой VIRUS_FOUND. Часто это происходит не из-за реальной угрозы, а из-за того, что новые сигнатуры не согласованы с системой оценок Rspamd, которая и принимает финальное решение — пропустить письмо, поместить в карантин или отклонить.

Второй, более узкий вариант той же проблемы — конкретный документ с активным содержимым (например, PDF со встроенным JavaScript для формы) стабильно ловится одной и той же сигнатурой, хотя вложение проверено вручную и опасности не несёт. Администратор добавляет сигнатуру в исключения, перезапускает ClamAV — а письмо с тем же вложением всё равно блокируется, потому что результат предыдущей проверки уже закэширован и переопределение сигнатуры на него не действует.

Как ClamAV и Rspamd вместе решают судьбу письма

В mailcow ClamAV не блокирует письма самостоятельно — он только сканирует вложения и передаёт название сработавшей сигнатуры в Rspamd, а окончательное решение принимает уже Rspamd на основе весов и композитных правил. Подключение SecuriteInfo штатно поддерживается начиная с mailcow версии 2023-07 и новее — именно тогда в composites.conf появились предопределённые оценки для его сигнатур. Без них (и без блока patterns) любое срабатывание SecuriteInfo Rspamd считает обычным вирусом: символ CLAM_VIRUS, вердикт VIRUS_FOUND с оценкой 2000 и отказ — как пишет документация, Rspamd «беспощадно помечает ВСЁ как VIRUS», хотя база ориентирована не только на вирусы, но и на маркетинговый спам.

Конфигурация делится на два файла, и это ключевая причина большинства проблем: правила сопоставления имени сигнатуры с типом (patterns { ... }) живут в data/conf/rspamd/local.d/antivirus.conf внутри секции clamav { ... }, а сами числовые оценки для этих типов — в data/conf/rspamd/local.d/composites.conf. Если добавить блок patterns не туда (например, вне секции clamav) — Rspamd в логе выдаёт ошибку unknown antivirus type: patterns, и правило просто не применяется, антивирус продолжает работать по дефолтным весам.

Если у вас DKIM/SPF/DMARC ещё не настроены или настроены частично, ложные блокировки ClamAV легко перепутать с проблемами доставки — оба симптома выглядят как «письмо не дошло». Прежде чем разбирать веса антивируса, стоит отдельно проверить базовую настройку DKIM, SPF и DMARC, чтобы не лечить не ту причину.

Схема прохождения вложения от сканирования ClamAV через веса composites.conf до решения Rspamd
ClamAV только называет сигнатуру — решение о блокировке принимает вес в composites.conf

Как правильно подключить SecuriteInfo и не получить лавину ложных срабатываний

Рабочий блок patterns для SecuriteInfo выглядит так — вставляется внутрь секции clamav в antivirus.conf:

patterns {
  CLAM_SECI_SPAM = "^SecuriteInfo\.com\.Spam.*";
  CLAM_SECI_JPG = "^SecuriteInfo\.com\.JPG.*";
  CLAM_SECI_PDF = "^SecuriteInfo\.com\.PDF.*";
  CLAM_SECI_HTML = "^SecuriteInfo\.com\.HTML.*";
  CLAM_SECI_JS = "^SecuriteInfo\.com\.JS.*";
}

После правки обязательно docker compose restart rspamd-mailcow, иначе Rspamd продолжит работать со старым конфигом. Веса уже заданы в composites.conf mailcow композитами: CLAMD_SPAM_FOUND — 5, CLAMD_BAD_PDF, CLAMD_BAD_JPG, CLAMD_ASCII_MALWARE, CLAMD_HTML_MALWARE и CLAMD_JS_MALWARE — по 8; меняется параметр score нужного композита. И именно здесь стоит быть избирательным, а не копировать чужой готовый конфиг один в один: разные проекты выкладывают разные наборы весов, и то, что подошло чужой инсталляции с большим потоком спама, может оказаться избыточно строгим для небольшой компании с редкими вложениями.

Сами базы подключаются на стороне ClamAV строками DatabaseCustomURL с персональным идентификатором в data/conf/clamav/freshclam.conf. Для бесплатных баз SecuriteInfo скорость скачивания ограничена 300 кБ/с, поэтому документация советует поднять ReceiveTimeout с 20 до 90 секунд, а с полным набором баз ClamAV занимает около 1,3 ГБ ОЗУ — на маленьком VPS это надо учесть заранее. Отдельно документация mailcow прямо предупреждает про базу spam_marketing.ndb из состава SecuriteInfo: она классифицирует адреса как маркетинговые/спам-рассылки и известна значительным числом ложных срабатываний — подключать её стоит на свой риск и точно не с тем же весом, что реальные вирусные сигнатуры. У поставщика есть и обратная база — securiteinfo.ign2 — список собственных исключений SecuriteInfo, которую имеет смысл подключать параллельно с patterns, а не вместо него. И ещё один практический нюанс: оба файла, antivirus.conf и composites.conf, могут быть перезаписаны штатным обновлением mailcow — конфигурацию стоит держать в отдельном бэкапе или скрипте, который накатывает её заново после апдейта.

Whitelist.ign2: точечное исключение конкретной сигнатуры

Когда проблема не в лавине ложных срабатываний от новой базы, а в одной конкретной сигнатуре на конкретном типе документов — например, PUA.Pdf.Trojan.EmbeddedJavaScript-1 на формах с JavaScript, — документация mailcow предлагает точечный путь: добавить имя сигнатуры в data/conf/clamav/whitelist.ign2 и перезапустить контейнер: docker compose restart clamd-mailcow. Это глобальное исключение по имени сигнатуры — оно применяется ко всем письмам, где эта сигнатура сработала бы, вне зависимости от отправителя или конкретного файла.

Важно понимать разницу между этим механизмом и allow-list по хешу файла, который описан уже не в документации mailcow, а в общей документации ClamAV: .ign2/ignore-list исключает сигнатуру целиком — то есть любое письмо, где ClamAV распознает именно эту сигнатуру, перестанет блокироваться. А allow-list по хешу разрешает конкретный файл, не трогая саму сигнатуру: MD5-подпись кладётся в файл .fp, SHA1 или SHA256 — в .sfp (генерируется sigtool --sha256 файл) — остальные письма с той же сигнатурой продолжат блокироваться. Для разовой легитимной формы разумнее точечный allow-list по хешу, а не глобальное исключение сигнатуры — иначе вы теряете защиту от реальных вредоносных PDF с этой же сигнатурой у других отправителей. Нюанс mailcow: штатно из data/conf/clamav/ в базу копируется только whitelist.ign2 (это делает стартовый скрипт clamd-mailcow), а файл .sfp придётся положить прямо в каталог баз /var/lib/clamav внутри контейнера — он живёт в томе clamd-db-vol-1, и после крупных обновлений его наличие стоит проверять.

Широкое исключение через whitelist.ign2 отключает сигнатуру для всех писем, а не только для конкретного проверенного документа. Для разового случая надёжнее allow-list по хешу файла (.fp/.sfp), а не глобальный whitelist.
Сравнение глобального исключения сигнатуры через whitelist.ign2 и точечного allow-list по хешу файла
Глобальное исключение проще, но точечный allow-list по хешу не открывает дыру для реальных угроз

Почему письмо всё ещё блокируется после исключения — кэш Redis

Самая частая причина, по которой администратор добавляет сигнатуру в whitelist.ign2, перезапускает clamd-mailcow, а письмо с тем же вложением всё равно получает VIRUS_FOUND, — результат предыдущей проверки уже закэширован в Redis. mailcow кэширует результаты сканирования ClamAV, чтобы не гонять повторную проверку одного и того же вложения на каждый пересыл, и если файл уже был проверен и помечен как заражённый до правки whitelist, старый результат продолжает действовать, пока кэш не очищен.

Команда для очистки именно кэша ClamAV в Redis из документации mailcow: source mailcow.conf; docker compose exec redis-mailcow sh -c "redis-cli -a $REDISPASS KEYS 'rs_cl*' | xargs redis-cli -a $REDISPASS DEL". После этого стоит заново отправить проблемное письмо (или попросить отправителя переслать его) — только тогда ClamAV просканирует вложение заново уже с учётом нового whitelist.ign2, а не отдаст старый результат из кэша. Я всегда включаю этот шаг в чек-лист при разборе жалоб на ложные срабатывания — без него можно потратить час, меняя конфиг и не понимая, почему ничего не меняется.

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

Кейс «Аналитика спроса»: подключили SecuriteInfo — заблокировали собственную рассылку

У клиента — маркетингового агентства «Аналитика спроса», 26 рабочих мест — администратор подключил SecuriteInfo к ClamAV для усиления защиты от фишинговых вложений после серии подозрительных писем клиентам агентства. Блок patterns скопировали со стороннего форума и вставили в antivirus.conf, не проверив, в какую секцию он попал, и не заглянув в лог Rspamd после перезапуска. В первый же день собственная рассылка агентства с HTML-версией письма и вложенным PDF-брифом начала получать VIRUS_FOUND и блокироваться на исходящей стороне, хотя вложение было тем же файлом, что рассылался месяцами без проблем.

Разбор занял около полутора часов: в логе clamd-mailcow нашли срабатывание сигнатуры из семейства SecuriteInfo.com.HTML.* на HTML-версию письма с трекинг-пикселем и JS-счётчиком открытий, а в логе Rspamd — ту самую ошибку unknown antivirus type: patterns: блок с форума вставили после закрывающей скобки секции clamav. В итоге каждое срабатывание SecuriteInfo становилось CLAM_VIRUS с отказом. Перенесли patterns внутрь clamav { ... } и перезапустили rspamd-mailcow; дальше HTML-срабатывание получало уже 8 баллов композита CLAMD_HTML_MALWARE и вместе с «маркетинговыми» символами самой рассылки всё равно дотягивало до порога. Снизили score этого композита до 3, оставив CLAMD_JS_MALWARE и остальные на 8, и отключили базу spam_marketing.ndb, которую администратор подключил вместе с остальными «на всякий случай». Глобально исключать сигнатуру через whitelist.ign2 не стали: похожая сигнатура на чужих вложениях у клиентов агентства блокироваться по-прежнему должна была. После правки и очистки кэша Redis рассылка пошла без блокировок, а полноценные вредоносные вложения на тестовых письмах ClamAV продолжил ловить.

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

Пошаговый чек-лист диагностики и исправления ложных срабатываний ClamAV в mailcow
Без очистки кэша Redis правка конфига может казаться нерабочей ещё час

Чек-лист диагностики ложных срабатываний ClamAV

Когда приходит жалоба на блокировку легитимного письма, я прохожу по одному и тому же порядку. Сначала — найти в логах ClamAV (docker compose logs clamd-mailcow | grep FOUND) точное имя сработавшей сигнатуры, а не гадать по симптомам. Затем понять, массовая это проблема (новая сигнатура с неверным весом после подключения сторонней базы) или разовая (конкретный файл и конкретная сигнатура) — от этого зависит, что править: composites.conf целиком или whitelist.ign2/allow-list точечно.

Дальше — если решили менять оценку сигнатуры, править score композита в composites.conf, а не переписывать patterns; если решили точечно исключить документ — allow-list по хешу, а не широкий whitelist.ign2, если есть риск, что та же сигнатура пригодится на других отправителях. И в конце — обязательно очистить кэш Redis по ключам rs_cl* и попросить переслать письмо заново, иначе результат правки будет не виден и создастся ложное впечатление, что настройка не сработала. Отдельно держу в голове, что оба конфига могут слететь при обновлении mailcow — после крупного апдейта стоит свериться, что patterns и веса всё ещё на месте.

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

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

Почему ClamAV в mailcow вообще не блокирует письма без сторонних баз?

Блокирует, но штатные сигнатуры ClamAV ориентированы в первую очередь на известные вредоносные файлы и покрывают меньше спам-паттернов. SecuriteInfo расширяет детект, но требует согласования весов через composites.conf — иначе легитимные письма попадают под те же жёсткие правила, что и реальные угрозы.

С какой версии mailcow можно подключать SecuriteInfo?

С 2023-07 и новее — именно тогда появились предопределённые оценки сигнатур SecuriteInfo в интеграции с Rspamd. На более старых версиях блок patterns можно добавить, но без готовых весов ложных срабатываний будет заметно больше.

В чём разница между whitelist.ign2 и allow-list по хешу?

whitelist.ign2 исключает сигнатуру целиком — все письма, где она сработала бы, перестают блокироваться. Allow-list по хешу разрешает конкретный файл (MD5 — в .fp, SHA1/SHA256 — в .sfp), не трогая сигнатуру для остальных писем. Для разового легитимного документа безопаснее allow-list по хешу.

Добавил сигнатуру в whitelist.ign2, перезапустил clamd-mailcow — письмо всё равно блокируется, почему?

Скорее всего, результат предыдущей проверки уже в кэше Redis. Нужно очистить ключи rs_cl* командой redis-cli -a $REDISPASS KEYS 'rs_cl*' | xargs redis-cli -a $REDISPASS DEL и заново прогнать письмо через антивирус.

Стоит ли подключать базу spam_marketing.ndb из SecuriteInfo?

Документация mailcow прямо предупреждает о значительном числе ложных срабатываний этой базы. Я подключаю её только с явно заниженным весом относительно вирусных сигнатур и только если у клиента реально высокий поток маркетингового спама, который не ловится стандартными правилами Rspamd.

Почему после обновления mailcow настройки антивируса снова стали давать ложные срабатывания?

antivirus.conf и composites.conf могут быть перезаписаны при обновлении mailcow — это отдельно указано в документации. Конфигурацию SecuriteInfo стоит хранить отдельно (скрипт, git, бэкап) и накатывать заново после каждого крупного апдейта.

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

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

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

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

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

Источники

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