Implementing DKIM with Exim

This article was updated in February 2014 to reflect changes policy and reporting options. The previous ADSP (Author Domain Signing Practices) information has been removed.

DomainKeys Identified Mail (DKIM) provides a method to confirm the origin of an e-mail. DKIM also provides some protection against tampering. Unlike SPF, this validation applies to the contents of the message when it is signed. Like SPF, the information required for validation is added to DNS.

This article covers signing outgoing email for the domain.  This implementation is based on Exim4 running on Ubuntu.  DNS record descriptions are for bindRecent versions of Exim now implement DKIM. This post documents my implementation of DKIM using Exim and Bind on Ubuntu or Debian. This implementation provides support for signing for the domain, but not for the author. Author signing should be done by the author using appropriate tools.

DomainKeys uses a _domainkey subdomain within the domain to which it is applied.  Signing keys become selector TXT_domainkey subdomain.  Multiple signing keys may exist for various reason such as: server specific keys, time based key rotation, or originator based keys.

Signing Outgoing Mail

This configuration uses a single selector named rsa1. The private key file is named accordingly.  You should choose a different name, and replace rsa1 with your own selector. When replacing your key, always use a new selector.

Generating a Signing Key

Before signing your mail you will need to generate a key and extract the public key.  You will need to keep the private key as long as you are signing with it.  If you generate multiple keys, rename the private key according to your naming convention.  The following commands will generate a private key in dkim.private.rsa1, and the base64 encoded public key in dkim.public.base64. Once you have configured bind you can delete the dkim.public.base64 file.  Change the value of SELECTOR for each.   The keys must be generated in (or moved to) your configuration directory (/etc/exim4) or some other location where your mail server can read it. (Use a key size of 1024 or larger, smaller key sizes may not validate.)

SELECTOR=rsa1
cd /etc/exim4
openssl genrsa -out dkim.private.$SELECTOR 1024
openssl rsa -in dkim.private.$SELECTOR -out dkim.public.der -pubout -outform DER
base64 < dkim.public.der > dkim.public.$SELECTOR 
rm dkim.public.der
chown root:Debian-exim dkim.private.$SELECTOR
chmod 440 dkim.private.$SELECTOR

Configuring Bind

You will need to add a TXT record to each domain for which you are signing email. For multiple domains signed with the same key, I find it simpler to separate the DKIM configuration into its own file. (This requires manually updating the serial numbers on the including domains.) Add the following lines to the zone file for each domain being signed.

;; Enable DKIM verification
$INCLUDE /etc/bind/dkim.conf

WARNING: You will need to increment the serial number in each of these zone files each time you update the include file. All signing keys will be valid for all configured domains. If you don’t want this to be the case, add the records directly to the applicable zone file(s).

Create your initial DKIM configuration. In the rsa1._domainkey selector record, replace <DKIM.PUBLIC.KEY> with the text from your dkim.public.base64 file. Ensure you don’t have any white space in the key.

;; Common DKIM information for signed domains
;;
;; See: http://tools.ietf.org/html/rfc6376
rsa1._domainkey                 IN      TXT     "v=DKIM1; k=rsa; t=y; p=<DKIM.PUBLIC.KEY>"
;; EOF

Reload or restart bind. Once all your secondary name servers have refreshed you will be ready to configure DKIM working in test mode. There are a number of sites such as check-auth@verifier.port25.com which will send you a validation report.

If you use a large key size (2048 is a practical limit for UDP transmission), then you may need to split the selector’s record. A TXT string can not contain more than 255 characters. A longer string can be split into parts by breaking it up into quoted parts. Use parentheses around the definition if you split it across lines. These parts a consolidated into one string without intervening white space. For example “abcdef” could be split using either of these techniques:

oneline    in TXT  "v=DKIM1; k=rsa; t=y; p=" "abc" "def"
manylines  in TXT ("v=DKIM1; k=rsa; t=y; p="
 "abc"
 "def")

The above instructions above provide the public key in a format which is easy to convert to manylines format. You will need to add quotes at the beginning and end of each line. Then wrap the while text starting with "v=DKIM1; in brackets.

Configuring Exim4

Update your Exim configuration.  If you are using the Ubuntu (Debian) configuration this is done by defining macros.  Using the split configuration these can be added to a file in /etc/exim4/conf.d/main.  I use the filename 00_localmacros.  Using the unsplit configuration add the definitions near the beginning of /etc/exim4/exim4.conf.template. These macros will alter the configuration of the remote_smtp transport to sign outgoing email.  The selector will be automatically set to the extension of the private key file.  If you are signing for only one domain, you may want to hard-code the DKIM_DOMAIN value to your domain.

# Enable DKIM
DKIM_CANON = relaxed
DKIM_DOMAIN = ${sender_address_domain}
DKIM_PRIVATE_KEY = CONFDIR/dkim.private.rsa1
DKIM_SELECTOR = ${extract{-1}{.}{DKIM_PRIVATE_KEY}}

If you are using another configuration method then you will need to add the DKIM specification to your external SMTP router(s).  This example is based on the Debian/Ubuntu remote_smtp transport that configures DKIM automatically using the  macros defined above.  If you are using another platform, or a smarthost you may have to add the DKIM configuration options shown here:

remote_smtp:
    driver = smtp
    dkim_canon = DKIM_CANON
    dkim_domain = DKIM_DOMAIN
    dkim_private_key = DKIM_PRIVATE_KEY
    dkim_selector = DKIM_SELECTOR
    ...

Restart or reload Exim to begin signing your outgoing email. You may want to examine the headers of an email sent to an external address to verify that it is being signed correctly. GMail makes it easy to see the raw message and adds a header indicating if the signature is valid.

