Skip to content

fix(rfc2136): normalize CNAME trailing dots to stop sync churn#6555

Open
dpacgdm wants to merge 1 commit into
kubernetes-sigs:masterfrom
dpacgdm:fix/rfc2136-cname-trailing-dot-normalize
Open

fix(rfc2136): normalize CNAME trailing dots to stop sync churn#6555
dpacgdm wants to merge 1 commit into
kubernetes-sigs:masterfrom
dpacgdm:fix/rfc2136-cname-trailing-dot-normalize

Conversation

@dpacgdm

@dpacgdm dpacgdm commented Jul 9, 2026

Copy link
Copy Markdown

Summary

  • Normalize DNSEndpoint CRD endpoints via NewEndpointWithTTL so CNAME/NS/PTR targets match Ingress/Service (trailing dots stripped).
  • Harden Targets.Same to ignore trailing dots when comparing FQDN targets, preventing plan churn when current (AXFR) and desired (CRD) forms differ.
  • Rebuild rfc2136 AXFR merge-appended targets through NewEndpointWithTTL so multi-RR names get the same normalization as the first RR.

Fixes asymmetric trailing-dot handling that caused RFC2136 CNAME records from DNSEndpoint CRDs (and their TXT ownership records) to be removed and re-added every sync cycle.

Related: #6199

Test plan

  • go test ./endpoint/ -run 'TestSame|TestTargetsSame|TestNewEndpointWithTTL'
  • go test ./plan/ -run TestPlanNoChurnCNAMETrailingDot
  • go test ./provider/rfc2136/ -run 'TestRfc2136RecordsNormalizesCNAMETrailingDot|TestRfc2136GetRecords'
  • go test ./source/ -run 'TestCRDSourceNormalizesCNAMETrailingDot|TestCRDSource'
  • Deploy with --provider=rfc2136 --rfc2136-tsig-axfr --source=crd and a DNSEndpoint CNAME; confirm subsequent syncs make no remove/add for unchanged records

Made with Cursor

DNSEndpoint CRDs and rfc2136 AXFR merge paths disagreed on trailing-dot
FQDN form for CNAME/NS/PTR targets, so every sync deleted and recreated
identical records. Normalize at CRD ingest, Targets.Same, and AXFR merge.

Signed-off-by: dpacgdm <dpac.gdm@gmail.com>
@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign raffo for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubernetes-prow
kubernetes-prow Bot requested a review from szuecs July 9, 2026 13:49
@kubernetes-prow kubernetes-prow Bot added the plan Issues or PRs related to external-dns plan label Jul 9, 2026
@kubernetes-prow
kubernetes-prow Bot requested a review from vflaux July 9, 2026 13:49
@kubernetes-prow kubernetes-prow Bot added provider Issues or PRs related to a provider source cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jul 9, 2026
@kubernetes-prow

Copy link
Copy Markdown

Hi @dpacgdm. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubernetes-prow kubernetes-prow Bot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 9, 2026
@ivankatliarchuk

Copy link
Copy Markdown
Member

Share setup for kubernetes to reproduce it pls

@dpacgdm

dpacgdm commented Jul 12, 2026

Copy link
Copy Markdown
Author

@ivankatliarchuk Sure — minimal setup to reproduce (same shape as #6199):

Prereqs

  • Cluster with the ExternalDNS DNSEndpoint CRD installed
  • An RFC2136-capable DNS server that supports AXFR + TSIG (issue reporter used Technitium)
  • ExternalDNS with --provider=rfc2136, --rfc2136-tsig-axfr, --source=crd, --registry=txt, --policy=sync

Minimal Deployment args

--source=crd
--provider=rfc2136
--rfc2136-host=<dns-server>
--rfc2136-port=53
--rfc2136-zone=example.com
--domain-filter=example.com
--rfc2136-tsig-keyname=<key>
--rfc2136-tsig-secret-alg=hmac-sha256
--rfc2136-tsig-secret=<secret>
--rfc2136-tsig-axfr
--policy=sync
--registry=txt
--log-level=debug

DNSEndpoint

apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
  name: myservice
spec:
  endpoints:
  - dnsName: myservice.example.com
    recordTTL: 3600
    recordType: CNAME
    targets:
    - traefik.example.com

What to look for (pre-fix)

On each sync, debug logs show identical CNAME + TXT ownership RRs being removed and re-added, e.g.:
Removing RR: myservice.example.com ... CNAME traefik.example.com
then immediately
Adding RR: myservice.example.com ... CNAME traefik.example.com

Cause: AXFR returns the CNAME target with a trailing dot (traefik.example.com.), while the CRD desired target has none (traefik.example.com), so the plan treats them as different. Ingress-sourced CNAMEs and A/AAAA CRD records are not affected the same way.

Full reporter setup + sanitised logs: #6199

I validated the mismatch/fix path with unit tests in this PR (plan, endpoint, source/crd, provider/rfc2136); I have not re-run a live in-cluster RFC2136 deploy myself. Happy to adjust the repro notes if you want a Kind-oriented variant.

@ivankatliarchuk

Copy link
Copy Markdown
Member

But how is your rfc2136 looks like?

@ivankatliarchuk

Copy link
Copy Markdown
Member

The reason I'm asking, I have a bind setup for rfc2136 and not observing that issue. Seems like something related to https://technitium.com/dns/

@dpacgdm

dpacgdm commented Jul 13, 2026

Copy link
Copy Markdown
Author

@ivankatliarchuk Good question — I don't run a live RFC2136 server myself. I reproduced the mismatch via unit/plan tests and traced it from the reporter's setup in #6199, where the backend is Technitium DNS.

Their AXFR logs show the CNAME target coming back with a trailing dot (traefik.example.com.) while the DNSEndpoint CR desired value has none (traefik.example.com), which makes the plan think every sync is a change. That matches the remove/add loop in their sanitised logs.

If your BIND setup returns or normalizes CNAME targets differently on AXFR, you may not see the churn — that would actually support treating this as a normalization gap rather than something every RFC2136 backend triggers. The fix still hardens comparison so CRD-sourced CNAME/NS/PTR targets match regardless of trailing-dot form in the AXFR response.

If you're up for a quick check: a DNSEndpoint CNAME (target without trailing dot) plus debug logs of the AXFR Record=... CNAME ... line would show whether BIND returns the target with or without the trailing dot in your environment.

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

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. plan Issues or PRs related to external-dns plan provider Issues or PRs related to a provider size/L Denotes a PR that changes 100-499 lines, ignoring generated files. source

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants