12 Май 2016

Отложенное пакетное обучение антиспам-фильтра используя Dovecot

Delayed packet antispam learning using Dovecot

Dovecot является одним из самых популярных серверов IMAP4/POP3 в силу своей надёжности, безопасности, производительности и богатому функционалу.

Разнообразие предоставляемых им средств ещё более раширяется при помощи набора подключаемых дополнений (plugins), к числу которых относится и Dovecot antispam plugin. Он позволяет автоматизировать процесс контентного обучения антиспам-систем встраивая его в простую операцию перемещения письма в или из папки СПАМ. Это решение позволяет сделать обучение прозрачным для всех почтовых клиентов поддреживающих протокол IMAP4 и не требует на их стороне никаких дополнительных настроек.

Традиционный метод работы Dovecot antispam plugin предполагает выполнение специального shell-скрипта вызывающего программный модуль обучения антиспам-системы непосредственно при действии пользователя связанного с помещением письма (или их набора) из или в папку СПАМ. Такой метод, помимо положительной строны в виде немедленной подстройки антиспам-системы, имеет и большой недостаток - в ряде клиентов, в частности в популярном веб-клиенте Roundcube, это приводит к заметным задержкам в работе системы, а иногда и невозможности завершения процесса обучения и, вследствие этого, даже и самого перемещения сообщений между папками при выборе некоторого количества писем за один приём.

Решением может быть использование механизма отсроченного обучения пакета помеченных как спам или не-спам писем по расписанию, реализацию которого я предлагаю вашему вниманию.

Имеем установленный сервер Dovecot на FreeBSD.

root@beta:~ # uname -v
FreeBSD 10.3-RELEASE-p2 #0: Wed May  4 06:03:51 UTC 2016     root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC
root@beta:~ # dovecot --version
2.2.24 (a82c823)

1. Установка Dovecot antispam plugin

Для начала установим, собственно, сам Dovecot antispam plugin.

root@beta:~ # pkg install dovecot2-antispam-plugin
Updating FreeBSD repository catalogue...
Fetching meta.txz: 100%    940 B   0.9kB/s    00:01
Fetching packagesite.txz: 100%    5 MiB 636.7kB/s    00:09
Processing entries: 100%
FreeBSD repository update completed. 25212 packages processed.
Updating database digests format: 100%
The following 1 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        dovecot2-antispam-plugin: 20130429_18

22 KiB to be downloaded.

Proceed with this action? [y/N]: y
Fetching dovecot2-antispam-plugin-20130429_18.txz: 100%   22 KiB  22.6kB/s    00:01
Checking integrity... done (0 conflicting)
[1/1] Installing dovecot2-antispam-plugin-20130429_18...
[1/1] Extracting dovecot2-antispam-plugin-20130429_18: 100%
Message from dovecot2-antispam-plugin-20130429_18:
---------------------------------------------------------------------

This port assumes you are known with Dovecot and have it installed and
running on the system you have installed this plugin on.

Further information on configuration can be found at:

    man 7 dovecot-antispam

---------------------------------------------------------------------

Далее следует подключить установленный плагин к основной конфигурации Dovecot.

root@beta:~ # cd /usr/local/etc/dovecot/
root@beta:/usr/local/etc/dovecot # cat dovecot.conf
# --- by Max Kostikov 2010...2016 v.20160505
...
# -- 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-spam
  antispam_mail_notspam = %u-ham
  antispam_mail_sendmail_args =
}
...
protocol imap {
  mail_plugins = $mail_plugins antispam imap_acl imap_quota
  imap_client_workarounds = delay-newmail tb-extra-mailbox-sep
}
...

При этом при перемещении письма (или нескольких) из или в папку СПАМ будет вызываться shell-script move-cmd.sh, которому в качестве параметров будет передаваться строка в формате пользователь-тип (spam или ham).

2. Скрипты перемещения и обучения

Создадим папку /var/spool/dovecot-learn для временного хранения писем перед обучением и дадим ей права доступа для пользователя, от которого работает Dovecot.

