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