How to make SSMTP authenticate to Postfix with SSL certificate

I needed a simple mail delivery agent on my local machine to route all mails to my hosted mail server running Postfix. To prevent open-relaying, in addition to SASL authentication, I maintain a list of IPs and netmasks for all clients who can simply connect and rely mail through it. Together with 'smtpd_recipient_restrictions' setting this makes the job done:

mynetworks = hash:/etc/postfix/network_table, cidr:/etc/postfix/network_table.cidr
smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination

However, for my local machine being on a dynamic ip, to relay mail, the client should either be using a) auth credentials; or b) have a certificate to authenticate with.

I didn't want to use login/password this time (and bother which account to choose) and decided to configure my local client to auth with SSL certificate.

Here is what I learned after some trial & error.

My choice was between sSMTP and Nullmailer. Nullmailer is a more robust solution as it runs as a daemon and continuously relies mail, being able to handle network and remote errors for a re-delivery. As a purist, though, I didn't want to add another extra process continuously running on my machine, so I decided in favour of a more simple sSMTP.

I pulled a seemingly latest version from Fedora repositories, named ssmtp_2.64.orig.tar.bz2.

Following settings are needed to configure it to authenticate with certificates:

mailhub=mail.xxxxx.com:465

UseTLS=Yes
UseTLSCert=Yes

TLSCert=/usr/local/etc/ssmtp/laptop.pem

After compiling and doing some tests, I realised that the settings were not used and client wasn't able to 'UseTLS' at all, due to default configuration omitting TLS altogether.

So be sure to compile ssmtp with "./configure --enable-ssl".

Then I stumbled over certificate format. I thought a self-signed SSL certificate, as generated by OpenSSL with the below should be enough:

openssl req \
       -newkey rsa:2048 -nodes -keyout domain.key \
       -x509 -days 999 -out domain.crt

However, ssmtp failed to parse certificate throwing various errors.

After looking at the code (ssmtp.c), I learned that it is expecting a 'chained certificate', which in addition contains private key.

So to make a chained certificate, just 'cat domain.key domain.crt > laptop.pem' and you have an acceptable format to use.

Now, we need to get a fingerprint for the certificate to allow Postfix to accept it.

Run:

openssl x509 -noout -fingerprint -md5 -in laptop.pem 
MD5 Fingerprint=32:1B:B9:39:48:57:4E:45:F1:7F:AC:71:66:8F:1C:6E

You can use sha1 instead, but I decided to leave Postfix with default settings of validating certificates using md5 hash. Weakness of MD5 is not critical for my application.

Now, we change Postfix configuration as follows:

relay_clientcerts = hash:/etc/postfix/relay_clientcerts

smtpd_tls_ask_ccert = yes

smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,permit_tls_clientcerts,reject_unauth_destination

and create '/etc/postfix/relay_clientcerts' with in a format of: (certificate hash^Tab^Anytext)

32:1B:B9:39:48:57:4E:45:F1:7F:AC:71:66:8F:1C:6E OK

Don't forget to execute 'postmap relay_clientcerts' and 'service postfix reload' to reload new config settings.

Now sending mail from command line rocks! =)

$ mail -s lalala vz@d........com
test mail
.
EOT

Oct 16 00:13:59 laptop sSMTP[30096]: Creating SSL connection to host
Oct 16 00:13:59 laptop sSMTP[30096]: SSL connection using ECDHE-RSA-AES256-GCM-SHA384
Oct 16 00:14:01 laptop sSMTP[30096]: Sent mail for vz@divide*verflow.com (221 2.0.0 Bye) uid=1000 username=vz outbytes=492