How to eliminate spam and protect your name with DMARC

 If you are reading this you are probably making my life harder

E-Mail sucks! Your users just keep clicking the links in those damn phishing mails. And you can’t do anything about it. Hell somebody might be sending spam in your name and you have no idea about it. Let me blow your mind: You can solve these problems, for free. And I will introduce you the tools you need.

The cure to your E-Mail headaches hides behind three small acronyms:

  • SPF: Sender Policy framework, tells others which mail servers are authorized to send E-Mail for your domain.
  • DKIM: Domain Keys Identified Mail, uses encryption and DNS to verify an E-Mail sender and that it was not altered in transit
  • DMARC: Domain based Message Authentication, Reporting and Conformance. Builds on SPF and DKIM and implements a policy and reporting system around them

The goal of this post is to get you to implement all three of these technologies. This is not an entirely selfless proposition since you will not only make your life easier, but every other mail admins life as well. To do this I will tell you which tools you need, how to implement them and how they work. By the end of this tutorial, a lot less Phishing mails will arrive in your users inboxes and you will have a deeper understanding of the involved technologies. I already hear you screaming: Wait these cookie cutter tutorials don’t work for me! My environment is different.

Well guess what? Mine is as well. In this post I will not just give you a bunch of configuration examples and send you on your way. I will give you a small overview of my environment as a reference and then actually explain what you are doing and why. Armed with this knowledge, adapting this tutorial to your own infrastructure will be a  breeze.

If you already have some of the systems in place feel free to jump to the point you are interested in.

  1. Let’s get on the same page –overview of my mail environment
  2. Wait you are not the mailman – Validate E-Mail Senders with SPF
  3. Not everyone should be allowed to speak for you – Create your own SPF Record
  4. Place a bouncer – Validate SPF within Postfixx
  5. But does it actually work? – Confirming your successful SPF implementation
  6. A Virtual signature for your company – Authenticate E-Mails with DKIM
  7. Teach your mail server to sign E-Mail – Implement DKIM with Postfix
  8. Validate your DKIM configuration
  9. How do I know if a message should be signed? – Enhancing DKIM with ADSP
  10. Enforce your rules with DMARC
  11. Publish your rules – Create your DMARC DNS record
  12. Know the law – Integrate opendmarc into Postfix on Debian
  13. You are not perfect – Verify your work
  14. Help out your fellow admins – DMARC reporting
  15. Know your Spam – get the most out of Spamassassin
  16. The finishing Touch – 6 Easy Steps to use Dovecot and Sieve to redirect spam to your users junk folder
  17. Helpful Links and more Information

Let’s get on the same page –overview of my mail environment

I promised you a look at my mail environment. Let me warn you it is “Historically Grown”. I hear you groan, well you are probably right. But If I can get my system to stop spam and phishing, so can you!

Network Diagram

A quick overview of the building blocks used for this:

  • Mail server: Debian 7, Postfix, Dovecot , responsible for one mailbox domain and one virtual alias domain
  • Antivirus and Spam check: Debian 7, Amavis-New, ClamAV, Spamassassin
  • Webmail: Debian 7, Apache, Horde
  • Database: Debian 7 MySQL
  • Domain Controller: Debian 7, Samba 4(Backport).

As promised this is not exactly ideal, I especially lack some redundancy. But I only have a fairly small user base and regular backups. While downtime is not ideal, I can restore a  Backup that is less then 12 hours old in less then an hour so cut me some slack.

The other thing you might notice, is that every service has it’s own (virtual) machine. This is due to two things:

  1. Linux Virtual containers with OpenVZ have close to 0 overhead
  2. When I started out and ran a bunch of services on one box, I quickly learned that the failure of one service can effect others in unexpected ways.

Since I am kind of using two domains, this tutorial will also cover how to implement these technologies for different domains. Even though I have only one real domain and one virtual alias domain, the steps are the same if you have two mailbox domains, or twenty.


Wait you are not the mailman – Validate E-Mail Senders with SPF

The first cornerstone that will help you get rid of phishing is SPF. SPF is an acronym for Sender Policy Framework and is in the simplest terms a way to ensure, that a Server is authorized to send an E-Mail for a domain. You probably wouldn’t open the door to a guy in a ski mask with crowbar in hand only because he tells you has your latest Amazon order. In the online world SPF is a way to recognize the guy with ski mask.

This is achieved with a set of special DNS records for your domain. These records contain information about which servers are allowed to send E-Mail for your domain and how certain you are about that information. This diagram shows you how SPF works:

SPF Diagram

As shown in the diagram your server does not need any special program to get your E-Mail SPF verified. Your Domain simply needs to have a SPF DNS record. On the receiving side you will however need a little more work. You have to implement a program that can check the SPF records of the domain you are receiving E-Mail from. With postfix the 3 easiest options are:

  • Spamassassin: You probably have this running already. But in my opinion you will want to fine tune the score for the SPF tests a little. Spamassassin helps you to mark failed SPF checks as spam, but not in a way that is useful for DMARC verification.
  • postfix-policyd-spf-python: This is the tool I recommend for the job. This allows you to rewrite the header to include the results of the SPF check or even outright reject E-Mail that fails the SPF check.
  • opendmarc: As the name suggest this is mainly a tool to implement DMARC. Opendmarcs capabilities include running SPF checks, but due to its limitations I still recommend using postfix-policyd-spf-python.

All of these tools test the E-Mail and write the results in some way before forwarding the Mail to the next part of the mail processing queue. I actually recommend using all three of those tools, but each one for its primary purpose. But before I get into that, I will show you how to set up your DNS, as this is done quickly and your changes will need a little while to propagate through all of the internet.

Not everyone should be allowed to speak for you – Create your own SPF Record

And setting a SPF record for your domain empowers you to tell the world which mail servers are allowed to send your E-Mails. Setting this record will take you less than five minutes and potentially saves everyone you are corresponding with a lot of headaches. Some of you might say: “Why would I care about that?”
Well that is easy. Because you are responsible! You are the mail admin and if unwanted E-Mail is sent in your name, warning everyone about the imposters is your duty.

Now that you are convinced that you need to set a SPF record, let me get all technical on you. A SPF record is aDNS  TXT record attached to your entire domain (or subdomain). This record starts with the SPF version that you are using. At the time of this writing there is only version one. The start of the record will look like this:


After that you will use so called mechanism to specify the hosts you want allow or disallow from sending E-Mail for your domain. But for those mechanism to be useful, you need to combine them with a qualifier. A mechanism can have one of four qualifies to determine what will happen with messages from the hosts that match the mechanism.These four qualifiers are:

  • Pass: expressed by “+” this qualifier is assumed if no other qualifier is used. The Server(s) listed with a pass qualifier are allowed to send mail for your domain.
  • Fail: expressed by a “-“ The servers listed here are NOT allowed to send mail for your domain. The plus usually also implies that mail should be rejected if SPF fails. I personally am not a friend of dropping or rejecting mail unless I am 110% certain that I do not want it, but unlike many other people I still recommend using the fail qualifier instead of soft fail. You should know which hosts can conceivably send E-Mail for your domain and you should disallow all other hosts from sending messages in your name.
  • Softfail: expressed by “~” basically states that those hosts are probably not allowed to send mail for your domain but you aren’t certain. As a result mail servers should accept but tag mail from those servers. I believe this is ok for testing SPF records while reducing the impact of mistakes.This qualifier can also be used during server transfers. (set the new server to pass and the old server to soft fail for a little while)
  • Neutral: expressed by “?” you are telling everyone, that you have no idea whether these servers are allowed to send mail for your server. Mail from those server will usually be accepted, but unless you are just testing new additions to your SPF record, setting this is not much use for anyone.

SPF offers plenty of mechanics to describe E-Mail Senders in its DNS record. I will only list the more useful ones, since you can cover pretty much any scenario with them:

  • all” you will only want to use this one with a “-“ qualifier in front of it, after you listed your servers. As the name suggests this mechanism encompasses all possible senders.
  • ipv4” You can use this mechanism to allow Ipv4 hosts and networks to send E-Mail for your domain. The network has to be specified in CIDR Notation. An entry could look like this:
  • ipv6” same as above but for ipv6. an entry could look like this:
  • a” you can use this to specify the a record of the allowed mail server(s). This can be combined with CIDR notation to allow the entire subnet to which the returned address belongs. Here are a couple of examples
    # use the a record of the current domain to determine valid sender IPs
    # all IPs in the /24 subnet of this a record are valid senders 
    # the next two work in the same way as above but they use the a record of
    # no matter what domain the SPF record is on
  • mx” this mechanism works like the “a” mechanism, except that it uses all mx records of the current or specified domain as valid senders. Adding a subnet is possible as well.
  • redirect” refers to the SPF record of another domain. If you include a domain, all hosts considered valid senders for that domain, will also be valid senders for your domain. All changes to their SPF will also affect your domain. You might need this if you are using outbound gateways or hosted E-Mail. Another use for this would be if you are using multiple Mail domains on your own server(s). With this mechanism in place you would only have to keep one SPF record updated. A redirect is specified like this:

Now that you know all the pieces, let me show you a few examples of complete SPF records:

  1. The public record for my main domain allows only one host, defined by an a record, to send mail. All other hosts are explicitly forbidden to send mail for my domain:
    v=spf1 –all
  2. This is actually the record I use in my local network. This record allows two different subnets to send E-Mail in my name:
    v=spf1 ip4: –all
  3. And on top of that I have a virtual alias domain that is hosted on my mail server. I set it up to redirect to my main domain:

This information should cover most use cases. Look up the record syntax page on if you need even more information.

Place a bouncer – Validate SPF within Postfix

Now that everyone knows your mail servers are the real deal, you might want to check the mail servers of all your incoming E-Mail. The tool of choice for this task is “postfix-policyd-spf-python”. You can use this policy service to just rewrite the headers of your incoming mail (useful for further processing) or you can outright reject E-Mails that fail the SPF checks.

Let’s start:

  1. The first step is obviously the installation of the policy service. This is a simple one liner in Debian Wheezy
    apt-get install postfix-policyd-spf-python
  2. Next you will have to edit the configuration file with the editor of your choice:
    nano /etc/postfix-policyd-spf-python/policyd-spf.conf
  3. As I explained earlier, I only want to add the SPF check headers to my E-Mails and I do not want to reject any messages for SPF failures. To achieve this I had to change following two lines of my default configuration:
    HELO_reject = False
    Mail_From_reject = False

    I personally choose not to reject any mail based on the SPF results, because I have a tiny user base and receive only relatively few phishing mails. If you receive lots of phishing and your users are likely to click all the nice links they get, you should consider rejecting mail that fails SPF checks. But this might lead to missing a few E-Mails if a legit sender did not configure his SPF properly.

  4. Next you need to add the policy service to your Postfix configuration. This requires two changes to your Postfix file. Open this file with you editor:
    nano /etc/postfix/
  5. You need to add the following option to your
    policy-spf_time_limit = 3600s

    This setting prevents your policy server from timing out during while E-Mail is being processed. In addition you will have to change your smtpd_recipient_restrictions setting. To be safe, you should add the check policy service at the end of your current recipient restrictions.

    smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination,check_policy_service unix:private/policy-spf

    Whatever you do, make sure that check policy comes after reject_unauth_destination! Otherwise your server will be an open relay to anyone with a correct SPF record. You will probably also want the policy check after you list your allowed senders, this way you wont check the SPF records of your outgoing mail.

  6. After those two changes you need to edit the configuration file of your Postfix installation:
    nano /etc/postfix/

    Once you have the file open add the following to the end:

    policy-spf  unix  -       n       n       -       -       spawn
         user=nobody argv=/usr/bin/policyd-spf

    This actually adds the policy-spf service that you configured earlier in the recipient restrictions.

  7. Now all you need to do is restart Postfix, so that your configuration changes can take effect:
    /etc/init.d/postfix restart

But does it actually work? – Confirming your successful SPF implementation

After this SPF should work for your domain. You can check this by testing with an external mail account. Gmail is a good candidate for this since they also have DKIM and DMARC set up correctly.
First send an E-Mail from your own domain to an external mailbox you control and check the message source. The header should contain a field named “Received-SPF” and this should contain pass. If this field has a fail or softfail, you need to look at your SPF Record again. If this header field is not present at all, the DNS record may not have propagated everywhere yet. In this case you have to wait a little. Your provider may not add SPF results to the header of incoming mails. In that case use another mail provider for this.

In order to check if SPF is validated for your incoming mail, simply check the headers of a few E-Mails that have arrived since the change. The received SPF field has to be present in the header now.

A Virtual signature for your company – Authenticate E-Mails with DKIM

In essence DKIM is a way to sign E-Mails coming from your domain and telling the world how your signature should look like. It achieves that outcome by encrypting the message body and certain header fields with asymmetric encryption and then hashing the result. The process is slightly more complicated than SPF. But don’t worry, just have a look at the diagram to get an overview of the entire DKIM process.:DKIM  Diagram

  1. The Outgoing Email’s body and a few of the header fields are hashed and then encrypted with your domains private key . The content of this field could look like this:
     v=1; a=rsa-sha256; c=relaxed/simple;;
        s=mail; t=1425776684;

    The content of the header has the following elements:

    • v: DKIM version
    • a: the used encryption and hash algorithms
    • c: the canonicalization algorithms that should be used for header  and body
    • d: domain name that issued this signature.
    • s: selector that is necessary to find the public key. In this case you would have to query in order to get the public key.
    • t: Time the signature was created on
    • bh: encrypted hash of the message body
    • h: a list of signed header fields. Even if it is not listed here, the DKIM header field is always included in this list
    • b: encrypted signature data
  2. The receiving mail server gets the mail and retrieves both the public key and the hash algorithm via DNS.
  3. Then the server takes all the information that was signed and hashes them in the same way the sender did
  4. These hashes are compared to the result of decrypting the signatures.
  5. If the two match DKIM will pass, else it will fail. The result of this DKIM test is added to the header of the E-Mail in a new “Authentication-Results” field.
  6. Processing of the E-Mail continues

All of this sounds complicated, but all the work will be done automatically by your mail server(s) once you finished the setup. You also don’t need to run you own CA for this, nor do you have to buy a certificate from one of the major Certificate Authorities. As with SPF, Spamassassin does DKIM checks by default. But they are again useless for DMARC tests. And since opendmarc is not capable of checking the DKIM validity of a message, you have to use opendkim if you want to be able to get your mail server DMARC compliant.

Teach your mail server to sign E-Mail – Implement DKIM with Postfix

Now that you are properly hyped about DKIM, I have even more good news: all the tools you need are available in the official Debian Wheezy repository. Those tools are called opendkim and opendkim-tools and I will walk you through their installation and configuration in easy to follow steps.

  1. Installing opendkim is a one liner on the shell:
    apt-get install opendkim opendkim-tools
  2. Next you will need to edit the default configuration of opendkim:
    nano /etc/opendkim.conf
  3. I will not list the entire configuration file here, but rather only the lines you need to edit. With this configuration opendkim is capable of signing E-Mails for multiple domains. But it also works for single domain scenarios:
    KeyTable           /etc/opendkim/key_table
    SigningTable       /etc/opendkim/signing_table
    ExternalIgnoreList /etc/opendkim/trusted_hosts
    InternalHosts      /etc/opendkim/trusted_hosts
    AutoRestart             Yes
    AutoRestartRate         10/1h
    Mode                    sv
    PidFile                 /var/run/opendkim/
    SignatureAlgorithm      rsa-sha256
    Canonicalization        relaxed/simple
    UserID                  opendkim:opendkim
  4. Next you need to create the directories that will be used for the keys and the additional configuration files:
    mkdir /etc/opendkim
    mkdir /etc/opendkim/
    mkdir /etc/opendkim/ 
  5. Now you need to create the list of trusted hosts:
    nano /etc/opendkim/trusted_hosts

    The contents of this file could look like this:

    #local host
    # local subnets that are trusted and do not need to be verified
  6. Now is the time to create the signing keys for your domain. The ‘–s’ option in opendkim-genkey specifies the selector or name of the key. I suggest using mail here, as that is the keys purpose. This step has to be repeated for every domain that you wish to sign E-Mail for
    cd /etc/opendkim/
    opendkim-genkey -s mail -d
    chown opendkim:opendkim mail.private
  7. Now that you have the key pairs, you can create the key table file. This file tells opendkim, about all the domains you want to sign and where to find their keys:
    nano /etc/opendkim/key_table

    An entry in this file looks like this:

    Every entry consists of four parts:

    1. is the KeyID. The KeyID is build as follows: selector._domainkey.domain.tld
    2. the domain this entry is for
    3. mail: is the selector
    4. /etc/opendkim/ the path to the private key


  8. Next you have to create the signing table:
    nano /etc/opendkim/signing_table

    In this file you will have to make one entry per domain you want to sign. Every entry consists of the domain and the corresponding KeyID:


  9. With this done, you have to ensure, that opendkim starts at boot time and has a socket. Simply edit the default file for opendkim to get this done:
    nano /etc/default/opendkim

    In Debian you simply need to uncomment following line:


    Using local Unix sockets requires slightly more work since the socket has to be readable by Postfix(Which starts in a chroot).

  10. Now you have to change the configuration of postfix.
    nano /etc/postfix/

    Depending on your current setup you may already have milters running. In this case you will only need to add the opendkim milter to the list of active milters. Otherwise add this to the end of the file:

    milter_protocol = 6
    milter_default_action = accept
    smtpd_milters = inet:localhost:12345 
    non_smtpd_milters = inet:localhost:12345


  11. If you are running a filtering solution like amavis-new, you should consider excluding the milter from running when it returns your E-mails to postfix. This will not change the success or validity of your DKIM tests, but it will reduce system load:
    nano /etc/postfix/

    Look for the configuration options of the port on which the mails are returned by amavis-new. This port will probably have a few options listed with the –o tag. Add a new line at the end of the existing options and make it look like this: inet    n       -       -       -       -       smtpd
    -o smtpd_milters=


  12. Before you can take your configuration live, you need to update the DNS records for your domain. This is simple since the DNS record you need has already been created for you during key creation. You simply need to copy the contents of following file:

    And add them as txt record for the host “” in your DNS.

  13. Once you are reasonably certain that your new record has propagated, you should restart opendkim and Postfix, so that your changes take effect:
    /etc/init.d/opendkim restart
    /etc/init.d/postfix restart

