Inhaltsverzeichnis

erweiterte gewichtete Prüfungen mittels policyd-weight

Das zweite wesentliche Standbein bei unserer SPAM- und Schadcode-Prüfung von eMails ist der Policy-Dämon policyd-weight von Robert Felber. Postfix leitet alle Client-Angaben bei der Einlieferung an den Policy-Dämon während des SMTP-Dialogs weiter, der dann eine Plausibilitätsprüfung vornimmt. Fälscht ein SPAMer den HELO Namen bzw. die Absenderangaben beim MAIL FROM, so wird dies ebenso bei der gewichteten Prüfung negativ bewertet, die das Vorhandensein, der Clioent-IP-Adresse auf einer oder mehreren RBLs.

Policyd-weight nimmt vereinfacht und zusammengefasst folgende Prüfungen vor:

  1. Passt der Hostname der beim HELO übertragen wurde, zu IP-Adresse und verweist seinerseits die IP-Adresse bei einer DNS reverse-Abfrage auf diesen Histnamen.
  2. Steht die IP-Adresse des einliefernden Hosts auf mehreren Sperrlisten? Dabei unterscheidet der Policy-Dämon zwischen einzelnen RBLs und vergibt entsprechend negative Punkte, sofern die IP-Adresse gelistet ist aber auch positive Punkte, wenn die IP-Adresse eben nicht gelistet ist.

Wird bei der Prüfung durch das komplexe Regelwerk festgestellt, dass Kombinationen aus den oben genannten Punkten zu negativ aufgefallen sind, dann wird die weitere Bearbeitung beendet und die Annahme der eMail mit einem 500er Fehler-Code final abgewiesen.

Download und Installation

Da es noch kein vorgefertigtes RPM in den einschlägigen RPM-Quellen gibt, welchen wir vertrauen, installieren wir den daemon selbst per Hand.

Wir holen uns also als erstes das besagte Perl-Script in der aktuellen Version von der Projektseite.

 # wget http://www.policyd-weight.org/policyd-weight

Als nächstes passen wir die Dateirechte an, so dass das Script ausgeführte werden kann.

 # chmod u+rx policyd-weight

Zum Schluß verschieben wir das script noch an Ort und Stelle unter /usr/local/bin.

 # mv policyd-weight /usr/local/bin/

Das vom daemon benötigte Perl-Paket perl-Net-IP installieren wir noch mit Hilfe von yum.

Konfiguration

Programmcheck

Als erstes überprüfen wir, ob sich das Perl-Script ausführen lässt. Hierzu fragen wir den Versionsstand des Scripts ab.

 # policyd-weight -v
 policyd-weight version: 0.1.15 beta-2, CacheVer: 5
 Perl version:           5.010001
 Net::DNS version:       0.65
 OS:                     Linux 2.6.32-220.17.1.el6.x86_64

Konfigurationsdatei erzeugen

Als nächstes erstellen wir uns die Default-Konfigurationsdatei.

 # policyd-weight defaults > /etc/policyd-weight.conf
 # vim /etc/policyd-weight.conf
