--- # Shared Gateway — HTTPS listeners on *.home.keithjackson.dev, TLS terminated with the wildcard cert # (docs/09). Implemented by Cilium (GatewayClass `cilium`). The ingress VIP is announced via Cilium # L2 announcement on the flat LAN (docs/09, OQ-1 resolved). Phase 0 STUB. apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: shared namespace: gateway annotations: # PUBLIC DNS TARGET = THE CLOUDFLARE TUNNEL (2026-11-08, operator decision; replaces the # 2026-10-03 `wan.${BASE_DOMAIN}` target). external-dns v0.23.0's gateway-httproute source reads # `target` from the GATEWAY only (source/gateway.go: `overrides: annotations. # TargetsFromTargetAnnotation(gw.Annotations)`) — never from an HTTPRoute — so this one value moves # EVERY published route on this Gateway at once. Each published `.${BASE_DOMAIN}` (and the # apex) becomes a CNAME to `.cfargotunnel.com`, and platform/externaldns/helmrelease.yaml # sets `--cloudflare-proxied` so the CNAME is orange-clouded: public clients resolve Cloudflare # anycast, Cloudflare terminates edge TLS (Total TLS) and hands the request to the in-cluster # cloudflared connectors (platform/cloudflared), which forward to this Gateway's ClusterIP. No # origin address of ours appears in public DNS, and no inbound WAN port is needed for HTTP(S). # # THE UUID IS A LITERAL, AND IT MUST EQUAL `tunnel:` IN platform/cloudflared/config.yaml. A CNAME # to any other .cfargotunnel.com is a dead name (Cloudflare answers 1016). That equality is # asserted on every gate run by conformance/tests/l0-static/internal-services-publish-no-public-dns # (assertion 9a). A literal rather than a cluster-var keeps this change off clusters/**. # # Scope: every route on this Gateway that publishes at all. `internal` services (and the # operator-designated LAN/VPN-only names: plex, headlamp) stay unpublished via their # `external-dns.alpha.kubernetes.io/controller: none` route annotation, which external-dns # applies BEFORE it reads this target. Those same names map to `http_status:404` in the tunnel's # ingress rules, so even a hand-made record cannot reach them through the tunnel. # # In-cluster clients are unaffected: CoreDNS answers ${BASE_DOMAIN} with ${GATEWAY_VIP} directly # (split-horizon). LAN clients use PiHole's EXPLICIT list (catalog/pihole.yaml, since 2026-11-09): # LAN-only names and gitea/nextcloud/keycloak/auth get ${GATEWAY_VIP}; every other published # name takes this public path through Cloudflare, from the LAN as from anywhere. # # `wan.${BASE_DOMAIN}` (platform/externaldns/ddns.yaml) is NO LONGER a target of anything here; # it stays only as the WireGuard udp/51820 endpoint. external-dns.alpha.kubernetes.io/target: "fc81938b-5855-47f1-8a27-6286f56dda93.cfargotunnel.com" spec: gatewayClassName: cilium listeners: - name: https protocol: HTTPS port: 443 hostname: "*.${BASE_DOMAIN}" tls: mode: Terminate certificateRefs: - kind: Secret name: wildcard-home-tls # produced by wildcard-certificate.yaml allowedRoutes: namespaces: from: Selector selector: matchLabels: # App namespaces opt in to attach HTTPRoutes here (WS-APPS renders the label + # ReferenceGrant from ServiceDefinition `exposure`). docs/09. gateway-access: "shared" - name: http-redirect protocol: HTTP port: 80 hostname: "*.${BASE_DOMAIN}" allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: "shared" # APEX listeners. The wildcard listeners above match ONLY subdomains: `*.home.keithjackson.dev` # does NOT intersect the bare apex `home.keithjackson.dev`, so an HTTPRoute for the apex host # (the Homepage landing dashboard, clusters/base/homepage.yaml) would bind to NO listener and # never serve. These two listeners give the apex its own hostname. TLS is the SAME wildcard cert: # `home.keithjackson.dev` is already a SAN on wildcard-certificate.yaml, so `wildcard-home-tls` # terminates the apex too — no extra Certificate needed. Same gateway-access=shared opt-in. - name: https-apex protocol: HTTPS port: 443 hostname: "${BASE_DOMAIN}" tls: mode: Terminate certificateRefs: - kind: Secret name: wildcard-home-tls # apex is a SAN on the wildcard cert (wildcard-certificate.yaml) allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: "shared" - name: http-apex-redirect protocol: HTTP port: 80 hostname: "${BASE_DOMAIN}" allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: "shared" # TODO(WS-NET, Phase 2): pin the L2-announced VIP / IP pool; add HTTP->HTTPS redirect HTTPRoute; # confirm cross-namespace ReferenceGrant pattern with WS-APPS. Ingress opens NORTH-SOUTH only — # it does not relax east-west default-deny (docs/09).