DKIM/Domainkeys signing via DKIMproxy

With the ever increasing volume of SPAM being transmitted, many email services look toward more elaborate ways to authenticate email. Beyond the obvious – content – two commonly used methods (perhaps with some variations), include sender policy framework (SPF), and DomainKeys Identified Mail (DKIM). Both of these require the use of DNS records to function. With SPF, a list of servers allowed to send mail for a domain is entered into the DNS. The focus of this article will be DKIM, and the older DomainKeys (mostly used by Yahoo!, although they do now check DKIM as well).

DKIM is a process for generating a cryptographic hash from an email (the body as well as select headers). This serves two main functions: firstly, the signature provides credibility, in that the private key is needed to generate the signature. Since the private key is not publicized, signing with it provides a degree of certainty that the server possessing the private key has approved the message (usually meaning that an authenticated user has sent the message). Secondly, since the cryptographic hash is dependent on the content of the email, the signature verifies that the email was not altered between the sending and receiving servers.

There are a number of ways of implementing DKIM and DomainKeys – one common way uses a milter for each algorithm. Another approach is that of DKIMproxy, with a signing module listening on a specified port, and selected emails proxied through the signing module. DKIMproxy implements both DomainKeys and DKIM specifications, with easy customizations of specifics on a per-domain or even per address basis.

Specifics (e.g. paths) of this article pertain to Amazon’s Linux distribution (RHEL/CentOS derived).

Pre-requisites

DKIMproxy has a number of Perl dependencies. These can be installed either via yum or CPAN.

For yum:

yum --enablerepo rpmforge install perl-Mail-DKIM perl-Net-Server

For CPAN:

(launch CPAN via: perl -MCPAN -e shell

Install CPAN via yum install perl-CPAN)

install Mail::DKIM

(Dependencies include: Crypt::OpenSSL::RSA, Digest::SHA, Mail::Address, MIME::Base64, Net::DNS)

install Net::Server
install Error (might not be required)

The CPAN method is preferable as it typically provides the most up-to-date packages.

Installation

After the dependencies are resolved, DKIMproxy should install – first, however, we need to download it (the current version, at the time of writing, is 1.4.1).

cd /usr/local/src
wget http://downloads.sourceforge.net/dkimproxy/dkimproxy-1.4.1.tar.gz
tar -xzvf dkimproxy-1.4.1.tar.gz
cd dkim*
./configure --prefix=/usr/local/dkimproxy
make install

The above commands download the software, and installs it to the path specified by the ‘prefix’ parameter.

We now create a user (dkim) for DKIMproxy to run as. In this case, we don’t need a user directory, and don’t want a shell assigned to the user either, for a bit of added security, we also lock the password (ideally, we don’t permit password logins to being with).

useradd -M -s /bin/false dkim
passwd -l dkim

To delete the password altogether (if one was set), run:

passwd -d dkim

You can also change the shell after the user is created by running:

usermod -s /sbin/nologin dkim

You can verify the user and group by running: id dkim

Setup the Init-script

We next want to setup the init script. DKIMproxy comes with a sample init script, but you might need to modify some paths. The following copies the init script, makes it executable, and adds it to the start-up.

cp /usr/loca/src/ dkimproxy-1.4.1/sample-dkim-init-script.sh /etc/init.d/dkimproxy
cd /etc/init.d
chmod +x dkimproxy
chkconfig --add dkimproxy

The values, from the init-script, which might need modification are:

  • DKIMPROXYUSER (set to the user you created above)
  • DKIMPROXYGROUP (set to the group of the user created above)
  • PIDDIR (should start with the install path, does not exist until script is run)
  • DKIMPROXY_IN_CFG (path to dkimproxy_in.conf, need not exist if not using in/verifier script)
  • DKIMPROXY_OUT_CFG (path to dkimproxy_out.conf, need not exist if not using out/signer script)

There are no options in the init-script to specify whether you are using the in, out, or both scripts. It is determined automatically based on which config files exist. The script will only start the filter(s) with existing config files. As such, do not create/rename config files for a filter you don’t want.

