Before proceeding with this tutorial, and at the risk of possibly overstating what is obvious to some, I want to make three things clear:

  1. DomainKeys and DKIM are not the same thing. They are two different methods of signing and verifying email.
  2. DomainKeys is now an outdated technology (the nicer sounding technical term is an “historical protocol”). Although some mailers may still sign their mail with DomainKeys, the technology has been abandoned and is no longer being developed. Basically, you no longer need it.
  3. DKIM (which stands for DomainKeys Internet Mail) is the new standard, and is therefore the method I recommend for those looking to sign outgoing and/or verify incoming emails.

I’ve already written an easy-to-follow tutorial explaining how to use OpenDKIM to set up DKIM on a RHEL or CentOS server running the Postfix mail server. It gets lots of comments, most of them positive, and the most common one being:

“Thanks, I got DKIM working. Now how do I get DomainKeys working, too?”

My usual reply is:

“You don’t have to. DomainKeys is outdated. DKIM is the new standard. You won’t get any extra credit from receiving mail servers for using both, so you can just use OpenDKIM and you’re good to go.”

However, that same question keeps getting asked… so I’ve reluctantly decided to write this follow-up tutorial for those who just can’t sleep at night unless they have DomainKeys working.

Again, DomainKeys is an outdated standard, so under no circumstances would I ever recommend using only DomainKeys to sign your outgoing mail. Eventually, it will go the way of the Dodo. But, if you really, really, REALLY want to use DomainKeys, then the only way you should even consider doing do is to first set up DKIM using OpenDKIM (by following the steps in my earlier tutorial), and then set up DomainKeys by re-using and relying on as much of of your DKIM installation as possible.

Before you start

This tutorial assumes the following:

  • You are running a “modern” RedHat-compatible Linux distro (RHEL 5, CentOS 5, Fedora, etc). I was running CentOS 5.5 when I did this.
  • You are running Postfix 2.3.3 or better (do postconf -d mail_version to check).
  • You have already set up DKIM using OpenDKIM as explained in my earlier tutorial. I make updates to that tutorial from time to time, so it’s a good idea to read through it again just to compare your configuration to make sure it’s up to date.
  • Your Postfix configuration is  currently working and successfully signing outgoing mail using DKIM (this is very important – you don’t want to troubleshoot two or three programs at once).
  • The necessary commands in this tutorial are done as root. If you don’t know what that means, then you probably shouldn’t be doing this. You may be able to get away with just using sudo, but I wanted to make sure I didn’t run into any path issues, so I just used su – root.

Of course, these steps will most likely work with slighter earlier or slightly later versions of RHEL/CentOS and/or Postfix. I used CentOS 5.5 and Postfix 2.8 to get this working on my system.

Set up DKIM first

I know I sound like a broken record, but to use this tutorial, you must already have OpenDKIM running on your system using the configuration explained in my earlier tutorial, because much of configuration of DomainKeys in this tutorial relies on file locations and settings from my DKIM tutorial. If your configuration is different, and you still choose to follow this tutorial, you do so at your own peril. :)

Download and install dk-milter

After you’ve got DKIM installed and working with OpenDKIM per my other tutorial, you’re ready to install dk-milter, which provides DomainKeys support. We’ll be using dk-milter version 1.0.2-1, which was the final version before the project was abandoned.

IMPORTANT: Do not confuse the dk-milter package with the dkim-milter package. dkim-milter is for DKIM, and it’s the antecedent to the OpenDKIM program. I recommend OpenDKIM because it’s much easier to set up, more flexible, and better supported, which is why it’s the I only method I use. dk-milter is for DomainKeys only.

TopDog Software has been kind enough to build RPMs for both the 32-bit and 64-bit versions of RHEL 5 / CentOS 5.

UPDATE: As of February 2011, the RPMs provided by TopDog Software are no longer available. Not to sound repetitive, but this should come as no surprise as DomainKeys is a deprecated and outdated technology, which has been completely replaced by DKIM, so these files are going to be harder and harder to find. A Google search of the filenames might help, and a visitor named Sooraj has posted a comment about building your own RPMs from dk-milter source files. To help in your search, the filenames for the final 32-bit and 64-bit RPMs were: dk-milter-1.0.2-1.i386.rpm and dk-milter-1.0.2-0.x86_64.rpm. You can also download the source code from the original dk-milter SourceForge page. Beyond that, you’re on your own. Good luck!

Presuming you can find (or build) an RPM for dk-milter, install the appropriate one for your system with either:

# rpm -Uvh dk-milter-1.0.2-1.i386.rpm

or

# rpm-Uvh dk-milter-1.0.2-0.x86_64.rpm

Modify the default dk-milter configuration

The RPM you installed copied the necessary binaries and default configuration files for dk-milter. However, we won’t be using the default settings. Because you already chose a selector, created keys, and set up your DNS records when you set up OpenDKIM, we can re-use the same selector, keys, and DNS records in dk-milter with a few simple modifications.

Remember your selector

When you configured OpenDKIM, you already decided the name of your selector. Your selector is a unique keyword that is associated with both keys (public and private), included in all the signatures, and is published in your DNS records. For simplicity, I chose the word default as my default selector in the OpenDKIM tutorial, and will do the same here. If you chose something different, remember to use it consistently in this tutorial, too. It just needs to be the same as the one you used with OpenDKIM.

Also, while this should go without saying, you should also use your mail domain instead of example.com throughout the following steps.

Edit the dk-milter configuration file

Use your favorite text editor to open the default dk-milter configuration file, which is located at /etc/sysconfig/dk-milter. By default, all the values in that file are commented out, meaning that unless you change them now, dk-milter will use the settings from its init script when it starts up.

