# Kubernetes Namespaces and RBAC: Roles, ServiceAccounts, Quotas and kubectl auth can-i

> Split a cluster with namespaces and control access with RBAC: Roles, ClusterRoles, bindings, ServiceAccounts, quotas, limit ranges and how to fix Forbidden errors.

- Source: https://www.itwonderlab.com/kubernetes-namespaces-rbac/
- Published: 2026-06-27
- Updated: 2026-06-27
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Namespaces

A **namespace** is a named partition of a [Kubernetes](https://www.itwonderlab.com/kubernetes/) cluster. Names are unique inside a namespace, so two teams can both have a Deployment called `web`. Namespaces also scope permissions ([RBAC](https://www.itwonderlab.com/rbac/)), quotas and network policies. They are for organization and policy, not a hard security boundary: pods in different namespaces share nodes and, by default, the network.

```bash
kubectl get namespaces
kubectl create namespace dev
kubectl get pods -n dev
kubectl get pods -A                                   # all namespaces
kubectl config set-context --current --namespace=dev  # default namespace for kubectl
```

Starting namespaces: `default`, `kube-system` (cluster components), `kube-public` and `kube-node-lease`. Do not deploy your apps into `default` or `kube-system`. Some objects are cluster-wide and have no namespace: nodes, PersistentVolumes, StorageClasses, Namespaces and ClusterRoles (`kubectl api-resources --namespaced=false`).

Deleting a namespace deletes everything inside it: `kubectl delete namespace dev`.

DNS crosses namespaces with the full name: `db.prod.svc.cluster.local`, see [Services](https://www.itwonderlab.com/kubernetes-services-ingress/).

## RBAC in one picture

Role-Based Access Control answers "may this subject do this verb on this resource?". Everything is allowed only if a rule says so: there are no deny rules.

![Kubernetes RBAC: a Role lists the verbs allowed on resources in a namespace, and a RoleBinding grants that Role to a user, group or ServiceAccount; ClusterRole and ClusterRoleBinding do the same cluster-wide](https://www.itwonderlab.com/media/tutorials/Diagrams/ITWL-K8s-RBAC.svg "Roles and bindings, namespaced and cluster-wide")

| Object | Scope | Meaning |
|---|---|---|
| `Role` | One namespace | A list of rules: API groups, resources, verbs |
| `ClusterRole` | Cluster | Same, but also for cluster resources (nodes) and reusable across namespaces |
| `RoleBinding` | One namespace | Grants a Role **or a ClusterRole** to subjects in that namespace |
| `ClusterRoleBinding` | Cluster | Grants a ClusterRole in every namespace |

Subjects are `User`, `Group` (both come from the authenticator: certificates, OIDC, or on EKS the aws-auth/Access Entries mapping) and `ServiceAccount` (an identity for pods).

Verbs: `get`, `list`, `watch`, `create`, `update`, `patch`, `delete`, plus `exec`, `portforward` (as subresources) and `impersonate`.

## Give a user read access to a namespace

```yaml title="rbac-dev-reader.yaml"
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: dev
rules:
  - apiGroups: [""]                 # "" is the core group (pods, services, configmaps...)
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-reader-binding
  namespace: dev
subjects:
  - kind: User
    name: maria@example.com
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
```

`roleRef` cannot be changed after creation: delete and recreate the binding.

The quick way, with the imperative commands:

```bash
kubectl create role pod-reader --verb=get,list,watch --resource=pods,pods/log -n dev
kubectl create rolebinding dev-reader-binding --role=pod-reader --user=maria@example.com -n dev
```

### The built-in ClusterRoles

Do not write everything from scratch. Reuse them with a RoleBinding:

| ClusterRole | Gives |
|---|---|
| `view` | Read most objects of a namespace, **not Secrets** |
| `edit` | Read and write most objects, including Secrets, but no RBAC |
| `admin` | `edit` plus Roles and RoleBindings in the namespace |
| `cluster-admin` | Everything, everywhere |

```bash
kubectl create rolebinding maria-edit --clusterrole=edit --user=maria@example.com -n dev
kubectl create clusterrolebinding ops-admin --clusterrole=cluster-admin --group=ops
```

Give `cluster-admin` to as few subjects as possible.

## ServiceAccounts: identities for pods

Pods call the API (an operator, a CI runner, Argo CD) with a **ServiceAccount**. Every namespace has `default`, but create one per app and give it only what it needs.

```bash
kubectl create serviceaccount deployer -n dev
kubectl create rolebinding deployer-edit --clusterrole=edit --serviceaccount=dev:deployer -n dev
```

```yaml
    spec:
      serviceAccountName: deployer
      automountServiceAccountToken: false     # if the pod does not call the API
```

The token is projected into `/var/run/secrets/kubernetes.io/serviceaccount/token`, short-lived and rotated. For an external system, create one on demand: `kubectl create token deployer -n dev --duration=1h`. Do not use long-lived Secret tokens unless you must.

## Check permissions: kubectl auth can-i

```bash
kubectl auth can-i create deployments -n dev
kubectl auth can-i delete pods -n prod
kubectl auth can-i get secrets --as=maria@example.com -n dev
kubectl auth can-i list pods --as=system:serviceaccount:dev:deployer -n dev
kubectl auth can-i --list -n dev                 # everything you may do
kubectl get rolebindings,clusterrolebindings -A -o wide | grep maria
kubectl describe clusterrole view
```

`--as` needs impersonation rights, which cluster admins have. It is the best way to test a Role before giving it to someone.

## Quotas and limits per namespace

Namespaces are also where you stop one team from using the whole cluster:

```yaml title="quota.yaml"
apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: dev
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.memory: 16Gi
    pods: "30"
    persistentvolumeclaims: "10"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
  namespace: dev
spec:
  limits:
    - type: Container
      defaultRequest: { cpu: 100m, memory: 128Mi }
      default: { memory: 256Mi }
```

When a quota covers CPU or memory, every pod must declare them, and the `LimitRange` supplies defaults so manifests without resources do not fail. See [requests and limits](https://www.itwonderlab.com/kubernetes-probes-resource-limits/).

```bash
kubectl describe quota -n dev
```

## Common errors

| Symptom | Cause | Fix |
|---|---|---|
| `Error from server (Forbidden): pods is forbidden: User "maria" cannot list resource "pods" in API group "" in the namespace "dev"` | No Role grants it | Read the message: user, verb, resource, group and namespace are all there. Add the rule |
| `... cannot list resource "pods" in API group "" at the cluster scope` | `kubectl get pods -A` needs a ClusterRole and ClusterRoleBinding | Use `-n`, or bind cluster-wide |
| `Forbidden` on `pods/exec`, `pods/log` or `pods/portforward` | Subresources need their own rule | Add `pods/exec` with verb `create` |
| `error: unable to upgrade connection: Forbidden` | Same: `exec` or `port-forward` | Same |
| `Error from server (NotFound): namespaces "dve" not found` | Typo in the namespace | `kubectl get ns` |
| `Error from server (AlreadyExists)` after changing a binding | A binding `roleRef` is immutable | Delete and create the binding |
| `escalating privileges`: `user "x" is attempting to grant RBAC permissions not currently held` | You cannot give more than you have | Use someone with the rights, or the `escalate` and `bind` verbs |
| `exceeded quota: dev-quota, requested: pods=1, used: pods=30, limited: pods=30` | ResourceQuota full | Delete unused objects or raise the quota |
| `must specify limits.memory` | A quota requires them, and there is no LimitRange | Add resources or a LimitRange |
| Pod cannot call the API: `serviceaccount "x" not found` or `Forbidden ... system:serviceaccount:dev:default` | Missing SA or binding | Create it and bind a Role |
| Namespace stuck in `Terminating` | Objects with finalizers, or an unavailable API (metrics-server) | `kubectl get all -n x`, `kubectl api-resources`, fix the APIService, then remove the finalizer as the last resort |
| `Unauthorized` (not `Forbidden`) | Authentication failed: expired token or certificate | Renew credentials. See [architecture errors](https://www.itwonderlab.com/kubernetes-architecture-components/) |

Remember: `Unauthorized` (401) means "who are you?" and `Forbidden` (403) means "I know you, but no".

## How to debug RBAC

1. Read the Forbidden message: it names the exact user, verb, resource and namespace.
2. `kubectl auth can-i <verb> <resource> --as=<user> -n <ns>` to reproduce it.
3. Find the bindings of the subject: `kubectl get rolebindings,clusterrolebindings -A -o json | jq -r '.items[] | select(.subjects[]?.name=="maria@example.com") | .metadata.namespace + "/" + .metadata.name'`.
4. `kubectl describe role|clusterrole <name>` to see the rules.
5. For a pod, check which ServiceAccount it uses: `kubectl get pod <pod> -o jsonpath='{.spec.serviceAccountName}'`.
6. The API server audit log (on a managed cluster, the cloud audit logs) shows the denied request.

More in [How to debug Kubernetes](https://www.itwonderlab.com/how-to-debug-kubernetes/).

## Frequently asked questions

**Where do users come from?** Kubernetes has no User object. Identities come from client certificates, OIDC tokens (Google, Okta, Entra ID) or the cloud (IAM on EKS). Only ServiceAccounts are objects.

**Role or ClusterRole?** Role for permissions in one namespace. ClusterRole for cluster resources, or to define a set once and bind it in many namespaces with RoleBindings.

**Can I deny something?** No. RBAC is additive. Grant less.

**Are namespaces secure isolation?** Not alone. Add RBAC, `ResourceQuota`, NetworkPolicies and Pod Security Admission (`pod-security.kubernetes.io/enforce: restricted` label on the namespace). For hard multi-tenancy use separate clusters.

**How many namespaces should I have?** One per team and environment or per application is common (`shop-dev`, `shop-prod`). Avoid hundreds without automation.

**How do I set the namespace for a whole manifest?** `kubectl apply -n dev -f file.yaml`, or `metadata.namespace`, or [Kustomize](https://www.itwonderlab.com/kustomize-kubernetes-overlays/) `namespace:`, or Helm `--namespace`.

**Is `default` ServiceAccount dangerous?** It has no permissions by default, but pods get its token mounted. Set `automountServiceAccountToken: false` where not needed.

## Next steps

Protect values with [ConfigMaps and Secrets](https://www.itwonderlab.com/kubernetes-configmaps-secrets/), scale with the [Horizontal Pod Autoscaler](https://www.itwonderlab.com/kubernetes-horizontal-pod-autoscaler/) and read the cluster layout in [Kubernetes architecture](https://www.itwonderlab.com/kubernetes-architecture-components/).
