don't_panic
personal and professional blog of mike pultz, technology specialist and serial entrepreneur.

12Feb/1023

Using DKIM in Exim

Since Exim 4.70, DKIM (DomainKeys Indentified Mail - RFC4871) has been supported by default.

The current implementation supports signing outgoing mail, as well as verifying signatures in incoming messages, using the acl_smtp_dkim ACL. By default, DKIM signatures are verified as new messages come in, though no action is taken unless you've implicitly configured rules in the DKIM ACL.

After installing Exim (>= 4.70), you should see debug logs for incoming mail from servers that have DKIM signatures setup- they look like:

DKIM: d=gmail.com s=gamma c=relaxed/relaxed a=rsa-sha256 [verification succeeded]
Verifying Incoming Mail

By default, Exim does not filter any mail based on the validity of the DKIM signature- it's up to you to add ACL rules to control what happens when you receive messages with "bad" signatures.

First add an ACL section for the DKIM processing; this should be included with your other ACL statements:

acl_smtp_dkim = acl_check_dkim

Next, after the "begin acl", section, add your DKIM ACL section, and by default, accept all messages in this ACL:

acl_check_dkim:

	accept

Now you need to decide what kind of rules you want to setup- you probably don't want to put a rule that applies to all domains- though, if the company went to the trouble of adding DKIM signatures to their e-mail, you'd hope they'd get it right, and not publish invalid public keys.

For now, let's add a simple rule for gmail; google knows what they're doing, so their systems should be setup correctly:

acl_check_dkim:

	#
	# check the DKIM signature for gmail
	#
	deny 	message 	= Common guys, what's going on?
		sender_domains 	= gmail.com
		dkim_signers 	= gmail.com
		dkim_status 	= none:invalid:fail

	accept

You can add as many rules, for whatever domains you want in this ACL.

Signing Outgoing Mail

Now that you're checking incoming mail, you probably want to sign mail coming out of your system. This is a relatively easy process, that I've broken down into three steps:

Step1- Generate a private and public key to sign your messages; you can do this easily with openssl:

#openssl genrsa -out dkim.private.key 768

Then extract the public key from the private key:

#openssl rsa -in dkim.private.key -out dkim.public.key -pubout -outform PEM

Step2- Configure the Exim remote-smtp transport to sign outgoing messages, using your new private key. You'll need to pick a domain and a selector for this process.

When remote SMTP servers validate your DKIM signatures, they simply do a DNS look up, based on the selector and your domain- the domain needs to (obviously) be a valid domain you own, that you can add DNS entries to, and the selector can be any string you want. So, for example, using the domain "example.com", and the selector "x", you would add to the remote_smtp transport in Exim:

remote_smtp:
        driver = smtp
        dkim_domain = example.com
        dkim_selector = x
        dkim_private_key = dkim.private.key
        dkim_canon = relaxed

This tells Exim to sign any outbound e-mail, using the domain example.com, the selector "x", and the private key we just generated. The dkim_canon = relaxed, sets the canonicalization method to use when signing messages. DKIM supports "simple" and "relaxed" algorithms- to understand the difference, see section 3.4 of the DKIM RFC.

Step3- add your DKIM public key to your DNS.

The DKIM public key generated above is advertised to other SMTP servers, using a DNS TXT record. In DNS for the domain example.com, add a new TXT record:

x._domainkey.example.com.   TXT v=DKIM1; t=y; k=rsa; p=<public key>

Where "x" is the selector you used above, and <public key> is the public key data (minus the key header/footer text).

When setup correctly, your DKIM text record should look something like this:

# host -t txt x._domainkey.example.com

x._domainkey.example.com descriptive text "v=DKIM1\; t=y\; k=rsa\; p=MIGfMA0GCS
qGSIb3DQEBAQUAA4GNADCBiQKBgQC5k8yUyuyu9UAVHHU7Al4ppTDtxFWsZ6Pqd9NWZnomtewBdz8I
2LJkqmA/3Cyb5Eiaqk4NulPFfDbfA0Lkw7SNyOS9BRN02KGtKIWjFqDwjB99haaWYw9H4IZcuJp0Y
q0kySCdBp/sPP+iTotdBiE85Jakw3tzgYkdvaS05ZUdBwIDAQAB"

