In der Plesk-Hölle: qmail-smtpd, SSL … und spamdyke

Wenn man etwas weiter hinter die Kulissen schaut, findet man manchmal ganz erstaunliche Dinge. Heute soll es mal um verschlüsseltes SMTP mit dem qmail-Paket von Plesk gehen. Für Verschlüsselung gibt es, um das ganz kurz zu erläutern, zwei Verfahren: TLS und SSL. TLS bedeutet: Man verbindet sich mit dem unverschlüsselten Port (normalerweise 25), der Server annonciert, dass er STARTTLS beherrscht, der Client sendet jenes STARTTLS, und die Verbindung ist verschlüsselt. Alternativ benutzt man SSL: Hier stellt der Server einen separaten Port bereit (normalerweise 465), auf dem dann direkt eine verschlüsselte Verbindung entgegengenommen wird.

Der aufmerksame Leser wird bereits eins ahnen: SSL über einen separaten Port kann man in der Art eines Wrappers um beliebige Dienste „drumherumstricken“, z.B. mit stunnel oder ucspi-ssl, ohne dass man den Dienst selbst anpassen muss. Dass ein Dienst hingegen ein Schlüsselwort wie STARTTLS versteht und daraufhin die Verschlüsselung einschaltet – da dürfte auf der Hand liegen, dass das nur mit einem Patch geht. Für qmail gibt es dafür seit langem einen etablierten Patch von Frederik Vermeulen, der (unter anderem) qmail-smtpd beibringt, auf STARTTLS zu reagieren. Im Vorbeigehen unterstützt er auch noch die traditionelle Variante mit dem separaten Port – ganz ohne Wrapper: Es reicht aus, die Umgebungsvariable SMTPS zu setzen, bevor qmail-smtpd aufgerufen wird, und das war’s.

Nun schauen wir uns mal an, wie das bei Plesk aussieht. Ich habe die Konfigurationsdateien jeweils auf das Wesentliche gekürzt, also bitte nicht wundern. Erstmal für den Standard-SMTP-Port 25:

# cat /etc/xinetd.d/smtp_psa
service smtp
{
  [...]
  server      = /var/qmail/bin/tcp-env
  server_args = /var/qmail/bin/relaylock /var/qmail/bin/qmail-smtpd [...]
}

… und schließlich für den Standard-SMTPS-Port 465:

# cat /etc/xinetd.d/smtps_psa
service smtps
{
  [...]
  server      = /var/qmail/bin/tcp-env
  server_args = /var/qmail/bin/relaylock /var/qmail/bin/qmail-smtpd [...]
}

Der xinetd erkennt an der service-Bezeichnung den numerischen Port, in dem er ihn über /etc/services auflöst. So weit, so gut.

Nun stellt sich aber doch eine Frage: Woher weiß qmail-smtpd im zweiten Fall denn, dass es bitte SSL sprechen möge und nicht Plaintext? Plesk arbeitet hier genauso mit dem etablierten TLS-Patch, der eigentlich verlangt, dass die Umgebungsvariable SMTPS gesetzt sein muss. Die wird hier aber doch gar nicht gesetzt..?!

Des Rätsels Lösung ist, dass Plesk hier ganz gewaltig trickst. Nicht etwa, dass es ein Leichtes gewesen wäre, in der /etc/xinet.d/smtpd_psa kurzerhand env = SMTPS=1 einzutragen: Nein, das wäre doch viel zu nachvollziehbar gewesen.

Stattdessen patcht Plesk. Und patcht. Und patcht. Details dazu finden sich in der Plesk-Knowledgebase. Wir schnappen uns das erste der Patche-Archive und schauen mal rein. Darin findet sich ein Haufen Patches mit so klangvollen Namen wie patch-pb, patch-pe oder auch patch-ps. Oder, und darum geht’s: patch-pu-tls. Darin steckt so eine Art Variante des etablierten qmail-TLS-Patches, „natürlich“ bereinigt um jegliche Kommentare oder Dokumentation. Und ganz am Ende, da findet sich plötzlich ein Teil, den es im Original nicht gibt, nämlich diesen hier:

--- ./tcp-env.c.orig    Thu Feb 20 11:33:43 2003
+++ ./tcp-env.c Thu Feb 20 11:37:05 2003
@@ -70,6 +70,9 @@
    temp[fmt_ulong(temp,localport)] = 0;
    if (!env_put2("TCPLOCALPORT",temp)) die();
 
+   if (localport == 465)
+      if (!env_put2("SMTPS","true")) die();
+
    byte_copy(&iplocal,4,&salocal.sin_addr);
    temp[ip_fmt(temp,&iplocal)] = 0;
    if (!env_put2("TCPLOCALIP",temp)) die();

Dass qmail-smtpd unter Plesk also auf „magische“ Weise plötzlich SSL spricht, liegt daran, dass das vorgeschaltete tcp-env die Variable SMTPS kurzerhand setzt, sobald es feststellt: Oh, ich laufe unter Port 465! Unnötig zu sagen, dass Plesk die man page von tcp-env nicht angerührt hat – die verrät von diesem Verhalten nichts. Da muss man sich dann auch nicht wundern, wenn in Foren Fragen wie diese auftauchen – und unbeantwortet bleiben (Hand aufs Herz, die Frage ist von 2007, und in dem Forum müsste ich mich auch erstmal registrieren, um antworten zu können):

