# HOWTO: Qmail and DomainKeys Identified Mail (DKIM) Signing

## lmegliol

Disclaimer: I'm no expert system administrator now am I an expert documentation writer, therefore the process I followed to get DKIM signing working on my system may not be the best way or the most secure way of getting it working.  I don't know of any particular problems with this method, but I'll leave that up to other people to determine.  Comments and improvements on the following are more than welcome.  I probably won't be able to give to much assistance with questions.

I wanted to get DomainKeys Identified Mail (DKIM) setup with qmail to sign my outgoing messages.  There is a fair amount of documentation out there about getting this done, but there are a few different approaches, and I wasn't able to get all of them working.  The following is the way that I managed to get it working.

Prerequesites:

qmail is installed

Perl is installed

I used the instructions at the following link to install qmail:

https://forums.gentoo.org/viewtopic-t-539101-highlight-qmail.html

I assume that my instructions will only work if you also following those instructions.

Use CPAN to install Mail::DKIM Perl module.  I did not check to see whether this was available as an ebuild.

```

# cpan

cpan> install Mail::DKIM

CPAN may require that you install other modules on which Mail::DKIM depends.  Say yes when prompted for those modules.

cpan> quit

```

Some instructions for getting DKIM working talk about the dkimsign.pl program that comes with the Mail::DKIM Perl module.  This script was not installed when the module was installed, but I did find it in the CPAN cuild directory.  For me:

```

# cp /root/.span/build/Mail-DKIM-0.30.1-XXXXXX/scripts/dkimsign.pl /usr/local/bin

Where XXXXXX is a seemingly random string of characters.

```

You may be able to find the script using:

```

# find / -name dkimsign.pl -print

```

Apply the following patch to that script:

```

diff -ru orig/dkimsign.pl modified/dkimsign.pl

--- orig/dkimsign.pl   2008-02-20 21:51:34.000000000 -0700

+++ modified/dkimsign.pl   2008-02-20 21:50:19.000000000 -0700

@@ -22,6 +22,7 @@

 my $expiration;

 my $identity;

 my $key_protocol;

+my $key;

 my @extra_tag;

 my $debug_canonicalization;

 my $binary;

@@ -34,6 +35,7 @@

       "domain=s" => \$domain,

       "expiration=i" => \$expiration,

       "identity=s" => \$identity,

+      "key=s" => \$key,

       "key-protocol=s" => \$key_protocol,

       "debug-canonicalization=s" => \$debug_canonicalization,

       "extra-tag=s" => \@extra_tag,

@@ -61,7 +63,7 @@

       Algorithm => $algorithm,

       Method => $method,

       Selector => $selector,

-      KeyFile => "private.key",

+      KeyFile => $key,

       Debug_Canonicalization => $debugfh,

       );

```

To apply:

```

# patch -p1 -l < dkimsign.pl.patch

```

This patch just allows the private key to be specified on the command line.  This is particularly important if you will be signing email messages from multiple domains.

```
# emerge -av libdomainkeys
```

```
# mkdir /etc/domainkeys
```

For each domain for which you would like to have messages signed:

```

# mkdir /etc/domainkeys/DOMAIN.TLD

# cd /etc/domainkeys/DOMAIN.TLD

# dknewkey default 1024 > default.pub

```

That directory will now have the public and private keys for the domain.

```
# chown -R root:qmail /etc/domainkeys
```

```
# chmod -R g=u-w,o= /etc/domainkeys
```

Add user qmailr to qmail group:

```
# usermod -a -G qmail qmailr
```

```
# mv /var/qmail/bin/qmail-remote /var/qmail/bin/qmail-remote.orig
```

Save the following script as /var/qmail/bin/qmail-remote

```

#!/bin/bash

[ "$DKSIGN" ] || DKSIGN="/etc/domainkeys/%/default"

[ "$DKREMOTE" ] || DKREMOTE="/var/qmail/bin/qmail-remote.orig"

if [[ $DKSIGN == *%* ]] ; then

    DOMAIN=${2##*@}

    DKSIGN="${DKSIGN%%%*}${DOMAIN}${DKSIGN#*%}"

fi

if [ -f "$DKSIGN" ] ; then

   tmp=`/bin/mktemp -t dk.sign.XXXXXXXXXXXXXXXXXXX`

   /bin/cat - >"$tmp"

   ( /usr/bin/dktest -s "$DKSIGN" -c nofws -h < "$tmp" 2>/dev/null | /bin/sed 's/; d=.*;/; d='"$DOMAIN"';/' ;

   /usr/local/bin/dkimsign.pl --type=dkim --selector=default --key="$DKSIGN" --method=relaxed <"$tmp" | /usr/bin/tr -d '\r' ;

   /bin/cat "$tmp" ) | "$DKREMOTE" "$@"

   retval=$?

   /bin/rm "$tmp"

   exit $retval

else

   exec "$DKREMOTE" "$@"

fi

```

```
# chown root:qmail /var/qmail/bin/qmail-remote
```

```
# chmod 755 /var/qmail/bin/qmail-remote
```

For each domain for which you want messages signed, you will need to add two TXT records to DNS.  To start, add the following two records:

```
_domainkey.DOMAIN.TLD IN TXT "t=y; o=~"
```

```
default._domainkey.DOMAIN.TLD IN TXT "t=y; k=rsa p=PUBLIC_KEY"
```

Replace PUBLIC_KEY above with the public key in /etc/DOMAIN.TLD/default.pub.  The public key will following the "p=" in that file.

The "t=y" means that you are just testing this domain key for now.  Remove that from the TXT records when you are certain that everything is working.

The "o=~" in the first TXT record means that you will not be signing every email message sent by the domain.  If you will be signing every email message from the domain, set that to "o=-".

There is one problem that I seem to have come across so far.  If you are using /var/qmail/control/smtproutes to route your email through another SMTP server, and if you are using a username and password in that file to AUTH your connetion, it may no longer work.  Mine stopped working when I setup DKIM.  I did not really need it, so I just stopped using it.  Using smtproutes without AUTH continued to work.

Some of the resources I used to get this working:

http://www.memoryhole.net/qmail/

http://patchlog.com/security/qmail-and-dkim/

http://qmail.jms1.net/patches/domainkeys.shtml

Again, any suggestions and improvements to these instructions are welcome.  If there are any known security issues with this setup, please let me know.Last edited by lmegliol on Tue Mar 25, 2008 2:41 am; edited 1 time in total

----------

## r00t440

Hi!  I think your guide totally rocks.  It's very true that there's very limited resources on how to setup qmail + DKIM. Most of them are very cryptic and not so n00b friendly.

You mentioned that you didn't check if there's any ebuild for "Mail::DKIM", there's actually one and it's called "dev-perl/Mail-DKIM". I wasn't able to install "Mail::DKIM" manually as stated on your guide, it gives me a compile error. But, doing "emerge dev-perl/Mail-DKIM" works just fine.

How about revising your howto into a more "Gentoo" way, I mean using "emerge"? 

After emerging Mail-DKIM, I can't seem to find the file: "dkimsign.pl" so I'm stuck again at implementing this DKIM stuff. I have a very simple qmail installation and I need DKIM to work because all my sent mail goes to spam folder of Yahoo, GMail, Hotmail, I guess every email service provider. I was able to make SPF working and probably DKIM is the only thing that prevents my sent mail from going to the Inbox of these email service providers. My email server configuration is "qmail-vpopmail-dovecot".

I guess your guide must be posted on Gentoo-Wiki.

Thanks and more power!

----------

## JC99

I am having a problem with the patching part. I created a file called "dkimsign.pl.patch" in /usr/local/bin/ when dkimsign.pl is located then ran following command...

```
patch -p1 < dkimsign.pl.patch
```

...and got the following error..

