Gateway target -> fc81938b-...cfargotunnel.com, --cloudflare-proxied globally, L4 service publishers (unifi, crafty-mc) pinned DNS-only, Plex web UI and Headlamp disowned (LAN/VPN-only). L0 internal-services-publish-no-public-dns gains assertion (9) tying the Gateway target to the tunnel UUID, requiring cloudflare-proxied=false on every service-source publisher, and keeping the tunnel's 404 list and the unpublished set consistent. L2 dns-exposure fixture opts out of proxying. Co-Authored-By: Claude Opus 5.5 <[email protected]>
103 lines
5.0 KiB
YAML
103 lines
5.0 KiB
YAML
---
|
|
# 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 `<svc>.${BASE_DOMAIN}` (and the
|
|
# apex) becomes a CNAME to `<tunnel-uuid>.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 <uuid>.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.
|
|
#
|
|
# LAN and in-cluster clients are unaffected: PiHole and CoreDNS answer ${BASE_DOMAIN} with
|
|
# ${GATEWAY_VIP} directly (split-horizon), so they never take this public path.
|
|
#
|
|
# `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).
|