System antyspamowy DSPAM

Do tej pory, jeśli chodzi o filtrowanie spamu, byłem użytkownikiem programu CRM114 Discriminator, który poza dowcipną nazwą (z filmu "Dr Strangelove") jest na rynku filtrów antyspamowych mniej więcej tym, czym Qmail wśród serwerów SMTP.

Rozbudowa serwera pocztowego stała się jednak dla mnie pretekstem, by zobaczyć co też się nowego na tym rynku wydarzyło i tak trafiłem na DSPAM. Czym charakteryzuje się DSPAM:

  • jest to filtr uczący się, oparty o analizę statystyczną Grahama-Bayesa i innych,
  • cytowana skuteczność rzędu 98-99%,
  • może działać w trybie jednoużytkownikowym albo w trybie serwera dla wielu użytkowników.
  • baza danych może być w plikach, a może być w bazie SQL (Oracle, MySQL, PostgreSQL, SQLite),
  • zapewnia łatwą integrację z ClamAV,
  • generalnie mocno skalowalny, z redundancją i innymi funkcjami odpowiednimi do odpowiedzialnego przetwarzania dużej ilości poczty.

Wyżej wymienione funkcje występują też w innych produktach, ale tutaj są rozwiązane wygodnie i wydajnie, zarówno dla admina i dla end-usera. W rezultacie zamiast instalować jak wszyscy kobylastego i perlowego Amavisa z ClamAV i Spamassasinem, postawiłem zainstalować DSPAM z ClamAV.

Autorzy DSPAM dobrze znają zarówno SpamAssassina i CRM114, jak i parę innych produktów i potrafią je w sposób krytyczny komentować (co nie znaczy - krytykować), co też dobrze świadczy o rozpoznaniu rynku.

Instalacja

DSPAM można zainstalować na dwa sposoby - albo jako filtr, w sensie binarki uruchamianej dla każdego maila przez dowolny MDA (procmail) lub MTA (Postfix) albo w trybie serwerowym (demon). Ten pierwszy jest prostszy w instalacji i nie wymaga roota.

Ale ponieważ każdorazowe forkowanie dspam na moim AMD-K6 trochę go przytykało (128 MB RAM) i pod kątem przyszłych instalacji produkcyjnych wybrałem instalację serwerową. W tym trybie DSPAM dostaje pocztę od MTA po LMTP i zwraca ją po LTMP lub SMTP.

W trybie serwerowym dspam musi być podpięty pod bazę SQL. Ja wybrałem MySQL. Ostatecznie lista nowych pakietów do instalacji wyglądała tak, wszystko było instalowane z portsów OpenBSD:

  • mysql-server-5.0.21
  • postfix-2.3.20060207 - snapshot zaktualizowany z CVS
  • clamav-0.88.2
  • dspam-3.6.3p0-mysql-clamav - zaktualizowany z CVS

Wszystkie instalują się "same" (pkg_add). W przypadku OpenBSD instalacja wymagała podania dodatkowych flag podczas budowania:

#  env FLAVOR="mysql clamav" DAEMON_SUPPORT=Yes make install

Uwaga: musiałem ręcznie wyciąć opcje --enable-virtual-alias z Makefile, bo jest domyślnie włączona dla flavorów mysql i pgsql. Z tą opcją każdy użytkownik ma swoje indywidualne statystyki co się zupełnie nie sprawdza w systemie z jednym użytkownikiem i wieloma aliasami lub w firmie, gdzie polityka antyspamowa jest raczej jedna.

Inicjalizacja MySQL

Jako root czy też administrator MySQL poleceniem "mysql mysql":

CREATE DATABASE dspam;
CREATE USER dspam IDENTIFIED BY 'Yohmath3';
GRANT ALL PRIVILEGES ON dspam.* TO dspam IDENTIFIED BY 'Yohmath3';
GRANT ALL PRIVILEGES ON dspam.* TO dspam@localhost IDENTIFIED BY 'Yohmath3';
USE dspam;
SOURCE /usr/local/share/examples/dspam/mysql/mysql_objects-space.sql;

Konfiguracja DSPAM

Konfiguracja DSPAM mieści się w pliku /etc/dspam.conf. Najważniejsze sekcje są omówione poniżej.

Wskazanie serwera SQL

MySQLServer             /var/run/mysql/mysql.sock
MySQLUser dspam
MySQLPass Yohmath3
MySQLDb dspam
MySQLCompress true
MySQLConnectionCache 10
MySQLUIDInSignature On

Jak widać nie musi to być serwer lokalny, co więcej może to być farma serwerów (DSPAM obsługuje failover.

Wejście na DSPAM

Konfigurujemy DSPAM, tak żeby nasłuchiwał na właściwych portach i odpowiednio zwracał sprawdzoną pocztę. Wszystko w dspam.conf. Parametry nasłuchiwania na LMTP:

ServerMode              standard
ServerPID /var/run/dspam.pid
ServerParameters "--deliver=innocent -d %u"
ServerIdent "localhost.localdomain"
ServerPort 24

Można użyć także lokalnych socketów uniksowych (ServerDomainSocketPath zamiast ServerPort), ale z jakiegoś powodu nie chciał mi się łączyć więc z lenistwa zrobiłem po socketach TCP.

DSPAM do Postfiksa

Dokąd DSPAM ma zwracać sprawdzoną pocztę:

DeliveryHost        127.0.0.1
DeliveryPort 10026
DeliveryIdent localhost
DeliveryProto SMTP

I jeszcze wskazanie gdzie nasłuchuje DSPAM dla klienta dspamc (wczytuje ten sam plik konfiguracyjny):

ClientHost      127.0.0.1
ClientPort 24

Integracja z ClamAV

Cała integracja z antywirusem zawiera się w następujących linijkach z dspam.conf:

ClamAVPort      3310
ClamAVHost 127.0.0.1
ClamAVResponse reject

Flaga reject powoduje odrzucanie zawirusowanej poczty na poziomie SMTP, accept powoduje je potajemne "zjadanie", spam - traktowanie jak spamu i poddawanie analogicznej procedurze (np. kwarantanna). Przetestowałem przy pomocy EICAR i działa.

Inne parametry

Katalog roboczy DSPAM, musi mieć uprawnienia dla użytkownika dspam. W przypadku SQL będą tam tylko zbierane statystyki, bez SQL będzie tam główna baza:

Home /var/dspam

Czy dspam ma logować przetworzone maile globalnie i wg użytkowników (w /var/dspam), przydaje się przy uruchomieniu, potem nie bardzo:

SystemLog on
UserLog off
SupressWebStats on

Tu należy dopisać konta, które biorą udział w przetwarzaniu poczty w systemie:

Trust root
Trust postfix
Trust nobody
...

Parametry klasyfikatora, szczegóły w manualu do dspam:

TrainingMode teft
TestConditionalTraining on
Feature chained
Feature whitelist
Feature noise
Algorithm graham burton
PValue graham
TrackSources spam nonspam
ProcessorBias on

Akcja dla spamu - tag lub quarantine. Jeśli tag, to spamSubject wskazuje napis, jaki będzie dodawany do tematu listu:

Preference "spamAction=tag"
Preference "spamSubject=SPAM"

Sygnatura DSPAM (potrzebna przy reklamacjach) ma być w nagłówkach. Może być też na końcu maila (message), ale w ten sposób psujemy np. podpisane wiadomości.

Preference "signatureLocation=headers"

Jeśli chcemy w każdym mailu mieć wyszczególnione na podstawie jakich zarzutów DSPAM uznał dany list za spam to możemy dodać linijkę:

Preference "showFactors=on"

Wszystkie opcje Preference, których jest więcej, są szczegółowo opisane w DSPAM Wiki Prefence Attributes.

Parametry związane z przetwarzaniem reklamacji - dokłady opis poniżej w rozdziale Reklamacje. Uwaga: te parametry całkowicie zmieniają sposób naliczania statystyk, tzn każdy email w systemie będzie miał oddzielne, co oczywiście psuje sens ich generowania i poprawiania. Dlatego ja mam te opcje wyłączone.

ParseToHeaders off
ChangeModeOnParse off

Przykład dspam.conf

Mój pełny plik konfiguracyjny można znaleźć tutaj dspam.conf.

Konfiguracja Postfiksa

Postfix do DSPAM

DSPAM jest podpinany pod Postfiksa jako content_filter z poziomu /etc/postfix/master.cf:

# tak było
#smtp inet n - - - - smtpd
# tak jest
smtp inet n - n - - smtpd
-o content_filter=lmtp:[127.0.0.1]:24

Wymusza to w Postfiksie wrzucanie każdego otrzymanego prez SMTP maila na port DSPAM przez LMTP. Trzeba też stworzyć kanał powrotny dla maili wracających z DSPAM, dodajmy na końcu master.cf:

127.0.0.1:10026 inet  n -       n       -       -        smtpd
-o content_filter= -o smtpd_authorized_xforward_hosts=127.0.0.0/8
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
-o smtpd_helo_restrictions= -o smtpd_client_restrictions= -o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8

Warto zauważyć, że mam wyłączony chroot (drugie "n" w każdej linijce) bo mi się nie chciało kopiować plików.

Reklamacje

DSPAM jak wszystkie systemy bayesowskie jest systemem uczącym się. Przez pierwsze kilkadziesiąt maili konieczna jest więc interwencja użytkownika, który pokaże systemowi, które maile są dla niego spamem, a które nie.

Akurat DSPAM dość dobrze radzi sobie z automatyczną klasyfikacją, bo wykorzystuje także mechanizmy pozastatystyczne (tak jak Spamassasin). W praktyce wymaga więc tylko nieznacznych korekt.

Jeśli dostaniemy spam zaznaczony jako Innocent, to musimy mieć możliwość odesłania go z powrotem z reklamacją i wskazaniem, że był to spam. Analogicznie robimy, jeśli przyjdzie "dobry" list zaznaczony jako Spam.

Aliasy dla reklamacji

Dla przyjmowania reklamacji zakładamy aliasy (/etc/mail/aliases):

spam-kravietz: "|/usr/local/bin/dspam --source=error --client --class=spam --user kravietz --mode=teft"
nospam-kravietz: "|/usr/local/bin/dspam --source=error --client --class=innocent --user kravietz --mode=teft"

Przeznaczenie tych aliasów jest następujące:

  • jeśli otrzymam spam zaklasyfikowany jako Innocent, to odsyłam go na spam-kravietz@domena, a DSPAM nauczy się go jako spamu,
  • jeśli otrzymam dobry list zaklasyfikowany jako Spam, to odsyłam go na nospam-kravietz.

Oczywiście, dla jednego usera dodatek -kravietz można sobie darować. Dla dużej bazy userów można użyć narzędzia dspam_genaliases, które wygeneruje aliasy automatycznie na podstawie /etc/passwd.

Użyte tutaj opcje są następujące:

  • --source error - przetwarzany list był uprzednio klasyfikowany (DSPAM spodziewa się w nim nagłówka X-DSPAM-Signature i ma być przeklasyfikowany,
  • --client - dspam ma działać w trybie klienta i połączyć się do działającej w trybie demona instancji dspam,
  • --class=spam,innocent - jaka ma być poprawna klasyfikacja (spam, nie spam),
  • --user kravietz - każdy użytkownik w systemie ma swój prywatny rekord z klasyfikacją (uwaga: brak "=").

Uwaga: DSPAM potrafi to robić automatycznie bez aliasów przy pomocy adresów postaci user-dodatek (recipient_delimiter=- w Postfix), ale jest w tym niekonsekwentny bo sam z kolei potrafi rozpoznawać tylko formę odwrotną tj. dodatek-user. Dlatego najlepiej stosować statyczne aliasy.

Preload

W DSPAM kanałów dla reklamacji może być wiele i są one bardzo wydajne, co umożliwia np. zainicjalizowanie filtra dużą bazą spamów, zebraną przez kilka tygodni. Więcej raczej nie ma sensu, bo chcemy poprawnie klasyfikować spam, który krąży dzisiaj a nie sprzed dwóch lat.

Do tego celu służy polecenie dspam_corpus. Mbox powinien być plikiem z mailami w formacie mbox:

# dspam_corpus --addspam kravietz mbox

Faktycznie dspam_corpus sprowadza się do takiego polecenia:

# cat mbox | dspam --source=corpus--client --class=spam --user kravietz --mode=teft

Parametr --source=corpus mówi systemowi, że wejście jest "żywym spamem", nieklasyfikowanym wcześniej.

Spam trap

Osoby z długim stażem pisania w Usenecie spod poprawionego adresu email, który od dawna jest na wszystkich możliwych listach spammerskich, mogą ten fakt wykorzystać do zwiększenia skuteczności uczenia DSPAM.

Jest to funkcja "szczepienia bazy" (inoculation). Polega na tym, że tworzymy alias związany z adresem, na który może przychodzić tylko spam i każdy otrzymany na ten adres list wskazujemy jako świeży spam.

Ja założyłem alias trap w /etc/mail/aliases:

trap: "|/usr/local/bin/dspam --source=inoculation --client --class=spam --user kravietz --mode teft"

A następnie w /etc/postfix/virtual przypisałem do niego odpowiednie, od dawna zaspamowanie adresy email:

a0@echelon.pl                                   trap
fake_kravietz@echelon.pl trap
joghung@echelon.pl trap
kravietz.invalid@echelon.pl trap
krawczykkravietz@echelon.pl trap
news1@echelon.pl

Oczywiście można to zrobić bez virtual, od razu w aliases. Kluczowa jest flaga flaga --source=inoculation w wywołaniu dspam.

 

Używanie

Jeśli wszystko działa poprawnie, to wszystkie przechodzące przez system listy powinny mieć nagłówek DSPAMA (X-DSPAM-Result). Jeśli nie mają lub poczta nie przechodzi to trzeba zobaczyć do logów i zdiagnozować co jest nie tak. Pomocne może być uruchomienie dspam z opcjami --debug --daemon.

Jeśli wszystko działa to jedyne operacje, które trzeba wykonywać to sporadyczne korygowanie klasyfikacji. Od strony systemu opisałem to w rozdziale Reklamacje.

Od strony użytkownika najlepsze do reklamacji jest rozszerzenie Mail Redirect dla Thunderbirda.

Procedura reklamacji jest trywialna:

  1. klikamy na źle sklasyfikowanym mailu prawym przyciskiem, wybieramy Redirect, można ich też zaznaczyć wiele,
  2. wpisujemy adres spam-user@domena jeśli chcemy wskazać że to spam, lub nospam-user@domena, jeśli mail został błędnie uznany za spam.

Zaletą Mail Redirect jest to, że pozwala równocześnie odbić wiele maili co bardzo ułatwia sprawę.

Uwaga: zwykły Forward nie zadziała, bo w trybie --source=error DSPAM oczekuje, że na wejściu będzie oryginalny mail z zachowanym w środku nagłówkiem X-DSPAM-Signature!

Przykładowe konfiguracje

  • dspam.conf - daemon mode, ClamAV i MySQL
  • rc.local - jak to wszystko startuje pod OpenBSD

Zobacz także