7 more component READMEs got role-in-Catalyst banners: - vpa, keda, reloader → per-host-cluster scaling/ops layer (§3.4). Reloader specifically calls out its role in Catalyst's secret- rotation flow (rolling deploy on K8s Secret hash change). - external-dns → per-host-cluster DNS-sync (§3.1); pairs with k8gb for the GSLB zone separation. - coraza → DMZ-block WAF on every host cluster (§3.1). - crossplane → per-Sovereign on the management cluster (§3.2); banner explicitly emphasizes the agreed "never a user-facing surface" rule (Users don't write Compositions in Application configs; Blueprint authors and advanced contributors do). Cross- references the no-fourth-surface clause in ARCHITECTURE §4/§7 and the Crossplane Composition section in BLUEPRINT-AUTHORING §8. - opentofu → repositioned as Phase-0-only, runs on `catalyst- provisioner` only, NOT installed on host clusters at runtime. opentofu drift fixes (uncovered by line-by-line read): - Section 5 line 182: "Bootstrap Wizard prompts for cloud credentials" → "Catalyst Bootstrap (Phase 0) prompts for cloud credentials" (banned term). - Same section line 186: "ESO PushSecrets sync to both regional OpenBao instances" — the active-active drift Pass 7 corrected elsewhere, still here. Replaced with "writes go to the primary OpenBao region only; replicas pick up via async perf replication". VALIDATION-LOG: Pass 10 entry added. Refs #37 |
||
|---|---|---|
| .. | ||
| README.md | ||
ExternalDNS
DNS synchronization (registers/deletes records via cloud DNS APIs). Per-host-cluster infrastructure (see docs/PLATFORM-TECH-STACK.md §3.1) — runs on every host cluster, primarily on the DMZ block. Pairs with k8gb (which serves the GSLB zone authoritatively) — ExternalDNS handles non-GSLB records and the parent zone delegation.
Status: Accepted | Updated: 2026-04-27
Overview
ExternalDNS synchronizes Kubernetes resources (Gateway, Service, Ingress) with external DNS providers, enabling automatic DNS record management.
Architecture
flowchart TB
subgraph K8s["Kubernetes"]
GW[Gateway API]
Svc[Services]
ExtDNS[ExternalDNS]
end
subgraph DNS["DNS Providers"]
CF[Cloudflare]
R53[Route53]
HDNS[Hetzner DNS]
end
subgraph GSLB["Global LB"]
k8gb[k8gb]
end
GW --> ExtDNS
Svc --> ExtDNS
ExtDNS --> CF
ExtDNS --> R53
ExtDNS --> HDNS
k8gb --> ExtDNS
Supported DNS Providers
| Provider | Availability |
|---|---|
| Cloudflare | Always |
| Hetzner DNS | If Hetzner chosen |
| AWS Route53 | If AWS chosen |
| GCP Cloud DNS | If GCP chosen |
| Azure DNS | If Azure chosen |
Configuration
ExternalDNS Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: external-dns
spec:
template:
spec:
containers:
- name: external-dns
image: registry.k8s.io/external-dns/external-dns:v0.14.0
args:
- --source=gateway-httproute
- --source=gateway-grpcroute
- --source=service
- --provider=cloudflare
- --cloudflare-proxied
- --txt-owner-id=openova
- --txt-prefix=_externaldns.
env:
- name: CF_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-credentials
key: api-token
k8gb Integration
ExternalDNS works with k8gb for GSLB:
flowchart LR
subgraph Region1["Region 1"]
App1[Application]
k8gb1[k8gb]
end
subgraph Region2["Region 2"]
App2[Application]
k8gb2[k8gb]
end
subgraph DNS
ExtDNS[ExternalDNS]
NS[NS Records]
end
k8gb1 -->|"Delegate"| ExtDNS
k8gb2 -->|"Delegate"| ExtDNS
ExtDNS --> NS
k8gb delegates a subdomain (e.g., gslb.<domain>) and handles health-based DNS responses within that zone.
Record Types
| Source | Record Type | Example |
|---|---|---|
| Gateway | A/CNAME | api.<domain> |
| Service (LoadBalancer) | A | svc.<domain> |
| k8gb GslbHttpRoute | NS delegation | gslb.<domain> |
TXT Registry
ExternalDNS uses TXT records to track ownership:
_externaldns.api.<domain> TXT "heritage=external-dns,external-dns/owner=openova"
This prevents ExternalDNS from modifying records it doesn't own.
Part of OpenOva