Lesson 04 of 15 · Identity & Access
OIDC login for kubectl
Replace shared admin kubeconfigs with real logins: configure the API server to trust an OIDC identity provider (Keycloak or Dex), log in from kubectl with kubelogin, and map IdP groups to RBAC.
The problem with shared kubeconfigs
Handing out copies of admin.conf means: no individual accountability, no MFA, no offboarding (the cert keeps working), and cluster-admin for everyone. OIDC fixes all four.
Instead of photocopying the master key for every teacher, the school uses its main reception (the identity provider). You sign in there each morning (with a password and a code on your phone), and get a day pass with your name and clubs printed on it. Classroom doors just check the pass is genuine and in date. If you leave the school, reception stops giving you passes.
How the flow works
you ──kubectl──► kubelogin ──browser──► IdP (Keycloak / Dex / Entra / Okta): password + MFA
◄──────── ID token (signed JWT: email, groups, expiry) ───────
kubectl ──Bearer <ID token>──► API server: checks signature, issuer, audience, expiry
→ user "oidc:asha@example.com", groups ["oidc:platform-admins"]
→ RBAC decides
Step 1: the identity provider
In Keycloak (as an example):
- Create a realm, e.g.
platform. - Create a client
kubernetes(OpenID Connect, public client or confidential; allowhttp://localhost:8000as a redirect URI for kubelogin). - Add a "Group Membership" mapper to the client with token claim name
groups(full group path off), so tokens carry groups. - Create groups (
platform-admins,developers) and users, and enable MFA (OTP or WebAuthn) in the realm's authentication flow.
The issuer URL is then https://sso.example.com/realms/platform. Dex is a lighter alternative that federates to GitHub, LDAP, SAML or another OIDC provider.
Step 2: the API server
Add the OIDC flags to kube-apiserver (on kubeadm, via apiServer.extraArgs in the cluster configuration, or by editing the static pod manifest):
--oidc-issuer-url=https://sso.example.com/realms/platform
--oidc-client-id=kubernetes
--oidc-username-claim=email
--oidc-username-prefix=oidc:
--oidc-groups-claim=groups
--oidc-groups-prefix=oidc:
The issuer must be reachable over HTTPS from the API server with a certificate it trusts (add --oidc-ca-file for a private CA). Recent Kubernetes versions also offer a structured authentication configuration file that can trust several issuers and map claims with CEL. Prefer it on new clusters where your version supports it.
Step 3: RBAC for IdP groups
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-platform-admins
subjects:
- kind: Group
name: "oidc:platform-admins" # prefix + IdP group name
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-developers-edit
namespace: shop
subjects:
- kind: Group
name: "oidc:developers"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit
apiGroup: rbac.authorization.k8s.io
Joining or leaving the developers group in the IdP now changes cluster access, with no YAML change.
Step 4: kubectl with kubelogin
Install kubelogin (for example with kubectl krew install oidc-login), then add a user that runs it:
users:
- name: oidc
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
command: kubectl
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://sso.example.com/realms/platform
- --oidc-client-id=kubernetes
interactiveMode: IfAvailable
$ kubectl --user=oidc get pods -n shop # opens the browser once; token cached and refreshed
$ kubectl --user=oidc auth whoami
ATTRIBUTE VALUE
Username oidc:asha@example.com
Groups [oidc:developers system:authenticated]
Now the kubeconfig you share contains no secrets: just the cluster address and how to log in.
Try it: OIDC on a lab cluster
This lab has several moving parts. Do it on a throwaway cluster, and allow an afternoon.
- Run Keycloak (its container image in dev mode is fine for a lab) behind HTTPS with a certificate your API server trusts. The HTTPS requirement is the usual stumbling block.
- Create the realm, client, groups mapper, two groups and two users as in step 1.
- Use
kubectl oidc-login setupwith your issuer and client ID to test the login and print the token claims. Check thatgroupsis present. - Configure the API server flags (for kind, use a
kubeadmConfigPatchesentry that setsapiServer.extraArgs) and create the RBAC bindings. - Log in as each user and compare
kubectl auth whoamiand whatkubectl auth can-i --list -n shopallows.
Going deeper: operating OIDC
- Token lifetime is your offboarding delay: keep ID tokens short (minutes), with refresh tokens controlled by the IdP.
- The IdP becomes critical infrastructure. Keep a break-glass path that doesn't depend on it (next lesson).
- For many clusters, a single auth broker (Dex, Pinniped, or a cloud's IAM integration) avoids configuring every API server separately.
- Managed Kubernetes often integrates cloud IAM instead (EKS access entries with IAM Identity Center, GKE with Google identities); the principles are the same.
Recap
- OIDC: the IdP authenticates (password + MFA), Kubernetes validates the token and applies RBAC.
- API server:
--oidc-issuer-url,--oidc-client-id, username/groups claims and prefixes (or the structured config file). - Bind IdP groups in RBAC; access follows group membership.
- kubelogin handles the browser login and tokens; kubeconfigs contain no secrets.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.