# 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 `.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 `.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 `.home.keithjackson.dev` to the internal Gateway VIP (split-horizon). This is the majority of services. - **`public` services** need a public record for `.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 `.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).