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:
- 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.
- 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.
- Daemon stoppen
# service policyd-weight stop
- Arbeitsverzeichnis löschen
# rm /tmp/.policyd-weight/* -rf
- Daemon starten
# service policyd-weight start