Setting up Sendmail on a dynamic IP, part3: DKIM

By Andrew Smith

Compared to part1 and part2, DKIM took a lot of effort to understand. The concept is relatively simple but the documentation is shit. It doesn’t help that there are at least three implementations of DKIM, though eventually (after some days) I figured out OpenDKIM is the only one that still matters.

In typical open source way the guides on the website are.. the ASCII readme files that come with the distribution.. which I don’t have a problem with in principle but in practice never works well. Plain text is hard to read in a browser. Anyway, that wasn’t the real problem. The real problem is that the documentation is in bizarre order, is definitely not for newbies, and has very significant parts missing. So as I usually do – I wrote my own notes and I’ll publish them here.

Step1: Install OpenDKIM

OpenDKIM doesn’t come with Slackware, and strangely doesn’t even have a slackbuild on slackbuilds.org. So I had to get the source and compile it myself. Not much of a problem since I’m a slackware user, but yes a problem because I’m a linux user.

Turns out the currently latest version of OpenDKIM (2.9.0) doesn’t compile on linux of any kind, because the code is using some BSD-only functions and the configure script didn’t know to replace them with non-BSD versions. I found a bug report and a guy in there claimed (in a backwards kind of way) that the bug will be fixed in the next version, but I didn’t have the time to wait for that so I got the previous version, 2.8.4, which worked.

I normally don’t mind when new software installs into /usr/local, but I hate when it starts to use ridiculous directories like /usr/local/var. After trying to accept that for a day I went back and started over – making sure it’s installed into /usr instead, here are the commands I used:

./configure --prefix=/usr
make
make install
ldconfig

The the rest of the time was spent figuring out why the instructions in the official readme are so retarded.

Step2: Generate keys

For reference: in the rest of this guide I will use my setup as an example. In this setup my SELECTOR is littlesvr-dkim (it can be any random string, though might as well call it your domain name).

First thing you need to do is generate a public/private key pair. This is actually quite easy because opendkim comes with a tool that will do it. The command sequence I used was:

mkdir /etc/dkim
cd /etc/dkim
chmod 700 .
opendkim-genkey -s littlesvr-dkim
ls -al

Make sure that the dkim directory and the files in it (particularly the keys) are only readable by root. If you’re wondering as I did – opendkim-genkey will generate a 1024bit key by default, which should be good enough for a number of years to come.

The command will generate two files, in my case named littlesvr.ca.private and littlesvr-dkim.txt (the latter is the public key in a DNS record format).

Step3: Configure opendkim

This almost doesn’t deserve its own step. First copy the sample config from /usr/share/doc/opendkim/opendkim.conf.simple to /etc/dkim/opendkim.conf and then the only change you have to make inside is the KeyFile path – it was /etc/dkim/littlesvr.ca.private for me.

Step4: Create a startup script

Usually a startup script is something you make when you’re done, but in this case you really want it to begin with, because the command to launch opendkim is complicated, so take your time now to create the script /etc/rc.d/rc.dkim, use this below as a starting point (replace any references to my server littlesvr.ca with your own server, private key path, and selector:

#!/bin/sh
# Start/stop/restart the domain keys identified mail milter daemon.

# Comma-separated (no spaces) list of the domains you want
# opendkim to work with:
MYDOMAINS=littlesvr.ca
# The name with full path of the private key you generated with opendkim-genkey
PRIVATE_KEY=/etc/dkim/littlesvr-dkim.private
# Your SELECTOR:
SELECTOR=littlesvr-dkim

dkim_start() {
   if [ -x /usr/sbin/opendkim ]; then
     echo "Starting domain keys identified mail milter daemon /usr/sbin/opendkim"
     /usr/bin/logger "Starting domain keys identified mail milter daemon /usr/sbin/opendkim"
     /usr/sbin/opendkim   -l -p local:/var/dkim/dkim.sock 
     -d $MYDOMAINS -k $PRIVATE_KEY -s $SELECTOR
   fi
}

# Stop dkim:
dkim_stop() {
   /usr/bin/logger "Stopping domain keys identified mail milter daemon"
   killall opendkim
   if [ -S /var/dkim/dkim.sock ]; then
     rm /var/dkim/dkim.sock
   fi
}

# Restart dkim:
dkim_restart() {
   dkim_stop
   sleep 1
   dkim_start
}

case "$1" in
'start')
   dkim_start
   ;;
