Установка и настройка SpamAssassin

Посмотрел останки своего старого сайта с целью восстановить оттуда что-нить ценное. Понял, что ничего ценного там не было кроме статьи о федерации молодежи за мир во всем мире (которая, похоже окончательно утеряна, но и фиг с ней, муниты и так почти исчезли (из Москвы по крайней мере)) и HOWTO по настройке SpamAssassin.
Статью про СпамАссассин решил восстановить. В несколько сокращенном варианте (сейчас уже только ленивый не знает что это такое и зачем он нужен) и с поправками на мои нынешние настройки.

Итак начнем.
Вначале общие вещи тезисно:

  • Спам надо фильтровать. Для этого следует использовать сочетание всех возможных (доступных) методик. Использование только одной (двух, трех) недостаточно: только анализ содержимого, или только RBL
    дают достаточно низкое срабатывание с большим количеством false-positive. Результаты анализа с использованием разных методик должны суммироваться и решение должно приниматься с учетом всех факторов (например письмо с подозрительным содержимым от постоянного корреспондента с положительной историей должны быть пропущены фильтром).
  • Фильтровать можно в трех точках: SMTP-сессия, сервер-сайд при доставке (но после SMTP) и клиент-сайд.
  • SMTP-сессия – наилучшая точка фильтрации, неугодным можно послать аутлуп прямо в сессии, но есть существенная проблема: не всегда можно фильтровать сообщения прямо во-время приема – например, может не хватать ресурсов для такой on-line фильтрации, или ресурсов будет хватать, но слишком много времени занимает фильтрация и отправляющая сторона отваливается по таймауту. Хотя с другой стороны на этом этапе вполне можно применять быстрые 100%ные методы (например достаточно надежные RBLи, проверки на соответствие RFC и еще некоторые простые эвристики)
  • При доставке можно устраивать полноценную фильтрацию. Но и тут существуют два “но”. Во-первых, нельзя посылать аутлупы, потому что в 99% случаев они уйдут на фальшивый From. Во-вторых (в принципе это следует из “во-первых”), раз мы никак не сообщаем отправителю об ошибке, то вероятность false-positive должна быть максимально низка (у меня это единица на десятки тысяч пришедших не спам писем и то это не очень хорошо (на самом деле может быть даже и более хороший показатель, потому что с причинами false-positive я боролся и в последнее время ничего такого нет:) )).
  • Client-side фильтрация тоже возможна, то ИМХО только в том случае, если серверная фильтрация невозможна в принципе.
  • SpamAssassin один из наиболее удачных инструментов, сочетающий большое количество методов анализа. Плюс ко всему он достаточно гибко настраивается и хорошо суппортится и развивается.
  • О моих настройках:

    • В SMTP-сессии проверяются только несколько вменяемых RBL и некоторые встроенные в postfix проверки (существование домена для адреса в поле From и пр.).
    • In-queue фильтрация при помощи SpamAssassin
    • По результатам проверки СпамАссассином почта размечается заголовками и всё. Остальное на совести пользователя.
    • По-ночам происходит автообучение+репорт в сетевые службы (Razor, Pyzor и т.д.) писем из определенной IMAP-папки.

    Настройки SpamAssassin.
    SpamAssassin установлен из официального репозитория Debian (из Etch).
    Ставится командой
    # aptitude install spamassassin spamc
    Те, у кого не Debian – ССЗБ.
    Настройки:
    # cat /etc/default/spamassassin

    #/etc/default/spamassassin
    # Duncan Findlay
    # WARNING: please read README.spamd before using.
    # There may be security risks.
    # Change to one to enable spamd
    ENABLED=1
    # Options
    # See man spamd for possible options. The -d option is automatically added.
    # SpamAssassin uses a preforking model, so be careful! You need to
    # make sure --max-children is not set to anything higher than 5,
    # unless you know what you're doing.
    OPTIONS="--max-children 5 --allow-tell --nouser-config --sql-config --username=mailfilter --groupname=mailfilter"
    # Pid file
    # Where should spamd write its PID to file? If you use the -u or
    # --username option above, this needs to be writable by that user.
    # Otherwise, the init script will not be able to shut spamd down.
    PIDFILE="/var/run/spamd.pid"
    # Set nice level of spamd
    #NICE="--nicelevel 15"

    Что здесь указано: во-первых включен spamd (ENABLED=1), во-вторых заданы его опции:

    • –max-children 5 – запускаем 5 дочерних процессов
    • –allow-tell – разрешаем использование обучения из-вне (обязательно читайте spamd(8) про риски)
    • –nouser-config –sql-config – работаем с конфигами из MySQL, не смотрим в домашние каталоги пользователей
    • –username=mailfilter –groupname=mailfilter – работаем из-под пользователя/группы mailfilter (создавать отдельно)

    Далее, включаем отдельные плагины:
    # cat /etc/spamassassin/init.pre

    # This is the right place to customize your installation of SpamAssassin.
    #
    # See 'perldoc Mail::SpamAssassin::Conf' for details of what can be
    # tweaked.
    #
    # This file contains plugin activation commands for plugins included
    # in SpamAssassin 3.0.x releases.  It will not be installed if you
    # already have a file in place called "init.pre".
    #
    ###########################################################################
    # RelayCountry - add metadata for Bayes learning, marking the countries
    # a message was relayed through
    #
    # Note: This requires the IP::Country::Fast Perl module
    #
    # loadplugin Mail::SpamAssassin::Plugin::RelayCountry
    # URIDNSBL - look up URLs found in the message against several DNS
    # blocklists.
    #
    loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
    # Hashcash - perform hashcash verification.
    #
    loadplugin Mail::SpamAssassin::Plugin::Hashcash
    # SPF - perform SPF verification.
    #
    loadplugin Mail::SpamAssassin::Plugin::SPF

    # cat /etc/spamassassin/v310.pre

    # This is the right place to customize your installation of SpamAssassin.
    #
    # See 'perldoc Mail::SpamAssassin::Conf' for details of what can be
    # tweaked.
    #
    # This file was installed during the installation of SpamAssassin 3.1.0,
    # and contains plugin loading commands for the new plugins added in that
    # release.  It will not be overwritten during future SpamAssassin installs,
    # so you can modify it to enable some disabled-by-default plugins below,
    # if you so wish.
    #
    ###########################################################################
    # DCC - perform DCC message checks.
    #
    # DCC is disabled here because it is not open source.  See the DCC
    # license for more details.
    #
    loadplugin Mail::SpamAssassin::Plugin::DCC
    # Pyzor - perform Pyzor message checks.
    #
    loadplugin Mail::SpamAssassin::Plugin::Pyzor
    # Razor2 - perform Razor2 message checks.
    #
    loadplugin Mail::SpamAssassin::Plugin::Razor2
    # SpamCop - perform SpamCop message reporting
    #
    loadplugin Mail::SpamAssassin::Plugin::SpamCop
    # AntiVirus - some simple anti-virus checks, this is not a replacement
    # for an anti-virus filter like Clam AntiVirus
    #
    loadplugin Mail::SpamAssassin::Plugin::AntiVirus
    # AWL - do auto-whitelist checks
    #
    loadplugin Mail::SpamAssassin::Plugin::AWL
    # AutoLearnThreshold - threshold-based discriminator for Bayes auto-learning
    #
    loadplugin Mail::SpamAssassin::Plugin::AutoLearnThreshold
    # TextCat - language guesser
    #
    loadplugin Mail::SpamAssassin::Plugin::TextCat
    # AccessDB - lookup from-addresses in access database
    #
    #loadplugin Mail::SpamAssassin::Plugin::AccessDB
    # WhitelistSubject - Whitelist/Blacklist certain subject regular expressions
    #
    loadplugin Mail::SpamAssassin::Plugin::WhiteListSubject
    ###########################################################################
    # experimental plugins
    # DomainKeys - perform DomainKeys verification
    #
    # External modules required for use, see INSTALL for more information.
    #
    #loadplugin Mail::SpamAssassin::Plugin::DomainKeys
    # MIMEHeader - apply regexp rules against MIME headers in the message
    #
    loadplugin Mail::SpamAssassin::Plugin::MIMEHeader
    # ReplaceTags
    #
    loadplugin Mail::SpamAssassin::Plugin::ReplaceTags

    # cat /etc/spamassassin/v312.pre

    # This is the right place to customize your installation of SpamAssassin.
    #
    # See 'perldoc Mail::SpamAssassin::Conf' for details of what can be
    # tweaked.
    #
    # This file was installed during the installation of SpamAssassin 3.1.2,
    # and contains plugin loading commands for the new plugins added in that
    # release.  It will not be overwritten during future SpamAssassin installs,
    # so you can modify it to enable some disabled-by-default plugins below,
    # if you so wish.
    #
    ###########################################################################
    ###########################################################################
    # experimental plugins
    # DKIM - perform DKIM verification
    #
    # Mail::DKIM module required for use, see INSTALL for more information.
    #
    #loadplugin Mail::SpamAssassin::Plugin::DKIM

    Тут комментировать нечего.
    Теперь непосредственно настройки:
    # cat /etc/spamassassin/local.cf

    #
    # See 'perldoc Mail::SpamAssassin::Conf' for details of what can be
    # tweaked.
    #
    ###########################################################################
    #
    #SCORING OPTIONS
    required_score 7
    #WHITELIST AND BLACKLIST OPTIONS
    # Список через пробел ящиков-ловушек
    blacklist_to    spam@gq.pp.ru
    # Указание использовать SQL для хранения autowhitelist
    auto_whitelist_factory Mail::SpamAssassin::SQLBasedAddrList
    #BASIC MESSAGE TAGGING OPTIONS
    #Выставляем в заголовок оценку и расшифровку по отдельным правилам (с оценками за каждое правило)
    add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTSSCORES(,)_ autolearn=_AUTOLEARN_ version=_VERSION_
    # Не изменять письма, только добавлять заголовки
    report_safe 0
    #LANGUAGE OPTIONS
    # Следующие строчки указывают, на каких языках Вам обычно приходят письма. Если
    # проверяемое письмо будет на другом языке, то его Spam-Level повысится.
    ok_languages ru en
    ok_locales ru en
    #NETWORK TEST OPTIONS
    # Я использую системы pyzor, razor и dcc
    # пакеты pyzor, razor и dcc-client соответственно
    #
    use_pyzor      1
    pyzor_options --homedir /var/lib/spamassassin/pyzor/
    use_razor2     1
    razor_config    /etc/spamassassin/razor-agent.conf
    use_dcc        1
    # Каталог в котором создается сокет dcc, череp который работает SpamAssassin
    dcc_home       /var/lib/dcc
    #BAYES AND LEARNING OPTIONS
    use_bayes       1
    use_bayes_rules 1
    # Так как мы будем использовать статистический bayes анализ то было бы логично
    # обучать его в процессе работы.
    bayes_auto_learn                      1
    # Теперь установим значение при превышении которого письмо будет рассматриваться
    # как спам и будет производиться обучение системы анализа.
    bayes_auto_learn_threshold_spam         12.0
    # То же самое, но наоборот для писем, которые не являются спамом
    bayes_auto_learn_threshold_nonspam      0.1
    #Для хранения базы байеса используем SQL
    bayes_store_module                      Mail::SpamAssassin::BayesStore::SQL
    #MISCELLANEOUS OPTIONS
    lock_method flock
    # Следующие правила изменяют критерии начисления очков.
    # В последней версии появились дополнительные проверки и некоторые из них слишком
    # нервничают на на кривых русских письмах, поэтому уменьшим значение этих правил:
    score SUBJ_ILLEGAL_CHARS        1.0
    score FROM_ILLEGAL_CHARS        1.5
    score HEAD_ILLEGAL_CHARS        1.5
    # Встречается на письмах с Subject:  Re: =?KOI8-R?B?+sHNxc7J1Ng=?= sendmail =?KOI8-R?B?zsE/?=
    score SUBJECT_ENCODED_TWICE     0.0
    # У меня рассылка "Велосипедизм" с subscribe.ru почему-то попадает.
    # Уменьшаем с 2.5 до 1.0
    score FUZZY_XPILL               1.0
    # Мой хак против спама-картинок
    include imagespam.cf

    # cat /etc/spamassassin/razor-agent.conf

    # See razor-agent.conf (5)
    # Change this to 5 for safer classification of MIME attachments.  This will let more spam through
    logic_method = 4
    # Uncomment the next line for logging via syslog
    # На самом деле не надо раскомментировать, иначе записи самого спамассассина в логах несколько извратятся.
    #logfile = sys-syslog
    razorhome = /var/lib/spamassassin/razor
    debuglevel = 1

    Создаем каталоги /var/lib/spamassassin/razor и /var/lib/spamassassin/pyzor, с правами 700 и владельцем mailfilter. Они будут использоваться соответственно службами razor и pyzor.
    Настраиваем MySQL хранилище:
    # cat /etc/spamassassin/mysql.cf

    #Способ ображения к базе байес
    bayes_sql_dsn                   DBI:mysql:spamassassin:localhost
    bayes_sql_username              spamassassin
    bayes_sql_password              password
    #Способ обращения к базе AWL
    user_awl_dsn                    DBI:mysql:spamassassin:localhost
    user_awl_sql_username           spamassassin
    user_awl_sql_password           password
    #Способ обращения к пользовательским провилам (у меня отключено)
    #user_scores_dsn                        DBI:mysql:spamassassin:localhost
    #user_scores_sql_username       spamassassin
    #user_scores_sql_password       password

    Не забываем поставить на этот файлик права 640 (группу задавать не имеет смысла, spamassassin читает этот файл еще до того, как делает chuid).
    Создаем БД в MySQL

    # mysql -u root -p
    Enter password:
    Welcome to the MySQL monitor.  Commands end with ; or g.
    Your MySQL connection id is 11061 to server version: 4.0.24_Debian-10sarge2-log
    Type 'help;' or 'h' for help. Type 'c' to clear the buffer.
    mysql> create database spamassassin;
    Query OK, 1 row affected (0.00 sec)
    mysql> grant all privileges on spamassassin.* to 'spamassassin'@'localhost' identified by 'password';
    Query OK, 0 rows affected (0.06 sec)
    mysql> Bye

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

    mysql -u spamassassin -p spamassassin < /usr/share/doc/spamassassin/sql/awl_mysql.sql
    mysql -u spamassassin -p spamassassin < /usr/share/doc/spamassassin/sql/bayes_mysql.sql
    mysql -u spamassassin -p spamassassin < /usr/share/doc/spamassassin/sql/userpref_mysql.sql

    У меня это создало вот такие таблицы:

    -- MySQL dump 10.11
    --
    -- Host: localhost    Database: spamassassin
    -- ------------------------------------------------------
    -- Server version       5.0.32-Debian_3-log
    /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
    /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
    /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
    /*!40101 SET NAMES utf8 */;
    /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
    /*!40103 SET TIME_ZONE='+00:00' */;
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    --
    -- Table structure for table `awl`
    --
    DROP TABLE IF EXISTS `awl`;
    CREATE TABLE `awl` (
    `username` varchar(100) collate utf8_unicode_ci NOT NULL default '',
    `email` varchar(200) collate utf8_unicode_ci NOT NULL default '',
    `ip` varchar(10) collate utf8_unicode_ci NOT NULL default '',
    `count` int(11) default '0',
    `totscore` float default '0',
    PRIMARY KEY  (`username`,`email`,`ip`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    --
    -- Table structure for table `bayes_expire`
    --
    DROP TABLE IF EXISTS `bayes_expire`;
    CREATE TABLE `bayes_expire` (
    `id` int(11) NOT NULL default '0',
    `runtime` int(11) NOT NULL default '0',
    KEY `bayes_expire_idx1` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    --
    -- Table structure for table `bayes_global_vars`
    --
    DROP TABLE IF EXISTS `bayes_global_vars`;
    CREATE TABLE `bayes_global_vars` (
    `variable` varchar(30) collate utf8_unicode_ci NOT NULL default '',
    `value` varchar(200) collate utf8_unicode_ci NOT NULL default '',
    PRIMARY KEY  (`variable`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    --
    -- Table structure for table `bayes_seen`
    --
    DROP TABLE IF EXISTS `bayes_seen`;
    CREATE TABLE `bayes_seen` (
    `id` int(11) NOT NULL default '0',
    `msgid` varchar(200) character set utf8 collate utf8_bin NOT NULL default '',
    `flag` char(1) collate utf8_unicode_ci NOT NULL default '',
    PRIMARY KEY  (`id`,`msgid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    --
    -- Table structure for table `bayes_token`
    --
    DROP TABLE IF EXISTS `bayes_token`;
    CREATE TABLE `bayes_token` (
    `id` int(11) NOT NULL default '0',
    `token` char(5) collate utf8_unicode_ci NOT NULL default '',
    `spam_count` int(11) NOT NULL default '0',
    `ham_count` int(11) NOT NULL default '0',
    `atime` int(11) NOT NULL default '0',
    PRIMARY KEY  (`id`,`token`),
    KEY `bayes_token_idx1` (`token`),
    KEY `bayes_token_idx2` (`id`,`atime`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    --
    -- Table structure for table `bayes_vars`
    --
    DROP TABLE IF EXISTS `bayes_vars`;
    CREATE TABLE `bayes_vars` (
    `id` int(11) NOT NULL auto_increment,
    `username` varchar(200) collate utf8_unicode_ci NOT NULL default '',
    `spam_count` int(11) NOT NULL default '0',
    `ham_count` int(11) NOT NULL default '0',
    `token_count` int(11) NOT NULL default '0',
    `last_expire` int(11) NOT NULL default '0',
    `last_atime_delta` int(11) NOT NULL default '0',
    `last_expire_reduce` int(11) NOT NULL default '0',
    `oldest_token_age` int(11) NOT NULL default '2147483647',
    `newest_token_age` int(11) NOT NULL default '0',
    PRIMARY KEY  (`id`),
    UNIQUE KEY `bayes_vars_idx1` (`username`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    --
    -- Table structure for table `userpref`
    --
    DROP TABLE IF EXISTS `userpref`;
    CREATE TABLE `userpref` (
    `username` varchar(100) collate utf8_unicode_ci NOT NULL default '',
    `preference` varchar(50) collate utf8_unicode_ci NOT NULL default '',
    `value` varchar(100) collate utf8_unicode_ci NOT NULL default '',
    `prefid` int(11) NOT NULL auto_increment,
    PRIMARY KEY  (`prefid`),
    KEY `username` (`username`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
    /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
    /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    -- Dump completed on 2007-02-23  9:05:48

    Ну и последнее, что осталось настроить: это мой обещанный хак против спама картинками:
    # cat /etc/spamassassin/imagespam.cf

    full __GQ_OE_HTML_META          /http-equiv=3DContent-Type content=3D/
    full __GQ_MIME_SRC_ATT          /src=3D"cid:/
    meta GQ_IMAGESPAM               __OE_MUA && HTML_MESSAGE && __GQ_OE_HTML_META && __GQ_MIME_SRC_ATT
    describe GQ_IMAGESPAM           GQ: Mail looks like image-spam
    score GQ_IMAGESPAM              2.5

    С ним стоит быть осторожнее – возможно он будет срабатывать на письмах из аутглюка-экспресс с html с картинками. У меня по крайней мере таких не бывает среди не спама.
    Ура, спамассассин настроен и им можно пользоваться.
    Например так:
    Настройки Postfix
    В /etc/postfix/main.cf

    mailbox_command = maildrop

    maildrop стоит из пакета courier-maildrop.
    # cat /etc/courier/maildroprc

    # Global maildrop filter file
    DEFAULT="$HOME/Maildir/"
    SHELL="/bin/sh"
    if ($SIZE < 512000)
    {
    #Here spamassassin goes
    xfilter "spamc"
    }

    Теперь все письма получают заголовок от SpamAssassin’а.
    Дальше (в общесистемном фильтре или в ~/.mailfilter) можно сделать что-нибудь вроде

    if (/^X-Spam-Status: Yes/:h)
    {
    to "$HOME/Maildir/.Junk"
    }

    Помимо этого у меня на имап сервер есть папки RealJunk и NotJunk. В первую я вручную перекладываю спам (прорвавшийся в Inbox + весь из папки Junk (вручную проверяю хотя бы Subject и From)), а во вторую false-positive, а точнее те письма, которые не попали в спам, но все же получили высокую оценку от байеса (false-positive у меня вообще не бывает).
    Письма из этих папок обрабатываются следующим скриптом:
    # cat /etc/cron.daily/sa-learn

    #!/bin/bash
    USERS=$(ldapsearch -x "(objectClass=mailAccount)" uid| awk '/^uid: [[:alnum:]]*$/ {print $2}')
    for USER in $USERS;do
            HOMEDIR=$(ldapsearch -x "(&(objectClass=mailAccount)(uid=$USER))" homeDirectory| awk '/^homeDirectory: [^[:space:]]*$/ {print $2}')
            for i in "$HOMEDIR"/Maildir/.RealJunk/{cur,new}/*;do
                    if [[ -f "$i" ]];then
                            spamc -u $USER -L spam -l -s 512000 < "$i" >/dev/null&&\
                            spamc -u $USER -C report -l -s 512000 < "$i" >/dev/null &&\
                            rm "$i" 2>/dev/null
                    fi
            done
            for i in "$HOMEDIR"/Maildir/.NotJunk/{cur,new}/*;do
                    if [[ -f "$i" ]];then
                            spamc -u $USER -L ham -l -s 512000 < "$i" >/dev/null&&\
                            spamc -u $USER -C revoke -l -s 512000 < "$i" >/dev/null&&\
                            rm "$i" 2>/dev/null
                    fi
            done
    done

    Там присутствует некоторая хитрость в получении Maildir’ов, для этого берутся homeDir из LDAP, но в остальном вроде все достаточно просто.

    3 thoughts on “Установка и настройка SpamAssassin”

      1. Оуч, наш старый знакомый все еще жив и теперь зовет себя “Педагогом”. Кошмар.
        Дети, будьте бдительный. Вам теперь угражают не только педофилы.

    Leave a Reply

    Your email address will not be published. Required fields are marked *