Setting your policy

DKIM is not MIME transparent, and any MIME type conversions are likely to break the validation.   If you send 8bit mime types, expect validation to fail when you send to sites which only support 7 bit formats.

The selector record above indicates that it is in testing mode (t=y).  This should make sure that validation failure does not get your email bounced.  When you are confident, in your configuration, you may want to switch to  indicate the selector (t=s) is strictly for the specified domain, or by removing the t tag entirely.

Adjust your zone configuration(s) and increment your DNS serial number(s) before reloading bind.  Once your changes have propagated through the Internet your new policy will be in place.

Many large mail domains such as gmail and yahoo will provide feedback on your DKIM signing if you add the appropriate DMARC records to your domains DNS entries. I will cover DMARC in a separate posting.

RFC 6651 “Extensions to DomainKeys Identified Mail (DKIM) for Failure Reporting” provides a means to request feedback on signature failures. This requires adding a report record to the <_domainkeys> subdomain. This may point to an email alias rather than a real mailbox. The version of Exim I am currently using does not support this option.

Maintaining Your Keys

You may want to periodically replace your signing key.  You may have as many keys and selectors as required.  There are points in the process where you should wait before proceeding. Use a new selector name and generate your new key and add it to bind as described above. Add an additional selector record, keeping the existing policy and selector records. You may want to add a comment in your zone file indicating when you implemented the new selector. Reload or restart bind.

You will need to wait for your changes to propagate to all of your authoritative name servers before updating Exim prepare your new key and selector record using a new selector.  Once all your secondary servers are serving the new selector, you can update Exim.  Update the DKIM_PRIVATE_KEY macro to use the new key.  Reload or restart Exim and you will be using the new key.

Cleaning up Old Keys and Selectors

Once you stop using a key, you can delete it. You may want to keep a history of the keys you used and their corresponding selector records.  This will allow you to validate old emails.

When you replace a selector, you should keep publishing it for at least one or two weeks.  This will allow any email in transit to be validated.  You can then remove the selector from bind or set the public key to an empty string. Reload you domain after making your change. Either of these changes will indicate to validation software that the selector is not valid.  Any validations done after the selector is removed will fail.

Handling a Key Compromise

If your key is compromised, you will need to replace it promptly.  Document as much information about the compromise as you can.  Communicate the problem to senior management so that they can respond appropriately.

Setup the new key and selector as specified above.  If you are willing to have some email bounce due to validation failures, you can remove the selector when you publish the new selector.   DKIM implementation is not yet to the state that many sites will bounce email for DKIM validation errors. Disable the compromised key as soon as possible.

9 thoughts on “Implementing DKIM with Exim

  1. julian

    Thanks got this working ( I had to change a couple of things:

    1. you need to cd to: /etc/exim4 before you create the keys or move the key there afterwards:

    mv dkim.private.key /etc/exim4/dkim.private.rsa1

    I added the macro to the head of the exim configuration but also had to add the details to the smpt_transport section before it worked:
    ie.
    remote_smtp:
    driver = smtp
    dkim_domain = (added my domain here)
    dkim_selector = rsa1
    dkim_private_key = CONFDIR/dkim.private.rsa1
    dkim_cannon = relaxed

    I use dnsmadeeasy so the final stage was to create a text record (as you describe for bind)

    Thankyou.

  2. Bill Thorsteinson Post author

    The post has been updated based on Julian’s comments. The remote_smtp transport documentation now uses the macros. The macro definitions have been changes to couple the selector id to the extension of the private key file. I hope you will find the revised document easier to use.

  3. Bretticus

    Finally! I how-to that actually works! Thank you so much for your concise and ACCURATE tutorial.

    One note: I am using Bind 9.6…

    I could not make the split work with a large key. Glad this tutorial recommends the smaller key. Will save you loads of time!

  4. Shahid

    I want to know, how to implement DKIM & Exim with multiple subdomains.

    Shahid

    1. Bill Thorsteinson Post author

      I can suggest three options. (I sign for multiple domains using a variation on the second option.)

      • You can sign all outgoing documents from the same domain. This is simplest. The following options require you to define selectors and keys for each sub-domain.
      • Publish the same key for all sub-domains, and sign the document using the same key and selector, but using the sub-domain as the dkim_domain.
      • Build a lookup list enabling you to lookup the key and/or selector for the sub-domain. You will need to publish the appropriate keys and selectors.
  5. Jochen Erwied

    The configuration entry under remote_smtp actually must be written ‘dkim_canon = …’ to be valid.

  6. Lekensteyn

    While looking at implementing DKIM, I found your helpful post. Some notes:

    The o= policy specified on _domainkeys.example.com does not seem to exist anymore, it got removed before the RFC was issued. See https://tools.ietf.org/rfcdiff?url2=draft-allman-dkim-ssp-02.txt. The final RFC mentioned ADSP (http://tools.ietf.org/html/rfc5617#page-7), but its use is not recommended because it is not really used (https://en.wikipedia.org/wiki/Author_Domain_Signing_Practices). I’d suggest to drop that _domainkeys.example.com policy as it may be more confusing than helpful.

    For rotating DKIM keys, I found this document also informative: http://www.maawg.org/sites/maawg/files/news/M3AAWG_DKIM_Key_Rotation_BP-2013-12.pdf

    1. Bill Thorsteinson Post author

      The post has been updated to reflect your comments and updated to reference newer RFCs. I never was comfortable with ADSP as specified. DMARC seems to provide a better solution. Thanks for the feedback.

Comments are closed.