Schedule icon
Webhook icon
Commands icon
Docker icon
If icon
SlackIncomingWebhook icon
Log icon

Prove Your Domain's Email Authentication Is Actually Enforced

Audit a domain's SPF, DKIM, and DMARC records from live DNS, report missing or weak configuration, and alert Slack until every check passes.

Categories
CloudInfrastructure

Diagram unavailable

We could not build the topology for this blueprint. The flow itself is valid, use the YAML on the left to run it.

Spoofed mail from your own domain is the cheapest attack in the book, and the defenses — SPF, DKIM, DMARC — rot silently: a vendor change breaks the include chain, a selector rotates out, DMARC sits on p=none forever because nobody scheduled the follow-up. This blueprint reads the records straight from live DNS on a cadence, extracts the DMARC policy, probes every selector your senders use, and reports a single verdict. Missing records are findings, not crashes, so the alert tells you exactly which record to add; enable strict mode and a monitoring-only p=none starts failing too.

How it works

  1. audit_records (io.kestra.plugin.scripts.shell.Commands on the Docker task runner) installs dig in a throwaway alpine:3.20 container and performs three lookups: SPF TXT on the apex, DMARC TXT at _dmarc.<domain>, and one TXT per selector in dkim_selectors at <selector>._domainkey.<domain>. Quoted chunked TXT records are concatenated before matching (long SPF strings arrive split), the p= policy is cut from the DMARC record, and the verdict — spf_found, dmarc_found, dmarc_policy, dkim_found, dkim_checked, overall_ok — is published through the ::{"outputs": ...}:: protocol. Absent records exit 0 with overall_ok: false; only a broken check (no dig, no network) exits non-zero.
  2. check_verdict (io.kestra.plugin.core.flow.If) branches on overall_ok. False: alert_auth_gaps posts each individual gap and the policy in force to Slack. True: log_pass records the configuration so the history shows when policy or selectors changed.
  3. The errors block alerts Slack when the audit itself fails — a crashed audit must never read as a passing domain.
  4. Triggers: a monthly Schedule (shipped disabled) plus a Webhook (email-auth-audit) to confirm record changes right after they are made.

What you get

  • One verdict across the three records that protect your domain from spoofing, computed from live DNS rather than a dashboard's cache.
  • Per-selector DKIM probing — the check follows the selectors your senders really use, not a guess.
  • A strict mode that turns p=none into a failure, so DMARC enforcement actually completes.
  • An auth_summary JSON output for dashboards or a compliance gate.

Who it's for

  • IT and mail admins who own the domain's DNS and are responsible for DMARC enforcement reaching p=reject.
  • Security teams doing continuous checks on subsidiary or acquired domains.
  • Anyone who inherited a domain with SPF, DKIM, and DMARC "somewhere" and no idea of their current state.

Why orchestrate this with Kestra

dig in a terminal shows today's records; Kestra shows the policy over time. The flow adds the cadence, the branch that separates enforcement from monitoring-only, the alert that names the gap, the webhook to verify a change the moment it lands, and an execution history where "when did DMARC go to p=reject" has an answer. It is the same pattern as the DNS propagation blueprint: DNS truth, made operational.

Prerequisites

  • Outbound UDP/53 from the Worker to public resolvers.
  • The DKIM selectors your mail senders publish (Google Workspace: google; Microsoft 365: selector1, selector2; add yours to the list).
  • A Slack incoming webhook for gap and failure alerts.

Secrets

  • SLACK_WEBHOOK_URL: Slack incoming webhook URL for gap and verifier-failure alerts.

Quick start

  1. Add the Slack webhook secret to your namespace.
  2. Set domain and list your real dkim_selectors.
  3. Run once and read auth_summary — compare it with dig TXT output by hand.
  4. Enable monthly_audit, then flip require_strict_dmarc to true when you are ready to fail on p=none.

How to extend

  • Fan out over many domains by calling this flow as a subflow from a domains input list.
  • Add an If on dmarc_policy == "none" that opens a ticket to finish enforcement — the classic stalled-DMARC pattern.
  • Chain this audit after dns-propagation-verifier when you change email-auth records: verify propagation first, audit the verdict second.
  • Push auth_summary into a dashboard alongside the other domain-health checks.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.