Authentication
Proving who a request is: certs, tokens, OIDC.
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:
kubectl auth whoami
ATTRIBUTE VALUEUsername kubernetes-adminGroups [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.
apiVersion: certificates.k8s.io/v1kind: CertificateSigningRequestmetadata:name: dev-alicespec:# base64 of a CSR built with -subj "/CN=dev-alice/O=developers"request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNU...signerName: kubernetes.io/kube-apiserver-clientexpirationSeconds: 604800usages:- client auth
kubectl apply -f csr.yamlkubectl certificate approve dev-alicekubectl get csr dev-alice
certificatesigningrequest.certificates.k8s.io/dev-alice createdcertificatesigningrequest.certificates.k8s.io/dev-alice approvedNAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITIONdev-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:
kubectl create token build-bot --duration=1h
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.
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:
curl -k -H "Authorization: Bearer not-a-real-token" https://10.0.0.10:6443/api
{"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.
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.
$ kubectl auth whoamiapiVersion: certificates.k8s.io/v1kind: 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-clientexpirationSeconds: 604800usages:- client auth$ kubectl apply -f csr.yaml$ kubectl certificate approve dev-alice$ kubectl get csr dev-alicecertificatesigningrequest.certificates.k8s.io/dev-alice createdcertificatesigningrequest.certificates.k8s.io/dev-alice approvedNAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITIONdev-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.