DomainKeys is a filter, used mostly by Yahoo, that validates mail against it’s outgoing mail server by using the combination of a public key displayed in a special TXT record in DNS as well as attaching a private key to all outgoing mail.

What is it? DomainKeys, like Sender Policy Framework(SPF) records, is a tactic that can be used to assist in mail verification of domains and mail servers by using the public/private key format. DomainKeys is not widely adopted but is part of the guidelines Yahoo provides to help ensure that mail is delivered and not deferred or rejected.

General Prerequisites:

* A unique IP address
* A Fully Qualified Domain Name (FQDN)
* Access to DNS for your domain

Step 1 is to install Postfix as your Mail Transfer Agent (MTA), a task which is too broad in scope for this little guide.  Then you’re going to need to install some Perl modules:

Crypt::OpenSSL::RSA

Mail::Address

MIME::Base64

Net::DNS

Net::Server

Test::More

Text::Wrap

Mail::DomainKeys

note that you may also require the Net::Server module as well.

Next we’ll install dkfilter:

1. cd /usr/local/src
2. wget http://jason.long.name/dkfilter/dkfilter-0.11.tar.gz
3. tar zxvf dkfilter*
4. ./configure –prefix=/usr/local/dkfilter
5. make install
6. useradd dkfilter

At this point all your dkfilter scripts should be in /usr/local/dkfilter, and you should consider the following before going any further:  Do you need dkfilter to process inbound mail as well as to send outbound mail?  I’ve put this together for signing outbound mail only – processing incoming mail for domainkeys turned out to place a serious load on the server – so as we go forward understand that this is just going to tag mail.  I’ve appended some notes about processing inbound mail at the very end of this guide so if you’re interested in the segue skip there.

Alright, so you want to sign outbound mail and for that we’re going to need to create a key.

1. cd /usr/local/dkfilter
2. openssl genrsa -out private.key 1024 #Note that this keyname will be important later
3. openssl rsa -in private.key -pubout -out public.key

so now we have a private.key file which domainkeys will use and a public.key file containing the public distributable key.  Now we’re going to have to do some DNS work.  You have to choose what’s called a selector for domainkeys – this can be just about anything, let’s call ours dk1.  Open the zonefile for your domain, in my example it’s going to be mydomain.org.  We’re going to be adding to the ORIGIN section, like this:

$ORIGIN mydomain.org.
_domainkey              IN      TXT  “t=y\; o=-”
dk1._domainkey       IN      TXT  “g=\; k=rsa\; p=J87YHtgEBAQUAA4GNADCBiQKBgQDNE0vqZlSd0r9Vdv+rmhkY5k2CwnYb
9O2ji4i5lAQnIw8Wz9uPOKNxOx+DkijHhbs0Ei4fco5e+14mvnBLQWpklzds1lwQfnRa7M2yjHNmhWX3B58UUQ/NpuBp4QUHxhzFdn9DIIUh899+D2zqrxA4RY
eUFcMKN9mN2O00UEqePKQIDAQAB”

these records will proceed any @, A, CNAME records you may have.   Save your zonefile and restart your DNS service.  I happen to use BIND so this is BIND-specific.

Now it’s time to edit your /etc/postfix/master.cf file to force outgoing mail to use the default submission port 587 for attaching mail with the private key.  First make a copy of master.cf and call it master.cf.orig, then edit master.cf:

# modify the default submission service to specify a content filter
# and restrict it to local clients and SASL authenticated clients only
#
submission inet n – n – - smtpd
-o smtpd_etrn_restrictions=reject
-o smtpd_sasl_auth_enable=yes
-o content_filter=dksign:[127.0.0.1]:10027
-o receive_override_options=no_address_mappings
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
#
# specify the location of the DomainKeys signing filter
#
dksign unix – - n – 10 smtp
-o smtp_send_xforward_command=yes
-o smtp_discard_ehlo_keywords=8bitmime
#
# service for accepting messages FROM the DomainKeys signing filter
#
127.0.0.1:10028 inet n – n – 10 smtpd
-o content_filter=
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
-o smtpd_helo_restrictions=
-o smtpd_client_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o smtpd_authorized_xforward_hosts=127.0.0.0/8

