Skip to main content

DKIM Milter

In order for high-profile email servers like Gmail and Outlook to receive emails we send, we need to sign our outgoing emails using DKIM. We use dkimpy-milter to do this.

dkimpy-milter is a milter, or mail filter. Postfix will pass incoming and outgoing emails through this milter, which will:

  • Sign outgoing emails using our DKIM key
  • Verify the DKIM signature of incoming emails

Installation

To install dkimpy-milter, install its Debian package:

# apt install dkimpy-milter

Enabling the service

Like Postfix and Dovecot, dkimpy-milter provides a systemd service which we will enable so the milter is automatically started:

# systemctl enable dkimpy-milter.service

Generating keys

Before configuring dkimpy-milter, we must generate keys which will be used to sign outgoing mail.

First, create and move into the /etc/postfix/dkim directory:

# install -d -m 0750 /etc/postfix/dkim
# cd /etc/postfix/dkim

According to dkimpy-milter's documentation,

Signing keys should be protected (owned by root:root with permissions 600 in a directory that is not world readable).

Using dknewkey(1)

An easy way to generate a key is to use the dknewkey(1) utility that comes with dkimpy-milter:

# dknewkey <keyname>

<keyname> can be anything you want, but it must be the same value used throughout the rest of the configuration. For simplicity, we use the name mail.

dknewkey(1) will generate a 2048-bit RSA key. If you want a larger key, you must generate it manually using OpenSSL.

Using OpenSSL

We can manually generate a private RSA key using OpenSSL:

# openssl genrsa -out <keyname>.key <keysize>
# chmod 600 <keyname>.key

keysize should be 2048 or 4096. This defines the length (in bits) of the newly generated key.

After generating the private key, we must extract the public key and create the DNS record text:

# printf 'v=DKIM1; k=rsa; h=sha256; p=%s' "$(openssl rsa -pubout -outform DER -in <keyname>.key | openssl base64 -A)" > <keyname>.dns

This will extract the public key, encode it in Base64, and print it with the proper formatting for a DKIM DNS record. The output will be written to <keyname>.dns.

Configuration

dkimpy-milter is configured by placing settings in the /etc/dkimpy-milter/dkimpy-milter.conf file. The contents are listed below. All of the options are commented and are fairly easy to understand, so they won't be explained further.

# Log to syslog
Syslog  yes

# Sign mail for kasad.com and subdomains
Domain      kasad.com
SubDomains  yes

# Specify the keyfile and selector to use
KeyFile   /etc/postfix/dkim/mail.key
Selector  mail

# Use this server's hostname as the auth server ID when
# generating the Authentication-Results header
AuthservID  HOSTNAME

# Listen on localhost on port 12301
# This is the socket Postfix will use to communicate with dkimpy-milter
Socket  inet:12301@localhost

Selector names

DKIM uses a selector, which is an arbitrary value that identifies a certain key. The key name and the selector need not match. The selector can be whatever you wish, as long as the same value is used in the DKIM DNS record.

DKIM DNS record

Mail servers that we send emails to will validate our DKIM signature by retrieving our public key from a DNS TXT record. Define a TXT record for the <selector>._domainkey.<domain> domain, where <selector> is the selector defined in the configuration file (mail in our case) and <domain> is your mail server's domain (kasad.com in our case). The content of this record should be the content of the <keyname>.dns file we generated earlier.

If the key used to sign mail is changed, the DNS record must be updated with the new public key. If the selector is changed, but the key remains the same, the record's content can remain and only the first domain component must be changed.

As an example record, dig TXT mail._domainkey.kasad.com outputs the following:

mail._domainkey.kasad.com. 214	IN	TXT	"v=DKIM1; h=sha256; k=rsa;  p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxOlorHTT/rsI5WWobgA0/+XRWAav1F5As1YoUVEUknIPbIJDuMIbEbV468XdHsp63PvwF2uz9A3iEefaGIMOpcJrgIcb3X5el0/x89kxK/zDDruiAzpcLwdy6urEmQhdRfoi1stdOhDlo8dNQj5vRORceJ2v5fUJ3VUV9eWd7cGOjhladUWedY" "gdIdiYqsbR6CeYIhpKK1v414 UmtB1sKcxHgxbROm+yjM6iJaSQbF9iLUlBEHOBfRc1vVuw0N+LQpRDNaaHSom0SusrMnXnjb33ANNCFMITwL9fZm9mR+sR+m+2QGvhLyODJMsxRCBKSSZhrjP2Csa80ZnOtNX91QIDAQAB"

Note that the public key is too long to fit in one TXT record, so it is broken into two. I do not know the specifics on how DNS or DKIM clients handle split TXT records. However, this is how most DKIM records look and I have not experienced any compatibility issues so far.