Validate your DKIM configuration

The validation is similar to what you did for SPF, E-mail an external web mail account that you have access to and then write an E-Mail back. You will need a mailbox at an provider that implements DKIM like Google Gmail.

Once you have done that look into the headers of both e-mails.You should be able to find a header field called “Authentication-Results”. In this field you will find a string containing “dkim=pass”. If not, check you configuration again.

In the setup that I am describing you will ultimately find two authentication results headers field. One for DKIM and one for DMARC. Other implementations like the one G-Mail uses create only one field for all tests.

How do I know if a message should be signed? – Enhancing DKIM with ADSP

While DKIM is a way to determine if a signed E-Mail is valid. It does not contain any way to deal with E-Mails that are not DKIM signed. You can solve this problem by implementing a DKIM extension called ADSP or Author Domain Signing Practices. ADSP is a DNS record that tells other mail servers what to do with unsigned E-Mail. You can also achieve this with DMARC, but I urge you to implement both. Simply because DMARC is not anywhere near as widely implemented as DKIM. Since opendkim does already check ADSP on the receiving side, you only have to set a DNS record to implement it fully.

You need to create a txt record for following host:

This txt record contains only one tag:


The three possible values here are:

  • unknown: both signed and unsigned mail is valid. If this is the case for your domain, you do not need to publish an adsp record as this is the default behavior in DKIM anyway
  • all: All mail from this domain is supposed to be signed. If you can manage to get all your mail signed this is a good setting. But check on your newsletters and such before setting this.
  • discardable: all mail from this domain is supposed to be signed and any mail that is not should be discarded. This is usually not needed. But if your organization deals with financial data or any other sensitive information do everyone a favor and set this.


Enforce your rules with DMARC

The last technology I would like to introduce you to is DMARC: Domain based Message Authentication, Reporting and Conformance. DMARC builds on both of the things that you just implemented in your mail environment. In the simplest terms DMARC allows you to create policies as to what should happen to an E-Mail if either one or both of the other checks fail. And it provides a standard for reporting that allows postmasters to be aware of phishing or spam in their name.

As the previous two technologies DMARC relies on a specially formatted DNS TXT record. I will help you to create this record for your domain and after that I will show you how to implement opendmarc in a step by step guide. This will allow you to check incoming mail for DMARC compliance and to report aggregated statistics back to domains who want them. Here is a rough overview of how the everything will work once you implemented DMARC:

DMARC  Diagram

Publish your rules – Create your DMARC DNS record

The DMARC record is a txt record for the host “”. Your DMARC record tells the outside world how they should handle E-mail that is coming from your domain. In addition it tells everyone how you would like to receive DMARC reports for your domain.
With that being said, DMARC does not require other mail servers to follow all the reporting guidelines you set. In addition none of these settings have any effect on how your mail server processes incoming messages. Creating a valid DMARC record is somewhat complex, due to all the information that supposed to go into it. I will use my own DMARC record as an example to explain all the pieces of a valid DMARC record. If you don’t care for this much in depth knowledge, you can use this DMARC record generator.

v=DMARC1; p=quarantine; rua=mailto:dmarc [at] skelleton [dot] net; ruf=mailto:dmarc [at] skelleton [dot] net; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400

As you can see the record contains several tags that are separated by semicolons. I will explain those in the order they appear in:

  1. v: DMARC version. This is a required field. For now it will have the value of ‘DMARC1’
  2. p: DMARC policy. This is an required field as well, you can set it to one of three values:
  • none:  Means don’t do anything if the DMARC verification fails. This is a good setting while you are still testing your DMARC implementation as it will not disrupt your outgoing mail if you made a mistake in your configuration.
  • quarantine: Mail that fails DMARC checks should be treated as suspicious. This is good middle ground setting if you want to ensure that none of your mail gets lost.
  • reject: Mail should be rejected If the DMARC verification fails. This is a good setting if your domain is used for phishing or if trust in your E-Mails is more important than occasional lost messages.
  • rua: This is an optional parameter and contains the address to which the DMARC aggregate report should be submitted. You can specify web addresses here, but I have not done so so far. If you do not set this option at all, you will not receive DMARC reports.
  • ruf: This is similar to the rua field, but these are the addresses for DMARC forensic reports. These reports contain detailed information about failed DMARC verifications of E-mail claiming to be from your domain.
  • fo: These are reporting options for the failure reports. This can have four possible values:
    • 0: generate a report if both SPF and DKIM tests failed
    • 1: generate a report if either the SPF or the DKIM test failed
    • s: generate a report if the SPF test failed
    • d: generate a report if the DKIM test failed
  • adkim: This option is optional and controls how strict the result of the DKIM verification should be interpreted. It defaults to relaxed if it is not present. Possible values are:
    • s: strict
    • r: relaxed
  • aspf: This option is optional and controls how strict the result of the SPF check should be interpreted. It defaults to relaxed if no value is set. Possible values are
    • s: strict
    • r: relaxed
  • pct: This is also optional and determines how many percent of the messages from your domain should have the DMARC verification done by other mail providers. The possible values here are integers between 0 and 100. It defaults to 100 if it is not set.
  • rf: This optional field lets you specify your preferred reporting format for forensic reports. It defaults to “afrf”. These are the possible values:
    1. afrf: Authentication Failure Reporting Format
    2. aodef: Accident Object Description Exchange Format
  • ri: This optional field is the interval at which you want to receive DMARC reports in seconds. It defaults  86400 seconds (one day). According to the DMARC specification every participating organization should be able to send reports at least once every day. Intervals as small as one hour are within the specification. But those smaller intervals are generally served on a best effort basis.
  • sp: The last field is also optional (and not present in my example). It is the subdomain policy. If you do not set this, the policy you set in the beginning will apply to your subdomains. If you set this you can use the same values as in the policy field. This can be useful if you know, that you never send mails from one of your subdomains. In that case you can set the subdomain policy to reject without any risk of your legitimate E-Mails being discarded.

