Email deliverability & spoofing defense. Packaged as
modules/EmailAuth(clockwork/email-auth) and categorized undersecurityinModuleCatalog. Inspects SPF 10-lookup limits, loop detection, DMARC enforcement policies, and DKIM selector probing across client domains.
Monitors email authentication records across your fleet’s root apex domains to prevent spoofing, maintain Google/Yahoo inbox deliverability compliance, and alert operators when client DNS records degrade or break.
Why DNS-over-HTTPS (DoH)
Traditional PHP dns_get_record() has three fatal problems for automated monitoring:
- Resolver caching: host-level DNS caches (systemd-resolved, dnsmasq, or ISP resolvers) can mask DNS updates for hours or return stale records.
- Ambiguous failures:
dns_get_record()conflatesNXDOMAIN(domain doesn’t exist), empty response (domain exists but has no TXT record), and network timeout/SERVFAIL into an empty array orfalse. That would trigger false “missing DMARC” alerts during transient resolver hiccups. - Untestable: native DNS lookups cannot be mocked with
Http::fakein automated tests.
EmailAuth introduces the DnsTxtResolver contract. By default, it uses DNS-over-HTTPS (DohDnsTxtResolver), querying Cloudflare (https://cloudflare-dns.com/dns-query) with an automated fallback to Google Public DNS (https://dns.google/resolve). A lookup failure is classified as unknown, never a false fail. A native NativeDnsTxtResolver is available for air-gapped or offline environments.
What gets checked
Checks run per registrable apex domain (via RootDomainResolver::resolve()), automatically deduplicated across sites so www.example.com and sub.example.com share a single check.
1. SPF (Sender Policy Framework)
- Single record rule: Exactly one
v=spf1record. Multiple SPF records cause permanent authentication errors per RFC 7208. - 10-DNS-lookup limit: Recursively expands
include:,redirect=,a,mx, andexistsmechanisms. Exceeding 10 lookups causes SPFpermerrorand inbox rejection. Warns at 8–10 lookups; fails at >10. - Loop & recursion detection: Detects circular
include:chains without infinite loops. - Void lookups: Tracks lookups returning
NXDOMAINor no records; warns if void lookups exceed 2. - Terminal qualifier: Recommends
-all(hard fail) or~all(soft fail). Flags?all(neutral) with a warning and+all(allow all) or missing qualifiers as critical failures. - Deprecated mechanisms: Flags deprecated
ptrlookups.
2. DMARC (Domain-based Message Authentication)
- Queries
_dmarc.{domain}for RFC 7489 compliance. - Ensures a single valid
v=DMARC1TXT record exists. - Policy enforcement:
p=rejectorp=quarantinepass.p=none(monitoring only) produces an advisory warning. - Reporting destination: Warns if
rua=aggregate report mailto destination is missing. - Inspects
pct=enforcement percentage andsp=subdomain policy.
3. DKIM (DomainKeys Identified Mail)
- DNS cannot enumerate DKIM selectors without knowing the key names in advance.
- Probes common industry selectors:
google,selector1,selector2(Microsoft 365),k1,s1,s2,mx,smtp,pm(Postmark),mandrill,default,dkim. - Allows operators to configure custom selectors per domain directly from the web drawer.
- Missing probe results are treated as advisory
unknown/warn, never a hard failure, because legitimate custom selectors may not match the standard probe list.
4. Parked & Non-Sending Domains
- Checks MX records: if no MX or null MX (
0 .) is found, the domain is evaluated as a parked/non-sending domain. - The recommended posture shifts to
v=spf1 -allwithp=reject, and DKIM records are marked as not applicable.
Surfacing and UI
- Security Hub Integration (
/security/email-author/email-auth): Seamlessly integrated as an active subtab in the unified Security navigation strip (Scans,WP Admins,Email Auth,Bans). Displays every client apex domain, associated site count, SPF lookup counts, DMARC policy pills (reject,quarantine,none), DKIM status, and overall grade. - Global Fleet Scan (“Scan all domains”): Operators can trigger an immediate, on-demand background scan across every active apex domain via the header button (
POST /email-auth/scan-all). Soft-deleted domains are automatically skipped. - Slide-Over Drawer: Click any domain to open the detail drawer featuring:
- Copyable raw DNS records (
v=spf1...,v=DMARC1...). - Plain-English advisory findings with color-coded severity (
fail,warn,info). - Suggested fix records tailored to the domain.
- Custom DKIM selector manager.
- One-click Ignore Domain toggle to silence alerts for third-party-managed client DNS.
- Instant Re-scan Domain button (
POST /email-auth/scan) for fast verification after client DNS updates. - Remove from Tracking: Safely soft-deletes the domain (
DELETE /email-auth/domains/{domain}), removing it from the fleet overview and excluding it from scheduled weekly scans.
- Copyable raw DNS records (
- Chat Alerts: Fires
email_auth_degradedto Slack and Mattermost only on state transitions (e.g., when a previously passing domain degrades tofail).
Emailing a client about a domain
Open a domain’s drawer on /email-auth and click Compose email (or go to /email-auth/domains/{domain}/notify). The composer sends a white-labeled email to the client:
- To is pre-filled with the report contacts of every client that owns a site on this apex domain (
RootDomainResolver); edit freely, comma-separated, max 20. - Your note comes first in the email (default: “We’ve noticed some issues with your domain’s email settings (DNS)… Would you like us to fix this for you?”), then What we found — each selected finding in plain English (
Modules\EmailAuth\Support\FindingExplainer), then a reply call-to-action, then Technical details for whoever manages DNS: the scanner message + code for each finding and the current SPF / DMARC / DKIM / MX state. - Findings:
failandwarnare pre-ticked,infois offered unticked,passis never offered. Unknown codes posted to the form are dropped. - Live preview renders the exact email beside the form.
- Branding = White Labeling → Client Reports (header colour, accent, logo, company name, support email) — same template family as the monthly client report. The logo is embedded inline (
App\Support\Mail\BrandLogo). Reply-To is the support email; the only link in the email is the supportmailto:(Control isn’t publicly reachable). - Send me a copy BCCs the operator. Each send writes an
email_auth_client_notice_sentaction-log row (recipients, subject, finding codes, mailer). - If
MAIL_MAILER=log, the composer shows a warning and the send is written to the log instead of delivered.
CLI and Scheduled Cadence
- Runs weekly via
clockwork:check-email-authon Mondays at 04:00 UTC (automatically excluding soft-deleted/trashed domains). - On-demand fleet scans or single-domain checks via CLI:
# Check all active, monitored domains
php artisan clockwork:check-email-auth
# Check a single domain
php artisan clockwork:check-email-auth --domain=example.com