/etc/policyd-weight.conf
# ----------------------------------------------------------------
#  policyd-weight configuration (defaults) Version 0.1.15 beta-2 
# ----------------------------------------------------------------
 
 
   $DEBUG        = 0;               # 1 or 0 - don't comment
 
   $REJECTMSG    = "550 Mail appeared to be SPAM or forged. Ask your Mail/DNS-Administrator to correct HELO and DNS MX settings or to get removed from DNSBLs";
 
   $REJECTLEVEL  = 1;               # Mails with scores which exceed this
                                    # REJECTLEVEL will be rejected
 
   $DEFER_STRING = 'IN_SPAMCOP= BOGUS_MX='; 
                                    # A space separated case-sensitive list of
                                    # strings on which if found in the $RET
                                    # logging-string policyd-weight changes
                                    # its action to $DEFER_ACTION in case
                                    # of rejects.
                                    # USE WITH CAUTION!
                                    # DEFAULT: "IN_SPAMCOP= BOGUS_MX="
 
 
   $DEFER_ACTION = '450';           # Possible values: DEFER_IF_PERMIT,
                                    # DEFER_IF_REJECT, 
                                    # 4xx response codes. See also access(5)
                                    # DEFAULT: 450
 
   $DEFER_LEVEL  = 5;               # DEFER mail only up to this level
                                    # scores greater than DEFER_LEVEL will be
                                    # rejected
                                    # DEFAULT: 5
 
   $DNSERRMSG         = '450 No DNS entries for your MTA, HELO and Domain. Contact YOUR administrator';
 
   $dnsbl_checks_only = 0;          # 1: ON, 0: OFF (default)
                                    # If ON request that ALL clients are only
                                    # checked against RBLs
 
   @dnsbl_checks_only_regexps = (
    # qr/[^.]*(exch|smtp|mx|mail).*\..*\../,
    # qr/yahoo.com$/
);                                  # specify a comma-separated list of regexps
                                    # for client hostnames which shall only
                                    # be RBL checked. This does not work for
                                    # postfix' "unknown" clients.
                                    # The usage of this should not be the norm
                                    # and is a tool for people which like to
                                    # shoot in their own foot.
                                    # DEFAULT: empty
 
 
   $LOG_BAD_RBL_ONLY  = 1;          # 1: ON (default), 0: OFF
                                    # When set to ON it logs only RBLs which
                                    # affect scoring (positive or negative)
 
## DNSBL settings
   @dnsbl_score = (
#    HOST,                    HIT SCORE,  MISS SCORE,  LOG NAME
    'pbl.spamhaus.org',       3.25,          0,        'DYN_PBL_SPAMHAUS',
    'sbl-xbl.spamhaus.org',   4.35,       -1.5,        'SBL_XBL_SPAMHAUS',
    'bl.spamcop.net',         3.75,       -1.5,        'SPAMCOP',
    'dnsbl.njabl.org',        4.25,       -1.5,        'BL_NJABL',
    'ix.dnsbl.manitu.net',    4.35,          0,        'IX_MANITU'
    #'rbl.ipv6-world.net',     4.25,          0,        'IPv6_RBL'  #don't use, kept for testing failures!
);
 
   $MAXDNSBLHITS  = 2;  # If Client IP is listed in MORE
                        # DNSBLS than this var, it gets
                        # REJECTed immediately
 
   $MAXDNSBLSCORE = 8;  # alternatively, if the score of
                        # DNSBLs is ABOVE this
                        # level, reject immediately
 
   $MAXDNSBLMSG   = '550 Your MTA is listed in too many DNSBLs';
 
## RHSBL settings
   @rhsbl_score = (
    'multi.surbl.org',             4,        0,        'SURBL',
    'rhsbl.ahbl.org',              4,        0,        'AHBL',
);
 
   $BL_ERROR_SKIP     = 2;  # skip a RBL if this RBL had this many continuous
                            # errors
 
   $BL_SKIP_RELEASE   = 10; # skip a RBL for that many times
 
## cache stuff
   $LOCKPATH          = '/tmp/.policyd-weight/';    # must be a directory (add
                                                    # trailing slash)
 
   $SPATH             = $LOCKPATH.'/polw.sock';     # socket path for the cache
                                                    # daemon. 
 
   $MAXIDLECACHE      = 60; # how many seconds the cache may be idle
                            # before starting maintenance routines
                            # NOTE: standard maintenance jobs happen
                            # regardless of this setting.
 
   $MAINTENANCE_LEVEL = 5;  # after this number of requests do following
                            # maintenance jobs:
                            # checking for config changes
 
# negative (i.e. SPAM) result cache settings ##################################
 
   $CACHESIZE       = 2000; # set to 0 to disable caching for spam results. 
                            # To this level the cache will be cleaned.
 
   $CACHEMAXSIZE    = 4000; # at this number of entries cleanup takes place
 
   $CACHEREJECTMSG  = '550 temporarily blocked because of previous errors';
 
   $NTTL            = 1;    # after NTTL retries the cache entry is deleted
 
   $NTIME           = 30;   # client MUST NOT retry within this seconds in order
                            # to decrease TTL counter
 
 
