Configuring OpenDKIM to sign Postfix emails
Overview
DomainKeys Identified Mail (DKIM) is a method used by modern MTAs which allows an organization to sign own emails before delivery. The main purpose of DKIM is to help fight spam by associating a domain name with an mail message. DKIM uses public-key cryptography which allows the sender to electronically sign his emails in a way that can be verified by recipients. The DKIM public key is stored in DNS in order to let receivers verify both the origin and integrity of a message and the private key is used to sign each outgoing message. The DKIM signature is added as a field to the message’s header before delivery. In this article we will implement mail signing using OpenDKIM on Debian Wheezy although this configuration should run fine on any Linux distribution.
Installing OpenDKIM
As a prerequisite we assume you have an already running Postfix mail server along with an DNS server. OpenDKIM is included in the standard Debian repositories. To install it run the following command:
# apt-get install opendkim opendkim-tools
This will create the startup initialization script and a system user called opendkim which will be used as owner for the OpenDKIM configuration files. The account has a restricted shell such as /bin/false. To see if the service is running type the following commmand in a terminal:
# netstat -nl | grep opendkim
This should display a line which indicates that the service is running and listening on a local UNIX socket.
unix 2 [ ACC ] STREAM LISTENING 42587490 /var/run/opendkim/opendkim.sock
On older Debian versions OpenDKIM was listening by default on localhost IP port 8891. Another way to check that OpenDKIM is running is to use the following command:
# ls -al /var/run/opendkim/
-rw-r–r– 1 opendkim opendkim 7 Dec 10 18:10 opendkim.pid
If everything is fine you should see the opendkim.pid file which contains the PID of the OpenDKIM application.
OpenDKIM key generation
Before we start configuring OpenDKIM we need to generate the keys used for mail signing. First we must prepare a directory to store the private keys. For easier management the directory should be named after the domain for which we are creating the keys, in my case I’ll use cioby.ro domain.
# mkdir -pv /etc/opendkim/cioby.ro/
If you need to setup additional domains for DKIM create separate directories under /etc/opendkim for storing the keys. As a security measure we need to restrict access to this directory to the opendkim user only.
# chown -R opendkim:opendkim /etc/opendkim
# chmod 700 /etc/opendkim/*
In order to generate the key pair OpeDKIM provides the opendkim-genkey tool. Go to the directory where you will store the key and run the following commands:
# cd /etc/opendkim/cioby.ro/
# opendkim-genkey -r -h rsa-sha256 -d cioby.ro -s email
The keys are saved in separate files: selector.private for the private key and selector.txt for the DNS TXT record that contains the public key in my case the files are called email.private and email.txt.. Let’s explain what each parameter of opendkim-genkey command does.
- -r – restricts the key for use in e-mail signing only. The default is to allow the key to be used for any service.
- -h – indicates a list of hash algorithms (rsa-sha256) which can be used with this key
- -d – specifies the domain (cioby.ro) which will use this key for signing. The default is “example.com”.
- -s – specifies the selector, (email) or name, of the key pair generated. The default is “default”.
Next we must secure our private key. Run the following commands:
# mv -v email.private email.key
# chown opendkim:opendkim *
# chmod 600 *
Publish public key in DNS
The next step is to copy the content of the public key /etc/opendkim/cioby.ro/mail.txt and setup a new TXT record on your DNS zone. The public key is generated in BIND DNS format and looks similar to the following:
email._domainkey IN TXT “v=DKIM1; h=sha256; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8u8B6x8cKgzqT+3/zKe+kBEekktN4p8AwLbGx/TZsV7bo0x3NyqIu2t9GKA29+6RCTZZDHXCVoWOSi3Zi9f2zDVYjPcVe+ISbM386RbJvfMSnjD8K6RAbOhTIAzHsC28jLSsFBmUzHeSyXCOAP+1Cn/sl5xdQIDAQAB” ; —– DKIM key email for cioby.ro
Next add the DNS TXT record to the appropriate zone file, increase the serial number of the zone, and reload your DNS server. After the public key has been published to DNS you can verify the key pair using the opendkim-testkey command:
# opendkim-testkey -vvv -d cioby.ro -s email -k /etc/opendkim/cioby.ro/email.key
opendkim-testkey: key loaded from /etc/opendkim/cioby.ro/email.key
opendkim-testkey: checking key ’email._domainkey.cioby.ro’
opendkim-testkey: key not secure opendkim-testkey: key OK
Note that OpenDKIM is reporting that the key is not secure. The message “key not secure” does not indicate an error. This relates to the fact that DNSSEC is not implemented on my DNS server and theoretically somebody could intercept the DNS lookup and replace it with their own key. Another method to check the pusblished DKIM key is to go here and type the selector and the domain name you are checking.
Configuring OpenDKIM
The main configuration file is opendkim.conf which is located under /etc directory on Debian/Ubuntu. The general format of entries in opendkim.conf is a parameter on one line followed by a value. Before we start configuring parameters in opendkim.conf we need to create some additional files. First we need to define the location of a file which maps key names to signing keys. Create a file called KeyTable in the /etc/opendkim directory. The syntax for each key is:
key_name domain:selector:/path/to/private_key_file
These values have the following meanings:
- key_name – identifier for the key name.
- domain – the name of the domain to use in the signature’s “d=” value.
- selector – the name of the selector to use in the signature’s “s=” value.
- /path/to/private_key_file – the path to a file containing a private key.
In my case the KeyTable file will have a line like the following:
cioby.ro cioby.ro:email:/etc/opendkim/cioby.ro/email.key
You can add multiple keys to this file based on the numbers of domains you have. Next we must set up the signing table which defines a table used to select one or more signatures to apply to a message based on the address found in the From: header field. Create a file called SigningTable in the /etc/opendkim directory. This file has the following syntax:
The above syntax have the following meaning:
- signer_of_the_message – the signer of the message taken from the From: header field of a message. Wildcards are allowed.
- key_name – identifier for the key name stored in the KeyTable file.
In my case the SigningTable file will have a line like the following:
*@cioby.ro cioby.ro
Since we want to use wildcards, we can’t actually use a regular flat file. Wildcards require a regular expression file, or “refile”. The above example is valid format. Then you need to create the TrustedHosts file in the same /etc/opendkim directory. This will contain a list of trusted IP’s, domain names, or range of IPs in CIDR notation (e.g. 172.16.120.0/24) when signing or verifying messages. Each IP or domain name must be specified on a separate line. One important thing to remember is that the localhost IP address 127.0.0.1 must exist in this file otherwise OpenDKIM won’t sign mail sent from your server. As an example the TrustedHosts file can contain entries like below:
127.0.0.1
cioby.ro
172.16.120.0/24
Now we need to change ownership of the newly created files to the opendkim user. Run the following command:
# chown opendkim:opendkim /etc/opendkim/{KeyTable,SigningTable,TrustedHosts}
Next we must set up the main configuration file /etc/opendkim.conf. After installation OpenDKIM on Debian comes with an preconfigured OpenDKIM file so it’s better to remove all content from this file and add the following lines:
Syslog yes
SyslogSuccess yes
LogWhy yes
UMask 022
OversignHeaders From,Subject
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList /etc/opendkim/TrustedHosts
InternalHosts /etc/opendkim/TrustedHosts
SignatureAlgorithm rsa-sha256
AutoRestart Yes
UserID opendkim:opendkim
In the next lines I’ll explain what each of these configuration parameters does.
- Syslog – this option indicates that OpenDKIM log messages should be sent to syslog instead of a specific log file.
- SyslogSuccess – is used to log additional entries indicating successful signing or verification of messages
- LogWhy – this option depend on the Syslog to be enabled and is used for very detailed logging about the logic behind the filter’s decision.
- UMask – this option indicates permissions mask to be used for file creation. In this case 022 indicates that by default only owner can write to the newly created files.
- OversignHeaders – specifies a list of headers which should be included in all signature header lists (the “h=” tag) even if they were not present at the time the signature was generated.
- KeyTable – gives the location of a file mapping key names to signing keys. If present, overrides any KeyFile setting in the configuration file.
- SigningTable – defines a table used to select one or more signatures to apply to a message based on the address found in the From: header field.
- ExternalIgnoreList – identifies a set of “external” hosts that may send mail through the server as one of the signing domains without credentials
- InternalHosts – identifies a set internal hosts whose mail should be signed rather than verified.
- SignatureAlgorithm – selects the signing algorithm to use when generating signatures. The default is rsa-sha256 if it is available, otherwise it will be rsa-sha1.
- AutoRestart – specifies that OpenDKIM should automatically re-start on failures.
- UserID – specifies that opendkim process should start under specified userid. The value is of the form userid[:group] in my case is opendkim:opendkim.
The system is highly configurable, and many more parameters (over 80) are described in the opendkim.conf man page. Since Postfix on Debian runs on chroot it is necessary to change the OpeDKIM socket path to allow Postfix to read it. In order to do this we need to create the directory hierarchy and setup correct permissions under /var/spool/postfix directory.
# mkdir -p /var/spool/postfix/var/run/opendkim
# chown opendkim:opendkim /var/spool/postfix/var/run/opendkim
Since that directory is owned by the user ‘opendkim’, the user ‘postfix’ which runs the postfix daemon cannot write to it, we have to add the system user ‘postfix’ to the ‘opendkim’ group and change permissions for group to write to the socket:
# usermod -a -G opendkim postfix
# chmod 775 /var/spool/postfix/var/run/opendkim/opendkim.sock
Lastly we must configure OpenDKIM with the correct path to local UNIX socket. For this we need to edit the the /etc/default/opendkim configuration file and add the following line:
SOCKET=”local:/var/spool/postfix/var/run/opendkim/opendkim.sock”
This could also be configured by specifying the Socket option in the /etc/opendkim.conf file.
Postfix Configuration
Now, we need to tell the Postfix about the existing milter, and where to connect with it. Open your Postfix main.cf file /etc/postfix/main.cf, and append to its content the following lines:
# OpenDKIM milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:/var/run/opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters
These parameters have the following meanings:
- milter_default_action – specifies the action to take in case a Milter (mail filter) application is unavailable or mis-configured
- milter_protocol – indicates the mail filter protocol version and optional protocol extensions for communication with a Milter application; prior to Postfix 2.6 the default protocol version is 2
- smtpd_milters – specifies the list of mail filter applications for new mail that arrives via the Postfix smtpd(8) server. Use space or comma as separator. The syntax for a Milter can be unix:/path/to/socket or inet:host:port which indicates the hostname and the TCP port to connect (e.g. inet:localhost:8891)
- non_smtpd_milters – specifies the list of mail filter applications for new mail that does’t arrive via the Postfix smtpd(8) server. $smtpd_milters specifies to use same value as the smtpd_milters option.
Then restart the Postfix server so that the configuration changes take effect.
# service postfix restart
Testing and troubleshooting OpenDKIM
Now that configuration is done is time for testing or troubleshooting in case something went wrong. The OpenDKIM daemon can be started with the following command:
# service opendkim start
To test that OpenDKIM is setup correctly you could send an email to one email account you have access and then check the email’s headers after you received it. Personally for testing I send an email from command line using the mail command to one of my mail accounts. When you expand the headers for an signed email you should see something like this:
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=cioby.ro; s=email; t=1388429659; bh=Y1y/ROlJQinwJpbQ8Hj4TEBjT4hG5pLH+ajf4xfUK50=;
h=Date:From:To:Subject:From:Subject;
b=CjtA+nj0zSsLOrredS0XoJ3AXpokYr5dk2LJWuCfMp1a+7ulWdJGv7nair398FeBorSWoJAgPWkQCWzeXQGnc2ktPYvrRkkpHZJVN+gxOr+Ikujg7LZ66Xk7ZAmUz90Uyg4X9qzc7NxCujAZtAr9kz1Wb7xf6/9fQ7ZzeA=
Another way to test is to Send a signed email to: check-auth@verifier.port25.com which will return an email telling you if things are working properly, and give you some pointers on troubleshooting if needed.
The first place to troubleshoot OpenDKIM problems is to monitor the mail logs.
# tail -f /var/log/mail.log | grep -i dkim
This command will filter the log and display only messages concerning OpenDKIM. If the email is sent but it’s not signed you should see a similar message in the log file:
Dec 30 18:58:25 servername opendkim[16587]: 23A1E2411D0: no signature data
In this case you should double check your /etc/opendkim/KeyTable file for mistakes. Remember every time you make a change to the configuration file you need to restart the OpenDKIM daemon.