Home > Server Admin > DKIM and DomainKeys for qmail

DKIM and DomainKeys for qmail

March 19th, 2009

DomainKeys and its successor DomainKeys Identified Mail (DKIM) are technologies that allow organizations to take responsibility for a message. This is done by cryptographically signing an email as it leaves an organization in route to its destination. The signature can be verified using the DNS system to establish trust. In theory the technologies help cut down on spam by proving a message originated from the domain it says it does.

Support for DomainKeys in qmail has existed for a while thanks to a patch by Russel Nelson. Kyle Wheeler created a set of wrapper scripts that can be used to provide support for DKIM and DomainKeys. Mihai Secasiu has some wrapper scripts similar to Kyle’s that provide support for DKIM via the libdkim library instead of Perl’s Mail::DKIM module.

The current methods take different approaches to implement DKIM and DomainKeys. The DomainKeys patch creates a single program, qmail-dk that is called before qmail-queue. This program signs or verifies all incoming messages (that may later become outbound) based on the existence of the DKSIGN and DKVERIFY variables. The DKIM wrapper scripts wrap qmail-remote to sign messages and wrap qmail-queue (or qmail-dk) to verify incoming messages. This can be easier understood by looking at the qmail big picture.

I tend to agree with separate programs for signing outbound messages and verifying inbound messages as this allows signing all outbound messages, even those (such as NDRs) that never pass through qmail-queue. I also prefer patching qmail as it tends to be a little easier and requires less configuration after qmail is installed.

In this post I will show you how to patch qmail to support DKIM as well as DomainKeys. My qmail DKIM/DomainKeys patch uses neither Russel Nelson’s DomainKeys patch nor Kyle Wheeler’s DKIM/DomainKey wrappers, but borrows ideas from both. My patch uses the libdomainkeys and libdkim libraries to do the actual signing and verifying. Rather than creating two new programs, I patch qmail-smtpd (for verifying) and qmail-remote (for signing) directly.

I’ll do my best to provide step by step instructions for patching and installing for you non-Gentoo users, but in my next post I’ll share my ebuild which does it all for you.


1. Install libdomainkeys

The libdomainkeys library is used to sign and verify DomainKeys signatures.

$ wget http://downloads.sourceforge.net/domainkeys/libdomainkeys-0.69.tar.gz
$ tar -xzf libdomainkeys-0.69.tar.gz
$ cd libdomainkeys-0.69
$ make
(If you get errors during make, edit the Makefile and add -lresolv to the end of the LIBS line)
$ sudo install -m 644 libdomainkeys.a /usr/local/lib
$ sudo install -m 644 domainkeys.h dktrace.h /usr/local/include
$ sudo install -m 755 dknewkey /usr/local/bin
$ cd ..


2. Install libdkim

The libdkim library is used to sign and verify DKIM signatures. You’ll need g++ to compile this on your system. The library claims to be portable, but I needed to patch it to get it to compile on my Gentoo box. I’ve also included a (slightly modified) patch from Mihai Secasiu that makes working with libdkimtest much easier.

$ wget http://downloads.sourceforge.net/libdkim/libdkim-1.0.19.zip
$ wget http://www.bltweb.net/qmail/libdkim-1.0.19-linux.patch
$ wget http://www.bltweb.net/qmail/libdkim-1.0.19-extra-options.patch
$ unzip libdkim-1.0.19.zip
$ cd libdkim/src
$ patch -p2 < ../../libdkim-1.0.19-linux.patch
$ patch -p2 < ../../libdkim-1.0.19-extra-options.patch
$ make
$ sudo make install
$ cd ../..


3. Patch and install qmail

I’m currently using John Simpson’s qmail Combined Patch Set for my qmail installation. The instructions below highlight how to apply my DKIM/DomainKeys patch on top of John’s combined patch. I’d highly recommend checking out John’s combined patch as it is about as close as you can get to an actively maintained qmail.

I’m not attempting to describe or document John’s patch in anyway in this post, as John runs an excellent site about qmail (qmail.jms1.net) that contains far more information than is contained here. Do not attempt to proceed without reading through John’s documentation as well as the rest of this post.

