25 Июнь 2017

Обучаем антиспам с помощью Sieve в Dovecot

Antispam learning using Dovecot Sieve

Title: Обучаем антиспам с помощью Sieve в Dovecot Content: Вернуться к теме обучения антиспам-фильтров в Dovecot, которая ранее уже затрагивалась в одной из статей на этом сайте, меня заставило недавнее сообщение от всё помнящего "Charlie Root" прекращении поддержки широко используемого для этих нужд расширения Antispam plugin.

To: root@my.server
Subject: my.server weekly security run output
Message-Id: <E1dObLP-000MHX-25@my.server>
From: Charlie Root <root@my.server>
Date: Sat, 24 Jun 2017 05:02:43 +0200

Checking for packages with security vulnerabilities:
dovecot2-antispam-plugin-20130429_29: Tag: expiration_date Value: 2017-07-31
dovecot2-antispam-plugin-20130429_29: Tag: deprecated Value: Use pigeonhole instead. See https://wiki2.dovecot.org/HowTo/AntispamWithSieve

-- End of security output --

Действительно, благодаря наличию расширения протокола Sieve для IMAP согласно стандарту RFC6785 в Dovecot ничто не мешат использовать его для тех же задач. В качестве примера воспользуемся статьёй в wiki Dovecot и реализуем уже знакомый механизм отложенного пакетного обучения антиспам фильтра в комплексе Spamassassin.

Итак, имеем установленный Dovecot 2 последней на момент написания статьи версии в среде FreeBSD.

root@beta:~ # uname -v
FreeBSD 11.0-RELEASE-p9 #0: Tue Apr 11 08:48:40 UTC 2017     root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC
root@beta:~ # dovecot --version
2.2.30.2 (c0c463e)

Создадим необходимые для работы нового механизма настройки в конфигурационном файле Dovecot расположенном по стандартному пути /usr/local/etc/dovecot/dovecot.conf, не забыв удалить старые, относящиеся к Antispam plugin.

root@beta:/usr/local/etc/dovecot # cd /usr/local/etc/dovecot/
root@beta:/usr/local/etc/dovecot # diff -u dovecot.conf.bak dovecot.conf
--- dovecot.conf.bak    2017-06-24 13:52:23.656134000 +0200
+++ dovecot.conf        2017-06-24 15:28:18.465615000 +0200
@@ -134,13 +134,18 @@

 # -- Spam autolearning
 plugin {
-  antispam_backend = mailtrain
-  antispam_trash = Trash
-  antispam_spam = Junk
-  antispam_mail_sendmail = /usr/local/etc/dovecot/move-cmd.sh
-  antispam_mail_spam = %u-report
-  antispam_mail_notspam = %u-revoke
-  antispam_mail_sendmail_args =
+  sieve_plugins = sieve_imapsieve sieve_extprograms
+  # From elsewhere to Spam folder
+  imapsieve_mailbox1_name = Junk
+  imapsieve_mailbox1_causes = COPY
+  imapsieve_mailbox1_before = file:/usr/local/etc/dovecot/report-spam.sieve
+  # From Spam folder to elsewhere
+  imapsieve_mailbox2_name = *
+  imapsieve_mailbox2_from = Junk
+  imapsieve_mailbox2_causes = COPY
+  imapsieve_mailbox2_before = file:/usr/local/etc/dovecot/report-ham.sieve
+  sieve_pipe_bin_dir = /usr/local/etc/dovecot
+  sieve_global_extensions = +vnd.dovecot.pipe
 }

 service auth {
@@ -209,7 +214,7 @@
 }

 protocol imap {
-  mail_plugins = $mail_plugins antispam imap_acl imap_quota last_login
+  mail_plugins = $mail_plugins imap_sieve imap_acl imap_quota last_login
   imap_client_workarounds = delay-newmail tb-extra-mailbox-sep
 }

Здесь приведён вывод в формате программы diff с отличиями предыдущей конфигурации от новой, где минусом помечены удаляемые строки, а плюсом наоборот — добавляемые. Во-первых, из описания используемых расширений mail_plugins протокола IMAP плагин antispam заменён на imap_sieve. Далее, все упоминания расширения Antispam начинающиеся со строки antispam_ удалены и добавлены два блока обработки перемещений сообщений: в папку со спамом Junk (переменные imapsieve_mailbox1_) и, соответственно, из неё (imapsieve_mailbox1_), а также ряд необходимых для функционирования новой схемы переменных и расширений.

Каждый из обработчиков вызывает свой Sieve-скрипт.

root@beta:/usr/local/etc/dovecot # cat report-spam.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];

if environment :matches "imap.user" "*" {
  set "username" "${1}";
}

pipe :copy "move-cmd.sh" [ "${username}-report" ];
root@beta:/usr/local/etc/dovecot # cat report-ham.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];

if environment :matches "imap.mailbox" "*" {
  set "mailbox" "${1}";
}

if string "${mailbox}" "Trash" {
  stop;
}

if environment :matches "imap.user" "*" {
  set "username" "${1}";
}