(lines breaks were added for readability- your entry should be one continuous line)

This DNS record is referred to as the "selector" record; you need to also setup a "policy" record. The policy record is your domains policy for domain keys- you should start with something like:

_domainkey.example.com. t=y; o=~;

The t=y specifies that you are in test mode and this should be removed when you are certain that your domain key setup is functioning properly. The "~" in the o=~ specifies that some of the mail from your domain is signed, but not all. You could also specify o=- if all of the mail coming from your domain will be signed.

Once you have all of that in-place,  restart Exim, and send out a message using the remote-smtp transport. You should now see a DKIM-Signature: header listed in the message headers, which lists your domain (as d=), and selector (as s=), as well as a signature for this e-mail, which can be validated against your public DKIM key, that you've published in DNS.

For more information, see the Exim DKIM page, or the DKIM RFC.

UPDATE:

Once you've set everything up, you can test your DKIM (and SPF and SenderID, etc) install, by using the port25.com validation service.

Just send an e-mail to check-auth@verifier.port25.com, and it will auto-respond with a validation report

Comments (23) Trackbacks (2)
  1. Thanks for the info. This is the first complete description that I found on the net which is really understandable.

    Theres a small and easily detectable spelling error in acl_check_dkim however:
    sender_domain = gmail.com
    should really be
    sender_domains = gmail.com

    What I am still looking for now is a real-live validation test. dkim-test@testing.dkim.org seems to be down, so except seeing that a dkim sig. is included I cannot yet check if the signature is really fully valid.

  2. @Marcel

    Thanks for the pointing out that mistake Marcel.

    As far as the live validation; port25.com has a similar auto-reply service; just send to check-auth@verifier.port25.com; their system also validates SPF, Sender-ID, and a few other things.

    Cheers,

    Mike

  3. Yup, found it as well in thee meantime. All working nicely now. Only one listserver sometimes breaking the signature, but all other messages seem to work perfect.

    One thing to add is that one should perhaps also add an AuthorDomainSigningPolicy record:

    _adsp._domainkey.yourdromain.something

    as a text record containing “dkim=all” to indicate one signs all messages. Other option would be “dkim=unknown” to indicate only some mail is signed (for testing that is probably recommended).

  4. Thanks for an excellent explanation. Please forgive my newbie question. I have multiple domains sending mail from the same server. Should there be a separate entry for each in the Exim configure file. How should they look?

  5. Jim,

    You don’t necessarily have to setup something different per domain- you can sign all your outbound e-mail from the same domain/selector.

    If you wanted to have a different config, based on the domain of the sender address, you could technically do a lookup based on the sender domain, and return values for each of the dkim_domain, dkim_selector and dkim_private_key values.

    Then you could store these values in a lookup db, or SQL database by sender domain.

    So as a quick (un-tested) example, you could do something like this:

    remote_smtp:
    driver = smtp
    dkim_domain = $sender_address_domain
    dkim_selector = x
    dkim_private_key = ${lookup pgsql{select key from dkim where domain = ${quote_pgsql:$sender_address_domain}}{$value}}
    dkim_canon = relaxed

    Which would use a common selector of “x”, the domain of the sender e-mail, and a SSL key looked up from a PostgreSQL database by domain.

  6. @mike
    Thanks. I am using the same domain/selector and getting a “pass” from check-auth@verifier.port25.com for “DKIM check” for all domains.

    Again, thanks for the excellent tutorial.

  7. Thanks a bunch for this great tutorial. I have a couple questions though. For starters, I’m on Debian and Nginx and I installed exim with this command: “apt-get -t lenny-backports install exim4″ which installed version 4.71. I then followed your tutorial for signing emails but my emails still don’t get signed. Step 1 seems to have gone off without a hitch as I can verify that both those files now exist and have data that looks right.

    I added the following lines to /etc/exim4/conf.d/transport/30_exim4-config_remote_smtp under driver = smtp:
    dkim_domain = dostuffright.com
    dkim_selector = whizbang-dkim
    dkim_private_key = /dkim.private.key
    dkim_canon = relaxed

    And restarted exim. I then added the txt record to my domain as you instructed. The problem is that my emails don’t seem to be getting signed. I’ve tried looking at the raw headers and sending emails to six different rotator services and I’m quite positive my emails aren’t getting signed. So my questions are:

    1) Is there something I need to do to turn signing on besides adding those lines to that file?
    2) What is the “selector”? Is it just some random word that has to be in both the header and the txt record?
    3) Is dkim_private_key just the path to the dkim public key on my server? That’s what I’m assuming it is.

    Thanks a bunch!
    Beau

  8. @Beau

    Hey Beau,

    I’m not 100% familiar with the Debian install; it seems to break the file up into components- usually I install from source and have just one big configure file- but this looks like the right spot.

    1) That should be all you need to at least sign out-going messages.

    2) Yup, the selector is just a random word that is specified in your config; it uses this word to do the DNS lookup against your domain- most of the time it’s kept really short (like a single character), but as long as it’s a valid hostname, it’s fine.

    3) Yes, that’s the full path to the private key; so in your example, your private key would have to be in /- is that correct? maybe it’s not signing your e-mails because it can’t find the key?

    Also, have you confirmed that messages are being sent through exim? can you see messages coming in/out in your main exim log file?

    Mike

  9. Hi Mike,

    Thanks for this great topic, I did that but I got permerror (key failed) for dkim and no sig for domainkeys.

    Authentication-Results: mta1045.mail.sk1.yahoo.com from=developers-heaven.net; domainkeys=neutral (no sig); from=developers-heaven.net; dkim=permerror (key failed)

    DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=developers-heaven.net; s=x; h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-ID:Date:Subject:To:From; bh=dZwg0roqeT4Z81/Gk6beBXednOetUVdfKD0HSi0smMw=; b=iwjc2Fu7aQAZ3nSb9Asu1DaPh27Lut0Ig2xEZ9FS5Frwnq57fJa8Vo4iaOKu/RokAIDVtgMUtZoh0JyBlpG18yJJILwuPO4ORzstS/fP9EGxfyLZBDZLtcSQOFRhc/dr;

    DKIM check details:
    ———————————————————-
    Result: permerror (invalid key: error reading public key: 3071417264:error:0D07209B:asn1 encoding routines:ASN1_get_object:too long:asn1_lib.c:142:;3071417264:error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header:tasn_dec.c:1281:;3071417264:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:380:Type=X509_PUBKEY;)

    May you please guide me how to fix.

  10. Thanks, this was incredibly helpful.

  11. The instructions need to say something about placement of the file “dkim.private.key”. As the example is written, the file gets placed in whatever is the current directory when you run ‘openssl’ and it isn’t clear how Exim finds it, since the remote_smtp definition doesn’t have an absolute path.

  12. Right- well- the file can be put wherever you want;

    the dkim_private_key option takes either the RSA key inline, or, if it starts with a /, then the full path to the file.

    Whatever path, obviously, needs to be accessible by the user executing the remote_smtp transport.

    Mike

  13. I’d love to pay you to setup DKIM on my server to sign outgoing messages because these instructions are useless for someone who doesn’t really understand in depth how to use command lines and exim.

    I’ve completed step 1. Generating a key using Open SSL… that part was easy to figure out…

    Step 2…
    Configure EXIM… wtf… where? What file? You give the text err command (not sure if it’s something I need to actually add to a file and if so what file is it, or if it’s a series of commands).

    Step 3 should be easy as well as I know how to go into WHM and edit my dns record BUT I don’t really understand how to actually get the public key I generated in step 1 (I simply used the command line to generate it.

    I DO NOT get most of this stuff but I can follow directions and I can understand it vaguely.

    Keith

  14. Is there a way to sign only ‘some’ outgoing mails?
    like authenticated smtp users or if the ‘from’ header matches a specific domain?

    Btw.: Here is how i threat incoming mail, might be interesting for someone looking for an example:

    #—————————–
    acl_check_dkim:

    # add a header to every message
    warn
    add_header = :after_received:X-CDC-DKIM: signer=”$dkim_cur_signer” status=”$dkim_verify_status” reason=”$dkim_verify_reason”
    log_message = CDC-DIKM: signer=”$dkim_cur_signer” status=”$dkim_verify_status” reason=”$dkim_verify_reason”

    # defer temporary problems
    defer
    dkim_status = invalid
    log_message = CDC-DIKM: Deferred
    message = Failed to validate DIKM Signature, problem may be temporary.

    # deny fakes
    deny
    dkim_status = fail
    message = DIKM Signature invalid.
    log_message = CDC-DIKM: Failed

    # accept the rest (correctly signend or unsigned)
    accept
    #—————————–

  15. Hey Keith,

    step 2- The main exim config file (it’s literally called “configure”) is the file you’re looking for- it’s exact location can vary depending on your OS- I install exim from source, and always put mine in /usr/local/exim/configure; most linux distros will likely put it in /etc- I would just try looking for some docs that indicates where it is, or just do a search on your machine.

    step 3- the public key file is just a plain text file that you can open with any text editor; so use a command line text editor (or cat or more it on the command line), or simply download the file using FTP (or whatever you have) and open it up in a text editor

    There will be a leading a trailing title (BEGIN/END certificate) that you need to cut out for your DNS record- and then the key is broken up into multiple lines- what you need to do, is turn this into one long line for your DNS entry.

    (I think some BIND implementations can take this in multiple lines, but I’m not sure what you’re running)

    Let me know if that helps,

    Mike

  16. I agree with the rest, simple explanation, nearly all necessary information.

    But it might be a good idea to add the hint, that the DNS entry containing the public key MUST start with the v= field, else validation fails. Just cost me half a day banging my head against this damn problem :)

    (Yes, I know, the RFC states it very clearly)

  17. Yup- good point- I can see that being a pain if you missed that-

    Cheers,

    Mike

  18. Thanks for this writeup. It helped me to get dkim to work in exim.
    To test you can use
    /usr/sbin/exim -v check-auth@verifier.port25.com
    From: www@yourdomain.com
    To: check-auth@verifier.port25.com
    Subject: dkimtest
    test
    ^D

    I got this error there somewhere
    LOG: MAIN PANIC
    DKIM: signing failed (RC -101)

    I guessed this was a problem with the generated key.
    I used this to make a new keypair:
    openssl genrsa -out private.key 1024
    openssl rsa -in private.key -pubout -out public.key

    For the rest i followed your recept. And then it worked.

  19. Looks like I’m almost there. Can you tell me why I’m not getting a signature “domainkeys=neutral (no sig)”? Looks like dkim passed though.

    Authentication-Results: mta1185.mail.sk1.yahoo.com from=removed_real_domain.com; domainkeys=neutral (no sig); from=removed_real_domain.com; dkim=pass (ok)

    DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=simple/simple; d=removed_real_domain.com; s=x; h=From:Message-Id:Date; bh=70yYafhTML7Wlue1K9LneDU8xUBRS7VyKI/lrGFlzyg=; b=UdWSZWWuwAciNYGKLcewIIDVgx8zvHQvPARGIxkyhMCbAaG+tRvgGNkrZehMBSqjCpFMsINwesmtIQ8fnFN+5vzv0+SQ9PPw7LkVO8bK5zMW/9/Hx5AupMqCb4GEQJdQ;

  20. Hey-

    Not sure- but if you posted your config,, and the output from dig for your key, I could look-

    Mike

  21. I am looking to do this as well but our server is running Exim 4.69 I believe. I’m still a bit confused about the DKIM keys. We have hosted Google Apps email that is signing dkim but our server also sends out some emails from our content management system. Would our server have it’s own private dkim key or can it use the same key google apps is using? Such a noob, sorry.

  22. @Chris, I just want to point something quickly, 8 or so months ago I was hit by a zero-day exploit that affected version 4.69. I’d suggest upgrading as soon as possible, as the exploit is out in the wild and automated.

    @Mike, thanks for this excellent tutorial.

  23. You can also send and email to mailtest@unlocktheinbox.com, it will respond and tell you what you have setup wrong and gives you information on how to fix it.


Leave a comment

(required)