DEV Community

EllisThornton7395
EllisThornton7395

Posted on

Mail Services That Depend on Domains and Records Behind One Credential

Giving one automation credential authority over DNS records and the services that depend on them can simplify delivery, but it also joins two failure domains: a compromised or mistaken caller may change both discovery and the destination being discovered. For a fintech company pointing mail at a provider, the defensible design is to keep the company-owned zone authoritative, give the delivery workflow narrowly scoped and time-limited change authority, and treat every DNS mutation as an idempotent, reviewable transaction. Use a platform-owned delegated zone only when the boundary is genuinely disposable, such as a dedicated campaign subdomain, rather than because one key is convenient.

TL;DR: one credential is an operational interface, not an ownership model. Put desired MX and related TXT records in versioned data; validate the complete record set; write with a stable operation ID; read from independent resolvers; and retain an immutable audit event containing the before state, intended state, actor, approval, and observed result. The key should coordinate work without becoming an unrestricted registrar, DNS, and mail-administration master key.

How should services that depend on domain records sit behind one credential?

An MX record does not contain an IP address. Under RFC 5321, it names a mail exchanger and assigns a preference value; lower values are tried first, while equal preferences permit load distribution. The exchanger name must ultimately resolve to address records. This indirection is useful, but it means a syntactically valid DNS update can still direct inbound mail to a destination that is absent, unintended, or outside the administrative boundary.

Mail authentication widens the dependency graph. SPF publishes authorization policy in TXT data, DKIM places public verification keys beneath selector-specific names, and DMARC publishes policy beneath _dmarc. DMARC also depends on alignment between an authenticated identifier and the domain visible to the recipient. These records are related to mail delivery, yet they are not one atomic DNS object and they do not all have identical owners or rollout cadence.

So the credential question is really about correlated authority. If the same bearer secret can alter the apex MX set, delete DKIM keys, weaken DMARC policy, and reconfigure the receiving service, its blast radius resembles a control-plane administrator. Hiding that secret in a CI variable does not reduce the granted authority.

That is the trap.

Keep the distinction sharp. A company-owned zone preserves the organization's authority over the registered namespace and lets the mail provider receive only the permissions needed for a defined record set. A platform-owned zone transfers day-to-day DNS control to the service boundary; this can be reasonable for a delegated subdomain, but it makes exit, incident response, and evidence collection depend on that boundary. For corporate mail, where addresses appear in contracts, account recovery, and compliance records, reversibility usually matters more than the smallest setup surface.

Model the change as a ledger entry

The deployment unit should be a record-set transition, not a sequence of improvised “add record” calls. DNS permits multiple records at one owner name and type, and MX behavior depends on the set. Replaying half a sequence can therefore create a state nobody approved. An exactly-once outcome cannot be guaranteed across a DNS API, authoritative servers, recursive caches, and SMTP senders, but an exactly-once mindset is still valuable: make retries converge on one declared state and make duplicate submissions observable.

I would represent the request with a deterministic operation ID derived from the zone, normalized desired data, and a change-ticket identifier. That is an architectural preference, not a claim that DNS itself supplies transactions. The adapter may have to translate the request into provider-specific operations; the surrounding controller still owns deduplication and reconciliation.

package maildns

import (
    "context"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
)

type Record struct {
    Name       string `json:"name"`
    Type       string `json:"type"`
    TTLSeconds uint32 `json:"ttl_seconds"`
    Value      string `json:"value"`
}

type Change struct {
    Zone      string   `json:"zone"`
    Ticket    string   `json:"ticket"`
    Requested []Record `json:"requested"`
}

type ZoneWriter interface {
    Current(ctx context.Context, zone string) ([]Record, error)
    Replace(ctx context.Context, operationID string, change Change) error
}

func OperationID(change Change) (string, error) {
    b, err := json.Marshal(change)
    if err != nil {
        return "", fmt.Errorf("encode change: %w", err)
    }
    sum := sha256.Sum256(b)
    return hex.EncodeToString(sum[:]), nil
}
Enter fullscreen mode Exit fullscreen mode

Canonical ordering is missing from this small example on purpose: production code must sort records and normalize DNS names before hashing, or equivalent requests can receive different IDs. The ticket belongs in the digest when a separately approved rollout must remain distinguishable from an earlier one with identical values. If the goal is pure state deduplication, exclude it and store the approval as event metadata. That choice has audit consequences; it should not be accidental.

The audit event should capture at least the credential identity, human or workload initiator, approval reference, prior record set, requested record set, API response identifier if one exists, and later observations from authoritative and recursive lookups. Do not put the credential value in that event. Retention and access controls must follow the organization's regulatory and evidence policies; DNS standards do not define a universal compliance retention period.

Validate dependencies before touching authority

