Kategorien

Navigation

Feeds/Meta

Blog RSS Feed
Kommentare RSS Feed

Manuel Josupeit-Walter - XING Profil
Manuel Josupeit-Walter - Amazon Wunschliste
Manuel Josupeit-Walter - XMPP (Jabber)
Manuel Josupeit-Walter - ICQ
Manuel Josupeit-Walter - Skype

Bloggeramt.de
BlogAlm
Add to Technorati Favorites

SpamPoison

josupeit.com > Weblog > Informatik und Technik > Linux und Serversoftware


SpamAssassin anhand von IMAP Flags lernen lassen

Datum:   30.01.2010, 14:38 Uhr
Kategorie:   Linux und Serversoftware Feed dieser Kategorie abonnieren
Kommentare:   0

All meine eingehenden E-Mails werden direkt meinem Homeserver zugestellt. Dort wird auch ein Großteil an Spam gefiltert. In der Vergangenheit trainierte ich den dort laufenden SpamAssassin durch bouncen der entsprechenden Mails an spezielle E-Mail Adressen, die die Mails dann an sa-learn pipeten. Da mir das immer wieder einmal auf den Keks ging fragte ich mich, ob es nicht viel einfacher möglich wäre, Spam anhand der Thunderbird Junk-Markierung lernen zu lassen.

Glücklicherweise bietet das IMAP4 Protokoll laut RFC 3501 die Möglichkeit, Mails mit Flags zu versehen. Von Hause aus gibt es beispielsweise Flags, wie \Seen oder \Deleted, die Kennzeichnen, ob eine Nachricht gelesen oder gelöscht wurde, jedoch erlaubt das Protokoll auch die Vergabe eigener Flags. Diese benutzerdefinierten Flags werden allerdings als Keywords bezeichnet. Thunderbird verwendet für die Markierung, ob eine Mail Spam oder Ham (also kein Spam) ist, die Keywords Junk und NonJunk, wobei nur Mails, die als Junk markiert wurden und explizit auf "Kein Junk" geklickt wurde, als NonJunk markiert werden.

Nun verwende ich ein Perl-Skript, das alle 15 Minuten als Cronjob gestartet wird, mit den Benutzerdaten des Administrators zu meinem Cyrus-IMAP Server verbindet und alle Ordner aller Benutzer auf Mails durchsucht, die entsprechend gekennzeichnet wurden und den Inhalt dann an SpamAssassin übergibt. Anschließend werden die Mails entsprechend als LernedJunk und LernedNonJunk gekennzeichnet. Dadurch werden automatisch alle Mails, die Thunderbird als Junk einstuft oder von Hand eingestuft werden bereits serverseitig gelernt und entsprechend stetig besser gefiltert.

Dieses Skript stelle ich hiermit unter GPLv3 zur Verfügung, vielleicht findet ja noch jemand Verwendung dafür:

#!/usr/bin/perl
use Mail::IMAPClient;
use MIME::Base64;

do('/etc/bayes_learn_spam.conf')
  or die "Unable to read configuration file /etc/bayes_learn_spam.conf";

my $imap = Mail::IMAPClient->new(
             Server => $host,
             Port => $port,
             Debug => 0)

  or die "Unable to connect to imap server";

$imap->has_capability("STARTTLS") and $imap->starttls;
$imap->tag_and_run("AUTHENTICATE PLAIN " . encode_base64("\0" . $authuser . "\0" . $password))
  or die "Unable to login to imap server with user " . $authuser;

my @folders = $imap->folders
  or die "Unable to retrieve user list from imap server";

my @users;

foreach $folder (@folders) {
  if ( $folder =~ /^user\..*$/i ) {
    $folder =~ s/^user\.([^\.]*).*/\1/i;

    if (! grep /$folder/, @users) {
      push (@users, $folder);
    }
  }
}

$imap->disconnect
  or die "Unable to close connection to imap server";

foreach $user (@users) {
  print "Processing messages for user " . $user . "...\n";
  undef($imap);

  my $imap = Mail::IMAPClient->new(
               Server => $host,
               Port => $port,
               Uid => 0,
               Peek => 1,
               Debug => 0)

    or die "Unable to connect to imap server";

  $imap->has_capability("STARTTLS") and $imap->starttls;
  $imap->tag_and_run("AUTHENTICATE PLAIN " . encode_base64($user . "\0" . $authuser . "\0" . $password))
    or die "Unable to do plain auth";

  foreach $folder ($imap->folders) {
    print "Processing folder " . $folder . "...\n";
    $imap->select($folder)
      or die "Unable to select imap folder " . $folder . " for user " . $user;

    @spam = $imap->search("UNDELETED KEYWORD " . $junktag .    " UNKEYWORD " . $learnedjunktag);
    @ham  = $imap->search("UNDELETED KEYWORD " . $nonjunktag . " UNKEYWORD " . $learnednonjunktag);

    # Process spam
    print "Learning " . @spam . " spam messages...\n";
    foreach (@spam) {
      $imap->store($_, "-FLAGS", $learnednonjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;

      @msg = $imap->message_string($_)
        or die "Unable to fetch spammy mail " . $_ . " from folder " . $folder . " for user " . $user;

      open(my $sa, "| " . $salearn . " --spam")
        or die ("Unable to pipe to sa-learn");

      print $sa @msg;
      close($sa);
      undef($sa);

      $imap->store($_, "+FLAGS", $learnedjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;
    }

    # Process ham
    print "Done. Learning " . @ham . " ham messages...\n";
    foreach (@ham) {
      $imap->store($_, "-FLAGS", $learnedjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;

      @msg = $imap->message_string($_)
        or die "Unable to fetch hammy mail " . $_ . " from folder " . $folder . " for user " . $user;

      open(my $sa, "| " . $salearn . " --ham")
        or die ("Unable to pipe to sa-learn");

      print $sa @msg;
      close($sa);
      undef($sa);

      $imap->store($_, "+FLAGS", $learnednonjunktag)
        or die "Unable to flag message " . $_ . " in folder " . $folder . " for user " . $user;
    }

    print "Done learning messages from folder " . $folder . ".\n";
  }

  $imap->disconnect;
  print "All messages for user " . $user . " have been processed.\n";
}


Die Konfiguration erfolgt über die Datei /etc/bayes_learn_spam.conf, die (entsprechend Zugriffsgeschützt) folgende Daten enthält:

$host              = 'localhost';
$port              = 143;
$authuser          = 'cyrus';
$password          = 'passwort';

$salearn           = '/usr/bin/sa-learn';

$junktag           = 'Junk';
$nonjunktag        = 'NonJunk';
$learnedjunktag    = 'LearnedJunk';
$learnednonjunktag = 'LearnedNonJunk';

 
Viel Spaß beim Trainieren. :-) Übrigens: Thunderbird bietet die Möglichkeit, benutzerdefinierte Schlüsselworte zu definieren und farbig hervorzuheben, so hat man immer und überall den Überblick, was gelernt wurde und was nicht...


GPLv3

Spamannahme verweigern mit Postfix und Postprox

Datum:   22.10.2009, 20:03 Uhr
Kategorie:   Linux und Serversoftware Feed dieser Kategorie abonnieren
Kommentare:   0

Wenn Sie einen eigenen Mailserver betreiben, haben Sie sich vielleicht auch schon gefragt wie es möglich ist, die Annahme von Spam oder mit Viren infizierter Nachrichten direkt zu verweigern, denn jede zurückgewiesene Mail ist dann nicht mehr Ihr Problem, sondern das des versendenden Mailservers.

Der Mail Transfer Agent (MTA) Postfix bietet hierfür geeignete Schnittstellen an, wie z.B. sogenanntes Pre-Queue Filtering. Dabei wird die eingehende Mail bevor sie in die Mail-Warteschlange aufgenommen wird bereits an einen SMTP-Proxy weitergegeben, das heißt, Postfix stellt die Mail einem anderen Mailserver zu, der idealerweise auf dem selben Host läuft. Dieser auf einen Anwendungsfall spezialisierte Mailserver ist jedoch nicht dafür zuständig, die Mail letztlich zuzustellen, sondern agiert als sogenannter Proxy. Er führt auf der ihm zugestellten Mail seine "Arbeit" aus, wie z.B. die Untersuchung auf Spam mittels SpamAssassin und gibt die Mail dann wahlweise wieder an Postfix zurück oder weist sie mit einem entsprechenden Statuscode ab. In letzterem Fall wird dann schließlich die Annahme verweigert.

Der in diesem Beispiel verwendete SMTP-Proxy nennt sich postprox. Postprox ist ein Programm, dass vom Standardeingabestream die eingehende E-Mail-Kommunikation erwartet und an einen anderen Server (eine weitere "Instanz" von Postfix) direkt weiterleitet, mit Ausnahnme des DATA Blocks (siehe auch: Funktionsweise des SMTP-Protokolls bei Wikipedia). Dieser Block wird nach Beendigung zunächst an ein über das Kommandozeilenargument -c angegebenes Programm weitergegeben. Der Rückgabewert dieses Programms soll im fehlerfreien Fall 0 und im Fehlerfall 1 sein, denn basierend auf diesem Rückgabewert wird der MTA die Mail annehmen (0) oder verweigern (1). Dabei wird der gesamte DATA Block zuvor in einer temporären Datei gespeichert, die das von Postprox aufgerufene Programm über die Umgebungsvariable $EMAIL öffnen kann (weitere Umgebungsvariablen finden sich in der Manpage von Postprox).

Möchte man, wie oben angedeutet, die Nachricht mit SpamAssassins spamc untersuchen, eignet sich folgendes Skript, dass beispielsweise unter /usr/bin/postprox_spamc_wrapper gespeichert wird:

#!/bin/sh
SPAMC=/usr/bin/spamc

$SPAMC -E <$EMAIL > $OUTFILE 2>/dev/null
STATUS=$?

if [ $STATUS -eq 1 ] ; then
  echo 550 Message is considered to be spam 1>&2
  exit 1
fi

exit 0


Nun muss postprox in den MTA eingebunden werden. Dazu wird Postfix' Konfigurationsdatei main.cf um folgende Zeile ergänzt:

smtpd_proxy_filter = 127.0.0.1:10024


Dadurch wird Postfix versuchen, eingehende Mails bereits vor der Warteschlange an den Proxy weiterzugeben, der lokal auf Port 10024 lauscht. Damit dort jedoch überhaupt jemand lauscht, muss nun noch die Date master.cf angepasst werden:

[127.0.0.1]:10024 inet n     n     n     -     20     spawn
        user=cyrus argv=/usr/lib/postfix/postprox -r -c /usr/bin/postprox_spamc_wrapper 127.0.0.1:10025


In dieser Zeile muss ggf. noch der Benutzername, sowie der Pfad zu postprox angepasst werden. Diese Zeile bewirkt, dass Postfix dafür sorgt, dass Postprox selbst nun auf Port 10024 auf eingehende Daten wartet und diese an den SMTP-Server auf dem lokalen Port 10025 weiterleitet. Auf diesem Port muss nun Postfix selbst wieder lauschen, dafür genügt folgender Eintrag in der Datei master.cf:

[127.0.0.1]:10025 inet n     -      n     -     -     smtpd
        -o smtpd_proxy_filter=
        -o mynetworks=127.0.0.1/32
        -o smtpd_authorized_xforward_hosts=127.0.0.1/32
        -o smtpd_client_restrictions=
        -o smtpd_data_restrictions=
        -o smtpd_end_of_data_restrictions=
        -o smtpd_helo_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject_unauth_destination
        -o smtpd_sender_restriction=
        -o receive_override_options=no_unknown_recipient_checks


Nach einem Neustart von Postfix wird dieser nun die Mais zunächst annehmen und an Postprox weiterreichen. Postprox wird mittels SpamAssassin prüfen, ob es sich um Spam handelt (Konfiguration von SpamAssassin beachten) und die Mail entweder zurückweisen oder über Port 10025 wieder an Postfix zurückgeben. Erst danach wird die Mail der Warteschlange zugeführt. Als Spam erkannte Mails werden so garnicht erst angenommen.

Auf diesem Weg lassen sich z.B. auch durch Viren infizierte Mails mit Clam AntiVirus überprüfen und früh abweisen. Falls Sie noch weitere Ideen haben, an welche Tools Postprox den DATA Block delegieren kann, bin ich für Kommentare dankbar. :-)

FPS-3003 mit "alter" Firmware flashen

Datum:   07.01.2009, 17:33 Uhr
Kategorie:   Linux und Serversoftware Feed dieser Kategorie abonnieren
Kommentare:   5

Vor kurzem ist leider einer der beiden USB Ports meines LevelOne Printservers FPS-3003 abgeraucht. Damit ich nur das Gerät tauschen muss und nicht noch das Netzteil, habe ich mir den selben Printserver erneut bestellt - vermeindlich den selben.

Die aktuelle Version 4 des Geräts wird nämlich mit einer völlig anderen Firmware ausgeliefert, die direktes drucken über LPR Warteschlagen oder RAW Ports 9100 und 9101 nicht mehr unterstützt. Ausschließlich die Fileserver-Funktionen über Samba und FTP sind geblieben, alles Übrige wird durch eine neue Software ersetzt, die (ausschließlich unter Windows) mit den angeschlossenen Druckern verbinden kann. Die Firmware lässt sich zwar über die Weboberfläche updaten, allerdings kann keine Firmware für Geräteversion 3 installiert werden, die die gewünschte Funktionalität auf dem augenscheinlich baugleichen Gerät Version 4 bereitstellt.

Über einen Umweg lässt sich dennoch die Firmware Version 1.26 [Update vom 29.12.2010: Deeplink nicht mehr möglich, siehe Kommentar von Rolf] auf dem Gerät installieren: über TFTP. Vorraussetzung dazu ist ein entsprechender Client auf einem Host im Netzwerk. Unter Vista beispielsweise kann ein solcher über Systemsteuerung/Programme und Funktionen/Windows-Funktionen ein- oder ausschalten nachinstalliert werden, eine Installation unter Windows XP ist ebenfalls möglich. Da TFTP ein einfaches und bekanntes Protokoll ist, gibt es aber auch für jede erdenkliche Linux Distribution entsprechende Clients.

Nun muss der TFTP-Server des Printservers aktiviert werden. Dazu befindet sich ein Taster mit der Aufschrift init am Gerät, der gedrückt werden muss, während die Stromversorgung des FPS-3003 zunächst getrennt und dann wiederhergestellt werden muss. Die blinkenden LEDs für beide USB-Ports signalisieren den erfolgreichen Start dieses "Rettungsmodus". Alle Einstellungen gehen jedoch leider bei diesem Vorgang verloren, so auch die IP-Adressen, die in diesem Modus auch nicht via DHCP bezogen werden. Demzufolge muss der Client, von dem aus die Firmware geflasht werden soll zunächst in das selbe Subnetz gebracht werden (Auslieferzustand des Printservers ist 192.168.1.10/24), das heißt, dem Host muss beispielsweise die IP-Adresse 192.168.1.1 und die Subnetzmaske 255.255.255.0 statisch zugewiesen werden, damit beide Geräte untereinander kommunizieren können.

Nun muss über die Kommandozeile (in Windows XP und Vista erreichbar über Start/Ausführen/cmd) die Firmware auf das Gerät übertragen werden. Damit dieser Vorgang erfolgreich abgeschlossen werden kann ist es nötig, die Firmware-Datei (mit der Dateiendung .bin) in app.bin umzubenennen. Der anschließende Konsolenaufruf

tftp -i 192.168.1.10 PUT app.bin

überträgt nun die Firmware aufs Gerät. Nun kann die Einrichtung über die Weboberfläche des Printservers beginnen: Viola, sogar die LPR und RAW Einstellungen lassen sich nun wieder vornehmen.

Abschließend möchte ich jedoch darauf aufmerksam machen, dass flashen von Hardware immer auch schief gehen und das Gerät dadurch kaputt gehen kann. Daher empfiehlt sich auch eine Sicherung der aktuellen Firmware (auch das ist sicher über TFTP möglich, allerdings habe ich das nicht getestet), um den Ursprungszustand wiederherstellen zu können. Ob durch die Ersetzung der Firmware durch eine andere Version die Herstellergarantie erlischt, ist mir leider nicht bekannt.

In diesem Sinne: Viel Spaß beim flashen... 8-)


[1]