Configuring Postfix mail submission to support SSL client certificates


Typically, when a mail user agent submits a message for delivery, it authenticates to the server with a username and password. If you are using that server as a "smart host" for a workstation, the server needs to know that the workstation can be trusted. One way you can do that is by using an SSL cetficate on the workstation (client), and having the server accept mail based on that identity.

This note illustrates how a postfix mail server can be configured to add support for SSL client certificates.

First, add to file:

relay_clientcerts = hash:/etc/postfix/clientcerts
smtpd_tls_fingerprint_digest = sha1

The clientcerts file will list the fingerprints for the SSL client certificates that we want to trust.

Next, search for all the smtpd_<whatever>_restrictions definitions that contain a permit_sasl_authenticated entry. Ensure that each one found also contains a permit_tls_clientcerts entry.

For instance, here is my smtpd_relay_restrictions entry:

smtpd_relay_restrictions =  
       check_recipient_access hash:$config_directory/access,

Next, modify your to similar to:

 submission inet n       -       -       -       -       smtpd
   -o syslog_name=postfix/submission
   -o smtpd_tls_security_level=encrypt
+  -o smtpd_tls_ask_ccert=yes
   -o smtpd_sasl_auth_enable=yes
   -o smtpd_reject_unlisted_recipient=no
   #-o smtpd_client_restrictions=$mua_client_restrictions
   #-o smtpd_helo_restrictions=$mua_helo_restrictions
   #-o smtpd_sender_restrictions=$mua_sender_restrictions
   -o smtpd_recipient_restrictions=
-  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
+  -o smtpd_relay_restrictions=permit_tls_clientcerts,permit_sasl_authenticated,reject
   -o milter_macro_daemon_name=ORIGINATING

The first change (enable enable smtpd_tls_ask_ccert ) tells the server to ask the client for its certificate, if it has one. Without this setting, the server will not ask for a client certificate, and instead will establish a an anonymous SSL connection.

The second change is to allow authorization by SSL certificate, as well as SASL (username/password) authentication.

The final step is to create your /etc/postfix/clientcerts file. This is a keyed file, where the keys are SHA1 fingerprints of recognized clients, and the values are ignored. We'll use the client hostname as the value, so the entries are self-documenting.

Here is mine:

20:05:06:99:be:c7:23:67:86:65:c2:91:ce:65:ce:8a:72:53:19:4f chinacat.chez-rufus.local

Use the openssl command to get the fingerprint of your client certificate.  For example:

$ openssl x509 -in /etc/ssl/certs/ssl-cert-snakeoil.pem -fingerprint -sha1 -noout
SHA1 Fingerprint=20:05:06:99:be:c7:23:67:86:65:c2:91:ce:65:ce:8a:72:53:19:4f

Once you've created the clientcerts file, don't forget to hash it.

/usr/sbin/postmap /etc/postfix/clientcerts

ssmtp Client Setup

For leaf workstations that use a mail smart host, ssmtp is a good choice. The version included in Debian has been modified to support client certificates.

The configuration to use this is:

 The localhost.pem file contains the SSL cert and key. This needs to be world-readable because ssmtp does not run with any privileges. It might be a good idea to generate a unique self-signed cert for this purpose. The risk is that anybody who can access your workstation and obtain this key will have be able to submit mail to your smarthost server.

postfix Client Setup

It's easy to configure a postfix client to use this setup. The settings I use to relay through the smart host are:

relayhost =

smtp_use_tls = yes
smtp_tls_loglevel = 1
smtp_tls_security_level = encrypt
smtp_tls_cert_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
smtp_tls_key_file = /etc/ssl/private/ssl-cert-snakeoil.key

Note that I'm relaying through the submission service (port 587) on the smart host. I've also set the smtp_tls_security_level to require an encrypted connection. Even though I'm using a self-signed cert that will fail chain-of-authorityh verification, it doesn't matter in this application. The  permit_tls_clientcerts check only cares whether the certificate fingerpring is listed in the /etc/postfix/clientcerts file.

Here is the log on the client that shows a smart host submission:

Mar 27 17:20:38 chinacat postfix/pickup[27925]: D1A53100444: uid=1201 from=<chip>
Mar 27 17:20:38 chinacat postfix/cleanup[27930]: D1A53100444: message-id=<20160327222038.D1A53100444@chinacat.
Mar 27 17:20:38 chinacat postfix/qmgr[27924]: D1A53100444: from=<chip [at] unicom [dot] com>, size=354, nrcpt=1 (queue ac
Mar 27 17:20:39 chinacat postfix/smtp[27932]: Untrusted TLS connection established to[192.10
8.105.60]:587: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
Mar 27 17:20:39 chinacat postfix/smtp[27932]: D1A53100444: to=<chip [at] soaustin [dot] net>,[192
.108.105.60]:587, delay=0.54, delays=0.02/0.02/0.39/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 4A6C
Mar 27 17:20:39 chinacat postfix/qmgr[27924]: D1A53100444: removed

The log shows an "Untrusted TLS connection" because the client did indeed present an SSL certificate, but it failed chain-of-authority checks because it's a self-signed cert.