$ wget http://cr.yp.to/software/qmail-1.03.tar.gz
$ wget http://qmail.jms1.net/patches/qmail-1.03-jms1.7.08.patch
$ wget http://www.bltweb.net/qmail/qmail-1.03-jms1.7.08-dkim-r1.patch
$ tar -xzf qmail-1.03.tar.gz
$ mv qmail-1.03 qmail-1.03-jms1.7.08
$ cd qmail-1.03-jms1.7.08
$ patch < ../qmail-1.03-jms1.7.08.patch
$ patch -p1 < ../qmail-1.03-jms1.7.08-dkim-r1.patch
$ sed -ie '1s/$/ -DDKIM/' conf-cc
$ make
$ make man
$ sudo make setup check
$ cd ..


4. Configure DKIM/DomainKeys signing

Signing is done by qmail-remote and is controlled by the dksign control file. Signatures are created using a private key on your system, and verified by a public key stored in the DNS for the email domain.

Generate keys

Before you can sign an email, you must create at least one public/private key pair. You should create key pairs for every domain you wish to sign. To create keys for example.com:

# mkdir -p /etc/domainkeys/example.com
# cd /etc/domainkeys/example.com
# dknewkey default 1024 > default.pub
# chown -R root:root /etc/domainkeys
# chmod 640 /etc/domainkeys/example.com/default
# chown root:qmail /etc/domainkeys/example.com/default

It is very important that the default file be readable only by root and the group which qmailr (the qmail-remote user) belongs to. This is the private key used for signing messages and, if compromised, would allow others to sign messages as your domain.

Now add a TXT entry to the DNS for default._domainkey.example.com containing the quoted part in the /etc/domainkeys/example.com/default.pub. NOTE: You normally want to include the quotes!

Configure control files

Create a file /var/qmail/control/dksign containing one line:

/etc/domainkeys/%/default

The % will be replaced with the domain name in the From: header (or the Sender: header if it exists). If no file exists for the given domain, parent domains will be tried. For example if the message is from foo@bar.example.com, /etc/domainkeys/bar.example.com/default will be tested first. If the file does not exist, /etc/domainkeys/example.com/default will be tested. If no key can be found, the message will not be signed. If a key exists, but cannot be read or contains invalid data, the message will not be sent and will remain in the queue until the problem is fixed.

If you do not create the /var/qmail/control/dksign file, no messages will be signed.

Test outbound signing

Now that DKIM/DomainKeys signing is configured, you can test it by sending an email to sa-test (at) sendmail dot net. This reflector will reply (within seconds) to the envelope sender with a status of the DomainKeys and DKIM signatures.

If you experience problems, consult the qmail-remote man page or post a comment below and I’ll try to help.


5. Configure DKIM/DomainKeys verification

Verification is performed by qmail-smtpd and is controlled by the DKVERIFY environment variable. Messages are only verified if DKVERIFY is set and RELAYCLIENT is not set. You may control which IP addresses are verified using the tcpserver access file (sometimes stored in /etc/tcprules.d/tcp.qmail-smtp).

When verifying a message, the contents of DKVERIFY are checked against the status of the DomainKeys and DKIM results. Each test result is represented by a letter. DKVERIFY should contain a series of letters for DomainKeys results, a comma, and then a series of letters for the DKIM results. If the letter is uppercase, the message will be rejected (hard error). If the letter is lowercase, the message will be deferred (soft error). The DKVERIFY variable can be set but empty, in which case messages will be verified and an Authentication-Results: header will be added but all messages will be accepted regardless of status.

The letters for DomainKeys results are:

Code Status Description
A OK The message contained a signature which correctly matched the contents of the message.
B BADSIG The message contained a signature which DID NOT correctly match the contents of the message. The signature may be forged, or the content may have been changed after the original server applied the signature.
C NOSIG The message did not contain a DomainKey-Signature header, or contained one which was missing a required field, or had a signature header without a “From:” header.
D NOKEY The public key needed to verify the signature does not exist (i.e. the authoritative DNS server for the domain says that the TXT record which should contain the key does not exist.)
E BADKEY The public key which was found in DNS is not usable.
F CANTVRFY The public key needed to verify the signature cannot be found, because the DNS server which should have the key is not responding, or returned a temporary error condition. The domainkeys specification says that the server SHOULD treat this as a soft error, telling the client to try their delivery again at some point in the future.
G SYNTAX The message is not in the proper format. This could be an improperly formatted email address, a duplicate “From:” header in the message, or any number of things which “confuse” the program.
H NORESOURCE Out of memory. The domainkeys specification says that the server SHOULD treat this as a soft error, telling the client to try their delivery again at some point in the future.
I ARGS Arguments are not usable
J REVOKED The key which was used to generate the signature has been revoked.
K INTERNAL There was an internal error in the libdomainkeys library

