dspam. Ищут пожарные, ищет милиция.

Сегодня просматривая отчет анализатора логов http сервера немало был удивлен поисками сорцов dspam в моем блоге. Мелочь, конечно, но приятно :-)
Поясняю: нефиг его тут искать! dspam валяется на sourceforge по адресу http://sourceforge.net/projects/dspam/.
На момент написания этой записи доступна версия dspam-3.9.0.tar.gz

Yours sincerely,
JRoger

Exim: избавляемся от спама за пол-часа. Часть 2 — результаты.

Итак, прошло немало времени с момента введения правил реджекта сообщенийspam(http://jroger.info/2009/11/01/23/).
Сегодня я поднял статистику exim за период с 31 июля по 16 ноября (три с половиной месяца)

Результаты очень радуют:

Top 50 mail rejection reasons by message count
----------------------------------------------
  Messages   Mail rejection reason
      3870   Rejected RCPT: Unknown user
      2351   RFC compliant date required.
      1421   HELO should be Fully Qualified Domain Name  Host.Domain.Tld  See RFC821
       807   Rejected RCPT: Sender verify failed
       403   RFC compliant to required.
       372   Message headers contain non ASCII chars.
       122   SMTP protocol synchronization error
       111   Rejected RCPT: relay not permitted
       105   HELO should be hostname.
        60   Unknown
        50   Rejected HELO/EHLO: syntactically invalid argument
        22   Closed connection in response to initial connection
        16   RFC 1918 IP address in HELO.
         5   Dropped: too many nonmail commands
         3   Rejected RCPT: Restricted characters in address
         2   Rejected RCPT: authentication required
         1   Closed connection in response to end of data

Из вставки видно, что несколько разумно сформированых правил отлупа срезали чуть больше пяти тысяч спам-сообщений.
Есть повод для гордости :-)

Sincerelly,
JRoger

Exim: избавляемся от спама за пол-часа

Надоело говно в ящиках и скотское количество smtp трафика?spam
Тогда приступим.

1. Конечно же exim!
2. Конечно же в исходнике!
3. Конечно же собираем ручками!
(сборку описывать не стану — этого добра в инетке не меньше, чем порнухи :)

После сборки-установки начинаем править конфиг. Прописываем свои домены.
По желанию собираем и подключаем avast антивирус (я использую).
Подключаем SMTP-аутентификацию юзеров (хоть mysql, хоть что-то другое)

Далее начинается самое интересное:

В acl_check_rcpt по дефолту присутствует следующее:

—————————————————

acl_check_rcpt:

[бла-бла-бла]

# Аутентифицированым везде у нас дорога
# Аутентифицированым везде у нас почет
  accept  authenticated = *

[бла-бла-бла]

—————————————————
это нам ничуть не мешает, а очень даже помогает

А далее начинаются проверки. В acl_check_data пихаем:
—————————————————

acl_check_data:

# Аутентифицированым везде у нас дорога
# Аутентифицированым везде у нас почет
#
# Приемлимо, если есть уверенность в отсутствии вирусни у юзеров, дабы не рассылали.
# Я юзаю на виндовых клиентах Thunderbird и никакого Бата или Некрофост Аутглюков (Экспресс)
#
  accept  authenticated = *

# Проверяем наличие RFC даты
# Немного спам-ботов балуют правильно оформленной датой. Режем.
#
  defer
    message   = RFC compliant date required.
    condition = ${if !match\
      {$h_date:}\
      {\N[0-9]\N}\
      {yes}{no}}

# Проверяем наличие RFC To
# Немного спам-ботов балуют правильно оформленным полем To. Режем.
#
  defer
    message   = RFC compliant to required.
    condition = ${if !match\
      {$h_to:}\
      {\N.+\N}\
    {yes}{no}}

# Проверяем наличие не ASCII символов, за исключением UTF-8
# Немного спам-ботов балуют правильно оформленной кодировкой. Режем.
#
# Тут надо с некоторой оглядкой - некоторые форумы и прочие самоделки рассылают как попало, без соблюдения кодировки.
#
  defer
    message   = Message headers contain non ASCII chars.
    condition = ${if !match\
      {${lc:$h_content-type:}}\
      {\Nutf-?8\N}\
    {yes}{no}}
    condition = \
      ${if \
        or {\
          {match{$rh_bcc:}{\N[\x80-\xff]\N}}\
          {match{$rh_cc:}{\N[\x80-\xff]\N}}\
          {match{$rh_date:}{\N[\x80-\xff]\N}}\
          {match{$rh_from:}{\N[\x80-\xff]\N}}\
          {match{$rh_reply-to:}{\N[\x80-\xff]\N}}\
          {match{$rh_sender:}{\N[\x80-\xff]\N}}\
          {match{$rh_subject:}{\N[\x80-\xff]\N}}\
          {match{$rh_to:}{\N[\x80-\xff]\N}}\
        }\
      {yes}{no}}