```
patching file dkimsign.pl

Hunk #2 FAILED at 35.

Hunk #3 FAILED at 63.

2 out of 3 hunks FAILED -- saving rejects to file dkimsign.pl.rej

```

What do I do? I have mail-mta/netqmail 1.05-r8 installed.

----------

## lmegliol

 *EvilEye wrote:*   

> I am having a problem with the patching part. I created a file called "dkimsign.pl.patch" in /usr/local/bin/ when dkimsign.pl is located then ran following command...
> 
> ```
> patch -p1 < dkimsign.pl.patch
> ```
> ...

 

Try 

```
patch -p1 --ignore-whitespace < dkimsign.pl.patch
```

or (same thing)

```
patch -p1 -l < dkimsign.pl.patch
```

----------

## lmegliol

 *r00t440 wrote:*   

> Hi!  I think your guide totally rocks.  It's very true that there's very limited resources on how to setup qmail + DKIM. Most of them are very cryptic and not so n00b friendly.
> 
> You mentioned that you didn't check if there's any ebuild for "Mail::DKIM", there's actually one and it's called "dev-perl/Mail-DKIM". I wasn't able to install "Mail::DKIM" manually as stated on your guide, it gives me a compile error. But, doing "emerge dev-perl/Mail-DKIM" works just fine.
> 
> How about revising your howto into a more "Gentoo" way, I mean using "emerge"? 
> ...

 

I'll post the content of the dkimsign.pl file here, but it will probably be a little long.  Obviously you don't need to apply the patch to this one.

```

#!/usr/bin/perl -I../lib

#

# Copyright (c) 2005-2007 Messiah College. This program is free software.

# You can redistribute it and/or modify it under the terms of the

# GNU Public License as found at http://www.fsf.org/copyleft/gpl.html.

#

# Written by Jason Long, jlong@messiah.edu.

use strict;

use warnings;

use Mail::DKIM::Signer;

use Mail::DKIM::TextWrap;

use Getopt::Long;

use Pod::Usage;

my $type = "dkim";

my $selector = "selector1";

my $algorithm = "rsa-sha1";

my $method = "simple";

my $domain; # undef => auto-select domain

my $expiration;

my $identity;

my $key_protocol;

my $key;

my @extra_tag;

my $debug_canonicalization;

my $binary;

my $help;

GetOptions(

                "type=s" => \$type,

                "algorithm=s" => \$algorithm,

                "method=s" => \$method,

                "selector=s" => \$selector,

                "domain=s" => \$domain,

                "expiration=i" => \$expiration,

                "identity=s" => \$identity,

                "key=s" => \$key,

                "key-protocol=s" => \$key_protocol,

                "debug-canonicalization=s" => \$debug_canonicalization,

                "extra-tag=s" => \@extra_tag,

                "binary" => \$binary,

                "help|?" => \$help,

                )

        or pod2usage(2);

pod2usage(1) if $help;

pod2usage("Error: unrecognized argument(s)")

        unless (@ARGV == 0);

my $debugfh;

if (defined $debug_canonicalization)

{

        open $debugfh, ">", $debug_canonicalization

                or die "Error: cannot write $debug_canonicalization: $!\n";

}

if ($binary)

{

        binmode STDIN;

}

my $dkim = new Mail::DKIM::Signer(

                Policy => \&signer_policy,

                Algorithm => $algorithm,

                Method => $method,

                Selector => $selector,

                KeyFile => $key,

                Debug_Canonicalization => $debugfh,

                );

while (<STDIN>)

{

        unless ($binary)

        {

                chomp $_;

                s/\015?$/\015\012/s;

        }

        $dkim->PRINT($_);

}

$dkim->CLOSE;

if ($debugfh)

{

        close $debugfh;

        print STDERR "wrong canonicalized message to $debug_canonicalization\n";

}

print $dkim->signature->as_string . "\n";

sub signer_policy

{

        my $dkim = shift;

        use Mail::DKIM::DkSignature;

        $dkim->domain($domain || $dkim->message_sender->host);

        my $class = $type eq "domainkeys" ? "Mail::DKIM::DkSignature" :

                        $type eq "dkim" ? "Mail::DKIM::Signature" :

                                die "unknown signature type '$type'\n";

        my $sig = $class->new(

                        Algorithm => $dkim->algorithm,

                        Method => $dkim->method,

                        Headers => $dkim->headers,

                        Domain => $dkim->domain,

                        Selector => $dkim->selector,

                        defined($expiration) ? (Expiration => time() + $expiration) : (),

                        defined($identity) ? (Identity => $identity) : (),

                );

        $sig->protocol($key_protocol) if defined $key_protocol;

        foreach my $extra (@extra_tag)

        {

                my ($n, $v) = split /=/, $extra, 2;

                $sig->set_tag($n, $v);

        }

        $dkim->add_signature($sig);

        return;

}

__END__

=head1 NAME

dkimsign.pl - computes a DKIM signature for an email message

=head1 SYNOPSIS

  dkimsign.pl [options] < original_email.txt

    options:

      --type=TYPE

      --method=METHOD

      --selector=SELECTOR

      --expiration=INTEGER

      --debug-canonicalization=FILE

  dkimsign.pl --help

    to see a full description of the various options

=head1 OPTIONS

=over

=item B<--expiration>

Optional. Specify the desired signature expiration, as a delta

from the signature timestamp.

=item B<--type>

Determines the desired signature. Use dkim for a DKIM-Signature, or

domainkeys for a DomainKey-Signature.

=item B<--method>

Determines the desired canonicalization method. Possible values are

simple, simple/simple, simple/relaxed, relaxed, relaxed/relaxed,

relaxed/simple.

=item B<--debug-canonicalization>

Outputs the canonicalized message to the specified file, in addition

to computing the DKIM signature. This is helpful for debugging

canonicalization methods.

=back

=head1 AUTHOR

Jason Long, E<lt>jlong@messiah.eduE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2006-2007 by Messiah College

This program is free software; you can redistribute it and/or modify

it under the same terms as Perl itself, either Perl version 5.8.6 or,

at your option, any later version of Perl 5 you may have available.

=cut

```

