Sunteți pe pagina 1din 8

DKIM Setup with Qmail wrapper

This document provides detailed steps for user who want to setup DKIM manually for their
Qmail installations. This is useful for servers running on control panels that do not support DKIM
configuration from its GUI.
Check this URL to see what a DKIM wrapper means: http://www.memoryhole.net/qmail/#dkim

This qmail wrapper needs 'dktest' that comes with libdomainkeys and dkimsign.pl that comes
with Perl's Mail::DKIM module.

libdomainkeys can be downloaded and installed from sourcefourge:
wget http://sourceforge.net/projects/domainkeys/files/latest/download
after downloading the .tar.gz, execute tar command to uncompress the file:
tar zxvf libdomainkeys-0.69.tar.gz

cd to the new folder libdomainkeys-0.69 and run make command. If you get an error, add -
lresolv at the end of 6th line so it becomes:
LIBS=-L. -ldomainkeys -lcrypto -lresolv
Now you have dktest and other executable files in the folder. Just copy them to /usr/bin folder.

To install dkimsign.pl: yum install perl-Mail-DKIM
if it says that there are no packages with that name, EPEL repositories needs to be added to
yum.
Adding EPEL repository to yum:
For Centos 5.x
wget http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-5.rpm
sudo rpm -Uvh remi-release-5*.rpm epel-release-5*.rpm

For Centos 6.x
wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo rpm -Uvh remi-release-6*.rpm epel-release-6*.rpm

Once installed you should see some additional repo definitions under the /etc/yum.repos.d
directory.
$ ls -1 /etc/yum.repos.d/epel* /etc/yum.repos.d/remi.repo
/etc/yum.repos.d/epel.repo
/etc/yum.repos.d/epel-testing.repo
/etc/yum.repos.d/remi.repo


Enable the remi repository
The remi repository provides a variety of up-to-date packages that are useful or are a
requirement for many popular web-based services. That means it generally is not a bad idea to
enable the remi repositories by default.
First, open the /etc/yum.repos.d/remi.repo repository file using a text editor of your choice:
sudo vim /etc/yum.repos.d/remi.repo

Edit the [remi] portion of the file so that the enabled option is set to 1. This will enable the remi
repository.

name=Les RPM de remi pour Enterprise Linux $releasever - $basearch
#baseurl=http://rpms.famillecollet.com/enterprise/$releasever/remi/$basearch/
mirrorlist=http://rpms.famillecollet.com/enterprise/$releasever/remi/mirror
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi
failovermethod=priority

You will now have a larger array of yum repositories from which to install. Make sure that
dkimsign.pl takes --key as our wrapper expects it. Here is a dkimsign.pl if you didnt get it from
anywhere else:
#!/usr/bin/perl -I../lib
#
# Copyright (c) 2005-2006 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.
# Modified by Kyle Wheeler
#
# dkimsign.pl --type=domainkeys --selector=default --
key=/etc/domainkeys/memoryhole.net/default --method=nofws < email
# dkimsign.pl --type=dkim --selector=default --
key=/etc/domainkeys/memoryhole.net/default --method=relaxed < email

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 $key = "private.key";
my $algorithm = "rsa-sha1";
my $method = "simple";
my $domain; # undef => auto-select domain
my $expiration;
my $identity;
my $key_protocol;
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,
"key=s" => \$key,
"expiration=i" => \$expiration,
"identity=s" => \$identity,
"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
Rename qmail-remote as qmail-remote-orig and make a new text file with name qmail-remote
and copy paste the code below into it and give execution permission to all. If domain for DKIM
needs to be a fixed domain name, 6th line can be changed to:
sender="<domain_name>"

qmail-remote.sh (Qmail wrapper)
#!/bin/bash
# version 6
PATH=/bin:/usr/bin:/usr/local/bin
shopt -q -s extglob
host="$1"
sender="$2"
# First, figure out who the sending domain is:
[[ -z "$sender" && "$DEFAULTDOMAIN" ]] && sender="@$DEFAULTDOMAIN"
[ -z "$sender" ] && sender=@`hostname -f`
DOMAIN="${sender##*@}"
# Sanity-check the sender
if [[ $DOMAIN == !(+([a-zA-Z0-9\!\#$%*/?|^{}\`~&\'+=_.-])) ]] ; then
# ' fix vim quote logic
echo "DSender address contains illegal characters."
exit 0
fi
# Now, fill in the basic variables (if they don't exist already)
[ "$DKREMOTE" ] || DKREMOTE="/var/qmail/bin/qmail-remote.orig"
[ "$DKSIGN" ] || DKSIGN="/etc/domainkeys/%/default"
# Now try and find the right subdomain, per RFC 4871
# (you can eliminate this loop if you don't want parent domains signing child
# domain's email)
if [ "$DOMAIN" ] ; then
while [ ! -r "${DKSIGN//\%/$DOMAIN}" ] ; do
# try parent domains, per RFC 4871, section 3.8
DOMAIN=${DOMAIN#*.}
DPARTS=( ${DOMAIN//./ } )
[ ${#DPARTS[*]} -eq 1 ] && DOMAIN="${sender##*@}" && break
done
fi
DKSIGN="${DKSIGN//\%/$DOMAIN}"
# Now that we have the correct DKSIGN value (i.e. the filename of the key to
# use to sign email), check to see if this file exists
if [ -r "$DKSIGN" ] ; then
# The key does exist, so now use it to generate signatures!
tmp=`mktemp -t dk.sign.XXXXXXXXXXXXXXXXXXX`
tmp2=`mktemp -t dk2.sign.XXXXXXXXXXXXXXXXXXX`
cat - >"$tmp"

# compute the DomainKey signature
error=`(dktest -s "$DKSIGN" -c nofws -h <"$tmp" | \
sed 's/; d=.*;/; d='"$DOMAIN"';/' > "$tmp2") 2>&1`
if [ "$error" ] ; then
# Communicate the problem to qmail (that's why the 'Z')
echo "ZDomainKey error: $error"
rm "$tmp" "$tmp2"
exit -1
fi

# compute the DKIM signature
error=`(dkimsign.pl --type=dkim --selector=default --domain="$DOMAIN" \
--key="$DKSIGN" --method=relaxed <"$tmp" | \
tr -d '\r' >> "$tmp2") 2>&1`
if [ "$error" ] ; then
# Communicate the problem to qmail (that's why the 'Z')
echo "ZDKIM error: $error"
rm "$tmp" "$tmp2"
exit -2
fi

# feed the signatures and the original message to the real qmail-remote
# I'm combining to a third temp file to allow the file to be "rewound",
# since some folks like that ability
tmp3=`mktemp -t dk3.sign.XXXXXXXXXXXXXXXXXXX`
cat "$tmp2" "$tmp" > "$tmp3"
rm "$tmp" "$tmp2"
"$DKREMOTE" "$@" < "$tmp3"
retval=$?
rm "$tmp3"
exit $retval
else
# No signature added
exec "$DKREMOTE" "$@"
fi

After wrapper is setup, public and private keys has to be generated for the domain name and
added to /etc/domainkeys/<domain_name>:
Private key file: /etc/domainkeys/<domain_name>/default
Public key file: /etc/domainkeys/<domain_name>/default.pub

To generate keys, dknewkey (part of libdomainkey) can be used. For example:
dknewkey default > default.pub
default will have the new private key and default.pub will have the public key.

S-ar putea să vă placă și