Files
home-network-k3s-impl/platform/externaldns

platform/externaldns

ExternalDNS — automatic public DNS records for public services (Cloudflare).

What this stub is

Flux HelmRelease watching Gateway/HTTPRoute + Service resources, publishing <service>.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 <svc>.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      <current WAN IP>            <- 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.