----------

## JC99

ok, I have 2 problems...

1) When I use this site to test my domain key it says the following..

```

DomainKey-Status: bad format: Key type rsa p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5atnsAT4+ykiElUixDjToZth3Y6ngNwusFxeK3osla57VJFDvJ3Dm9C65FGxkuXycIlJZrCP+YCtdTVPjsf4eKMwC2eyKhNZKpZn5SZd6wUwjea7UX4LNqr0DXr/trOVSCPttU4KqswAobc6dGPB1JtX4b1fcMjjuo0uEuAZKuwIDAQAB is not valid

DKIM-Status: Unrecognized version 1 (This signature appears to be from an older draft of the standard)

```

Here is my bind configuration...

```
_domainkey.jasoncarson.ca. IN TXT "t=n; o=-"

default._domainkey.jasoncarson.ca. IN TXT "t=n; k=rsa p=you can see it above but for some reason is not valid"

```

2) I have finished the configuration based on this howto but when I email yahoo I have a problem, it says the following in the header(in bold)...

 *Quote:*   

> From Jason Carson Tue Mar 25 11:13:03 2008
> 
> Return-Path: <jason@jasoncarson.ca>
> 
> Authentication-Results: mta494.mail.mud.yahoo.com  from=jasoncarson.ca; 
> ...

 

----------

## lmegliol

I can only make guesses as to what the problem is and suggestions on some things to try.  

In your TXT records...

```
_domainkey.jasoncarson.ca. IN TXT "t=n; o=-"

default._domainkey.jasoncarson.ca. IN TXT "t=n; k=rsa p=you can see it above but for some reason is not valid"
```

Make sure that the first one is "o=~" and not "o=-", which it appears to be on my screen.  That is a tilde character, not a hyphen.

The other think I might do is completely remove the "t=n;" from those lines if you aren't doing testing.  It may be necessary, but I don't know that it is and doubt it.

