neunzehn83.de

Ein Mann, ein Blog, kein Plan.

Postfix: reject_sender_login_mismatch pro SASL username

Die Postfix-Konfiguration kann einem einige graue Haare verursachen, so wie bei dieser Problemstellung hier. Ich habe manchmal den Eindruck, dass Postfix gar nicht dazu entwickelt worden ist, E-Mails zu senden und zu empfangen, aber dennoch so flexibel ist, dass es sich irgendwie zu einem smtpd konfigurieren lässt..

Prinzipiell erlaubt Postfix mit permit_sasl_authenticated jedem SASL-authentifizierten Benutzer beliebige FROM E-Mail-Adressen zu verwenden. Mit der Option reject_sender_login_mismatch wird dieses Verhalten verboten und überprüft ob der SASL-Username bzw. die damit verknüpften Mail-Adressen und -Aliase mit dem FROM der E-Mail übereinstimmt.

Wenn man die Option reject_sender_login_mismatch nun nur auf bestimmte SASL-Usernamen beschränken will, wird es kompliziert. Das lässt sich nämlich nicht so einfach in der Postfix-Konfiguration (main.cf) einstellen, da sämtliche Lookup-Tables nicht den SASL-Username als Index benutzen.

Was wir wollen

Der User foo@example.org soll ausschließlich E-Mails mit foo@example.org als Absender senden können.
Der User bar@example.org soll hingegen beliebige Absenderadressen wählen können.

Das geht nur über einen Policy Daemon: Hört sich kompliziert an, ist aber im einfachsten Fall ein simples Script, dem von Postfix unter anderem der SASL-Username übergeben wird.

smtpd Policy Daemon mit PHP

Das funktioniert mit anderen Scriptsprachen wie Python oder Perl natürlich ähnlich, und wahrscheinlich sogar besser, wir machen das aber mal, weil wir es können, mit PHP!

Unser PHP Policy Daemon soll unter /usr/local/bin/policyd liegen (chmod +x nicht vergessen, chroot beachten). Die Parameter von Postfix kommen über STDIN. Das Ergebnis geben wir ganz normal über echo an Postfix zurück. Als Ergebnis kommt ACCEPT oder REJECT in Frage, oder aber jede bekannte Postfix UCE restriction, also auch reject_sender_login_mismatch! (Siehe dazu auch access(5))

/usr/local/bin/policyd

#!php@1
#!/usr/bin/php
<?php
$allow_relay = array(
    'bar@example.org',
);

$fp = fopen('php://stdin', 'r');
while (true) {
    $line = fgets($fp, 512);
    if (strpos($line, '=')) {
        list($k, $v) = explode('=', trim($line));
        $env[$k] = $v;
    }  
    if($line == "\n") break;
}
fclose($fp);

if ($env['sasl_username']) {
    if (!in_array($env['sasl_username'], $allow_relay)) {
        echo "action=reject_sender_login_mismatch\n\n";
        die();
    }
}
echo "action=DUNNO\n\n";

Simple String-Funktionen: Ist der sasl_username nicht in unserem Array mit erlaubten Relay-Logins, wird die reject_sender_login_mismatch UCE restriction zurück gegeben. Das FROM muss nun also mit sasl_username übereinstimmen. Im anderen Fall wird ein "DUNNO" zurückgegeben, was Postfix dazu veranlasst mit der nächsten Regel weiter zu machen. reject_sender_login_mismatch gilt dann für diesen sasl_username also nicht.

Wichtig ist hier, den STDIN mit fgets zeilenweise zu lesen und aufzuhören sobald eine leere Zeile empfangen wird. Versucht man den gesamten STDIN über file_get_contents zu holen, beträgt der Timeout 60 Sekunden bis das Script fortgeführt wird und Postfix das Ergebnis übergeben werden kann.

Neben dem sasl_username kommen über STDIN noch weitere Parameter, wie z.B. die Absender-Adresse, Empfänger-Adresse oder die Client-IP. Eine vollständige Übersicht gibt es hier: http://www.postfix.org/SMTPD_POLICY_README.html#protocol

Postfix Konfiguration

master.cf

policy  unix    -       n       n       -       0       spawn
    user=nobody argv=/usr/local/bin/policyd

main.cf

smtpd_sender_restrictions = permit_mynetworks,
    check_policy_service unix:private/policy,
    permit_sasl_authenticated,
    [..]   

~$ /etc/init.d/postfix restart

Das wars!

Geschrieben am Sonntag, 29. Januar 2012 und abgelegt unter Linux.