You should now execute “postfix reload” to make this active for outbound mail.

Before we start this up you should change permissions on your .key file to 777.  You can start this filter up at the command line thusly:

/usr/local/dkfilter/bin/dkfilter.out –keyfile=/usr/local/dkfilter/private.key –selector=dk1 –domain=mydomain.org –method=nofws 127.0.0.1:10027 127.0.0.1:10028 &

Because I need this to start automatically if the server reboots I put together a little startup script to assist:

#! /bin/sh
#
## This script will start the dkfilter.out daemon which will listen on
## port 10027 and sign all outgoing mail with a private key that
## corresponds to a public key available via a specially crafted DNS record

case “$1″ in
start)
echo “starting dkfilter outbound mail signing daemon”
/usr/local/dkfilter/bin/dkfilter.out –keyfile=/usr/local/dkfilter/private.key –selector=dk1 –domain=mydomain.org –method=nofws 127.0.0.1:10027 127.0.0.1:10028 &
;;
stop)
echo “stopping dkfilter outbound mail signing daemon”
ps aux | grep dkfilter.out | awk ‘{ print $2; }’ | xargs kill -9
;;
restart)
echo “restarting …”
stop
start
;;
*)
echo “Usage dkfilter_startup {start|stop|restart}”
exit 1
;;
esac

Now, for my purposes the dkfilter.out script was going to be used to send out coordinated mailings and there were no clients involved.  However, if you were to try and send outgoing mail from a client like Outlook using this mail server you MUST change your outgoing mail port from the default 25 to the submission port 587 or the filter will fail to attach the domainkey.

You can test this out by sending mail to a gmail.com account and viewing the headers.  If you’ve done this correctly you will see domainkey status=good in the header information.  If you see domainkey status=badformat you know your public and private keys don’t match.  If it reads domainkey status=no key you very likely have too many or too few characters in your DNS selector key value.

Lastly, edit your master.cf and include the following in the mapping table if you’re sending mail from just the local machine like I am (for mailings):

pickup    fifo  n       -       n       60      1       pickup

-o content_filter=dksign:[127.0.0.1]:10027

Final thoughts:  domainkeys, like spf records, are techniques used to increase the likelihood of mail delivery.  With all the spammers out there and the staggering volumes of illegitimate mail being blasted at the unsuspecting, ISPs are becoming much more aggressive about the types of mail they allow their customers to receive.  While this is good from the perspective of customer service it poses some problems for people interested in mail delivery.  DomainKeys filters do not offer a guarantee that your mail will reach a mailbox successfully.  They do, however, increase the change your mail will be accepted and they can be considered a best practice for mail-based advertising or any other situation where you’d be sending volumes of mail from a single server.

Promised section on inbound processing:

# Before-filter SMTP server. Receive mail from the network and
# pass it to the content filter on localhost port 10025.
#
smtp inet n – n – - smtpd
-o smtpd_proxy_filter=127.0.0.1:10025
-o smtpd_client_connection_count_limit=10
#
# After-filter SMTP server. Receive mail from the content filter on
# localhost port 10026.
#
127.0.0.1:10026 inet n – n – - smtpd
-o smtpd_authorized_xforward_hosts=127.0.0.0/8
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o smtpd_data_restrictions=
-o mynetworks=127.0.0.0/8
-o receive_override_options=no_unknown_recipient_checks

Inbound, I found, was a little spotty and caused some pretty serious load issues.  I’m sure Yahoo has some kind of custom inbound filter to handle these.

Other notes:  I’ve worked on setting this up on clusters that use NAT for direct routing.  For inbound mail this is kind of a nightmare, but outbound isn’t so bad.  You should be aware that from the outside your mail will appear to come from a single IP which may or may not resolve to your sending domain name.  This can be an issue for matching the public key – Yahoo certainly dislikes it.