Files
home-network-k3s/docs/09-dns-and-ingress.md
T
jakman85andClaude Opus 4.8 cde2f2330c Resolve OQ-4: scoped Cloudflare API token for DNS-01 + ExternalDNS
Operator chose Option A. cert-manager solves ACME DNS-01 and ExternalDNS
manages *.home.keithjackson.dev records via a scoped Cloudflare token
(Zone.DNS.Edit, keithjackson.dev), stored SOPS-encrypted. Operator provides
the token once (new prereq T-2.4p); T-2.4/T-2.5 unblocked. Only OQ-BKP
(off-cluster DR backup target, Phase 4) remains open.

Updated: docs 00,08,09; task board.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:02:59 -05:00

86 lines
4.9 KiB
Markdown

# 09 — DNS & Ingress
Serves: **R-DNS-01/02/03**, **R-C2-01**, works with **R-TLS-***. Related:
[ADR-0002 (Cilium/Gateway API)](../decisions/adr-0002-cni-cilium.md).
## Naming model
- All services live at `<service>.home.keithjackson.dev` (e.g. `plex.home.keithjackson.dev`).
- The **root** `home.keithjackson.dev` serves the **Command & Control UI** ([12](12-command-center-ui.md)).
- Public apex `*.keithjackson.dev` resolves to the network's public IP; `*.home.keithjackson.dev`
is the platform's zone.
## DNS automation (R-DNS-02)
- **ExternalDNS** watches Gateway/HTTPRoute (and Service) resources and manages records in the
authoritative DNS provider (Cloudflare, per OQ-4) automatically — a new service's DNS appears from
its declared intent, no manual record editing.
- Records for `*.home.keithjackson.dev` are created/updated/removed as services come and go.
## Split-horizon resolution (R-DNS-03)
Internal and external answers differ:
- **External** (`*.home.keithjackson.dev` from the internet): resolves to the public IP; reaches
the edge router, which forwards `public`-class services to the in-cluster Gateway.
- **Internal** (LAN clients): resolves `<service>.home.keithjackson.dev` to the **internal Gateway
address** directly, avoiding hairpinning through the public IP.
- **PiHole** (itself an onboarded service) provides internal LAN DNS and the split-horizon answers
for `.home.keithjackson.dev`; the trusted Let's Encrypt cert is served either way ([08](08-tls-and-certificates.md)).
> DNS is thus both a platform capability *and* a hosted service. Bootstrapping order matters: the
> platform needs working DNS before PiHole is fully managed — handled by the bring-up sequence in
> [`roadmap/phases.md`](../roadmap/phases.md).
## Ingress (north-south) via Gateway API
- **Gateway API implemented by Cilium** is the ingress layer (no separate ingress controller;
reuses the CNI, [ADR-0002](../decisions/adr-0002-cni-cilium.md)).
- A shared **Gateway** defines HTTPS listeners on `*.home.keithjackson.dev` terminating TLS with the
cert-manager cert ([08](08-tls-and-certificates.md)).
- Each service contributes an **HTTPRoute** mapping its hostname to its Service — rendered from the
ServiceDefinition, not hand-authored.
- **Exposure class** is declared per service:
- `internal` — reachable only on the LAN (no edge port-forward; internal DNS only).
- `public` — additionally reachable from the internet via the edge router forwarding 80/443 to the
Gateway.
## Edge integration (single SOHO router — OQ-1 resolved)
- The environment has **one SOHO edge router**: no VLANs, no VPN, no services, no API/IaC. Its only
ingress job is **manual port-forwarding of 80/443 (and specific service ports, e.g. Plex 32400,
Minecraft 25565) to the Gateway VIP** for `public` services. This is a documented manual edge step
(the SOHO device has no automation) — the analogue of the old pfSense manual-config exception.
Confirm the router can port-forward (basic NAT; almost all can).
- No multi-WAN inbound rules — single WAN ([03](03-networking-isolation-and-egress.md)).
- `internal` services are never port-forwarded; they rely on internal DNS + Gateway on the LAN.
- **Ingress VIP mechanism — resolved: Cilium L2 announcement (ARP/NDP)** on the flat LAN. BGP is not
available on a SOHO router, and a single flat subnet makes L2 announcement the natural choice.
## DNS record depth (note for `*.home.keithjackson.dev`)
A wildcard `*.keithjackson.dev` matches only **one** label deep — it covers `home.keithjackson.dev`
but **not** `plex.home.keithjackson.dev`. Consequences:
- **`internal` services need no public record** — PiHole resolves `<svc>.home.keithjackson.dev` to
the internal Gateway VIP (split-horizon). This is the majority of services.
- **`public` services** need a public record for `<svc>.home.keithjackson.dev`. Per OQ-4 (resolved),
**ExternalDNS manages these automatically** via the scoped Cloudflare API token — no manual record
editing (see [08](08-tls-and-certificates.md)).
## Interaction with default-deny
Ingress opens **north-south** paths to the specific service; it does **not** relax **east-west**
default-deny ([03](03-networking-isolation-and-egress.md)). A service being publicly reachable does
not let other in-cluster services reach it unless an explicit allow rule exists.
## Acceptance gates
- A new service's `<service>.home.keithjackson.dev` record appears automatically and routes to it.
- `internal` services are unreachable from the public internet; `public` services are reachable and
TLS-valid.
- Internal clients resolve to the internal Gateway (no public hairpin) and get a trusted cert.
- The root `home.keithjackson.dev` serves the C2 UI.
## Resolved
- **OQ-1** — single SOHO edge (manual port-forward only); ingress VIP via **Cilium L2 announcement**.
- **OQ-4** — scoped **Cloudflare API token** drives ExternalDNS (records) and cert-manager DNS-01
(certs) automatically; see [08](08-tls-and-certificates.md).