Kampf gegen Spam mit Postfix und Spamassassin

Fighting Spam with Postfix and Spamassassin

Neuigkeiten

News

Ein Jahr Statistik über eingegangenen Spam hat einige Erkenntnisse zutage gefördert.

One year of spam statistics discovered some insight.

Inhalt

Contents

Version: September 2004.

Änderungen gegenüber den Vorversionen (Juli/August 2004):

  • Verbesserungen in der automatisierten Meldung von Spam
  • automatische Eintragung von Mailempfängern in der Whitelist des Absenders
  • Postfix Version 2.1

Revision: September 2004.

Changes since last version (July/August 2004):

  • improvements in automated notifications
  • automatic whitelisting of mail addressees
  • Postfix version 2.1

Einleitung

Preface

Die Flut unerwünschter elektronischer Werbung (Spam, auch UCE = Unsolicited Commercial Email genannt) hat gigantische Ausmaße angenommen. Mein privater Posteingang (einschließlich einiger externer Postfächer) mit Adressen, die schon viele Jahre aktiv sind, war vor einigen Monaten auf mehrere hundert Nachrichten täglich angewachsen, wovon mehr als 95% Spam waren. Aber es gibt Netzbürger, die über tausende Spamnachrichten täglich klagen.

Aber es geht noch schlimmer. Der Ausschnitt aus dem Maillog zeigt die zurückgewiesenen Nachrichten einer einzigen Stunde. Das Netz der Spammer hat meine Domain barnim.net zum Ziel seiner Wörterbuchattacken erkoren. Zwischen 3.000 und 5.000 Sendeversuche an nicht existierende Adressen registriere ich täglich. Und auffällig sind die fast zeitgleichen Anfragen an denselben Postfachnamen von verschiedenen Absendern. Das bedeutet: Hier ist eine Heerschar gekaperter PC in aller Welt mit sogenannten Spambots infiziert, und die Besitzer dieser Zombie-PC wissen nichts davon.

A tremendous amount of unsolicited commercial email (UCE, spam) is flooding mailboxes. Because I use a number of addresses of which some are now active for years, a few months ago my private email entry hat grown to several hundred messages a day. More than 95% of those were spam. But some netizens complain about thousands of daily spam messages.

But even worse: The mail log below shows refused messages from some randomly selected hour. The world wide network of spammers selected my domain barnim.net as a target for brute force dictionary attacks. The server logs between 3,000 and 5,000 attempts per day of sending to nonexisting adresses. You will notice coinciding requests for the same address from different servers. A horde of captured PC throughout the world is infected with so-called spambots and their unsuspecting owners are not even aware.

Jul 12 20:01:40 reject: RCPT from bzq-82-81-192-6.cablep.bezeqint.net[82.81.192.6]: 550 <sdougal@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:02:19 reject: RCPT from smtp07.auna.com[62.81.186.17]: 450 <0002011402@mcimail.com>: Sender address rejected: Domain not found
Jul 12 20:04:06 reject: RCPT from 24.213.57.147.static.up.mi.chartermi.net[24.213.57.147]: 550 <richard.akre@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:04:32 reject: RCPT from unknown[164.125.148.168]: 550 <mkrofchi@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:05:01 reject: RCPT from adsl-68-78-135-65.dsl.emhril.ameritech.net[68.78.135.65]: 550 <mkrofchi@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:05:53 reject: RCPT from c-24-30-101-39.we.client2.attbi.com[24.30.101.39]: 550 <rajagopalan_v@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:08:44 reject: RCPT from rwcrmxc20.comcast.net[204.127.198.46]: 554 <notify@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:09:19 reject: RCPT from cdm-66-76-192-127.mthm.cox-internet.com[66.76.192.127]: 550 <rrood@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:12:14 reject: RCPT from cliente-217216140195.uBRala01.supercable.es[217.216.140.195]: 550 <simonwilkinson@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:14:26 reject: RCPT from 24.229.166.105.res-cmts.mtp.ptd.net[24.229.166.105]: 550 <richard.bomber.lancaster@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:27:28 reject: RCPT from c-65-34-173-182.se.client2.attbi.com[65.34.173.182]: 550 <raines@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:40:54 reject: RCPT from 12-217-230-73.client.mchsi.com[12.217.230.73]: 550 <rainbowkevin@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:41:04 reject: RCPT from pool-151-202-111-175.ny325.east.verizon.net[151.202.111.175]: 550 <ntziorkas@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:41:32 reject: RCPT from 6.140.171.66.subscriber.vzavenue.net[66.171.140.6]: 550 <rainbowkevin@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:42:13 reject: RCPT from chello213047097076.5.12.vie.surfer.at[213.47.97.76]: 550 <ntziorkas@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:42:24 reject: RCPT from user-0ce2h7g.cable.mindspring.com[24.225.68.240]: 550 <ntziorkas@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:42:39 reject: RCPT from c-67-173-175-195.client.comcast.net[67.173.175.195]: 550 <ntziorkas@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:43:15 reject: RCPT from unknown[211.211.249.193]: 550 <nw-b5request@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:44:11 reject: RCPT from dsl-082-083-068-054.arcor-ip.net[82.83.68.54]: 550 <nw-b5request@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:45:39 reject: RCPT from 68-189-112-8.ca.charter.com[68.189.112.8]: 550 <nw-b5request@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:45:46 reject: RCPT from pcp660796pcs.prshng01.fl.comcast.net[68.35.245.48]: 550 <nw-b5request@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:45:55 reject: RCPT from c-24-14-225-53.client.comcast.net[24.14.225.53]: 550 <oerbeck@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:46:08 reject: RCPT from unknown[218.51.48.248]: 550 <oerbeck@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:46:48 reject: RCPT from wbar10.dal1-4-13-088-201.dsl-verizon.net[4.13.88.201]: 550 <oerbeck@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:47:48 reject: RCPT from 12-220-223-24.client.insightBB.com[12.220.223.24]: 550 <ollieweb@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:48:02 reject: RCPT from pcp08760834pcs.mtlrel01.nj.comcast.net[68.36.30.253]: 550 <ollieweb@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:48:13 reject: RCPT from S010600055d80a442.wp.shawcable.net[24.79.194.112]: 550 <ollieweb@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:49:35 reject: RCPT from unknown[218.22.149.229]: 550 <ollieweb@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:50:08 reject: RCPT from c-67-162-175-186.client.comcast.net[67.162.175.186]: 550 <popeyes@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:50:54 reject: RCPT from 212.199.144.202.forward.012.net.il[212.199.144.202]: 550 <popeyes@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:50:58 reject: RCPT from cpe-66-189-103-225.ma.charter.com[66.189.103.225]: 550 <ordersusa@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:51:06 reject: RCPT from d198-166-219-27.abhsia.telus.net[198.166.219.27]: 550 <popeyes@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:51:08 reject: RCPT from modemcable123.106-203-24.mc.videotron.ca[24.203.106.123]: 550 <ordersusa@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:51:29 reject: RCPT from adsl-69-104-81-136.dsl.pltn13.pacbell.net[69.104.81.136]: 550 <popeyes@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:51:37 reject: RCPT from adsl-67-37-184-18.dsl.chcgil.ameritech.net[67.37.184.18]: 550 <ordersusa@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:51:38 reject: RCPT from user-0c6sdlc.cable.mindspring.com[24.110.54.172]: 550 <popeyes@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:53:11 reject: RCPT from unknown[67.176.126.139]: 550 <orsi_vale@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:53:14 reject: RCPT from c-67-172-12-5.client.comcast.net[67.172.12.5]: 550 <pphp@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:53:29 reject: RCPT from 82-35-8-42.cable.ubr02.hari.blueyonder.co.uk[82.35.8.42]: 554 <82-35-8-42.cable.ubr02.hari.blueyonder.co.uk>: Helo command rejected: Access denied
Jul 12 20:53:47 reject: RCPT from CPE-69-76-180-63.kc.rr.com[69.76.180.63]: 550 <sports4life8@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:53:49 reject: RCPT from ool-18bea0de.dyn.optonline.net[24.190.160.222]: 550 <orsi_vale@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:53:49 reject: RCPT from 12-220-223-24.client.insightBB.com[12.220.223.24]: 550 <pphp@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:54:24 reject: RCPT from adsl-68-72-126-25.dsl.chcgil.ameritech.net[68.72.126.25]: 550 <pphp@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:55:26 reject: RCPT from h004005a622fb.ne.client2.attbi.com[66.30.203.54]: 550 <outslay@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:55:54 reject: RCPT from c-66-229-148-167.we.client2.attbi.com[66.229.148.167]: 550 <outslay@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:56:07 reject: RCPT from c-24-126-159-19.we.client2.attbi.com[24.126.159.19]: 550 <outslay@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:56:18 reject: RCPT from unknown[61.105.79.105]: 550 <outslay@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:56:22 reject: RCPT from adsl-69-105-54-215.dsl.irvnca.pacbell.net[69.105.54.215]: 550 <prettyboy3@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:56:51 reject: RCPT from adsl-68-92-87-113.dsl.stlsmo.swbell.net[68.92.87.113]: 550 <outslay@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:57:36 reject: RCPT from dsl-aur5-b0b.dial.inet.fi[80.221.145.11]: 550 <smmaclean@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:57:48 reject: RCPT from va-spotsy-cuda2-c4b-179.frbgva.adelphia.net[68.70.169.179]: 550 <prettyboy3@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:57:50 reject: RCPT from lns-th2-7-82-64-99-134.adsl.proxad.net[82.64.99.134]: 550 <p0_ga@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:58:31 reject: RCPT from modemcable123.106-203-24.mc.videotron.ca[24.203.106.123]: 550 <p0_ga@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:59:26 reject: RCPT from M1927P029.adsl.highway.telekom.at[80.121.112.221]: 550 <prettyboy3@barnim.net>: Recipient address rejected: Access denied
Jul 12 20:59:59 reject: RCPT from unknown[212.179.163.65]: 504 <OEMCOMPUTER>: Helo command rejected: need fully-qualified hostname

Anforderungen

Requirements

Meine Anforderungen an ein Mailsystem sind:

  1. Volle Kontrolle über Schwarze und Weiße Listen, individuell einstellbar für jedes Familienmitglied.
  2. Basisschutz vor Viren und Würmern bereits auf dem Server.
  3. Einsammeln von Nachrichten aus externen Postfächern, für jedes Familienmitglied extra.
  4. Weniger als 1% unerkanntes Spam.
  5. Schutz vor Verlust fälschlich als Spam identifizierter Nachrichten (sogenannte "false positives").
  6. Senden und Empfangen von Emails sowohl über verschlüsselte als auch unverschlüsselte Verbindungen möglich.
  7. Offen für die Bereitstellung von Maildienst für familienfremde Personen (d.h. mit eingeschränkter Vertrauensstellung).
  8. Sicher gegen Mißbrauch als offenes Mailrelay.
  9. Ausschließlich Verwendung freier Software bzw. Open Source auf dem Server.

Die Lösung, die ich vorstelle, basiert auf dem Betrieb eines eigenen >Mailservers. Im Artikel über das Einrichten eines Dedicated Servers sind die benötigten Konfigurationsdateien im Detail aufgelistet. Der angehende Postmaster kann die Anleitung verwenden, um auf dieser Basis seine eigenen Ideen zu verwirklichen.

These are my basic requirements on a mail server:

  1. Full and individual control of black and white lists for each member of my family.
  2. Basic protection from virii and worms on the server.
  3. Configurable and individual collecting of mails from external mail boxes.
  4. Not more than 1% unrecognised spam.
  5. Prevent unrecognised losing of false positives.
  6. Sending and receiving of mails via encrypted as well as unencrypted connections.
  7. Extendable to serve also persons with restricted level of trust.
  8. Non-relaying mail server.
  9. Exclusive use of free or open source software.

The solution presented here is based on an own dedicated >mail server. Configuration files necessary to set up the server are listed there. The novice postmaster can use this guide and implement her own ideas.

Voraussetzungen

Prerequisites

Softwareinstallation

Software Installation

Der Mailserver wird auf einem Linux-System installiert, ich empfehle >Debian. Eine Grundeinrichtung des Servers ist im >Rootserver-Artikel beschrieben. Der Mailserver benötigt folgende Pakete, die mit dselect installiert werden:

  • postfix und postfix-tls
  • clamav, clamav-base, clamav-daemon, clamav-freshclam
  • spamassassin mit allen vorgeschlagenen Abhängigkeiten und razor
  • postgrey
  • fetchmail
  • courier-pop mit allen vorgeschlagenen Abhängigkeiten und courier-pop-ssl sowie courier-ssl
  • sasl2-bin mit allen vorgeschlagenen Abhängigkeiten
  • libsasl2-modules
  • vrfy

In Ergänzung zu den sehr komfortablen Funktionen von Postfix werden wir einige Shellscripte einführen, die eine Spambehandlung näher an den individuellen Bedürfnissen jedes einzelnen Nutzers erlauben. Durch seinen modularen Aufbau ermöglicht Postfix hervorragend solche Ergänzungen.

Einen Vergleich zwischen Postfix und Qmail kann ich nicht ziehen. Ich habe mich an Postfix gewöhnt und bin mit seinen Möglichkeiten zufrieden. Die Sicherheit scheint gut zu sein. Einige Denial-of-Service Attacken sind in 2003 bekannt geworden; inzwischen liegen neue Freigaben der Software vor. Ich gehe hier von Version 2.1 aus.

Spamassassin liefert sehr gute Ergebnisse und wird ständig weiterentwickelt.

Clam Antivirus ist ein freier serverbasierter Virenscanner. Die Software ist noch sehr jung, und die Aktualisierung der Virensignaturen hält (noch) nicht Schritt mit dem raschen Generationswechsel heutiger Viren und Würmer. C't Ausgabe 8/2004 liefert hierzu eine Auswertung. Clam AV kann derzeit nur als Ergänzung einer clientbasierten Lösung gesehen werden, sofern Sie nicht das Geld für einen kommerziellen serverbasierten Scanner ausgeben wollen.

The mail server will be installed on a Linux system, I recommend >Debian. The base installation is described in my >rootserver article. Following packages should be installed with dselect:

  • postfix and postfix-tls
  • clamav, clamav-base, clamav-daemon, clamav-freshclam
  • spamassassin with all dependencies, plus razor
  • postgrey
  • fetchmail
  • courier-pop with all dependencies, courier-pop-ssl and courier-ssl
  • sasl2-bin with all dependencies
  • libsasl2-modules
  • vrfy

We will add a number of own shell scripts that complement Postfix and allow to process spam very close to the needs of every user. The modular design of Postfix facilitates such extensions rather well.

I will not compare Postfix with Qmail or other products. I got used to Postfix and am quite satisfied with the provided functionality. Security seems to be good. A few DoS attacks were published in 2003 but new software releases are now available. This description requires Postfix release 2.1 or newer.

Spamassassin provides very good results and is continuously improved.

Clam Antivirus is a free server based virus scanner. This software is quite fresh and not always reliable, since virus signatures are not updated that frequently as those of commercial products. Refer to C't issue 8/2004 for a detailed evaluation. Thus, Clam AV in its current state should be complemented by a client based solution.

Schlüsselgenerierung

Key Generation

Bevor es mit dem Mailserver losgeht, brauchen Sie einige Schlüssel für die Sicherheit von Postfix und Courier.

