Research: short-lived id_token refresh for static cluster credentials #38
Labels
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lab/iac#38
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The original problem
Kanidm id_tokens are short-lived, but the credential stores the cluster tooling uses are static — Argo CD cluster secrets, Sveltos's stored kubeconfig Secrets, and Vault's Kubernetes auth
token_reviewer_jwt. The chain #35/#36/#37/#34 exists so Kanidm is the central identity store for all actors (humans, agents, machines), and every tool that trusts Kanidm authenticates as a Kanidm service account (ADR-0006 machine-as-principal).Candidate mechanisms (evaluated)
execProviderConfig(argoproj/argo-cd#4600) and Sveltos supports exec providers (self-refreshing the stored Secret). Superseded — a kubeconfig carrying a static token pointed at the k8s-hub needs no exec at all.token_reviewer_jwtsingle-writer (ignore_changes) hazard.backendAuth.oauthTokenExchange; now the k8s-hub authz-bridge on Cilium).DECISION — Golden Path: per-request token exchange at the gateway (agentgateway → Cilium k8s-hub, 2026-09)
Every actor (human or machine) holds a single Kanidm identity; no stored short-lived tokens exist anywhere. For machines this is a Kanidm service-account API token — the only long-lived credential, held by the actor itself, never replicated. All access to Kanidm-trusting lab services flows through the k8s-hub, a TLS-terminating Gateway API (Cilium) proxy whose authz-bridge performs the RFC 8693 exchange per request:
The gateway caches the exchanged token only until Kanidm's
expires_in(900s). The stale window is gone — a fresh token is minted on demand; the subject token is never rotated or written into consumer stores.Per-consumer shape:
server: https://<cluster>.k8s.animeteamspeak.moe,config.bearerToken: <argocd-<cluster> SA API token>, no CA (Let's Encrypt, public trust)server: https://<cluster>.k8s.animeteamspeak.moe,token: <sveltos-<cluster> SA API token>token_reviewer_jwtis thevaultKanidm SA API token; TokenReview calls go through the k8s-hub apiserver route (exchanged per request, audience = the cluster's apiserver client)agentgateway config essentials (v1.4.1) — historical; the gateway migrated to the Cilium Gateway API k8s-hub authz-bridge in 2026-09 (per-cluster
OauthClient/TokenExchangePolicy/OidcPolicyin theoauth.animeteamspeak.moe/providers.animeteamspeak.moegroups, scopesopenid groups_name, exchanged id_token forwarded, Let's Encrypt TLS):host: auth.animeteamspeak.moe:443,path: /oauth2/token,policies: { backendTLS: {} }is required — TLS does not auto-enable on :443 despite the docs (without it the gateway connects plaintext and the ingress 404s).clientAuth: { clientId: <service>, method: clientSecretPost }— public client, no secret, matcheskanidm-login.py.audiences: [<service-client-id>],scopes: [openid, groups]. The gateway forwards the exchangedaccess_token(RFC 9068at+jwt) upstream.Validated facts (live, kanidm/server 1.10.4 + agentgateway v1.4.1):
access_token(at+jwt,aud=<client>),id_token,refresh_token,expires_in: 900,token_type: Bearer.clientSecretPost).groupsclaim requires the client's scope map to grantgroupsto the SA's groups (ADR-0005). SAs whose groups lack it getaccess_denied(e.g.kanidmclaimtestinginargocd_users: available scopes{argocd_role, email, openid}).issued_token_type: "AccessToken"(bare enum name) instead of the RFC 8693 URN; agentgateway strictly validated and rejected it. Fixed with patched Kanidm1.11.0-itt-fixon portable; the direct exchange now returns the URN and the agentgateway E2E returns 200. Not upstreamed.Deliverables (status)
expires_in(900s). No stale window.token_reviewer_jwt: thevaultKanidm SA API token through the gateway, same path even in-cluster (see #29).issued_token_typeconformance — #39 closed (patched Kanidm1.11.0-itt-fix, exchange verified 200).Linked
token_reviewer_jwtresolved here)