The letters for the DKIM results are:

Code Status Description
A OK The message contained a signature which correctly matched the contents of the message.
B FAIL The message failed verification
C BAD_SYNTAX The DKIM-Signature header could not be parsed or had bad tags/values
D SIG BAD RSA verify failed
E SIG BAD (testing) RSA verify failed but testing
F SIG EXPIRED Signature is expired (x= is old)
G SELECTOR INVALID Selector doesn’t parse or contains invalid values
H SELECTOR MISMATCH Selector granularity doesn’t match
I SELECTOR REVOKED The selector was revoked (p= is empty)
J DOMAIN TOO LONG The domain name is too long to request
K DNS TEMP FAIL Temporary DNS error requesting public key
L DNS PERM FAIL Permanent DNS error requestion public key
M PUBLIC KEY INVALID Public key isn’t valid or can’t be parsed
N NO SIG The message contains no DKIM signatures
O NO VALID SIG The message contains no valid signatures
P BAD BODY HASH The message body doesn’t verify
Q ALGORITHM MISMATCH The selector (h=) doesn’t match signature (a=)
R STAT INCOMPAT Incompatible v=

I recommend a DKVERIFY value of DEGIJKfh,CGHIJMQRkl. This will only reject improperly formatted messages. Messages that don’t verify will still be allowed. I would advise against rejecting messages that don’t verify as there are still some problems with DomainKeys and DKIM (such as mailing lists). Rather than rejecting bad signatures, incorporate the Authentication-Results header into your broader spam prevention strategy.

The Authentication-Results header

All messages received by qmail-smtpd when DKVERIFY is set will add an Authentication-Results header to the incoming message. This header conforms to the IETF internet draft. Here’s an example from one of my emails:

Authentication-Results: bltweb.net; domainkeys=pass (ok); dkim=pass (ok)


6. Examples

Here are some examples to help you configure your box. Anything that normally should be private is made up.

Keys

For my bltweb.net domain name, here’s what my keys look like (these are not the actual keys installed on my system, those are private):

$ ls -l /etc/domainkeys/bltweb.net
total 8.0K
-rw-r----- 1 root qmail 887 Mar 4 18:49 default
-rw-r--r-- 1 root root  254 Mar 4 18:49 default.pub

$ cat /etc/domainkeys/bltweb.net/default.pub
default._domainkey IN TXT “k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbFnVeFZdlud6/xvLoMt2/g9qrQzZjg6mopp4IYgPwNxRfQTsvYJo4dxP/aIt5UcL1YWtEnOm6/VL+wzj33WvVGL8GWdJDcUWGpCOysWuKasH/sXCaxoZSFMNM02K5pOgzaIVinWZNLIv+yaDSnBC3zb35HoQOnU4KLySECWPRuQIDAQAB”

$ sudo cat /etc/domainkeys/bltweb.net/default
—–BEGIN RSA PRIVATE KEY—–
MIICXAIBAAKBgQDbFnVeFZdlud6/xvLoMt2/g9qrQzZjg6mopp4IYgPwNxRfQTsv
YJo4dxP/aIt5UcL1YWtEnOm6/VL+wzj33WvVGL8GWdJDcUWGpCOysWuKasH/sXCa
xoZSFMNM02K5pOgzaIVinWZNLIv+yaDSnBC3zb35HoQOnU4KLySECWPRuQIDAQAB
AoGAXuZniI2JuwK8Pg4LghEmhKK0waKnmIubnfYuVis+0XrKVEiJPoh1xSevfd7n
K3IDJQ9By8K8a8b3gGtH7fX3ktWWFNz++DpewvWzFksC++7rhZoarBC1puWxVNYI
M4xdqEtKXHIzaj3nRHM76RBD5htqa2hZkIDqfK7vDVZUkEECQQD0C5pmMGaBjO1K
bC0hs8dMogxsrnwooIiHg1FO0WhOXGxKYuQGxXjR/fNz8gUyeicCPB3/piKaucGT
OY1X0b9FAkEA5dHhTQTnkMD0pLow6yXTehy8NWzmIl9/EeIQu9HoXVpIGePy4Mrr
ydJzaisQ+RJ8dO5C+1PeR89IRYdeGS/l5QJBAKHRG8SMbTuTdTe2uMozCYA/pttd
asgJgd3Q7dXENlRXJhrArY/r2ivrJkUIAfgxVLI/qGh+AU30w2zaaWUEl70CQEe7
wv8vULg2AiaIl0xOejvbTEPAwfRoqlkCnwaA9m5tB6RNKjpQHFjaf3vcBWg5BO/a
jr2z5+WyJXTOU+i4sqECQC/lZY/0/cgEyyD0UL+oqYrVmlIm5Sc9Pnsu1fIRsfgC
SnHS8/eTTUxNERGIYso4+wVFHR82oR8hucVYa8iY7CM=
—–END RSA PRIVATE KEY—–
Signing Configuration

To sign emails for all domains for which I have a key in /etc/domainkeys, I set the control/dksign configuration file:

$ ls -l /var/qmail/control/dksign
-rw-r--r-- 1 root root 31 Mar 17 14:02 /var/qmail/control/dksign

$ cat /var/qmail/control/dksign
/etc/domainkeys/%/default
Verify Configuration

Here’s an example of my /etc/tcprules.d/tcp.qmail-smtp file. Make sure you regenerate the cdb file after editing your tcp.qmail-smtp file!

# Connections from localhost are allowed to relay
127.0.0.1:allow,RELAYCLIENT="",RBLSMTPD=""

# Everyone else can’t relay unless they auth
# All signed mail is allowed, even if it’s bad, but still prepend the
# Authentication-Results header
:allow,DKVERIFY="",AUTH_UNSET_DKVERIFY=""

# Or if I want to use the recommend DKIM settings, comment out the line
# above and use
# :allow,DKVERIFY="DEGIJKfh,CGHIJMQRkl",AUTH_UNSET_DKVERIFY=""


7. Finished

That’s it. You should now have a qmail installation capable of signing and verifying messages. More information is contained in the qmail-smtpd and qmail-remote man pages.

If you have any comments or find any bugs, please feel free to post a comment below.

Server Admin , , , ,

  1. February 22nd, 2016 at 04:29 | #1

    A smooth sea never made a skillful mariner, neither do uninterrupted prosperity and success qualify for usefulness and happiness. The storms of adversity, like those of the ocean, rouse the faculties, http://weddinginvitations4u.net/invite:161961505706220130 and excite the invention, prudence, skill and fortitude or the voyager. The martyrs of ancient times, in bracing their minds to outward calamities, acquired a loftiness of purpose and a moral heroism worth a lifetime of softness and security.

  2. June 4th, 2017 at 17:23 | #2

    Unquestionably believe that which you stated. Your favorite justification seemed to be on the net the simplest thing to be
    aware of. I say to you, I definitely get irked while people
    think about worries that they plainly don’t know about.
    You managed to hit the nail upon the top and also defined out the
    whole thing without having side effect , people could
    take a signal. Will probably be back to get more.
    Thanks

  3. June 7th, 2017 at 21:26 | #3

    Hmm it looks like your website ate my first comment
    (it was extremely long) so I guess I’ll just sum it up
    what I wrote and say, I’m thoroughly enjoying your blog.

    I as well am an aspiring blog writer but I’m still new to everything.
    Do you have any helpful hints for inexperienced blog writers?
    I’d really appreciate it.

Comment pages
  1. July 4th, 2011 at 09:48 | #1
  2. February 17th, 2012 at 03:09 | #2
  3. April 30th, 2013 at 04:13 | #3
  4. March 8th, 2014 at 05:30 | #4