Die SSL-/TLS-Schlüssel zur Absicherung des Email-Verkehrs liegen je nach Linux-Distribution unter /etc/ssl oder unter /usr/share/ssl. Wir gehen hier vom ersten Fall aus; andernfalls sind einige der nachfolgenden Konfigurationsdateien sinngemäß anzupassen. Auf eine detaillierte Erläuterung der einzelnen Kommandos verzichte ich hier. Unter http://httpd.apache.org/docs-2.0/ssl/ssl_faq.html gibt es eine gute Erläuterung. Einige Anmerkungen stehen als Kommentar oder echo-Anweisung im folgenden Script.

>make_sslkeys

Dieses Script erzeugt eine Certification Authority (CA) und Zertifikate für Webserver, SMTP-Server und POP3-Server. Außerdem werden die .p12- und .crt-Schlüssel zum Export in Outlook/Internet Explorer bzw. Netscape/Mozilla bereitgestellt. Für den Import von Zertifikaten in Outlook/Internet Explorer siehe >diese Beschreibung.

Die Bedienung von openssl ist alles andere als erfreulich. Das Script basiert in einigen Teilen auf sign.sh (Copyright 1998-1999 Ralf S. Engelschall) aus mod_ssl.

Damit keine Abfrage einer Paßphrase beim Start des Mailservers kommt, wird sie aus dem privaten Serverschlüssel gelöscht. Der ungeschützte Schlüssel muß dann auf jeden Fall unlesbar für alle außer Root sein.

Die Schlüssel gut sichern. Das Script verpackt die erzeugten Schlüssel in sslkeys.tar und gibt am Schluß noch einen Hinweis, auf welchem Server welche Schlüssel vorhanden sein sollten. Zertifikate (*.crt und *.p12) gehören immer in das Unterverzeichnis ./certs, private Schlüssel (*.key und *.pem) in ./private. Die nicht auf dem Server benötigten privaten Schlüssel sollten von dort gelöscht werden.

Der Fingerprint, von dem im Script die Rede ist, wird weiter unten noch einmal eine Rolle spielen, sofern Sie auch einen "internen" Mailserver betreiben wollen.

Before we start with the mailserver itself we need several keys to encrypt the mail traffic.

SSL/TLS keys to protect the mail traffic are stored in /etc/ssl or/usr/share/ssl, depending on the Linux version. We assume the first case, otherwise some of the configurationsfiles to follow have to be adapted appropriately. If a detailed explanation of the commands is required, please refer to the documentation under http://httpd.apache.org/docs-2.0/ssl/ssl_faq.html.

>make_sslkeys

This script generates a Certification Authority (CA), together with several certificates for web, SMTP and POP3 servers. Besides that, .p12 and .crt keys for exporting to Outlook/Internet Explorer and Netscape/Mozilla are provided. Refer to >this document to see how to import the certificates to the respective mail programs.

It is not really straightforward to use openssl. The script shown here has been derived from sign.sh (Copyright 1998-1999 by Ralf S. Engelschall) from mod_ssl.

To avoid a passphrase request when the mailserver is started, we delete it from the private server key. This unprotected key has to be unreadable for everyone except root.

Do not forget to store the keys safely. The script packs all keys in an archive sslkeys.tar and prints a notification which keys have to be installed on which server. Certificates (*.crt und *.p12) are always stored in the subdirectory ./certs, private keys (*.key und *.pem) in ./private. All unused keys should be deleted from the server

If you also want to set up an "internal" mail server, the fingerprint mentioned in the script will be needed later.

SMTP-Mailserver mit Postfix

SMTP Mailserver with Postfix

Postfix mit TLS und SASL

Postfix with TLS and SASL

In diesem Beispiel ist von den Postfächern

  • myself@mydomain.net
  • otheruser@mydomain.net
  • hosteduser@sub.mydomain.net

die Rede. Die ersten beiden gehören zu Nutzern, die einen Linux-Account besitzen. Die dritte gehört zu einem Nutzer, der einen sogenannten virtuellen Account hat: ein Postfach unter /home/hosted/hosteduser/Maildir.

Das folgende Initialisierungsscript legt die Nutzereinträge für diese drei Nutzer bei Postfix und Courier an und startet Postfix neu. Nach Änderungen an Konfigurationsdateien sollte dieses Script immer aufgerufen werden.

In this example we use the mailboxes

  • myself@mydomain.net
  • otheruser@mydomain.net
  • hosteduser@sub.mydomain.net

The first and second belong to users who have their own Linux avcount. The third belongs to a user with no Linux user name and a so-called virtual account. This is a mailbox under /home/hosted/hosteduser/Maildir.

The following script adds these three users to Postfix and Courier and restarts the mail server. You should always call this script after changing any configuration file.

$ vi /etc/postfix/config.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/config.sh

cd /etc/postfix || exit 1
PATH=/etc/postfix:/usr/local/bin:/usr/sbin:$PATH; export PATH
SASLDB=/etc/sasldb2
SASL_REALM=mydomain.net

function remap
{
        postmap $1
        chmod 644 $1 $1.db
}

remap map_transport
remap map_sender_access_fakelocal
remap map_relay_clientcerts
remap map_smtpd_sender_login_maps
remap map_virtual_uid_gid
remap map_virtual_mailbox
remap map_virtual
remap map_recipient_access
remap map_alias
remap map_helo_access
remap map_sender_access_fromaddress

rm $SASLDB

function sasldbentry
{
        echo $2 | saslpasswd2 -c -a smtpd -u $SASL_REALM $1
}

sasldbentry myself                         pAssWoRd
sasldbentry otheruser                      SeSaM
sasldbentry hosteduser@sub.mydomain.net    PaRoLe

chmod 644 $SASLDB

cat >/usr/lib/sasl2/smtpd.conf <<-EOF
        mech_list: login cram-md5 digest-md5
        pwcheck_method: auxprop
        log_level: 3
EOF
chmod 644 /usr/lib/sasl2/smtpd.conf
cp /usr/lib/sasl2/smtpd.conf /etc/postfix/sasl/smtpd.conf

postfix reload

USERDB=/etc/courier/userdb
rm -rf $USERDB
mkdir $USERDB

function userdbentry
{
        userdb -f $USERDB/$1 $2 set uid=$3 gid=$4 home=$5 mail=$6
        echo $7 | userdbpw -md5 | userdb -f $USERDB/$1 $2 set pop3pw
}

userdbentry mydomain.net myself 500 500 /home/myself /home/myself/Maildir pAssWoRd
userdbentry mydomain.net otheruser 501 500 /home/otheruser /home/otheruser/Maildir SeSaM
userdbentry sub.mydomain.net hosteduser@sub.mydomain.net 601 600 /home/hosted/hosteduser /home/hosted/hosteduser/Maildir PaRoLe

chmod 700 $USERDB

makeuserdb

exit 0

In main.cf wird die Grundkonfiguration des Mailservers festgelegt.

The base configuration of the mail server is defined by main.cf.

$ vi /etc/postfix/main.cf
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/main.cf

command_directory = /usr/sbin
daemon_directory  = /usr/lib/postfix
program_directory = /usr/lib/postfix

smtpd_banner = $myhostname ESMTP $mail_name
setgid_group = postdrop
biff         = no

append_dot_mydomain = no
myorigin            = mydomain.net
mydomain            = mydomain.net
myhostname          = mail.mydomain.net
mydestination       = mydomain.net,
                      sub.mydomain.net,
                      otherdomain.net,
                      localhost
mynetworks          = 275.184.307.3, 127.0.0.0/8
recipient_delimiter = +

virtual_mailbox_base = /

Die Konfiguationsdateien map_... werden weiter unten erläutert.

The configuration files map_... will be explained later.

relay_clientcerts    = hash:/etc/postfix/map_relay_clientcerts
transport_maps       = hash:/etc/postfix/map_transport
alias_maps           = hash:/etc/postfix/map_alias
alias_database       = hash:/etc/postfix/map_alias
virtual_mailbox_maps = hash:/etc/postfix/map_virtual_mailbox
virtual_uid_maps     = hash:/etc/postfix/map_virtual_uid_gid
virtual_gid_maps     = hash:/etc/postfix/map_virtual_uid_gid
virtual_maps         = hash:/etc/postfix/map_virtual

local_recipient_maps =
relay_domains        = $mydestination
home_mailbox         = Maildir/

mailbox_size_limit    = 150000000
message_size_limit    = 134217728
virtual_mailbox_limit = 134217728
bounce_size_limit     = 50000
header_size_limit     = 102400

unknown_local_recipient_reject_code = 450

smtpd_sasl_auth_enable      = yes
smtpd_sasl_application_name = smtpd
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain     = mydomain.net
broken_sasl_auth_clients    = yes

smtpd_use_tls                   = yes
smtpd_tls_ask_ccert             = yes
smtpd_tls_key_file              = /etc/ssl/private/mail.mydomain.net.key
smtpd_tls_cert_file             = /etc/ssl/certs/mail.mydomain.net.crt
smtpd_tls_CAfile                = /etc/ssl/certs/ca.crt
smtpd_tls_CApath                = /etc/ssl/certs
smtpd_tls_loglevel              = 1
smtpd_tls_received_header       = no
smtpd_tls_session_cache_timeout = 3600s
tls_random_source               = dev:/dev/urandom

Manche schwören auf "Teergruben" (engl. Tarpits), um das Internet von Spam zu befreien. Die Idee ist so einfach wie wirkungslos. Durch verzögerte Reaktion des empfangenden Servers bei einer unerwünschten Nachricht (z.B. einer Nachricht, deren Empfänger nicht bekannt ist - siehe die Logdatei oben) soll der Absender ausgebremst werden. Doch das nützt nichts mehr in Zeiten, da Tausende und Abertausende Zombie-PC als absendende Mailserver fungieren.

Die beste Maßnahme ist, nach dem ersten Fehler in der Kommunikation den Empfang sofort abzubrechen und die Wahrheit kundzutun: Empfänger nicht bekannt. Das vergeudet bei uns am wenigsten Ressourcen. Andere Methoden wie SPF werden zum beim Kampf gegen Spam mehr beitragen.

Tarpits ("Teergruben" in German) are believed to clean the internet from spam and they are hence very popular. The idea is as simple as useless: The mail sender shall be slowed down by delayed responses of a receiving server that recognises an unwanted message. This could be a message for a unknown mailbox name, see above. But who cares in times of myriads of zombie PC if some of them become unable to deliver their messages?

The best measure as often in life is to quit immediately and tell the truth: receiver unknown. This is a good way to save our own resources. There are better methods to fight spam, like SPF.

reject_code = 550


smtpd_hard_error_limit   = 1

Mit einem HELO nimmt der Absender die Verbindung zum Empfänger auf. Schon was in der HELO-Meldung steht, kann vollkommen erlogen sein. Wer keinen gültigen Namen angibt (das sollte ein vollständiger Domainname sein), darf gar nicht weitersprechen. Es lohnt allerdings nicht, auf die Existenz dieses Domainnamens im DNS zu prüfen, da viele (auch professionelle) Absender ihre Mailserver nachlässig konfigurieren und ihrem Mailserver keinen registrierten Sub-Domainnamen zuweisen.

Wer sich bereits vor dem HELO durch ein SASL-Login ausgewiesen hat oder seine Nachricht über einen registrierten vertrauenswürdigen Mailserver abliefert, wird ohne weitere Prüfung vorgelassen.

Saying HELO, the sender contacts the receivng mail server. Even the contents of the HELO line may be faked, but we can do a simple check. Who does provide a valid name (which in fact has to be a domain name), is not allowed to proceed. But I do not recomment to check this domain name for existence since many (also professional) configure their mail servers carelessly, not assigning a valid subdomain to their server.

Who already identified by a SASL login ("permit_sasl_authenticated") or who delivered his message to a registered trusted mail server ("permit_tls_clientcerts") may pass without further checks..

smtpd_helo_required     = yes
smtpd_helo_restrictions = permit_tls_clientcerts
                          permit_sasl_authenticated
                          permit_mynetworks
                          reject_invalid_hostname
                          reject_non_fqdn_hostname
                          reject_unauth_pipelining
                          check_helo_access hash:/etc/postfix/map_helo_access

Als nächstes schickt der Absender eine MAIL FROM-Nachricht. Hier wird strenger geprüft: Der dort enthaltene Domainname muß existieren.

Außerdem läßt sich hier schon feststellen, wenn jemand sich fälschlich für einen lokalen Nutzer ausgibt. Wer als Absendeadresse "myself@mydomain.net" angibt, sollte bereits von einer der ersten drei Bedingungen der smtpd_sender_restrictions angenommen worden sein, denn nur authentifizierte Benutzer dürfen unter solch einem Namen senden. Wer als "irgendwer@mydomain.net" bis zum ersten check_sender_access vordringt, kann seine Absendeadresse nur gefälscht haben.

The next piece of the mail to be delivered is a MAIL FROM line. Here control is more stringent: the doamin name givene there has to exist.

We check also if a sender falsely claims to be a local user. Users with a sending address like "myself@mydomain.net" should already have been accepted by one of the first three conditions of smtpd_sender_restrictions since only authenticated users are allowed to use such a name.

# sender restrictions (MAIL FROM)
# 1. permit_mynetworks, permit_tls_clientcerts, permit_sasl_authenticated
#    Permit everyone who is certainly welcome.
#    Note: permit_mynetworks is necessary for fetchmail (contacting smtpd from localhost);
#    fetchmail will not notice our reject (code 450), hence not flush the mail and fetch it again the next run.
#    Everyone who tries to send from one of our domain names should not pass beyond this step 1.
# 2. check_sender_access hash:/etc/postfix/map_sender_access_fakelocal
#    Check for our own domain names. Reject every faked MAIL FROM.
# 3. reject_unknown_sender_domain, reject_non_fqdn_sender, reject_sender_login_mismatch
#    Common UCE criteria.
# 4. check_sender_access hash:/etc/postfix/map_sender_access_fromaddress
#    To lock out anyone else whom we do not like.
# No step beyond permit_sasl_authenticated+permit_tls_clientcerts with faked local addresses,
# all local smtpd users have to be TLS or SASL authenticated.
smtpd_sender_restrictions = permit_mynetworks
                            permit_tls_clientcerts
                            permit_sasl_authenticated
                            reject_unknown_sender_domain
                            reject_non_fqdn_sender
                            reject_unauth_pipelining
                            check_sender_access hash:/etc/postfix/map_sender_access_fakelocal
                            reject_sender_login_mismatch
                            check_sender_access hash:/etc/postfix/map_sender_access_fromaddress
smtpd_sender_login_maps   = hash:/etc/postfix/map_smtpd_sender_login_maps

Als nächstes schickt der Absender eine RCPT TO-Nachricht. Die Prüfungen hier dürften keine Überraschungen mehr bieten. Durch reject_unauth_destination ist der Mailserver nicht als Relay verwendbar, wenn der Absender sich nicht über SASL angemeldet hat.

Greylisting

Greylisting ist die gegenwärtig wirkungsvollste Methode Spam einzudämmen, das durch Netze von Spambots erzeugt wird. Jeder einzelne Absender von Spam führt sich auf wie ein Mailserver, ist aber kein vollständiger Mailserver. Ein echter Mailserver kann mit Übermittlungsfehlern umgehen und wenn nötig die Übertragung wiederholen. Spambots sind aber klein und einfach gehalten. Der Mehraufwand, eine Fehlerbehandlung zu implementieren, lohnt sich für Spammer erst, wenn die Technik des Greylisting sich stark verbreitet hat.