Uncomment and edit the necessary lines in the file so that it appears as follows:

# Default values
#
USER="opendkim"
PORT="inet:10035@localhost"
SIGNING_DOMAIN="example.com"
SELECTOR_NAME="default"
KEYFILE="/etc/opendkim/keys/${SIGNING_DOMAIN}/${SELECTOR_NAME}"
SIGNER=yes
VERIFIER=no
CANON=simple
REJECTION="bad=r,dns=t,int=t,no=a,miss=r"
EXTRA_ARGS="-h -l -D -i /etc/opendkim/TrustedHosts"
SYSCONFIG="/etc/sysconfig/dk-milter"
MILTER_GROUP="opendkim"

# User configuration
#
#PORT0="inet:10034@localhost"
#SIGNER0=no
#PORT1="inet:10035@localhost"
#VERIFIER1=no
#...

Notice that we’re referencing the opendkim user and group on lines 3 and 14 of this file, because the directory and private key you previously set up for DKIM is owned (and can only be read by) the opendkim user and group.

Also notice on line 12 that we’re re-using the /etc/opendkim/TrustedHosts file you set up for OpenDKIM. This is comparable to the InternalHosts directive in your OpenDKIM configuration file. You’re telling dk-milter that these domains are internal (with the -i flag), so it’s OK to sign their outgoing mail.

Finally, notice that I left the User configuration settings on lines 16-22 commented out. You can delete them if you want, since we won’t need them for this implementation.

Edit your Postfix configuration

Since you already added lines to your Postfix main.cf file to tell Postfix how to interact with OpenDKIM, you can just add the milter information for dk-milter to the end of those same lines. Find the lines in your Postfix main.cf that look like this:

smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

and edit them to look like this:

smtpd_milters = inet:localhost:10035, inet:localhost:8891
non_smtpd_milters = inet:localhost:10035, inet:localhost:8891

Notice that I put the milter info for dk-milter first and for OpenDKIM last. This actually determines the order of each milter’s headers in your outgoing mail (the first shall be last and the last shall be first). I recommend using this order, which places the DKIM signature above the DomainKeys signature in your outgoing email. That way, the receiving mailer will parse the DKIM information (the newer protocol) first.

Start dk-milter and restart Postfix

You’re ready to fire things up. Start dk-milter with:

# service dk-milter start

You should get a message that says:

Starting DomainKeys milter (dk-filter #0):     [  OK  ]

If you get an error message, don’t freak out. You probably just mistyped something in one of the config files. Go to the line number of the file listed, and check your work against the example(s) in this tutorial. Then try starting up dk-milter again.

Once dk-milter starts successfully, restart Postfix with:

# service postfix restart

If everything looks good, you may want to consider running chkconfig on dk-milter to make sure it starts when you reboot:

# chkconfig --level 2345 dk-milter on

Modifying DNS records

While DomainKeys (via dk-milter) will rely on the same DNS information as DKIM (via OpenDKIM) for selector information, there is one minor addition and one minor change you need to make to your domain’s DNS records to support the older DomainKeys protocol.

First, you need to add a TXT Record to your zone file that reads:

_domainkey.example.com IN TXT "t=y; o=~"

The _domainkey TXT record is slightly comparable to the _adsp TXT record you added when setting up DKIM. It basically publishes your DomainKeys policies. The t=y means the domain is in test mode, so only use this setting while you’re testing things out. Once you’ve verified it works, remember to change it to t=n. The o=~ means that some emails from this domain will be signed, but not all. A setting of o=- means that every email from this domain will be signed, and that a messages origin should be in doubt if it comes from this domain unsigned. For now, the safest setting is o=~.

Second, you need to edit the TXT file for your selector and remove the v=DKIM1; and g=*; arguments, as these sometimes cause issues for a few mail servers when they attempt to validate the older DomainKeys.

Testing Things Out

As always, the best way to see that everything is working on the server side is to keep an eye on your /var/log/maillog file. Do a:

# tail -f /var/log/maillog

When dk-milter starts (or restarts), you should see lines like:

dk-filter[19535]: Sendmail DomainKeys Filter v1.0.2 starting (args: -u opendkim-milt -p inet:10035@localhost -d /etc/mail/dkim/trusted-hosts -s /etc/mail/domainkeys/keylist -S default -b sv -c simple -C bad=r,dns=t,int=t,no=a,miss=r -h -l -D -k -i /etc/mail/dkim/trusted-hosts -P /var/run/dk-filter0.pid)

Unlike OpenDKIM, dk-milter doesn’t log anything when you send a mail that gets successfully signed, so the best way to check that your signed mail is being authenticated and that your DNS records are properly set up is to use one of the free testing services. My favorites are:

Each of these will tell you if things are working properly, and give you some pointers on troubleshooting if needed. If  Brandon Checketts’ Email Validator gives an error message in the DomainKeys field that reads:

Unable to verify signature
  granularity does not match address

That probably means the g=*; setting from your selector’s DNS TXT record is either still in your record or that the record is being cached by Brandon’s server. Wait an hour and then try again.

If you have a Gmail account, you can also send a signed message there for a quick and easy test. I like to click the Show Original link (under the Reply drop-down on the right) to see the signed headers. You should see headers for DKIM as well as DomainKeys.

The one result that everyone seems to want is this reply from the verifier.port.25.com test:

==========================================================
Summary of Results
==========================================================
SPF check:          pass
DomainKeys check:   pass
DKIM check:         pass
Sender-ID check:    pass
SpamAssassin check: ham

If you get that result, then congratulations – you’re signing mail with DKIM and DomainKeys. Sure, it’s a little redundant, but apparently overkill is just your style. :)

Good luck! Pease post in the comments with your successes, questions, or suggestions.