# positve (i.,e. HAM) result cache settings ###################################
 
   $POSCACHESIZE    = 1000; # set to 0 to disable caching of HAM. To this number
                            # of entries the cache will be cleaned
 
   $POSCACHEMAXSIZE = 2000; # at this number of entries cleanup takes place
 
   $POSCACHEMSG     = 'using cached result';
 
   $PTTL            = 60;   # after PTTL requests the HAM entry must
                            # succeed one time the RBL checks again
 
   $PTIME           = '3h'; # after $PTIME in HAM Cache the client
                            # must pass one time the RBL checks again.
                            # Values must be nonfractal. Accepted
                            # time-units: s, m, h, d
 
   $TEMP_PTIME      = '1d'; # The client must pass this time the RBL
                            # checks in order to be listed as hard-HAM
                            # After this time the client will pass
                            # immediately for PTTL within PTIME
 
 
## DNS settings
   $DNS_RETRIES     = 2;    # Retries for ONE DNS-Lookup
 
   $DNS_RETRY_IVAL  = 2;    # Retry-interval for ONE DNS-Lookup
 
   $MAXDNSERR       = 3;    # max error count for unresponded queries
                            # in a complete policy query
 
   $MAXDNSERRMSG    = 'passed - too many local DNS-errors';
 
   $PUDP            = 0;    # persistent udp connection for DNS queries.
                            # broken in Net::DNS version 0.51. Works with
                            # Net::DNS 0.53; DEFAULT: off
 
   $USE_NET_DNS     = 0;    # Force the usage of Net::DNS for RBL lookups.
                            # Normally policyd-weight tries to use a faster
                            # RBL lookup routine instead of Net::DNS
 
 
   $NS              = '';   # A list of space separated NS IPs
                            # This overrides resolv.conf settings
                            # Example: $NS = '1.2.3.4 1.2.3.5';
                            # DEFAULT: empty
 
 
   $IPC_TIMEOUT     = 2;    # timeout for receiving from cache instance
 
   $TRY_BALANCE     = 0;    # If set to 1 policyd-weight closes connections
                            # to smtpd clients in order to avoid too many
                            # established connections to one policyd-weight
                            # child
 
# scores for checks, WARNING: they may manipulate eachother
# or be factors for other scores.
#                                       HIT score, MISS Score
   @client_ip_eq_helo_score          = (1.5,       -1.25 );
   @helo_score                       = (1.5,       -2    );
   @helo_from_mx_eq_ip_score         = (1.5,       -3.1  );
   @helo_numeric_score               = (2.5,        0    );
   @from_match_regex_verified_helo   = (1,         -2    );
   @from_match_regex_unverified_helo = (1.6,       -1.5  );
   @from_match_regex_failed_helo     = (2.5,        0    );
   @helo_seems_dialup                = (1.5,        0    );
   @failed_helo_seems_dialup         = (2,          0    );
   @helo_ip_in_client_subnet         = (0,         -1.2  );
   @helo_ip_in_cl16_subnet           = (0,         -0.41 );
   @client_seems_dialup_score        = (3.75,       0    );
   @from_multiparted                 = (1.09,       0    );
   @from_anon                        = (1.17,       0    );
   @bogus_mx_score                   = (2.1,        0    );
   @random_sender_score              = (0.25,       0    );
   @rhsbl_penalty_score              = (3.1,        0    );
   @enforce_dyndns_score             = (3,          0    );
 
 
   $VERBOSE = 0;
 
   $ADD_X_HEADER        = 1;    # Switch on or off an additional 
                                # X-policyd-weight: header
                                # DEFAULT: on
 
 
   $DEFAULT_RESPONSE    = 'DUNNO default'; # Fallback response in case
                                           # the weighted check didn't
                                           # return any response (should never
                                           # appear).
 
 
 
#
# Syslogging options for verbose mode and for fatal errors.
# NOTE: comment out the $syslog_socktype line if syslogging does not
# work on your system.
#
 
   $syslog_socktype = 'unix';   # inet, unix, stream, console
 
   $syslog_facility = "mail";
   $syslog_options  = "pid";
   $syslog_priority = "info";
   $syslog_ident    = "postfix/policyd-weight";
 
 
#
# Process Options
#
   $USER            = "polw";      # User must be a username, no UID
 
   $GROUP           = "";          # specify GROUP if necessary
                                   # DEFAULT: empty, will be initialized as 
                                   # $USER
 
   $MAX_PROC        = 50;          # Upper limit if child processes
   $MIN_PROC        = 3;           # keep that minimum processes alive
 
   $TCP_PORT        = 12525;       # The TCP port on which policyd-weight 
                                   # listens for policy requests from postfix
 
   $BIND_ADDRESS    = '127.0.0.1'; # IP-Address on which policyd-weight will
                                   # listen for requests.
                                   # You may only list ONE IP here, if you want
                                   # to listen on all IPs you need to say 'all'
                                   # here. Default is '127.0.0.1'.
                                   # You need to restart policyd-weight if you
                                   # change this.
 
   $SOMAXCONN       = 1024;        # Maximum of client connections 
                                   # policyd-weight accepts
                                   # Default: 1024
 
 
   $CHILDIDLE       = 240;         # how many seconds a child may be idle before
                                   # it dies.
 
   $PIDFILE         = "/var/run/policyd-weight.pid";

Hat man Änderungen an der Konfigurationsdatei vorgenommen, so kann mit mit Hilfe des folgenden Aufrufs einen Syntaxcheck durchführen.

 # perl -c /etc/policyd-weight.conf
 /etc/policyd-weight.conf syntax OK

User polw anlegen

Für den Betrieb benötigen wir noch einen User polw, den wir mit

 # useradd -r -s /sbin/false polw

anlegen.

Startscript anlegen

Zum Starten unseres Daemon legen wir uns noch ein Startscript an.

 # vim /etc/init.d/policyd-weight
/etc/init.d/policyd-weight
#!/bin/sh
#
# chkconfig: 2345 79 31
# description: Postfix Policy Weight Server
#
# processname: policyd-weight
#
 
# Source function library.
. /etc/rc.d/init.d/functions
 
# Source networking configuration.
. /etc/sysconfig/network
 
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
 
prog=policyd-weight
policydweight=/usr/local/bin/$prog
 
# Source an auxiliary options file if we have one, and pick up OPTIONS,                                                                                                     
if [ -r /etc/sysconfig/$prog ]; then
    . /etc/sysconfig/$prog
fi
 
start() {
    echo -n $"Starting $prog: "
    $policydweight start
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
}
stop() {
    echo -n $"Stopping $prog: "
    $policydweight stop
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
}
 
restart() {
    stop
    start
}
 
reload() {
    echo -n $"Reloading $prog: "
    $policydweight reload
    RETVAL=$?
    echo
}
 
# See how we were called.
case "$1" in
    start)
        start
    ;;
    stop)
        stop
    ;;
    restart)
        restart
    ;;
    reload)
        reload
    ;;
    *)
        echo $"Usage: $0 {start|stop|restart|reload|}"
        exit 1
esac
 
exit $RETVAL

Damit unser Script auch ausgeführt werden kann, ändern wir noch die Dateiberechtigungen.

 # chmod +x /etc/init.d/policyd-weight

Postfix Konfiguration

Bei den smtpd_recipient_restrictions tragen wir nun zur Aktivierung folgenden Eintrag nach dem greylisting-Eintrag ein.

 # vim /etc/postfix/main.cf
...   
# Greylisting via postgrey checken via Unix-Socket      (Kapitel 9.2.5 postgrey installieren)
        check_policy_service unix:postgrey/socket,
# Policyd-Weight check over TCP-Connection              (Kapitel 9.3 policyd-weight installieren)
        check_client_access btree:/etc/postfix/policyd_weight_client_whitelist,
        check_policy_service inet:127.0.0.1:12525,
...

Ausnahmeregelungen für Absendeserver

Leider kommt es auch vor, dass vor allem große Provider hinter ihren MX ein cluster betreiben, die mit wechselnden IP-Adrressen aus einem Pool aufschlagen. Damit können unter Umständen Probleme bei der Bewertung entstehen. Diese schalten wir dann bei Bedarf in der policyd_weight_client_whitelist frei.

 # vim /etc/postfix/policyd_weight_client_whitelist
