Phase 5: DKIM/DMARC/SPF

Phase 5: DKIM/DMARC/SPF Authentication

Objective

Install OpenDKIM and OpenDMARC as postfix milters. Configure DKIM signing for outbound mail, DKIM/DMARC verification for inbound mail, and SPF checking. After this phase, every message carries Authentication-Results headers — the same headers Abnormal Security reads via M365 Graph API.

Concepts

Term Meaning

Milter

Mail filter — a plugin interface for postfix/sendmail. Milters inspect and modify messages during SMTP transaction.

DKIM Signing

Outbound: hash message body + selected headers, sign with private key, add DKIM-Signature header.

DKIM Verification

Inbound: extract DKIM-Signature, fetch public key from DNS, verify hash. Result goes in Authentication-Results.

DMARC Alignment

The From: domain must align with either the SPF domain or DKIM signing domain. Misalignment = DMARC fail.

Authentication-Results

Header added by the receiving MTA summarizing SPF, DKIM, and DMARC verdicts. This is what Abnormal reads.

OpenDKIM Installation

sudo dnf install -y opendkim opendkim-tools
sudo systemctl enable opendkim

Generate DKIM Key Pair

sudo mkdir -p /etc/opendkim/keys
sudo opendkim-genkey -s default -d inside.domusdigitalis.dev -D /etc/opendkim/keys/
sudo chown opendkim:opendkim /etc/opendkim/keys/default.private

# View public key (this goes in DNS — Phase 4)
cat /etc/opendkim/keys/default.txt

OpenDKIM Configuration

sudo tee /etc/opendkim.conf <<'EOF'
Syslog          yes
Mode            sv
Canonicalization relaxed/simple
Domain          inside.domusdigitalis.dev
Selector        default
KeyFile         /etc/opendkim/keys/default.private
Socket          inet:8891@localhost
SigningTable    refile:/etc/opendkim/signing.table
KeyTable        /etc/opendkim/key.table
InternalHosts   /etc/opendkim/trusted.hosts
EOF

# Signing table
echo "*@inside.domusdigitalis.dev default._domainkey.inside.domusdigitalis.dev" | sudo tee /etc/opendkim/signing.table

# Key table
echo "default._domainkey.inside.domusdigitalis.dev inside.domusdigitalis.dev:default:/etc/opendkim/keys/default.private" | sudo tee /etc/opendkim/key.table

# Trusted hosts
sudo tee /etc/opendkim/trusted.hosts <<EOF
127.0.0.1
::1
10.50.1.91
10.50.1.0/24
EOF

OpenDMARC Installation

sudo dnf install -y opendmarc
sudo systemctl enable opendmarc

OpenDMARC Configuration

sudo tee /etc/opendmarc.conf <<'EOF'
Socket inet:8893@localhost
AuthservID mail-01.inside.domusdigitalis.dev
RejectFailures false
SPFSelfValidate true
HistoryFile /var/run/opendmarc/opendmarc.dat
EOF

SPF Verification

sudo dnf install -y pypolicyd-spf

Add to postfix main.cf:

sudo postconf -e "policy-spf_time_limit = 3600s"

Add to master.cf:

# Append SPF policy service
echo "policy-spf  unix  -       n       n       -       -       spawn
  user=nobody argv=/usr/libexec/postfix/policyd-spf" | sudo tee -a /etc/postfix/master.cf

Connect Milters to Postfix

sudo postconf -e "milter_default_action = accept"
sudo postconf -e "milter_protocol = 6"
sudo postconf -e "smtpd_milters = inet:localhost:8891, inet:localhost:8893"
sudo postconf -e "non_smtpd_milters = inet:localhost:8891"
sudo postconf -e "smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policy-spf"

Restart All Services

sudo systemctl restart opendkim opendmarc postfix
sudo systemctl status opendkim opendmarc postfix

Test Authentication Headers

# Send a test message
echo "Subject: Auth test from Phase 5
This is a test of DKIM signing and authentication." | sendmail -v evan@inside.domusdigitalis.dev

# Inspect headers on delivered message
# Look for: DKIM-Signature, Authentication-Results (spf, dkim, dmarc)
head -50 ~/Maildir/new/$(ls -t ~/Maildir/new/ | head -1)

Expected headers:

Authentication-Results: mail-01.inside.domusdigitalis.dev;
    dkim=pass header.d=inside.domusdigitalis.dev;
    spf=pass smtp.mailfrom=inside.domusdigitalis.dev;
    dmarc=pass header.from=inside.domusdigitalis.dev
DKIM-Signature: v=1; a=rsa-sha256; d=inside.domusdigitalis.dev; s=default; ...

Verification Checklist

  • OpenDKIM running: systemctl is-active opendkim

  • OpenDMARC running: systemctl is-active opendmarc

  • DKIM key in DNS: dig @10.50.1.90 TXT default._domainkey.inside.domusdigitalis.dev +short

  • Outbound mail has DKIM-Signature header

  • Delivered mail has Authentication-Results header with spf, dkim, dmarc verdicts

  • opendkim-testkey -d inside.domusdigitalis.dev -s default -vvv passes