HowTo: Postfix with DKIM

Reading Time: 4 minutes

In this post, I would like to show, how to use DKIM together with postfix to enhance the chance, that mails send by your mail server are not marked as spam at the receiver. In my last post, I showed how to set up postfix in front of scalix to filter incoming mails. As also the outgoing mails will use postfix I would like to insert the DKIM header into the mails by postfix. To do this, I will use opendkim on debian. Before I start to explain how to configure it, I would like to explain DKIM.

What is DKIM

DKIM or DomainKeys Identified Mail is a procedure, developed by Yahoo in order do validate the sender of a mail. This can be helpful to detect if the mail is spam or not.

If a mail is sent, the sending mail server will add a DKIM header to the mail. This header includes information like the signing domain and a selector, besides other information and the signing key. The receiver will then use both information to query a TXT (DNS) record for this domain to get the public signing key. With the help of this key, the receiver of the mail will recalculate the signing key on his own and then compare the one in the mail with the calculated one. If both matches, the mail was not altered during transmission and comes from a trusted source.

Using DKIM with Postfix

To sign outgoing emails and check incoming emails, you can use OpenDkim on Debian together with postfix.

The first step is to install OpenDkim on Debian:

apt-get install opendkim

After OpenDkim is installed, you need to configure it to work with Postfix. Open the main configuration file for OpenDkim:

/etc/opendkim.conf

The file should look like this:

# This is a basic configuration that can easily be adapted to suit a standard
# installation. For more advanced options, see opendkim.conf(5) and/or
# /usr/share/doc/opendkim/examples/opendkim.conf.sample.

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

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

# Commonly-used options; the commented-out versions show the defaults.
#Canonicalization simple
#Mode sv
#SubDomains no
#ADSPAction continue

# Always oversign From (sign using actual From and a null From to prevent
# malicious signatures header fields (From and/or others) between the signer
# and the verifier. From is oversigned by default in the Debian pacakge
# because it is often the identity key used by reputation systems and thus
# somewhat security sensitive.
OversignHeaders From

# List domains to use for RFC 6541 DKIM Authorized Third-Party Signatures
# (ATPS) (experimental)

#ATPSDomains example.com

AutoRestart Yes
AutoRestartRate 10/1h
SyslogSuccess Yes
LogWhy Yes

Canonicalization relaxed/simple

ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable

Mode sv
PidFile /var/run/opendkim/opendkim.pid
SignatureAlgorithm rsa-sha256

UserID opendkim:opendkim

Socket inet:12301@localhost

The last part is the interesting one. Save the file. We now need to create some additional files to get it working.

I will start with this file:

/etc/opendkim/TrustedHosts

This file is used to specify hosts and domains which are not checked but signed. The file could look like this:

127.0.0.1
localhost
10.3.5.0/24

*.flomain.de

According to the content of this file, all emails from localhost, 127.0.0.1 and from the internal network 10.3.5.0/25, where the Scalix host is located will not be checked, but signed with the DKIM key. The “*.flomain.de” entry will make sure that not only mails from the root domain are signed, but also mails from sub domains.

The next file is the KeyTable:

/etc/opendkim/KeyTable

This file is used to link the domain selector to the private key which is stored in a file. It could look like this:

mail._domainkey.flomain.de flomain.de:mail:/etc/opendkim/keys/flomain.de/mail.private

The separator is “mail”. You can use any separator as you like.

The last file is the file with the signing table:

/etc/opendkim/SigningTable

This one instructs OpenDkim, which key should be used for which mail address. Basically, every mail user could have their own private key. In reality, this makes no sense and you would specify one key for the domain like this:

*@flomain.de mail._domainkey.flomain.de

We have now everything in place, except the keys. Generating the keys is the next step. The good news is, that OpenDkim already includes the tools to generate the key.

You can create the key in every folder on your server and copy the key then into the key directory or you change to the keys directory now:

cd /etc/opendkim/keys/

You should create sub folders for every domain:

mkdir flomain.de

and change into this created directory. To generate the key use this command:

opendkim-genkey -s mail -d floamin.de

Where “s” is the selector and “d” is the domain.

This will generate two files. The key file, which should be kept private by changing the owner to “opendkim” and make it available only for this user, and a txt file which has the txt record the for the DNS server included.

I know have to add the content of the txt file to the DNS server as a txt record.

You can check, if the record is correct by using this site:

http://www.protodave.com/tools/dkim-key-checker/

You need to insert the selector and the domain and the tool will query your DNS server and check if the record is there and configured correctly.

If this is working, you need to tell postfix to use Opendkim to check and sign messages.

Open this file:

/etc/default/opendkim

and add this to the end of the file:

SOCKET="inet:12301@localhost"

Save the file and open the Postfix configuration:

/etc/postfix/main.cf

Check if those lines are present:

milter_protocol = 2
milter_default_action = accept

and add the following lines:

smtpd_milters = inet:localhost:12301
non_smtpd_milters = inet:localhost:12301

If the two commands above are already there, e.g. if you use milter to connect SpamAssassin to postfix, simply add the Opendkim milter at the end, separated by a “,”.

After restarting both services, Opendkim and Postfix, it should work. You can test this by sending a mail to “[email protected]”. You should receive a reply containing the result of the DKIM check like this:

==========================================================
Summary of Results
==========================================================
SPF check:          pass
DomainKeys check:   neutral
DKIM check:         pass
Sender-ID check:    pass
SpamAssassin check: ham

If you see “pass” for the DKIM check, then your DKIM configuration is working.

If you look into the source code of your emails and see the DKIM header twice, you should adjust your Postfix master config:

/etc/postfix/master.cf

You should have an entry like this at the end of the file:

127.0.0.1:10025 inet n - - - - smtpd
 -o content_filter=
 -o local_recipient_maps=
 -o relay_recipient_maps=
 -o smtpd_restriction_classes=
 -o smtpd_delay_reject=no
 -o smtpd_client_restrictions=permit_mynetworks,reject
 -o smtpd_helo_restrictions=
 -o smtpd_sender_restrictions=
 -o smtpd_recipient_restrictions=permit_mynetworks,reject
 -o smtpd_data_restrictions=reject_unauth_pipelining
 -o smtpd_end_of_data_restrictions=
 -o mynetworks=127.0.0.0/8
 -o smtpd_error_sleep_time=0
 -o smtpd_soft_error_limit=1001
 -o smtpd_hard_error_limit=1000
 -o smtpd_client_connection_count_limit=0
 -o smtpd_client_connection_rate_limit=0
 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks

Add this to the last line:

,no_milters

After restarting Postfix, you should have only one DKIM header in your mails.

If you have any questions, regarding this post or if you would like provide feedback, please use the comment function below.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.