Использование спам ловушек для обучения байесовских фильтров - часть 2.


После двух месячного использования спам ловушек и анализа их эфективности, захотелось привести все в более правильный вид.

Как все будет работать:

  • Всю информацию по спам ловушкам храним в файле sqlite 3, можно и в MySQL - поменять нужно будет не много.
  • Подключение к exim:
    • Информация обо всех получателях для нашего домена будем собирать в нашу базу
    • Действующей спам ловушкой будем считать все адреса которые соответствуют правилам:
      • общее количество попыток доставки на этот адрес больше X
      • адрес включен для работы в качестве спам ловушки (Т.к. запихнул в самое начало всех проверок у exim, то требовалось, чтобы имеющиеся адреса, не попадали в черный список. Насколько корректно, запихивать в самое начало проверок, пока не осознал. Из мыслей, возможно, полезно запихнуть до антивирусной проверки, чтобы на вирусах тоже училось как плохому)
    • Все письма приходящие на адрес действующей спам ловушки складываем в определенную папочку. Была идея, более стандартно, складывать в какой-нибудь почтовый ящик, но т.к. для локальной доставки у меня используется Dovecot, то решил, что это вызовет дополнительные накладные расходы. А операция записи в файл, вроде, должна быть менне затратной.
  • Обучение спам фильтров (их может несколько разных):
    • Скрипт обучения вызываем по крону с какой-нить периодичностью, у меня - 5 минут. Чем чаще, тем раньше будет распознан новый вид спама.
      Была идея дергать скрипт из exim на каждом входящем письме, но боюсь, что при имеющихся >30 тыс. писем за рабочий день скрипт будет вызываться более 20 раз в минуту и нагрузка на сервер будет неадекватная.
    • Удаляем все дубли сообщений, т.к. на байесовые фильтры они не действуют (вроде).

Теперь, подробнее с примерами.

В конфигурационный файл exim добавляем:

В начало, для определения необходимых параметров:

#Файл базы данных
MY_ANTISPAM_DB=/var/db/my_anti_spam/spamtraps
#Количество писем, после которого включается спам ловушка
MY_ANTISPAM_MAXCOUNT=30
# Куда складываем входящий спам для обучения
MY_ANTISPAM_TRAP_DIR=/var/spool/mail/trap/spam

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

  warn domains          = +local_domains
        set acl_m_spamtrap      = ${lookup sqlite { MY_ANTISPAM_DB \
INSERT OR IGNORE into spamtraps(mail) values('$local_part@$domain'); \
UPDATE spamtraps SET \
count_mail=count_mail+1,total_mail=total_mail+1, last_access=CURRENT_TIMESTAMP \
where mail='$local_part@$domain';}{1}}

После него добавляет разрешающее правило для спам ловушек

  accept domains       = +local_domains
        condition = ${lookup sqlite { MY_ANTISPAM_DB \
select mail from spamtraps where mail='$local_part@$domain' and \
count_mail>MY_ANTISPAM_MAXCOUNT and enable=1;}{1}}

В секцию роутеров или в самое начало или перед локальной доставкой добавляем наш роутер (надеюсь помним/знаем, что последовательность роутеров критична):

spam_trap:
  driver = accept
  domains = +local_domains
  condition = ${lookup sqlite { MY_ANTISPAM_DB \
select mail from spamtraps where mail='$local_part@$domain' \
and count_mail>MY_ANTISPAM_MAXCOUNT and enable=1;}{1}}

# При желании, сюда можно добавить правила, для проверки имеющихся наших пользователей, что-то типа
#  condition = ${lookup mysql{SELECT maildir \
#FROM mailbox \
#WHERE username = LCASE('${quote_mysql:$local_part@$domain}') \
#AND active = 1}{no}{yes}}


  transport = spam_trap

И в секцию транспортов добавляем наш транспорт на доставку всего спама в директорию:

spam_trap:
  driver = appendfile
  directory = MY_ANTISPAM_TRAP_DIR
  create_directory
  directory_mode = 0777
  maildir_format
  mode = 0666
  no_mode_fail_narrower

Создаем необходимую нам таблицу:

echo "
CREATE TABLE spamtraps (
mail TEXT primary key,
count_mail INTEGER DEFAULT 0,
total_mail INTEGER DEFAULT 0,
create_time TIMESHTAMP DEFAULT CURRENT_TIMESTAMP,
last_access TIMESHTAMP DEFAULT CURRENT_TIMESTAMP,
enable INTEGER DEFAULT 1
);
" | sqlite3 /var/db/my_anti_spam/spamtraps
 

Не забываем владельца на файл с базой данных установить такого же, под которым работает exim, у меня это mailnull:

chown mailnull /var/db/my_anti_spam/spamtraps

На всякий случай, локальных пользователей можно занести в базу данных как отключенных.

echo "
INSERT OR REPLACE into spamtraps
(mail,enable)
values
(\"you_mail@your_domain\",0);" | sqlite3 /var/db/my_anti_spam/spamtraps

После перезапуска exim, все должно начать работать. Главное, не забыть проверить, что письма доходят до наших пользователей.

Скрипт для обучения спам фильтров (все параметры поменять на необходимые):

#!/bin/sh
spam_cmd="/usr/local/bin/spamprobe -d /var/db/spamprobe"
rspamd_cmd=" /usr/local/bin/rspamc "
path4train="/var/spool/mail/trap/spam/"
clear_after="+7d"
date=` date  "+%Y-%m-%d %H:%M"`
# Удаляем все дублирующиеся файлы
/usr/local/bin/fdupes -q -d -N ${path4train}/new/ | grep '[+]' | cat -b
#Проходим по всем файлам и скармливаем спам фильтрам
for file in ${path4train}/new/[0-9]*
do
        if [ ! -e "$file" ]       # Проверка наличия файла.
        then
#          echo "Файл $file не найден."; echo
          continue                # Переход к следующей итерации.
        fi
#Выводим для логов текущее время и обратабываемый файл.
echo ${date} `ls -l "$file"`
 ${spam_cmd} spam "$file" > /dev/null
 ${rspamd_cmd} learn_spam "$file" > /dev/null
# Переносим обработанные файлы
mv "$file" ${path4train}/cur/ > /dev/null
done

#Удаляем старые файлы
find ${path4train}/cur/ -type f -ctime ${clear_after} -delete