All About LAMP Create web applications with Linux, Apache, MySQL, PHP and other open source technologies

How to Setup DKIM for Postfix in Fedora using dkim-milter

09.28.2009 · Posted in Email
This article was written by Daniel Lam. About the author: Daniel Lam is an IT consultant in Sydney, Australia. If you are interested in contributing in this site, check this out.

Overview

DKIM is a technology that allows mail senders to attach a signature in outgoing mails, so that recipients can check the signatures against DNS records of the sender to see if mail is indeed sent from there.

Because free email providers like gmail make use of DKIM to determine if sender is sending spam, i.e. “if your domain does not implement DKIM, you are a spammer”, it is important to implement DKIM for your mail servers to avoid your legitimate out-going emails be classified as spam.

Context

This article describes how to implement DKIM for Postfix in Fedora using the dkim-milter open-source module. This solution has been tested with Fedora 10.

DKIM allows a domain to be associated with multiple “signatures”. Each signature is identified by its “selector”. In this example, we are going to create only one signature with its selector named “default”.

Steps

1. Generate a private key

openssl genrsa -out default.private 1024

A “default.private” key file will be generated. It will be moved to a specific location later.

2. Generate a public key for this private key

openssl rsa -in default.private -pubout -out default.public -outform PEM

A file with filename “default.public” will be generated with content like

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVqyBW3CvurzAJrWvw/rbiMVL2
6lytBkhIrgEBWjGWEjjhM6mQpQWLq9VR46xlL4OT6UdVtO8QOMEVI23LN0fwtrPc
/auwHC2U9joUWTWVjOMZWEywOHwATGevh9TApt2hQJkWjMy/xmCIqBs9VZIweRlf
VFqc9WEu6VamGe9C3QIDAQAB
-----END PUBLIC KEY----

It will be used to create a DNS TXT record. See next step.

3. Create a DNS record of type TXT

Modify DNS records and add a record of type TXT:

TXT record name
default._domainkey

TXT record value
v=DKIM1; g=*; k=rsa; p=<content of default.public>

Note that the prefix “—–BEGIN PUBLIC KEY—–” and suffix “—–END PUBLIC KEY—-” should not be put in the TXT record value.

This DNS record will be retrieved by mail receivers who want to verify emails with DKIM signatures. The record name “default._domainkey” tells verifier that the “selector” of this signature is “default”, therefore if you are changing selector name to something else, make sure you change all of them consistently.

4. Install dkim-milter in Fedora

Run the following as root to install the dkim-milter pacakge.

yum install dkim-milter

5. Enable dkim-milter to run on start-up

Make sure dkim-milter service will run on start-up by running this command:

chkconfig --level 3 dkim-milter on

Note that your server may use a different “runlevel”. You can check “/etc/inittab” to see which run level you are on.

6. Move private key to appropriate location

As root, copy the private key to the location specified by the “keylist” (refer to next step) and make sure it is readable by dkim-milter:

mkdir /etc/dkim-milter/
mv default.private /etc/dkim-milter/default
chown dkim-milter.dkim-milter /etc/dkim-milter/default

Make sure the filename of private key file matches the “selector” name specified in the DNS record.

7. Add an entry to the keylist for dkim-milter to read

Add the following line to /etc/mail/dkim-milter/keys/keylist. Replace <domain.com> with your domain name.

*:<domain.com>:/etc/dkim-milter/default

8. Configure postfix to use dkim-milter

Add the following lines to /etc/postfix/main.cf to ask postfix to use dkim-milter.

smtpd_milters = unix:/var/run/dkim-milter/dkim-milter.sock
non_smtpd_milters = unix:/var/run/dkim-milter/dkim-milter.sock

9. Start dkim-milter and restart postfix

Start dkim-milter service and restart postfix using the following commands. Or restart the server.

service dkim-milter start
service postfix restart

10. Change file permissions of the Mail Filter Socket file

Change file permissions of the “Mail Filter Socket” file and its parent directory to allow postfix to write to it:

chmod 755 /var/run/dkim-milter
chmod 777 /var/run/dkim-milter/dkim-milter.sock

Changing dkim-milter.sock permission unfortunately is required EVERYTIME after dkim-milter servuce is restarted. This is because dkim-milter resets the file to mode 755 that postfix cannot read.

For Fedora, one way to have this done automatically is to add the chmod command in /etc/rc.d/rc.local, so that it will be run on start-up everytime.

Errors / Configuration Problems

Gmail header says “dkim=neutral (body hash did not verify)”

If you are sending emails using mail() function in php, these out-going emails will not be verified by Gmail, for some unknown reason. Opening the mail source in Gmail shows that there is a line “dkim=neutral (body hash did not verify)”, hinting that the key in DKIM signature does not match the public key from DNS. Only Gmail knows why.

You may also notice that sending emails directly from the command-line in your server will be DKIM-verified by Gmail, resulting in a “dkim=pass” in the mail source headers. Therefore, a work-around for this problem is to modify your php scripts so that it will do a system call to a shell script, which will indirectly send your email out. This way, Gmail will verify your email and put it to the inbox of recipient instead of marking it as spam.

Gmail header says “dkim=neutral (no key)”

It means that gmail couldn’t find a matching key to verify your signature.