Know the law – Integrate opendmarc into Postfix on Debian

Now that your DMARC record is set, you need to integrate some kind of DMARC verification into your Postfix server. I choose the milter opendmarc for this task. Unfortunately it is not in the Debian Wheezy default repository. But you can use Wheezy-Backports to get an installation package. Which makes it easy to upgrade later on. I will walk you through the installation step by step:

  1. Since opendmarc is currently only available as a backport, you will have to add the Debian backports repository. To do that you need to edit your sources.list file:
    nano /etc/apt/sources.list

    Add following line to the file:

    deb wheezy-backports main contrib


  2. Now you need to update the list of available packages and then you can install opendmarc using the backports repository:
    aptitude update
    aptitude -t wheezy-backports install opendmarc
  3. Once the installation is done, you need to edit a few things in the opendmarc configuration file:
    nano /etc/opendmarc.conf

    You need to add, change or uncomment the following lines in this file:

    PidFile /var/run/ #Debian default
    RejectFailures false
    Syslog true
    UMask 0002
    UserID opendmarc:opendmarc
    IgnoreHosts /etc/opendmarc/ignore.hosts
    HistoryFile /var/run/opendmarc/opendmarc.dat
    #for testing:
    SoftwareHeader true

    Let me explain those options to you, so that you can change this to fit your needs:

    • AuthservID: Sets what is used as AuthservID when processing E-Mails. This should be the hostname of your mail server or another unique string
    • PidFile: Path to the PID file
    • RejectFailures: This is a Boolean, if this is true E-Mails that fail DMARC verification will be rejected by your mail server. I prefer simply tagging the mail so I set this to false.
    • Syslog: true or false. Tells opendmarc, whether it should log to syslog or not
    • TrustedAuthservIDs: these AuthservIDs are assumed to be valid inputs for DMARC assessment. This can prevent the DMARC tests from running several times if you have multiple mail servers in your organization
    • UMask: the PID file and the socket file are created with this umask
    • UserID: the user and group running the opendmarc service separated by a colon.
    • IgnoreHosts: The path to the Ignored Hosts list
    • HistoryFile: The path under which the History file should be created. This file is necessary if you want to be able to create aggregate reports to send out to other organizations
    • SoftwareHeader: adds a “Dmarc-Filter” header with the opendmarc version in every processed mail. This is good to have during testing
    • ForensicReporting options seem to be broken in the version of opendmarc that I used. When I tried to uncomment them, opendmarc would not start because of unrecognized parameters.


  4. Now you need to create the Ignore hosts file that you specified in the configuration:
    mkdir /etc/opendmarc/
    nano /etc/opendmarc/ignore.hosts

    This file should contain a list of networks and hosts that you trust. Their mail will not be checked by opendmarc. Put one host or subnet per line into the file:



  5. With this part of the configuration complete, you just need to make a little change to the default file:
    nano /etc/default/opendmarc

    Put following line in the file:



  6. Now you can start opendmarc
    /etc/init.d/opendmarc start
  7. Next you have to add opendmarc to the milers in postfix. To do that edit your configuration file:
    nano /etc/postfix/

    Find the milters you set up earlier for DKIM and add the opendmarc milter after DKIM(the order is important, as opendmarc needs the DKIM check results):



  8. Finally you need to reload your postfix configuration:

Now you have a mail server that performs DMARC verification on incoming mail.

You are not perfect – Verify your work

The easiest way to verify your setup is the same as before. Use an external mailbox write an E-Mail to it and then write one to you from that mailbox. Check the headers for the Authentication-Results field, that contains dmarc=pass. Remember that you should find two “Authentication-Results” headers if you followed this guide.

If you are in Germany and you are using Gmail for your verification, make sure that you write from a address. Google’s German mail domain does not seem to have DMARC set up. This took me an embarrassing amount of time to figure out since everyone said that Google has DMARC implemented.

Help out your fellow admins – DMARC reporting

While your server now performs DMARC checks and has a DMARC record set, it is not DMARC compliant yet. For that you are missing reporting capabilities. I will show you how to send aggregate DMARC reports to get you compliant with the reporting part of DMARC. Trying to send forensic reports is a bad idea for two reasons:

  1. There are privacy concerns with forensic reporting if your users subscribe to mailing lists. You can read a little more on this here.
  2. As mentioned above the configuration options for forensic reporting were not recognized in the version of opendmarc that I used. So this would require spending on a lot of time on something that is potentially a huge risk to the privacy of your users.

