Publish SPF, DKIM and DMARC and check they agree

short · 45 min · Objective 4.1

Task

Publish the three records for a domain you control in your own lab DNS, then evaluate messages against them and find the alignment failure that SPF alone cannot see. Finish by publishing a BIMI record and explaining why it changes nothing until DMARC is at enforcement.

Steps

  1. In your local resolver, create a zone for lab.example and publish an SPF TXT record naming your Linux VM's address as the only permitted sender.
  2. Generate a DKIM key pair and publish the public key as a TXT record at a selector you choose.
  3. Publish a DMARC record at _dmarc.lab.example with p=none and an rua address — the position every deployment starts from.
  4. Write /tmp/evaluate.py: given a message file and the zone, it checks whether the sending address is permitted by SPF, whether the DKIM signature verifies, and — the part that matters — whether either passing domain ALIGNS with the From: header domain. Have it print one line per result, spf=pass|fail, dkim=pass|fail and dmarc=pass|fail -- the Verify reads the dmarc= line.
  5. Evaluate three messages: one fully aligned, one where SPF passes for a different domain than the From: header, and one with a broken DKIM signature.
  6. Publish a BIMI record at default._bimi.lab.example -- a TXT record beginning v=BIMI1; with an l= tag pointing at a logo URL on your own lab domain. Receivers that support BIMI would still show no logo for this domain, and the next step asks you to say why.
  7. Record in /tmp/email-auth.md which of the three messages would be delivered under p=none, p=quarantine and p=reject, why the second message is the one SPF alone cannot catch, and why the BIMI record does nothing while DMARC stays at p=none.

Files the Verify reads

The Verify block reads these by name, so save them exactly here:

  • /tmp/msg-aligned.eml -- the fully aligned message from step 5.
  • /tmp/msg-misaligned.eml -- the message where SPF passes for a different domain than the From: header.

Verify

dig +short TXT lab.example @127.0.0.1 | grep -c "v=spf1"
dig +short TXT _dmarc.lab.example @127.0.0.1 | grep -c "v=DMARC1"
python3 /tmp/evaluate.py /tmp/msg-aligned.eml | grep -ci "dmarc=pass"
python3 /tmp/evaluate.py /tmp/msg-misaligned.eml | grep -ci "dmarc=fail"
dig +short TXT default._bimi.lab.example @127.0.0.1 | grep -c "v=BIMI1"
grep -ciE "p=none|enforcement|quarantine or reject" /tmp/email-auth.md

All six must be non-zero. The fourth is the one that teaches: the misaligned message passes SPF — genuinely, for the attacker's own domain — and fails DMARC, because DMARC is the only one of the three that requires the passing domain to match what the recipient actually sees in the From: line. The last two show the BIMI record exists and that you know why it is inert: a logo is displayed only for a domain whose DMARC policy is at enforcement.

Notes

Moving from p=none to enforcement is the step most domains never take, and BIMI exists partly as the incentive to take it. On a real domain, read the aggregate reports for several weeks first: every legitimate sender you have not yet accounted for -- the marketing platform, the ticketing system, the scanner that emails PDFs -- starts failing the moment the policy tightens.

This is an independent study companion for CompTIA Security+ SY0-801 and is not produced by or endorsed by CompTIA.