root@beta:/usr/local/etc/dovecot # cd /var/spool/
root@beta:/var/spool # mkdir dovecot-learn
root@beta:/var/spool # chown mailnull:mail dovecot-learn
root@beta:/var/spool # ll
total 18
drwxrwx---  2 smmsp     smmsp   512 20 ноя  2014 clientmqueue/
drwxr-xr-x  2 mailnull  mail    512 12 май 22:26 dovecot-learn/
drwxr-x---  8 mailnull  mail    512 19 окт  2015 exim/
drwxrwxr-x  2 uucp      dialer  512  6 май 10:01 lock/
drwxr-xr-x  2 root      daemon  512 11 ноя  2014 lpd/
drwxr-xr-x  5 root      daemon  512 10 ноя  2015 mqueue/
drwx------  2 root      daemon  512 11 ноя  2014 opielocks/
drwxr-xr-x  3 root      daemon  512 11 ноя  2014 output/
drwxr-xr-x  2 spamd     spamd   512  9 мар  2014 spamd/

Создадим скрипт для подготовки базы для обучения антиспам-фильтра.

root@beta:/var/spool # cd /usr/local/etc/dovecot/
root@beta:/usr/local/etc/dovecot # touch move-cmd.sh
root@beta:/usr/local/etc/dovecot # chown mailnull:mail move-cmd.sh
root@beta:/usr/local/etc/dovecot # chmod a+x move-cmd.sh
root@beta:/usr/local/etc/dovecot # ll move-cmd.sh
-rwxr-xr-x  1 mailnull  mail  251 12 май 22:19 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

Чтобы изменения в конфигурации и сам скрипт начали работать, не забудем перезагрузить конфигурацию Dovecot.

root@beta:/usr/local/etc/dovecot # service dovecot restart
Stopping dovecot.
Waiting for PIDS: 86747.
Starting dovecot.

Если теперь попробовать перенести какое-нибудь сообщение в папку СПАМ, то наш скрипт создаст файл с телом письма и именем в формате пользователь-(spam или ham)-код.msg во временной папке для последующей обрабоки скриптом обучения по расписанию. При этом действие протоколируется в лог-файл.

root@beta:/usr/local/etc/dovecot # tail /var/log/dovecot-learn.log
2016-05-12 23:16:17 MOVE user@my.server-spam-86786.msg
root@beta:/usr/local/etc/dovecot # ll /var/spool/dovecot-learn/
-rw-------  1 mailnull  mail  127237 12 май 23:16 user@my.server-spam-86786.msg

Как видно, в данной почтовой системе в качестве имени пользователя используется полный e-mail адрес. Число в конце имени файла это номер процесса Dovecot (PID), который его создал.

Создадим файл обучающего shell-скрипта.

root@beta:/usr/local/etc/dovecot # touch learn-cmd.sh
root@beta:/usr/local/etc/dovecot # chown mailnull:mail learn-cmd.sh
root@beta:/usr/local/etc/dovecot # chmod a+x learn-cmd.sh
root@beta:/usr/local/etc/dovecot # ll learn-cmd.sh
-rwxr-xr-x  1 mailnull  mail  0 12 май 23:07 learn-cmd.sh*

Сам скрипт learn-cmd.sh вызывает, в данном случае, утилиту для обучения антиспам-фильтра из пакета Spamassassin. Однако, он без труда может быть адаптирован под любой другой обработчик.

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 (spam or ham)
        now=$(date +"%F %T")
        echo -n "$now LEARN $i: " >> $log
                if [ $maxsize -gt `stat -f%z $dir/$i` ];
        then
                sa-learn -u $user --$act $dir/$i >> $log
        else
                echo -n "skipped due exceed maximum size" >> $log
        fi
        rm -f $dir/$i
done

Действия также протоколируются в тот же лог-файл, который используется предыдущим скриптом.

Добавим его в cron для выполнения, например, один раз в три часа.

root@beta:/usr/local/etc/dovecot # cat /etc/crontab | grep learn-cmd.sh
0       */3     *       *       *       mailnull /usr/local/etc/dovecot/learn-cmd.sh >/dev/null 2>&1

И пронаблюдаем результат работы для нашего примера.

root@beta:/usr/local/etc/dovecot # tail /var/log/dovecot-learn.log
2016-05-12 23:16:17 MOVE user@my.server-spam-86786.msg
2016-05-13 00:00:00 LEARN user@my.server-spam-86786.msg: Learned tokens from 1 message(s) (1 message(s) examined)

3. PROFIT!

Статья была полезной? Тогда прошу не стесняться и поддерживать деньгами через PayPal или Яндекс.Деньги.