Beginning february Google and Yahoo start to require DMARC. They ramp this up slowly: first only temporary errors, then gradually increasing rejection rate (see Google documentation). Some requirements also only affect large senders with more than 5000 messages per day. Still DMARC is actually a good idea and it doesn't hurt to implement it.
What is DMARC?
First some background on DMARC: actually DMARC builds upon two other security mechanisms that you may already know:
- Sender Policy Framework (SPF): is a simple DNS record that specifies which servers should be trusted to send mail for this domain.
For example for my domain it looks like this:
ff02.de. TXT "v=spf1 mx a:ananas.ff02.de ~all"
(it allows the ingoing mailserver, my second server and disallows all other). The options are pretty well documented in http://www.open-spf.org/SPF_Record_Syntax/ - Domain Keys Identified Mail (DKIM): in this case the mail server signs outgoing mails with a key. The key is published in a DNS record. DKIM has the advantage that it survives forwarding.
DMARC is then a policy that defines what should happen if a message fails one of the two security mechanisms above. It has the nice feture that failing messages can be reported to a mail address of your choice.
There are also ARC headers that keep the authenticity chain for forwarded messages. Did not yet look too much into it, but maybe there's a follow-up blog post coming about this.
DKIM for Postfix
Signing messages is handled by OpenDKIM. For my configuration I followed the docs in Arch wiki and Debian.
After installing the package with yay -S opendkim
, I configured the config files:
# /etc/opendkim/opendkim.conf
# Secure base cofiguration from https://wiki.archlinux.org/title/OpenDKIM#Security
Selector default
Socket local:/var/spool/postfix/opendkim/opendkim.sock
Syslog Yes
TemporaryDirectory /run/opendkim
UMask 002
UserID opendkim
# To tolerate tools that fiddle with headers
Canonicalization relaxed/simple
# main domain
Domain ff02.de
# Config based on https://wiki.archlinux.org/title/OpenDKIM#Multiple_domains
KeyTable file:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
# /etc/opendkim/KeyTable
default._domainkey.ff02.de ff02.de:default:/etc/opendkim/keys/ff02.de/default.private
Contrary to what the Arch wiki says, you dont need multiple line if you use the same key.
# /etc/opendkim/SigningTable
*@ff02.de default._domainkey.ff02.de
*@simon-dreher.de default._domainkey.ff02.de
# /etc/opendkim/TrustedHosts
127.0.0.1
::1
localhost
37.187.17.172
babaco.ff02.de
opendkim-genkey --restrict --selector default --domain ff02.de --directory /etc/opendkim/keys/ff02.de --bits=2048 --verbose
chown -R opendkim:mail /etc/opendkim
cat /etc/opendkim/keys/ff02.de/default.txt
Then set in default._domainkey TXT
in every domain to the output from default.txt
(remove the brackets and quotes).
You can test that the DKIM config is correct with sudo -u opendkim opendkim-testkey -vv
.
Optionally you can also specify a domain with -d simon-dreher.de
.
As I run Postfix with a chroot, the socket file needs to be under /var/spool/postfix
and therefore needed to create and fix the owner of the file: chown -R opendkim:postfix /run/opendkim/opendkim.sock
In my case I also needed to change the group, as Postfix didn't have he permissions to access the socket file:
# /etc/systemd/system/opendkim.service.d/override.conf
[Service]
# Use the group "postfix", so the socket uses that group and postfix can access it
Group=
Group=postfix
And then we can finally start OpenDKIM:
systemctl daemon-reload
systemctl start opendkim
To make Postfix communicate with OpenDKIM, we add config like this to main.cf
:
non_smtpd_milters = unix:/run/opendkim/opendkim.sock
smtpd_milters = unix:/run/opendkim/opendkim.sock
# Also run bounce through milters, so bounces are signed by DKIM
internal_mail_filter_classes = bounce
and systemctl restart postfix
.
DMARC
I used the DMARC wizard to generate my DMARC record. You could also do it manually if you want.
For the start I was conservative and used the most permissive policy (none = don't block anything): v=DMARC1; p=none; rua=mailto:postmaster@simon-dreher.de;
Then set this record into _dmarc.simon-drher.de TXT
.
With multiple domains, you can use a reporting adress of the same domain (easier) or point all of them to the same address (then need whitelisting on the receiving domain).
Test
Finally we also want to validate that DKIM and DMARC now work. The https://www.mail-tester.com that was linked in the Debian docs is really easy to use: generate a mail address, send a test mail to it and see the score. the downside is, that you can only test 3 mails per day for free. Pro tip: Write a bit more that "Test" into your mail, otherwise you get warnings because pyzor thinks that it looks like spam.
It will probably tell you that the List-Unsubscribe
header is missing, but this is not relevant if you don't operate a mailing list.
You could also configure to validate DMARC on incoming mail with OpenDMARC, but for now saved this for another weekend project.