# Домен не может быть localhost или localdomain
# На "дурака" не проканает. Режем.
#
  defer
    message   = ${domain:$h_from:} is a wrong domain.
    hosts     = ! : !+relay_from_hosts
    condition = ${if match\
      {${domain:$h_from:}}\
      {\N^(localhost|localhost\.local(domain)?|local(domain)?)$\N}\
    {yes}{no}}

# Не принимаем почту с "голых" IP-адресов (RFC 1918)
# С "голых" IP посылаем нахуй. Это 100% спам!
#
  deny hosts   = !+relay_from_hosts
     message = RFC 1918 IP address in HELO.
     condition = ${if match\
     {$sender_helo_name}\
     {\N^(\[)?(10\.[0-9]{1,3}|172\.(1[6-9]|2[0-9]|31)|192\.168)\.[0-9]{1,3}\.[0-9]{1,3}(\])?$\N}\
     {yes}{no}}

# Удаленный хост должен резольвиться
# Не резольвишься? Иннах, спам-бот!
#
  deny hosts   = !+relay_from_hosts
     message = HELO should be hostname.
     condition = ${if !match\
     {$sender_helo_name}\
     {\N.*[A-Za-z].*\N}\
     {yes}{no}}

# Удаленный хост должен иметь FQDN
# Резольвишься как попало? Иннах, спам-бот!
#
  deny hosts   = !+relay_from_hosts
     message = HELO should be Fully Qualified Domain Name  Host.Domain.Tld  See RFC821
     condition = ${if !match\
     {$sender_helo_name}\
     {\N.*[A-Za-z].*\..*[A-Za-z].*\N}\
     {yes}{no}}

# Поврежденные аттачи - фтопку!
# Кривой MIME нам не нужен! Иннах, спам-бот!
#
  deny
    message   = This message contains malformed MIME $demime_reason.
    condition = ${if >\
      {$demime_errorlevel}{2}\
      {1}{0}}

# Виндово-исполняемое говно - фтопку!
# Иннах, инфицированя тачка и/или тупой юзер!
#
  deny
    message = Attachment has unsupported file format .$found_extension. try text, PDF or ODF instead.
    demime  = bat:btm:cmd:com:cpl:dat:dll:exe:lnk:msi:pif:prf:reg:scr:vb:vbs:url

# Проверяем на вирусы
# У меня подкдючен avast.
# Не забываем включить опцию demime в src/EDITME при сборке exim
#
  deny
    message = Message contains a virus or other harmful content ($malware_name)
    demime = *
    malware = *

—————————————————

Вот так, кратенько, с шутками и прибаутками мы срезаем до 99% всевозможного спама.

Проверить с этими правилами работу spamassassin или dspam у меня не было ни возможности, ни времени.
Хотя, все еще впереди :-)

Удачи и поменьше спама вашему хосту.

Sincerelly,
JRoger

DSPAM — избавляемся от спама.

На моем узле установлена FreeBSD и MTA Exim.
В один прекрасный момент количество получаемого спама увеличилось настолько, что просто не обращать на него внимание стало невозможно. Тут-то и начались поиски солюшенов.
Spamassassin меня не совсем устроил. Особенно процесс установки Perl пакетов :-)))
Зато DSPAM понравился целиком и полностью.
Итак. Устанавливаем DSPAM.

Скачиваем с оригинального сайта — Nuclear Elephant: The DSPAM Project

Распаковываем: tar xvzf dspam-3.8.0.tar.gz
Конфигуряем с использованием mySQL

./configure \
--enable-neural-networking \
--disable-bias \
--with-storage-driver=mysql_drv \
--with-mysql-includes=/usr/local/include \
--with-mysql-libraries=/usr/local/lib \
--enable-debug \
--with-dspam-home-owner=exim \
--with-dspam-group=mail \
--with-delivery-agent=/usr/local/sbin/exim \
--with-quarantine-agent=/usr/local/sbin/exim \
--enable-experimental \
--enable-domain-scale

Если вы используете «виртуальных пользователей», то необходимо добавить
—enable-virtual-users

Далее запускаем make && make install
Готово! DSPAM установлен!
Теперь приступим к конфигурированию Exim и DSPAM.

Для начала необходимо создать БД и пользователя в mySQL
Непременно используйте InnoDB сторидж во избежание отвала в корку mysql, либо повреждения базы данных!

admin@localhost#> mysqladmin -uroot -p create dspam
admin@localhost#> mysql -uroot -p dspam < tools.mysql.drv/mysql_objects-4.1.sql admin@localhost#> mysql -uroot -p dspam < tools.mysql.drv/virtual_users.sql admin@localhost#> mysql -uroot -p dspam
mysql> grant all on dspam.* to dspam@localhost identified by 'пароль';
mysql> exit

