Accept intra-cluster NOTIFY on secondaries via allow-notify #14

Merged
benvin merged 1 commits from benvin/allow-notify-intra-cluster into main 2026-07-25 22:54:18 +10:00
Owner

Why

Dynamic updates (external-dns RFC2136 on bind-externaldns, dns-updater nsupdates on bind-authoritative) land on the primary and bump the zone serial; the primary NOTIFYs the secondaries — but the secondaries REFUSE the NOTIFY:

zone k8s.syd1.au.unkin.net/IN: refused notify from non-primary: 10.42.3.197#40514

10.42.3.197 is the primary pod's own IP. Secondaries transfer from the primary Service ClusterIP (stable across primary pod restarts), and BIND derives a zone's implicit allow-notify from its primaries list — the ClusterIP. But k8s Services NAT only the inbound direction, so the primary pod's NOTIFYs egress with its pod IP as source and never match. Replication then only happens on the SOA refresh timer (3600s): a 1-hour propagation delay on every dynamic zone.

Changes

  • Add PrimaryPodAddresses to bind.RenderInput.
  • On secondaries only, render an options-scope allow-notify covering the primary pod IP(s) plus the transfer address (an explicit allow-notify replaces the implicit primaries-derived default, so the transfer source is kept). Emitted only when the primary pod IP is known; nothing changes when it is not. This covers both catalog member zones (they inherit options defaults) and plain secondary zones.
  • Populate the primary pod IP in the cluster controller the same way seeding/also-notify already resolves it; the existing Pod watch re-renders the ConfigMap when the pod IP changes across restarts.
  • Add render unit tests: secondary emits allow-notify with pod IP + transfer address; primary emits none; omitted when the pod IP is unknown.

go build ./..., go test ./..., and go vet ./... all pass.

Rollout

Config-generation change only — it takes effect after an operator image release (tag v*) and an argocd image bump. Not done here. Once the new ConfigMap renders, secondaries roll and begin accepting NOTIFYs, dropping dynamic-zone propagation from ~1h to seconds.

## Why Dynamic updates (external-dns RFC2136 on `bind-externaldns`, dns-updater nsupdates on `bind-authoritative`) land on the primary and bump the zone serial; the primary NOTIFYs the secondaries — but the secondaries REFUSE the NOTIFY: ``` zone k8s.syd1.au.unkin.net/IN: refused notify from non-primary: 10.42.3.197#40514 ``` `10.42.3.197` is the primary pod's own IP. Secondaries transfer from the primary Service ClusterIP (stable across primary pod restarts), and BIND derives a zone's implicit `allow-notify` from its `primaries` list — the ClusterIP. But k8s Services NAT only the inbound direction, so the primary pod's NOTIFYs egress with its pod IP as source and never match. Replication then only happens on the SOA refresh timer (3600s): a 1-hour propagation delay on every dynamic zone. ## Changes - Add `PrimaryPodAddresses` to `bind.RenderInput`. - On secondaries only, render an options-scope `allow-notify` covering the primary pod IP(s) plus the transfer address (an explicit `allow-notify` replaces the implicit `primaries`-derived default, so the transfer source is kept). Emitted only when the primary pod IP is known; nothing changes when it is not. This covers both catalog member zones (they inherit options defaults) and plain secondary zones. - Populate the primary pod IP in the cluster controller the same way seeding/also-notify already resolves it; the existing Pod watch re-renders the ConfigMap when the pod IP changes across restarts. - Add render unit tests: secondary emits `allow-notify` with pod IP + transfer address; primary emits none; omitted when the pod IP is unknown. `go build ./...`, `go test ./...`, and `go vet ./...` all pass. ## Rollout Config-generation change only — it takes effect after an operator image release (tag `v*`) and an argocd image bump. Not done here. Once the new ConfigMap renders, secondaries roll and begin accepting NOTIFYs, dropping dynamic-zone propagation from ~1h to seconds.
unkinben added 1 commit 2026-07-25 22:48:55 +10:00
Accept intra-cluster NOTIFY on secondaries via allow-notify
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
7771711682
Secondaries transfer catalog member and plain secondary zones from the
primary Service ClusterIP (stable across primary pod restarts), and BIND
derives a zone's implicit allow-notify from its primaries list. But the
primary pod's NOTIFYs egress with its *pod* IP as source — k8s Services
NAT only the inbound direction — so BIND refuses them as "refused notify
from non-primary" and replication falls back to the SOA refresh timer, a
1-hour propagation delay on every dynamic zone (external-dns RFC2136 and
dns-updater nsupdates alike).

Render an options-scope allow-notify on secondaries covering the primary
pod IP (and the transfer address, since an explicit allow-notify replaces
the primaries-derived default). The cluster controller resolves the
primary pod IP the same way it already does for seeding/also-notify, and
the existing Pod watch re-renders the ConfigMap when the pod IP changes.
benvin merged commit 671c43b05b into main 2026-07-25 22:54:18 +10:00
benvin deleted branch benvin/allow-notify-intra-cluster 2026-07-25 22:54:18 +10:00
Sign in to join this conversation.