Skip to content

Product1 publisher3 min readPublished

A public OIDC client with PKCE replaces the cluster certificate that outlives its owner

Self-hosted Kubernetes still hands out certificate files that keep working after their owner leaves. The fix in a CNCF walkthrough turns on one Keycloak toggle, because a confidential client only moves the shared static credential onto every laptop.

The Product Desk · Product desk

Illustration accompanying A public OIDC client with PKCE replaces the cluster certificate that outlives its owner

What happened

  • Managed cloud Kubernetes ships IAM or SSO integration, while self-hosted clusters default to a static client certificate or long-lived token that is issued once and rarely revisited.
  • The CNCF post's prescription is an OIDC provider such as Keycloak in front of the cluster, with access following an account and its group membership so changes are group edits instead of file distribution.
  • The Keycloak client it specifies is public: client authentication off, standard flow on, direct access grants off, PKCE required with S256, and redirect URIs limited to loopback addresses.
  • Three parts have to agree: kubectl with the kubelogin exec plugin, the provider issuing an ID token with username and groups, and kube-apiserver started with the three oidc flags.
  • Kubernetes has no first-class user object, so RBAC binds to the usernames and groups asserted in the token, which means the provider has to be told to include group membership.

Compiled by The Product DeskSomething wrong?How this is made

Why it matters

  • cost Whoever picks the confidential client is signing up for a fleet-wide config push every time a laptop goes missing, and the bill lands on the platform team rather than the person who lost it.
  • decision The client type is chosen once and undone everywhere, so a team that starts confidential pays a per-machine edit later to become public.
  • capability Because the API server only fetches signing keys, the control plane can validate identities without an ongoing network path to the provider, which keeps the IdP out of the request path for token checks.
  • constraint Group membership in the token is the ceiling on what RBAC can express, so the migration is not done when logins work; it is done when the groups claim arrives and a group-bound role resolves.

Someone leaves in March. The kubeconfig on their old laptop still works, because nothing in the cluster's authentication path asks whether the holder should still have access [3]. Getting it back means finding every copy of the file, and the CNCF post is candid that in practice this does not get finished [4].

That file authenticates whoever holds it, and the cluster has no view of who that is, even though teams treat it as a credential that can be revoked like one [3].

The choice that decides how the rollout ages is the client type. A confidential client issues a secret, and that secret gets pasted into the kubelogin plugin config on every machine that needs cluster access [10]. The post's description is exact: a secret distributed to everyone who uses it is a shared static credential with extra steps, and rotating it becomes a coordinated config push rather than disabling one compromised identity [11]. Put numbers on a team of thirty. One lost laptop means thirty config edits under the confidential client, against one identity disabled in Keycloak under the public one, and that ratio holds at whatever your headcount is [1].

PKCE is what makes the secret unnecessary rather than merely absent. The client generates a random value locally, sends only a hash of it to start the login, then proves possession of the original when it redeems the authorization code, so an interceptor holding the code alone cannot complete the exchange [13]. OAuth 2.1 already puts native and command-line clients in this category [12]. It is also why the redirect URI carries a wildcard port on loopback: the code comes back to a listener the plugin opens locally at login time, not to anything you host [3].

The part that will page you on Monday is the groups claim. Keycloak needs a Group Membership protocol mapper on the client scope, writing to the claim name `groups` [16]. Without it, login succeeds and the token carries a username, but no group binding matches, so the user lands with only whatever that username was granted directly [2]. The migration is verified by whether a group-bound role resolves, not by whether the browser redirect came back clean.

The post argues from the shape of the credential rather than from incident counts, which is the right register for a decision made once at install time and then inherited [17]. Sort the credentials on your own cluster path against two tests: whether one person's access can be removed without touching anyone else's machine, and whether anything secret has to exist in more than one place. The static certificate fails the first [4]. A confidential client passes the first and fails the second [10]. Both fail for the long-lived shared token that plenty of on-prem clusters actually run on [2]. Public client with PKCE is the cell that passes both, and the price is the group mapper work plus a usable browser on every machine that needs to log in [8].

What to watch

  • Whether Keycloak's defaults for a new client land on client authentication off with PKCE required, or leave both to the operator to find.
  • Whether Kubernetes distributions and installers expose --oidc-issuer-url, --oidc-client-id and --oidc-groups-claim as first-class install options.
  • Whether teams keep a break-glass certificate for provider outages, re-creating the unrevocable file they just removed.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories