skip to content

When a Kubernetes API server trusts an OIDC identity provider, what does the API server verify and what does a kubeconfig exec credential plugin do?

level: middleimportance: should knowfreq 52%

answer

  1. server verifies, client fetches
  2. issuer, audience, claim mappings
  3. prefix avoids name clashes
  4. ExecCredential on stdout
  5. re-run on expiry

basics

~20 s

The API server only verifies ID tokens from a configured issuer and maps claims to a username and groups. A kubeconfig exec plugin performs the login, caches the token and gives it to kubectl, which sends it as a bearer token.

solid answer

~40 s

The API server is a verifier: via `--oidc-issuer-url`, `--oidc-client-id` and the claim flags, or an `AuthenticationConfiguration` file passed with `--authentication-config`, it trusts one or more issuers, checks each ID token's signature and audience, and maps a username claim and a groups claim, usually with a prefix like `oidc:`, into the request identity. It never shows a login page. On the client, the kubeconfig `users` entry has an `exec` block; client-go runs that plugin, reads an `ExecCredential` from stdout, caches the token until its `expirationTimestamp`, and re-runs the plugin when it expires or after a 401. The plugin owns the browser flow and refresh tokens. RBAC then binds the prefixed groups.

code

yaml · 14 lines
yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AuthenticationConfiguration
jwt:
- issuer:
    url: https://sso.example.com
    audiences:
    - kubectl-spot-mix-38
  claimMappings:
    username:
      claim: email
      prefix: "oidc:"
    groups:
      claim: groups
      prefix: "oidc:"

go deeper

for a junior

Remember that kubectl gets its token from a plugin and the API server just checks it and reads the username and groups.

for a middle

Explain issuer and audience checks, claim mappings and prefixes, the ExecCredential contract, and when client-go re-runs the plugin.

for a senior

Discuss token lifetime as the effective revocation delay, prefixed group bindings, identity-provider outages, and why the structured file's reload and multiple issuers matter operationally.

for a principal

Weigh putting all human identity in one identity provider against its availability risk, and decide how group ownership and break-glass access are governed.

## Two halves that never talk to each other directly Single sign-on for `kubectl` is split between the **client** and the **API server**, and neither one runs a browser login on behalf of the other: - The **API server** is only a **token verifier**. It is told which OpenID Connect issuer to trust and how to turn a verified ID token's claims into a Kubernetes username and groups. It never redirects anyone to a login page. - `kubectl` does not perform the OIDC login flow itself either. A kubeconfig `users` entry points at an **exec credential plugin**, an external program that obtains an ID token from the identity provider (often by opening a browser), caches it, and hands it to `kubectl`, which sends it as `Authorization: Bearer <token>`. The OIDC protocol, token format and claim semantics belong to the OIDC and JWT topics; this answer covers only the Kubernetes side of each half. ## The API server half There are two ways to configure the verifier, and they are **mutually exclusive** when the file configures a JWT authenticator: | Style | Where | Main settings | |---|---|---| | Flags | kube-apiserver command line | `--oidc-issuer-url`, `--oidc-client-id`, `--oidc-username-claim`, `--oidc-username-prefix`, `--oidc-groups-claim`, `--oidc-groups-prefix`, `--oidc-ca-file` | | Structured file | `--authentication-config` pointing at an `AuthenticationConfiguration` (`apiserver.config.k8s.io/v1`) | a `jwt` list; each entry has `issuer.url`, `issuer.audiences`, `claimMappings.username`, `claimMappings.groups`, optional CEL `claimValidationRules` | What the mapping settings do: - The **username claim** defaults to `sub` with the flags. With the flags, any username claim other than `email` is prefixed with the issuer URL unless you set a prefix yourself (`-` disables prefixing). - The **groups claim** names a claim holding a string or list of strings; each value becomes a group, which is what RBAC bindings should target. - A **prefix** such as `oidc:` keeps identity-provider names from colliding with certificate users or `system:` names. In the structured file a prefix is **required** whenever `claim` is set, though it may be the empty string. The structured file adds what the flags cannot: **several issuers** at once, CEL-based claim validation and mapping, and **reloading** when the file changes, without restarting the API server. ## The client half: exec credential plugins The kubeconfig `exec` block names a command, its arguments, `apiVersion: client.authentication.k8s.io/v1`, and `interactiveMode` (`Never`, `IfAvailable` or `Always`; required for the `v1` API version). client-go then: 1. Runs the command and reads an `ExecCredential` object from its standard output, whose `status` carries a `token` (or `clientCertificateData` and `clientKeyData`) and an optional `expirationTimestamp`. 2. Caches that credential and reuses it for every request until `expirationTimestamp` passes, then runs the plugin again. 3. On an HTTP 401 response, runs the plugin again for later requests. The request that failed is not replayed. 4. Passes cluster details to the plugin in the `KUBERNETES_EXEC_INFO` environment variable when `provideClusterInfo: true`. The plugin, not `kubectl`, owns refresh tokens and browser pop-ups. That is why the same kubeconfig works for any identity provider. ## A worked example On the 38-node cluster that runs the fraud-rules engine, an engineer in the identity provider's `fraud-rules-oncall` group logs in: - The plugin returns an ID token that expires in 5 minutes. - The API server verifies it against the configured issuer and audience and derives username `oidc:[email protected]` with group `oidc:fraud-rules-oncall`. - A RoleBinding in the `fraud-rules` namespace names the group `oidc:fraud-rules-oncall`. - Partway through a 13-minute `kubectl drain` of a spot node, the token expires; client-go re-runs the plugin, which silently uses its refresh token, and the drain continues with a fresh token. ## Checking the result Two quick checks confirm the wiring before anyone relies on it: - `kubectl auth whoami` with the SSO kubeconfig prints the username and groups the API server derived. If the groups list lacks the prefixed group, the groups claim name or the token's contents are wrong, not RBAC. - A 401 response means the token was rejected at authentication (wrong issuer, audience or expiry); a 403 means it authenticated and RBAC refused it. ## Limits to design around - The API server only checks the token it is given. Removing someone from the identity provider takes effect when their current token expires, so keep ID token lifetimes short. - Group changes also apply only on the next token. - If the identity provider is unreachable, new logins fail, so a separate break-glass credential is needed.

  • Why set a username and groups prefix such as oidc: rather than using the raw claim values?
    Without a prefix, an identity-provider group called `system:masters` or a user called `admin` would collide with names that other authenticators or built-in bindings already use. A prefix keeps every OIDC identity in its own namespace, so RBAC bindings clearly target identity-provider principals. With the flags, non-email usernames are prefixed with the issuer URL by default; in the structured file a prefix must be set whenever `claim` is set.
  • What must be true for an exec plugin's output to be accepted by kubectl?
    The plugin must print an `ExecCredential` to stdout in the same `client.authentication.k8s.io` version that the kubeconfig requested, with `status.token` or a client certificate and key. For the `v1` API version the kubeconfig must also set `interactiveMode`. If the plugin needs a terminal but kubectl has no usable stdin, `interactiveMode: Always` makes the call fail instead of hanging.

saying these in an interview costs you the question

  • The API server redirects kubectl users to the identity provider's login page.
  • kubectl has the OIDC browser flow built in, so no plugin is needed.
  • Removing a user from the identity provider cuts their cluster access immediately.
  • Groups for OIDC users must be created as objects in Kubernetes first.
  • The --oidc-* flags and an authentication-config JWT section can be combined.