ManageSieve: серверные фильтры, которые срабатывают всегда — из Roundcube и Thunderbird
Правила в Outlook и Thunderbird — иллюзия автоматизации: они работают, только пока клиент открыт, и не синхронизируются между устройствами. Настоящая фильтрация живёт на сервере, на этапе доставки. Разбираем Sieve (RFC 5228) и протокол ManageSieve (RFC 5804, порт 4190), поднимаем их на Dovecot Pigeonhole и подключаем к Roundcube.
EvilMail Team31 июля 2026 г.12 мин чтения
Почему ваши правила в клиенте не работают
Классическая история. Пользователь настроил в Thunderbird правило «письма из рассылки — в папку Lists», неделю радуется чистому инбоксу, а в понедельник утром находит в INBOX сорок писем от тех же рассылок. Почему? Ноутбук в выходные был выключен, почта прилетала на телефон, а правило живёт внутри Thunderbird и срабатывает только когда он запущен и успел синхронизировать папку.
Это системный дефект клиентской фильтрации, а не случайный сбой. У неё три врождённые проблемы:
Правила исполняются только при запущенном клиенте. Письмо, пришедшее ночью, разложится не в момент доставки, а когда вы утром откроете приложение — если вообще откроете именно этот клиент.
Правила не синхронизируются между устройствами. Один набор фильтров в вебе, другой на телефоне, третий на десктопе. Их приходится дублировать вручную, и они неизбежно расходятся.
POP3 конфликтует с локальными перемещениями. Если один клиент забирает почту по POP3 и утаскивает её к себе, остальные её вообще не увидят, и правила на них молчат.
Вывод простой: фильтр должен стоять
до
того, как письмо легло в ящик, а не после. То есть на сервере, в момент локальной доставки. Тогда он срабатывает на 100% входящих, одинаково для всех устройств, и ему безразлично, включён ли у вас хоть один клиент.
Где Sieve исполняется на самом деле
Sieve — это не плагин почтовой программы и не «правила», которые кто-то экспортирует. Это язык фильтрации, стандартизированный в RFC 5228, а исполняет его движок на сервере в момент локальной доставки (LDA/LMTP). Клиент к самому исполнению отношения не имеет.
Полная цепочка входящего письма выглядит так:
Ключевой момент: скрипт один на аккаунт, лежит на сервере и срабатывает на каждом входящем письме. Порядок исполнения фиксированный — сначала глобальный sieve_before (сюда обычно кладут раскладку спама), затем пользовательский скрипт, затем sieve_after. Пользователь правит только свой средний слой.
Анатомия Sieve-скрипта
Скрипт состоит из трёх вещей: require для подключения расширений, тестов (header, address, envelope, size) и действий. Вот рабочий пример, который делает три полезных дела сразу:
Implicit keep. Если ни одно действие не разложило письмо (fileinto) и не отменило доставку (discard), Sieve по умолчанию кладёт письмо в INBOX. Это «неявный keep». Именно поэтому в блоке рассылок стоит stop — без него после fileinto управление пошло бы дальше и логика могла задвоиться.
`stop` прекращает исполнение всего скрипта, а не только текущего блока. Здесь рассылка уходит в Lists, и до проверки [email protected] дело не доходит — это осознанный приоритет.
`vacation` без `:addresses` — граната. Автоответчик обязан знать, какие адреса считаются «вашими», иначе он начнёт отвечать на служебные адреса рассылок, и вы получите mail loop. По стандарту vacation не отвечает на письма с признаками bulk и на списки, но :addresses — ваша основная страховка.
Расширения, которые стоит держать в голове: vacation (RFC 5230), imap4flags (RFC 5232), variables (RFC 5229), relational (RFC 5231), плюс regex, editheader, spamtest/virustest и date. Каждое подключается через require, и без него соответствующие команды просто не скомпилируются.
ManageSieve: протокол, а не файл
Скрипт живёт в ~/sieve на сервере. Но как дать пользователю его редактировать, не выдавая SSH и права на файловую систему? Для этого есть отдельный протокол — ManageSieve, RFC 5804, TCP-порт 4190.
Важная оговорка про порт. Старые сборки Dovecot слушали ManageSieve на порту 2000, но 2000 официально закреплён IANA за cisco-sccp, поэтому сегодня это антипаттерн. Используйте только 4190.
Сессия текстовая и очень похожа на IMAP: CAPABILITY → STARTTLS → AUTHENTICATE (SASL) → работа. Набор команд компактный:
LISTSCRIPTS — список скриптов с отметкой активного.
PUTSCRIPT "name" {...} — загрузить или перезаписать скрипт.
GETSCRIPT "name" — забрать тело.
CHECKSCRIPT {...} — проверить синтаксис без сохранения.
SETACTIVE "name" — сделать скрипт активным (активным может быть только один).
DELETESCRIPT / RENAMESCRIPT — удалить или переименовать.
Самое ценное свойство протокола: на PUTSCRIPT и CHECKSCRIPT сервер компилирует и валидирует скрипт и при ошибке возвращает человекочитаемый ответ с номером строки. Невалидный фильтр физически не станет активным. Это принципиально безопаснее правки файла по SSH, где опечатка тихо ломает доставку.
Поднимаем на Dovecot Pigeonhole
Движок Sieve в Dovecot — это Pigeonhole. Нужны два пакета: dovecot-sieve (движок для LDA/LMTP) и dovecot-managesieved (демон протокола на 4190).
Что здесь происходит по строкам. Набор пользовательских скриптов лежит в ~/sieve/, активный симлинкуется в ~/.dovecot.sieve и компилируется в бинарник .svbin рядом — при доставке движок читает именно скомпилированную версию. sieve_max_script_size = 1M отсекает гигантские скрипты. sieve_before подключает глобальную антиспам-раскладку, которая отработает раньше пользовательского скрипта.
Права критичны. В виртуальных ящиках evilmail (/var/mail/vhosts/) каталог ~/sieve и файлы в нём должны принадлежать vmail — 5000:5000. Если владелец другой, managesieved не сможет писать, а LDA — читать, и вы получите тихий отказ без явной ошибки.
В ответ должен прийти листинг с "SIEVE" "fileinto vacation imap4flags ..." и STARTTLS. И обязательно запретите plaintext-логин без TLS: disable_plaintext_auth = yes. Порт 4190 в открытом виде наружу выставлять нельзя.
Подключаем Roundcube и Thunderbird
В Roundcube всё делает плагин managesieve. В config.inc.php:
Пользователь получает в настройках раздел «Фильтры» с визуальным конструктором: он собирает условия и действия мышкой, а плагин под капотом генерирует Sieve-код и отправляет его через PUTSCRIPT + SETACTIVE. Параметр managesieve_raw_editor = true дополнительно включает режим прямого редактирования кода — для того, что визуальный UI построить не умеет: переменные, регулярки, вложенная логика.
Thunderbird работает через расширение Sieve: тот же порт 4190, тот же TLS, но оно сразу открывает редактор кода. Удобно, если вы пишете фильтры руками и хотите видеть исходник, а не собирать его из блоков.
Оба клиента правят один и тот же серверный скрипт. Отредактировали в вебе — телефон и десктоп немедленно фильтруют по новым правилам, потому что фильтрует сервер, а не они.
Отладка: когда письмо не разложилось
Половина работы с Sieve — это разбор «почему письмо оказалось не там». Инструменты:
bash
# список и активный скрипт пользователя
doveadm sieve list -u user@domain
# вытащить тело конкретного скрипта
doveadm sieve get -u user@domain roundcube
# прогнать скрипт на реальном письме, ничего не сохраняя
sieve-test roundcube.sieve message.eml
# массовый прогон по ящику
sieve-filter -u user@domain roundcube.sieve INBOX
sieve-test показывает, какие действия сработали бы на конкретном .eml — самый быстрый способ поймать логику. В логах Dovecot ищите строки вида sieve: msgid=<...> stored mail into mailbox 'Lists' — они прямо говорят, куда движок положил письмо.
Типовые грабли, в порядке частоты:
Письмо всё равно в INBOX. Не отменён implicit keep: забыли stop или discard после нужного действия, либо условие не сматчилось (проверьте sieve-test).
vacation зациклился. Автоответ ушёл на рассылку или на другого автоответчика. Лечится полным списком :addresses и тем, что vacation не отвечает на bulk и списки.
TLS handshake fail на 4190. Клиент требует STARTTLS, а сертификат или конфиг демона его не отдаёт. Проверьте openssl s_client вручную.
Скрипт не сохраняется. Упёрлись в sieve_max_script_size или нет прав на ~/sieve (не 5000:5000).
Чеклист внедрения
Порт 4190 доступен только через TLS; disable_plaintext_auth = yes.
dovecot-managesieved запущен и отвечает на openssl s_client -starttls sieve.
sieve_before подключён для глобальной раскладки спама.
Raw-редактор включён в Roundcube для продвинутых пользователей.
Бэкап активного скрипта делается через doveadm sieve get.
Логи Dovecot мониторятся на признаки mail loop от vacation.
Если скрипты версионируются в git — CHECKSCRIPT (или sieve -c) прогоняется в CI до деплоя.
Перенеся фильтрацию с клиента на сервер, вы получаете один набор правил, который отрабатывает на каждом письме независимо от того, включён ли хоть один почтовый клиент. А ManageSieve оставляет пользователю право менять эти правила самому — из привычного веб-интерфейса, без SSH и без риска сломать доставку опечаткой.