Debian Wheezy, Postfix und SRS

7. August, 2013

Ich betreibe für vom.tc einen Postfix-Mailserver und habe dort vor ewigen Zeiten SPF-Prüfung aktiviert. Unter wheezy ist das eher langweilig:

apt-get install postfix-policyd-spf-perl


Danach die master.cf ergänzen um

spfcheck  unix  -       n       n       -       0       spawn
    user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl


und main.cf um

smtpd_recipient_restrictions =
        permit_mynetworks
        reject_unauth_destination
        check_policy_service unix:private/spfcheck

Wer sich mit SPF auseinandergesetzt hat, weiß, dass es zu Problemen bei der Weiterleitung von E-Mails kommen kann.
Beispiel: paul@example.com leitet seine E-Mails an paul@example.net weiter. Schickt nun peter@example.net eine E-Mail an paul@example.com wird der Mailserver von example.com die E-Mail empfangen und den Empfänger auf paul@example.net ändern. Nutzt example.net SPF wird er diese E-Mail ablehnen, da der Absender peter@example.net ist und example.com keine E-Mails im Namen von example.net verschicken darf. Works As Designed. Dough.

Lösung: Sender Rewriting Scheme (kurz SRS)

Die Idee bei SRS ist es die ursprüngliche Absenderadresse in eine Adresse zu kodieren, für die der Mailserver Mails laut SPF verschicken darf. Dabei wird ein Hash und ein Zeitstempel ergänzt, um SPF-gültigen SPAM zu vermeiden und gleichzeitig eventuelle Fehlermeldungen an den ursprünglichen Absender weiterleiten zu können.

Etwas googlen hat mich dann an diese Orte gebracht:

Für mich war recht schnell klar, dass ich möglichst viel mit den Boardmitteln machen möchte. Nachdem die libsrs2-0 mit squeeze scheinbar endet, habe ich mir das Paket srs genauer angeguckt und entlang des Quelltextes oben zwei eigene Wrapper „srs_encode.sh“ und „srs_decode.sh“ geschrieben. Deren Verwendung möchte ich hier einmal skizzieren; sämtliche Dateien habe ich im Gist 6163083 hinterlegt.

1. Schritt: Das srs-Tool ans laufen bekommen

apt-get install srs
– anschließend müssen wir uns ein Secret für das Tool ausdenken. Meine Vorredner haben PHP verwendet um gleich mehrere Secrets zu erstellen, da aber das Tool nur das erste Secret zum Kodieren und alle zum Dekodieren nutzt, ist es in meinen Augen eher eine Sicherheitslücke als ein Vorteil bei einem einzelnen Server mehrere Zeilen zu verwenden. Bei mehreren Servern könnte man die Reihenfolge der Secrets allerdings durchtauschen, um aus der Adresse ableiten zu können, wo sie erstellt wurde.
Jede Art einen zufälligen String zu erzeugen ist gut, eine Methode, die einen sicheren Zufallszahlengenerator verwendet, besser. Ich habe
makepasswd --count 1 --minchars 42 --maxchars 84 >/etc/postfix/srs.secrets
verwendet (makepasswd ist aus dem gleichnamigen Paket). Anschließend per
chmod u=r,go= /etc/postfix/srs.secrets
sicher gehen, dass niemand das Secret liest.

Ein erster Test:
srs --alias=some.tld --secretfile=/etc/postfix/srs.secrets mail@example.com
> SRS0=fFqU=RU=example.com=mail@some.tld
srs --reverse --secretfile=/etc/postfix/srs.secrets SRS0=fFqU=RU=example.com=mail@some.tld
> mail@example.com

Es ist durchaus gängig eine eigene Sub-Domain für SRS-Adressen zu verwenden; einfach als --alias= beim Kodieren angeben. Das Dekodieren liest nur den Text links des @ und klappt bei euch hoffentlich nicht für mein Beispiel hier 😉

2. Wrapper Skripte

Die Skripte waren etwas tricky, da srs z.B. beim Dekodieren von E-Mail-Adressen mit einer anderen Groß-/Kleinschreibung im Hash zusätzlich zur dekodierten E-Mail-Adresse eine Warnung ausgibt. Um die beiden Skripte zu verwenden, ladet euch einfach srs-decode.sh und srs-encode.sh runter und legt sie unter /opt/srs/ ab (den Pfad werde ich gleich verwenden).