You will need access to a working MySQL database to follow my step by step instructions. Setting up a MySQL server is not in the scope of this tutorial:

  1. Copy the DMARC database schema SQL script to your database server(if that is not the same as your mail server). You can find the SQL script under following path:
  2. Edit the script to fit your needs. The default is mostly fine. But if you do not wish to create your database users by hand, you should uncomment and edit these two lines in the script (to uncomment them remove the leading –):
    -- CREATE USER 'opendmarc'@'localhost' IDENTIFIED BY 'changeme';
    -- GRANT ALL ON opendmarc.* to 'opendmarc'@'localhost';

    With this part you create a database user and allow him access to the database. The following parts of this statement may need to be modified:

    • ‘opendmarc’@: This is the username, you can choose whatever you like as username. But make sure that you put it into quotes. (The @ is not part of the username, but simply here to differentiate the username from the database name)
    • localhost’: this the host from which the database user is allowed to connect. If the MySQL database runs on your mail server, you can leave localhost here. If your MySQL database runs on a different server, put the IP of your mail server here.
    • changeme’: This is the user password. I suggest a nice strong randomly generated password here.
    • opendmarc.*: This is the database name. if you did not change anything in the upper part of the script, leave this alone.
  3. connect to the database with an account with sufficient privileges to create a new database and run the script. One of the simpler ways of doing this would be the following commands on the database server:
    cd /path/to/schema.mysql/
    mysql -u root -p < schema.sql

    You can replace the root user with any user, that has the right to create new databases and users. The command will prompt you for the user password and then execute the script.

  4. Once the database exists go back to your mail server and create a new script to read the history file into the database and send out the reports.

    I used following script for this:

    REPORT_EMAIL='dmarc [at] example [dot] com''
    mv ${WORK_DIR}/opendmarc.dat ${WORK_DIR}/opendmarc_import.dat -f
    cat /dev/null > ${WORK_DIR}/opendmarc.dat
    /usr/sbin/opendmarc-import --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose < ${WORK_DIR}/opendmarc_import.dat
    /usr/sbin/opendmarc-reports --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose --interval=86400 --report-email $REPORT_EMAIL --report-org $REPORT_ORG
    /usr/sbin/opendmarc-expire --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose

    You will need to fill in the database details of your previously created database here.

    Make the script executable:

    chmod +x /etc/opendmarc/report_script

    And run it under the opendmarc user as a test:

    su -c "/etc/opendmarc/report-script" -s /bin/bash opendmarc


  5. When the script worked as expected you can add it to your cron jobs:
    nano /etc/crontab

    Add following line:

    1 0 * * * opendmarc /etc/opendmarc/report-script

    This example will execute the script every day at 00:01 under the user opendmarc.

  6. During testing you probably want to receive a copy of every outgoing DMARC report. You can simply add one of your Mailboxes as bcc for every Message sent by the DMARC address. Add following line to your postfix
    sender_bcc_maps = hash:/etc/postfix/bcc_map

    Create the file bcc_map with following content:

    dmarc [at] example [dot] com mailboxforbcc [at] example [dot] com

    Create the mapping database:

    postmap /etc/postfix/bcc_map

    Restart postfix:

    /etc/init.d/postfix restart

Now you are done with the DMARC implementation and people can be certain, that E-Mail coming from your domain is legit. The last goal left is: redirecting spam to your users junk folders.

Know your Spam – get the most out of Spamassassin

You have to recognize spam messages before you can redirect them to the Junk folders. My tool of choice for this task is amavis-new with Spamassassin. You might have noticed earlier, that I do not actually reject any incoming E-Mails, because I do not want to accidentally loose any important messages. Detecting malicious E-Mails is entirely up to amavis-new in my setup, as you can see in this diagram :

spamassasin diagram

I will assume, that you already have Spamassassin running in some form. I am going to just suggest a few score tweaks and show you the basics of writing your own rules. This will enable you to use the changes you made earlier to filter spam more effectively. Both the score changes and the custom rules should be added to the end of the Spamassassin configuration file.

nano /etc/spamassassin/

The easiest part of this is changing the score of existing rules. The syntax for this looks like this:

score RULE_NAME 1.0
  • score: tells the configuration that you wish to change the score of a rule
  • RULE_NAME: is the name of the Spamassassin rule, this can be any default or custom rule
  • 1.0: This is the score that any messages matching the rule get. A positive score, means that it is more likely to be spam. A negative Score means that the message is more likely to be ham. And a score of 0 means, that the test will not be run.

I made the following score changes to the default SPF and DKIM rules, as I found the scores of them to be not harsh enough:

#Adjust scores for SPF FAIL
score SPF_FAIL 4.0
score SPF_HELO_FAIL 4.0
score SPF_SOFTFAIL 3.0

#adjust DKIM scores
score DKIM_ADSP_ALL 3.0

Avoid using huge negative scores for a SPF or DKIM pass. Even spammers sometimes use those two these days. Scarcely any legitimate mail servers have implemented them incorrectly though, so you can usually give harsher penalties to a fail of those tests.

I found however that not all tests that I whished to do were included Spamassassin. So I had to write my own rules. This is actually fairly simple. The most important thing to remember is, that you always have to escape special characters in your regex. If you don’t you might end up searching for mistakes in the wrong place for hours. Trust me….

Here are a few rule examples that I have written for further DKIM and DMARC checks:

#dmarc fail
header CUST_DMARC_FAIL Authentication-Results =~ /mail\.example\.com; dmarc=fail/

#dmarc pass
header CUST_DMARC_PASS Authentication-Results =~ /mail\.example\.com; dmarc=pass/
score CUST_DMARC_PASS -1.0


The rule syntax has the following elements:

  • Type of the rule: header, meta, body, rawbody: The type of the rule describes what part of the E-Mail the rule matches. Meta rules are special. They let you link multiple rules with logical ‘and’ or ‘or’ operators and will be true if your entire construct returns a true. In order to deal with SPF, DKIM and DMARC, you will only need header and meta rules.
  • Rule name: CUST_DMARC_FAIL: You need to specify a name for the rule. I recommend using a fixed prefix for your custom rules. This will make debugging the rules easier if something does not do what you want it to. You should watch out for this special naming convention: If you prefix your rule with ‘__’ it will only be evaluated as part of a meta rule.
  • The rule itself is different for header rules and meta rules.
    • Header rules: usually consists of two parts. The header field that should be searched and a regex that matches the desired content in the header field. For example: ‘Authentication-Results =~ /mail\.example\.com; dmarc=fail/ ’ Matches the header field ‘Authentication-Results’ if it contains the string ‘; dmarc=fail’. The header field that you match is separated from the regex by ‘=~’. The regex that describes your search text is started and ended by ‘/’. All special characters like a dot or an ‘@’ have to be escaped with a ‘\’.
    • Meta rules: consist of rules linked by logical operators. For example: ‘DKIM_SIGNED && !(DKIM_VALID || DKIM_VALID_AU) ’,  The Operator ‘&&’ signals a logical ‘and’ and the operator ‘||’ signals a logical ‘or’. Expressions in parentheses will be evaluated first.

Once you tuned all the rules above, your spam will be tagged reliably.

The finishing Touch – 6 Easy Steps to use Dovecot and Sieve to redirect spam to your users junk folder

As I said a bunch of times already I don’t like loosing E-Mail. But I don’t want anybody to be annoyed by inboxes full of spam. While many E-Mail programs are able to to automatically move tagged spam, many smartphone clients do not have that ability. That is why I use Sieve server side filters as a plugin for Dovecot. Sieve allows you to write simple scripts, that change the local mail delivery. In this last part of the I will walk you through installing Sieve and setting up a global script that always gets executed first.

Sieve allows you to do many other cool things including letting your users write their own scripts through a mail client, but that is out of scope for this article.

  1. Sieve is packaged in Debian, so it is fairly easy to install:
    apt-get install dovecot-sieve
  2. Once the installation is done you need to activate sieve in your configuration:
    nano /etc/dovecot/conf.d/15-lda.conf

    Change this section to include sieve:

    protocol lda {
      # Space separated list of plugins to load (default is global mail_plugins).
      mail_plugins = $mail_plugins sieve


  3. Next you need to change the default sieve configuration of dovecot to run always run a script before running user scripts:
    nano /etc/dovecot/conf.d/90-sieve.conf

    Here you need to find the plugin section and uncomment or add the following option:

    plugin {
    sieve_before = /var/mail/SpamToJunk.sieve

    If you happen to have an old monolithic dovecot.conf that does not include the /etc/dovecot/conf.d directory, you have to make all these changes in /etc/dovecot/dovecot.conf.


  4. Now that you configured dovecot to use the Script, you still have to create it. But that is fairly easy:
    nano /var/mail/SpamToJunk.sieve

    Use this script to move all spam into the Junk folder of the corresponding Mailbox:

    require "fileinto";
    if header :comparator "i;ascii-casemap" :contains "X-Spam-Flag" "YES"  {
        fileinto "Junk";


  5. Since this script will be run on every incoming E-Mail, it is a good idea to compile it:
    sievec /var/mail/SpamToJunk.sieve
  6. Restart dovecot to enable your new configuration:
    /etc/init.d/dovecot restart

Now you have a mail server that is much better at detecting spam and phishing attempts and whose E-Mails can be authenticated by other mail servers.

Feel free to leave a comment or shoot me an E-Mail if you have any questions!


Helpful Links and more Information




37 thoughts on “How to eliminate spam and protect your name with DMARC”

  1. Awesome article, its helped me get my email server set up with spf/dkim/dmarc. My only question is regarding the Authentication-Results header. I have three of these headers now in all my incoming emails and I was wondering if it was possible to combine these into a single header? Google currently does this with their emails and it looks so much nicer to have it all in one header. How would I go about getting it like this?:

    spf=pass ( domain of security [at] domain [dot] com designates as permitted sender) smtp.mail=security [at] domain [dot] com;
    dmarc=pass (p=QUARANTINE dis=NONE)

    1. With the tools used here, it would be fairly hard. You would have to write a script modifies the header fields according to your wishes after the DMARC check ran.
      You might be able to accomplish that by using postfix header checks as shown is these two tutorials:

      The other option is probably finding a dmarc milter that does all the checks by itself, but so far i have not seen one.

  2. I got a comment from Richard via E-Mail:

    Great guide to get started with SPF/DKIM/DMARC, thanks!

    It is however missing the AddAllSignatureResults setting for opendkim.conf. Without it only the first Authentication-Result header is parsed, and DMARC might not see the DKIM results. This is the default in opendkim 2.10.0, but jessie has 2.9.2 for now.

    Something that should probably be added is that Postfix skips the first header when passing mail to milters. So the Received-SPF header will get lost. See this discussion for a solution:

    Another thing worth mentioning is the IgnoreAuthenticatedClients setting in opendmarc.conf to prevent flagging SMTP clients as failing DMARC. You do need opendmarc 1.3.1 (from stretch) though because of
    He later added this as well:

    One more addition the my initial comment:

    Amavis deletes the Authentication-Results headers if $myauthservid is the same as AuthservID in opendmarc.conf. They both default to the local hostname by default. To use both together set

    $myauthservid = “amavis.local”;

    in /etc/amavis/conf.d/50-user, see

    That is some great advise you should look out for. I will definitely look into it once I finish my current project.

    1. The last comment (the one Richard sent you via email) fixed all my problems using Debian Wheezy + Postfix + policyd-spf + OpenDKIM + OpenDMARC.
      Please integrate it into the guide ASAP 🙂

        1. I set IN TXT “dkim=discardable” but if someone sends a mail without signature it doesn’t trigger the spamassassin rule:

          Content analysis details: (8.3 points, 5.0 required)

          pts rule name description
          —- ———————- ————————————————–
          5.0 CUST_DMARC_FAIL No description available.
          4.0 SPF_FAIL SPF: sender does not match SPF record (fail)
          0.0 HTML_MESSAGE BODY: HTML included in message
          -0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3)
          [ listed in]
          -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at, low
          [ listed in]
          -0.0 RCVD_IN_MSPIKE_WL Mailspike good senders


  3. I noticed some typos in your openmdmarc script. password and are missing quotation marks.

    REPORT_EMAIL=’dmarc [at] example [dot] com’’

    Thanks for this awesome writeup. Very helpful and informative.

  4. make my this comment:

    Chris Aguilar
    March 18, 2016 at 09:36


    Thanks for this wonderful article! It help me to setup my mail server.

  5. This article is awesome but I’m having issues checking DKIM signatures on inbound mail. When mail arrives it gives this error:

    opendkim[24893]: 088603FF24: key retrieval failed (s=4d515a, ‘’ query timed out

    I get the same error no mater what domain.

    I can do a regular lookup and have no problems getting the key:

    dig TXT
    ;; Truncated, retrying in TCP mode.

    ; <> DiG 9.8.4-rpz2+rl005.12-P1 <> TXT
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17466
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 4

    ; IN TXT

    ;; ANSWER SECTION: 172 IN TXT "v=DKIM1\; k=rsa\; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsAoj+Fk9NuQcNs0+VTVwJlDNEh/" "PXn7QHc4Fad12kEfWdOoHmWV5WyKQ8xXl7ki6tlV3DFwWOQza1VmzsgJIGYVX9SPsas5YwHKHigfpVXdrIHPJJDC+Lt3FSplMOSfiRqp7L4i8NWbwM9irymIILX6aAxMRf8dpolWrDNjcdr" "+6CW4HbRDlzHjsrf8TPlX87K1N1W53/vgKCVlMhwbIJuQ9yzxGEZOQnEbnclisrfs1L4pxabHr9Ai41mxlBA5vk+cqpVJd2ch5/X5LGeme2sXquTqtyVck" "+r6PfoD3MAmTsK6AiYGeHgNPmpC0DaeVXOMwRlzETOpT1hXet7OLwwIDAQAB"

    ;; AUTHORITY SECTION: 94211 IN NS 94211 IN NS 94211 IN NS 94211 IN NS

    ;; ADDITIONAL SECTION: 9451 IN A 9451 IN A 9451 IN A 9451 IN A

    ;; Query time: 5 msec
    ;; SERVER:
    ;; WHEN: Thu Jul 28 14:34:43 2016
    ;; MSG SIZE rcvd: 629

    I don't know what else to check. The outbound mail signature works great. Any ideas?

    1. I have not had this issue so far. But If you are using apparmor or selinux, the context for the service may not have the rights to run the DNS query.

      If that is not the case, it may help to run opendkim in the foreground for debugging the issue.

      1. The issue is that OpenDKIM uses it’s own libraries for resolving. The problem occurs when OpenDKIM is behind a firewall and outgoing DNS queries are blocked. Either open port 53/udp and 53/tcp towards the internet or add this line to your opendkim,conf if you have no possibilty to have access to external DNS servers:

        Nameservers a.b.c.d[,e.f.g.h]

        And choose the nameserver(s) that you will find in /etc/resolv.conf. The idea behind this all is security.

        Very helpful howto BTW!


  6. I used this very nice howto some time ago and I think it truly deserves a BIG THANK for putting everything together so nicely. The information on that website made my whole DKIM / SPF / OPENDMARC configuration possible within hours instead of spending days on it.
    Again, good job!

  7. Just one important thing:
    You need to add the “PublicSuffixList” option to your opendmarc.conf file, as DMARC needs to know what the so-called organizational domain is (that is: belongs to the organization that owns, but is a domain of its own and not a subdomain of the organization owning
    Get the public suffix list from here: and update it regularly.

  8. You don’t mention in your article if you’re using the SpamAssassin DKIM module. I’m assuming you are because you assign scores to the DKIM_ADSP_* rules. Isn’t this redundant? Why use OpenDKIM at all if you are using the SpamAssassin DKIM plugin? This just results in two Authentication-Results headers with potentially different results. Am I missing something?

  9. Great article. Had SPF already but added DKIM and DMARC over the last two days.

    Strange thing: sent a test mail from and have only one Authentication-Results header showing a dkim pass. There is a DMARC-Filter: OpenDMARC Filter v1.2.0 header, the dat file gets updated and there’s an “opendmarc[29382]: 3D535200BFA: pass” in the mail.log. But no header in the email that spamassassin can apply rules against.

    Anything I should be checking?

  10. Very good tutorial, worked like a charm almost immediately. Also used pages for SPF and DMARC and very useful indeed. I am using Centos 7 so some small changes had to be made but it was very obvious what I should do.Thanks for sharing! Helpful!

  11. I would suggest using unix sockets is more appropriate for a single-machine installation. I don’t want to be critical of this howto, because it’s fantastic and still relevant today, but teaching people how to go through the slightly added difficulty of doing this with unix sockets is, I think, a better approach.

    Why unix sockets? Less overhead (you’re not dumping data through the TCP/IP stack), better security, and better readability in your configuration files. There is no doubt which milter is which when you use unix sockets.

    How to do it?
    1) Create and chown the socket directories (this assumes opendkim and opendmarc are already installed):
    sudo mkdir /var/spool/postfix/opendkim
    sudo chown opendkim /var/spool/postfix/opendkim
    sudo mkdir /var/spool/postfix/opendmarc
    sudo chown opendmarc /var/spool/postfix/opendmarc

    2) Set opendkim and opendmarc to use unix sockets. This is in the default files mentioned above (/etc/default/opendkim and /etc/default/opendmarc)

    3) Give postfix access to the opendkim and opendmarc group so it can access the sockets:
    sudo usermod postfix -a -G opendkim
    sudo usermod postfix -a -G opendmarc

    4) Change the milter settings for postfix in /etc/postfix/main.cfg

    See how that improves the readability? Plus, two fewer ports open is two fewer ports you have to check when, say, you look at a list of open ports on your server and are tracking down why on earth those ports are open. In short, there are really good reasons why unix sockets are generally used for inter-process communication.

  12. Great post!

    About the report_script: My mysql database had strict-mode turned on and I got the following error:
    opendmarc-import: failed to create table ID: Field ‘repuri’ doesn’t have a default value

    I googled and used to turn it off.
    Got an error about it not being able to create reports- google it and added a “cd /var/tmp”. The reports were mailed out successfully after those changes.

Leave a Reply

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