Email Auth: DNS-Only Audit
Tiny Python CLI to quickly check SPF, DMARC, MTA-STS, TLS-RPT, and optional DKIM selectors.
8/16/2025 • 3 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 isquarantine
orreject
atpct=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.
- Presence, single record, and final
- DMARC
- Presence,
p=
policy,pct
, and whetherrua
is set. Notes weak/monitor mode.
- Presence,
- MTA-STS (DNS only)
_mta-sts.<domain>
TXT likev=STSv1; id=...
(does not fetch the HTTPS policy file).
- SMTP TLS Reporting (TLS-RPT)
_smtp._tls.<domain>
TXT likev=TLSRPTV1; rua=...
.
- DKIM (optional)
- For each selector you pass (no enumeration), confirms
v=DKIM1
and non-emptyp=
at<selector>._domainkey.<domain>
.
- For each selector you pass (no enumeration), confirms
Requirements
- Python 3.8+
dnspython
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
orp=reject
andpct>=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 ofrua
so you’ll actually receive failure reports.
DKIM
- Only checks selectors you pass (ex:
--dkim s1,s2
). - Confirms
v=DKIM1
and a non-emptyp=
key. (Emptyp=
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 toquarantine
orreject
when ready; setpct=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
, andrua=
set - [ ]
_mta-sts
TXT published (and separately host the HTTPS policy) - [ ]
_smtp._tls
TLS-RPT TXT withrua=
- [ ] DKIM selectors live with non-empty
p=
(for each sending platform)