Improve mailing: Always send bulk E-Mails with SPF and DKIM

on under developer
3 minute read

Out Of Date Warning

This article was published on 20/06/2013, this means the content may be out of date or no longer relevant.
You should verify that the technical information in this article is still up to date before relying upon it for your own purposes.

When deciding, how to send mails to customers efficiently, there is always a problem of how to sustain deliverability and not get classified as spammy service. Besides organisational measures, like interesting content and visible unsubscribe links, there are 2 DNS settings, which should be set anyway: DKIM and SPF.

SPF

The SPF record roughly contains a list of servers, that are allowed to send mails on behalf of the domain. It is easy to do:

$ dig heise.de TXT

heise.de.               3600    IN      TXT     "v=spf1 ip4:193.99.144.0/24 ip4:193.99.145.0/24 ?all"

Here, heise.de authorizes the both ip subnets to send mails on behalf of "@heise.de". Besides IPs, you can reference the A and CNAME records. (I use the standard tool dig, to query current DNS settings)

To get that, login to your DNS service provider and add a new DNS entry:

  • Type TXT
  • host: your server's hostname
  • Content: v=spf1 mx a ip4:123.123.123.123 -all
  • TTL: e.g. 3600 (seconds)

This way, you allow mails only come from your server's MX, A-records or manual IP. The -all forbidds any other server to send e-Mails for your domain.

Checking

Just send a mail from your server to a mail provider (like gmail) and view the e-mail header ("full source"). It should contain:

spf=pass (google.com: domain of XXX@yyy designates 123.123.123.123 as permitted sender) smtp.mail=xxx@yyy;

DKIM

Besides SPF, which is relatively easy to implement, there is the somewhat more complicated DKIM, a.k.a. Domain Keys. Here, you also need two custom DNS TXT entries and also have to configure your mail sending daemons (MTU) and/or applications to sign your mails.

First, generate a public and private key pair

Using openssl:

openssl genrsa -out dkim.private.key 1024
openssl rsa -in dkim.private.key -out dkim.public.key -pubout -outform PEM

Second, put the public key in the DNS record

Create a DNS entry:

  • Type TXT
  • Host: _domainkey.YOURHOST.com
  • Content: t=s; o=~;

Choose a "selector", which can be a kind of name for the validations. Below, I will use x. Common is also default. Create another DNS entry

  • Type TXT
  • Host: x._domainkey.YOURHOST.com
  • Content: v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQE... after p= put your public key from above without BEGIN/END header and all lines joined

After waiting some time, check the 2 DKIM DNS keys with:

$ dig _domainkey.HOSTNAME.com TXT
$ dig x._domainkey.HOSTNAME.com TXT

or use an online tool: http://dkimcore.org/c/keycheck or http://www.protodave.com/tools/dkim-key-checker/

Third, enable your MTA and/or Application to sign outgoing e-Mails

e.g. Exim (German guide),

or Rails:

# Gemfile
gem "dkim"

# config/initializers/dkim.rb
if Rails.env.production?
  # Configure dkim globally (see above)
  Dkim::domain      = 'YOURHOSTNAME.com'
  Dkim::selector    = 'x'
  Dkim::private_key = File.read('config/dkim/dkim.private.key')
  # This will sign all ActionMailer deliveries
  ActionMailer::Base.register_interceptor(Dkim::Interceptor)
end

Verify success

Just send an e-Mail to e.g. Gmail/Yahoo/Outlook.com, end check the mail's source code: Authentication-Results: mx.google.com; spf=pass (google.com: domain of noreply@HOSTNAME designates 123.123.123.123 as permitted sender) smtp.mail=noreply@HOSTNAME; dkim=pass header.i=@HOSTNAME

Troubleshooting

  • dkim=policy (weak key) The private key needs at least 1024 bits, otherwise GMail will fail the DKIM test.

  • dkim=fail (test mode) Your DNS entry has an t=y part, which enables test mode

  • dkim=neutral (bad format) Check the format of your DNS entry. Maybe there are some spaces/newlines

  • DNS resolving does not update Try a shorter key length (1024bit).

Happy Mailing!