# ============================================================================= # L2 BEHAVIORAL — Trusted TLS (R-TLS-01, R-TLS-03) [RUNNABLE] # Catalog: docs/14 "Trusted TLS (R-TLS-01/02)" # Spec: docs/09 (Gateway API + DNS-01 wildcard), ADR-0006 (LE issuers) # # GIVEN the shared Gateway terminating TLS with the cert-manager wildcard cert, # WHEN an in-cluster probe dials it by hostname (SNI *.home.keithjackson.dev) and # verifies the chain against the SYSTEM trust store, # THEN the served cert is non-self-signed, unexpired, covers the wildcard SAN, and # chains to a PUBLICLY-TRUSTED CA root (R-TLS-01). The wildcard SAN also covers # internal-only hostnames, so internal services get the same trusted cert (R-TLS-03). # # TRUST MODEL: the cluster issues from Let's Encrypt **PRODUCTION** (ACME_CLUSTER_ISSUER # = letsencrypt-prod), whose root (ISRG Root X1) is in the system trust store — so the # probe verifies against the container's DEFAULT CA bundle (no --cacert). A clean verify # is exactly what real in-cluster OIDC clients (oauth2-proxy, Grafana) depend on. The # probe resolves the hostname via in-cluster split-horizon DNS (coredns-custom → Gateway # VIP, R-DNS-01) — the same path real clients use — so no --connect-to hack is needed. # Replaced the earlier LE-staging + embedded-root check when dev switched to LE-prod so # in-cluster SSO clients trust the cert (DECISIONS-LOG Session 7). # ============================================================================= apiVersion: chainsaw.kyverno.io/v1alpha1 kind: Test metadata: name: trusted-tls labels: conformance.home.k3s/R-TLS-01: "true" conformance.home.k3s/R-TLS-03: "true" annotations: conformance.home.k3s/requirements: "R-TLS-01, R-TLS-03" conformance.home.k3s/layer: "L2" conformance.home.k3s/safety-critical: "false" conformance.home.k3s/executable: "true" conformance.home.k3s/intent: >- The shared Gateway serves a valid, unexpired, non-self-signed wildcard cert that chains to a publicly-trusted CA root (LE production); the wildcard SAN covers internal-only hostnames too. spec: steps: - name: then-gateway-cert-chains-to-trusted-root try: - apply: resource: apiVersion: batch/v1 kind: Job metadata: name: tls-probe spec: backoffLimit: 4 activeDeadlineSeconds: 180 template: spec: restartPolicy: Never containers: - name: probe image: curlimages/curl:8.11.1 command: - sh - -c - | set -eu # Probe TWO wildcard hostnames — one "public", one "internal-only". Both # match *.home.keithjackson.dev, so a clean verify against the SYSTEM CA # bundle for BOTH proves R-TLS-01 (chains to a publicly-trusted root) AND # R-TLS-03 (the same trusted wildcard covers internal-only names). Names # resolve via in-cluster split-horizon DNS to the Gateway VIP. curl (no # --cacert, no -k) validates against the default CA bundle and exits 0 on # the gateway's 404 once TLS+hostname verify; it exits 60 if the chain or # hostname fails. TLS terminates at the gateway before HTTP routing, so no # backend is needed. for host in conformance.home.keithjackson.dev internal-only.home.keithjackson.dev; do curl -sS --retry 5 --retry-connrefused --retry-delay 3 --max-time 20 \ -o /dev/null "https://${host}/" echo "TLS_TRUSTED ${host}" done echo ALL_TRUSTED - assert: resource: apiVersion: batch/v1 kind: Job metadata: { name: tls-probe } status: { succeeded: 1 }