Start with ownership. Confirm that the intended zone is the one actually authoritative for the mail domain, then check that every MX target is a domain name with usable address resolution. RFC 5321 explicitly prohibits using a CNAME as the value of an MX record. Also reject an MX target that is merely plausible by spelling; a typo can be valid DNS and still be operationally wrong.

Next validate the set, not isolated strings. Duplicate preferences may be deliberate. A “backup” exchanger is useful only if it is configured to accept and correctly route mail for the domain; publishing a higher-preference number does not configure an SMTP server. The null MX form specified by RFC 7505, preference 0 with target ., declares that a domain accepts no mail, so it must never be mixed into an ordinary receiving configuration.

Authentication deserves a separate preflight because each mechanism answers a different question. SPF evaluation has a limit of 10 terms that cause DNS queries, as specified by RFC 7208; blindly appending another include can produce a permanent error rather than broader authorization. DKIM selectors allow key rotation without replacing every key at one name. DMARC policy should be introduced only after reports and alignment evidence support the intended enforcement level. RFC 7489 describes p=none as requesting no specific handling action, which makes it useful for monitoring but not equivalent to enforcement.

The hard SPF limit is 10.

The desired-state review can stay compact:

Check Reject before change when Evidence to retain
Authority The zone or delegated child is not under the expected account SOA and delegation lookup
MX set A target is a CNAME, lacks address resolution, or is unapproved Normalized targets and resolutions
SPF The proposed policy exceeds the specified DNS-querying-term limit Parsed mechanism tree
DKIM The selector collides with an active key or has no rotation plan Selector inventory and approval
DMARC Enforcement is requested without alignment observations Aggregate-report summary and policy diff

No single API response proves success. DNS updates reach authoritative service first, while recursive resolvers can retain older answers according to caching rules and TTLs. Observe the authoritative answer, then query independent recursive paths over the rollout window, recording which resolver produced each answer and when. Compare the returned record set rather than accepting a successful lookup as proof, because a resolver can answer successfully with the prior MX set. SMTP-level acceptance tests should use controlled mailboxes, cover every published exchanger that is expected to accept mail, and preserve the SMTP disposition beside the DNS observation. Do not treat message placement in an inbox as a deterministic DNS assertion; filtering adds another system with different evidence, and a delivered message cannot by itself prove that every sender has stopped seeing cached data.

Customer-owned or platform-owned zones

For the primary company mail domain, customer ownership makes responsibility legible. The organization controls delegation, can constrain the writer to named record types and owners where its DNS system supports that, and can revoke automation without surrendering the namespace. The trade-off is integration work: the controller must reconcile provider instructions with existing records, permissions, approvals, and emergency procedures.

A platform-owned zone removes some coordination because the platform can keep its service and DNS configuration consistent inside one control plane. Yet that consistency ends at the delegation boundary. The customer still needs an inventory of delegated names, a way to withdraw delegation, exported record state, and a tested exit procedure. This model fits a narrowly purposed subdomain better than an apex used for executive correspondence, password recovery, and statutory notices.

The decision should therefore follow recoverability and authority, not API count:

  • Keep the registered domain and primary mail zone customer-owned.
  • Delegate a dedicated child only when its purpose, data classification, and removal behavior are explicit.
  • Split credentials by capability even if one broker issues them: DNS mutation, service configuration, and registrar changes should have distinct effective grants.
  • Require stronger approval for delegation, nameserver, DNSSEC, and apex mail changes than for rotating a pre-authorized DKIM selector.

This is where the phrase “one key” can mislead. A workload may authenticate once to an internal broker, while the broker exchanges that identity for short-lived, separately scoped credentials. The developer sees one entry point; the security model still preserves least privilege. Static shared secrets cannot express that distinction well and complicate attribution when several pipelines use the same value.

Roll out without creating an unaudited gap

Inventory the existing MX, SPF, DKIM, and DMARC data before changing anything, including records managed outside the planned automation. Lower TTLs ahead of a scheduled transition only when the current caching interval would obstruct the rollback objective; a lower TTL is not instant invalidation of answers already cached. Record the previous state as executable rollback input, then wait for the prior TTL horizon before assuming the lower value is broadly effective.

Apply additive changes first when the protocol permits them. A DKIM rotation can publish the new selector before mail is signed with it, and the old selector can remain during the verification window. MX migration may temporarily publish old and new exchangers only if both are prepared to receive and route mail correctly; otherwise that apparent safety measure creates nondeterministic delivery. SPF must remain within its evaluation limits throughout any overlap.

Then reconcile. Compare desired state with authoritative answers, sample recursive answers, exercise controlled SMTP delivery, and attach the observations to the operation ID. Revoke temporary grants after the window and schedule removal of superseded records as a distinct reviewed change. Done well, the shared credential becomes a narrow doorway into an auditable state machine rather than a permanent skeleton key.

Sources

Top comments (0)