Skip to content
ohsudo, Home
Kubernetes

Kubernetes "Forbidden: User cannot list resource": give exactly the rights it needs with RBAC

Fix Kubernetes "Forbidden: User cannot list resource": read who and what is missing, test with kubectl auth can-i, then add a Role and RoleBinding.

Intermediate4 min readApplies to Kubernetes 1.28+ · kubectl · EKS · AKS · GKE · k3s · kind

by OhSudo ··

Not sure this is your cause?Find it step by step: Pod not running →

Part of a learning path:Kubernetes: debugging pods

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:ci:deployer" cannot list resource "pods" in API group "" in the namespace "web"

This one tells you everything. Kubernetes knows who you are, so logging in worked. That identity has no right to do this one thing. The message names all four parts of what’s missing: who, the action, the resource, and the namespace.

  1. Whoserviceaccount ci:deployer(works)
  2. RoleBindinglinks who to a Role
  3. Roleno "list pods" in namespace web(breaks here)
Logging in worked. RBAC grants rights through a binding to a role; no binding gives this identity this verb on this resource in this namespace.

TL;DR

Terminal window
kubectl auth can-i list pods -n web --as=system:serviceaccount:ci:deployer
kubectl create role pod-reader --verb=get,list,watch --resource=pods -n web
kubectl create rolebinding deployer-pod-reader --role=pod-reader --serviceaccount=ci:deployer -n web

Run those as a cluster admin. The first answers no, before the fix, and yes after.

1. Read the message

Part Here Means
User "..." system:serviceaccount:ci:deployer Who: here the service account deployer in the namespace ci
cannot list list The verb: get, list, watch, create, update, patch or delete
resource "pods" pods What
in API group "" the core group "" holds pods, services and secrets. Deployments are in apps
in the namespace "web" web Where. With no namespace, it’s cluster-wide

A person shows up as their user name, like User "jane@example.com", or as an IAM role on EKS.

2. Ask the cluster

Check any permission, for anyone, without trying it for real:

Terminal window
kubectl auth can-i list pods -n web --as=system:serviceaccount:ci:deployer
kubectl auth can-i --list -n web --as=system:serviceaccount:ci:deployer

The second lists everything that identity can do in that namespace. To see who you are:

Terminal window
kubectl auth whoami

3. Grant it: a Role and a RoleBinding

RBAC has two halves. A Role lists what’s allowed, and a RoleBinding gives that Role to someone, in one namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: web
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer-pod-reader
namespace: web
subjects:
- kind: ServiceAccount
name: deployer
namespace: ci
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Terminal window
kubectl apply -f rbac.yaml

Note that the service account lives in ci, and the binding in web: a binding grants rights in its own namespace, to someone from anywhere. Use the YAML ⇄ JSON converter if your tooling wants JSON.

For a person, use kind: User with their name as it appears in the message, or better, kind: Group, so that a team shares one binding.

4. Cluster-wide resources

Some resources don’t belong to a namespace: nodes, namespaces themselves, persistent volumes, and CRDs. For those, a Role can’t help:

nodes is forbidden: User "jane@example.com" cannot list resource "nodes" in API group "" at the cluster scope

Use a ClusterRole, with a ClusterRoleBinding:

Terminal window
kubectl create clusterrole node-reader --verb=get,list,watch --resource=nodes
kubectl create clusterrolebinding jane-node-reader --clusterrole=node-reader --user=jane@example.com

A ClusterRole can also be bound with a plain RoleBinding, which grants it in one namespace only. That’s a good way to reuse the built-in view and edit roles:

Terminal window
kubectl create rolebinding jane-view --clusterrole=view --user=jane@example.com -n web

5. Managed clusters

EKS: an IAM user or role first has to be mapped into the cluster, with an access entry, or the older aws-auth ConfigMap. Without that you get Unauthorized, not this. Once mapped, its Kubernetes rights come from access policies or RBAC as above.

AKS with Microsoft Entra ID and GKE can use cloud roles for Kubernetes rights. On GKE, IAM and RBAC both apply, and either one can allow a request.

6. Helm and operators

helm install acts with your rights, so a chart that creates a ClusterRole needs a user who may do so. An operator’s pods use their own service account. When an operator logs this error, its chart’s RBAC is out of date, or it was installed limited to one namespace and is reaching into another.

Warning

Don’t bind cluster-admin to a service account to fix this, even “for now”: it gives full control of the cluster to anything that can read that account’s token, such as a compromised CI job. Grant the verbs and resources in the message, in the namespace in the message. kubectl auth can-i tells you when you’ve got it right.

Shell, we have a problem!

Similar, but different: error: You must be logged in to the server (Unauthorized) means your credentials didn’t work at all. x509: certificate signed by unknown authority is about trusting the cluster. admission webhook ... denied the request comes from a policy, not RBAC. And runAsNonRoot errors come from a pod’s security settings.

$ ./practice.sh

Practice this in the terminal

Open the Terminal Playground with this article’s scenario loaded. It is safe and runs in your browser.

Open the terminal →

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles