From 330c99eb8bddb9ca5ff3a38adf32fc1153fb7ce5 Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Fri, 28 Aug 2026 13:01:37 -0500 Subject: [PATCH] feat(keycloak): enable offline_access for kubernetes OIDC client Add the offline_access optional client scope to the `kubernetes` kubelogin client so the operator's kubectl can request long-lived OFFLINE refresh tokens (--oidc-extra-scope=offline_access) instead of being forced to re-auth on the realm's ONLINE session timeouts (ssoSessionIdleTimeout 30m / ssoSessionMaxLifespan 10h). Offline tokens honour offlineSessionIdleTimeout 30d with no hard max lifespan. - keycloak-realm-import.yaml: add optionalClientScopes: [offline_access] to the kubernetes client so a fresh realm import reproduces it. - k8s-oidc-rbac.yaml: document the kubeconfig exec-arg (--oidc-extra-scope=offline_access) + clear-cache/re-login steps. Realm session timeouts unchanged; scope is optional (opt-in) and client-only. Applied live via kcadm (reversible). Co-Authored-By: Claude Opus 4.8 --- platform/keycloak/k8s-oidc-rbac.yaml | 22 ++++++++++++++++++++ platform/keycloak/keycloak-realm-import.yaml | 8 +++++++ 2 files changed, 30 insertions(+) diff --git a/platform/keycloak/k8s-oidc-rbac.yaml b/platform/keycloak/k8s-oidc-rbac.yaml index 22ee422..d55184d 100644 --- a/platform/keycloak/k8s-oidc-rbac.yaml +++ b/platform/keycloak/k8s-oidc-rbac.yaml @@ -11,6 +11,28 @@ # # BREAK-GLASS: the cert-based admin in the bootstrap kubeconfig is unaffected by any of this and # keeps full access even if Keycloak/OIDC is down — do not remove it. +# +# OPERATOR KUBECONFIG (kubelogin / int128 oidc-login) — the `keycloak-oidc` user exec block: +# users: +# - name: keycloak-oidc +# user: +# exec: +# apiVersion: client.authentication.k8s.io/v1beta1 +# command: kubectl +# args: +# - oidc-login +# - get-token +# - --oidc-issuer-url=https://keycloak.home.keithjackson.dev/realms/homelab +# - --oidc-client-id=kubernetes +# - --oidc-extra-scope=offline_access # long-lived OFFLINE token — avoids frequent re-auth +# The `--oidc-extra-scope=offline_access` arg makes kubelogin request an OFFLINE refresh token instead +# of an ONLINE one, so the CLI is not forced to re-login on the realm's ONLINE session timeouts +# (ssoSessionIdleTimeout 30m / ssoSessionMaxLifespan 10h). Offline tokens honour offlineSessionIdleTimeout +# (30 days idle) with offlineSessionMaxLifespanEnabled=false (no hard cap). Backed by the `offline_access` +# OPTIONAL client scope on the `kubernetes` client (see keycloak-realm-import.yaml). After adding the arg, +# clear the cached ONLINE token once so the next login mints an offline one: +# kubectl oidc-login clean # (or: rm -f ~/.kube/cache/oidc-login/*) +# kubectl get nodes # re-login once to obtain the 30-day offline token apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: diff --git a/platform/keycloak/keycloak-realm-import.yaml b/platform/keycloak/keycloak-realm-import.yaml index 9cd920d..5c5bad4 100644 --- a/platform/keycloak/keycloak-realm-import.yaml +++ b/platform/keycloak/keycloak-realm-import.yaml @@ -151,6 +151,14 @@ spec: - email - profile - roles + # offline_access (optional): lets kubelogin request a long-lived OFFLINE refresh token via + # `--oidc-extra-scope=offline_access`, so the operator's kubectl isn't forced to re-auth on the + # ONLINE session idle/max timeouts (ssoSessionIdleTimeout 30m / ssoSessionMaxLifespan 10h). + # Offline tokens honour offlineSessionIdleTimeout (30d) with offlineSessionMaxLifespanEnabled=false + # (no hard cap) — see k8s-oidc-rbac.yaml header for the kubeconfig exec-arg. Optional (not default) + # so it is opt-in per the client's request scopes; realm session timeouts stay unchanged. + optionalClientScopes: + - offline_access protocolMappers: - name: groups protocol: openid-connect