Das Prinzip des Greylisting besteht darin, eine eingehende Mail beim ersten Sendeversuch mit einer fingierten Fehlermeldung (z.B. "Empfangsserver momentan nicht verfügbar") zurückzuweisen, sich den Vorgang aber zu merken. Erst beim zweiten Sendeversuch wird die Nachricht angenommen. Da "guter" Mailverkehr in der Regel über korrekt implementierte Mailserver abgewickelt wird, ist die einzige Auswirkung auf diesen, daß die Nachrichten jeweils etwa fünf Minuten verzögert werden.

Der unter localhost:60000 laufende Policyservice verwendet den Daemon postgrey, der durch Debian automatisch auf diesem lokalen Port installiert wird. Das ist ein Unterschied zum SPF-Service, der nur als Perl-Script zur Verfügung steht und deshalb noch einen zusätzlichen Eintrag in master.cf benötigt.

 

# recipient restrictions (RCPT TO)
# 1. permit_tls_clientcerts, permit_sasl_authenticated
#    Permit everything for senders who are authenticated.
# 2. reject_unknown_recipient_domain, reject_non_fqdn_recipient
#    Recipient domain checks.
# 3. reject_unauth_destination, reject_unauth_pipelining
# 4. check_recipient_access hash:/etc/postfix/map_recipient_access_grey, check_policy_service unix:private/spf
#    SPF test only for destinations that are also in the positive list.
#    Mail to unknown mailboxes is blocked here.
# 5. check_recipient_access hash:/etc/postfix/map_recipient_access_white, reject
#    Process mails on after check of positive list
smtpd_recipient_restrictions = permit_tls_clientcerts
                               permit_sasl_authenticated
                               reject_unknown_recipient_domain
                               reject_non_fqdn_recipient
                               reject_unauth_destination
                               reject_unauth_pipelining
                               check_recipient_access hash:/etc/postfix/map_recipient_access_grey
							   check_policy_service inet:127.0.0.1:60000
                               check_policy_service unix:private/spf
                               check_recipient_access hash:/etc/postfix/map_recipient_access_white
                               reject

Vertrauenswürdige Partner-Mailserver weisen sich durch ein TLS-Zertifikat aus. Die Fingerprints aller akzeptierten Zertifikate stehen in map_relay_clientcerts.

 

$ vi /etc/postfix/map_relay_clientcerts
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_relay_clientcerts
# fingerprints of trusted mailservers that are allowed to inject mails

4A:F6:1G:7B:2D:A2:FD:0B:80:D6:3A:5D:57:24:F8:EE mail.intra.mydomain.net

In map_transport wird für jeden Domainnamen festgelegt, ob er für lokale oder virtuelle Nutzer steht. Im DNS sollte allerdings nicht nur mydomain.net einen MX-Eintrag haben, sondern auch sub.mydomain.net.

 

$ vi /etc/postfix/map_transport
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_transport
# all domains served by this mailserver

mydomain.net       local:
otherdomain.net    local:
sub.mydomain.net   virtual:

Die Postfächer und die Linux User-/Group-IDs der Nutzer.

 

$ vi /etc/postfix/map_virtual_mailbox
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_virtual_mailbox

myself@mydomain.net            /home/myself/Maildir/
otheruser@mydomain.net         /home/otheruser/Maildir/
hosteduser@sub.mydomain.net    /home/hosted/hosteduser/Maildir/
$ vi /etc/postfix/map_virtual_uid_gid
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_virtual_uid_gid

myself@mydomain.net            500:500
otheruser@mydomain.net         501:500
hosteduser@sub.mydomain.net    601:600

Schließlich noch alle Alias-Namen.

 

$ vi /etc/postfix/map_virtual
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_virtual
# all registered receiver aliases

# MYDOMAIN.NET ###########################################################
# family
myalias@mydomain.net           myself@mydomain.net
# special
www@mydomain.net               myself@mydomain.net
info@mydomain.net              myself@mydomain.net
support@mydomain.net           myself@mydomain.net
postmaster@mydomain.net        myself@mydomain.net
abuse@mydomain.net             myself@mydomain.net
security@mydomain.net          myself@mydomain.net
hostmaster@mydomain.net        myself@mydomain.net
webmaster@mydomain.net         myself@mydomain.net

# OTHERDOMAIN.NET ###########################################################
# special
www@otherdomain.net            myself@mydomain.net
info@otherdomain.net           myself@mydomain.net
support@otherdomain.net        myself@mydomain.net
postmaster@otherdomain.net     myself@mydomain.net
abuse@otherdomain.net          myself@mydomain.net
security@otherdomain.net       myself@mydomain.net
hostmaster@otherdomain.net     myself@mydomain.net
webmaster@otherdomain.net      myself@mydomain.net
$ vi /etc/postfix/map_alias
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_alias

MAILER-DAEMON   root
root            myself

Es folgen einige Regeln zum Ablehnen oder Annehmen bestimmter Absende- und Empfangsadressen.

 

$ vi /etc/postfix/map_helo_access
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_helo_access
# mailservers who may not even say HELO to us

reallybadguys.com    REJECT
.reallybadguys.com   REJECT
$ vi /etc/postfix/map_sender_access_fromaddress
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_sender_access_fromaddress
# MAIL FROM addresses to be rejected without additional checks

reallybadguys.com    REJECT
$ vi /etc/postfix/map_recipient_access_grey
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_recipient_access_grey
# local mail addresses to be accepted or rejected by postfix

notify@mydomain.net          REJECT

# following entries according to map_virtual_mailbox:
myself@mydomain.net          DUNNO
myself@otherdomain.net       DUNNO
otheruser@mydomain.net       DUNNO
hosteduser@sub.mydomain.net  DUNNO

# following entries according to map_virtual:
myalias@mydomain.net         DUNNO
www@mydomain.net             DUNNO
info@mydomain.net            DUNNO
support@mydomain.net         DUNNO
postmaster@mydomain.net      DUNNO
abuse@mydomain.net           DUNNO
security@mydomain.net        DUNNO
hostmaster@mydomain.net      DUNNO
webmaster@mydomain.net       DUNNO
www@otherdomain.net          DUNNO
info@otherdomain.net         DUNNO
support@otherdomain.net      DUNNO
postmaster@otherdomain.net   DUNNO
abuse@otherdomain.net        DUNNO
security@otherdomain.net     DUNNO
hostmaster@otherdomain.net   DUNNO
webmaster@otherdomain.net    DUNNO

# reject for all other (unknown) mailboxes
mydomain.net                 REJECT
otherdomain.net              REJECT
sub.mydomain.net             REJECT
$ vi /etc/postfix/map_recipient_access_white
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_recipient_access_white
# local mail addresses to be accepted or rejected by postfix

# following entries according to map_virtual_mailbox:
myself@mydomain.net          OK
myself@otherdomain.net       OK
otheruser@mydomain.net       OK
hosteduser@sub.mydomain.net  OK

# following entries according to map_virtual:
myalias@mydomain.net         OK
www@mydomain.net             OK
info@mydomain.net            OK
support@mydomain.net         OK
postmaster@mydomain.net      OK
abuse@mydomain.net           OK
security@mydomain.net        OK
hostmaster@mydomain.net      OK
webmaster@mydomain.net       OK
www@otherdomain.net          OK
info@otherdomain.net         OK
support@otherdomain.net      OK
postmaster@otherdomain.net   OK
abuse@otherdomain.net        OK
security@otherdomain.net     OK
hostmaster@otherdomain.net   OK
webmaster@otherdomain.net    OK
$ vi /etc/postfix/map_sender_access_fakelocal
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_sender_access_fakelocal
# REJECT for all MAIL FROM addresses that claim to come from here but actually do not

mydomain.net      REJECT
otherdomain.net   REJECT
sub.mydomain.net  REJECT
$ vi /etc/postfix/map_smtpd_sender_login_maps
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_smtpd_sender_login_maps
# allowed MAIL FROM addresses with their respective SASL login names

myself@mydomain.net            myself
myself@otherdomain.net         myself
otheruser@mydomain.net         otheruser
hosteduser@sub.mydomain.net    hosteduser@sub.mydomain.net

Steuerung über Namenslisten

Flow Control by Name Lists

Wie Postfix und die verschiedenen nachfolgenden Scripte die Nachricht von einem bestimmten Absender bzw. für einen bestimmten Empfänger behandeln, wird über eine Reihe von Namenslisten gesteuert. Die auf .lst endenden Listen enthalten einfache Namen, die auf .rxp endenden Listen enthalten vorformatierte Reguläre Ausdrücke.

 

$ vi /etc/postfix/authenticated.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/authenticated.lst
# users that are SASL or TLS authenticated if sending mails

myself@mydomain.net
myself@otherdomain.net
otheruser@mydomain.net
hosteduser@sub.mydomain.net
$ vi /etc/postfix/authorized.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/authorized.lst
# users that are authorized to inject mail to postfix

# authenticated addresses:
myself@mydomain.net
myself@otherdomain.net
otheruser@mydomain.net
hosteduser@sub.mydomain.net
# addresses for server-generated messages:
MAILER-DAEMON@mydomain.net
MAILER-DAEMON@otherdomain.net
# mimicry addresses:
myself@mycompany.com
# no need to include here:
#postmaster@mydomain.net
#notify@mydomain.net
$ vi /etc/postfix/mailboxes_family.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/mailboxes_family.lst
# mailboxes of privileged (family) users

myself@mydomain.net
otheruser@mydomain.net
$ vi /etc/postfix/mailboxes_hosted.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/mailboxes_hosted.lst
# mailboxes of hosted users

hosteduser@sub.mydomain.net
$ vi /etc/postfix/sender_domains.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/sender_domains.lst
# allowed sender domains

mydomain.net
otherdomain.net
mycompany.com
$ vi /etc/postfix/spamc_individual.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/spamc_individual.lst
# users who do their individual spam filtering with own scoring schema

myself@mydomain.net
otheruser@mydomain.net
myalias@mydomain.net
www@mydomain.net
info@mydomain.net
support@mydomain.net
postmaster@mydomain.net
abuse@mydomain.net
security@mydomain.net
hostmaster@mydomain.net
webmaster@mydomain.net
myself@otherdomain.net
www@otherdomain.net
info@otherdomain.net
support@otherdomain.net
postmaster@otherdomain.net
abuse@otherdomain.net
security@otherdomain.net
hostmaster@otherdomain.net
webmaster@otherdomain.net

# not: hosteduser@sub.mydomain.net
$ vi /etc/postfix/usernames_family.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/usernames_family.lst
# receiver names that correspond to a family mailbox

myself@mydomain.net
myself@otherdomain.net
otheruser@mydomain.net
myalias@mydomain.net
www@mydomain.net
info@mydomain.net
support@mydomain.net
postmaster@mydomain.net
abuse@mydomain.net
security@mydomain.net
hostmaster@mydomain.net
webmaster@mydomain.net
myself@otherdomain.net
www@otherdomain.net
info@otherdomain.net
support@otherdomain.net
postmaster@otherdomain.net
abuse@otherdomain.net
security@otherdomain.net
hostmaster@otherdomain.net
webmaster@otherdomain.net
$ vi /etc/postfix/usernames_hosted.lst
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/usernames_hosted.lst

hosteduser@sub.mydomain.net
$ vi /etc/postfix/own_domains.rxp
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/own_domains.rxp

^mydomain\.net$
^otherdomain\.net$
^sub\.mydomain\.net$
^.*\.mydomain\.net$
^.*\.otherdomain\.net$
^.*\.sub\.mydomain\.net$
^275\.184\.307\.3$

Viren- und Spamfilter

Virus and Spam Filter

Jede ausgehende Email wird auf Viren geprüft und gegebenenfalls gar nicht erst hinausgelassen. EIngehende Mails werden einer SPF-Prüfung und einem Virentest unterzogen.

Postfix prüft nach dem SPF-Verfahren ("Sender Permitted From" oder auch "Sender Policy Framework") durch den Aufruf eines Policy-Scripts, ob der Absender eine zulässige Absendeadresse angegeben hat. Das soll insbesondere vor gefälschten Mails von Freemail-Accounts schützen. Es funktioniert aber nur, wenn der Eigentümer des absendenden Mailservers einen entsprechenden Eintrag im DNS hinterlegt hat.

Das ist leider selten der Fall. Wir geben ein gutes Beispiel und legen einen SPF-Eintrag für mydomain.net an.

 

$ vi /etc/postfix/master.cf
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/master.cf

# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       n       -       -       smtpd -o content_filter=spamfilter:dummy
#628      inet  n       -       -       -       -       qmqpd
pickup    fifo  n       -       -       60      1       pickup
cleanup   unix  n       -       -       -       0       cleanup
qmgr      fifo  n       -       -       300     1       qmgr
#qmgr     fifo  n       -       -       300     1       nqmgr
rewrite   unix  -       -       -       -       -       trivial-rewrite
bounce    unix  -       -       -       -       0       bounce
defer     unix  -       -       -       -       0       bounce
flush     unix  n       -       -       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
smtp      unix  -       -       -       -       -       smtp
relay     unix  -       -       -       -       -       smtp
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq     unix  n       -       -       -       -       showq
error     unix  -       -       -       -       -       error
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       n       -       -       lmtp

# interfaces to non-Postfix software
spf       unix  -       n       n       -       -       spawn
  user=nobody argv=/usr/bin/perl /etc/postfix/spf-policy.pl
spamfilter unix -       n       n       -       -       pipe
  flags=Rq user=spam argv=/etc/postfix/spamfilter.sh postfix none ${sender} ${recipient}

# only used by postfix-tls
#tlsmgr   fifo  -       -       n       300     1       tlsmgr
#smtps    inet  n       -       n       -       -       smtpd
#  -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
#587      inet  n       -       n       -       -       smtpd
#  -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes
trace     unix  -       -       -       -       0       bounce
verify    unix  -       -       -       -       1       verify

Das Kernstück allen weiteren Filterns ist das Shell-Script spamfilter.sh. Die Einstellungen in master.cf bewirken, daß spamfilter.sh die Nachricht zu sehen bekommt, nachdem der SMTP-Daemon die oben genannten ersten Prüfungen abgeschlossen hat.

Auf besondere Effizienz habe ich bei diesem Script verzichtet. Es fallen große Mengen an temporären Dateien an. Für einen Server, der täglich Tausende Nachrichten verdauen muß, ist dieses Script also nicht geeignet.

 

$ vi /etc/postfix/spamfilter.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/spamfilter.sh
# parameter 1: modus (fetchmail, postfix)
# parameter 2: fetchmail box or "none"
# parameter 3: sender (MAIL FROM)
# parameter 4...n: recipients (RCPT TO)

#########################################################################################################
# flags
#########################################################################################################
check_virus=yes
log_spamstatus=no
debug=no
logheaders=yes
logverbose=yes

#########################################################################################################
# set up environment
#########################################################################################################
mydomain=mydomain.net
my_received_id="mail.$mydomain (Postfix)"
postmaster=myself@$mydomain
SPAMUSER=spam
cd /etc/postfix || { cat | sendmail -i -f $postmaster -- $postmaster; exit 0; }
PATH=/etc/postfix:/usr/local/bin:/usr/sbin:$PATH; export PATH
MAILHEADERS=/var/log/mailheaders
null=/dev/null
subjectlength=28
OPMODE="$1"
FETCHBOX="$2"
MAIL_FROM="$3"
shift; shift; shift
RCPT_TO="$@"
RCPT_COUNT=`echo "$RCPT_TO" | wc -w | tr -d ' '`
logname="spamfilter.sh/$OPMODE"
auth_magic=`auth_magic.sh $mydomain`