'stop')
   dkim_stop
   ;;
'restart')
   dkim_restart
   ;;
*)
   echo "usage $0 start|stop|restart"
esac

You want to make sure that the script has execute permissions and you run it automatically from rc.local. I expect in most setups it doesn’t matter when it starts relative to Sendmail.

Step5: Set up DNS records

If everything worked so far – you can go ahead and publish the appropriate DNS records. It’s safe to do this now because those records won’t be used until your sendmail is reconfigured (next step).

This is probably what the official setup guide is the worst at. Basically it tells you to go and read the RFC – yeah, thanks.
First you have to create a TXT record for _domainkey.whateveryourserver.ca – which in my case is _domainkey.littlesvr.ca. The value of the record should be “o=-” which means all the outgoing mail will be signed (as opposed to some of it).

The second record you have to create will have your actual key. This is another TXT record, this time for SELECTOR._domainkey.whateveryourserver.ca (in my case for littlesvr-dkim._domainkey.littlesvr.ca) with the value from the .txt file generated by oepndkim-genkey. It looked to me like I had to remove some whitespace and cut out everything from that file between the () brackets, but maybe I didn’t have to. What I ended up using as a value is this (wrapped for the blog):

v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC36RsUh9bccxZAy
2NuOr4nfD3+nxXJQVsGCt3iF/pEwZZjkkqzfiGUkfTDMICviDgfcqES
aQg8GPwFd/IKTSErQy09g2XPtvbro3CtAHnarkjTji4RUqiptcdk3H
K83rKdx5hXH0mVITojobn+dsT1+pqToBt4TTQ0CfY2SyiDVQIDAQAB

These records obviously need to be created in your DNS server. If you have your own – you know what to do. In my case – I went to my dynamic DNS provider’s website and created them there. They are static, and don’t need to be updated as my server’s IP changes.

You can test your changes with dig. I ran these two:

dig _domainkey.littlesvr.ca TXT
dig littlesvr-dkim._domainkey.littlesvr.ca TXT

I couldn’t figure out whether opendkim-testkey didn’t work or it was silently telling me that my setup was correct.

Step6: Update sendmail configuration

Very little needs to be done here, most of the work is the typical pain of updating sendmail config files. But if you’ve set up your own sendmail, you’re probably able to remember which .mc file had your (pre-compiled) configuration. Mine was /usr/share/sendmail/cf/cf/sendmail-slackware-tls-sasl-andrew.mc

I edited it and added these lines right after LOCAL_DOMAIN (but probably doesn’t matter where):

dnl# DKIM Support (added 4 feb 2014)
INPUT_MAIL_FILTER(`dkim',`S=local:/var/dkim/dkim.sock')dnl

Yours will be the same assuming you configured opendkim with the same prefix as I recommended.

To update the actual configuration I ran ./Build sendmail-slackware-tls-sasl-andrew.mc (in that dir) and copied the built sendmail-slackware-tls-sasl-andrew.cf overwriting /etc/mail/sendmail.cf

Before restarting sendmail – double check your DNS settings, and that opendkim is running.

Step7: Test

Once your DNS records are set and your Sendmail is passing emails through OpenDKIM – you’re ready to test it, and you should test it as soon as you can, because if something didn’t work right – you may end up with sent mails that won’t arrive at their destination and you won’t get a bounce message to know it happened.

Luckily testing is quick and easy – just send an email to check-auth@verifier.port25.com (the same service I used in part2).

If you’ve done the same stuff I have in these three blog posts – you should be in awesome shape spam-wise! I should have done this years ago, if only I had a guide like this I would have :)