pipe :copy "move-cmd.sh" [ "${username}-revoke" ];

Оба они передают обработку перемещаемого сообщения одному и тому же shell-скрипту move-cmd.sh, который ничем не отличается от ранее используемого совместно с раширением Antispam согласно описанного в статье "Отложенное пакетное обучение антиспам-фильтра используя Dovecot" механизма.

root@beta:/usr/local/etc/dovecot # ll move-cmd.sh
-rwxr-xr-x  1 mailnull  mail  328 24 июня  14:46 move-cmd.sh*
root@beta:/usr/local/etc/dovecot # cat move-cmd.sh
#!/bin/sh

# Move message to temporary directory for learning
# (c)2016 by Max Kostikov http://kostikov.co e-mail: max@kostikov.co

dir="/var/spool/dovecot-learn"                  # directory for messages
log="/var/log/dovecot-learn.log"
now=$(date +"%F %T")

echo "$now MOVE $*-$$.msg" >> $log
cat<&0 >> $dir/$*-$$.msg

exit 0

Вкратце, он сохраняет перемещаемое сообщение в специальной папке, откуда впоследствии согласно расписанию в cron будет вызван пакетный обработчик learn-cmd.sh, который и передаст все накопленные сообщения на обучение Spamassassin.

root@beta:/usr/local/etc/dovecot # ll learn-cmd.sh
-rwxr-xr-x  1 mailnull  mail  704 20 сент.  2016 learn-cmd.sh*
root@beta:/usr/local/etc/dovecot # cat learn-cmd.sh
#!/bin/sh

# Learn Spamassassin from temporary directory
# (c)2016 by Max Kostikov http://kostikov.co e-mail: max@kostikov.co

dir="/var/spool/dovecot-learn"                  # saved messages for learning
log="/var/log/dovecot-learn.log"
maxsize=2097152                                 # in bytes

for i in `ls ${dir}`;
do
        tmp=`echo $i | sed 's|-[0-9]*\.msg$||'`
        user=`echo $tmp | sed 's|-.*$||'`       # get username
        act=`echo $tmp | sed 's|^'$user'-||'`   # get action (report for spam or revoke for ham)
        now=$(date +"%F %T")
        echo -n "$now LEARN $i: " >> $log
        if [ $maxsize -gt `stat -f%z $dir/$i` ];
        then
                spamc -s $maxsize -u $user -C $act < $dir/$i >> $log
        else
                echo "skipped due exceed maximum size" >> $log
        fi
        rm -f $dir/$i
done

В данном случае используется универсальный клиент Spamassassin spamc, который во-первых, заметно быстрее традиционно применяемого для обучения клиента sa-learn, а, во-вторых, обладает дополнительным функционалом по отправке уведомлений о спаме во внешние системы фильтрации (детали см. в статье "Отправляем отчёты о спаме во внешние сервисы через Spamassassin"). Обратите внимание, что для реализации возможности обучения через него демон spamd должен быть запущен с ключом -l или --allow-tell.

Теперь осталось скомпилировать созданные sieve-скрипты в бинарную форму и перезапустить сервис Dovecot, попутно проверив наличие скрипта-обработчика в списке заданий cron.

root@beta:/usr/local/etc/dovecot # sievec report-spam.sieve
root@beta:/usr/local/etc/dovecot # sievec report-ham.sieve
root@beta:/usr/local/etc/dovecot # ll *.svbin
-rw-r--r--  1 root      mailnull  458 25 июня  13:17 report-ham.svbin
-rw-r--r--  1 root      mailnull  364 25 июня  13:17 report-spam.svbin
root@beta:/usr/local/etc/dovecot # cat /etc/crontab | grep learn-cmd
15      *       *       *       *       mailnull /usr/local/etc/dovecot/learn-cmd.sh >/dev/null 2>&1
root@beta:/usr/local/etc/dovecot # service dovecot restart
Stopping dovecot.
Waiting for PIDS: 10892.
Starting dovecot.

За результатами работы обработчиков можно, как и ранее, наблюдать в файле протокола /var/log/dovecot-learn.log.

root@beta:/usr/local/etc/dovecot # tail /var/log/dovecot-learn.log
2017-06-24 17:19:47 MOVE foo@my.server-report-1134.msg
2017-06-24 18:15:00 LEARN foo@my.server-report-1134.msg: Message successfully reported/revoked

Ну и, в качестве финальной точки, удаляем ставший уже ненужным Antispam plugin.

root@beta:/usr/local/etc/dovecot # pkg delete dovecot2-antispam-plugin
Checking integrity... done (0 conflicting)
Deinstallation has been requested for the following 1 packages (of 0 packages in the universe):

Installed packages to be REMOVED:
        dovecot2-antispam-plugin-20130429_29

Number of packages to be removed: 1

Proceed with deinstalling packages? [y/N]: y
[1/1] Deinstalling dovecot2-antispam-plugin-20130429_29...
[1/1] Deleting files for dovecot2-antispam-plugin-20130429_29: 100%

Задача решена!