Debian: Postfix + DMARC

OpenDMARC is an open source implementation of the Domain-based Message Authentication, Reporting and Conformance (DMARC) specification.

DMARC is a policy for mail transfer, which is already supported by some common mail providers. It depends on Sender Policy Framework and DKIM. DMARC provides a policy for outgoing mail and checks incoming mails for compliance with that policy. The policy is published via a DNS TXT record. It is explained in #DMARC Record.

Step 1: Install Packages

First, make sure that OpenDMARC is installed. On a Debian instance, you can do this with:

# apt install opendmarc

Step 2: OpenDMARC DNS Records

The DMARC record is a txt record for the host “_dmarc.example.com”. 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.

Step 3: Configure OpenDMARC

Edit OpenDMARC configuration file /etc/opendmarc.conf

AuthservID mail.example.com
Socket local:/var/spool/postfix/opendmarc/opendmarc.sock
PidFile /var/run/opendmarc.pid #Debian default
RejectFailures false
Syslog true
TrustedAuthservIDs mail.example.com,mail2.example.com
UMask 0002
UserID opendmarc:opendmarc
IgnoreHosts /etc/opendmarc/ignore.hosts
HistoryFile /var/lib/opendmarc/opendmarc.dat
  • 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.

Step 4: Setup Ignorehosts

Create the OpenDMARC configuration directory and set ignore hosts

# mkdir /etc/opendmarc/
# vi /etc/opendmarc/ignore.hosts

Define the hosts to ignore:

192.168.0.0/16

Step 5: Create Directories

Create the OpenDMARC directories and proper permissions

# mkdir -p /var/lib/opendmarc
# chown opendmarc:opendmarc /var/lib/opendmarc
# mkdir /var/spool/postfix/opendmarc/
# chown opendmarc:opendmarc /var/spool/postfix/opendmarc

Step 6: Postfix Integration

Configure Postfix integration by editing configuration file /etc/postfix/main.cf

smtpd_milters=inet:localhost:12345,unix:/opendmarc/opendmarc.sock
non_smtpd_milters=inet:localhost:12345,unix:/opendmarc/opendmarc.sock

Step 7: OpenDMARC 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:

There are privacy concerns with forensic reporting if your users subscribe to mailing lists. You can read a little more on this here.

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: /usr/share/doc/opendmarc/schema.mysql
    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'

Load the schema into the database:

# cd /path/to/schema.mysql/
# mysql -u root -p < schema.sql

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. /etc/opendmarc/report_script with the following data

#!/bin/bash
DB_SERVER='database.example.com'
DB_USER='opendmarc'
DB_PASS='password
DB_NAME='opendmarc'
WORK_DIR='/var/run/opendmarc'
REPORT_EMAIL='dmarc [at] example [dot] com'
REPORT_ORG=example.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

Make the reporting 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

When the script worked as expected you can add it to a daily cron job

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