# platform/externaldns **ExternalDNS** — automatic public DNS records for `public` services (Cloudflare). - **Spec:** [docs/09 — DNS & Ingress](../../../home-network-k3s/docs/09-dns-and-ingress.md) (ExternalDNS is named in [docs/01](../../../home-network-k3s/docs/01-architecture-overview.md); no dedicated ADR) - **Serves:** R-DNS-01/02/03 - **Owner:** WS-NET (token from WS-SEC) ## What this stub is Flux `HelmRelease` watching Gateway/HTTPRoute + Service resources, publishing `.home.keithjackson.dev` records in Cloudflare from declared intent (no manual record editing, R-DNS-02). ## Split-horizon note (docs/09) A wildcard `*.keithjackson.dev` matches only one label deep, so `.home.keithjackson.dev` needs its own record. **`internal` services need NO public record** — PiHole resolves them to the internal Gateway VIP (split-horizon). ExternalDNS manages **`public`** services' records only. ## Public target + in-cluster DDNS (2026-10-03) Gateway-published records are **CNAMEs to `wan.home.keithjackson.dev`**, not the Gateway VIP: ``` plex.home.keithjackson.dev CNAME wan.home.keithjackson.dev <- external-dns (Gateway `target` annotation) wan.home.keithjackson.dev A <- cloudflare-ddns (ddns.yaml), every 5m ``` - The `target` annotation is on the **shared Gateway** (`platform/gateway/gateway.yaml`), so it applies to every route that publishes at all. `internal` routes (`controller: none`) still publish nothing. - `ddns.yaml` runs `favonia/cloudflare-ddns` in this namespace with the same `cloudflare-api-token`. It detects the WAN IP through Cloudflare's trace endpoint, so the pod **must not** carry `egress-class: vpn`. - LAN and pods resolve `*.home.keithjackson.dev` to the Gateway VIP via PiHole and CoreDNS (split-horizon) and never use the public path. - **Nodes resolve through public DNS** (`node_baseline_dns_servers`), so from a node, the apiserver's OIDC issuer `keycloak.home.keithjackson.dev` resolves to the WAN IP. kubectl SSO therefore relies on the router hairpinning (NAT reflection) WAN:443 back to the Gateway. Check: `dig +short @1.1.1.1 plex.home.keithjackson.dev` → `wan.home.keithjackson.dev.` then the WAN IP; `kubectl -n external-dns logs deploy/cloudflare-ddns`. ## Secrets Cloudflare API token referenced **by name** (`cloudflare-api-token`, shared with cert-manager), **SOPS-encrypted** (WS-SEC, OQ-4). ## TODO (later phases) - **Phase 2 (WS-NET):** confirm `gateway-httproute` source; prove an auto record for a test route (R-DNS-02, Phase-2 exit gate). - **Phase 6 (WS-APPS):** PiHole provides the internal split-horizon answers as it onboards.