System antyspamowy DSPAM

2007-01-05 00:00:00 +0000


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:

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:

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.


<h2>Inicjalizacja MySQL</h2> <p>Jako root czy też administrator MySQL poleceniem "mysql mysql": </p> <pre>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;
</pre> <div class="editsection" style="float: right; margin-left: 5px">
</div><h2>Konfiguracja DSPAM</h2> <p>Konfiguracja DSPAM mieści się w pliku /etc/dspam.conf. Najważniejsze sekcje są omówione poniżej. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Wskazanie serwera SQL</h3> <pre>MySQLServer /var/run/mysql/mysql.sock
MySQLUser dspam
MySQLPass Yohmath3
MySQLDb dspam
MySQLCompress true
MySQLConnectionCache 10
MySQLUIDInSignature On
</pre> <p>Jak widać nie musi to być serwer lokalny, co więcej może to być farma serwerów (DSPAM obsługuje failover. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Wejście na DSPAM</h3> <p>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: </p> <pre>ServerMode standard
ServerPID /var/run/dspam.pid
ServerParameters "–deliver=innocent -d %u"
ServerIdent "localhost.localdomain"
ServerPort 24
</pre> <p>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. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>DSPAM do Postfiksa</h3> <p>Dokąd DSPAM ma zwracać sprawdzoną pocztę: </p> <pre>DeliveryHost 127.0.0.1
DeliveryPort 10026
DeliveryIdent localhost
DeliveryProto SMTP
</pre> <p>I jeszcze wskazanie gdzie nasłuchuje DSPAM dla klienta dspamc (wczytuje ten sam plik konfiguracyjny): </p> <pre>ClientHost 127.0.0.1
ClientPort 24
</pre> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Integracja z ClamAV</h3> <p>Cała integracja z antywirusem zawiera się w następujących linijkach z dspam.conf: </p> <pre>ClamAVPort 3310
ClamAVHost 127.0.0.1
ClamAVResponse reject
</pre> <p>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. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Inne parametry</h3> <p>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: </p> <pre>Home /var/dspam
</pre> <p>Czy dspam ma logować przetworzone maile globalnie i wg użytkowników (w /var/dspam), przydaje się przy uruchomieniu, potem nie bardzo: </p> <pre>SystemLog on
UserLog off
SupressWebStats on
</pre> <p>Tu należy dopisać konta, które biorą udział w przetwarzaniu poczty w systemie: </p> <pre>Trust root
Trust postfix
Trust nobody

</pre> <p>Parametry klasyfikatora, szczegóły w manualu do dspam: </p> <pre>TrainingMode teft
TestConditionalTraining on
Feature chained
Feature whitelist
Feature noise
Algorithm graham burton
PValue graham
TrackSources spam nonspam
ProcessorBias on
</pre> <p>
Akcja dla spamu - tag lub quarantine. Jeśli tag, to spamSubject wskazuje napis, jaki będzie dodawany do tematu listu: </p> <pre>Preference "spamAction=tag"
Preference "spamSubject=SPAM"
</pre> <p>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. </p> <pre>Preference "signatureLocation=headers"
</pre> <p>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ę: </p> <pre>Preference "showFactors=on"
</pre> <p>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. </p> <pre>ParseToHeaders off
ChangeModeOnParse off
</pre> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Przykład dspam.conf</h3> <p>Mój pełny plik konfiguracyjny można znaleźć tutaj dspam.conf. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h2>Konfiguracja Postfiksa</h2> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Postfix do DSPAM</h3> <p>DSPAM jest podpinany pod Postfiksa jako content_filter z poziomu /etc/postfix/master.cf: </p> <pre># tak było
#smtp inet n - - - - smtpd
# tak jest
smtp inet n - n - - smtpd
-o content_filter=lmtp:[127.0.0.1]:24
</pre> <p>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: </p> <pre>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
</pre> <p>Warto zauważyć, że mam wyłączony chroot (drugie "n" w każdej linijce) bo mi się nie chciało kopiować plików. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Reklamacje</h3> <p>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. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Aliasy dla reklamacji</h3> <p>Dla przyjmowania reklamacji zakładamy aliasy (/etc/mail/aliases): </p> <pre>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"
</pre> <p>Przeznaczenie tych aliasów jest następujące: </p> <ul><li> jeśli otrzymam spam zaklasyfikowany jako Innocent, to odsyłam go na spam-kravietz@domena, a DSPAM nauczy się go jako spamu, </li><li> jeśli otrzymam dobry list zaklasyfikowany jako Spam, to odsyłam go na nospam-kravietz. </li></ul> <p>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: </p> <ul><li> –source error - przetwarzany list był uprzednio klasyfikowany (DSPAM spodziewa się w nim nagłówka X-DSPAM-Signature i ma być przeklasyfikowany, </li><li> –client - dspam ma działać w trybie klienta i połączyć się do działającej w trybie demona instancji dspam, </li><li> –class=spam,innocent - jaka ma być poprawna klasyfikacja (spam, nie spam), </li><li> –user kravietz - każdy użytkownik w systemie ma swój prywatny rekord z klasyfikacją (uwaga: brak "="). </li></ul> <p>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. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Preload</h3> <p>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: </p> <pre># dspam_corpus –addspam kravietz mbox
</pre> <p>Faktycznie dspam_corpus sprowadza się do takiego polecenia: </p> <pre># cat mbox | dspam –source=corpus–client –class=spam –user kravietz –mode=teft
</pre> <p>Parametr –source=corpus mówi systemowi, że wejście jest "żywym spamem", nieklasyfikowanym wcześniej. </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h3>Spam trap</h3> <p>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: </p> <pre>trap: "|/usr/local/bin/dspam –source=inoculation –client –class=spam –user kravietz –mode teft"
</pre> <p>A następnie w /etc/postfix/virtual przypisałem do niego odpowiednie, od dawna zaspamowanie adresy email: </p> <pre>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
</pre> <p>Oczywiście można to zrobić bez virtual, od razu w aliases. Kluczowa jest flaga flaga –source=inoculation w wywołaniu dspam.  </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h2>Używanie</h2> <p>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: </p> <ol><li> klikamy na źle sklasyfikowanym mailu prawym przyciskiem, wybieramy Redirect, można ich też zaznaczyć wiele, </li><li> 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. </li></ol> <p>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! </p> <div class="editsection" style="float: right; margin-left: 5px">
</div><h2>Przykładowe konfiguracje</h2> <ul><li> dspam.conf - daemon mode, ClamAV i MySQL </li><li> rc.local - jak to wszystko startuje pod OpenBSD </li></ul> <div class="editsection" style="float: right; margin-left: 5px">
</div><h2>Zobacz także</h2> <ul><li> Nuclear Elephant DSPAM </li><li> Installation of DSPAM </li><li> Administracja i konfiguracja </li><li> Różne scenariusze instalacji DSPAM, od najprostszych (procmail) do najbardziej rozbudowanych. </li></ul><p> </p>