srs-encode.sh müsst ihr noch so anpassen, dass eure lokalen Domains durch den regulären Ausdruck erkannt werden und nicht umgewandelt werden (erzeugt nur Verwirrung) und die richtige Domain hinter --alias= steht.

Die Wrapper verwenden außer srs noch das Paket liburi-perl, das aber vermutlich bei den meisten ohnehin schon installiert ist.

Auch hier lohnt ein kleiner Test. Auf jede Eingabe der Form „get some@mail“ solltet ihr eine Antwort bekommen. Fängt die Antwortzeile mit 200 an, so wird Postfix die Adresse ändern, bei 400 und 500 wird sie nicht geändert. Die E-Mailadressen werden „URL-Encoded“ übertragen – wundert euch also nicht über %40 statt @.

3. Als Postfix-Tauglichen Dienst betreiben

Ein weiteres Paket, dass ohnehin auf den meisten Servern vorhanden ist, ist openbsd-inetd. Durch anfügen dieser drei Zeilen an die /etc/inetd.conf und Aufrufen von
service openbsd-inetd restart
machen wir aus unseren beiden Wrapper-Skripten per TCP erreichbare Dienste. Auch das sollten wir besser testen:

postmap -q mail@example.com tcp:10001
> SRS0=fFqU=RU=example.com=mail@some.tld
postmap -q SRS0=fFqU=RU=example.com=mail@some.tld tcp:10002
> mail@example.com

Wenn hier alles funktioniert (besser mehr Adressen testen!) können wir im letzten Schritt die Postfix-Konfiguration anpassen:

4. Postfix konfigurieren

Es gibt ein paar Adressen, die wir auf keinen Fall ändern wollen. Diese tragen wir in die /etc/postfix/pfix-srs.norewrite ein. Eine Vorlage findet ihr unten, bzw. hier
Immer, wenn wir die Datei ändern, rufen wir anschließend
postmap /etc/postfix/pfix-srs.norewrite
auf, um die von Postfix gelesene /etc/postfix/pfix-srs.norewrite.db zu aktualisieren.

In Postfix‘ main.cf kommen dann diese Zeilen, die dafür Sorgen, dass nur in der Kommunikation nach außen die umgeschriebenen Adressen verwendet werden. Ein
service postfix reload
später schlägt die Stunde der Wahrheit.

Getestet werden sollte das Weiterleiten einer extern abgeschickten E-Mail genau so, wie das Senden an eine SRS-Adresse und der allgemeine Betrieb (E-Mail von/an lokal). Wie immer ist der Teufel ein Eichhörnchen.

Alle Dateien auf einen Blick

Oh, eine Datei fehlt natürlich: die /etc/postfix/srs.secrets, die in Schritt 1 erstellt wurde 😉

Danke fürs lesen!

Ich freue mich über Feedback und auch um schönere Wrapper-Skripte z.B. in Perl.