So, how does gmail (or any other dkim verifier) find a matching key? It relies on the “selector name” from the email header. In the email header there should be something like “DKIM-Signature: … s=some_selector_name; t=1254783208;…”, here the “some_selector_name” is the selector name. Dkim-milter specified that as your selector name as instructed by /etc/mail/dkim-milter/keys/keylist file.

Now if you look at the DNS record for your domain, it needs to have a DKIM public key record (TXT type DNS record) with the name “some_selector_name._domainkey” and value being the matching public key. If the name of the record is not “some_selector_name._domainkey”, the verifier will not be able to use the public key, because the selector name does not match.

You might have followed an example from internet to set your selector name to “domain.com_default.key.pem” and the DNS record name to “default._domainkey”, which are not matching each other and therefore getting this no-key message.

Reference

  • http://www.howtoforge.com/postfix-dkim-with-dkim-milter-centos5.1 Instructions similar to this article, but for CentOS instead of Fedora.
  • http://testing.dkim.org/reflector.html Free testing to check if your DKIM implementation works.
  • Config files related to dkim-milter in Fedora:

    Socket file that Postfix connects to
    /var/run/dkim-milter/dkim-milter.sock

    Key List file
    /etc/mail/dkim-milter/keys/keylist

    Actual private key (can be anywhere as specified by “Key List”)
    /etc/dkim-milter/<key filename>

    Config file
    /etc/mail/dkim-milter/dkim-filter.conf

    Another config file but does not appear to be read by the service
    /etc/sysconfig/dkim-milter

    Postfix config that specifies where dkim-milter socket is:
    /etc/postfix/main.cf

13 Responses to “How to Setup DKIM for Postfix in Fedora using dkim-milter”

  1. [...] Postfix, use these instructions from All About LAMP. If you want to use sendmail as I did, here are my additional [...]

  2. Henry Hughes says:

    Brilliant article. Had me up and running on my test rig in no time, thank you.

    I’m curious as to why you mention using openssl command to create the keys, when you can use the utilities that comes with dkim-milter itself, eg:

    /usr/sbin/dkim-genkey -r -d mydomain.com

    This outputs 2 files, private and the exact string for TXT dns record.

    Incidentally, a bug has been reported regarding the permisions problem with the .sock file:
    https://bugzilla.redhat.com/show_bug.cgi?id=527423
    … unsolved as yet.

  3. Daniel Lam says:

    Good to know that it helped! Regarding the use of openssl – that was just because I was not aware of the dkim-genkey tool. Thanks for pointing out!

  4. ThomasRutter says:

    The “did not verify” problem when using PHP seems to be caused by email bodies containing the “\r” character. That is, a carraige return. Postfix’s sendmail implementation expects line breaks to be “\n” only.

    Stripping these out before using mail() fixed the problem for me, and all emails verify DKIM properly with PHP posting to sendmail->postfix->dkim-filter

    I’ve seen problems before with PHP sending mail with “\r\n” instead of “\n”. It’s strange, because email itself wants “\r\n”, however PHP (or sendmail) seems to expect “\n” only. Not sure how this affects things if you’ve configured PHP not to use sendmail.

    My guess is that PHP or sendmail is converting line breaks before dkim-filter, and dkim-filter sees a version with incorrect “\r\r\n” line breaks, then these are stripped out when the mail is transmitted, breaking DKIM’s signature. Unfortunately dkim-filter’s “relaxed” body verification doesn’t mitigate this problem as it deals only with whitespace within a line – position and type of line breaks ignored.

  5. ThomasRutter says:

    Trailing blank lines seems to do it too. Looks like we have to be careful about filtering our mail before submitting it to PHP’s mail() function. I expect there to be other things that trip it up.

    I think I’ll abstract it out with a library.

  6. Daniel Lam says:

    Thanks for the information! Appreciate it!

  7. Trailing blank lines seems to do it too. Looks like we have to be careful about filtering our mail before submitting it to PHP’s mail() function. I expect there to be other things that trip it up.

    I think I’ll abstract it out with a library.

  8. skyhorse says:

    Henry Hughes is right, I have tried with openssl many times but getting hardfail with google.
    After changing to dkim-genkey, I got passed.

    Thank you alot for this post.

  9. Trailing blank lines seems to do it too. Looks like we have to be careful about filtering our mail before submitting it to PHP’s mail() function. I expect there to be other things that trip it up.

    I think I’ll abstract it out with a library.

  10. After extensive test, I found out the magic number of 990 for my php mail() stop working on dkim error “dkim=neutral (body hash did not verify)”

    It turns out to be the configure of postfix limit of 990 and automatically add some line break after the 990th character, which caused the dkim fail.

    lmtp_line_length_limit (default: 990)

    The LMTP-specific version of the smtp_line_length_limit configuration parameter. See there for details.

    This feature is available in Postfix 2.3 and later.

  11. [...] Tags: DKIM, ftp I am trying to install DKIM for my postfix so that i have a signature. I am following this guide: http://allaboutlamp.com/2009/09/setup-dkim-for-postfix-in-fedora-using-dkim-milter/ [...]

  12. [...] I am trying to install DKIM for my postfix so that i have a signature. I am following this guide: http://allaboutlamp.com/2009/09/setup-dkim-for-postfix-in-fedora-using-dkim-milter/ [...]

Leave a Reply