Sign Outgoing Emails using DKIM

Content:

DKIM, or DomainKeys Identified Mail, is a feature used to prevent spoofing of outgoing messages from a server. It does this by signing each outgoing email with a private key. To check that the message is valid, the recipient can use the published public key for the domain to verify the digital signature on the email.

Should the DKIM check fail, the receiving server will mark the message as spam. This is one of the methods used to filter out spam messages from real ones.

Along with SPF, DKIM is a necessary feature if you want to enable DMARC – another security-related email feature you might want to add to your server. Check out our article if you want to learn how to do this.

Installing OpenDKIM

In order to use DKIM, you’ll need an application on your server that implements the DKIM authentication system. OpenDKIM is the recommended way to do this.

You can install OpenDKIM using

apt install opendkim

or the equivalent for your distro.

Some additional packages will be pulled in along with opendkim, which are required for it to function correctly.

Setting Up Folders

Once that’s complete, you’ll need to create a directory where the signing keys and config files will be saved.

In theory, you can choose any directory you wish, as long as OpenDKIM has permissions to access it. In practice, /etc/opendkim or /etc/dkimkeys tend to be the common directories used for this purpose. This article will be using /etc/opendkim.

mkdir /etc/opendkim

Inside the directory we just created, we’ll have a folder called ‘keys’. Inside this, we’ll have a folder matching our domain name.

mkdir -p /etc/opendkim/keys/example.com

Adding -p to the mkdir command will ensure all parent directories are created as required.

Setting Up DKIM Keys

DKIM requires two keys – a private key used to sign emails, and a public key receiving mail servers can use to check the private key is valid.

To set up the keys, they’ll need to be created, before configuring OpenDKIM to use them.

Generating DKIM Keys

Create the public and private keys needed, using opendkim-genkey. The breakdown of the command is as follows:

opendkim-genkey -b {blocksize} -d {domain} -D {key directory} -s {selector} -v
  • -b {blocksize}: Sets the block size used to generate the key. This parameter can be omitted – the default value is 1024.
  • -d {domain}: The domain we’re generating the key for.
  • -D {key directory}: Directory where the keys will be saved, take this from the previous step.
  • -s {selector}: This parameter is a unique name for the key generated – set this to anything you like. It’s important to make a note of this as we’ll need it later on. This parameter can be omitted – by default the value is default.
  • -v: Gives a more verbose output. Not required, but useful.

A full example, with parameters populated, can be seen below.

opendkim-genkey -b 2048 -d example.com -D /etc/opendkim/keys/example.com -s key2022 -v

If the command is successful, you should see something like this.

opendkim-genkey: generating private key
opendkim-genkey: private key written to key2022.private
opendkim-genkey: extracting public key
opendkim-genkey: DNS TXT record written to key2022.txt

The .private file contains the private key, with the .txt file containing the corresponding public key. You’ll need the content of the .txt file later on.

Adding the Key to OpenDKIM

For OpenDKIM to use the key, we need to make it aware that a key exists. To do this, a couple of files need to be created.

touch /etc/opendkim/KeyTable
touch /etc/opendkim/SigningTable

The KeyTable file will store a reference to the private key that was generated earlier. The format of the entry is as follows:

{selector}._domainkey.{domain} {domain}:{selector}:{path to .private file}

The parts in italics need to be substituted for the relevant values. For the example key create above, this is the entry that would be required.

key2022._domainkey.example.com example.com:key2022:
/etc/opendkim/keys/example.com/key2022.private

Note that this is all one line, it’s just broken over two due to the length in this article.

Next, you need to open the SigningTable file.

This file will tell OpenDKIM which messages should be signed with the key. The record here is structured as follows:

{user}@{domain} {selector}._domainkey.{domain}

If you want to use apply OpenDKIM for all email addresses from your domain, use the wildcard (*) as the user.

*@example.com key2022._domainkey.example.com

If you specify specific usernames, add a separate entry for each.

Setting Allowed Hosts

There’s one more file that need to be set up, which will define the hosts allowed to use the DKIM keys.

touch /etc/opendkim/TrustedHosts

In this file, you’ll want to add a list of identifiers, which correspond to your network.

127.0.0.1
::1
localhost
{server ip}
{domain}
{mx domain}

You’ll definitely want the first three lines, which define the current system OpenDKIM is running on.

In addition, add records corresponding to the IP address of your server (IPv4 and IPv6 if you use both), along with your domain, and MX domain (if it’s different to your domain). If you’re unsure, double check the MX record on your domain.

Changing OpenDKIM Settings