11 Antworten zu “Debian Wheezy, Postfix und SRS”

  1. Chris M sagt:

    Super Zusammenfassung! Läuft wie es soll.

  2. Roman sagt:

    Hallo Matthias,

    danke für diese Seite, die Anleitung ist gut geschrieben und hat schnell zum Erfolg geführt.

    Einzig ein kleines Problem ist geblieben: Sofern die mails via aliases weitergeleitet werden zieht irgendwie das Rewriting nicht. Ist dir das ebenfalls aufgefallen, hast vielleicht sogar eine Idee zur Lösung?

    Gruß
    Roman

    • TheConstructor sagt:

      Hallo Roman,

      tatsächlich habe ich dieses Schema auch schon mit Aliasen genutzt. Bei mir hat das geklappt, allerdings kommt es hier mit Sicherheit auf die Reihenfolge an und ich würde dir empfehlen dort einmal zu schauen.
      Wenn du mir eine Fehlermeldung geben kannst, werde ich versuchen das selber noch einmal nach zu vollziehen.

      Grüße

      Matthias

      • Roman sagt:

        Hi Matthias,
        das mit den Aliasen war offensichtlich ein Problem meiner recht speziellen Konfiguration und funktioniert inzwischen.

        Jetzt hab ich nur noch ein Problem mit Bounces – ich bounce selbst in postfix mittels header_checks. Diese Bounces laufen dann allerdings bei der SRS-Adresse ein… es findet kein decode statt. Ich schätze, dass es mit Bounces von „ausserhalb“ anders wäre – da ich aber adhoc nicht weiß wie ich welche provozieren kann, hab ich das nicht getestet.

        Werden bei Dir denn eigene Bounces korrekt an den ursprünglichen mailserver übermittelt?

        Gruß
        Roman

        • TheConstructor sagt:

          Hallo Roman,

          bei mir werden Bounces ganz normal an den Absender geschickt. Möglicherweise werden für header_checks da noch Verrenkungen gebraucht, da sender_canonical_maps bereits aktiv war(?). Normalerweise sorgt recipient_canonical_maps dafür, dass Bounces korrekt ankommen.

          Im einfachsten Fall hast du bei encode und decode unterschiedliche Secret-Dateien. Ansonsten musst du fürchte ich jemand mit mehr Postfix-Kentnissen fragen.

          Grüße

          Matthias

          • Roman sagt:

            Die secret-daten sind es nicht… ich habe das Gefühl, dass das dekodieren nicht stattfindet, weil der bounce nicht via smtp reinkommt (da er ja intern erzeugt wurde).
            Ich prüf das nochmal genauer und geb nochmal feedback, wenn ich mehr weiß 😉

            Gruß
            Roman

          • Roman sagt:

            Und Berichtigung: keine Ahnung was mit dieser einen Mail los war, aber seither werden alle Nachrichten korrekt „gebounced“. 🙂

  3. Christian sagt:

    Danke für den Post, funktioniert 1a!

  4. denterra sagt:

    Hallo Matthias,

    vielen Dank für die tollen Skripte und somit auch für die Ausarbeitung der Codierung für Postfix. Selber hätte das wahrscheinlich einige Zeit gedauert.
    Die Skripte sind nun in abgewandelter Form schon seit 1 1/2 Jahren im produktiven Einsatz, und dank multi-threaded Zugriff durch inetd sehr performant unterwegs.
    Da du hiermit die Lücke für SRS in Postfix am besten und elegantesten gelöst hast hast du dir eine kleine Spende verdient. Führe doch einen PayPal Spenden Button ein 😉

    MfG

    • TheConstructor sagt:

      Danke denterra!

      Ich freue mich, dass du spenden würdest, aber im Moment reichen mir die aufmunternden Kommentare. Ich freue mich natürlich auch über Empfehlungen, wie meine Lösung verbessert werden kann 😉

      Falls doch ganz dringend Geld den Besitzer wechseln soll, habe ich bereits einen Flattr-Button unter dem Beitrag.

      Grüße

      TC

      • denterra sagt:

        Hallo Matthias,

        habe einen kleinen Tipp zur dynamischen Anpassung des Skriptes an mehrere Domains (sofern via vmail und MySQL gearbeitet wird), sowie zur Einbindung eines Configfiles:


        #!/bin/bash

        DIR=`dirname "$(readlink -f "$0")"`

        test -f $DIR/srs.conf && . $DIR/srs.conf

        while read line; do
        email="${line#get }"
        email="$(perl -MURI::Escape -e 'print uri_unescape($ARGV[0]);' "${email}")"
        if [[ "${line}" == "${email}" ]]; then
        echo "400 Empty input"
        else
        unset t_std t_err t_ret
        domain=`echo "${email}" | awk -F '@' '{print $2}'`
        id=`mysql --defaults-file=${mycnf} -ss -e "SELECT id FROM vmail.domains WHERE domain='${domain}' LIMIT 1" 2> /dev/null`
        if [ -n "${id}" ]; then
        t_err="${email}"
        t_ret=1
        else
        eval "$( srs --alias="${alias}" --hashlength="${hashlength}" --secretfile="${secrets}" "${email}" 2> >(t_err=$(cat); typeset -p t_err) > >(t_std=$(cat); typeset -p t_std); t_ret=$?; typeset -p t_ret )"
        fi
        if [[ $t_ret -eq 0 ]]; then
        echo "200 $(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "${t_std}")"
        else
        echo "500 $(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "${t_err}")"
        fi
        fi
        done

        exit 0

        Somit kann das Skript auf mehreren Mailservern laufen, komplett ohne Anpassung des Skriptes selber.
        Es muss lediglich die Config angepasst werden.

        MfG

Schreibe eine Antwort

XHTML: Folgende Tags sind erlaubt: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>


Canonical URL by SEO No Duplicate WordPress Plugin