When i copy smtps_psa to another port / service, SSL does not work. Is SSL hard-coded somewhere in Plesk’s qmail ? For instance : i would like to run smtps on port 665

Aber nun gut, lassen wir das für einen Moment so stehen und wenden uns dem Thema zu, das mich erst auf diese ganze Thematik gebracht hat, und das für mich vor allem deshalb so ein „Aufreger“ ist, weil es dazu Howtos über Howtos gibt und praktisch alle in puncto SSL falsch sind – wahrscheinlich schreiben alle voneinander ab. Hier findet sich in zig Howtos der Hinweis, dass Spamdyke auf Plesk-Systemen in /etc/xinetd.d/smtp_psa und /etc/xinetd.d/smtps_psa eingetragen werden muss. Das ist so auch nicht ganz falsch – allerdings geben fast alle Howtos an, dass man dort das Gleiche eintragen soll; viele liefern sogar konkrete Konfigurationsbeispiele, die aber vor Veröffentlichung definitiv nie getestet worden sein können. Dann hätte nämlich auffallen müssen, dass sie in der Realität überhaupt nicht funktionieren – nicht mit SSL.

Und so finden wir falsche Howtos im offiziellen Plesk-Forum, im inoffiziellen Plesk-Forum, im Serversupport-Forum, nochmal dort, im Howto des RootForums, in der Anleitung von huschi.net, bei Blue Orix oder bei Haggybear, um nur eine kleine Auswahl an Quellen zu nennen. Hier und da (aber Ausnahmen bestätigen ja bekanntlich die Regel) findet sich auch jemand, der es richtig macht, zum Beispiel The BOFH. Andere kriegen es auch hin, so wie Alexander Koch, wenngleich auf ganz andere Weise (nämlich, in dem gar nicht von der SMTPS-Funktionalität Gebrauch gemacht wird, sondern in dem stunnel als Wrapper eingesetzt wird – was aber fraglos auch eine legitime Lösung ist, nur eben eine aufwendige).

Im Endeffekt laufen alle Howtos darauf hinaus, dass man spamdyke vor den Aufruf von qmail-smtpd setzen soll. Aus

server_args = [...] /var/qmail/bin/qmail-smtpd [...]

soll also werden:

server_args = [...] /usr/local/bin/spamdyke -f /etc/spamdyke.conf \
              /var/qmail/bin/qmail-smtpd [...]

Und genau da liegt der Knackpunkt. Die TCP-Verbindung kommt jetzt ja nun nicht mehr bei qmail-smtpd an, sondern bei spamdyke. Und so schön und gut es auch ist, dass qmail-smtpd eine Umgebungsvariable namens SMTPS interpretiert, und so tricky und gruselig es ist auch ist, dass Plesks tcp-env diese Umgebungsvariable heimlich setzt: spamdyke hat deswegen noch lange keine Ahnung davon – und spricht deshalb auch auf Port 465 schlicht und einfach Plaintext. Warum um alles in der Welt sollte es auch etwas anderes tun?

Praktischerweise kann man spamdyke sagen, dass es SSL sprechen soll. Nämlich, in dem man tls-level=smtps setzt anstelle des Defaults tls-level=smtp. Nur tut das keins der oben genannten Howtos! Es kann also überhaupt nicht funktionieren.

Die Abhilfe ist dann letztlich trivial. Entweder man legt sich eine zweite Konfigurationsdatei für spamdyke an, zum Beispiel eine spamdyke-smtps.conf, die eine Kopie des Originals ist, aber tls-level=smtps beinhaltet. In der xinetd-Konfiguration sieht das dann so aus:

# cat /etc/xinetd.d/smtps_psa
service smtps
{
  [...]
  server      = /var/qmail/bin/tcp-env
  server_args = /var/qmail/bin/relaylock \
                /usr/local/bin/spamdyke -f /etc/spamdyke-smtps.conf \
                /var/qmail/bin/qmail-smtpd [...]
}

Oder man hat verständlicherweise keine Lust, eine zweite Konfigurationsdatei zu pflegen, und setzt die Angabe direkt als Parameter mit in den Programmaufruf ein:

# cat /etc/xinetd.d/smtps_psa
service smtps
{
  [...]
  server      = /var/qmail/bin/tcp-env
  server_args = /var/qmail/bin/relaylock \
                /usr/local/bin/spamdyke -f /etc/spamdyke.conf --tls-level=smtps \
                /var/qmail/bin/qmail-smtpd [...]
}

So, und damit wir jetzt zum Schluss alle nochmal herzhaft lachen können:

Alle, wir alle inklusive mir, hätten eigentlich nur die ganz offizielle INSTALL.txt von spamdyke lesen müssen. Dort lesen wir schnöde vier Zeilen:

   Plesk users can also use spamdyke for their SMTPS connections by adding it to
   the /etc/xinetd.d/smtps_psa file.  spamdyke's configuration in that file will
   need to include the options "tls-level" (set to "smtps") and
   "tls-certificate-file".

Das wär’s auch schon gewesen. Aber andererseits: Ansonsten wäre ich nie darauf gekommen, wie Plesk das mit dem SSL eigentlich macht. Insofern hat sich’s doch gelohnt …