Generate the RSA Keys

A common location for the keys is in the dkimproxy folder:

cd /usr/local/dkimproxy

We start by generating a private key. If you will be using a different key for each domain (probably a good idea), you may want to include the domain in the file name. You also need to specify the length of the key (in bits) – common values include 512, 768, and 1024. Since some registrars impose a 256 character limit on TXT DNS records, a private key exceeding 1024 bits may result in the public key exceeding that limit. UDP typically has a 512 byte limit which would limit keys above 4096 bits. Additionally, longer keys require more processing per email.

You can verify an existing RSA key by running:

openssl rsa -in domain.priv.key -check

You can find information (such as key length) from an existing RSA key by running:

openssl rsa -in domain.priv.key -text -noout

If you aren’t using a pre-existing key, generate a 1024 bit private RSA key:

openssl genrsa -out domain.priv.key 1024

We now generate a public key from the private key – specifying the private key as the input, and the desired filename as the output.

openssl rsa -in domain.priv.key -pubout -out domain.pub.key

Set permissions and ownership on the keys:

chown dkim:dkim *.key
chmod 400 *.key

 

Configure DKIMproxy-OUT

In order to easily configure multiple domains, we will be availing of the sender_map parameter. Due to this, we can remove all domain/signature specific entries (domain, signature, keyfile, selector) from the config file, but must add the path to the sender_map file.

Start by copying the example file

cp /usr/local/dkimproxy/etc/dkimproxy_out.conf.example /usr/local/dkimproxy/etc/dkimproxy_out.conf

Edit /usr/local/dkimproxy/etc/dkimproxy_out.conf:

Remove (or comment out)
domain, signature (may be two), keyfile, selector

From the default file, this will leave only listen and relay parameters.

Add:

sender_map /usr/local/dkimproxy/etc/senders

Create file ‘/usr/local/dkimproxy/etc/senders’:

The format of the sender_map file is:

  • one domain per line;
  • signature types (dkim/domainkeys) separated by commas;
  • signature details (e.g. c, a, s, key) in brackets, beside signature type, each separated by a comma.

 

General Signature Parameters

Parameter Notes
s Selector (without domain name)
d domain to sign for (default is domain matched)
i Identity (default is omitted)
key Full path to private key

 

DKIM Specific Parameters

Parameter Notes
c Canonicalization (simple/relaxed) (default is simple)
a Hash algorithm (rsa-sha1/rsa-sha256)

 

DomainKeys Specific Parameters

Parameter
c Canonicalization (simple/nofws) (default is simple)
a Hash algorithm (rsa-sha1)

Note: DomainKeys only supports the SHA-1 hash, while DKIM also supports SHA-256. If you set a=rsa-sha256 for DomainKeys, DKIMproxy will fail to start (throwing a ‘signing error: Can’t call method “new” on an undefined value at Signer.pm’).

Example

domain.com dkim(c=relaxed, a=rsa-sha256,s=mail,key=/usr/local/dkimproxy/domain.priv.key), domainkeys(c=nofws,a=rsa-sha1,s=mail,key=/usr/local/dkimproxy/domain.priv.key)

 

Setup the DNS Records

In order for your DKIM signature to be verified, you need to add a TXT record to your domain’s DNS. In this case a single DNS record is being setup for both DKIM and DomainKeys – as such, certain optional parameters are omitted.

It is typical to add 2 records – one for ‘policy’ and one containing the public key.

The policy record

Type TXT
Name _domainkey
Contents o=~; t=y o: outbound policy: ~ (some emails signed); – ( all emails signed); ! (all signed, no 3rd party signatures); . (does not send emails)
t: flags: n (not testing); s (no subdomains); y (test mode)
n: notes (human readable)
r: reporting email address (where to report invalid results)

Change the ‘contents’ once you are sure everything is setup right.

The selector record

Type TXT
Name selector._domainkey Replace ‘selector’ with the selector (e.g. ‘mail’) used
Contents p=[base64 key] k: key type; h: hash algorithm; v: version (must come first);  g: granularity; p: public key; s: service type; n: notes; t: flags

