Monatsarchiv für Januar 2012

 
 

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

#!/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!

neunzehn83.de jetzt auch über IPv6 erreichbar

Dank IPv6 zu Hause über den Tunnelbroker Sixxs konnte ich nun endlich meinen vServer fit für “das neue Internet” machen. Natives IPv6 ist wohl in absehbarer Zeit nicht zu erwarten, da von meinem ISP, der Kabel Baden-Württemberg GmbH, noch kein Termin zur Einführung von IPv6 genannt wurde.

Eigentlich ganz einfach

Alles was wir brauchen ist:

  • eine IPv6-Adresse
  • einen Nameserver, der per IPv6 erreichbar ist
  • DNS AAAA-Records
  • Apache für den Dualstack-Betrieb konfigurieren

Eine IPv6-Adresse

Der vServer ALUMINIUM von Netcup, auf dem neunzehn83.de läuft, ist IPv6 fähig. Per OpenVCP-Panel kann man bis zu 15 einzelne IPv6-Adressen aktivieren. Nach einem Reboot stehen diese zur Verfügung.

~$ sudo ifconfig
eth0      Link encap:Ethernet  HWaddr 00:24:21:b4:xy:ab
      inet addr:188.40.201.74  Bcast:188.40.201.127  Mask:255.255.255.192
      inet6 addr: 2a01:4f8:100:5462:0:bc28:c94a:1/64 Scope:Global
      inet6 addr: 2a01:4f8:100:5462:0:bc28:c94a:2/64 Scope:Global

Nameserver

Für IPv4 schreibt die DeNIC mindestens zwei Nameserver in unterschiedlichen /24 Netzen vor. Bei IPv6 genügt im Moment noch ein einziger. Wie bereits erwähnt, verwende ich die Nameserver von Regworld, meinem Domain-Registrar. Zusätzlich läuft auf dem vServer noch ein Bind Nameserver als zweiter Secondary. Dieser lauscht auch gleichzeitig an der IPv6-Adresse und wird somit als einziger Nameserver für IPv6-Anfragen genutzt.

Die Nameserver von Regworld kommen dafür leider nicht in Frage, da diese (noch) nicht per IPv6 erreichbar sind. Das lässt sich aber relativ leicht lösen, indem man einen Bind-Nameserver an eine oder mehrere IPv6 adressen binded und als reinen Forwarder für die Regworld-Nameserver via IPv4 konfiguriert.

In meinem Fall gebe ich mich aber mal mangels RAM mit nur einem Nameserver für IPv6 zufrieden.

Zu guter Letzt muss noch ein IPv6 GLUE-Record für den Nameserver angelegt werden. Das geht im Domainrobot von Regworld (AutoDNS) genau so wie bei IPv4: Man schreibt den IPv6-Glue einfach per Leerzeichen vom v4-Glue getrennt dahinter:

ns.example.org 1.1.1.1 ::1

Beim DeNIC-Whois sieht das dann so aus:

DNS AAAA-Records

Was der A-Record bei v4 ist der AAAA (Quad-A) Record bei v6. Diesen legen wir jetzt also für die Domain (neunzehn83.de) und die www-Subdomain an. Zusätzlich habe ich eine neue Subdomain ipv6.neunzehn83.de erstellt, die nur ein AAAA aber kein A Record hat, somit also exklusiv über IPv6 erreichbar ist.

@    IN A    188.40.201.74    
@    IN AAAA 2a01:4f8:100:5462::bc28:c94a:1
www  IN A    188.40.201.74
www  IN AAAA 2a01:4f8:100:5462::bc28:c94a:1
ipv6 IN AAAA 2a01:4f8:100:5462::bc28:c94a:1

IPv6 hat übrigens Priorität vor IPv4, weshalb alle Besucher die sowohl mit IPv4 als auch mit IPv6 unterwegs sind, diese Webseite bereits über IPv6 betrachten.

Apache Dualstack

