Introduction

As you probably know, spam is a huge problem. Not only is spam annoying and costly for receivers, the efforts to combat spam makes the lives of legitimate email senders difficult.

In recent years, a number of efforts have been made to help combat spam by introducing new mechanisms by which the identity of an email sender can be verified. Among these are SPF (Sender Policy Framework)wikipedia-spf, SenderID, DomainKeys, and DKIM (DomainKeys Identified Mail). (See this general article on email authentication for more details.)

QMail is a great Mail Transfer Agent. It is very modular and ‘unixie’ in that it is composed of several smaller programs that each do a single task and that are chained together to accomplish larger, end-to-end tasks. One of the things that is nice about this is that modifying the behavior of QMail is often a matter of wrapping the relevant QMail component with a script that does some preprocessing or postprocessing before invoking the original component.

We are interested in making QMail do DomainKeys and DKIM signing of messages sent from our mail server. There are a number of ways to do this. One way involves patching and building parts of QMail. This would be a good solution except that current patches don’t guarantee that all mail sent from the mail server will be signed and they only do DomainKeys signing. Here, we adopt the wrapping approach.

What this document covers

This document is intended to help you set up outgoing email signing using DKIM/domainkeys under QMail on FreeBSD. It does not cover the verification of DKIM/domainkey headers for received mail, although some of the references at the end of this article cover that. I wrote this to document the steps I needed to take to get this working, and out of frustration that there didn’t seem to be a good comprehensive set of instructions out there that worked for me.

Prerequisites

This document assumes:

  • You are running freebsd 6-ish
  • You have set up QMail according to the Life with QMail instructions
  • You have installed OpenSSL

    $ cd /usr/ports/security/openssl; sudo make install clean
    
  • You have Perl installed

  • You have linux binary compatibility installed and enabled and runtime libraries installed (not sure this is strictly necessary)
  • You have access to the DNS records for your domain and can modify them

Install ‘libdomainkeys’

  1. Installing from ports doesn’t install the ‘dktest’ program. You need to build the library from source in order to get this (as far as I can tell anyway).

    1. Create a temporary location for your build, say ‘~/downloads’
    2. Download the libdomainkeys source from sourceforge into your temporary location. Extract the archive and enter the resulting directory.
    3. Edit ‘Makefile’

      The part in the original makefile that looks like

      UNAME := $(shell uname)
      ifeq   ($(UNAME), SunOS)
             LIBS += -lsocket
      endif
      
      ifeq   ($(UNAME), UnixWare)
             CFLAGS += -DUNIXWARE
      endif
      

      should match

      #UNAME := $(shell uname)
      #ifeq   ($(UNAME), SunOS)
      #       LIBS += -lsocket
      #endif
      #
      #ifeq   ($(UNAME), UnixWare)
      #       CFLAGS += -DUNIXWARE
      #endif
      

      and the part that looks like

      (if $(MAKE) dnstest >/dev/null 2>&1; then echo -lresolv; else echo ""; fi) >dns.lib
      

      should look like

      (if ! $(MAKE) dnstest >/dev/null 2>&1; then echo -lresolv; else echo ""; fi) >dns.lib
      
    4. Build the dern thing

      $ make
      
    5. Assuming the compilation worked, install the required files by:

      $ install -m 644 libdomainkeys.a /usr/local/lib/
      $ install -m 644 domainkeys.h dktrace.h /usr/local/include/
      $ install -m 755 dknewkey /usr/local/bin/
      $ install -m 755 dktest /usr/local/bin/
      

Install ‘Mail::DKIM’

Note: This would be much easier using CPAN and the cpan shell, but I am not a Perl expert and don’t know how to install the ‘dkimsign.pl’ and ‘dkimverify.pl’ scripts using that method.

  1. Download the Mail::DKIM package from CPAN into your temporary location, extract it, and change to the resulting directory.
  2. Issue the following commands:

     $ perl Makefile.PL
     $ make
     $ make test
     $ sudo make install
    
  3. Patch dkimsign.pl to allow for keyfile parameter

    You can apply this patch:

     --- dkimsign.pl.old    Thu Feb 28 11:26:46 2008
     +++ dkimsign.pl    Thu Feb 28 11:34:12 2008
     @@ -14,6 +14,7 @@
      use Getopt::Long;
      use Pod::Usage;
    
     +my $key_file = 'private.key'; # (lritter 01/23/2008): Added --key-file as a parameter to script
      my $type = "dkim";
      my $selector = "selector1";
      my $algorithm = "rsa-sha1";
     @@ -39,6 +40,7 @@
            "extra-tag=s" => \@extra_tag,
            "binary" => \$binary,
            "help|?" => \$help,
     +          "key-file=s" => \$key_file, # (lritter 01/23/2008): Added --key-file as a parameter to script
            )
        or pod2usage(2);
      pod2usage(1) if $help;
     @@ -61,7 +63,7 @@
            Algorithm => $algorithm,
            Method => $method,
            Selector => $selector,
     -      KeyFile => "private.key",
     +      KeyFile => $key_file, # (lritter 01/23/2008): Added --key-file as a parameter to script
            Debug_Canonicalization => $debugfh,
            );
    
  4. Install ‘dkimsign.pl’ and ‘dkimverify.pl’

    $ sudo install -m 755 scripts/dkimsign.pl /usr/local/bin/
    $ sudo install -m 755 scripts/dkimverify.pl /usr/local/bin/
    