Last, I expect that the public key you put into your DNS record will take some time to propagate, which may explain the missing key.  Mine didn't take all that long, but I'd wait a couple of days and keep trying.

Anyone who knows more about these things can feel free to correct me.

----------

## JC99

Here is your patch...

```

diff -ru orig/dkimsign.pl modified/dkimsign.pl

--- orig/dkimsign.pl   2008-02-20 21:51:34.000000000 -0700

+++ modified/dkimsign.pl   2008-02-20 21:50:19.000000000 -0700

@@ -22,6 +22,7 @@

 my $expiration;

 my $identity;

 my $key_protocol;

+my $key;

 my @extra_tag;

 my $debug_canonicalization;

 my $binary;

@@ -34,6 +35,7 @@

       "domain=s" => \$domain,

       "expiration=i" => \$expiration,

       "identity=s" => \$identity,

+      "key=s" => \$key,

       "key-protocol=s" => \$key_protocol,

       "debug-canonicalization=s" => \$debug_canonicalization,

       "extra-tag=s" => \@extra_tag,

@@ -61,7 +63,7 @@

       Algorithm => $algorithm,

       Method => $method,

       Selector => $selector,

-      KeyFile => "private.key",

+      KeyFile => $key,

       Debug_Canonicalization => $debugfh,

       );

```

There is something wrong with this patch that causes the following error at this test site...

```
DKIM-Status: Unrecognized version 1 (This signature appears to be from an older draft of the standard)
```

If you don't patch dkimsign.pl the test website just says

```
DKIM-Status: failed (no signature found)
```

So how do you modify this patch in order to get version 2 working?

----------

## lmegliol

I'm sorry, but I have no idea.  If it wasn't clear from my earlier posts, I am no expert at this.  It took quite a bit of fumbling for me to get this working for myself.

All I can tell you is that when I email Yahoo I do not receive the error that you do. 

```

Authentication-Results: mta302.mail.re4.yahoo.com  from=XXXXX.com; domainkeys=pass (ok)

Comment: DomainKeys? See http://domainkeys.sourceforge.net/

DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws;

  s=default; d=XXXXX.com;

  b=TMDIGxaHFmssFGOJOEXXXXXXXXXXqQtedupHDA/mr2VrvtLcDXXXXXXXXXXXiDyBBJ22ctvKfVUs8XXXXXXXXXXGUHkpWNISH1J4GHHLAF2JhpPqC298tOQ=;

  h=Received:Received:Date:Return-Path:To:From:Subject:Message-ID:X-Priority:X-Mailer:MIME-Version:Content-Transfer-Encoding:Content-Type;

DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=XXXXX.com; h=date:to:

   from:subject:message-id:mime-version:content-transfer-encoding:

   content-type; q=dns/txt; s=default; bh=gne/YcmrwENzAQ68T3S0BaiNc

   7g=; b=BlKb8yAiNxZO+E/2g8Sucs0mKp3XXXXXMfTngnQoptRly3WnluUG

   omEUjK+VlkI1Nboc7utyHtxNSmyN1XXXXXIbTplLplX9meCA2jviwqA37Dx

   DdVfywGQaMiK1rfyfuiaVDy1vXXXXXriQ45OP2cVAoRY4=

```

I would search for that error on Google.  I did a little searching myself and I found multiple pages on the first page of Google results that show that some of the sites testing DKIM give that error, and it is a false report.

http://www.mail-archive.com/dkim-milter-discuss@lists.sourceforge.net/msg00102.html

http://forum.mailenable.com/viewtopic.php?t=8166&start=195&sid=13e586cf5cb6669a9a7c1cb68cd0d559

Though they aren't referencing the same test site you are, it is possible that all of the test sites are using the same code base.

In any event, my real email messages to real recipients (not test servers) are working just fine.  I suggest you try sending a real message out to yourself or someone else who has an account with a provider that checks DKIM.  Yahoo! works for me.

----------