/etc/postfix/policyd_weight_client_whitelist
# Django : 2012-02-03
# Datei zu Definition von Ausnahmeregeln für die gewichtete Überprüfung des Policyd-weight Daemon.
# Policyd-weight ist ein E-Mail-Filter für den Postfix Mail Transfer Agent der von Robert Felber 
# in Perl entwickelt wurde. Policyd-weight untersucht die Mail bei der Einlieferung anhand des 
# Envelope Sender, des Envelope To und der HELO-Daten, die während des SMTP-Handshakes übertragen 
# werden und vergibt für verschiedene Kriterien Punkte. Dabei werden zum Beispiel Realtime Blackhole 
# Listen abgefragt oder die DNS-Konfiguration des Absenders überprüft. Für jeden Regelverstoß gibt 
# es negative Punkte und ab einer bestimmten Wertung wird die Mail abgelehnt.
#
# Nach Änderungen an den policyd-weight Ausnahmeliste policyd_weight_client_whitelist ist die 
# zugehörige Datenbank-Datei mit Hilfe von "postmap /etc/postfix/policyd_weight_client_whitelist"
# zu erstellen und der Daemon von den Änderungen mit einem reload in Kenntnis zu setzen!
# "service policyd-weight reload"
#
# Django : 2008-10-08 
# 1und1.com (big pool, inserted by Django)
kundenserver.de         OK

Nach Änderungen an den policyd-weight Ausnahmeliste policyd_weight_client_whitelist ist die zugehörige Datenbank-Datei mit Hilfe von

 # postmap /etc/postfix/policyd_weight_client_whitelist

zu erstellen und der Daemon von den Änderungen mit einem reload in Kenntnis zu setzen!

 # service policyd-weight reload

erster manueller Start des Daemon

Nun können wir das erste mal den Daemon anstarten.

 # service policyd-weight start

Im Maillog wird der Start des Daemon entsprechend quittiert.

Jun  8 23:18:10 vml000080 postfix/policyd-weight[21188]: policyd-weight 0.1.15 beta-2 started and daemonized. conf:/etc/policyd-weight.conf; GID:493 493 EGID:493 493 UID:496 EUID:496; taint mode: 1
Jun  8 23:18:10 vml000080 postfix/policyd-weight[21188]: cache_query: start: calling spawn_cache()
Jun  8 23:18:10 vml000080 postfix/policyd-weight[21191]: cache spawned

In der Prozessliste finden wir zwei Prozesse, die gestartet wurden:

 # ps auxw | grep policyd-weight
 polw     21191  0.0  0.6 149096 11448 ?        Ss   23:18   0:00 policyd-weight (cache)
 polw     21216  0.0  0.6 149096 11700 ?        Ss   23:23   0:00 policyd-weight (master)

Mittels lsof können wir nun noch überprüfen, welcher Port von policyd-weight verwendet wird.

 # lsof -i :12525
 COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
 perl    21216 polw    4u  IPv4  75589      0t0  TCP localhost:12525 (LISTEN)

automatisches Starten des Dienste beim Systemstart

Damit der postgrey-Daemon automatisch bei jedem Systemstart startet, denn ohne laufenden policyd-weight-daemon verweigert nun unser postfix die Annahme der Nachrichten, kann die Einrichtung des Start-Scripte über folgenden Befehle erreicht werden:

 # chkconfig policyd-weight on

Die Überprüfungung ob die beiden Dienste (Daemons) postfix und postgrey wirklich bei jedem Systemstart automatisch mit gestartet werden, kann durch folgenden Befehle erreicht werden:

 # chkconfig --list | grep post*
 postfix        	0:off	1:off	2:on	3:on	4:on	5:on	6:off
 postgrey       	0:off	1:off	2:on	3:on	4:on	5:on	6:off
 # chkconfig --list | grep poli*
 policyd-weight 	0:off	1:off	2:on	3:on	4:on	5:on	6:off

Wichtig sind jeweils die Schalter on bei den Runleveln - 2 3 4 5.

Bewertungsbeispiele

erfolgreiche negative Bewertung einer Anlieferung

Am nachfolgenden Beispiel sehen wir, den erfolgreichen Mailversand nach der Bewertung durch policyd-weight.

Oct 14 22:31:30 nss postfix/smtpd[14044]: connect from www.linuxtv.org[212.227.166.180]
Oct 14 22:31:34 nss postfix/policyd-weight[2715]: decided action=PREPEND X-policyd-weight: using cached result; rate: -7.6; <client=212.227.166.180> <helo=www.linuxtv.org> <from=vdr-bounces+michl=naush.org@linuxtv.org> <to=grossermeister@naush.org>; delay: 1s 
Oct 14 22:31:34 nss postgrey[21879]: action=pass, reason=triplet found, client_name=www.linuxtv.org, client_address=212.227.166.180, sender=vdr-bounces+michl=naush.org@linuxtv.org, recipient=michl@naush.org 
Oct 14 22:31:34 nss postgrey[21879]: cleaning up old logs... 
Oct 14 22:31:34 nss postfix/smtpd[14044]: 31F5E76022D: client=www.linuxtv.org[212.227.166.180]
Oct 14 22:31:34 nss postfix/cleanup[14048]: 31F5E76022D: message-id=<Pine.LNX.4.64.0810142329220.5339@shogun.pilppa.org>
Oct 14 22:31:34 nss postfix/qmgr[1304]: 31F5E76022D: from=<vdr-bounces+michl=naush.org@linuxtv.org>, size=3523, nrcpt=1 (queue active)
Oct 14 22:31:34 nss postfix/smtpd[14044]: disconnect from www.linuxtv.org[212.227.166.180]

erfolgreiche positive Bewertung einer Anlieferung

In der Regel dauert es nicht all zulange und ein SPAMer wird erfolgreich positiv bewertet, also als SPAM erkannt und die Annahme abgelehnt.

Jun  6 08:46:11 pml010001 postfix/policyd-weight[6560]: weighted check:  IN_DYN_PBL_SPAMHAUS=3.25 NOT_IN_SBL_X
BL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 DSBL_ORG=SKIP(0) BAD_MX=6.825 BOGUS_MX=5.35 CL_IP_NE
_HELO=4.75 RESOLVED_IP_IS_NOT_HELO=1.5 (check from: .nausch. - helo: .computer. - helo-domain: .computer.)  FR
OM_NOT_FAILED_HELO(DOMAIN)=6.25; <client=115.246.189.233> <helo=computer> <from=michael@nausch.org> <to=michae
l@nausch.org>; rate: 23.425 
Jun  6 08:46:11 pml010001 postfix/policyd-weight[6560]: decided action=550 Mail appeared to be SPAM or forged. Ask your Mail/DNS-Administrator to correct HELO and DNS MX settings or to get removed from DNSBLs; MTA helo: computer, MTA hostname: unknown[115.246.189.233] (helo/hostname mismatch); <client=115.246.189.233> <helo=computer> <from=michael@nausch.org> <to=michael@nausch.org>; delay: 0s 
Jun  6 08:46:11 pml010001 postfix/smtpd[31326]: NOQUEUE: reject: RCPT from unknown[115.246.189.233]: 550 5.7.1 <michael@nausch.org>: Recipient address rejected: Mail appeared to be SPAM or forged. Ask your Mail/DNS-Administrator to correct HELO and DNS MX settings or to get removed from DNSBLs; MTA helo: computer, MTA hostname: unknown[115.246.189.233] (helo/hostname mismatch); from=<michael@nausch.org> to=<michael@nausch.org> proto=SMTP helo=<computer>

Fehlerbehandlung

Sollte im maillog folgende Fehlermeldung auftauchen …

Feb 25 07:52:27 vml000080 postfix/policyd-weight[10649]: warning: cache_query: $csock couln't be created: connect: Connection refused, calling spawn_cache()

… dann wird der Mailsever die Annahme der Nachrichten mit einem temporären Fehler ablehnen. 451 4.3.5 Server configuration problem

Dann einfach den Deamon neu starten und sein temporäres Arbeitsverzeichnis vorher löschen.

  1. Daemon stoppen
    # service policyd-weight stop
  2. Arbeitsverzeichnis löschen
    # rm /tmp/.policyd-weight/* -rf
  3. Daemon starten
    # service policyd-weight start

Links