Zuerst werden die Kopfzeilen der Nachricht extrahiert. Ihre Auswertung gibt schon einige Information über die Herkunft der Email. Wir können insbesondere feststellen, welche Nachricht nicht direkt an diesen Mailserver geschickt wurde, sondern von einem externen Postfach eingesammelt wurde. An einigen Stellen müssen zum Schutz der Privatsphäre solche Nachrichten anders behandelt werden.

In authorized.lst befindet sich eine Liste aller erlaubten Absendenamen. Wenn die hier geprüfte Mail von einem dieser Absender stammt, handelt es sich mit Sicherheit um eine ausgehende Mail, denn eingehende Mails mit derart gefälschten Absendenamen hätte bereits der SMTP-Daemon aussortiert.

 

#########################################################################################################
# detect status of mail
#########################################################################################################
TMP_original=`mktemp -p /tmp` || { cat | sendmail -i -f "$MAIL_FROM" -- $postmaster; exit 0; }
cat >$TMP_original
TMP_oldheaders=`mktemp -p /tmp` || { cat $TMP_original | sendmail -i -f "$MAIL_FROM" -- $postmaster; exit 0; }
mawk -f extract_headers.awk <$TMP_original >$TMP_oldheaders
TMP_mail_plus=`mktemp -p /tmp` || { cat $TMP_original | sendmail -i -f "$MAIL_FROM" -- $postmaster; exit 0; }

LOGTEXT="mail_from=<$MAIL_FROM>"
from_authenticated=no
from_authorized=no
from_family=no
if [ $OPMODE = postfix ]
then
        # authenticated: everyone authenticated by SASL or coming from trusted mailserver
        # (e.g. internal mailserver, TLS fingerprint authenticated)
        if match_plain.sh "$MAIL_FROM" authenticated.lst >$null 2>$null
        then
                from_authenticated=yes
                LOGTEXT="$LOGTEXT authenticated"
        fi
        # authorized: superset of authenticated, comprising additionally:
        # - automatically server-generated mails, e.g. from MAILER-DAEMON@*
        # - registered mimicry addresses, e.g. faked corporate mail addresses
        if match_plain.sh "$MAIL_FROM" authorized.lst >$null 2>$null
        then
                from_authorized=yes
                [ $from_authenticated = no ] && LOGTEXT="$LOGTEXT authorized"
        fi
        # family: privileged users whose mails have to be automatically certified with Habeas stamp
        if match_plain.sh "$MAIL_FROM" usernames_family.lst >$null 2>$null
        then
                from_family=yes
                LOGTEXT="$LOGTEXT family"
        fi
fi
LOGTEXT="$LOGTEXT rcpt_to=<$RCPT_TO>"

Einige Informationen, die der SMTP-Daemon leider nicht den Kopfzeilen hinzufügen kann, fügen wir hier selbst ein: Wer die Nachricht geschickt hat, wie er sich mit HELO gemeldet hat und so weiter. Diese Information wird später in weiteren Scripten nochmals benötigt, z.B. nondelivery.sh.

 

#########################################################################################################
# identify basic SMTP envelope parameters
#########################################################################################################
HELO=""
CLIENT=""
if [ $OPMODE = postfix ]
then
        heloline=`grep 'Received: from .* by '"$my_received_id"' with' $TMP_oldheaders`
        if [ X"$heloline" != X ]
        then
                # 1=helo 2=client_ip 3=time
                # Received: from $HELO ($CLIENT_REV [$CLIENT]) by $my_received_id with $PROTOCOL id $IDENT
                #   for <$DESTINATION>; $TIME
                helopattern='^Received: from \([^ ]*\) ([^ ]* [[]\([^]]*\)]) by '"$my_received_id"' [^;]*; \(.*\)$'
                HELO=`echo $heloline |   sed -e 's/'"$helopattern"'/\1/'`
                CLIENT=`echo $heloline | sed -e 's/'"$helopattern"'/\2/'`
                TIME=`echo $heloline |   sed -e 's/'"$helopattern"'/\3/'`
                LOGTEXT="$LOGTEXT client=<$CLIENT> helo=<$HELO> time=<$TIME>"
                new_helopattern='^\(Received: from [^ ]* ([^ ]* [[][^]]*]) by '"$my_received_id"
                new_helopattern="$new_helopattern"' with [^ ]* id [^ ]*\)\( for <\([^>]*\)>\)\{0,1\}[^;]*;\(.*\)$'
                new_heloline=`echo "$heloline" | \
                        sed -e 's/'"$new_helopattern"'/\1 for <\3> count '"$RCPT_COUNT"' from <'"$MAIL_FROM"'>; \4/'`
        fi
fi

#########################################################################################################
# filter out more header information and rewrite headers
#########################################################################################################
subject=`grep -i '^Subject:' $TMP_oldheaders | sed -e 's/^[^:]*: //;s/</{/g;s/>/}/g' | tr -d '\n' | tr '[:cntrl:]' '?'`
[ `expr length "$subject"` -gt $subjectlength ] && subject=`expr substr "$subject" 1 $subjectlength`"..."
ident=`grep -i '^Message-ID:' $TMP_oldheaders | sed -e 's/^[^:]*: //;s/[><]//g' | tr -d '\n' | tr '[:cntrl:]' '?'`
mailsize=`wc -lc <$TMP_original | sed -e 's/ *\([^ ]*\) *\([^ ]*\)/\1 lines, \2 bytes/'`
LOGTEXT="$LOGTEXT subject=<$subject> ident=<$ident> size=<$mailsize>"
if [ X"$HELO" = X ]
then
        date=`grep -i '^Date:' $TMP_oldheaders | sed -e 's/^[^:]*: //'`
        LOGTEXT="$LOGTEXT date=<$date>"
fi

if [ X"$new_heloline" = X ]
then
        cat <$TMP_oldheaders >$TMP_mail_plus
else
        sed -ne ':a;/'"$helopattern"'/s/^.*$/'"$new_heloline"'/;tx;/^$/bx;p;n;ba;:x;p;n;bx' \
                <$TMP_oldheaders >$TMP_mail_plus
fi
[ $from_authenticated = yes ] && echo "X-SASL-AUTHENTICATED: $MAIL_FROM by $auth_magic" >>$TMP_mail_plus
[ $OPMODE = fetchmail ] && echo "X-Fetchmail-Retrieved: $FETCHBOX" >>$TMP_mail_plus
sed -ne '/^$/,$p' <$TMP_original >>$TMP_mail_plus

Zum Virentest benutzen wir Clam Antivirus, einen Open Source Virenscanner. Die Virensignaturen, die Clam über das Internet zur Verfügung stellt, sind nicht besonders aktuell. Auf einen zweiten Virenscanner im Client PC sollte man nicht verzichten.

Clam kann mit komplett MIME-codierten Emails umgehen. Das nochmalige Entpacken und einzelne Prüfen der Anlagen scheint überflüssig zu sein. Ich konnte bislang keinen Fall feststellen, in dem beide Prüfungen verschiedene Ergebnisse gebracht hätten.

 

#########################################################################################################
# run spam and virus tests and write log summary
#########################################################################################################
infected=no
if [ $check_virus = yes ]
then
        virusinfo=`clamscan --verbose --disable-summary $TMP_original 2>&1 1>$null`
        if [ $? = 1 ]
        then
                infected=yes
                LOGTEXT="$LOGTEXT virus=<`echo "$virusinfo" | sed -e 's/^[^:]*: //;s/ FOUND$//'`>"
        fi
fi

if [ $log_spamstatus = yes -a $infected != yes -a $from_authenticated = no ]
then
        spaminfo=`spamc -c -u $SPAMUSER <$TMP_original`
        [ $? = 1 ] && LOGTEXT="$LOGTEXT spam=<$spaminfo>"
fi

logger -p mail.info -t $logname "$LOGTEXT"
if [ $logheaders = yes ]
then
        cat $TMP_oldheaders >>$MAILHEADERS
        echo " -----------------------------------------------------------------" >>$MAILHEADERS
fi

Ausgehende Mails werden gesendet, sofern sie nicht dem Virenscanner aufgefallen sind.

Zu sendende Mails werden mit Kopfzeilen versehen, die die Virenprüfung bezeugen. Mails von privilegierten Familienmitgliedern werden außerdem mit urheberrechtlich geschützen Kopfzeilen von Habeas versehen, die einen heiligen Eid schwören, daß diese Mail kein Spam sei.

Überflüssige Kopfzeilen werden entfernt, insbesondere solche, die Aufschluß über unsere interne Netzstruktur geben könnten.

Damit das Script add_blackwhitelist.sh die Empfängeradresse zur Weißen Liste des Absenders hinzufügen kann, muß es mit den Privilegien des Absenders oder des Superusers (wie hier) ausgeführt werden. Sudo muß konfiguriert werden wie im Kommentar erläutert.

 

#########################################################################################################
# process message per recipient
#########################################################################################################
while [ X"$1" != X ]
do
        TO="$1"
        #################################################################################################
        # outbound
        #################################################################################################
        if [ $from_authorized = yes ]
        then
                # this branch is not taken for fetched mails
                if [ $infected = yes ]
                then
                        # never deliver infected mails
                        nondelivery.sh authorized null virus "$virusattach" \
                                "$MAIL_FROM" "$TO" "$RCPT_COUNT" "$HELO" \
                                "$CLIENT" "$TIME" <$TMP_mail_plus
                elif [ $from_family = yes ]
                then
                        # mails from family users are certified with Habeas stamp
                        mawk -f suppress_internals.awk <$TMP_mail_plus | \
                        mawk -f plus_habeas.awk | \
                        mawk -f plus_viruscheck.awk | \
                        sendmail -i -f "$MAIL_FROM" -- "$TO"
                        # current user is "spam", su to root in order to change user configuration files
                        sudo /etc/postfix/add_blackwhitelist.sh white "$MAIL_FROM" "$TO"
                else
                        # from hosted user
                        mawk -f suppress_internals.awk <$TMP_mail_plus | \
                        mawk -f plus_viruscheck.awk | \
                        sendmail -i -f "$MAIL_FROM" -- "$TO"
                        sudo /etc/postfix/add_blackwhitelist.sh white "$MAIL_FROM" "$TO"
                fi

Eingehende Mails werden verteilt. Kann die Mail nicht zugestellt werden, weil sie verseucht ist, oder ist sie als Spam aufgefallen, so wird nondelivery.sh aufgerufen.

 

        #################################################################################################
        # inbound
        #################################################################################################
        else
                to_family=no
                match_plain.sh "$TO" usernames_family.lst >$null 2>$null && to_family=yes
                to_hosted=no
                match_plain.sh "$TO" usernames_hosted.lst >$null 2>$null && to_hosted=yes
                if [ $to_family = $to_hosted ]
                then
                        logger -p mail.info -t $logname -- \
                                "alert: <$TO> to_family=<$to_family> to_hosted=<$to_hosted>"
                        to_family=no
                        to_hosted=yes
                fi
                [ $to_family = yes ] && nondelivery_dest=family || nondelivery_dest=hosted
                if [ $infected = yes ]
                then
                        # never deliver infected mails
                        nondelivery.sh null $nondelivery_dest virus "$virusattach" \
                                "$MAIL_FROM" "$TO" "$RCPT_COUNT" "$HELO" \
                                "$CLIENT" "$TIME" <$TMP_mail_plus
                elif match_plain.sh "$TO" spamc_individual.lst >$null 2>$null
                then
                        # no spam check here, will be done per-user, initiated by .forward
                        mawk -f plus_viruscheck.awk <$TMP_mail_plus | \
                        sendmail -i -f "$MAIL_FROM" -- "$TO"
                else
                        if TMP_spamchecked=`mktemp -p /tmp`
                        then
                                # spamc to set spam header tags only,
                                # procmail or the user client can filter by X-Spam-Status tags
                                mawk -f plus_viruscheck.awk <$TMP_mail_plus | \
                                spamc -f -u $SPAMUSER >$TMP_spamchecked
                                sendmail -i -f "$MAIL_FROM" -- "$TO" <$TMP_spamchecked
                                if grep -i '^X-Spam-Flag: YES' $TMP_spamchecked >$null 2>$null
                                then
                                        nondelivery.sh null $nondelivery_dest spam "" \
                                                "$MAIL_FROM" "$TO" "$RCPT_COUNT" "$HELO" \
                                                "$CLIENT" "$TIME" <$TMP_spamchecked
                                fi
                                rm $TMP_spamchecked
                        else
                                sendmail -i -f "$MAIL_FROM" -- $TO <$TMP_mail_plus
                        fi
                fi
        fi
        shift
done
rm -f $TMP_mail_plus $TMP_oldheaders $TMP_original

exit 0

SPF

SPF

Hier ein Perl-Programm, das den SPF-Test für eine Mailadresse durchführt.

 

$ vi /etc/postfix/spf-policy.pl
#!/usr/bin/perl

# mengwong@pobox.com
# Wed Dec 10 03:52:04 EST 2003
# postfix-policyd-spf
# version 1.06
# see http://spf.pobox.com/

use Fcntl;
use Sys::Syslog qw(:DEFAULT setlogsock);
use strict;

# ----------------------------------------------------------
#                      configuration
# ----------------------------------------------------------

# to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://spf.pobox.com/downloads.html

  my @HANDLERS;
  push @HANDLERS, "testing";
  push @HANDLERS, "sender_permitted_from"; use Mail::SPF::Query;

my $VERBOSE = 0;

my $DEFAULT_RESPONSE = "DUNNO";

#
# Syslogging options for verbose mode and for fatal errors.
# NOTE: comment out the $syslog_socktype line if syslogging does not
# work on your system.
#

my $syslog_socktype = 'unix'; # inet, unix, stream, console
my $syslog_facility = "mail";
my $syslog_options  = "pid";
my $syslog_priority = "info";
my $syslog_ident    = "postfix/policy-spf";

# ----------------------------------------------------------
#                  minimal documentation
# ----------------------------------------------------------

#
# Usage: smtpd-policy.pl [-v]
#
# Demo delegated Postfix SMTPD policy server.
# This server implements SPF.
# Another server implements greylisting.
# Postfix has a pluggable policy server architecture.
# You can call one or both from Postfix.
#
# The SPF handler uses Mail::SPF::Query to do the heavy lifting.
#
# This documentation assumes you have read Postfix's README_FILES/SMTPD_POLICY_README
#
# Logging is sent to syslogd.
#
# How it works: each time a Postfix SMTP server process is started
# it connects to the policy service socket, and Postfix runs one
# instance of this PERL script.  By default, a Postfix SMTP server
# process terminates after 100 seconds of idle time, or after serving
# 100 clients. Thus, the cost of starting this PERL script is smoothed
# out over time.
#
# To run this from /etc/postfix/master.cf:
#
#    policy  unix  -       n       n       -       -       spawn
#      user=nobody argv=/usr/bin/perl /usr/libexec/postfix/smtpd-policy.pl
#
# To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
#
#    smtpd_recipient_restrictions =
#       ...
#       reject_unknown_sender_domain
#       reject_unauth_destination
#       check_policy_service unix:private/policy
#       ...
#
# NOTE: specify check_policy_service AFTER reject_unauth_destination
# or else your system can become an open relay.
#
# To test this script by hand, execute:
#
#    % perl smtpd-policy.pl
#
# Each query is a bunch of attributes. Order does not matter, and
# the demo script uses only a few of all the attributes shown below:
#
#    request=smtpd_access_policy
#    protocol_state=RCPT
#    protocol_name=SMTP
#    helo_name=some.domain.tld
#    queue_id=8045F2AB23
#    sender=foo@bar.tld
#    recipient=bar@foo.tld
#    client_address=1.2.3.4
#    client_name=another.domain.tld
#    [empty line]
#
# The policy server script will answer in the same style, with an
# attribute list followed by a empty line:
#
#    action=dunno
#    [empty line]
#

# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: client_address=208.210.125.227
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: client_name=newbabe.mengwong.com
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: helo_name=newbabe.mengwong.com
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: protocol_name=ESMTP
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: protocol_state=RCPT
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: queue_id=
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: recipient=mengwong@dumbo.pobox.com
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: request=smtpd_access_policy
# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: sender=mengwong@newbabe.mengwong.com

# ----------------------------------------------------------
#                      initialization
# ----------------------------------------------------------

#
# Log an error and abort.
#
sub fatal_exit {
  syslog(err  => "fatal_exit: @_");
  syslog(warning => "fatal_exit: @_");
  syslog(info => "fatal_exit: @_");
  die "fatal: @_";
}

#
# Unbuffer standard output.
#
select((select(STDOUT), $| = 1)[0]);

#
# This process runs as a daemon, so it can't log to a terminal. Use
# syslog so that people can actually see our messages.
#
setlogsock $syslog_socktype;
openlog $syslog_ident, $syslog_options, $syslog_facility;

# ----------------------------------------------------------
#                           main
# ----------------------------------------------------------

#
# Receive a bunch of attributes, evaluate the policy, send the result.
#
my %attr;
while (<STDIN>) {
  chomp;
  if (/=/)       { my ($k, $v) = split (/=/, $_, 2); $attr{$k} = $v; next }
  elsif (length) { syslog(warning=>sprintf("warning: ignoring garbage: %.100s", $_)); next; }

  if ($VERBOSE) {
    for (sort keys %attr) {
      syslog(debug=> "Attribute: %s=%s", $_, $attr{$_});
    }
  }

  fatal_exit ("unrecognized request type: '$attr{request}'") unless $attr{request} eq "smtpd_access_policy";

  my $action = $DEFAULT_RESPONSE;
  my %responses;
  foreach my $handler (@HANDLERS) {
    no strict 'refs';
    my $response = $handler->(attr=>\%attr);
    syslog(debug=> "handler %s: %s", $handler, $response);
    if ($response and $response !~ /^dunno/i) {
      syslog(info=> "handler %s: %s is decisive.", $handler, $response);
      $action = $response; last;
    }
  }

  syslog(info=> "decided action=%s", $action);

  print STDOUT "action=$action\n\n";
  %attr = ();
}

# ----------------------------------------------------------
#                     plugin: SPF
# ----------------------------------------------------------
sub sender_permitted_from {
  local %_ = @_;
  my %attr = %{ $_{attr} };

  my $query = eval { new Mail::SPF::Query (ip    =>$attr{client_address},
                                           sender=>$attr{sender},
                                           helo  =>$attr{helo_name}) };
  if ($@) {
    syslog(info=>"%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s",
           $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@);
    return "DUNNO";
  }
  my ($result, $smtp_comment, $header_comment) = $query->result();

  syslog(info=>"%s: SPF %s: smtp_comment=%s, header_comment=%s",
         $attr{queue_id}, $result, $smtp_comment, $header_comment);

  if    ($result eq "pass")  { return "DUNNO"; }
  elsif ($result eq "fail")  { return "REJECT " . ($smtp_comment || $header_comment); }
  elsif ($result eq "error") { return "450 temporary failure: $smtp_comment"; }
  else                       { return "DUNNO"; }
  # unknown, softfail, neutral and none all return DUNNO

  # TODO XXX: prepend Received-SPF header.  Wietse says he will add that functionality soon.
}

# ----------------------------------------------------------
#                     plugin: testing
# ----------------------------------------------------------
sub testing {
  local %_ = @_;
  my %attr = %{ $_{attr} };

  if (lc address_stripped($attr{sender}) eq
      lc address_stripped($attr{recipient})
      and
      $attr{recipient} =~ /policyblock/) {

    syslog(info=>"%s: testing: will block as requested",
           $attr{queue_id});
    return "REJECT smtpd-policy blocking $attr{recipient}";
  }
  else {
    syslog(info=>"%s: testing: stripped sender=%s, stripped rcpt=%s",
           $attr{queue_id},
           address_stripped($attr{sender}),
           address_stripped($attr{recipient}),
           );

  }
  return "DUNNO";
}

sub address_stripped {
  # my $foo = localpart_lhs('foo+bar@baz.com'); # returns 'foo@baz.com'
  my $string = shift;
  for ($string) {
    s/[+-].*\@/\@/;
  }
  return $string;
}

Für den eigenen Mailserver tragen wir im DNS folgenden Wert ein: (Siehe bei SPF für weitere Informationen.)

 

mydomain.net.  3600  IN  TXT  "v=spf1 a mx ptr a:otherdomain.net a:mycompany.com -all

Bearbeitung von Kopfzeilen

Processing of Mail Headers

Das Filtern und Umgestalten der Kopfzeilen erfolgt mit awk. Es folgen die benötigten Steuerdateien.

 

$ vi /etc/postfix/extract_headers.awk
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/extract_headers.awk

BEGIN { RS="\n\n+"; }
/.*/ { gsub("\n\t"," "); print; exit 0; }
END { exit 0; }
$ vi /etc/postfix/plus_habeas.awk
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/plus_habeas.awk

/^$/ {
"/etc/postfix/auth_magic.sh mydomain.net" | getline signature
"date -R" | getline sendtime;
print "X-Server-Signature:", signature, ";", sendtime;
print "X-Habeas-SWE-1: winter into spring";
print "X-Habeas-SWE-2: brightly anticipated";
print "X-Habeas-SWE-3: like Habeas SWE (tm)";
print "X-Habeas-SWE-4: Copyright 2002 Habeas (tm)";
print "X-Habeas-SWE-5: Sender Warranted Email (SWE) (tm). The sender of this";
print "X-Habeas-SWE-6: email in exchange for a license for this Habeas";
print "X-Habeas-SWE-7: warrant mark warrants that this is a Habeas Compliant";
print "X-Habeas-SWE-8: Message (HCM) and not spam. Please report use of this";
print "X-Habeas-SWE-9: mark in spam to .";
print; exit 0; }
/.*/ { print; }
END { while (getline==1) print; }
$ vi /etc/postfix/plus_viruscheck.awk
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/plus_viruscheck.awk

/^$/ {
"/usr/sbin/clamd -V" | getline clamversion;
"date -R" | getline scantime;
"hostname" | getline scanhost;
print "X-Virus-Scanned: by", scanhost, "with", clamversion, ";", scantime;
print "X-Virus-Status: No";
print; exit 0; }
/.*/ { print; }
END { while (getline==1) print; }
$ vi /etc/postfix/suppress_internals.awk
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/suppress_internals.awk

BEGIN { skipreceived=0; }
/^$/ { print; exit 0; }
/^X-Mailer: / { next; }
/^X-Fetchmail-Retrieved: / { next; }
/^Delivered-To: / { next; }
/^X-SASL-AUTHENTICATED: / { next; }
/^X-Spam-Report: / { next; }
/^X-Spam-Status: / { next; }
/^Received: from mail.intra.mydomain.net/ { skipreceived=1; next; }
/^Received: / { if (skipreceived==0) print; next; }
/^      / { if (skipreceived==0) print; next; }
/^ / { if (skipreceived==0) print; next; }
/.*/ { skipreceived=0; print; }
END { while (getline==1) print; }

Die globale Konfiguration von Spamassassin erfolgt mit folgender Datei.

 

$ vi /etc/spamassassin/local.cf
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/spamassassin/local.cf

report_safe            0
rewrite_subject        0
fold_headers           0
use_terse_report       0
spam_level_stars       1
check_mx_attempts      3
check_mx_delay         5
allow_user_rules       0
auto_whitelist_factor  0
use_bayes              1
bayes_auto_learn       0
required_hits          5.0

add_header spam Flag _YESNOCAPS_
add_header all Status _YESNO_, hits=_HITS_ required=_REQD_ tests=_TESTSSCORES(,)_ bayes=_BAYES_ relaysuntrusted=_RELAYSUNTRUSTED_ autolearn=_AUTOLEARN_ scanned=[_DATE_] version=_VERSION_ postmaster=_CONTACTADDRESS_

add_header all Level _STARS(*)_
add_header all Checker-Version SpamAssassin _VERSION_ (_SUBVERSION_) on _HOSTNAME_

header TO_undisclosed To =~ /undisclosed/i
describe TO_undisclosed To: undisclosed

header SASL_AUTHENTICATED X-SASL-AUTHENTICATED =~ /$auth_fingerprint/i
describe SASL_AUTHENTICATED X-SASL-AUTHENTICATED: $auth_fingerprint

body Postmaster_Magic /$auth_fingerprint/i
describe Postmaster_Magic Postmaster_Magic ($auth_fingerprint)

score Postmaster_Magic     -100
score HABEAS_SWE           -100
score HABEAS_VIOLATOR        16

Mailkonfiguration der Nutzer

User specific Mail Configuration

Jeder Nutzer kann externe Postfächer zum Absammeln der dort auflaufenden Email angeben. Die Nachrichten werden ebenfalls mit spamfilter.sh geprüft.

 
$ vi /home/myself/.fetchmailrc
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/home/myself/.fetchmailrc

poll pop.gmx.net protocol pop3 timeout 30
username "myself@gmx.net" password "pAssWoRd"
is "myself@mydomain.net" here smtpname "myself@mydomain.net"
mda "/etc/postfix/spamfilter.sh fetchmail myself@gmx.net %F %T"
antispam 450
flush nokeep
$ crontab -u myself -e
*/15    *    *    *    *    /usr/local/bin/fetchmail

Jeder Nutzer kann seine Mail nochmals nach eigenem Gutdünken bearbeiten und filtern.

 

$ vi /home/myself/.forward
"| /usr/bin/procmail -t"
$ vi /home/myself/.procmailrc
#
# (c) Thomas Bez  2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/home/myself/.procmailrc

:0:
* ^X-SASL-AUTHENTICATED
Maildir/

:0fw
* < 256000
| /usr/local/bin/spamc

:0:
* ^X-Spam-Flag: YES
{
  :0 c
  | /etc/postfix/logspam.sh spam

  :0:
  * ^X-Spam-Status: .*USER_IN_BLACKLIST=
  | cat >/dev/null

  :0:
  | /etc/postfix/nondelivery.sh null family spam
}

:0:
* ^X-Spam-Flag: YES
| cat >/dev/null

:0:
Maildir/

Die Nutzer dürfen aus Sicherheitsgründen keine zusätzlichen Regeln für Spamassassin aufstellen, sondern nur vordefinierten Regeln neue Bewertungen zuordnen oder Absendeadressen zu Schwarzen und Weißen Listen zuordnen.

 

$ vi /home/myself/.spamassassin/user_prefs
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/home/myself/.spamassassin/user_prefs

version_tag     myself
whitelist_from  *@goodguys.org
blacklist_from  *@badguys.com

Wer seine Mails nicht nochmals behandeln will, legt sie gleich im Postfach ab.

 

$ vi /home/hosted/hosteduser/.procmailrc
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/home/hosted/hosteduser/.procmailrc

:0:
Maildir/

Rückmeldungen an Absender und Internet Provider

Notifying Senders and Internet Providers

Rückmeldungen an wirkliche Spam-Absender sind natürlich vollkommen überflüssig. Was aber, wenn der Spammer gar kein Spammer war? Sondern unser Bekannter bei seiner Wortwahl unvorsichtig war und dem Bayes-Filter von Spamassassin auffällig geworden ist?

Durch nondelivery.sh wird die Nachricht nach verschiedenen Kriterien bewertet. Das Grundprinzip ist:

  1. Wenn die Nachricht einen niedrigen "Spamgrad" hat und die Emailadresse des Absenders erreichbar ist, erhält der Absender einen Hinweis, daß seine Nachricht (wahrscheinlich) den Empfänger nicht erreicht.
  2. Wenn die Nachricht einen so hohen "Spamgrad" hat, daß wir mit größter Wahrscheinlichkeit von tatsächlichem Spam ausgehen können, versuchen wir an den Internetprovider des Absenders (bzw. dem Besitzer des letzten Mailrelays in der Kette) eine Beschwerde zu schicken.
  3. Bei allen Nachrichten zwischendrin, bei denen wir uns nicht ganz sicher sind, tun wir nichts.
 
$ vi /etc/postfix/nondelivery.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.myself.net:/etc/postfix/nondelivery.sh
# parameter 1: from where (authorized or null)
# parameter 2: where to (family, hosted or null)
# (shift; shift)
# parameter 1: reason "spam", "spf", "unknown" or "virus"
# parameter 2: additional virus info (if reason=virus or reason=spf)
# parameter 3: $MAIL_FROM or empty
# parameter 4: $TO or empty
# parameter 5: $RCPT_COUNT or empty
# parameter 6: $HELO or empty
# parameter 7: $CLIENT or empty
# parameter 8: $TIME or empty

################################################################################################################
# initialize
################################################################################################################
cd /etc/postfix || exit 0
PATH=/etc/postfix:/usr/local/bin:/usr/sbin:$PATH; export PATH

mydomain=mydomain.net
my_received_id="mail.$mydomain (Postfix)"
notifier=notify@$mydomain
postmaster=postmaster@$mydomain
logname=nondelivery.sh
checkpoints=""
auth_magic=`auth_magic.sh $mydomain`
subjectlength=28

# either notify: if the message was classified spam but there is a chance that this is a false positive or
#   if the message contains malicious code but was possibly not sent with malicious intent
# or reportabuse: if there is no doubt that this was real spam or a malicious attack
# or none: if we are not absolutely sure or
#   if notification or abuse reporting is not possible for some reason
notify=yes
reportabuse=yes
check_whois=no
abuse_manual=no
abuse_guess=no
infopostmaster=no

Die hier festgelegten Grenzwerte sind Erfahrungswerte. Es empfiehlt sich, die Standard-Scores von Spamassassin nicht zu sehr zu verbiegen, da sonst dieser Bewertungsalgorithmus irregeleitet werden könnte. Und es wäre peinlich, wenn wir uns bei einem ISP über Spam beschwerten, welches gar keines ist.

 

# fine-tunable conditions
maxrcptcount=2
# maxnotifyspamscore: maximum spam score for notifications
# maxnotifyaddrscore: maximum address heuristics score for notifications
# minreportspamscore: minimum spam score for abuse reports (spam)
# minreportspamscorevrfy: minimum spam score for abuse reports (spam) if MAIL FROM could not be verified
# minreportaddrscorevirus: minimum address heuristics score for abuse reports (virus)
# minreportaddrscorevirusvrfy: minimum address heuristics score for abuse reports (virus) if MAIL FROM
#                              could not be verified
maxnotifyspamscore=12
maxnotifyaddrscore=4
minreportspamscore=20
minreportspamscorevrfy=15
minreportaddrscorevirus=6
minreportaddrscorevirusvrfy=3

