Email Auth: DNS-Only Audit

Tiny Python CLI to quickly check SPF, DMARC, MTA-STS, TLS-RPT, and optional DKIM selectors.

8/16/20253 min

TL;DR — One-file Python CLI that inspects a domain’s DNS for SPF, DMARC, MTA-STS, TLS-RPT, and (if you specify selectors) DKIM.
PASS = SPF present with ~all or -all and DMARC policy is quarantine or reject at pct=100.
No HTTP fetches, no heavy deps.

What this checks

  • SPF
    • Presence, single record, and final all qualifier (~all or -all recommended). Flags +all and multiple records.
  • DMARC
    • Presence, p= policy, pct, and whether rua is set. Notes weak/monitor mode.
  • MTA-STS (DNS only)
    • _mta-sts.<domain> TXT like v=STSv1; id=... (does not fetch the HTTPS policy file).
  • SMTP TLS Reporting (TLS-RPT)
    • _smtp._tls.<domain> TXT like v=TLSRPTV1; rua=....
  • DKIM (optional)
    • For each selector you pass (no enumeration), confirms v=DKIM1 and non-empty p= at <selector>._domainkey.<domain>.

Requirements

pip install dnspython

Usage

# Basic
python3 email_auth_lite.py example.com

# With specific DKIM selectors
python3 email_auth_lite.py example.com --dkim s1,s2

# Note: --strict exists for interface parity but has no effect in this LITE build

Example output

(Normalized www.example.com -> example.com)

Email Auth LITE for example.com
===============================

[SPF]
  - v=spf1 include:_spf.example.net ~all
  - all=~

[DMARC]
  - v=DMARC1; p=reject; pct=100; rua=mailto:dmarc@reports.example.com
  - policy=reject; pct=100; rua=True

[MTA-STS]
  - present

[TLS-RPT]
  - present

[DKIM]
  - (pass --dkim s1,s2 to check selectors)

[Baseline]
  PASS: True

PASS criteria (baseline)

  • SPF present and ends with ~all or -all.
  • DMARC present with p=quarantine or p=reject and pct>=100.

If either is missing/weak, baseline shows PASS: False and lists reasons.

Field-by-field notes

SPF

  • Should be one record at the apex you’re checking; multiple records break evaluation.
  • +all is unsafe and will be flagged; prefer ~all (soft fail) or -all (hard fail).

DMARC

  • p=none is monitor-only (flagged as not enforcing).
  • pct<100 reduces enforcement and is noted.
  • rua missing means you won’t get aggregate reports—called out as a note.

MTA-STS

  • DNS TXT _mta-sts.<domain> proves intent (v=STSv1; id=...).
    This tool does not fetch the HTTPS policy file (https://mta-sts.<domain>/.well-known/mta-sts.txt).

TLS-RPT

  • Looks for v=TLSRPTV1 and presence of rua so you’ll actually receive failure reports.

DKIM

  • Only checks selectors you pass (ex: --dkim s1,s2).
  • Confirms v=DKIM1 and a non-empty p= key. (Empty p= implies revoked.)

Common outcomes & fixes

  • Multiple SPF records → Consolidate into one v=spf1 ... record.
  • SPF ends with +all → Change to ~all or -all.
  • DMARC p=none → Move to quarantine or reject when ready; set pct=100.
  • No rua in DMARC/TLS-RPT → Add a reporting mailbox (can be third-party).
  • DKIM selector missing → Verify selector name and publish v=DKIM1; p=<public-key> at <selector>._domainkey.<domain>.

Limitations (by design)

  • DNS-only: no HTTP fetch of the MTA-STS policy file.
  • No DKIM selector discovery/enumeration.
  • Does not validate the reachability of rua mailboxes or parse external policies.

Troubleshooting

  • Timeouts / no answer → Public resolvers sometimes rate-limit. Re-run or switch networks.
  • Subdomains → Run the tool on the exact domain you care about (e.g., mail.example.com) if policies are delegated.
  • “PASS but still failing delivery” → This tool validates signals, not message flow. Check mail logs and alignment (SPF/DKIM alignment for DMARC).

Quick remediation checklist

  • [ ] One SPF record, ending in ~all or -all
  • [ ] DMARC present with p=quarantine|reject, pct=100, and rua= set
  • [ ] _mta-sts TXT published (and separately host the HTTPS policy)
  • [ ] _smtp._tls TLS-RPT TXT with rua=
  • [ ] DKIM selectors live with non-empty p= (for each sending platform)