DKIM is “DomainKeys Identified Mail”, one of a number of technologies designed to let your email flow freely to its intended recipients, even while the world is full of spam. It’s part of something called DMARC, which is an overall statement of how you’re using various email protection technologies.
DMARC is getting increasing amounts of attention these days, within governments and in the private sector. If you implement it correctly, your email is more likely to be delivered smoothly to GMail, Hotmail and AOL among others. This year the UK government moved to using strong DMARC policies, and it’s gradually becoming an accepted part of best practice.
This brief article should give an up to date view of how to set things up if you’re using exim4 on Debian (exim 4.84 on Debian 8 at time of writing). It assumes that you’re comfortable administrating a Debian machine, and in particular that you have at least a basic understanding of exim.
Some background on DKIM
DKIM works in the following way:
The server that sends the original email (maybe a GMail server, or maybe your company’s mail machine) adds a signature to the outbound email, using a private cryptographic key. The signature also specifies a selector, which identifies the key being used. (This allows you to have several selectors, and hence several keys, for different servers.)
Any other server that receives that email can verify the signature. It does this by looking in your domain’s DNS records for the public part of the cryptographic key. If you send emails from
me@mydomain.com
, then a server receiving your email can look up the signature’s selector within_domainkey.mydomain.com
to find the public key it needs to verify the signature.
Webmail systems like GMail take care of DKIM for you, providing you either use their interface directly, or send your mail out through them. But if you run your own email server, you need to set things up yourself.
DKIM signing for your outgoing email
This is based on an article by Steve Kemp on the Debian Administration website. I’ve simplified the instructions slightly, and corrected what appears to be a single error.
Install the right version of exim4
Debian has two versions of exim: “light” and “heavy”. You’re probably using light, which is the default, but you need heavy for DKIM signing support. You should be able to do the following without any interruptions to email service, because exim configuration is separate from the light and heavy packages:
apt-get install exim4-daemon-heavy
/etc/init.d/exim4 restart
apt-get purge exim4-daemon-light
Generating the keys
We’ll store our cryptographic keys in /etc/exim4/dkim
. Except for
the first two lines, you can just copy the following straight into a
root shell (or make a script out of them if you’re going to be doing
this a lot).
DOMAIN
you just want to set to the domain part of your email
address. SELECTOR
will identify your key in the DNS. You’ll want to
be able to change keys in future, and one way of making this easy is
to use dates.
export DOMAIN=mydomain.com # change this to your own domain!
export SELECTOR=20161122 # a date is good for rotating keys later
cd /etc/exim4
# Ensure the dkim directory exists
test -d dkim || (umask 0027; mkdir dkim; chgrp Debian-exim dkim)
# Generate the private key
OLD_UMASK=`umask`; umask 0037
openssl genrsa -out $DOMAIN-private.pem 1024 -outform PEM
# Extract the public key
openssl rsa -in $DOMAIN-private.pem -out $DOMAIN.pem -pubout -outform PEM
umask $OLD_UMASK; unset OLD_UMASK
chgrp Debian-exim $DOMAIN{-private,}.pem
# Format the public key for DNS use
echo -n 'v=DKIM1; t=y; k=rsa; p=';\
sed '$ { x; s/\n//g; p; }; 1 d; { H; d; }' $DOMAIN.pem
The very last line reads the public key file and converts it into the
right format to use as a DNS record. Apart from some stuff at the
start (including t=y
, which is “testing mode”, which you’ll want to
remove once you’ve got everything working properly), this involves
stripping the comment lines at the start and end, and then squashing
everything else onto one line, which we do using sed(1)
.
Note at this point we haven’t placed the key files into
/etc/exim4/dkim
– they’re still in /etc/exim4
. This is because we
want to set up the DNS record (so others can verify our signatures)
before we start signing using our new private key.
A note about key length: here, we’ve created a 1024 bit private key. You can create a longer one, say 2048 bits, but some DNS hosts have difficulty with the length of record generated (although Google have documented a way round that). I hope that 1024 will continue for some time to be sufficient to avoid compromise, but don’t take my word for it: check for the latest advice you can find, and if in doubt be more cautious. If you need to split a key across multiple DNS records, ensure you test this carefully.
Setting up the DNS record
Now you need to create a DNS record to contain the public key. The
last line of the above commands printed out the value of that DNS
record, which will start “v=DKIM1; t=y; ...
”. The name will be SELECTOR._domainkey.mydomain.com
. The
type will be TXT
(which these days is mostly a general-purpose type
for DNS uses that don’t have their own type).
In a lot of DNS control panels, and in most DNS server zone files, you don’t need to include your domain itself, so you’ll just need to add something like:
SELECTOR._domainkey TXT v=DKIM1; t=y; ... from above
Configuring exim
Exim on Debian already has suitable configuration to create DKIM
signatures; it just needs configuring appropriately. We can do this by
adding lines to (or creating) the /etc/exim4/exim4.conf.localmacros
file, which is read in immediately before the main configuration
file. We want the following:
# DNS selector for our key.
DKIM_SELECTOR = 20161122
# Get the domain from the outgoing mail.
DKIM_DOMAIN = ${lc:${domain:$h_from:}}
# The file is based on the outgoing domain-name in the from-header.
DKIM_FILE = /etc/exim4/dkim/DKIM_DOMAIN-private.pem
# If key exists then use it, if not don't.
DKIM_PRIVATE_KEY = ${if exists{DKIM_FILE}{DKIM_FILE}{0}}
Once you’ve added these lines, you should restart exim (service exim4
restart
or similar).
There are three important macros here (the fourth, DKIM_FILE
, is
just to avoid duplication and potential errors in configuration):
DKIM_SELECTOR
is the selector we chose earlier.DKIM_DOMAIN
sets the domain we’re signing for (which we extract from the email’sFrom:
header, and lowercase it).DKIM_PRIVATE_KEY
is either0
(don’t sign) or points to a private key file. (It can also contain the key as a literal string, but we’re not going to use that.)
So this configuration says, simply: if there’s a file
/etc/exim4/dkim/<DOMAIN>-private.pem
for the domain I’m sending
from, then DKIM sign that email using the key in that file and the
given selector.
Note that this can be different for every email. If you know that you’ll only ever send email from one domain for this server, then you can simplify this configuration even further. However, in practice I’ve always run mail servers for multiple domains, and it’s easy enough to set this up from the beginning.
All the domains you send from that support DKIM will use the same
selector, but you could replace that with a lookup to be able to vary
things if you wanted. (Similarly, you could include the
DKIM_SELECTOR
in the DKIM_FILE
, which could make rotating keys
easier in future.)
And enabling DKIM signatures
All the configuration is in place, but the key files aren’t in
/etc/exim4/dkim
, so let’s move them into place.
mv /etc/exim4/$DOMAIN*.pem /etc/exim4/dkim
Checking this works
mail-tester.com
provides a simple way
to test things like DKIM setup. Go to the site, and it’ll give you an
email address. Send an email to it from your server, and it’ll show
you a useful report on what it saw. DKIM is in the “authenticated”
section, along with SPF, DMARC and some other details.
If this didn’t work, then you need to go digging. The first thing to
look for is in the exim mainlog (/var/log/exim4/mainlog
). If you
have a line like this:
2016-11-21 18:21:27 1c8tDb-0003IV-NR DKIM: signing failed (RC -101)
then you have some work to do to figure out what went wrong. (-101
is PDKIM_ERR_RSA_PRIVKEY
, which you can chase through the code to
find that it means exim_rsa_signing_int()
returned an error, but
that’s not helpful on its own.)
What you want to do is to raise exim’s logging verbosity. You can do this with another line in the local macros file:
MAIN_LOG_SELECTOR = +all
This macro is used as the default for log_selector
,
which
Exim documents in a detailed if largely unhelpful fashion. +all
should throw a lot more detail into your mainlog
that will help
you figure out what’s going on.
Rotating your keys
You are advised to “rotate” your DKIM keys (ie generate new ones, get them into the DNS and then start using them for signing) regularly; I suggest either every three or six months. (Three months conveniently matches the refresh period for Let’s Encrypt certificates.)
In order to do that, you basically do the same thing as above: first you create the new keys, and add the public key as a new DNS record using a new selector. Then you want to wait long enough for that DNS record to appear (which depends on how your DNS hosting works, but usually a few minutes is enough for a completely new record). Then you can move the new key files into place and flip the selector in your exim configuration (don’t forget to restart exim).
This is the point where putting the selector into the filename can help; you can move the files into place first, then flip the selector and restart exim to start using the new keys.
What next?
You’ll want to drop the t=y
part of your DNS record to move out of
testing mode. It’s unclear what different providers actually do in
testing mode; some accounts suggest that they do full DKIM processing,
but others suggest that they verify the DKIM signature but then won’t
act on it. Removing the testing flag once you’re happy everything is
working smoothly is in any case a good idea.
Once you have DKIM set up, you probably want to add in SPF. Once that’s there, you can tie them together into a public policy using DMARC. Google has a support article on adding a DMARC record, including advice on gradual rollouts.
If the steps in this article don’t work for you, please get in touch so I can update it!