You can publish both the policy and public key in a single record if desired (or you may wish to include some policy records in the selector record for services which do not look for a policy record).

Note: remember to remove all line breaks from the base64 encoded public key.

You can use dig or host to read your DNS records.  (Note, both enclose the ‘contents’ in double quotes, and escape semicolons)

For the policy record:

dig +short -ttxt _domainkey.domain.com

For the selector record:

dig +short -ttxt selector._domainkey.domain.com

 

Edit Postfix configuration

The basis of the DKIMproxy has mail received by Postfix, on a specific port (typically, the submission port – 587), relayed through DKIMproxy (which will sign the email) and then sent back to Postfix on port 25 (the typical SMTP port) from where it is dispatched. Any mail arriving on port 25 is not signed.

To modify a working copy of Postfix, we need only edit the master.cf file.

/etc/postfix/master.cf:

Firstly setup the submission listener service – there is probably already one existing, but it is likely commented out:

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

Of key importance is specifying the filter (dksign) and its port (10027) as well as limiting access (we only want authenticated users to have their email signed – other emails (e.g. sent by PHP mail() command) will still work, but will not be signed.

We need to add two more blocks (at the end of the file) to complete the configuration. The first block is named to match that specified above, and transports messages to the filter:

dksign    unix  -       -       n       -       4       smtp
-o smtp_send_xforward_command=yes
-o smtp_discard_ehlo_keywords=8bitmime,starttls

The second block sets up an smtp listener to receive the messages that have been signed:

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

To finish the setup, we start DKIMproxy and we reload Postfix:

service dkimproxy start
service postfix reload

 

Final Notes

Existing applications may continue to send mail without interruption on port 25 – this mail will not be signed. To have mail from an application signed, you must change the port to 587. If you do not accept external mail, you may want to keep port 587 firewalled.

DKIMproxy logs to /var/log/maillog

You can increase the verbosity of the Postfix logs by adding ‘-v’ to the command on any line of master.cf (e.g. smtpd -v).

Roundcube

To change the port, modify $rcmail_config['smtp_port'] in config/main.inc.php

You do not need to specify $rcmail_config['smtp_user'] or $rcmail_config['smtp_pass'] (but may want to); you may need to specify $rcmail_config['smtp_server'] = 'localhost';.

Some FastCGI setups of RoundCube might need the install path explicitly specified:

In program/include/iniset.php, replace (around line 45):

define('INSTALL_PATH', dirname($_SERVER['SCRIPT_FILENAME']).'/');

with:

define('INSTALL_PATH', '/path/to/roundcube/root/');

 

Checking your setup

The following are some useful resources to validate various parts of your setup:

 

References

DKIMproxy home page: http://dkimproxy.sourceforge.net/

DKIM specifications: http://www.ietf.org/rfc/rfc4871.txt

By cyberx86

Just a random guy who dabbles with assorted technologies yet works in a completely unrelated field.

2 comments

  1. Hi,

    I’m stuck on adding the requiered lines on /etc/postfix/master.cf (On postfix reload throws: postfix reload /usr/sbin/postconf: fatal: file /etc/postfix/master.cf: line 3: bad field count postfix/postfix-script: fatal: cannot execute /usr/sbin/postconf!)
    Everythig worked as expected, i have installed the dkimproxy in /usr/local/dkimproxy but i can’t get it work cause the master.cf postfix file wont let me ad those lines.
    I have suse 13.1 running with postfix, i successfully created spf and domainkey records and i would like to install this component to avoid getting marked as spam by google and hotmail.

    Any suggestions would be appreciated.
    Thanks in advance,
    Eugen P.

    1. Firstly, I would strongly recommend OpenDKIM instead of DKIMproxy I find the setup is simpler and the end result more versatile.
      Secondly – check line 3 of your master.cf. The line is likely the first line under the submission block. The options need to be indented. All non-indented lines need to have 8 fields. So, it should be:

      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

      The same applies for the other blocks in master.cf

Leave a comment

Your email address will not be published. Required fields are marked *