Skip to content

fix: enforce single-value CNAME/ALIAS and flag coexistence conflicts#57

Open
ecv wants to merge 1 commit into
mainfrom
fix/pdns-cname-alias-conflict
Open

fix: enforce single-value CNAME/ALIAS and flag coexistence conflicts#57
ecv wants to merge 1 commit into
mainfrom
fix/pdns-cname-alias-conflict

Conversation

@ecv

@ecv ecv commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

What

Addresses the two non-blocker 422 modes from #51:

Follows #54 (duplicate-NS, Mode 1) and complements #52 (surface pdns detail in status).

Mode 2 — single-value CNAME/ALIAS

buildRRSets appended one record per entry for CNAME and ALIAS with no constraint (the CNAME case even had // TODO: ...violation of the RFC...we'll allow it). A DNSRecordSet (or discovery aggregating the same answer from multiple resolvers) with repeated/multiple content produced a multi-valued RRset that PowerDNS rejects:

RRset www.ibm.com. IN CNAME: only one such record allowed
RRset www.ibm.com. IN CNAME: duplicate record with content "...edgekey.net"

Both cases now keep exactly one record (first non-empty entry wins), so a malformed input can no longer produce an invalid payload.

Mode 3 — flag ALIAS coexistence conflicts distinctly

An ALIAS at a name that already holds another RRset returns 422 "Conflicts with pre-existing RRset". That's a well-formed record that cannot coexist — not a transient provider error. New pdns.IsConflict() classifies it, and the reconciler sets a distinct Programmed=False reason Conflict (vs generic PDNSError), so it's greppable in the DNSRecordSet status and clearly actionable.

Deferred (intentionally)

Pre-flight conflict prevention — checking existing RRsets and refusing/clearing before the PATCH — is not included. Doing it correctly needs the real PowerDNS coexistence rules (e.g. ALIAS at apex legitimately coexists with SOA/NS, so a naive "any other type = conflict" check would wrongly block valid apex ALIAS) plus integration testing against pdns. Left for a follow-up; #56 tracks it. The current PR makes the conflict safe and legible; the 30s-capped reconcile backoff still self-heals once the conflicting record is removed.

Test

  • internal/pdnsTestBuildRRSets_CNAMEAliasSingleValue (multi/dup collapse to one, first-wins), TestIsConflict (conflict vs non-conflict 422 vs non-422).
  • go build ./... clean; internal/pdns + internal/controller unit tests pass. (Docker/testcontainers integration test unrelated, not run.)

Closes #55
Closes #56
Refs #51

🤖 Generated with Claude Code

Two PowerDNS 422 modes from #51 beyond the duplicate-NS blocker:

Mode 2 — the operator could send a CNAME or ALIAS RRset with the same
content repeated (multi-valued), which PowerDNS rejects ("only one such
record allowed" / "duplicate record"). buildRRSets now keeps a single
record for these single-valued types (first non-empty entry wins), so a
malformed multi-valued input can no longer produce an invalid payload.

Mode 3 — an ALIAS written at a name that already holds a conflicting
RRset returns 422 "Conflicts with pre-existing RRset". This is a
well-formed record that cannot coexist, not a transient provider error.
Classify it (pdns.IsConflict) and surface a distinct Programmed=False
reason (Conflict) so it is greppable in the DNSRecordSet status and not
mistaken for a retryable PDNS error.

Pre-flight conflict prevention (predicting coexistence before the PATCH)
is deferred: it needs validated PowerDNS coexistence rules — ALIAS at
apex legitimately coexists with SOA/NS — plus integration testing.

Closes #55
Closes #56
Refs #51

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ecv

ecv commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

DNS RFCs say don't allow coexistence, single CNAME value only, no other records.

If a CNAME record is present at a node, no other data should be present; this ensures that the data for a canonical name and its aliases cannot be different. (RFC 1034 section 3.6.2, RFC 1912 section 2.4) The exception is when DNSSECis being used, in which case there can be DNSSEC related records such as RRSIG, NSEC, etc. (RFC 2181 section 10.1) – wiki summary of rfc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mode 3: ALIAS written over pre-existing RRset → PowerDNS 422 conflict Mode 2: CNAME RRset can be multi-valued/duplicate → PowerDNS 422

1 participant