Skip to content

Use single-issuer structured AuthenticationConfiguration for k8s OIDC

Date: 2026-07-01 Status: Accepted Decision: hl-idws Deciders: Sean Brandt, Claude

k3s currently trusts Authentik for kubectl OIDC via legacy --oidc-* apiserver flags. The Keycloak-side kubernetes public PKCE client + k8s-{admins,developers,viewers} groups exist and are verified (hl-5g05.12). Grounding against the LIVE cluster changed the original assumptions: it runs k8s v1.36 (structured AuthenticationConfiguration is GA at apiserver.config.k8s.io/v1 since 1.34 — not the v1beta1 assumed earlier); only the operator uses k8s OIDC (3-user trust model); and the existing oidc:k8s-* ClusterRoleBindings already match the Keycloak group names, so the cutover needs zero RBAC changes. This supersedes hl-9rhj, which chose a dual-issuer transition.

Migrate the k3s apiserver to a file-based AuthenticationConfiguration (apiserver.config.k8s.io/v1) with a SINGLE JWT authenticator trusting only Keycloak (https://id.fzymgc.house/realms/fzymgc, audiences [kubernetes], username claim email + prefix oidc:, groups claim groups + prefix oidc:). Remove all --oidc-* flags atomically in the same render (they are mutually exclusive with --authentication-config). Cut straight over — no transitional dual-issuer trust.

  • The dual-issuer rationale of hl-9rhj (incremental per-user migration without invalidating sessions) does not apply: there is effectively one k8s-OIDC user, so there is no fleet of sessions to migrate independently.
  • The config file is reversible (revert + rolling restart) and a non-OIDC client-cert break-glass kubeconfig exists on every server, so an atomic cutover has a safe rollback path — the concern that rejected the atomic flag switch in hl-9rhj is mitigated here.
  • Single issuer is materially simpler and lower-risk than maintaining two concurrent authenticators.
  • apiserver.config.k8s.io/v1 (GA since 1.34) is the correct API for the live 1.36 cluster; the earlier v1beta1 assumption is outdated.
  • Existing oidc:k8s-* RBAC already matches the Keycloak groups (with the retained oidc: prefix), so no binding churn.
  • Single-issuer structured AuthenticationConfiguration (chosen): one Keycloak JWT authenticator; atomic --oidc-* removal; reversible via config revert + break-glass; zero RBAC change.
  • Dual-issuer structured AuthenticationConfiguration (rejected; supersedes hl-9rhj): trust Authentik + Keycloak concurrently, drop Authentik at Phase 5. Over-engineered for a single operator; adds a second trust anchor + more complex rollout for a migration-continuity benefit that a one-user cluster does not need.
  • Legacy --oidc-* issuer repoint (rejected): keep the deprecated flag mechanism, just point it at Keycloak. Lowest churn and viable in 1.36, but does not modernize the auth mechanism and leaves the cluster on a deprecated path.
  • Positive: simpler config, one trust anchor, correct GA API, zero RBAC change, reversible cutover with break-glass.
  • Negative: a brief OIDC-down window during the ForceNew rolling restart (covered by client-cert break-glass); no transitional grace period for any second k8s-OIDC identity (none exists).
  • Neutral: Authentik kubernetes provider teardown becomes a separate follow-up once green; the k3s rollout is gated by an R2-specific issuer-discovery health check (non-crashing failure mode).