Authentication

Proving who a request is: certs, tokens, OIDC.

Intermediate10 min · lesson 43 of 65
In plain terms
Authentication is showing ID at the door. Kubernetes keeps no guest list of its own — it trusts your badge (a certificate, a token, or your company SSO login) to prove who you are.

Kubernetes has no list of users. There's no table of humans anywhere in the cluster, no CREATE USER command, and nothing you can query to see who Alice is or when she was added. This throws almost everyone the first time. You'd expect a system this big to keep its own roster of who's allowed in, the way a database or an operating system does. It just doesn't. Every human who touches the cluster is somebody else's problem to keep track of, and Kubernetes only ever checks their credential in the moment they show up.

Think of the API server as the front desk that every single request has to pass through. (API just names the doorway that other software talks to; here, treat it as the one entrance to the whole cluster.) The guard at this desk has never met you, and he never will. He keeps no guest list. He checks exactly one thing: was your badge printed by a machine he already trusts? Show a badge from a trusted issuer and you walk in. That's authentication, and that's all it is. It proves who a request is, before anything decides what that request is allowed to do. Authorization comes next (RBAC, short for Role-Based Access Control, which decides whether you may), then admission control gets its turn. Right now, only the name on the badge matters.

The authenticator chain

When a request lands, the API server runs the credential past a line of authenticators, one after another, until one of them recognizes it. Client certificate first, then bearer tokens, then whatever external identity provider you've wired up. The first authenticator to say 'yes, I know this one' wins, and it hands back a username and a set of groups. If every authenticator just shrugs, the request counts as anonymous, and on any properly locked-down cluster anonymous gets bounced with a 401 before it goes any further. Curious what your own credential resolves to? Ask the cluster directly:

shell
kubectl auth whoami
output
ATTRIBUTE VALUE
Username kubernetes-admin
Groups [system:masters system:authenticated]

A username and a list of groups. That's the whole output of authentication, nothing more. Now look at system:masters sitting in that list. That group is wired straight into the RBAC layer as full cluster-admin, no RoleBinding required and no way to argue with it. So the group stamped on your badge can matter every bit as much as the name printed next to it.

Handing a human a client certificate

Getting a person into the cluster the old-fashioned way works like getting a badge printed at reception. The cluster's certificate authority (CA, the one badge printer that everybody trusts) signs a certificate for them. Kubernetes then reads exactly two fields off that cert and ignores the rest: the Common Name (CN) becomes the username, and the Organization (O) becomes the group. Sign a cert with CN=dev-alice and O=developers, and the front desk sees a user named dev-alice who belongs to the developers group. You register that nowhere. The signature is the whole story. The clean, auditable way to get a cert signed is the CertificateSigningRequest object, so the cluster's own CA does the signing and nobody is emailing private keys around.

csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: dev-alice
spec:
# base64 of a CSR built with -subj "/CN=dev-alice/O=developers"
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNU...
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 604800
usages:
- client auth
shell
kubectl apply -f csr.yaml
kubectl certificate approve dev-alice
kubectl get csr dev-alice
output
certificatesigningrequest.certificates.k8s.io/dev-alice created
certificatesigningrequest.certificates.k8s.io/dev-alice approved
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
dev-alice 5s kubernetes.io/kube-apiserver-client kubernetes-admin 7d Approved,Issued

'Approved,Issued' means the signer actually minted the certificate. Pull it out with kubectl get csr dev-alice -o jsonpath='{.status.certificate}', base64-decode it, and hand it to Alice along with the CA cert. She drops both into a kubeconfig file, and now she can authenticate. She still can't do a single thing until RBAC grants it. But the front desk knows her name.

Tokens for the things that aren't people

A Pod (Kubernetes' smallest running unit, one or more containers bundled together) can't walk up to the front desk with a printed badge. So it carries a token instead: a signed JWT (JSON Web Token, which is really just a compact signed string of claims about who you are), mounted right into the container by Kubernetes itself. Service accounts are the one identity type the cluster genuinely issues and manages on your behalf. Modern tokens are short-lived and audience-bound, minted on demand through the TokenRequest API rather than sitting in a Secret forever waiting to leak. You can mint one by hand to see what it looks like:

shell
kubectl create token build-bot --duration=1h
output
eyJhbGciOiJSUzI1NiIsImtpZCI6Imt1YmVy...truncated...q3nY0Zg9lJ2fA1oQ

That token identifies the caller as system:serviceaccount:default:build-bot. Present it to the API server and you are that identity for exactly one hour, then it expires on its own. No cleanup job to remember, and no forever-credential quietly leaking out of some old backup.

A leaked client certificate cannot be revoked
A client certificate stays valid until the day it expires, full stop. Kubernetes keeps no certificate revocation list (CRL), so if an admin cert leaks, you cannot cancel that one cert on its own. Your only real kill switch is rotating the cluster CA, which invalidates every certificate that CA ever signed and means re-issuing to everybody at once. That's why long-lived admin certs are dangerous, why you keep cert lifetimes short (that expirationSeconds field above), and why OIDC with short tokens is the grown-up answer for human access.

401 is not 403

Confuse these two and you'll spend an hour debugging the wrong layer. Present a bad or expired credential straight to the API server and watch what comes back:

shell
curl -k -H "Authorization: Bearer not-a-real-token" https://10.0.0.10:6443/api
output
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "Unauthorized",
"reason": "Unauthorized",
"code": 401
}

A 401 Unauthorized means the front desk never recognized the badge at all. The identity didn't resolve, end of story. A 403 Forbidden is the opposite: the guard knows exactly who you are, and RBAC has decided this particular action is off limits for you. So when you hit a 401, stop rereading your Roles and RoleBindings. The credential itself is the problem. Maybe an expired cert, maybe the wrong CA baked into the kubeconfig, maybe a malformed or stale token. When you hit a 403, the credential is fine and you've got an authorization gap to close instead.

For a real team, certificates just don't scale, mostly because of that revocation trap in the warning above. So organizations point the API server at an OIDC (OpenID Connect) provider, which in plain terms is your company's single sign-on (SSO). People log in through something like Okta or Keycloak, the provider hands back a short-lived token, and their group membership rides along inside the token's claims. Revoking someone stops being a cluster operation at all. You disable their account in the identity provider, and it takes hold within minutes as their current token expires. On current clusters (v1.30 and up) you wire this up with a structured AuthenticationConfiguration file passed to the API server through --authentication-config. It replaced the old scatter of --oidc-issuer-url and --oidc-username-claim flags, and it lets you validate claims with CEL expressions (small inline rules that run before anything else) so a token has to pass your checks before its identity is ever trusted.

How the API server resolves an identity
1Request hits theAPI serverThe secure connection is set…2Client-certauthenticatorSigned by the cluster CA? If…3Bearer-tokenauthenticatorA service-account JWT or a…4External identityproviderOIDC token from your SSO, with…5IdentityestablishedFirst match returns a username…

Anonymous auth should be off on real clusters. If it is on, treat it as an incident.

Client certs for humans do not rotate themselves. Prefer OIDC with short-lived tokens.

Impersonation is powerful for debugging and dangerous if granted broadly. Audit who has it.

Try this

Run kubectl auth whoami, then try the same with a different context or an impersonation flag if you have rights. See that every request has a user identity.

terminal
$ kubectl auth whoami
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
$ metadata:
name: dev-alice
$ spec:
# base64 of a CSR built with -subj "/CN=dev-alice/O=developers"
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNU...
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 604800
usages:
- client auth
$ kubectl apply -f csr.yaml
$ kubectl certificate approve dev-alice
$ kubectl get csr dev-alice
certificatesigningrequest.certificates.k8s.io/dev-alice created
certificatesigningrequest.certificates.k8s.io/dev-alice approved
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
dev-alice 5s kubernetes.io/kube-apiserver-client kubernetes-admin 7d Approved,Issued
$ kubectl create token build-bot --duration=1h
$ curl -k -H "Authorization: Bearer not-a-real-token" https://10.0.0.10:6443/api

Takeaway

Authentication proves who. Certificates, bearer tokens, and OIDC all become a username and groups on the request.

Quick check
01A developer's client certificate (CN=dev-alice, O=developers) leaks, and it doesn't expire for another year. What actually stops that certificate from authenticating to the cluster?
Incorrect — There is no user object to delete. Kubernetes stores no human users, so there is nothing to remove. The cert keeps authenticating.
Incorrect — That is authorization, not authentication. The certificate still authenticates as dev-alice, and anything bound to the developers group still applies. You have made her identified but maybe unprivileged, not locked out.
Correct — There is no certificate revocation list to consult. A signed cert is valid until expiry, so the only true kill switch is rotating the CA, which invalidates every cert at once. This is exactly why short-lived OIDC tokens are preferred for people.
Incorrect — The API server does not check a CRL for client certs. There is no such list to add the serial to.
02kubectl auth whoami returns Groups [system:masters system:authenticated]. What does membership in system:masters give that caller, and how?
Incorrect — most groups work that way, but system:masters is the exception, wired into authorization with no binding required.
Correct — system:masters maps to cluster-admin inside RBAC itself, so the group on the badge can matter as much as the username.
Incorrect — it is a built-in super-user group, not a flag describing the account type.
Incorrect — system:masters is unrestricted read and write everywhere, that is full admin, not read-only.
03A script's kubectl call suddenly fails with code 401 and message Unauthorized. Where should you look first?
Incorrect — that describes 403 Forbidden; 401 fires before authorization ever runs.
Incorrect — admission runs after authentication and authorization, so a 401 never reaches it.
Correct — 401 means no authenticator recognized the badge, so the identity never resolved; fix the credential, not the Roles.
Incorrect — verbs are an authorization concern (403); at 401 the request was never authenticated, so permissions are irrelevant.

Related