Blog-like notes

Enabling DMARC with Postfix

I’ve just enabled DMARC support on my mail server (which already supported SPF and DKIM), and since it did not go as smoothly as I would have wanted, here are some notes for those interested.

The mail server is Postfix, running on a Debian Stable (“Jessie” at the time of this writing). The planned setup is as follows:

Interaction between pypolicyd-spf and OpenDMARC

In the above setup, OpenDMARC relies on pypolicyd-spf and OpenDKIM adding both a Authentication-Results header (one for SPF validation and one for DKIM validation) to the processed message.

However, for some reasons,1 Postfix never sends the first header of a message to the milters. Consequently, the Authentication-Results header prepended by policyd-spf cannot be seen by OpenDMARC.

There are several ways of fixing that problem:

  1. getting rid of pypolicyd-spf, and let OpenDMARC performs SPF validation itself (it can do that since its version 1.3.0);
  2. getting rid of pypolicyd-spf, and use a milter-based SPF validation program (headers prepended by a milter are always seen by the subsequent milters);
  3. make sure there’s always another header before the one prepended by policyd-spf.

I tested the first option and quickly discarded it, as SPF validation in OpenDMARC is rather buggy—and does not seem to be a priority for OpenDMARC’s developers, as this bug, which leads to false positive results, remains unfixed even though an obvious patch was provided. I also did a (admittedly quick) search for milter-based SPF implementations, but was not convinced by what I found (many of them seem abandoned).

I thus choose the third option, even though it’s kind of a (ugly) hack: I had Postfix prepend a dummy header even before attempting SPF validation, so that the header prepended by pypolicyd-spf could always be seen by the OpenDMARC milter.

Pypolicyd-spf setup

I mainly use the default configuration of pypolicyd-spf as provided by the Debian package, save for the two following lines I added:

Header_Type = AR
Authserv_Id = mail.incenp.org

The first line instructs pypolicyd-spf to generate a Authentication-Results header instead of a Received-SPF header. This is mainly cosmetic: both headers are allowed by RFC 7208, and recent versions of OpenDMARC can extract SPF results from both (older versions could not use the Received-SPF header).

The second line explicitly sets the Authentication Identifier field (RFC 7001, §2.4) that will also be used by OpenDKIM and OpenDMARC.

Integration with Postfix is done through the smtpd_recipient_rstrictions directive in /etc/postfix/main.cf:

smtpd_recipient_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    check_recipient_access regexp:/etc/postfix/recipient_access,
    check_policy_service unix:private/policyd-spf

The /etc/postfix/recipient_access contains the following line, which prepends the aforementioned dummy header to all messages:

/.\@./ PREPEND X-Incenp: Dummy header

OpenDMARC setup

OpenDMARC itself did not cause noticeable problems, save for one little glitch: the IgnoreAuthenticatedClients option is ignored in OpenDMARC 1.3.0 (as provided in Debian Jessie). The bug has been fixed in OpenDMARC 1.3.1, though, and I decided the bug was annoying enough to justify pulling down OpenDMARC 1.3.1 from Debian Stretch.

  1. Seemingly to ensure compatibility with Sendmail, according to this message from Postfix’s developer Wietse Venema.