Prevent spoofing with DKIM

Email sender spoofing is the act of pretending to be in control of someone else’s email address. This is a common problem with phishing ( Frequently scammers send emails with a sender address of and hope that the recipient falls for it and trusts them. In fact SMTP does not care which sender address you send. Many mail service providers enforce that you send emails only using your own email address. But some do not. And spammer and scammers obviously do not care at all.

Example without DKIM

So a new method was conceived that added a cryptographic signature to the header of an email that the recipient could check to verify the authenticity of the sender and the integrity of the email. The signature is created using a private key that only the sending mail server has. It can then be verified by the recipient by downloading the corresponding public key from the DNS zone of the sending domain and running a signature check. This works very similar to PGP or S/MIME signing – just on a domain level. Your mail server can sign all outgoing emails automatically. The method used nowadays is called Domain Keys Identified Mail – or short: DKIM.

Let’s take an example. I have just sent an email from GMail to my personal email account on my own mail server. Google uses DKIM signing so the email got this additional header from Google’s mail servers:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; s=20161025;

I need Google’s DKIM public key to verify that signature. It is stored in their DNS zone as a TXT record of “”. The “20161025” is the key selector that is mentioned in the signature as “s=20161025“. You can use any number of keys as long as you create the signatures with the matching private key.  The “_domainkey” part is the standard subdomain for DKIM keys. So let’s get that TXT record:

dig +short txt

“k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXNZF1j8sJPDleRjf9SPBNem0ik58kF1ilC1nUgKAttl9v7FX9hXJXPmLNhVtSKVZ8yruaeOZLeIxtgtk1s81zzIE5Mj0AiGn2wlFt4kYfqlDfYe95YLQHjynu4i7vj1Tj” “ksf62btcCbL+3XhbK+oD5PlqYhXHWuzoKoEp5L4lCihgkONvU/oy7NNeE6quqfF/y0YSLwF2WVA2Kd8L6R0Ar2dYT/3wZCFknI7xhvPqh9HNcIWBELGPwtXcsHbX1wvBlCgNQAUcdJrf2YWzAwqmZ564/1ipL1IMk1nafPJk75ktumVNz6ORuIn3jbZWp9rRpnaeI9cu/8KfSKH2EY9QIDAQAB”

This is the public key that I can use to verify the signature. An automated check can be done using the “opendkim-testmsg” tool. I can run it and paste the entire email including headers and body into it. If it doesn’t complain with an error message then the signature is correct.

Sounds good? Okay, let’s implement that for your email domain, too.

Creating a keypair

As explained above you need a private key that your mail server will use and a public key that gets added to your DNS zone. A simple tool to create and verify DKIM signatures is contained the OpenDKIM toolset. Another helpful tool is “dig” which allows to query DNS records. It works like “nslookup” but is more versatile. Install both:

apt install opendkim-tools dnsutils

rspamd has its built-in DKIM signing module enabled by default. If you put your key file into /var/lib/rspamd/dkim/ using a certain naming scheme it will pick it up automatically. Create that directory to store keys in:

mkdir /var/lib/rspamd/dkim
chown _rspamd:_rspamd /var/lib/rspamd/dkim

Create your keypair:

opendkim-genkey -d -s 2018022301

The selector (-s) I chose is 2018022301 because that’s the day I created it. The first (01) key on 2018-02-23. It doesn’t matter though – you can name it anything you want. If you are lazy and unconcerned you could even use “dkim” as the selector and later spare some work by not needing DKIM maps that define which key is supposed to be used for each domain. “dkim” is the default selector if you do not use maps. However if you need to replace your key later without invalidating previously sent emails you are in trouble. So I recommend you rather use maps as explained further below. It gives you more flexibility and is pretty easy to do.

You should now find two files in your current directory:

  • 2018022301.private ➠ the RSA private key
  • 2018022301.txt ➠ the DNS record

Adding the DNS record

Before you start signing your emails you must make sure that the public key is properly present in your DNS zone for the domain you are sending emails from. Otherwise the recipient will be unable to verify the signature and may incorrectly assume that the email was spoofed.

Take a look at the 2018022301.txt file. It will look something like this:

2018022301._domainkey IN TXT ( “v=DKIM1; h=sha256; k=rsa; ”
“wgmwpxYq1plagGHhi0+gLO9A6kDeoahh7nRQpqqcn9YwuU6Qy4IOmdhBRjTp4GL/zLLWQqwZXFnMAtDArAFlHpMekNKIppAfzNGIz/bBkj4DkMfo72wfOq+YapDJbnhjQyzSAREwIDAQAB” ) ; —– DKIM key 2018022301 for

If you are running your own DNS server you should be able to copy this entire file and put it into your DNS zone. However if your internet provider offers you just a web interface to manage your domains then create a new TXT record with a host name of “2018022301._domainkey” and concatenate all strings within the brackets into a single string without any brackets, newlines or quotes. In my example:

2018022301._domainkey ➠ v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0n1YM3xVwwkp042w+shwyt2QFrUvumfiwOTAc464kQI1awGw96bgrDuRqWllT8rpI7w39qhwf2qBIGBu45O1EyLvZSD3gbf5Riw2HpvD1UcM6Jk0Ga0ii5NgYSA2Ua2zuSBeXj/1To4bmfvwKqgVTwkND08oLA/zplg38kyIVVRsC2OS4zFGpHeuD3q8fS+im9KIf6Oi62QcKKwgmwpxYq1plagGHhi0+gLO9A6kDeoahh7nRQpqqcn9YwuU6Qy4IOmdhBRjTp4GL/zLLWQqwZXFnMAtDArAFlHpMekNKIppAfzNGIz/bBkj4DkMfo72wfOq+YapDJbnhjQyzSAREwIDAQAB

Depending on your ISP it may take a while until the new record is visible on the internet. You can use dig to verify that:

dig +trace txt

If you get the TXT entry like as follows then you are ready to enable DKIM signing in rspamd for that domain:

TXT “v=DKIM1; h=sha256; k=rsa;\194\160p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0n1YM3xVwwkp042w+shwyt2QFrUvumfiwOTAc464kQI1awGw96bgrDuRqWllT8rpI7w39qhwf2qBIGBu45O1EyLvZSD3gbf5Riw2HpvD1UcM6Jk0Ga0ii5NgYSA2Ua2zuSBeXj/1To4bmfvwKqgVTwkND08oLA/zplg38kyIVVRsC2OS4zFGpH” “euD3q8fS+im9KIf6Oi62QcKKwgmwpxYq1plagGHhi0+gLO9A6kDeoahh7nRQpqqcn9YwuU6Qy4IOmdhBRjTp4GL/zLLWQqwZXFnMAtDArAFlHpMekNKIppAfzNGIz/bBkj4DkMfo72wfOq+YapDJbnhjQyzSAREwIDAQAB” “” from server in 24 ms.

Enabling DKIM maps in rspamd

As explained above it is advised to use DKIM maps. It’s nothing fancy. Just a simple file defining which selector you want to use for a certain domain. rspamd will assume that your selector is always “dkim” unless specified otherwise in a map. If you used “dkim” then you may get into trouble when you later want to replace your key. DNS is a sluggish system and propagating a new DKIM public key may take a day. Emails signed with a newer key may get rejected while the DNS record is not yet known everywhere in the world.

Using maps is simple. First we need to change the selector_map setting of the dkim_signing module. To do that create a new file in /etc/rspamd/local.d/dkim_signing.conf and make it contain just these two lines:

path = "/var/lib/rspamd/dkim/$domain.$selector.key";
selector_map = "/etc/rspamd/";

The configuration is pretty self-explaining. rspamd will look for the domain-to-key mapping in the file. Create that file and make it contain this line: 2018022301

That’s all really. rspamd now knows that whenever it sees an outgoing email from it will get the DKIM private key from /var/lib/rspamd/dkim/ and use it to sign the email.

Reload the configuration:

service rspamd reload

This method works well if you just have a few domains that virtually never change. If you are rather serving random customer domains you should consider putting the keys into a Redis database instead as described in the documentation.

Adding the domain key to rspamd

Move the private key that was created by opendkim-genkey to the location where rspamd will look for it:

mv 2018022301.private /var/lib/rspamd/dkim/

The name of the file has to be  DOMAIN + dot + SELECTOR + “.key” like above. If you name the file incorrectly you will get an error in your rspamd.log file like “lua_dkim_sign_handler: cannot load dkim key /var/lib/rspamd/dkim/“.

Make sure that only _rspamd can read it:

chown _rspamd /var/lib/rspamd/dkim/*
chmod u=r,go= /var/lib/rspamd/dkim/*

rspamd will automatically pick up the files and does not need to be restarted.

Send a test email

If you have another email account at another location then you could just send a test email there via your mail server. If you take a look at the received email it should have a DKIM header now like:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;;
	s=2018022301; t=1519400838;
DKIM signature verification

To verify the signature copy the entire test email (including headers and body), run opendkim-testmsg in your shell and paste the email (finish with CTRL-D).

If you get no output then the signature verified correctly. But if you get something like “opendkim-testmsg: dkim_eom(): Unable to verify” then double-check your DNS record.

You can also use the service to verify that your signatures are working well.


Adding DKIM signatures is a good first step. But you can take it further by telling receiving mail servers that they should not accept any email from your domain without a valid signature or from servers that you do no operate. There are two concepts that aim to help. The older SPF and the newer DMARC. Either of them means creating a machine-readable string in a predefined format and adding a TXT record to your DNS zone. Receiving mail servers can check those records and take your advice (as the domain owner) what to do if the criteria of the email are not met. It could accept the email anyway or flag it as spam or reject it altogether.

Let’s take a look at a typical SPF record:

"v=spf1 ip4: mx ~all"

What it means:

  1. this is an SPF record of version 1 of the standard (there is currently no other version)
  2. please accept emails from the IP address
  3. alternatively accept emails from any server that is mentioned in our domain’s MX record (the server(s) that receive email for your domain)
  4. any other email should be considered suspicious – it might be spam or worse

There are websites like SPFwizard that help you create your SPF string to add to your DNS domain. Keep in mind though:

  • You should know which mail servers send email from your domain. Do not forget to include mailing list or newsletter services that send in your name.
  • Start with “~all” to mark emails as spam that do not meet the criteria. If all goes well switch to “-all” after a few weeks if you like.
  • Note that forwarding emails from your domain may break SPF because suddenly the email appears to be coming from an IP address that is not authorized. This has been a common problem for mailing lists and is gradually being fixed by resending the email from the domain of the mailing list service.

I mentioned that DMARC is the newer standard. So why use SPF anyway? Because some email providers value your effort if you use SPF, too. Technically it’s sufficient to specify a DMARC entry. In my opinion restricting the IP addresses allowed to send is a little dangerous and a little inflexible. It is fare more interesting to require that emails from your domain have a valid DKIM signature. Such a record may roughly look like:

"v=DMARC1; p=reject; adkim=s"

However to create a proper DMARC entry I suggest you use one of the web sites that aid you there and explain the restrictions and extra features.

9 thoughts on “Prevent spoofing with DKIM

  • 2018-03-02 at 14:41


    Thank you for your tutorial!

    I am going to configure my mail server in the near future according to your tutorial. It is very nice of you to provide such a detailed guide. I have been researching this possiblity for few months and have found a lot of other tutorials but by far yours is the best. I probably will try to replace MariaDB with Postgres though.

    There is one thing you did not mention which was mentioned in some other tutorials: Are there any special requirements about PTR DNS records?

    They say that without proper configuration of PTR record ones mail could be rejected by the other big ISP e-mail providers (e.g. Google, Microsoft, etc). I am interested in this informtion because it is not always possible to change a value of the record easily. Could you clarify this please?

    Thanks again for your tutorial – it is awesome!

  • 2018-03-03 at 17:55

    Perfect 🙂 It took a while until I figured out the right syntax for bind, but now it works perfectly 😉

  • 2018-03-04 at 10:57

    Since rspamd provide dkim generator such as rspamadm dkim_keygen -b 2048 -s 2017 -k /var/lib/rspamd/dkim/2017.key, what is the advantage of using opendkim-genkey?

  • 2018-03-05 at 08:14

    Is there any support for database driven dkim signing? opendkim you could do that.

    • 2018-03-05 at 08:47

      If you consider Redis a database – then yes. rspamd is offloading a lot to Redis for performance reasons. In a larger setup you should consider putting DKIM keys there.

      • 2018-03-07 at 16:46

        Is Redis the only method? As I use postgresql already for database, I would like to also place dkim keys there. Then backups (barman) has everything in one place.

  • 2018-03-08 at 12:36

    Some notes, worth to be added to the main text, I think.

    If your record is not accepted by your DNS hoster, a 2048 bit key might be too long. You can generate a shorter key by specifying -b 1024 instead of -b 2048 and running the rspamadm dkim_keygen command again.
    Most of these fields only allow 255 chars of length, since being database varchar 255 fields.
    opendkim-genkey –bits=1024 -d domain.tld -s 201801

    I also changed /etc/rspamd/local.d/dkim_signing.conf adding the mismatch option:

    path = “/var/lib/rspamd/dkim/$domain.$selector.key”;
    selector_map = “/etc/rspamd/”;

    ### Enable DKIM signing for alias sender addresses
    allow_username_mismatch = true;

    I am not sure whether this and/or the following made dkim verification work.

    But for sure this configuration needs to be copied to /etc/rspamd/local.d/arc.conf, since the ARC module makes use of the same parameters, and to prevent the rspamd.log error “task; lua_dkim_sign_handler: cannot load dkim key /var/lib/rspamd/arc/ cannot stat private key /var/lib/rspamd/arc/ No such file or directory”

    cp -R /etc/rspamd/local.d/dkim_signing.conf /etc/rspamd/local.d/arc.conf

    Which finally did it.

  • 2018-03-08 at 17:41

    Another thing I found and forgot to mention in my previous post, that
    dig +short +trace txt
    did not work at all. I only found this
    dig txt +short
    giving me the right answer.


Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.