Recipient Verification for mandatory TLS

Abstract

If mandatory TLS modus for some recipients or domains is chosen, mails may get defered until bounce time, cause best recipient MX server has not TLS activated. This behave is by design but may confuse your senders, to workaround I tested a recipient verification via TLS.

If mandatory TLS mode for some recipients or domains is chosen, mails may get deferred until the expiry of the maximal queue lifetime, since the "best" recipient MX server doesn't have TLS activated. This "works as designed" but may confuse your senders -- as a workaround I created recipient verification via TLS.

PROLOGUE

In present times, I often get asked about preparing mailservice with mandatory TLS policy encrypt. Think of giving this option choosable via some online GUI to your customers. Unfortunately, normal mail users are suprised about the fact, that mails which can't get delivered via TLS secure channel will not get delivered at all - instead they'll stay in the queue until the maximal queue liftime is exceeded (usually five days). A simple "one time test" of the recipients' mailserver TLS capabilities may integrated in the GUI. But this does not honor the fact, that capabilities might change any time in the future.

Solution with Postfix

My idea to workaround these problem is using the postfix recipient verify service via TLS only. During my test this works fine. But be aware, as any verify service via SMTP, there a common limitations, like best MX recipient mailserver is not reachable by whatever reason, or it use some hard way of greylisting which might result in temporary 4XX Errors to the verify service, so verify status gets undeliverable at any testing time. The postfix verify service stores the verification results in cache, you might consider to lower the default caching time, but be warned, too much and fast probing may also result in some blacklisting by the recipient mailservice.

From technical side automatic fallback to opportunistic TLS policy may can be done, i.e via smtp_fallback_relay, but this falls back no TLS modus auto.

So my opinion is, if someone decides to use TLS Level encrypt only, this should be strictly followed anyway, anycase, anytime. The best compromise seems using the verify service with via TLS only, with small modifications, have fast verify decisions with relative short caching result times, at last present a good and informal smtpd_reject_footer in case verify via TLS fails. After this ,the sender may decide switch back to probably opportunistic TLS level, which should configured as default on modern mailsystems.

You need to configure a second postfix instance. in instance in master.cf:

127.0.0.1:10025     inet  n       -       -       -       -       smtpd
127.0.0.1:10026     inet  n       -       -       -       -       smtpd
    -o smtpd_recipient_restrictions=check_recipient_access,hash:/etc/postfix-2/tls_recipient_access,reject
    ...
verify_tls  unix  -       -       n       -       -       smtp
    -o smtp_tls_security_level=encrypt

Optional /etc/postfix-2/tls_recipient_access perhaps used as some manual whitelist, which also may created by a TLS CRON verify script. Here is a rough draft of such a script:

#!/bin/bash
rm /etc/postfix-2/tls_recipient_access.in
HOSTNAME=`hostname`
for i in `cat /etc/postfix-2/tls_policy | awk 'BEGIN { FS = " " } ; { print $1}'` ; do
SERVER=`dig -t mx $i | grep "MX" | grep -v ";" | awk 'BEGIN { FS = " " } ; { print $1 " " $6 " " $5}' | sort -n -k 3 | head -1 | cut -d' ' -f2  | sed 's/.$//'`
TLSTEST=`swaks -t -tls -s $SERVER -f postmaster@$HOSTNAME -t @$i -q TLS --ehlo $HOSTNAME | grep "TLS started" | awk 'BEGIN { FS = " " } ; { print $2 " " $3}'`
if [ "$TLSTEST" == "TLS started" ]; then
echo "$i OK" >> /etc/postfix-2/tls_recipient_access.in
fi
done;
postmap hash:/etc/postfix-2/tls_recipient_access.in && mv /etc/postfix-2/tls_recipient_access.in.db     /etc/postfix-2/tls_recipient_access.db
postmulti -i postfix-2 -p reload

Additional perhaps some pimp to get give more info in case verification failed:

...
DATE=`date`
...
else
echo "$i REJECT rejected mail to $i cause secure TLS smtp verify at $SERVER failed at $DATE" >> /etc/postfix-2/tls_recipient_access.in
...

Note

My small tests resulted in running that script works also with Greylisting servers, but that may not guaranteed at every recipient mailserver ! Script resulting verify restriction table only, may also used in your simple none two instance setup postfix mailserver setup at smtp income level. To make sure you will get recent MX Records refresh your DNS cache before running script.

main.cf in second instance:

