Setting Up DKIM And SRS In Postfix

In my previous post Custom Domain E-mails With Postfix And Gmail: The Missing Tutorial, we set up a Postfix mail server on a custom domain that integrates seamlessly with Gmail.

However, the tutorial skipped two important security standards that will help prevent e-mails routed through our server from being marked as spam: DKIM and SRS. This article will show you how to add support for DKIM and SRS to a Postfix server.

Similar to the previous tutorial, we will assume a Ubuntu server in our examples.

Step 1: DKIM

DKIM, short for DomainKeys Identified Mail, is a mechanism for

  1. A sender e-mail program to sign an outgoing e-mail message, and

  2. A recipient e-mail program to verify said signature.

More concretely, let’s say Gmail receives an e-mail message from myserver.example.com. DKIM allows Gmail to verify that the e-mail was indeed sent by the designated e-mail server program on myserver.example.com, and not by, say, a virus running on myserver.example.com or a malicious user who happens to have access to myserver.example.com.

We will use OpenDKIM for this tutorial. To install OpenDKIM on Ubuntu:

$ sudo apt-get install opendkim opendkim-tools

/etc/opendkim.conf

Edit /etc/opendkim.conf to match the following:

# OpenDKIM config.

# Log to syslog
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
# Required to use local socket with MTAs that access the socket as a non-
# privileged user (e.g. Postfix)
UMask                   002

Mode                    sv
PidFile                 /var/run/opendkim/opendkim.pid
UserID                  opendkim:opendkim
Socket                  inet:[email protected]

Canonicalization        relaxed/simple
SignatureAlgorithm      rsa-sha256

# Sign for example.com with key in /etc/opendkim.d/mail.private using
# selector 'mail' (e.g. mail._domainkey.example.com)
Domain                  example.com
KeyFile                 /etc/opendkim.d/mail.private
Selector                mail

ExternalIgnoreList      refile:/etc/opendkim.d/TrustedHosts
InternalHosts           refile:/etc/opendkim.d/TrustedHosts

You can check out a detailed explanation for the meaning of each option with man opendkim.conf.

/etc/opendkim.d/TrustedHosts

Create the directory /etc/opendkim.d and put the following in /etc/opendkim.d/TrustedHosts. This instructs the OpenDKIM server to sign e-mails delivered by any server matching these expressions (such as myserver_2.example.com).

127.0.0.1
::1
localhost
192.168.0.1/24

*.example.com

DKIM keys

Now, let’s generate our DKIM signing keys:

$ cd /etc/opendkim.d
$ sudo opendkim-genkey -s mail -d example.com

This will produce two files in /etc/opendkim.d: our private key, mail.private, and our public key, mail.txt.

We should make sure only OpenDKIM can read the private key, so that a malicious program or user on the same server won’t be able to forge our signature:

$ chmod 600 mail.private
$ chown opendkim:opendkim mail.private

DKIM public key DNS record

The next step is to publish our public key through DNS, so that any recipient e-mail program can verify our signature. If we look at our public key mail.txt generated in the previous step, it should look like something like this:

mail._domainkey IN      TXT     ( "v=DKIM1; k=rsa; "
          "p=<alphabetical soup>" )  ; ----- DKIM key mail for example.com

Create the following TXT DNS record for example.com (how to update DNS records will depend on the DNS hosting provider):

  • Name: mail._domainkey.example.com

  • Value: v=DKIM1; k=rsa; p=<alphabetical soup>

where <alphabetical soup> is the public key found in mail.txt after p=. Note that DNS records will take a while (depending on our provider, up to a day) to propagate.

OpenDKIM server

We can now start the OpenDKIM server with

$ sudo /etc/init.d/opendkim start

Postfix

The last step is to tell Postfix to use OpenDKIM to sign outgoing e-mail messages. Add the following to /etc/postfix/main.cf:

# Milter settings.
milter_protocol = 2
milter_default_action = accept
# OpenDKIM runs on port 12301.
smtpd_milters = inet:localhost:12301
non_smtpd_milters = inet:localhost:12301

If you already have other milters configured (such as SpamAssassin), simply add inet:localhost:12301 to your existing smtpd_milters and non_smtpd_milters lines, prefixed by a comma.

Let’s now restart Postfix with the new configuration:

$ sudo postfix reload

…and we’re done!

Step 2: SRS

SRS, short for Sender Rewriting Scheme, is a standard for including forwarding / relay information in a forwarded / relayed e-mail message.

For example, suppose [email protected] sends an e-mail to [email protected], and our Postfix server on myserver.example.com forwards this e-mail to [email protected]. SRS allows our Postfix server on myserver.example.com to attach a virtual sticky note on the e-mail message explaining this situation to Gmail. Otherwise, Gmail might become suspicious of why myserver.example.com is producing messages that purport to come from hotmail.com, which spammers and phishers are wont to do.

We will use PostSRSd to implement SRS in our Postfix server. It works out of the box with Postfix and is a breeze to set up, but unfortunately is not included in the official Ubuntu / Debian package repositories.

OpenSRSd

Let’s build and install OpenSRSd from source:

# Dependencies.
$ sudo apt-get install unzip cmake

# Download and extract source code from GitHub.
$ cd /tmp
$ curl -L -o postsrsd.zip \
    https://github.com/roehling/postsrsd/archive/master.zip
$ unzip postsrsd.zip

# Build and install.
$ cd postsrsd-master
$ mkdir build
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/usr ../
$ make
$ sudo make install

The default config provided by PostSRSd (/etc/default/postsrsd) will pretty much work out of the box for our case.

The install script will also conveniently install an Upstart script for PostSRSd. Let’s start it now:

$ sudo service postsrsd start

Postfix

Finally, we configure Postfix to use PostSRSd. Add the following to /etc/postfix/main.cf:

# PostSRSd settings.
sender_canonical_maps = tcp:localhost:10001
sender_canonical_classes = envelope_sender
recipient_canonical_maps = tcp:localhost:10002
recipient_canonical_classes= envelope_recipient,header_recipient

And restart Postfix with the new configuration:

$ sudo postfix reload

That’s it!