Посмотрел останки своего старого сайта с целью восстановить оттуда что-нить ценное. Понял, что ничего ценного там не было кроме статьи о федерации молодежи за мир во всем мире (которая, похоже окончательно утеряна, но и фиг с ней, муниты и так почти исчезли (из Москвы по крайней мере)) и HOWTO по настройке SpamAssassin.
Статью про СпамАссассин решил восстановить. В несколько сокращенном варианте (сейчас уже только ленивый не знает что это такое и зачем он нужен) и с поправками на мои нынешние настройки.
Итак начнем.
Вначале общие вещи тезисно:
- Спам надо фильтровать. Для этого следует использовать сочетание всех возможных (доступных) методик. Использование только одной (двух, трех) недостаточно: только анализ содержимого, или только RBL
дают достаточно низкое срабатывание с большим количеством false-positive. Результаты анализа с использованием разных методик должны суммироваться и решение должно приниматься с учетом всех факторов (например письмо с подозрительным содержимым от постоянного корреспондента с положительной историей должны быть пропущены фильтром). - Фильтровать можно в трех точках: SMTP-сессия, сервер-сайд при доставке (но после SMTP) и клиент-сайд.
- SMTP-сессия – наилучшая точка фильтрации, неугодным можно послать аутлуп прямо в сессии, но есть существенная проблема: не всегда можно фильтровать сообщения прямо во-время приема – например, может не хватать ресурсов для такой on-line фильтрации, или ресурсов будет хватать, но слишком много времени занимает фильтрация и отправляющая сторона отваливается по таймауту. Хотя с другой стороны на этом этапе вполне можно применять быстрые 100%ные методы (например достаточно надежные RBLи, проверки на соответствие RFC и еще некоторые простые эвристики)
- При доставке можно устраивать полноценную фильтрацию. Но и тут существуют два “но”. Во-первых, нельзя посылать аутлупы, потому что в 99% случаев они уйдут на фальшивый From. Во-вторых (в принципе это следует из “во-первых”), раз мы никак не сообщаем отправителю об ошибке, то вероятность false-positive должна быть максимально низка (у меня это единица на десятки тысяч пришедших не спам писем и то это не очень хорошо (на самом деле может быть даже и более хороший показатель, потому что с причинами false-positive я боролся и в последнее время ничего такого нет:) )).
- Client-side фильтрация тоже возможна, то ИМХО только в том случае, если серверная фильтрация невозможна в принципе.
О моих настройках:
- В 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, но в остальном вроде все достаточно просто.
http://forum.truesite.ru/viewtopic.php?t=958
Оуч, наш старый знакомый все еще жив и теперь зовет себя “Педагогом”. Кошмар.
Дети, будьте бдительный. Вам теперь угражают не только педофилы.