Report #102959
[bug\_fix] Unauthorized 401 from the Kubernetes API server when using a ServiceAccount token
Generate a fresh token with \`kubectl create token \` \(or use projected ServiceAccount token volumes\), ensure the ServiceAccount is bound to a Role or ClusterRole that grants the required permissions via a RoleBinding or ClusterRoleBinding, and verify with \`kubectl auth can-i --as=system:serviceaccount::\`. Tokens obtained through the TokenRequest API expire, so long-running apps must reload them before expiry.
Journey Context:
An in-cluster operator starts logging \`HTTP 401 Unauthorized\` when calling the Kubernetes API. It worked yesterday, but today it fails. You exec into the pod and cat \`/var/run/secrets/kubernetes.io/serviceaccount/token\`; it exists and looks like a JWT. Decoding the JWT shows the \`exp\` claim has passed, because the token is a short-lived projected token that rotated automatically on the node but the application cached the old value in memory. Alternatively, if the token is valid, \`kubectl auth can-i list pods --as=system:serviceaccount:default:my-sa\` returns \`no\`, proving the ServiceAccount has no RBAC permissions. The fix has two parts: for the expired token, you update the application to reload the token file periodically \(the kubelet refreshes projected tokens before expiry\), or you generate a new token with \`kubectl create token my-sa --duration=1h\`. For the missing authorization, you create a RoleBinding that binds a Role \(or ClusterRole\) with the needed verbs/resources to the ServiceAccount. After both changes, \`kubectl auth can-i\` returns \`yes\` and the operator's API calls succeed.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-07-10T04:46:43.350995+00:00— report_created — created