Ключевые слова:zmailer, mail, mta, spam, virus, (найти похожие документы)
From: Игорь Ченцов
Newsgroups: email
Date: Mon, 18 Jul 2006 18:21:07 +0000 (UTC)
Subject: Борьба со СПАМ-ом на примере почтового сервера zmailer.
Оглавление
----------
- Введение
- Почему zmailer ?
- Первая линия обороны - smtp сервер
- Блокирование по IP адресу
- Политики приёма сообщений
- Использование RBL.
- Контекстный фильтр
- А если я получаю почту по uucp ?
- Контекстный фильтр на стадии маршрутизации
- Последняя линия обороны
- Итоги
- Ссылки
Введение
Наверное, что такое СПАМ объяснять не нужно никому, за последние годы
эта проблема стала повсеместной. Ещё каких-нибудь 5-6 лет назад мы
только усмехались, удаляя из почтового ящика очередное предложение
заняться "американским английским языком", и не подозревали, что
придётся разыскивать нужное письмо в ворохе хлама, подавляя
непреодолимое желание удалить все входящие сообщения и тут же сменить
E-mail.
Данная статья служит иллюстрацией методов борьбы со СПАМ-ом на примере
почтового сервера zmailer (http://www.zmailer.org).Она не претендует на
то - что применив эти методы Вы тутже перестанете получать ненужные
письма и наступит "светлое завтра". Статья написана для облегчения
начинающим сисадминам понимания - каким образом можно выставить заслон
на пути со СПАМ-ом, на каких рубежах идёт основная борьба - какие
методы в этой борьбе используются.
Почему zmailer ?
Выбор в качестве примера почтового сервера Zmailer
(http://www.zmailer.org) был определён рядом причин.
Во первых, это не очень распространённый почтовый сервер, что позволит
читателю не зарываться в конкретные реализации sendmail или postfix.
Во вторых, zmailer обладает модульной структурой. Smtp-сервер,
почтовый маршрутизатор (router), программа отслеживающая запуск
агентов по расписанию (scheduler), и почтовые агенты доставляющие
почту работают как отдельные процессы, что позволяет разбить общую
задачу доставки почты на отдельные подзадачи которые облегчают
понимание сути проблемы.
В третьих, в основе языка конфигурации почтового маршрутизатора лежит
язык, очень похожий на язык Bourne sh, с небольшими дополнениями. Он
так и называется zmsh (ZMailer Shell). Соответственно вносить
изменения в конфигурацию не составляет никакого труда. И это доступно
сисадмину с любой квалификацией (если, конечно, он знает чего хочет).
Ну и в последних, это просто очень неплохой почтовый сервер :-) За
информацией отсылаю на сайт www.zmailer.org - где вы можете
скачать исходники и попытаться его использовать.
Первая линия обороны - smtp сервер
Итак, надо знать, как же почтовое сообщение попадает к нам в ящик,
чтобы установить на этом пути необходимые заслоны для нежелательных
сообщений. Я не буду вдаваться в подробности взаимодействия MTA и MUA
программ, предположив стандартную схему работы с электронной почтой -
Почтовый сервер принимает сообщения, определяет что с ним необходимо
сделать и затем, с помощью транспортного агента (в терминах zmailer),
или доставляет сообщение в почтовый ящик клиента, или отправляет его
по соответсвующему маршруту.
В настоящее время основная масса электронной почты попадает к нам на
сервер по протоколу smtp (Simple Mail Transfer Protocol, действующая
редакция RFC-2821). В кратце приведу схему действия данного протокола
(естественно сокращённую, кто тянется к знаниям и хочет полноты
картины - отсылаю к документу RFC-2821). Итак удалённый компьютер
пытается соединиться с сервером на порту 25. Сервер ему отвечает -
<- 220 localhost ZMailer Server 2.99.57.pre4 #1 ESMTP ready
->HELO xxxxx
<- 250 xxxxx
->MAIL FROM: <xxxxx>
<-250 2.1.0 Sender syntax Ok;
->RCPT TO: <xxxx>
<-250 2.1.5 Recipient address syntax Ok
->DATA
<-354 Start mail input; end with <CRLF>.<CRLF>
-> данные
-><CRLF>.<CRLF>
<-250 2.0.0 accepted
->QUIT
<-221 2.0.0
Итак, перед Вами стандартная smtp сессия, тут видна обычная
последовательность запросов клиента (HELO, MAIL FROM, RCPT TO, DATA,
QUIT) на каждую из которых сервер отвечает кодом возврата - 2xx -
успешно, 5xx - фатальная ошибка, 4xx - нефатальная ошибка, 3xx -
предупреждение. Ещё надо отметить, что поля заголовка From: и To:
могут (и чаще всего так и бывает) не соответствовать командам MAIL
FROM и RCPT TO.
Соответственно мы хотим выставить первую линию обороны в самом начале
цикла приёма почты - в момент как какой-то клиент начинает соединяться
с нашим smtp сервером. На самом первом этапе мы можем с помощью
firewall-а просто блокировать запросы поступающие с плохо
зарекомендовавших себя адресов. Это первая линия обороны - в этот
момент мы блокируем весь траффик с данных хостов и сетей не особо
разбираясь - зачем идёт запрос с данного адреса (или усложняем правила
и блокируем запросы только на почтовый сервер). Если вы не ждёте
письма скажем из экзотической Бразилии или Папуа-Новой Гвинеи это
неплохой подход ограничить приём непосредственно рассылаемого СПАМ-а.
Но, к сожалению, часто нельзя сказать, кому именно принадлежит данный
IP адрес, а с развитием хостинга, практически невозможно определить
кто скрывается за тем или иным адресом или сетью.
Какие же средства предлагает нам zmailer ? Внутрь smtp-сервера
встроена обработка правил фильтрации (SMTP-server policy filtering
rules), которая определяет - отбить письмо или, можно его пропустить.
В файле <zmailer-dir>/db/smtp-policy.src задаётся тип проверки адреса
в команде mail from:, использование RBL(Rollback Blocking List), и
блокирование адресов с которых идёт запрос. Итак по порядку -
Блокирование по IP адресу
Добавив в файл <zmailer-dir>/db/smtp-policy.spam строчку вида
[xxx.xxx.xxx.xxx]/xx (например [4.245.197.0]/24) мы блокируем запросы
на наш smtp сервер с подсети 4.245.197.0 (аналогично в файле access
для sendmail мы бы написали 4.245.197). Не забывайте выполнять скрипт
policy-builder.sh когда, что-то меняете в настройках - этот скрипт
(аналогично make maps для sendmail) производит изменения в db файлах -
который и использует smtp-сервер для получения информации о
настройках.
Политики приёма сообщений
В zmailer предлагается использовать одну из следующих политик приёма
сообщений -
1. Принимаются сообщения только от "известных" хостов, которые
прописаны непосредственно в конфигурационных файлах. Может быть
такой подход и хорош для внутрикорпоративной почты, но совершенно
нереален - когда smtp-сервер "смотрит" в интернет.
2. Проверка по "существованию" в DNS - т.е. Адрес отправителя
должен существовать в DNS в виде A или MX записи
3. Проверка по MX - для домена отправителя должна существовать запись
MX в DNS
4. Проверка по "существованию" в DNS и MX проводятся для адресов
отправителя и получателя (не используйте данную политику - если не
знаете к чему это приведёт)
Использование RBL.
Некоторое время назад основным источником СПАМ-а были так называемые
открытые почтовые (Open Relay) сервера. Что же это такое ? Открытый
почтовый сервер (открытый релей) это почтовый сервер, который
позволяет проходить через себя почтовым сообщениям, в которых ни
получатель, ни автор сообщения не являются локальными пользователями.
Т.е. грубо говоря через такой почтовый сервер можно безнаказанно
рассылать СПАМ. Более подробную информацию об опасности открытых
релеев можно прочитать (в том числе и на русском языке) -
http://ordb.org/faq/. В сети стали создаваться базы данных
открытых релеев (пример такой базы ordb.org).
Как же использовать такие базы для блокирования нежелательных
сообщений ? Работа данных БД использует DNS адреса типа A RR в своей
зоне. На примере ordb.org, если вы принимаете SMTP сессию с адреса
[192.89.123.5], вам нужно проверить наличие:
5.123.89.192.relays.ordb.org. IN A 127.0.0.2. Zmailer предлагает
использование RBL списка rbl.maps.vix.com или любого другого
основанного на том же принципе. В файле
<zmailer-dir>/db/smtp-policy.srcнеобходимо прописать строчки (для
проверки по спискам rbl.maps.vix.com, dul.maps.vix.com,
relays.mail-abuse.org)
_rbl0 rcpt-dns-rbl +:relays.ordb.org:relays.mail-abuse.org
_rbl1
(+ соответствует rbl.maps.vix.com).
Но, к сожалению, в современном мире СПАМ, в основном пересылают dialup
и dsl клиенты, которых в данных базах нет :-(. Что же делать ?
Приходится использовать -
Контекстный фильтр
Когда мы получаем письмо, smtp-сервер сохраняет его во временном файле
- вот здесь мы и можем проверить его на СПАМ и вирусы. Мне кажется это
не самая удачная идея - но "жёстко" ответить клиенту в smtp сессии
что он спаммер - возможно. Вообще говоря контекстный фильтр - это
самая мощная возможность проверки на СПАМ. Но использование его внутри
smtp-сессии мне представляется не совсем обоснованным. Другое
использование данного метода мы рассмотрим ниже.
Написав строчку в конфигурационном файле smtpserver.conf в <zmailer dir> -
PARAM contentfilter /usr/local/sbin/smtp-contentfilter
мы создаём исполняемый файл smtp-contentfilter примерно такого
содержания (в качестве ловца СПАМ-а используется пакет Spamassassin
spamassassin.apache.org, здесь же проводится и проверка на вирусы
с помощью ClamAV www.clamav.net)
#!/usr/bin/perl
use File::Scan::ClamAV;
$SPAMC="/usr/local/bin/spamc";
$CLAM_PORT='/var/run/clamav/clamd';
select(STDOUT); $| = 1;
printf "#hungry\n";
while (<>) {
chomp;
$fname = $_;
$rc = filter( $fname );
printf "%s\n", $rc;
printf "#hungry\n";
}
exit 0;
sub filter {
local($fname) = @_;
local($msg);
open(SP, "< ".$fname) ||
return "0 Oops.. filter can't open file $fname";
# Scan the envelope thru to spot the magic "env-end" line
while (<SP>) {
chomp;
last if ($_ eq 'env-end');
}
# Ok, either EOF, or got that "env-end" token.
local ($TMP_FILE)="/tmp/" . (time ^ $$);
open (OUT,">$TMP_FILE") ||
return "0 Oops.. filter can't open file $TMP_FILE";
print OUT while (<SP>);
close SP;
close OUT;
# Проверяем на вирусы
local $av= new File::Scan::ClamAV (port => $CLAM_PORT);
if ($av->ping) {
local ($file,$virus)=$av->scan($TMP_FILE);
local ($err) = $av->errstr();
if ($err) {
$msg.=" Ошибка при проверке на вирус $err";
} elsif ($virus) {
unlink $TMP_FILE;
return "-1 $virus found";
}
}
# А теперь на СПАМ
if ( -x $SPAMC ) {
my $chSPAM=`$SPAMC -c < $TMP_FILE`;
my ($spamnum,$spamrange) = split(/\//,$chSPAM);
if ($spamnum > $spamrange) {
unlink $TMP_FILE;
return "-1 SPAM Found $chSPAM";
}
}
return "0 $msg";
}
Сделаем ещё одно замечание - если возвращается отрицательное число -
то smtp сессия заканчивается с кодом 5xx, если положительное - то
сессия заканчивается с 2xx - но письмо никуда не отправляется и
перемещается в холодильник (freezer).
ВНИМАНИЕ !!! Это пример - и он не учитывает ситуации "гонок" и
прочих "прелестей" использования временных файлов. Для обеспечения
безопасности используйте соответствующие методы !!!
А если я получаю почту по uucp ?
Но, к сожалению, почта не всегда попадает на сервер посредством smtp
протокола. Многие ещё используют uucp для приема почты. Конечно, адреса
типа host1!host2!host3!user давно вышли из моды, но, иногда, когда
связь с сетью не очень устойчивая, приходится прописывать MX запись в
DNS на своего провайдера, а почту забирать по uucp. Опять же, как
говорилось выше, использовать контекстный фильтр в smtp сессии не
очень правильная затея. Особенно при проверке почты на вирусы.
Представте, Вам пришёл архив из 1500 тысячи запакованных файлов,
почтовый клиент на той стороне может и не дождаться, когда антивирус
это всё пережуёт. Вот тут zmailer предоставляет возможность
использовать
Контекстный фильтр на стадии маршрутизации
Ни одно письмо на почтовом сервере не может пройти мимо процесса
маршрутизации, сервер же должен знать куда его направить, локальному
пользователю, по smtp наружу, или может быть по uucp на внутренний
почтовый сервер. А так, как настройки маршрутизатора почты выглядят
как обычные sh файлы - мы можем поместить туда контекстный фильтр. Мне
кажется лучшее место, куда поместить фильтр - это место окончания
маршрутизации, перед самым моментом помещения письма в соответсвующую
очередь. По этому открываем файл <zmailer-dir>/cf/process.cf в
текстовом редакторе, находим в нём функцию process (file) и добавляем
туда несколько строк -
LOGMSG=() # This is a LIST of files where to log..
#| The LOGMSG variable is used by the intercept facility (in crossbar.cf)
#| to make sure only a single copy of a message is saved when required.
#| Each sender - recipient address pair can cause an intercept which can
#| specify a file to save the message to. This variable is appended to
#| elsewhere, and processed at the end of this function.
###-> Это уже наша добавка
ch=`/usr/local/sbin/router-contentfilter $POSTOFFICE/router/$file`
case "$ch" in
1*)
/bin/mv "$file" /SPAM/`/bin/date +%s`
return
;;
*);;
esac
###-> Конец добавки
case "$file" in
# [0-9]*.x400) x400 "$file" ;;
Что же здесь понаписано ? Запуск фильтра router-contentfilter с
параметром временного файла, где лежит наше письмо. Если фильтр вернул
значение 1 - то письмо перемещается в папку со СПАМ-ом.
Ну конечно же и пример router-contentfilter. Чтобы немного
разнообразить код - здесь, для проверки на вирус, используется
антивирус Касперского (www.kaspersky.ru). И маленькое замечание по
поводу файла с почтой - в начале его существует заголовок по которому
zmailer определяет - что же делать с данным письмом. Выглядит он
примерно так -
external
rcvdfrom [222.236.103.182] ([222.236.103.182]:10500 "HELO 222.236.103.182")
comment [222.236.103.182] [222.236.103.182]:10500 "HELO 222.236.103.182" smtp -auth: <none>)>
with SMTP
from <aforesaid@edsamail.com.ph.>
todsn NOTIFY=FAILURE,DELAY ORCPT=rfc822;a@mysite.ru INRCPT=rfc822;af>to <a@mysite.ru.>
env-end
В этих полях описываются данные smtp сессии. И по ним тоже можно
устраивать проверки.
Но для проверок стандартными средствами (Kaspersky AV и Spamassassin)
приходится этот заголовок попросту "отрезать". Наш код ненамного
отличается от smtp-contentfilter -
#!/usr/bin/perl
$SPAMC="/usr/local/bin/spamc";
$TIMEOUT=30;
$AVPSocket= "/var/run/AvpCtl";
my $fname = $ARGV[[ ;]]
exit 0 unless $fname;
my $rc = filter( $fname );
print "$rc\n";
exit 0;
sub filter {
local($fname) = @_;
local($msg);
open(SP, "< ".$fname) ||
return "0 Oops.. filter can't open file $fname";
# Scan the envelope thru to spot the magic "env-end" line
while (<SP>) {
chomp;
last if ($_ eq 'env-end');
}
# Ok, either EOF, or got that "env-end" token.
local ($TMP_FILE)="/tmp/" . (time ^ $$);
open (OUT,">$TMP_FILE") ||
return "0 Oops.. filter can't open file $TMP_FILE";
print OUT while (<SP>);
close SP;
close OUT;
# Проверяем на вирусы
socket(AVP,PF_UNIX,SOCK_STREAM,0);
unless (connect(AVP,sockaddr_un($AVPSocket))) {
msg.=" Can't Connect to $AVPSocket:$!";
} else {
my $tm=substr(localtime(),4,15);
# Убираем буферизацию
select(AVP);$|=1;select(STDOUT);
print AVP "<0>$tm:$file\000" ;
$SIG{ALRM} = sub { die "alarm\n" };
eval {
alarm ($TIMEOUT);
read(AVP,$ExitCode,1);
read(AVP,$flag,1);
if (ord($flag) == 1) {
read(AVP,$ans_l,4);
read(AVP,$answ,unpack('L',$ans_l));
}
alarm(0);
}; # eval
close AVP;
if ($@ && $@ ne "alarm\n") {
$msg.=" AVP Check - Unknow error $@" ;
} elsif ($@) {
$msg.=" AVP Check - TimeOut";
} else {
my %lo_map=(
0 => " No viruses were found",
1 => " Virus scan was not complete",
2 => " Found corrupted or changed virus",
3 => " Suspicious objects were found",
4 => " Known viruses were detected",
5 => " All viruses disinfected",
6 => " All viruses deleted",
7 => " File AvpDaemon is corrupted",
8 => " Corrupted objects were found"
);
$msg.="$lo_map{$ExitCode}:$answ" if ($ExitCode);
if ($ExitCode == 4) {
unlink $TMP_FILE;
return "1 $msg" ;
}
}
}
# А теперь на СПАМ
if ( -x $SPAMC ) {
my $chSPAM=`$SPAMC -c < $TMP_FILE`;
my ($spamnum,$spamrange) = split(/\//,$chSPAM);
if ($spamnum > $spamrange) {
unlink $TMP_FILE;
return "1 SPAM Found $chSPAM";
}
}
return "0 $msg";
}
ВНИМАНИЕ !!! Это пример - и он не учитывает ситуации "гонок" и
прочих "прелестей" использования временных файлов. Для обеспечения
безопасности используйте соответствующие методы !!!
Не забудьте перезапустить процесс router - чтобы изменения вступили в
силу.
Последняя линия обороны
Нам осталось рассмотреть последний рубеж - на котором можно
"поймать" СПАМ, а именно перед самым помещением письма в почтовый
ящик. Здесь, вобщем, сказать нечего кроме того, что связка procmail
(www.procmail.org) Spamassassin работает довольно успешно.
Что касается zmailer - он достаточно просто позволяет заменить
внутреннюю программу доставки почты mailbox на procmail. Это описано в
документации (файл procmail).
Итоги
Мы рассмотрели 4 линии обороны от СПАМ-а -
1. FireWall
2. Во время smtp сессии
3. Во время маршрутизации почты
4. Во время доставки почты в ящик пользователя
Конечно, существуют и другие методы защиты, например использование
авторизации на smtp сервере, систему spf (spf.pobox.com) и т.д. и т.п.
- но здесь рассматривались основные методы борьбы со СПАМ-ом. Эти
методы могут быть использованы в любой почтовой системе, а не только в
описанном в данной статье почтовом сервере Zmailer.
Буду надеятся, что эта статья поможет сисадминам в выстраивании
обороны от СПАМ-а.
Ссылки
Почтовый сервер Zmailer рttp://www.zmailer.org
Доставка почты в локальные ящики Procmail http://www.procmail.org
Проверка сообщений на СПАМ SpamAssassin http://spamassasin.apache.org
Open Source антивирус ClamAV http://www.clamav.net
Буду благодарен тем - кто переведёт данную статью на английский язык
Расписал с чего всё началось и по какому мокару работает эта статистика. Реально помогает заблокировать по IP кучу спама и темсамым значительно экономится ресурс почтового сервера.