Dual-Stack nennt man das Verfahren, bei dem der Apache sowohl per IPv4 also auch per IPv6 erreichbar ist. Alles was dafür nötig ist, ist den Apachen auch auf der IPv6-Adresse lauschen zu lassen und die Virtualhost-Konfiguration um die IPv6-Adresse zu erweitern.

Listen 188.40.201.74:80
Listen [2a01:4f8:100:5462::bc28:c94a:1]:80

<Virtualhost 188.40.201.74:80 [2a01:4f8:100:5462::bc28:c94a:1]:80>
[...]
</Virtualhost>

Nach einem Apache-Restart ist alles klar.

IPv6 und Froxlor

Dummerweise schreibe ich für diesen Server die Apache-Configs nicht selbst, sondern lass das Froxlor machen. Froxlor ist der Nachfolger von SysCP, unterstützt aber ebenso noch kein Dualstack, weshalb ich selbst an der Source Hand angelegt habe. Alle Änderungen finden sich in diesem Patch, welchen ich auch in einem Thread bei Froxlor zur Diskussion veröffentlicht habe. In der Datenbank muss die Varchar-Länge des ip-Feldes der panel_ipsandports-Tabelle auf 55 vergrößert werden.

ALTER TABLE `panel_ipsandports` CHANGE `ip` `ip` VARCHAR( 55 ) NOT NULL DEFAULT ''

Nun können unter IPs und Ports Dualstack-IPs angeben werden, indem die IPv4 einfach durch ein Leerzeichen von der IPv6-Adresse getrennt wird:

Achtung: der Patch berücksichtigt nur die Apache und Bind Konfiguration. NGIX und Lighttpd sind unverändert und somit wahrscheinlich nicht funktionsfähig.

IPv6 und PHP

Wenn die eigene Webseite auch per IPv6 erreichbar ist, kann das auch Auswirkungen auf PHP-Scripte haben. $_SERVER['REMOTE_ADDR'] beinhaltet dann nämlich die IPv6-Adresse als String und die ist in den meisten Fällen deutlich länger als eine IPv4-Adresse. Das kann ein Problem werden, wenn die IP-Adresse in einer Datenbank als ein VARCHAR(15) gespeichert wird. Hier benötigt man nun ein VARCHAR(40). Auch ip2long() funktioniert nicht mit IPv6-Adressen.

Froxlor Bug: maximale Länge von ServerAlias (Apache)

SysCP bzw. dessen Nachfolger Froxlor kümmert sich hier auf diesem Server um das Erstellen diverser Konfigurationsdateien sowie um die Verwaltung der virtuellen User für E-Mail- und FTP-Zugänge.

Schon vor einiger Zeit habe ich festgestellt, dass bei Verwendung vieler Alias-Domains die erzeugte Apache-Konfiguration fehlerhaft sein kann. Dann nämlich, wenn die ServerAlias-Direktive mehr als 8000 Zeichen hat. Scheinbar hat noch keiner zuvor so viele Alias-Domains einer Hauptdomain hinzugefügt. 8000 Zeichen hört sich auch viel an, doch wenn pro Domain jeweils noch die www-Subdomain hinzukommt, reichen bereits ca. 150 Domains um dieses Limit zu erreichen.

Der Apache vHost-Container unterstützt mehrere ServerAlias-Direktiven, so dass bevor die 8000 Zeichen erreicht werden einfach ein neuer ServerAlias-Eintrag erzeugt werden sollte. Die Datei cron_tasks.inc.http.10.apache.php in scripts/jobs ist für das Erstellen der Apache-Konfigurationsdateien verantwortlich und mit wenigen Zeilen auf dieses Verhalten anpassbar (Patch).

Da ich nicht nach jedem Update daran denken möchte, diese Änderung einzuspielen, habe ich bei Froxlor mal ein Ticket erstellt und diesen Patch angehängt. Vielleicht schafft er es ja in die 0.9.27 :)