DKIM with Exim

Gmail recently blocked a legit email originating from my server. It seems they have again raised the bar to be able to use their services, and it's time to setup DKIM signing for my private server (running Exim4).

One of my last posts was about using Debian's mail-submit server to get my @debian.org emails signed with DKIM. That post also explains the basics of DKIM, so I'm not going to recap them here.

In order to automatically sign all mails from my mailserver with DKIM, there are basically three steps required:

  1. create a DKIM certificate
  2. publish the public key via DNS
  3. setup Exim4 to sign the emails with the private key

Creating a DKIM certificate

A DKIM-certificate is just an RSA certificate, which can be created with openssl.

Rather than doing that manually, we can use a the opendkim-genkey utility (conveniently available in Debian via the opendkim-utils package).

We can specify the target domain via the --domain flag (although this is mainly cosmetics), and the selector via the --selector flag (which is a mandatory setting with DKIM and important for managing keys over time). While the value for the domain is obvious (and since this is going to be used for my private server, I'm using umlaeute.mur.at), the selector is an arbitrary string. For convenience I just pick the current date (at the time of key generation). I'm quite fond of using subdomains for sending emails, so I'll throw in the --subdomains flag to indicate that my key is allowed to use that as well.

(For whatever reasons the utility is installed in /usr/sbin, so if you are not running as root, you might need to specify the full path to the binary.)

1/usr/sbin/opendkim-genkey --domain=umlaeute.mur.at --subdomains --selector=$(date +%Y%m%d)

That's it!

This will create two files (named after the chosen <selector>) in the current directory, the private key (in my case 20221012.private) and the public key (20221012.txt), the latter conveniently formatted as DNS TXT record.

Publishing the certificate via DNS

The next step is to publish the public key via DNS. The TXT record needs to be made available under <SELECTOR>._domainkey.<DOMAIN> (in my case that would be 20221012._domainkey.umlaeute.mur.at).

The TXT record can be found verbatim in the public key file as generated by opendkim-genkey (here: 20221012.txt), and looks like this:

120221012._domainkey.umlaeute.mur.at.	IN	TXT	( "v=DKIM1; h=sha256; k=rsa; "
2	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA664STmgQrQ0SBYEuhAGQJZEP+4tUrhnY+VoO/9Jh/32FKDTF/ctsS6P33qBTvNkObdbWykKcqj/yOqYe7WmeiD+0IdHML2owyGXRtLmoHBplldPZ68FTQT20q+YD9orz8UeSXVdzzQHnYSj6Ytg0QkHa3N+Yu6ql9v8rQ7Yvb+wV7JCoHBoKRs62Wy3hWIQz6nuxTbIceZmnlE"
3	  "TfX3c2BRu0TOZqEGfJKVNgcfvF/LsbPuPMoM2mlacHtpMmMtjrnDJGEmopvCl8Jv8VZkGNZRohrTMODc6EAVuvHvoQr4s+aANfhx+IHhrNyhRPnW/wZGy8X/2wb3F6kPsfh/uETwIDAQAB" )  ; ----- DKIM key 20221012 for umlaeute.mur.at

Since I do not operate the DNS-server for my private domain, I have forwarded the request to add the TXT record to the friendly staff at mur.at.

The current status of the TXT record can be queried with:

1dig TXT +noall +answer 20221012._domainkey.umlaeute.mur.at

Signing emails with Exim4

Once the DKIM public key is published in the DNS, it's time to setup the signing within Exim4 on my Debian system.

The basic Exim4 configuration that ships with Debian already comes with full support for DKIM, so we only need to tell it to actually use it by setting the following three variables:

  • DKIM_DOMAIN the domain
  • DKIM_SELECTOR our chosen selector
  • DKIM_PRIVATE_KEY the full path to the private key file (20221012.private)

For this I put the private key into a directory /etc/exim/dkim/ on the mailserver, and renamed it to <DOMAIN>-<SELECTOR>.private.pem (so it's really /etc/exim/dkim/umlaeute.mur.at-20221012.private.pem).

However, I don't hardcode the name, but instead use the DKIM_DOMAIN and DKIM_SELECTOR variables (which are mandatory anyhow), to calculate it.

The following snippet goes into a newly created file /etc/exim4/conf.d/50_local-dkim:

 1################### DKIM ####################
 2# enable DKIM signing of emails
 3
 4DKIM_DOMAIN = umlaeute.mur.at
 5DKIM_SELECTOR = 20221012
 6
 7.ifdef DKIM_SELECTOR
 8DKIM_FILE = CONFDIR/dkim/DKIM_DOMAIN-DKIM_SELECTOR.private.pem
 9DKIM_PRIVATE_KEY = ${if exists{DKIM_FILE}{DKIM_FILE}{0}}
10.endif

Remember that the private key is supposed to be private, so make sure you set up the permissions correctly: only the Exim-process (which runs as the Debian-exim group on typical Debian installations) needs access to it (and it only needs to read it), anybody else should be denied:

1# ls -lha /etc/exim4/dkim/
2total 16K
3drwxr-x--- 2 root Debian-exim 4.0K Oct 12 13:06 .
4-rw-r----- 1 root Debian-exim 1.7K Oct 12 12:39 umlaeute.mur.at-20221012.private.pem

Testing the setup

The obvious way to test the new setup is to just send an email via the mailserver. It should now contain DKIM-Signature header. If the receiving SMTP-server runs SpamAssassin, a few DKIM-related checks will be run, and at least the DKIM_SIGNED and DKIM_VALID checks (and probably DKIM_VALID_AU and DKIM_VALID_EF as well) should now succeed.