Create your key pair

  1. Create the directory where you want your key pairs to reside

     $ sudo mkdir -p /etc/domainkeys/mail.example.com/
    
  2. Create your key pair:

     $ sudo cd /etc/domainkeys/mail.example.com/
     $ sudo /usr/local/ssl/bin/openssl genrsa -out rsa.private 768
     $ sudo /usr/local/ssl/bin/openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM
     $ sudo mv rsa.private default
    
  3. Set the permissions of the keys directory and the public key

     $ sudo chown -R qmailq /etc/domainkeys
     $ sudo chgrp qmail default
     $ sudo chmod 0640 default
    

    Note that the chgrp command is not in any docs or how-tos that I’ve seen but seems necessary for the ‘qmail-remote’ program to access the file. This is less than optimal as, if it’s only going to be accessed by ‘qmail-remote’, the permissions should reflect this. I may try to revisit this at a later time.

  4. Create the public key that will go in your DNS.

     $ grep -v ^- rsa.public | perl -e 'while(<>){chop;$l.=$_;}print "t=y; p=$l;\n";'
    

    I’m going to refer to this as the ‘DNS-public-key’.

Create your DNS records

The details on how to do this will vary depending on the system you use for DNS. You want to create two TXT records for your domain. For example:

_domainkey.mail.example.com.  IN TXT  "t=y; o=-;"
default._domainkey.mail.example.com.  IN TXT  "DNS-public-key"

While your testing this, it’s a good idea to set the TTL for these to a pretty small number so you can make changes easily.

For and explanation of the DKIM/domainkeys DNS record syntax, see the DKIM RFC (4870), specifically pages 9 (and on) and 21 (and on).

Wrap up ‘qmail-remote’

  1. Go to your qmail bin directory:

     $ cd /var/qmail/bin
    
  2. Move your existing ‘qmail-remote’ out of the way:

     $ sudo mv qmail-remote qmail-remote.orig
    
  3. Create a file called ‘qmail-remote-wrapper.sh’ with the following contents (taken from memoryhole):

     #!/usr/local/bin/bash
     DOMAIN="mail.example.com"
     DKREMOTE="/var/qmail/bin/qmail-remote.orig"
     DKSIGN="/etc/domainkeys/$DOMAIN/default"
     tmp=`/usr/bin/mktemp -t dk.sign.XXXXXXXXXXXXXXXXXXX`
     /bin/cat - >"$tmp"
     ( /usr/local/bin/dktest -s "$DKSIGN" -c nofws -h <"$tmp" 2>/dev/null | \
         /usr/bin/sed 's/; d=.*;/; d='"$DOMAIN"';/' ;
         /usr/local/bin/dkimsign.pl --type=dkim --selector=default \
             --key-file="$DKSIGN" --method=relaxed <"$tmp" | \
             /usr/bin/tr -d '\r' ;
         /bin/cat "$tmp" ) | \
             "$DKREMOTE" "$@"
     retval=$?
     /bin/rm "$tmp"
     exit $retval
    

    Make sure that you change DOMAIN to match your domain, DKREMOTE to match the name/path of the original qmail-remote program that you moved in the last step, and DKSIGN to match the location of the keys created in the section ‘Create your key pair’.

  4. Make the wrapper executable:

     $ sudo chmod a+x qmail-remote-wrapper.sh
    
  5. Link the wrapper to ‘qmail-remote’

     $ sudo ln -s qmail-remote-wrapper.sh qmail-remote
    

Testing your email signing

  1. Go to authentication section of deliverability.com. At the bottom of the page, you will find a randomly generated email address that looks like:
    XXxXXxXX@www.deliverability.com

    I will refer to this as the ‘test-address’.

  2. Create a temporary folder for testing purposes, say:

     $ mkdir ~/email_testing
    
  3. In the testing folder, create a file called ‘deliverability.com_test_message’ with the following content:

     to:test-address
     from: some-user@mail.example.com
     subject: test message
    
     This is a test.
    
  4. Send the test message:

     $ cat ~/email_testing/deliverability.com_test_message | /var/qmail/bin/qmail-inject
    
  5. Go to the authentication section of deliverability.com and submit the ‘test-address’ (the one you just sent to) with the form at the bottom of the page.

    You should see a check mark under the ‘DomainKey’ heading. Note that if you look closely you will notice that the website says “DKIM-Status: Unrecognized version 1″. This is incorrect. The RFC clearly states that the ‘v’ flag MUST be set to ’1′. The verifier is not DKIM compliant. For the most part DKIM is even less prevalent than DomainKeys (though there seems to be a consensus that DKIM is the way of the future…).

Tips

  • You can view your qmail log while your testing with:

    $ tail -f /var/log/qmail/current
    
  • ‘dk@dk.crynwr.com’ forwards email to it to a number of verification services that end mail back to the sending account.

References

February 27, 2008 · Uncategorized · · [Print]

3 Comments to “Setting up DKIM and Domainkeys Email Signing with QMail”

  1. Teresa says:

    I liked reading your blog…keep up the good work.

  2. Tamsy says:

    Thank you for the great tutorial.

    Following your described way finally the signing is working with our mail server.

    Great work and thank you for publishing this :-)

  3. Thanks for writing a tutorial but no thanks for excluding me by only writing out a partial how-to (like every other linux tutorial on the internet). I am getting so frustrated with Linux heads that assume every one else is an expert in the OS.

    These are my problems with your tutorial:

    • It doesn’t explain how to download the Mail::DKIM package (I know to use wget, but a full novice wouldn’t).

    • It doesn’t explain how to extract the package (I know to use tar, but not what options I should use).

    Etc, etc, etc.

    For all of the extra words you write, you could have written clear, concise, command-only operations that would help a much wider array of people.

    Just some crabby advice from a frustrated Linux noob.

    On, to the next hopefully complete tutorial…

Leave a Reply