address_verify_default_transport = verify_tls
smtp_tls_policy_maps = hash:/etc/postfix-2/tls_policy
address_verify_map = btree:/var/lib/postfix-2/verify
smtpd_recipient_restrictions = reject_unverified_recipient
smtpd_reject_footer = \c.the recipient address couldnt verified via secure tls channel
unverified_recipient_reject_code = 550
address_verify_poll_count = 3  # default
address_verify_poll_delay = 3s # default
address_verify_sender = <> # try postmaster
###values-might-need-fixing###
address_verify_positive_expire_time = 1d
address_verify_positive_refresh_time = 1d
address_verify_negative_refresh_time = 1d
address_verify_negative_expire_time = 1d
address_verify_negative_cache = yes

Finally limit the bounce time global in second instance main.cf as final solution if any verify results are buggy by whatever reasons and mail was queued.

bounce_queue_lifetime = 12h
delay_warning_time = 6h

/etc/postfix-2/tls_policy

example.com encrypt

main.cf in first instance

...
transport_maps = hash:/etc/postfix/transport
...

/etc/postfix/transport

example.com  verify_tls_fallback:[127.0.0.1]:10025
# user@example.com verify_tls_fallback:[127.0.0.1]:10025

You like to i.e use sql tables, but using them for transport is not recommended, so an alternative might be using FILTER

master.cf first instance

...
verify_tls_fallback  unix  -       -       n       -       -       smtp
    -o smtp_fallback_relay=127.0.0.1:10026
...

ADDITIONAL IDEAS

The Swiss Army Knife SMTP Tool swaks may give more opportunities to feed its results in a i.e a Database. Swaks may also validate and display ssl crts.

swaks -t -tls -s mail.sys4.de -f postmaster@mail.example.de -t @sys4.de -q TLS --ehlo mail.example.de --tls-verify --tls-ca-path /etc/ssl/certs --tls-get-peer-cert

This will produce some output like this

=== Trying mail.sys4.de:25...
=== Connected to mail.sys4.de.
<-  220 mail.sys4.de ESMTP Postfix
-> EHLO mail.tuxzilla.de
<-  250-mail.sys4.de
<-  250-PIPELINING
<-  250-SIZE 40960000
<-  250-ETRN
<-  250-STARTTLS
<-  250-ENHANCEDSTATUSCODES
<-  250-8BITMIME
<-  250 DSN
-> STARTTLS
<-  220 2.0.0 Ready to start TLS
=== TLS started with cipher TLSv1:ECDHE-RSA-AES256-SHA:256
=== TLS no local certificate set
=== TLS peer DN="/serialNumber=tumaqyzmnDu8m1ss6qhFTdtgazyg6/An/OU=GT53160044/OU=See  www.rapidssl.com/resources/cps (c)12/OU=Domain Control Validated - RapidSSL(R)/CN=mail.sys4.de"
=== -----BEGIN CERTIFICATE-----
...
=== -----END CERTIFICATE-----
~> QUIT
<~  221 2.0.0 Bye
=== Connection closed with remote host.

if validate will fail you will see something like this:

*** TLS startup failed (connect(): error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed)
*** STARTTLS attempted but failed

Please be aware that it may always fail with self created ssl crts !

If you like, grep your existing logs from a mailserver with opportunistic TLS for hostnames SMTP TLS was working in the past, check them again with swaks, store ssl crt , cipher or whatever you like in a database and compare results from time to time with some alarming if something has changed i.e for human security inspection. Or you might consider doing automatic change to mandatory TLS with domains/hosts where TLS SMTP worked in the past with opportunistic TLS but get alarmed if some ssl parameters changed on recipient mailservers.

EPILOG

This Solution is not the Holy Grail, but worked in testing environment, it might be better to have some milter or policy server to handle tls verification, which should include cache and is tolerant of any case of tmp errors or connection problems on the recipient side. Some other idea maybe is using an access table feeded from some daily cron TLS check verify script to include in second instance ( see example ), or feed CRON verify TLS check results in your own RBL. Perhaps include that RBL in second instance. Some main advantage of using your own RBL might be, it should be includable in multiple postfix servers more easy. Anyway, you might play around with other cascading order of instances, or do other combining of restrictions, perhaps you might think using a verify script access table only solution is better design. Finally Wietse Venema has released a patch with postfix-2.12-20140316. This resolves some old requests to make hard/soft errors more configurable, this may also help in early bounce TLS mandatory Modus.

Robert Schetterer, 02. March 2014

   postfix    tls    verify