Syndicate
Site (RSS, Atom)
Contact
Weblog status
Total entries: 78
Last entry: 2022-10-16 13:52:24
Last updated: 2022-10-16 14:12:58
powered by vim, bash, cat, grep, sed, and nb 3.4.2

2014-10-12 21:22:10

Perfect Forward Secrecy (PFS) für Postfix

Dieser Blog-Eintrag beschreibt die Konfiguration von Postfix, um PFS zu nutzen. PFS steht für "Perfect Forward Secrecy", was nachträgliches Entschlüsseln von (aufgezeichneten) Verbindungen erschwert. Im deutschsprachigen Raum wird meist dieser Begriff verwendet, während im englischsprachigen Raum häufig nur FS / Forward Secrecy als Bezeichnung dafür verwendet wird.

Seit Beginn des NSA-Skandals 2013 ist das Interesse an Verschlüsselung stark gewachsen. So gibt es seitdem auch viele Anleitungen und Tipps. Dieser Blogpost gehört zu einer kleinen Serie von Posts zu dem Thema. Ein Anlass dazu waren unter anderem viele unkorrekte Aussagen rund um FS und die Konfiguration diverser Software dafür. Um es hoffentlich besser zu machen, werde ich anhand von konkreten Beispielen meine Konfigurationsvorschläge prüfen und auch Belege und Referenzen zu einigen Aussagen liefern. Als Testumgebungen dienen zwei Server mit RHEL / Centos 5 und 6 (postfix-2.3.3/openssl-0.9.8e bzw. postfix-2.6.6/openssl-1.0.1e). Weiterhin setzt dieser Artikel Grundlagen der Systemadministration voraus. Es ist auch keine Anleitung / Konfiguration, die per Copy & Paste übernommen werden kann. Anleitungen aus dem Netz sollte man generell nicht unter Ausschaltung der grauen Zellen übernehmen. ;-)

Der Server mit RHEL 6 dient als SMTP-Server und -Client und der RHEL-5-Server nur als Client zum Testen. Auch Postfix auf RHEL 5 kann für Forward Secrecy konfiguriert werden. Hier dient der Server aber jedoch als "veralteter" Client mit OpenSSL 0.98, um speziell die Abwärtskompatibilität zu prüfen. Der Server ist zu Beginn vorbereitend konfiguriert. Neben einer Standardkonfiguration sind bereits Zertifikate erstellt und eingebunden (für smtp und smtpd).

Zum Testen der Verbindung kann man ein kleines Testscript nutzen, das TLS-Verbindungen anhand einer Liste von Ciphers prüft. Dazu müssen wir erst einmal wissen, was bei Postfix der Default für die Liste von Ciphers ist:

[postfix-2.3.3]$ grep -rh DEF.*D_CIPH src/global/*params.h
#define DEF_SMTPD_TLS_MAND_CIPH "medium"
#define DEF_SMTP_TLS_MAND_CIPH  "medium"
#define DEF_LMTP_TLS_MAND_CIPH  "medium"

[postfix-2.3.3]$ grep -r DEF.*CLIS src/global/*params.h \
  |cut -d" " -f2-
DEF_TLS_HIGH_CLIST      "!EXPORT:!LOW:!MEDIUM:ALL:+RC4:...
DEF_TLS_MEDIUM_CLIST    "!EXPORT:!LOW:ALL:+RC4:@STRENGTH"
DEF_TLS_LOW_CLIST       "!EXPORT:ALL:+RC4:@STRENGTH"
DEF_TLS_EXPORT_CLIST    "ALL:+RC4:@STRENGTH"
DEF_TLS_NULL_CLIST      "!aNULL:eNULL+kRSA"

"medium" ist also für smtp und smtpd der Default und es bedeutet "!EXPORT:!LOW:ALL:+RC4:@STRENGTH" als String zur Erzeugung der Cipher List mit OpenSSL. Bei RHEL 6 / postfix 2.6 finden wir ähnliche Defaults (auf dem laufenden System kurz und schmutzig geprüft):

# strings /usr/sbin/postconf|grep EXPORT
ALL:!EXPORT:+RC4:@STRENGTH
ALL:!EXPORT:!LOW:+RC4:@STRENGTH
ALL:!EXPORT:!LOW:!MEDIUM:+RC4:@STRENGTH

Als erstes "erlauben" wir auf dem Server TLS durch Setzen von smtpd_tls_security_level und smtp_tls_security_level auf "may". Mit der CipherSuite rufen wir dann ssltest.sh auf dem Client auf und erhalten:

$ SSLCipherSuite=$(
> openssl ciphers '!EXPORT:!LOW:ALL:+RC4:@STRENGTH'|tr ":" " "
> ) ./ssltest.sh 127.0.0.1 8025 smtp
ADH-AES256-SHA: YES
DHE-RSA-AES256-SHA: YES
DHE-DSS-AES256-SHA: NO (ssl handshake failure)
AES256-SHA: YES
KRB5-DES-CBC3-MD5: NO (no ciphers available)
KRB5-DES-CBC3-SHA: NO (no ciphers available)
ADH-DES-CBC3-SHA: YES
EDH-RSA-DES-CBC3-SHA: YES
EDH-DSS-DES-CBC3-SHA: NO (ssl handshake failure)
DES-CBC3-SHA: YES
DES-CBC3-MD5: NO (ssl handshake failure)
ADH-AES128-SHA: YES
DHE-RSA-AES128-SHA: YES
DHE-DSS-AES128-SHA: NO (ssl handshake failure)
AES128-SHA: YES
RC2-CBC-MD5: NO (ssl handshake failure)
KRB5-RC4-MD5: NO (no ciphers available)
KRB5-RC4-SHA: NO (no ciphers available)
ADH-RC4-MD5: YES
RC4-SHA: YES
RC4-MD5: YES
RC4-MD5: YES

(Auf Port 8025 ist ein Tunnelendpunkt zu Port 25 des RHEL-6-Servers.)

Damit stellen wir folgende Punkte fest:

  1. Verschlüsselung ist möglich (siehe oben, "may")
  2. Client und Server handeln ADH-AES256-SHA aus
  3. diese Cipher bietet kein FS
  4. bereits die zweite Cipher bietet FS
  5. unsichere Verschlüsselung bieten beide an (RC4-MD5)

Laut Wietse Venema bzw. postfix.org unterstützt Postfix schon out-of-the-box Forward Secrecy:

The Postfix >= 2.2 SMTP server supports forward secrecy in its default configuration. If the remote SMTP client prefers cipher suites with forward secrecy, then the traffic between the server and client will resist decryption even if the server's long-term authentication keys are later compromised.

Anleitungen und Tipps, die etwas anderes behaupten, sind daher mit Vorsicht zu genießen. Jetzt schauen wir mal kurz, welche Cipher der Server mit sich selbst aushandeln würde:

# SSLCipherSuite=$(
> openssl ciphers '!EXPORT:!LOW:ALL:+RC4:@STRENGTH'|tr ":" " "
> ) ./ssltest.sh 127.0.0.1 25 smtp
ECDHE-RSA-AES256-GCM-SHA384: NO (ssl handshake failure)
ECDHE-ECDSA-AES256-GCM-SHA384: NO (ssl handshake failure)
ECDHE-RSA-AES256-SHA384: NO (ssl handshake failure)
ECDHE-ECDSA-AES256-SHA384: NO (ssl handshake failure)
ECDHE-RSA-AES256-SHA: NO (ssl handshake failure)
ECDHE-ECDSA-AES256-SHA: NO (ssl handshake failure)
DHE-DSS-AES256-GCM-SHA384: NO (ssl handshake failure)
DHE-RSA-AES256-GCM-SHA384: YES
DHE-RSA-AES256-SHA256: YES
DHE-DSS-AES256-SHA256: NO (ssl handshake failure)
DHE-RSA-AES256-SHA: YES
...
RC4-SHA: YES
RC4-MD5: YES
RC4-MD5: YES
PSK-RC4-SHA: NO (no ciphers available)
KRB5-RC4-SHA: NO (no ciphers available)
KRB5-RC4-MD5: NO (no ciphers available)

Hier einigt sich der Server "mit sich selbst" auf DHE-RSA-AES256-GCM-SHA384 und somit auf eine "ephemeral" Cipher mit Forward Secrecy. Aber wir haben ja einen Client mit RHEL 5. Für den nächsten Test setzen wir smtpd_tls_eecdh_grade auf "strong", so wie es teilweise vorgeschlagen wird. Damit erhalten wir...

ADH-AES256-SHA: YES
DHE-RSA-AES256-SHA: YES
DHE-DSS-AES256-SHA: NO (ssl handshake failure)
AES256-SHA: YES
KRB5-DES-CBC3-MD5: NO (no ciphers available)
KRB5-DES-CBC3-SHA: NO (no ciphers available)
ADH-DES-CBC3-SHA: YES
EDH-RSA-DES-CBC3-SHA: YES
EDH-DSS-DES-CBC3-SHA: NO (ssl handshake failure)
DES-CBC3-SHA: YES
DES-CBC3-MD5: NO (ssl handshake failure)
ADH-AES128-SHA: YES
DHE-RSA-AES128-SHA: YES
DHE-DSS-AES128-SHA: NO (ssl handshake failure)
AES128-SHA: YES
RC2-CBC-MD5: NO (ssl handshake failure)
KRB5-RC4-MD5: NO (no ciphers available)
KRB5-RC4-SHA: NO (no ciphers available)
ADH-RC4-MD5: YES
RC4-SHA: YES
RC4-MD5: YES
RC4-MD5: YES

... genau das gleiche Ergebnis wie vorher. Allein damit haben wir noch nichts erreicht.

Ideal wäre eigentlich eine Vorgabe der Cipher List. Dafür gibt es den Parameter smtpd_tls_ciphers. Bei diesem gibt es nur einen kleinen Haken: Es gibt ihn erst ab Postfix 2.6. Das haben wir zwar auf dem Server, aber eine kompatible Konfiguration für sowohl RHEL 6 als auch 5 wäre natürlich ideal. Bei Postfix 2.3, wo das noch nicht konfigurierbar ist, wird intern "export" genutzt. Indem wir den Trick verwenden, eben diese Export-List zu konfigurieren (statt "medium", "high" etc.), würde das auch unverändert auf älteren Postfix-Releases laufen.

Deshalb setzen wir smtp_tls_mandatory_ciphers, smtpd_tls_mandatory_ciphers, smtp_tls_ciphers und smtpd_tls_ciphers auf "export" und zu "export" passend die Option tls_export_cipherlist. Als Wert für tls_export_cipherlist nehmen wir den unter OpenSSL cipher suite for forward secrecy beschriebenen String. Das Ergebnis kann sich sehen lassen:

ADH-AES256-SHA: NO (ssl handshake failure)
DHE-RSA-AES256-SHA: YES
DHE-DSS-AES256-SHA: NO (ssl handshake failure)
AES256-SHA: NO (ssl handshake failure)
KRB5-DES-CBC3-MD5: NO (no ciphers available)
KRB5-DES-CBC3-SHA: NO (no ciphers available)
ADH-DES-CBC3-SHA: NO (ssl handshake failure)
EDH-RSA-DES-CBC3-SHA: NO (ssl handshake failure)
EDH-DSS-DES-CBC3-SHA: NO (ssl handshake failure)
DES-CBC3-SHA: NO (ssl handshake failure)
DES-CBC3-MD5: NO (ssl handshake failure)
ADH-AES128-SHA: NO (ssl handshake failure)
DHE-RSA-AES128-SHA: YES
DHE-DSS-AES128-SHA: NO (ssl handshake failure)
AES128-SHA: NO (ssl handshake failure)
RC2-CBC-MD5: NO (ssl handshake failure)
KRB5-RC4-MD5: NO (no ciphers available)
KRB5-RC4-SHA: NO (no ciphers available)
ADH-RC4-MD5: NO (ssl handshake failure)
RC4-SHA: NO (ssl handshake failure)
RC4-MD5: NO (ssl handshake failure)
RC4-MD5: NO (ssl handshake failure)

Der Client mit RHEL 5 kann eine TLS-Verbindung mit dem Server nur mittels zweier Ciphers herstellen. Beide bieten FS. Und geeinigt haben sich beide auf "DHE-RSA-AES256-SHA". Wir schauen nochmal kurz, wie sich der Server mit sich selbst "unterhält", weiterhin mit dem Postfix-Default "medium" als Client:

# SSLCipherSuite=$(
> openssl ciphers '!EXPORT:!LOW:ALL:+RC4:@STRENGTH'|tr ":" " "
> ) ./ssltest.sh 127.0.0.1 25 smtp
ECDHE-RSA-AES256-GCM-SHA384: YES
ECDHE-ECDSA-AES256-GCM-SHA384: NO (ssl handshake failure)
ECDHE-RSA-AES256-SHA384: YES
ECDHE-ECDSA-AES256-SHA384: NO (ssl handshake failure)
ECDHE-RSA-AES256-SHA: YES
ECDHE-ECDSA-AES256-SHA: NO (ssl handshake failure)
DHE-DSS-AES256-GCM-SHA384: NO (ssl handshake failure)
DHE-RSA-AES256-GCM-SHA384: YES
DHE-RSA-AES256-SHA256: YES
DHE-DSS-AES256-SHA256: NO (ssl handshake failure)
DHE-RSA-AES256-SHA: YES
DHE-DSS-AES256-SHA: NO (ssl handshake failure)
DHE-RSA-CAMELLIA256-SHA: YES
DHE-DSS-CAMELLIA256-SHA: NO (ssl handshake failure)
AECDH-AES256-SHA: NO (ssl handshake failure)
ADH-AES256-GCM-SHA384: NO (ssl handshake failure)
ADH-AES256-SHA256: NO (ssl handshake failure)
ADH-AES256-SHA: NO (ssl handshake failure)
ADH-CAMELLIA256-SHA: NO (ssl handshake failure)
ECDH-RSA-AES256-GCM-SHA384: NO (ssl handshake failure)
ECDH-ECDSA-AES256-GCM-SHA384: NO (ssl handshake failure)
ECDH-RSA-AES256-SHA384: NO (ssl handshake failure)
ECDH-ECDSA-AES256-SHA384: NO (ssl handshake failure)
ECDH-RSA-AES256-SHA: NO (ssl handshake failure)
ECDH-ECDSA-AES256-SHA: NO (ssl handshake failure)
AES256-GCM-SHA384: NO (ssl handshake failure)
AES256-SHA256: NO (ssl handshake failure)
AES256-SHA: NO (ssl handshake failure)
CAMELLIA256-SHA: NO (ssl handshake failure)
PSK-AES256-CBC-SHA: NO (no ciphers available)
ECDHE-RSA-DES-CBC3-SHA: NO (ssl handshake failure)
ECDHE-ECDSA-DES-CBC3-SHA: NO (ssl handshake failure)
EDH-RSA-DES-CBC3-SHA: NO (ssl handshake failure)
EDH-DSS-DES-CBC3-SHA: NO (ssl handshake failure)
AECDH-DES-CBC3-SHA: NO (ssl handshake failure)
ADH-DES-CBC3-SHA: NO (ssl handshake failure)
ECDH-RSA-DES-CBC3-SHA: NO (ssl handshake failure)
ECDH-ECDSA-DES-CBC3-SHA: NO (ssl handshake failure)
DES-CBC3-SHA: NO (ssl handshake failure)
DES-CBC3-MD5: NO (ssl handshake failure)
PSK-3DES-EDE-CBC-SHA: NO (no ciphers available)
KRB5-DES-CBC3-SHA: NO (no ciphers available)
KRB5-DES-CBC3-MD5: NO (no ciphers available)
ECDHE-RSA-AES128-GCM-SHA256: YES
ECDHE-ECDSA-AES128-GCM-SHA256: NO (ssl handshake failure)
ECDHE-RSA-AES128-SHA256: YES
ECDHE-ECDSA-AES128-SHA256: NO (ssl handshake failure)
ECDHE-RSA-AES128-SHA: YES
ECDHE-ECDSA-AES128-SHA: NO (ssl handshake failure)
DHE-DSS-AES128-GCM-SHA256: NO (ssl handshake failure)
DHE-RSA-AES128-GCM-SHA256: YES
DHE-RSA-AES128-SHA256: YES
DHE-DSS-AES128-SHA256: NO (ssl handshake failure)
DHE-RSA-AES128-SHA: YES
DHE-DSS-AES128-SHA: NO (ssl handshake failure)
DHE-RSA-SEED-SHA: YES
DHE-DSS-SEED-SHA: NO (ssl handshake failure)
DHE-RSA-CAMELLIA128-SHA: YES
DHE-DSS-CAMELLIA128-SHA: NO (ssl handshake failure)
AECDH-AES128-SHA: NO (ssl handshake failure)
ADH-AES128-GCM-SHA256: NO (ssl handshake failure)
ADH-AES128-SHA256: NO (ssl handshake failure)
ADH-AES128-SHA: NO (ssl handshake failure)
ADH-SEED-SHA: NO (ssl handshake failure)
ADH-CAMELLIA128-SHA: NO (ssl handshake failure)
ECDH-RSA-AES128-GCM-SHA256: NO (ssl handshake failure)
ECDH-ECDSA-AES128-GCM-SHA256: NO (ssl handshake failure)
ECDH-RSA-AES128-SHA256: NO (ssl handshake failure)
ECDH-ECDSA-AES128-SHA256: NO (ssl handshake failure)
ECDH-RSA-AES128-SHA: NO (ssl handshake failure)
ECDH-ECDSA-AES128-SHA: NO (ssl handshake failure)
AES128-GCM-SHA256: NO (ssl handshake failure)
AES128-SHA256: NO (ssl handshake failure)
AES128-SHA: NO (ssl handshake failure)
SEED-SHA: NO (ssl handshake failure)
CAMELLIA128-SHA: NO (ssl handshake failure)
IDEA-CBC-SHA: NO (ssl handshake failure)
IDEA-CBC-MD5: NO (ssl handshake failure)
RC2-CBC-MD5: NO (ssl handshake failure)
PSK-AES128-CBC-SHA: NO (no ciphers available)
KRB5-IDEA-CBC-SHA: NO (no ciphers available)
KRB5-IDEA-CBC-MD5: NO (no ciphers available)
ECDHE-RSA-RC4-SHA: NO (ssl handshake failure)
ECDHE-ECDSA-RC4-SHA: NO (ssl handshake failure)
AECDH-RC4-SHA: NO (ssl handshake failure)
ADH-RC4-MD5: NO (ssl handshake failure)
ECDH-RSA-RC4-SHA: NO (ssl handshake failure)
ECDH-ECDSA-RC4-SHA: NO (ssl handshake failure)
RC4-SHA: NO (ssl handshake failure)
RC4-MD5: NO (ssl handshake failure)
RC4-MD5: NO (ssl handshake failure)
PSK-RC4-SHA: NO (no ciphers available)
KRB5-RC4-SHA: NO (no ciphers available)
KRB5-RC4-MD5: NO (no ciphers available)

Hier wird sich auf ECDHE-RSA-AES256-GCM-SHA384 geeinigt und die restlichen möglichen Ciphers bieten auch alle FS.

Jetzt bleiben noch diese geheimnisvollen "dh"-Dateien, die man laut diverser Anleitungen erzeugen muss, um Forward Secrecy zu erhalten. Auf postfix.org steht hierzu:

Postfix >= 2.2 support 1024-bit-prime EDH out of the box, with no additional configuration, but you may want to override the default prime to be 2048 bits long, and you may want to regenerate your primes periodically.

Es ist also nicht notwendig, diese zu erzeugen. Auf dieser Seite steht auch, wie man das konfiguriert. Für die erwähnte periodische Neuerzeugung der DH-Parameter kann man ja einen wöchentlichen Cronjob nutzen und somit die Sicherheit erhöhen. Diese Dateien werden fälschlicherweise oft "Schlüssel" genannt. Es sind aber keine Schlüssel sondern Parameter für das DH-Verfahren (man könnte sie sich eher wie einen "Pool" mit Zahlen vorstellen). Wenn man sie ändert, wird im Gegensatz zu einer "Schlüsseländerung" weiterhin Verschlüsselung möglich sein.

Weitere interessante Konfigurationsoptionen in dem Zusammenhang sind smtpd_tls_loglevel, smtp_tls_loglevel und smtpd_tls_received_header, die für Logging und Infos im Mailheader zu aktivieren sind.

Fazit: Mit Postfix >= 2.2 ist Forward Secrecy möglich. Getestet haben wir mit 2.6 auf dem Server und 2.3 auf dem Client. Mittels eines kleinen Tricks ist es auch mit Postfix 2.2 möglich, genaue Vorgaben für die gewünschten Ciphers zu machen und damit sichere Verschlüsselung mit Forward Secrecy zu erhalten.

Summary: Postfix >= 2.2 provides support for Forward Secrecy out of the box. To enable TLS you must configure your certificate(s) and activate smtpd_tls_security_level and smtp_tls_security_level by setting it to "may". To set a specific cipher list you may use smtpd_tls_ciphers but this is only available since Postfix 2.6. Older releases have a fixed default of "export" for this option. This can be used as a "trick" to use the same config for 2.2 and 2.6: Set smtp_tls_mandatory_ciphers, smtpd_tls_mandatory_ciphers, smtp_tls_ciphers und smtpd_tls_ciphers to "export" and configure your desired cipher list as value of tls_export_cipherlist. For a strong and compatible cipher list with only FS ciphers please take a look at: OpenSSL cipher suite for forward secrecy.


Posted by Frank W. Bergmann | Permanent link | File under: ssl, encryption, openssl, smtp