# buffer message and headers
TMP_headers=`mktemp -p /tmp` || exit 0
TMP_full_message=`mktemp -p /tmp` || exit 0
cat >$TMP_full_message
sed -ne '/^$/q;p' <$TMP_full_message >$TMP_headers

Die Informationen über absendenden Mailserver, HELO-Identifikation etc. wird wieder zusammengesucht. Da nondelivery.sh nicht immer direkt von spamfilter.sh aus aufgerufen wird, können wir diese Werte nicht immer als Parameter mitgeben, sodaß wir sie eventuell wieder aus den Kopfzeilen zusammensetzen müssen.

 

date=`grep -i '^Date:' $TMP_headers | sed -e 's/^[^:]*: //'`
ident=`grep -i '^Message-ID:' $TMP_headers | sed -e 's/^[^:]*: //;s/[><]//g' | tr -d '\n' | tr '[:cntrl:]' '?'`

# collect parameters
origin="$1"
destination="$2"
shift; shift
reason="$1"
addinfo="$2"
if [ X"$3" != X ]
then
        mailfrom="$3"
        rcptcount="$5"
        helo="$6"
        client_ip="$7"
        time="$8"
else
        if heloline=`grep 'Received: from .* by '"$my_received_id"' with' $TMP_headers`
        then
                # 1=helo 2=client_ip 5=rcpt_count_or_empty 7=mailfrom 8=time
                # Received: from $HELO ($CLIENT_REV [$CLIENT]) by $my_received_id with $PROTOCOL id $IDENT
                #   for <$DESTINATION> count $RCPT_COUNT from <$SOURCE>; $TIME
                helopattern='^Received: from \([^ ]*\) ([^ ]* [[]\([^]]*\)]) by '"$my_received_id"
                helopattern="$helopattern"' with [^ ]* id [^ ]*'
                helopattern="$helopattern"'\( for <[^>]*>\)\{0,1\}\( count \([[:digit:]]*\)\)\{0,1\}'
                helopattern="$helopattern"'\( from <\([^>]*\)>\)\{0,1\}[^;]*; \(.*\)$'
                helo=`echo "$heloline" |      sed -e 's/'"$helopattern"'/\1/'`
                client_ip=`echo "$heloline" | sed -e 's/'"$helopattern"'/\2/'`
                rcptcount=`echo "$heloline" | sed -e 's/'"$helopattern"'/\5/'`
                mailfrom=`echo "$heloline" |  sed -e 's/'"$helopattern"'/\7/'`
                time=`echo "$heloline" |      sed -e 's/'"$helopattern"'/\8/'`
        fi
        [ X"$mailfrom" = X ] && \
                mailfrom=`grep -i '^Return-Path:' $TMP_headers | sed -e 's/^[^:]*: //' | extractmail.sh single`
fi

# notifications got to MAIL FROM, no notification if mail address can not be extracted unambiguously
[ X"$mailfrom" = X ] && notify=no
[ X"$rcptcount" = X ] && rcptcount=0

if [ X"$client_ip" = X ]
then
        logger -p mail.info -t $logname -- "error: no client info"
        exit 1
fi
client_rev=`dig +short -x "$client_ip" | sed -e 's/^\(.*\)\.$/\1/'`

# extract additional header info
spamscore=`grep '^X-Spam-Level:' $TMP_headers | sed -e 's/[^*]//g' | wc -c | tr -d ' '`
[ X"$spamscore" = X ] && spamscore=0
receiver=`grep -i '^To:' $TMP_headers | sed -e 's/^[^:]*: //' | tr -d '\n' | tr '[:cntrl:]' '?'`
sender=`grep -i '^From:' $TMP_headers | sed -e 's/^[^:]*: //' | extractmail.sh single` || notify=no
subject=`grep -i '^Subject:' $TMP_headers | sed -e 's/^[^:]*: //;s/</{/g;s/>/}/g' | tr -d '\n' | tr '[:cntrl:]' '?'`
[ `expr length "$subject"` -gt $subjectlength ] && subject=`expr substr "$subject" 1 $subjectlength`"..."

Mit heuristischer Methode versuchen wir aus den CLIENT-, HELO- und MAIL FROM-Informationen zu ermitteln, ob der Absender seriös sein könnte, und vergeben dafür eine Punktzahl.

 

################################################################################################################
# some heuristical consideration if the sender information is valid
################################################################################################################
# set only one of those:
score_from_differs=3
score_fromdomain_differs=5

# set any of those:
score_client_unknown=2
score_probable_dialup=2
score_client_unequal_helo=1

# set only one of those:
score_helo_void=3
score_numeric_helo=2
score_no_helo_ip=3

addrscore=0
scores=""

if [ X"$mailfrom" != X"$sender" ]
then
        mailfromdom=`echo "$mailfrom" | sed -e 's/^[^@]*@\(.*\)$/\1/'`
        senderdom=`echo "$sender" | sed -e 's/^[^@]*@\(.*\)$/\1/'`
        if [ X"$mailfromdom" != X"$senderdom" ]
        then
                { addrscore=`expr $addrscore + $score_fromdomain_differs`; scores="$scores fromdomain"; }
        else
                { addrscore=`expr $addrscore + $score_from_differs`; scores="$scores from"; }
        fi
fi
if [ X"$client_rev" = X -o X"$client_rev" = Xunknown ]
then
        { addrscore=`expr $addrscore + $score_client_unknown`; scores="$scores client=unknown"; }
else
        echo "$client_rev" | grep -q '^.*-.*-.*-.*$' && \
                { addrscore=`expr $addrscore + $score_probable_dialup`; scores="$scores dialup"; }
fi
if [ X"$helo" = X ]
then
        { addrscore=`expr $addrscore + $score_helo_void`; scores="$scores nohelo"; }
        heloip=""
        { addrscore=`expr $addrscore + $score_client_unequal_helo`; scores="$scores client!=helo"; }
elif echo "$helo" | grep -q '[0-9]$'
then
        { addrscore=`expr $addrscore + $score_numeric_helo`; scores="$scores numhelo"; }
        heloip="$helo"
        [ X"$helo" != X"$client_ip" ] && \
                { addrscore=`expr $addrscore + $score_client_unequal_helo`; scores="$scores client!=helo"; }
else
        heloip=`dig +short "$helo"`
        [ X"$heloip" = X ] && \
                { addrscore=`expr $addrscore + $score_no_helo_ip`; scores="$scores heloip"; }
        [ X"$helo" != X"$client_rev" ] && \
                { addrscore=`expr $addrscore + $score_client_unequal_helo`; scores="$scores client!=helo"; }
fi
[ $addrscore -gt 9 ] && addrscore=9

Mit dem Kommando vrfy wird eine Emailadresse verifiziert, ohne daß wir wirklich eine Testmail senden müssen.

Wenn eine Nachricht als Spam aussortiert wurde, weil der Absender auf der Schwarzen Liste eine unserer Nutzer steht, senden wir weder Mitteilung noch Beschwerde aus. Es könnte sich um Personen handeln, die nicht erfahren sollen, daß sie nicht mehr beliebt sind.

 

################################################################################################################
# check all conditions if notification or reporting is necessary and possible
################################################################################################################
# if there are too many RCPT TO addresses, this is most likely spam
[ $rcptcount -gt $maxrcptcount ] && notify=no

if [ $spamscore -gt $maxnotifyspamscore -o $addrscore -gt $maxnotifyaddrscore ]
then
        checkpoints=${checkpoints}A
        notify=no
fi

addinfo1=""
vrfy_message=`vrfy -hlsn "$mailfrom"`
vrfy_result=$?
if [ $vrfy_result != 0 ]
then
        checkpoints=${checkpoints}B
        notify=no
fi

# do not report abuse for hosted user destinations, except for SPF violations
[ $notify = yes -o X"$destination" != Xfamily ] && reportabuse=no
case $reason in
unknown )
        reportabuse=no
        ;;
spam )
        checkpoints=${checkpoints}C
        if [ $vrfy_result != 0 ]
        then
                [ $spamscore -lt $minreportspamscorevrfy ] && reportabuse=no
        else
                [ $spamscore -lt $minreportspamscore ] && reportabuse=no
        fi
        # blacklisted senders are not spammers,
        # usually these are parties with which we were but do not want to stay in contact
        if grep -i '^X-Spam-Status: .*USER_IN_BLACKLIST=' $TMP_headers 1>/dev/null 2>/dev/null
        then
                checkpoints=${checkpoints}Z
                notify=no
                reportabuse=no
        fi
        ;;
virus )
        checkpoints=${checkpoints}D
        if [ $vrfy_result != 0 ]
        then
                [ $addrscore -lt $minreportaddrscorevirusvrfy ] && reportabuse=no
        else
                [ $addrscore -lt $minreportaddrscorevirus ] && reportabuse=no
        fi
        ;;
spf )
        checkpoints=${checkpoints}E
        notify=no
        reportabuse=yes
        ;;
esac
if [ X"$origin" = Xauthorized ]
then
        # of course, do not report abuse for our own users
        checkpoints=${checkpoints}F
        reportabuse=no
else
        checkpoints=${checkpoints}G
        if match_wildcard.sh "$helo" own_domains.rxp 1>/dev/null 2>/dev/null
        then
                # always report abuse if someone uses our own address for HELO
                checkpoints=${checkpoints}H
                notify=no
                reportabuse=yes
                addinfo2="forged HELO identification: $helo"
        fi
fi
if [ $reportabuse = yes ] && match_wildcard.sh "$helo" helo_whitelist.rxp 1>/dev/null 2>/dev/null
then
        checkpoints=${checkpoints}N
        reportabuse=no
fi
if [ $reportabuse = yes ]
then
        checkpoints=${checkpoints}I
        notify=no
        [ X"$client_rev" = X -o X"$client_rev" = Xunknown ] && check_whois=yes
fi
abuse_mail=""

Die einzige Information zur Email, auf die wir uns fast sicher verlassen können, ist die IP-Adresse des absendenden Clients. Über Reverse DNS hat bereits Postfix dazu (wenn möglich) einen Domainnamen gefunden. Mit Hilfe des Network Abuse Clearinghouse suchen wir dazu eine Emailadresse, über die dem Provider der Mißbrauch seines Netzes mitgeteilt werden kann.

 

abuse_mail=""
if [ $reportabuse = yes -a $check_whois = no ]
then
        checkpoints=${checkpoints}J
        # get abuse reporting address for sending client from www.abuse.net
        abuse_mail=`dig +short $client_rev.contacts.abuse.net TXT 2>/dev/null | head -n 1 | sed -e 's/"//g;s/;//g'`
        if [ `echo "$abuse_mail" | wc -w` -eq 1 ]
        then
                # check if abuse reporting address responds
                if vrfy -hlsn "$abuse_mail" >/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}K
                else
                        checkpoints=${checkpoints}L
                        check_whois=yes
                fi
        else
                checkpoints=${checkpoints}M
                check_whois=yes
        fi
fi

Leider scheitert die Methode über das Network Abuse Clearinghouse außerordentlich oft. Wir wollen die Provider, die es nicht nötig habe, sich dort zu registrieren, aber nicht in Ruhe lassen. Wenn jemand etwas gegen Netzmißbrauch tun kann, sind es schließlich die Provider.

Wir benutzen die Information, die das Kommando whois liefert, um eine geeignete Adresse zur Rückmeldung herauszufischen. Dazu wird, beginnend mit whois.arin.net, zuerst das richtige Whois-Register gesucht.

 

################################################################################################################
# derive abuse reporting information from WHOIS entries available
################################################################################################################
if [ $check_whois = yes ]
then
        checkpoints=${checkpoints}O
        abuse_mail=""
        TMP_whois=`mktemp -p /tmp` || exit 0
        # get first information from ARIN
        whois_server=whois.arin.net
        whois -h $whois_server +"$client_ip" >$TMP_whois
        # figure out referenced regional source WHOIS registries
        if grep -Ei 'ReferralServer.*whois\.ripe\.net' $TMP_whois 1>/dev/null 2>/dev/null
        then
                checkpoints=${checkpoints}'*'
                whois_server=whois.ripe.net
                whois -h $whois_server "$client_ip" >$TMP_whois
        elif grep -Ei 'ReferralServer.*whois\.lacnic\.net' $TMP_whois 1>/dev/null 2>/dev/null
        then
                checkpoints=${checkpoints}'+'
                whois_server=whois.lacnic.net
                whois -h $whois_server "$client_ip" >$TMP_whois
        elif grep -Ei 'ReferralServer.*whois\.apnic\.net' $TMP_whois 1>/dev/null 2>/dev/null
        then
                checkpoints=${checkpoints}'-'
                whois_server=whois.apnic.net
                whois -h $whois_server "$client_ip" >$TMP_whois
        fi
        # find referenced national registries
        if grep -Ei '^remarks.*http://whois\.nic\.or\.kr/english/index\.html' $TMP_whois 1>/dev/null 2>/dev/null
        then
                checkpoints=${checkpoints}1
                whois_server=whois.nic.or.kr
                whois -h $whois_server "$client_ip" >$TMP_whois
        elif grep -Ei 'Allocated to KRNIC Member' $TMP_whois 1>/dev/null 2>/dev/null
        then
                if grep -Ei 'KRNIC Whois Database at' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}2
                        whois_server=whois.krnic.net
                        whois -h $whois_server "$client_ip" >$TMP_whois
                fi
        elif grep -Ei 'Korea under APNIC' $TMP_whois 1>/dev/null 2>/dev/null
        then
                if grep -Ei 'KRNIC Whois DB' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}3
                        whois_server=whois.nic.or.kr
                        whois -h $whois_server "$client_ip" >$TMP_whois
                fi
        elif grep -Ei 'mirrored by APNIC' $TMP_whois 1>/dev/null 2>/dev/null
        then
                if grep -Ei 'TWNIC whois server at' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}4
                        whois_server=whois.twnic.net
                        whois -h $whois_server "$client_ip" >$TMP_whois
                elif grep -Ei 'JPNIC whois server at' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}5
                        whois_server=whois.nic.ad.jp
                        whois -h $whois_server "$client_ip" >$TMP_whois
                fi
        elif grep -Ei 'assigned to Brazilian users' $TMP_whois 1>/dev/null 2>/dev/null
        then
                if grep -Ei 'can be found at the WHOIS server' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}6
                        whois_server=whois.nic.br
                        whois -h $whois_server "$client_ip" >$TMP_whois
                fi
        fi

Das am besten passende Whois-Register wird nach einer geeigneten Mailadresse abgesucht.

 

        # scan for abuse notification address
        while [ X"$abuse_mail" = X ]
        do
                if grep -Ei 'spam[^[:space:]]*@' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}a
                        abuse_mail=`cat $TMP_whois | \
                                grep -Ei 'spam[^[:space:]]*@' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/'`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei 'abuse[^[:space:]]*@' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}b
                        abuse_mail=`cat $TMP_whois | \
                                grep -Ei 'abuse[^[:space:]]*@' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/'`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei '^[[] ISP Network Abuse Contact Information ]' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}c
                        abuse_mail=`cat $TMP_whois | \
                                sed -ne '/^[[] ISP Network Abuse Contact Information ]/,$p' | \
                                grep -Ei '^e-mail' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei 'spam.*issues' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}d
                        abuse_mail=`cat $TMP_whois | \
                                sed -ne '/[Ss][Pp][Aa][Mm].*issues/,$p' | \
                                grep -i 'send mail' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei 'abuse.*issues' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}e
                        abuse_mail=`cat $TMP_whois | \
                                sed -ne '/[Aa][Bb][Uu][Ss][Ee].*issues/,$p' | \
                                grep -i 'send mail' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei '^OrgAbuseEmail' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}f
                        abuse_mail=`cat $TMP_whois | \
                                grep -Ei '^OrgAbuseEmail' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei '^person:[[:space:]]*Host Master' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}g
                        abuse_mail=`cat $TMP_whois | \
                                sed -ne '/^[Pp]erson:.*[Hh]ost [Mm]aster/,$p' | \
                                grep -Ei '^e-mail' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei '^person' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}h
                        abuse_mail=`cat $TMP_whois | \
                                sed -ne '/^[Pp]erson/,$p' | \
                                grep -Ei '^e-mail' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei '^TechEmail' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}i
                        abuse_mail=`cat $TMP_whois | \
                                grep -Ei '^TechEmail' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei '[[]Reply Mail]' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}j
                        abuse_mail=`cat $TMP_whois | \
                                grep -Ei '[[]Reply Mail]' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -i 'hostmaster@' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}k
                        abuse_mail=`cat $TMP_whois | \
                                grep -i 'hostmaster@' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -i 'admin@' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}l
                        abuse_mail=`cat $TMP_whois | \
                                grep -i 'admin@' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep -Ei '^Email' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}m
                        abuse_mail=`cat $TMP_whois | \
                                grep -Ei '^Email' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break
                if grep '@' $TMP_whois 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}n
                        abuse_mail=`cat $TMP_whois | \
                                grep '@' 2>/dev/null | head -n 1 | \
                                sed -e 's/^.*[[:space:]]\([^[:space:]]*@[^[:space:]]*\).*$/\1/' 2>/dev/null`
                fi
                [ X"$abuse_mail" != X ] && break

Für Provider, die nicht einmal im Whois-Eintrag eine Mailadresse angeben, pflegen wir selbst eine kleine Liste in special_abuse.rxp.

 

                # providers which do not publish abuse reporting addresses in their WHOIS record
                first_line=`head -n 1 $TMP_whois`
                [ X"$first_line" != X ] && abuse_mail=`match_wildcard.sh "$first_line" special_abuse.rxp`
                if [ X"$abuse_mail" != X ]
                then
                        checkpoints=${checkpoints}x
                        abuse_manual=yes
                        break
                fi
                if cat $TMP_whois | grep -Ei '^IMPSAT CORP' 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}y
                        abuse_mail="abuse@impsat.net"
                        abuse_manual=yes
                elif [ X"$client_rev" != X -a "$client_rev" != unknown ] && \
                        echo "$client_rev" | grep -E '^.*\.[^.]*\.[^.]*$' 1>/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}z
                        abuse_mail=`echo "$client_rev" | sed -e 's/^.*\.\([^.]*\.[^.]*\)$/abuse@\1/'`
                        abuse_guess=yes
                fi
                break
        done
        if [ X"$abuse_mail" = X ]
        then
                checkpoints=${checkpoints}P
                abuse_mail="NONE"
                reportabuse=no
                infopostmaster=yes
        else
                if echo "$abuse_mail" | grep -E '^mailto:' 1>/dev/null 2>/dev/null
                then
                        abuse_mail=`echo "$abuse_mail" | sed -e 's/^mailto:\(.*\)$/\1/'`
                fi
                if vrfy -hlsn "$abuse_mail" >/dev/null 2>/dev/null
                then
                        checkpoints=${checkpoints}Q
                else
                        checkpoints=${checkpoints}R
                        abuse_mail="DEAD:$abuse_mail"
                        reportabuse=no
                fi
        fi
fi
return_mail="$mailfrom"
[ $notify = yes ] || return_mail=""

logger -p mail.info -t $logname -- \
        "reason=<$reason> mail_from=<$mailfrom> subject=<$subject> helo=<$helo> client_ip=<$client_ip> time=<$time> origin=<$origin> destination=<$destination> rcptcount=<$rcptcount> spamscore=<$spamscore> heuristics=<$addrscore> verify=<$vrfy_result> notify=<$return_mail> reportabuse=<$abuse_mail> check=<$checkpoints>"

Wenn wir festgestellt haben, daß der Absender der Email benachrichtigt werden sollte, konstruieren wir für ihn eine Benachrichtigung und senden sie ab.

Das Anhängen unserer Serversignatur bewirkt, daß diese Benachrichtigung ihrerseits nicht im Spamfilter hängenbleiben kann.

Jede Antwort auf diesen Hinweis unterbinden wir dadurch, daß wir Postfix Nachrichten an notify@mydomain.net zurückweisen lassen.

 

if [ $notify = yes ]
then
        TMP_notification=`mktemp -p /tmp` || exit 0
        (
                # use a dead sender address for this notificaton in order to prevent bounce mails
                echo "From: $notifier"
                echo "To: $return_mail"
                echo "Cc: $postmaster"
                echo "Subject: Nachricht nicht zugestellt / Message not delivered"
                echo "MIME-Version: 1.0"
                echo "Content-Type: multipart/mixed; boundary=nextpart"
                echo "Importance: normal"
                echo "Content-Type: text/plain; charset=iso-8859-1"
                echo "Content-Transfer-Encoding: quoted-printable"
                echo
                # echo '----+----1----+----2----+----3----+----4----+----5----+----6----+----7--'
                cat <<-EOF
                        Dies ist eine automatisch erzeugte Meldung von
                        $postmaster an $return_mail.

                        Ihre Nachricht vom $date
                        von $sender an $receiver
                        konnte nicht zugestellt werden.
                EOF
                case $reason in
                spam)
                        echo "Grund: Die Nachricht wurde als elektronische Werbung (Spam) erkannt."
                        ;;
                spf)
                        echo "Grund: Die Nachricht tr=E4gt eine unerlaubte Absendeadresse."
                        echo "$addinfo" | sed -e 's/=/=3D/g'
                        ;;
                unknown)
                        echo "Grund: Der angegebene Empf=E4nger ist nicht bekannt."
                        ;;
                virus)
                        echo "Grund: Die Nachricht ist mit sch=E4dlichem Code infiziert."
                        echo "$addinfo" | sed -e 's/=/=3D/g'
                        ;;
                esac
                echo
                # echo '----+----1----+----2----+----3----+----4----+----5----+----6----+----7--'
                [ $reason = spam ] && cat <<-EOF
                        Entsprechend der Disposition des Postverwalters und des Empf=E4ngers
                        wird die Nachricht automatisch gel=F6scht oder archiviert. Bitte gehen
                        Sie davon aus, da=DF die Nachricht nicht den gew=FCnschten Adressaten
                        erreicht.

                        #######################################################################
                        # Wenn Ihre Nachricht keine Massensendung war und f=FCr den Empf=E4nger
                        # pers=F6nlich bestimmt war, sollten Sie auf anderem Wege Kontakt
                        # mit dem Empf=E4nger aufnehmen und ihn um Aufnahme Ihrer Adresse
                        # $sender in die Wei=DFe Liste bitten.
                        #######################################################################

                        Benachrichtigen Sie $postmaster, wenn Sie meinen, da=DF Ihre
                        Nachricht ungerechtfertigt zur=FCckgewiesen wurde. Bitte f=FCgen Sie
                        den Text dieser Email Ihrer Antwort bei. Bitte antworten Sie nicht
                        direkt auf diese Information, sondern senden Sie Ihre Nachricht an
                        $postmaster.

                EOF
                # echo '----+----1----+----2----+----3----+----4----+----5----+----6----+----7--'
                cat <<-EOF
                        ------------------------------------------------------------------------
                        This is an automatically generated notification from
                        $postmaster to $return_mail.

                        Your message of $date
                        from $sender to $receiver
                        could not be delivered.
                EOF
                case $reason in
                spam)
                        echo "Reason: Your message was classified as UCE (spam)."
                        ;;
                spf)
                        echo "Reason: The sender address is not legitimate."
                        echo "$addinfo" | sed -e 's/=/=3D/g'
                        ;;
                unknown)
                        echo "Reason: The recipient address is unknown."
                        ;;
                virus)
                        echo "Reason: The message is infected with maliciuos code."
                        echo "$addinfo" | sed -e 's/=/=3D/g'
                        ;;
                esac
                echo
                [ $reason = spam ] && cat <<-EOF
                        According to the disposition of postmaster and recipient, the message
                        will be automatically deleted or quarantined. You should assume that
                        your message will not reach its desired destination.

                        #######################################################################
                        # If your message was directed at the receiver directly and was not
                        # bulk mail, you should contact the receiver by other means and ask to
                        # become whitelisted with your address $sender.
                        #######################################################################

                        Notify $postmaster if you feel that the the rejection
                        of your message is unjustified. Please add the text of this message
                        to your reply. Please do not respond directly to this information,
                        send your mail to $postmaster instead.

                EOF
                # echo '----+----1----+----2----+----3----+----4----+----5----+----6----+----7--'
                cat <<-EOF
                        ------------------------------------------------------------------------
                        Message headers:
                EOF
                # take mailheaders from standard input
                # encode '=' to comply with Quoted-Printable
                # suppress some internals from notification mail
                mawk -f suppress_internals.awk <$TMP_headers | sed -e 's/=/=3D/g'
                # include my server signature here, replies containing the magic will pass through the spam scanner
                cat <<-EOF

                        ------------------------------------------------------------------------

                        Signature: $auth_magic
                        Contact: $postmaster
                        Please include this message and above signature line in your replies.
                EOF
        ) | cat >$TMP_notification
        cat $TMP_notification | /usr/sbin/sendmail -i -f $notifier -- "$return_mail"
        # store a copy of this message
        cat $TMP_notification | /usr/sbin/sendmail -i -f $notifier -- "$postmaster"
        rm $TMP_notification

War die empfangene Email zweifelsfrei Spam, versuchen wir den Internetprovider des Absenders zu kontaktieren. Wir hängen alle verfügbare Information an außer im Fall virenverseuchter Mails. Wir identifizieren uns als Postmaster und warten ab, ob der Internetprovider sich bei uns meldet. Etliche tun das mit einer automatisierten Antwort. So unterhalten sich Automaten mit Automaten.

 

elif [ $reportabuse = yes ]
then
        TMP_report=`mktemp -p /tmp` || exit 0
        (
                # use postmaster as sender here in order to get automated ISP abuse department replies
                echo "From: $postmaster"
                echo "To: $abuse_mail"
                echo "Cc: $postmaster"
                echo "Subject: Network abuse notification"
                echo "MIME-Version: 1.0"
                echo "Content-Type: multipart/mixed; boundary=nextpart"
                echo "Importance: normal"
                echo "Content-Type: text/plain; charset=iso-8859-1"
                echo "Content-Transfer-Encoding: quoted-printable"
                echo
                # echo '----+----1----+----2----+----3----+----4----+----5----+----6----+----7--'
                cat <<-EOF
                        This automatically generated message shall inform you about a case of
                        network abuse in your area of concern.

                EOF
                case $reason in
                spam)
                        echo "The message below was classified as UCE (spam)."
                        ;;
                spf)
                        echo "According to SPF test the sender address is not legitimate."
                        ;;
                virus)
                        echo "The message is infected with maliciuos code $addinfo." | sed -e 's/=/=3D/g'
                        ;;
                esac
                cat <<-EOF
                        The message was sent at $time,
                        sending client was $client_ip.

                EOF
                if [ $abuse_manual = yes ]
                then
                        # echo '----+----1----+----2----+----3----+----4----+----5----+----6----+----7--'
                        cat <<-EOF
                                It is good common practice to maintain usable WHOIS records. Since no
                                abuse reporting information could be extracted from $whois_server,
                                your email $abuse_mail was retrieved from a public
                                abuse registry service.
                        EOF
                elif [ $abuse_guess = yes ]
                then
                        cat <<-EOF
                                It is good common practice to maintain usable WHOIS records. Since no
                                abuse reporting information could be extracted from $whois_server,
                                your email $abuse_mail was automatically derived
                                from the IP reverse mapping $client_rev.
                        EOF
                elif [ $check_whois = yes ]
                then
                        cat <<-EOF
                                Your email $abuse_mail was automatically extracted
                                from your $whois_server database entry.
                        EOF
                else
                        cat <<-EOF
                                Your email $abuse_mail is recorded as an
                                abuse reporting address for $client_rev
                                by the Network Abuse Clearinghouse (http://www.abuse.net).
                        EOF
                fi
                cat <<-EOF

                        We suggest that you follow up this abuse of your network resources
                        or check with your customers for possible spambot infections of their
                        infrastructure.

                        Please contact $postmaster if you have any questions.
                        Kindly attach this message to your response.

                EOF
                if [ $reason = virus ]
                then
                        # remove the infected body, it would be filtered out by the ISP's mail server anyway
                        cat <<-EOF
                                Message headers (infected body removed):
                                ------ snip ------------------------------------------------------------
                        EOF
                        mawk -f suppress_internals.awk <$TMP_headers | sed -e 's/=/=3D/g'
                        cat <<-EOF
                                ------ snip ------------------------------------------------------------
                                (End of message headers)
                        EOF
                else
                        cat <<-EOF
                                Complete message:
                                ------ snip ------------------------------------------------------------
                        EOF
                        mawk -f suppress_internals.awk <$TMP_full_message | sed -e 's/=/=3D/g'
                        cat <<-EOF
                                ------ snip ------------------------------------------------------------
                                (End of message)
                        EOF
                fi
                if [ $check_whois = yes ]
                then
                        cat <<-EOF

                                WHOIS entry for $client_ip from $whois_server:
                                ------ snip ------------------------------------------------------------
                        EOF
                        cat $TMP_whois | sed -e 's/=/=3D/g'
                        cat <<-EOF
                                ------ snip ------------------------------------------------------------
                        EOF
                fi
                # include my server signature here, replies containing the magic will pass through the spam scanner
                cat <<-EOF

                        Signature: $auth_magic
                        Contact: $postmaster
                        Please include this message and above signature line in your replies.
                EOF
        ) | cat >$TMP_report
        cat $TMP_report | /usr/sbin/sendmail -i -f $notifier -- "$abuse_mail"
        # store a copy of this message
        cat $TMP_report | /usr/sbin/sendmail -i -f $notifier -- "$postmaster"
        rm $TMP_report
fi

Wir lassen uns informieren, falls es nicht gelungen ist, eine Adresse für die Abuse-Meldung zu finden. Wir können dann unser Script nachbessern. In der Regel wird ein weiterer Eintrag zu special_abuse.rxp hinzuzufügen sein.

 

if [ $infopostmaster = yes ]
then
        (
                echo "From: $notifier"
                echo "To: $postmaster"
                echo "Subject: Could not send abuse report"
                echo "MIME-Version: 1.0"
                echo "Content-Type: multipart/mixed; boundary=nextpart"
                echo "Importance: normal"
                echo "Content-Type: text/plain; charset=iso-8859-1"
                echo "Content-Transfer-Encoding: quoted-printable"
                echo
                # echo '----+----1----+----2----+----3----+----4----+----5----+----6----+----7--'
                cat <<-EOF
                        Abuse report could not be sent.
                        Reporting address detected: $abuse_mail.
                        Check process: <$checkpoints>.

                EOF
                case $reason in
                spam)
                        echo "The message below was classified as UCE (spam)."
                        ;;
                spf)
                        echo "According to SPF test the sender address is not legitimate."
                        ;;
                virus)
                        echo "The message is infected with maliciuos code $addinfo." | sed -e 's/=/=3D/g'
                        ;;
                esac
                if [ $reason = virus ]
                then
                        # remove the infected body, it would be filtered out by the ISP's mail server anyway
                        cat <<-EOF
                                Message headers (infected body removed):
                                ------ snip ------------------------------------------------------------
                        EOF
                        mawk -f suppress_internals.awk <$TMP_headers | sed -e 's/=/=3D/g'
                        cat <<-EOF
                                ------ snip ------------------------------------------------------------
                                (End of message headers)
                        EOF
                else
                        cat <<-EOF
                                Complete message:
                                ------ snip ------------------------------------------------------------
                        EOF
                        mawk -f suppress_internals.awk <$TMP_full_message | sed -e 's/=/=3D/g'
                        cat <<-EOF
                                ------ snip ------------------------------------------------------------
                                (End of message)
                        EOF
                fi
                cat <<-EOF

                        Reverse domain lookup for $client_ip from $whois_server:
                        ------ snip ------------------------------------------------------------
                EOF
                dig -x "$client_ip" | sed -e 's/=/=3D/g'
                cat <<-EOF
                        ------ snip ------------------------------------------------------------

                        WHOIS entry for $client_ip from $whois_server:
                        ------ snip ------------------------------------------------------------
                EOF
                cat $TMP_whois | sed -e 's/=/=3D/g'
                cat <<-EOF
                        ------ snip ------------------------------------------------------------

                        Signature: $auth_magic
                        Contact: $postmaster
                        Please include this message and above signature line in your replies.
                EOF
        ) | /usr/sbin/sendmail -i -f $notifier -- "$postmaster"
fi

[ $check_whois = yes ] && rm -f $TMP_whois
rm -f $TMP_headers $TMP_full_message
exit 0

Dies sind die Steuerdateien, die nondelivery.sh benötigt:

 

$ vi /etc/postfix/helo_whitelist.rxp
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/helo_whitelist.rxp

^.*\.intra\.mydomain\.net$
$ vi /etc/postfix/special_abuse.rxp
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/special_abuse.rxp
# Format: "WHOIS entry search pattern"    abuse_address@isp.net
#   "^Qwest Communications"             abuse@qwest.net
$ vi /etc/postfix/map_smtpd_sender_accounts
#
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/map_smtpd_sender_accounts
# MAIL FROM addresses with their respective local accounts for automatic b/w list management

myself@mydomain.net                     myself
myself@otherdomain.net                  myself
otheruser@mydomain.net                  otheruser

Weitere benötigte Shellscripte folgen hier.

 

$ vi /etc/postfix/add_blackwhitelist.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/add_blackwhitelist.sh
# add a black or white list entry
# parameter 1: "black" or "white"
# parameter 2: local mail user address
# parameter 3: mail address to be blacklisted

logname=add_blackwhitelist.sh
cd /etc/postfix || exit 1

pattern1=`echo "$2" | sed -e 's/\./\\./g'`
username=`grep -E "^$pattern1[[:space:]]" map_smtpd_sender_accounts 2>/dev/null | head -n 1 | \
        sed -e 's/^[^[:space:]]*[[:space:]]*\(.*\)$/\1/'` || exit 1
logger -t $logname "add to ${1}list of $username: $3"
if [ ! -d /home/$username -o ! -f /home/$username/.spamassassin/user_prefs ]
then
        logger -t $logname "... $username directory error"
        exit 1
fi
pattern2=`echo "$3" | sed -e 's/\./\\./g;s/\*/.*/g'`
if grep -E "^[[:space:]]*(black|white)list_from[[:space:]]*$pattern2[[:space:]]*$" \
        /home/$username/.spamassassin/user_prefs >/dev/null 2>/dev/null
then
        logger -t $logname "... $3 already listed"
        exit 0
fi

echo "# `date`" >>/home/$username/.spamassassin/add_blackwhitelist
echo ${1}list_from $3 >>/home/$username/.spamassassin/add_blackwhitelist
echo ${1}list_from $3 >>/home/$username/.spamassassin/user_prefs
logger -t $logname "... $3 added"

exit 0
$ vi /etc/postfix/auth_magic.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/auth_magic.sh
# parameter 1: mydomain

servername=mail.$1
mailcert=/etc/ssl/certs/$servername.crt
fingerprint=`openssl x509 -noout -fingerprint -in $mailcert | sed -e 's/MD5 Fingerprint=//'`
echo "$servername ($fingerprint)"
exit 0
$ vi /etc/postfix/match_plain.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/match_plain.sh
# matches plain or wildcard names against a list containing plain entries
# parameter 1: actual address (e.g. myself@mydomain.net or *@mydomain.net)
# parameter 2: match list filename (entries e.g. myself@mydomain.net)

rline=`echo $1 | sed -e 's/\./\\\\./g;s/\*/.*/g;s/^/^/;s/$/$/'`
grep -v '^#' $2 | grep -v '^$' | grep -i "$rline"
exit $?
$ vi /etc/postfix/match_wildcard.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/match_wildcard.sh
# matches plain names against a list containing plain or wildcard entries (preformatted regex)
# parameter 1: actual address (e.g. myself@mydomain.net)
# parameter 2: match list filename (entries are preformatted regular expressions, e.g. ^.*@mydomain\.net$)
# match is analysed until first white space character, return value is string after white space
# match/return entire line if no white space contained

TEMP1=`mktemp -p /tmp` || exit 1;
echo "$1" >$TEMP1
IFS='
'

for line in `grep -v '^#' "$2" | grep -v '^$'`
do
        case "$line" in
        "'"* )
                match=`echo "$line" | sed -e 's/^'"'"'\([^'"'"']*\)'"'"'[[:space:]]*.*$/\1/' 2>/dev/null`
                ;;
        '"'* )
                match=`echo "$line" | sed -e 's/^"\([^"]*\)"[[:space:]]*.*$/\1/' 2>/dev/null`
                ;;
        * )
                match=`echo "$line" | sed -e 's/^\([^[:space:]]*\)[[:space:]]*.*$/\1/' 2>/dev/null`
                ;;
        esac
        grep -Ei "$match" $TEMP1 1>/dev/null 2>/dev/null
        if [ $? = 0 ]
        then
                rm -f $TEMP1
                case "$line" in
                "'"* )
                        reply=`echo "$line" | sed -e 's/^'"'"'[^'"'"']*'"'"'[[:space:]]*\(.*\)$/\1/' 2>/dev/null`
                        ;;
                '"'* )
                        reply=`echo "$line" | sed -e 's/^"[^"]*"[[:space:]]*\(.*\)$/\1/' 2>/dev/null`
                        ;;
                * )
                        reply=`echo "$line" | sed -e 's/^[^[:space:]]*[[:space:]]*\(.*\)$/\1/' 2>/dev/null`
                        [ X"$reply" = X ] && reply="$line"
                        ;;
                esac
                echo "$reply" | sed -e 's/^^//;s/$$//;s/\.\*/*/g;s/\\\././g'
                exit 0
        fi
done

rm -f $TEMP1
exit 1
$ vi /etc/postfix/extractmail.sh
#!/bin/sh
# (c) Thomas Bez <bez@tedesca.net> 2004
# http://www.bez.tedesca.net/homeoff/antispam.html
#
# mail.mydomain.net:/etc/postfix/extractmail.sh
#parameter 1: single or multiple (default)

error=no
count=0
IFS='
'

for line in `sed -e 's/"[^"]*"//g;s/"[^"]*$//' | tr ',;' '\n'`
do
        if echo $line | grep -q '^[[:space:]]*"[^"]*"[[:space:]]*<\([a-zA-Z0-9_.-]*@[a-zA-Z0-9_.-]*\)>[[:space:]]*$'
        then
                echo $line | sed -e 's/^[[:space:]]*"[^"]*"[[:space:]]*<\([a-zA-Z0-9_.-]*@[a-zA-Z0-9_.-]*\)>[[:space:]]*$/\1/'
                count=`expr $count + 1`
                continue
        fi
        if echo $line | grep -q '^[^<]*<\([a-zA-Z0-9_.-]*@[a-zA-Z0-9_.-]*\)>[[:space:]]*$'
        then
                echo $line | sed -e 's/^[^<]*<\([a-zA-Z0-9_.-]*@[a-zA-Z0-9_.-]*\)>[[:space:]]*$/\1/'
                count=`expr $count + 1`
                continue
        fi
        if echo $line | grep -q '^[[:space:]]*\([a-zA-Z0-9_.-]*@[a-zA-Z0-9_.-]*\)[[:space:]]*$'
        then
                echo $line | sed -e 's/^[[:space:]]*\([a-zA-Z0-9_.-]*@[a-zA-Z0-9_.-]*\)[[:space:]]*$/\1/'
                count=`expr $count + 1`
                continue
        fi
        error=yes
done

[ $count -eq 0 ] && error=yes
[ X"$1" = Xsingle -a $count -gt 1 ] && error=yes

[ $error = no ] && exit 0 || exit 1

Die hier vorgestellten Beispiele können Sie als Tar-Archiv herunterladen.

 

Dateiverweise

File index

Kommandoscripte:

/etc/postfix/config.sh
/etc/postfix/spf-policy.pl
/etc/postfix/spamfilter.sh
/etc/postfix/nondelivery.sh
/etc/postfix/auth_magic.sh
/etc/postfix/match_plain.sh
/etc/postfix/match_wildcard.sh
/etc/postfix/extractmail.sh
/etc/postfix/add_blackwhitelist.sh

Standardeinstellungen:

/etc/postfix/main.cf
/etc/postfix/master.cf
/etc/spamassassin/local.cf

Postfix Hashmaps:

/etc/postfix/map_relay_clientcerts
/etc/postfix/map_transport
/etc/postfix/map_virtual_mailbox
/etc/postfix/map_virtual_uid_gid
/etc/postfix/map_virtual
/etc/postfix/map_alias
/etc/postfix/map_helo_access
/etc/postfix/map_sender_access_fromaddress
/etc/postfix/map_recipient_access_grey
/etc/postfix/map_recipient_access_white
/etc/postfix/map_sender_access_fakelocal
/etc/postfix/map_smtpd_sender_login_maps

Namenslisten:

/etc/postfix/authenticated.lst
/etc/postfix/authorized.lst
/etc/postfix/mailboxes_family.lst
/etc/postfix/mailboxes_hosted.lst
/etc/postfix/sender_domains.lst
/etc/postfix/spamc_individual.lst
/etc/postfix/usernames_family.lst
/etc/postfix/usernames_hosted.lst
/etc/postfix/own_domains.rxp
/etc/postfix/helo_whitelist.rxp
/etc/postfix/special_abuse.rxp
/etc/postfix/map_smtpd_sender_accounts

Awk-Steuerdateien:

/etc/postfix/extract_headers.awk
/etc/postfix/plus_habeas.awk
/etc/postfix/plus_viruscheck.awk
/etc/postfix/suppress_internals.awk

Nutzerspezifische Einstellungen:

/home/myself/.fetchmailrc
/home/myself/.forward
/home/myself/.procmailrc
/home/myself/.spamassassin/user_prefs

 
-rwxr-xr-x    1 500      500          1281 Sep 11 14:42 add_blackwhitelist.sh*
-rwxr-xr-x    1 500      500           370 Sep 11 14:49 auth_magic.sh*
-rw-r--r--    1 500      500           303 Sep 11 14:50 authenticated.lst
-rw-r--r--    1 500      500           536 Sep 11 14:50 authorized.lst
-rwxr--r--    1 500      500          1657 Sep 11 14:43 config.sh*
-rw-r--r--    1 500      500           601 Sep 11 14:50 dynamicmaps.cf
-rw-r--r--    1 500      500           231 Sep 11 14:50 extract_headers.awk
-rwxr-xr-x    1 500      500          1138 Sep 11 14:44 extractmail.sh*
-rw-r--r--    1 500      500           177 Sep 11 14:51 helo_whitelist.rxp
-rw-r--r--    1 500      500           236 Sep 11 14:51 mailboxes_family.lst
-rw-r--r--    1 500      500           208 Sep 11 14:51 mailboxes_hosted.lst
-rw-r--r--    1 500      500          5567 Sep 11 14:51 main.cf
-rw-r--r--    1 500      500           185 Sep 11 14:51 map_alias
-rw-r--r--    1 500      500           249 Sep 11 14:52 map_helo_access
-rw-r--r--    1 500      500          1243 Sep 11 14:52 map_recipient_access_grey
-rw-r--r--    1 500      500           992 Sep 11 14:52 map_recipient_access_white
-rw-r--r--    1 500      500           296 Sep 11 14:53 map_relay_clientcerts
-rw-r--r--    1 500      500           320 Sep 11 14:53 map_sender_access_fakelocal
-rw-r--r--    1 500      500           252 Sep 11 14:53 map_sender_access_fromaddress
-rw-r--r--    1 500      500           346 Sep 11 14:53 map_smtpd_sender_accounts
-rw-r--r--    1 500      500           404 Sep 11 14:53 map_smtpd_sender_login_maps
-rw-r--r--    1 500      500           265 Sep 11 14:54 map_transport
-rw-r--r--    1 500      500          1227 Sep 11 14:54 map_virtual
-rw-r--r--    1 500      500           324 Sep 11 14:54 map_virtual_mailbox
-rw-r--r--    1 500      500           268 Sep 11 14:54 map_virtual_uid_gid
-rw-r--r--    1 500      500          1538 Sep 11 14:54 master.cf
-rwxr-xr-x    1 500      500           495 Sep 11 15:35 match_plain.sh*
-rwxr-xr-x    1 500      500          1955 Sep 11 15:36 match_wildcard.sh*
-rwxr-xr-x    1 500      500         30539 Sep 11 14:46 nondelivery.sh*
-rw-r--r--    1 500      500           290 Sep 11 14:55 own_domains.rxp
-rw-r--r--    1 500      500           940 Sep 11 14:55 plus_habeas.awk
-rw-r--r--    1 500      500           433 Sep 11 14:55 plus_viruscheck.awk
-rw-r--r--    1 500      500           218 Sep 11 14:55 sender_domains.lst
-rw-r--r--    1 500      500          1240 Sep 11 14:57 spamassassin_local_cf
-rw-r--r--    1 500      500           706 Sep 11 14:57 spamc_individual.lst
-rwxr-xr-x    1 500      500          9715 Sep 11 14:47 spamfilter.sh*
-rw-r--r--    1 500      500           257 Sep 11 14:57 special_abuse.rxp
-rwxr-xr-x    1 500      500          7633 Sep 11 14:30 spf-policy.pl*
-rw-r--r--    1 500      500           659 Sep 11 14:57 suppress_internals.awk
-rw-r--r--    1 500      500           383 Sep 11 14:59 user_fetchmailrc
-rw-r--r--    1 500      500            25 Jul 13 19:11 user_forward
-rw-r--r--    1 500      500           667 Sep 11 14:59 user_procmailrc_full
-rw-r--r--    1 500      500           167 Sep 11 14:59 user_procmailrc_null
-rw-r--r--    1 500      500           234 Sep 11 14:59 user_spamassassin_userprefs
-rw-r--r--    1 500      500           675 Sep 11 14:59 usernames_family.lst
-rw-r--r--    1 500      500           180 Sep 11 15:00 usernames_hosted.lst

	

Copyright (c) 2004 Thomas Bez <bez@tedesca.net>. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation. See http://www.gnu.org/copyleft/fdl.html for license conditions.