С БД на этом покончено.

Далее необходимо сконфигурить екзим. Я взял стандартные роутеры и транспорты из wiki дспама. Работает. Итак:

Запихиваем в конфиг экзима сразу после userforward router следующее:

dspam_router:
no_verify
check_local_user
# When to scan a message :
# - it isn't already flagged as spam from Spamassassin
# - it isn't already flagged as spam from DSPAM
# - it isn't already scanned
# - it isn't local
# - it isn't from one internal domain user to another
# - it is less than 512k in size
condition = "${if and { \
{!def:h_X-Spam-Flag:} \
{!def:h_X-FILTER-DSPAM:} \
{!eq {$received_protocol}{local}} \
{ <= {$message_size}{512k}} \
}\
{1}{0}}"
headers_add = "X-FILTER-DSPAM: by $primary_hostname on $tod_full"
driver = accept
transport = dspam_spamcheck
# Which users to run dspam for.
#local_parts = /usr/local/etc/exim/dspam-testers
dspam_addspam_router:
driver = accept
local_part_prefix = spam-
transport = dspam_addspam
dspam_falsepositive_router:
driver = accept
local_part_prefix = nospam-
transport = dspam_falsepositive

В транспорты пихаем этот текст

dspam_spamcheck:
driver = pipe
command = "/usr/local/bin/dspam --deliver=innocent --user ${lc:$local_part} -f \
'$sender_address' -- %u"
home_directory = "/tmp"
current_directory = "/tmp"
user = exim
group = mail
log_output = true
return_fail_output = true
return_path_add = false
message_prefix =
message_suffix =

dspam_addspam:
driver = pipe
command = "/usr/local/bin/dspam --class=spam --source=error --user \
${lc:$local_part} -f '$sender_address' -- %u"
home_directory = "/tmp"
current_directory = "/tmp"
user = exim
group = mail
log_output = true
return_fail_output = true
return_path_add = false
message_prefix =
message_suffix =

dspam_falsepositive:
driver = pipe
command = "/usr/local/bin/dspam --class=innocent --source=error \
--deliver=innocent,spam --user ${lc:$local_part}\
-f '$sender_address' -- %u"
home_directory = "/tmp"
current_directory = "/tmp"
user = exim
group = mail
log_output = true
return_fail_output = true
return_path_add = false
message_prefix =
message_suffix =

Естественно, не забываем исправить user и group под реальное положение дел :-)

Запускаем, проверяем.
Если при доставке почты получаем что-то, вроде

Child process of address_pipe transport returned 127 (could mean unable to exec or command does not exist) from command: /usr/local/bin/dspam

значит exim не может запустить /usr/local/bin/dspam, поскольку пермишн a+x у запускаемого отсутствует. Ставим, проверяем снова.

Затем проводим обучение системы, пересылая спамовые письма на адрес spam-ваш_ящик@ваш.домен и неспамовые на nospam-ваш_ящик@ваш.домен.
Поскольку юзеры зело ленивы, можно сделать спам-фильтр не индивидульным для каждого ящика, а общим, для всей системы. Возможно это съэкономит вам несколько миллионов нераных клеток, и несколько миллиардов байт на жестком диске :-)

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

X-Spam-Score: 5.8 (+++++)
X-Spam-Report: Spam detection software, running on the system "mx.myhost.com", has
identified this incoming email as possible spam. The original message
has been attached to this so you can view it (if it isn't spam) or label
similar future email. If you have any questions, see
postmaster@myhost.com for details.
Content preview: XXXXXXXXXXX XXXXXXXX   X X X X X X X X
XXXXXX!
              XXXXXXXXXX!
XXXXXXX! XXXXXX XXXXXXX
XXXXXX.        XXXXXXXX XX
XXXXXXX. XXX (495) 6 4 . 8 4 . 4 3 2 [...]
Content analysis details: (5.8 points, 5.0 required)
pts rule name description
---- ---------------------- --------------------------------------------------
0.4 SPF_HELO_FAIL SPF: HELO does not match SPF record (fail)
[SPF failed: Please see http://www.openspf.org/why.html?sender=ocn.ne.jp&ip=221.189.3.135&receiver=sunrise.sgpi.kz]
0.8 HTML_FONT_FACE_CAPS BODY: HTML font face has excess capital characters
0.0 HTML_MESSAGE BODY: HTML included in message
0.2 HTML_FONT_BIG BODY: HTML tag for a big font size
1.8 RCVD_IN_BL_SPAMCOP_NET RBL: Received via a relay in bl.spamcop.net
[Blocked - see ]
2.5 RCVD_IN_XBL RBL: Received via a relay in Spamhaus XBL
[221.189.3.135 listed in sbl-xbl.spamhaus.org]
X-Spam-Flag: YES

На этом заканчиваю. Будут вопросы — пишите.