Seit wohl mehr als zwei Jahren wundere ich mich hin und wieder, weshalb mein SpamAssassin bei der Beurteilung, ob es sich bei einer eintreffenden E-Mail um Spam oder nicht handelt, nicht langsam mal ein wenig cleverer wird. Nutze ich doch schliesslich sa-learn
und verfüttere ihm gesammelte Spam-E-Mails, damit deren Signaturen erkannt und zukünftig nicht mehr durchgelassen werden. Irgendwie wurde es aber nie besser. Gefühlt kam immer mehr Spam durch. Aber das Thema war auch nicht so wichtig, dass ich es mit Nachdruck verfolgt hätte. Bis vor ein paar Tagen.
Ich habe auf meinem Server eine Konfiguration mit Exim4, SpamAssassin (sa-exim
) und Cyrus als IMAP-Server. Der Server ist MX für einige Domains und überprüft eingehende E-Mails bereits während und nach den SMTP-Headern nach verschiedenen Methoden auf Spam. Sicher als Spam eingestufte E-Mails werden nicht angenommen, E-Mails, bei denen es nicht so klar ist (mittlerer Spam-Score), landen in einem Quarantäne-Ordner und werden hin und wieder begutachtet.
Kürzlich bin ich hinter das Rätsel gekommen, weshalb das Erlernen neuer Spam-Signaturen nicht funktioniert hat: Spam-E-Mails in diesem Quarantäne-Ordner werden von mir von Zeit zu Zeit in einen Spam-Ordner verschoben oder gelöscht. Nun bringt SpamAssassin ja die nette Funktion mit sich, dass man ihn Spam oder Ham “lernen” lassen kann. Da ich auf meinem Server allerdings IMAP verwende, die mitgelieferte Version von sa-learn
nur Folder im mbox-Format lesen kann, wurde also zwei Jahre lang ein Ordner indiziert, welcher zwar (warum auch immer, vermutlich Altlast aus der Zeit vor IMAP) vorhanden war, allerdings keineswegs mit den Spam-Mails gefüttert wurde. Gna, so simpel…
Also habe ich neulich nach einer (einfachen) Methode gesucht, um dem SpamAssassin direkt meinen IMAP-Spam-Folder lesen und auswerten zu lassen. Und siehe da, auf dieser Seite wurde ich mit folgendem Script fündig:
#!/usr/bin/perl<br /> #<br /> # imap-sa-learn.pl, wrapper around sa-learn for imap mail boxes<br /> # Copyright (C) 2004 David M. Zendzian<br /> # Copyright (C) 2006 Koen Martens<br /> #<br /> # This library is free software; you can redistribute it and/or<br /> # modify it under the terms of the GNU Lesser General Public<br /> # License as published by the Free Software Foundation; either<br /> # version 2.1 of the License, or (at your option) any later version.<br /> #<br /> # This library is distributed in the hope that it will be useful,<br /> # but WITHOUT ANY WARRANTY; without even the implied warranty of<br /> # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br /> # Lesser General Public License for more details.<br /> #<br /> # You should have received a copy of the GNU Lesser General Public<br /> # License along with this library; if not, write to the Free Software<br /> # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA<br /> #<br /> # Feed mail from an imap mail folder to sa-learn. Options:<br /> #<br /> #<br /> # Derived from: http://www.dmzs.com/tools/files/spam.phtml<br /> # Derived by : Koen Martens,<br /> # Derived on : October, 13th (Friday), 2006<br /> # License : LGPL<br /> #<br /> # Things to try if it doesn't work<br /> # 1) Use -v for verbose output<br /> # 2) Check your local.cf for spamassassin bayes_path settings.<br /> #<br /> # Also be sure to check that your spamassassin is truely using the<br /> # bayes files (-D manual startup of spamd to debug there)<br /> #<br /> # Modified by ZMI 20090519 ( michael.monnerie@is.it-management.at )<br /> #</p> <p>use Mail::IMAPClient;<br /> use Getopt::Long;</p> <p>sub usage {<br /> $rv = "--host name Host name\n";<br /> $rv = "-p 1234 Port number\n";<br /> $rv = "--port=1234 Port number\n";<br /> $rv.= "--spam Classify messages as spam\n";<br /> $rv.= "--ham Classify messages as ham\n";<br /> $rv.= "-m name Set mailbox name\n";<br /> $rv.= "-mbox name Set mailbox name\n";<br /> $rv.= "-u username Username to login with\n";<br /> $rv.= "--user username Username to login with\n";<br /> $rv.= "-p password Password to login with\n";<br /> $rv.= "--pass password Password to login with\n";<br /> $rv.= "--password password Password to login with\n";<br /> $rv.= "--debug 1 more verbose output, good for scripts\n";<br /> $rv.= "--debug 2 fully verbose output\n";<br /> $rv.= "-d Delete messages after processing\n";<br /> $rv.= "--delete Delete messages after processing\n";<br /> $rv.= "-h Show this help text\n";<br /> $rv.= "--help Show this help text\n";<br /> return $rv;<br /> }</p> <p>my $host='localhost';<br /> my $port=143;<br /> my $spam=0;<br /> my $ham=0;<br /> my $mbox='learnspam';<br /> my $user='foo';<br /> my $pass='bar';<br /> # ZMI 20090519: variables for easy reconfiguration:<br /> my $salearnbin="/usr/bin/sa-learn";<br /> my $tmpfile="/tmp/imapsalearn$$";</p> <p>my $delete=0;<br /> # debug 1: terse reporting<br /> # debug 2: full reporting<br /> my $debug=0;<br /> my $help=0;</p> <p>if(not GetOptions( 'host=s' =&amp;gt; \$host,<br /> 'p|port=i' =&amp;gt; \$port,<br /> 'spam' =&amp;gt; \$spam,<br /> 'ham' =&amp;gt; \$ham,<br /> 'm|mbox=s' =&amp;gt; \$mbox,<br /> 'u|user=s' =&amp;gt; \$user,<br /> 'p|pass|password=s' =&amp;gt; \$pass,<br /> 'debug=i' =&amp;gt; \$debug,<br /> 'd|delete' =&amp;gt; \$delete,<br /> 'h|help' =&amp;gt; \$help)) {<br /> die(usage());<br /> }</p> <p>if( ($spam &amp;amp;&amp;amp; $ham) || not($spam || $ham) || $help) {<br /> die(usage());<br /> }</p> <p>my $spamhamopt=$spam?"--spam":"--ham";</p> <p>my $salearn;</p> <p>print "connecting...\n";<br /> my $imapdebug=0;<br /> if ($debug &amp;gt;= 2) { $imapdebug=1; }<br /> my $imap = Mail::IMAPClient-&amp;gt;new( Server=&amp;gt; "$host:$port",<br /> User =&amp;gt; $user,<br /> Password =&amp;gt; $pass,<br /> Debug =&amp;gt; $imapdebug);</p> <p>if (!defined($imap)) { die "IMAP Login Failed"; }<br /> print "connected...\n";</p> <p># print out the total counts for each mailbox<br /> print "getting spam count...\n";<br /> my $spamcount = $imap-&amp;gt;message_count($mbox);<br /> print $spamcount, ($spam?" spam":" ham")," to process\n";</p> <p># Process the spam mailbox<br /> print "get all messages...\n";<br /> $imap-&amp;gt;select($mbox);<br /> my @msgs = $imap-&amp;gt;search("ALL");<br /> $|=1;<br /> print "processing...\n";<br /> for (my $i=0;$i &amp;lt;= $#msgs; $i++) { print "$i"; # I put it into a file for processing, doing it into a perl var &amp;amp; piping through sa-learn just didn't seem to work $imap-&amp;gt;message_to_file("$tmpfile",$msgs[$i]);</p> <p># execute sa-learn w/data<br /> if ($debug &amp;gt;= 2) {<br /> $salearn = `$salearnbin -D --no-sync $spamhamopt $tmpfile`;<br /> print "-------\nlearn: ",$salearn,"-------\n";<br /> }<br /> else {<br /> $salearn = `$salearnbin --no-sync $spamhamopt $tmpfile`;<br /> if ($debug == 1) { print ": ",$salearn; }<br /> else { print "\r"; }<br /> }</p> <p># delete processed message<br /> if($delete) {<br /> $imap-&amp;gt;delete_message($msgs[$i]);<br /> }<br /> unlink("$tmpfile");<br /> }<br /> print "\nDone processing..\n";<br /> if($delete) {<br /> $imap-&amp;gt;expunge();<br /> }<br /> $imap-&amp;gt;close();</p> <p>$imap-&amp;gt;logout();</p> <p># integrate learned stuff<br /> my $sarebuild = `$salearnbin --sync`;<br /> if ($debug &amp;gt;= 2) { print "\n-------\nRebuild: ",$sarebuild,"-------\n"; }<br /> else { if ($debug == 1) { print "\nRebuild: ",$sarebuild; } }
Habe es als Script hinterlegt, die Perl-Module nachgezogen, die Config angepasst und es funktioniert sofort. Prima, so soll’s sein. Ich empfehle, das Script ohne Parameter aufzurufen, sondern die gewünschten Werte im Code zu hinterlegen. Da ggf. ein Passwort eingetragen werden muss, muss natürlich auf die Berechtigungen geachtet werden. Ich habe es ausschliesslich für root les- und ausführbar gemacht.
Aufwand:
- 5 Minuten nach der Ursache suchen
- 5 Minuten wundern und Kopfschütteln
- 2 Minuten nach einer
sa-learn
Lösung für IMAP suchen - 1 Minute Script einbauen
- 1 Minute Config anpassen
- ein Leben lang freuen
So einfach kann’s manchmal sein, wenn man endlich mal die Muße hat, nach der Ursache zu suchen.
Hi,
danke für den Post.
Wie sammelst du aber *spam* bzw *ham* von anderen Boxen ein?