The main OpenDKIM configuration file needs to be edited, to make use of the KeyTable , SigningTable and TrustedHosts files that have been created. There are also a couple of other settings that need tweaking.

Open the config file

nano /etc/opendkim.conf

and add the following lines to the bottom of the file.

KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
InternalHosts refile:/etc/opendkim/TrustedHosts
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts

The first two are self-explanatory – they tell OpenDKIM to use the KeyTable and SigningTable files as the KeyTable and SigningTable respectively.

The InternalHosts entry tells OpenDKIM to treat these addresses as internal to your server, and OpenDKIM will sign all emails sent through a matching address.

The ExternalIgnoreList entry tells OpenDKIM to skip incoming email validation for the domains listed in the file. As these are our own addresses, there is no point in checking if the email sent to yourself is DKIM-signed.

Elsewhere in the file, check that the following entries are present and active – you might need to change the values to match. If the line begins with a ‘#’, the line is commented out – simply remove the # to activate the entry.

Canonicalization       relaxed/simple
Mode                   sv

OpenDKIM is now configured, and ready to go.

Adding the Public Key to Your Domain

The domain needs a DNS record, containing the DKIM public key. The server receiving the email will check the public key, to make sure the private key used to sign the email was valid.

If another server attempts to spoof your email domain, they will be unable to sign emails with the private key corresponding to your public key. These emails will be identified as spam, even if the spoofing server tries to implement DKIM itself.

Adding the DNS Record

The public key is contained in the .txt file generated alongside the private key.

If you take a look in this file, you’ll see a record that starts {selector}._domainkey IN TXT. This contains the full command needed to create the record.

Log in to to your domain host, and create a new TXT record. This is where the DKIM key will be added.

Copy the section of the public key contained within the brackets – it should start with v=, and end after the p= string. Everything else can be discarded.

v={version}; h={hash}; k={algo}; p={public key}

This is the format of the entry you need to create.

  • v={version}: DKIM version number, this should be DKIM1.
  • h={hash}: Hash algorithm used to create digest data. sha256 is recommended.
  • k={algo}: Cryptographic algorithm used to decode the DKIM signature, rsa recommended.
  • p={key}: Base64 encoded public key, generated by opendkim-genkey.
  • s={email}: Not shown above, will need to be added manually. Specifies the service the DKIM key is to be used for.
v=DKIM1; h=sha256; k=rsa; "p=I1IbABgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz69HNR+ipYH4w4rXrVAkI3GIrIUh/RniCUfHqqdowPl41Mf0oQh/FVCxqW4I79djrrcyiTXQaeBrZBELbmJtjwgRc/e6bPTcPOYey3FWHbZtHp/PZImjykxQdsrL5cQtWcm7k+6kLPpGH5R0Cd4cejNMIWchtQXC8qjp+hOukZR/6zzxKlUgyEGlWeGYXcf0sWvvNCx8d7uFvRj3GWdRJ9GD6ZDduoxLH6BHB2760tz8na68evDjwxQuXDyNjWznkWYsQHAbLNEySqJYX2u4VfmvD3brFm6OaAPgfsEi6OPhTI+95DhwZ3NWC9aepavLNh+38C2mUC9Amgw9JA+jQ2uRLpcc"

Take extra care when copying the public key. Some text editors prevent horizontal scrolling, so it is easy to cut off part of the key without realising. cat is a great tool to use to read the file and output cleanly to a terminal.

cat /etc/opendkim/keys/example.com/key2022.txt

It’s also important to ensure the key is one sequence – you might find it is split, with quote marks around each section. Simply remove the inner quote marks, and put the entire key into one block.

Add the DNS record to your domain. Congratulations, you should now be ready for testing!

Testing the DKIM Certificate

Reboot your email server, to ensure all settings changes have taken effect.

To test your certificate, I recommend using the ‘DKIM Lookup’ feature of MX Toolbox.

In the search box, enter

{domain}:{selector}

For example,

example.com:key2022

If everything is set up correctly, you should see an output similar to the below.

DKIM result
Successful DKIM test result

If you’re experiencing a failed test result, double check the errors given by the test tool.

The most likely issue is with the public key itself. If this is the case, check that it has been copied over correctly, and any line breaks have been removed.

Connecting to an MTA

The DKIM side should now be complete. However, at this stage, emails will still not be signed with the private key.

For this to happen, your MTA needs to be configured to connect to OpenDKIM, and pass along outgoing messages for signing.

How to do this is MTA dependent. If your using Postfix, check out our guide here to get it configured correctly.