Kubernetes Security & Hardening›04 · OIDC login for kubectl

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.

Advanced
Key wordsOIDCKeycloakDexkubeloginID tokengroups claimAPI server flagsexec credential plugin
kubectl + oidc-login plugin Browser login + MFA Identity provider Keycloak, Entra, Okta… kube-apiserver validates token signature, issuer, audience, groups RBAC groups → roles 1 2 ID token (JWT) 3 4 5 no passwords in kubeconfig
kubectl login through your identity provider: short-lived tokens, groups mapped by 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):

  1. Create a realm, e.g. platform.
  2. Create a client kubernetes (OpenID Connect, public client or confidential; allow http://localhost:8000 as a redirect URI for kubelogin).
  3. Add a "Group Membership" mapper to the client with token claim name groups (full group path off), so tokens carry groups.
  4. 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.

  1. 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.
  2. Create the realm, client, groups mapper, two groups and two users as in step 1.
  3. Use kubectl oidc-login setup with your issuer and client ID to test the login and print the token claims. Check that groups is present.
  4. Configure the API server flags (for kind, use a kubeadmConfigPatches entry that sets apiServer.extraArgs) and create the RBAC bindings.
  5. Log in as each user and compare kubectl auth whoami and what kubectl auth can-i --list -n shop allows.

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.