Error from server (Forbidden): The request was authenticated and RBAC denied it
A Forbidden error means Kubernetes knows who you are and has decided you may not do this. The message names the user, the verb, and the resource, which is everything needed to write the missing rule.
Applies to: All Kubernetes versions with RBAC enabled
What it means
Authentication and authorization are separate stages, and Forbidden is a failure of the second. The identity was established successfully; the request was then evaluated against RBAC and no rule permitted it. The message follows a fixed shape — User "…" cannot list resource "pods" in API group "" in the namespace "…" — and every field in it is needed to fix the problem. Two of them are easy to misread. An empty API group means the core group, which is what most common resources are in, not a missing value. And a message that says at the cluster scope means the request was not namespaced, which requires a ClusterRole and a ClusterRoleBinding rather than their namespaced equivalents.
Most common causes
- No Role or ClusterRole grants the verb on that resource to this identity.
- A Role exists but the RoleBinding is in a different namespace from the resource being accessed.
- A cluster-scoped request being made with only a namespaced Role granted.
- A workload using the default ServiceAccount, which has almost no permissions by design.
- The wrong API group named in the rule — resources outside the core group need their group specified exactly.
- A subresource such as
pods/logorpods/execnot granted, even though the parent resource is. - A rule granting
getwhen the client is doinglistorwatch. These are distinct verbs.
How to diagnose it
- Read every field of the message: the user, the verb, the resource, the API group, and the namespace or cluster scope.
- Check your own effective permission:
kubectl auth can-i list pods -n NAMESPACE. - Check another identity's:
kubectl auth can-i list pods --as=system:serviceaccount:NS:NAME -n NAMESPACE. This is the fastest way to test a workload's access without deploying anything. - Find the bindings that apply:
kubectl get rolebindings,clusterrolebindings --all-namespaces -o wide | grep NAME. - Read the role's actual rules:
kubectl describe clusterrole NAME. - For a workload, confirm which ServiceAccount it runs as:
kubectl get pod POD -o jsonpath='{.spec.serviceAccountName}'. An empty value meansdefault.
How to fix it
- Add a rule granting exactly the verb, resource, and API group named in the error — no more.
- Bind at the right scope: a RoleBinding for one namespace, a ClusterRoleBinding for cluster-wide access. A ClusterRole referenced by a RoleBinding grants only within that namespace, which is a useful and frequently missed pattern.
- Give the workload a dedicated ServiceAccount rather than relying on
default. - Grant subresources explicitly where they are needed —
pods/logandpods/execare separate frompods. - Verify with
kubectl auth can-iafter changing anything, rather than redeploying to find out.
Notes
RBAC is purely additive: there are no deny rules, so a permission that exists cannot be revoked by adding another rule. Removing access means removing or narrowing the binding that grants it, which is worth knowing before searching for a deny rule that does not exist.
Related
- Unauthorized — The request was not authenticated
- cannot list resource at the cluster scope — A cluster-scoped request needs a ClusterRoleBinding
Sources
- Kubernetes documentation — Using RBAC Authorization
- Kubernetes documentation — Authorization Overview
- Kubernetes documentation — Configure Service Accounts for Pods