# ============================================================================= # L2 BEHAVIORAL — native-OIDC SSO via the central IdP (R-IDP-01) [RUNNABLE] # Spec: docs/15 identity-and-access; ADR-0014. Impl: Grafana as a Keycloak OIDC client # (platform/monitoring/kube-prometheus-stack.yaml auth.generic_oauth) — the self-contained SSO proof. # # ENVIRONMENT-AGNOSTIC (R-ENV-02): the expected IdP host is built from the ${BASE_DOMAIN} of the # CLUSTER UNDER TEST, read from flux-system/cluster-vars (the ConfigMap Flux substitutes from) by # a test-level chainsaw binding — so this asserts each cluster redirects to ITS OWN Keycloak, which # is exactly what would catch a cross-environment OIDC issuer left pointing at the other cluster. # # GIVEN Grafana is deployed and configured as an OIDC client of the Keycloak `homelab` realm, # WHEN an unauthenticated browser starts the OIDC login (GET /login/generic_oauth) # -> THEN Grafana redirects (302) to the CENTRAL IdP's authorize endpoint on # keycloak.${BASE_DOMAIN}/realms/homelab — proving SSO is driven by the one IdP # (R-IDP-01: central Identity Provider as the single user directory). # # Goes GREEN once the grafana-oidc client-secret hand-off is filled and Keycloak + Grafana are up. # chainsaw v0.2.12: Job + status assert; no test-level finally. # ============================================================================= apiVersion: chainsaw.kyverno.io/v1alpha1 kind: Test metadata: name: oidc-sso-grafana labels: conformance.home.k3s/R-IDP-01: "true" annotations: conformance.home.k3s/requirements: "R-IDP-01" conformance.home.k3s/layer: "L2" conformance.home.k3s/safety-critical: "false" conformance.home.k3s/executable: "true" conformance.home.k3s/intent: >- Grafana's OIDC login redirects to the central Keycloak realm — SSO is driven by the one IdP. spec: timeouts: apply: 60s assert: 3m exec: 3m # -- R-ENV-02: the IdP hostname suffix is READ FROM THE CLUSTER UNDER TEST # (flux-system/cluster-vars — the ConfigMap Flux substitutes ${BASE_DOMAIN} from). bindings: - name: baseDomain value: (x_k8s_get($client, 'v1', 'ConfigMap', 'flux-system', 'cluster-vars').data.BASE_DOMAIN) steps: - name: given-grafana-is-an-oidc-client try: - assert: resource: apiVersion: apps/v1 kind: Deployment metadata: name: kube-prometheus-stack-grafana namespace: monitoring status: availableReplicas: 1 - name: then-login-redirects-to-keycloak-realm try: - apply: resource: apiVersion: batch/v1 kind: Job metadata: name: grafana-oidc-probe spec: backoffLimit: 2 activeDeadlineSeconds: 150 template: spec: restartPolicy: Never containers: - name: probe image: busybox:1.36.1 env: - name: BASE_DOMAIN value: ($baseDomain) command: - sh - -c - | set -u svc="http://kube-prometheus-stack-grafana.monitoring.svc.cluster.local" # busybox wget has no --max-redirect; -S dumps every response header # (incl. the initial 302 Location to the IdP) before following, so the # grep below still matches the Keycloak authorize URL. (Grafana verified # live: 302 -> keycloak.${BASE_DOMAIN}/realms/homelab/.../auth.) idp="keycloak.${BASE_DOMAIN}/realms/homelab" out="$(wget -S -O /dev/null "$svc/login/generic_oauth" 2>&1 || true)" echo "$out" if echo "$out" | grep -qiF "$idp"; then echo "GRAFANA_OIDC_REDIRECT_OK -> ${idp}" else echo "FAIL: Grafana did not redirect to ${idp} (this cluster's Keycloak homelab realm)"; exit 1 fi - assert: resource: apiVersion: batch/v1 kind: Job metadata: name: grafana-oidc-probe status: succeeded: 1 finally: - script: content: 'echo "teardown: chainsaw deletes the per-test namespace (Job included)"'