Namespaces & RBAC
Namespaces group namespaced resources; RBAC decides who can do which verbs on which resources. Use Role/RoleBinding for namespace-scoped access, ClusterRole/ClusterRoleBinding for cluster-wide access, and kubectl auth can-i to debug permission problems safely.
Mental Model
RBAC is a mapping between a subject, permissions, and a binding. The subject is a user, group, or service account. The permissions live in a Role or ClusterRole. The binding attaches those permissions to the subject.
RBAC connects subjects to permissions through bindings.
Namespaces
Namespaces isolate names for namespaced resources such as Pods, Deployments, Services, ConfigMaps, Secrets, Roles, and RoleBindings. They do not isolate nodes, persistent volumes, storage classes, namespaces themselves, or cluster-wide RBAC objects.
# List namespaces and age.
kubectl get namespaces
# Show labels, annotations, finalizers, and lifecycle status for one namespace.
kubectl describe namespace <namespace>
# Show common namespaced resources in one namespace.
kubectl get deploy,sts,ds,job,cronjob,pod,svc,ingress,cm,secret,pvc -n <namespace>
# Set a default namespace for the current context.
# Be careful: this changes where kubectl commands run when -n is omitted.
kubectl config set-context --current --namespace=<namespace>
# Clear the default namespace for the current context.
kubectl config set-context --current --namespace=default
RBAC Objects
| Object | Scope | Use It For |
|---|---|---|
Role |
Namespace | Permissions inside one namespace. |
RoleBinding |
Namespace | Attach a Role or ClusterRole to a subject inside one namespace. |
ClusterRole |
Cluster | Cluster-wide permissions or reusable permission templates. |
ClusterRoleBinding |
Cluster | Attach a ClusterRole cluster-wide. Use sparingly. |
ServiceAccount |
Namespace | Identity used by Pods, controllers, CI jobs, and operators inside the cluster. |
Debug Permissions With can-i
kubectl auth can-i is the safest first tool. It asks the API server authorization layer whether a subject can perform a specific action.
# Check your current user's permission in a namespace.
kubectl auth can-i get pods -n <namespace>
kubectl auth can-i create deployments -n <namespace>
kubectl auth can-i patch deployments -n <namespace>
# Check a ServiceAccount's permission.
# Format is system:serviceaccount:<namespace>:<serviceaccount-name>.
kubectl auth can-i list pods -n <namespace> \
--as=system:serviceaccount:<namespace>:<serviceaccount-name>
# Check cluster-scoped permissions. Do not add -n for cluster-scoped resources.
kubectl auth can-i list nodes
kubectl auth can-i get clusterroles
# Show all permissions the current identity has in one namespace.
kubectl auth can-i --list -n <namespace>
Least-Privilege Patterns
Start with the smallest permission that matches the job. Most app teams need namespace-level access, not cluster-admin. Most workloads need a custom ServiceAccount only if they call the Kubernetes API.
apiVersion: v1
kind: Namespace
metadata:
name: app # Namespace where the application and RBAC objects live.
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: deployer # Identity used by CI/CD or an in-cluster deploy job.
namespace: app
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-manager
namespace: app # Role permissions apply only inside this namespace.
rules:
- apiGroups: ["apps"] # Deployments and ReplicaSets are in the apps API group.
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch"] # No delete unless the job truly needs it.
- apiGroups: [""] # Empty string is the core API group.
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list", "watch"] # Useful for deployment diagnostics.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer-can-manage-deployments
namespace: app
subjects:
- kind: ServiceAccount
name: deployer
namespace: app # ServiceAccount namespace must be explicit.
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: deployment-manager # The Role being granted.
Read-Only App Viewer
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-readonly
namespace: app
rules:
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
resources:
- pods
- pods/log # Required to read Pod logs.
- services
- endpoints
- configmaps
- events
- deployments
- replicasets
- statefulsets
- daemonsets
- jobs
- cronjobs
- ingresses
verbs: ["get", "list", "watch"] # Read-only verbs.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-readonly-binding
namespace: app
subjects:
- kind: Group
name: sre-readonly@example.com # Replace with the client's identity provider group name.
apiGroup: rbac.authorization.k8s.io
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: app-readonly
ServiceAccounts In Pods
Pods run as a ServiceAccount. If you do not specify one, they use the namespace's default ServiceAccount. Avoid granting permissions to default; create a named ServiceAccount per workload or controller that needs API access.
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-api-reader
namespace: app
automountServiceAccountToken: true # Set false if this workload does not need the Kubernetes API token.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: app
spec:
replicas: 2
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
serviceAccountName: app-api-reader # Pod identity used for Kubernetes API calls.
automountServiceAccountToken: true # Can be false to prevent token mounting.
containers:
- name: app
image: nginx:1.27
Inspect Existing RBAC
# Namespace-scoped RBAC.
kubectl get role,rolebinding,serviceaccount -n <namespace>
kubectl describe role <role-name> -n <namespace>
kubectl describe rolebinding <binding-name> -n <namespace>
# Cluster-scoped RBAC. Be careful with broad ClusterRoleBindings.
kubectl get clusterrole,clusterrolebinding
kubectl describe clusterrole <clusterrole-name>
kubectl describe clusterrolebinding <clusterrolebinding-name>
# Find bindings that mention a specific ServiceAccount name.
kubectl get rolebinding,clusterrolebinding --all-namespaces -o yaml | grep -B5 -A10 '<serviceaccount-name>'
Troubleshooting Forbidden Errors
A Forbidden error means authentication succeeded but authorization denied the action. Copy the exact verb, resource, API group, namespace, and subject from the error.
# Example Forbidden error:
# User "system:serviceaccount:app:deployer" cannot patch resource "deployments" in API group "apps" in namespace "app"
# Reproduce the authorization question exactly.
kubectl auth can-i patch deployments.apps -n app \
--as=system:serviceaccount:app:deployer
# If denied, inspect the subject's bindings.
kubectl get rolebinding -n app -o yaml | grep -B8 -A20 'deployer'
kubectl get clusterrolebinding -o yaml | grep -B8 -A20 'deployer'
# Inspect the referenced Role or ClusterRole and confirm it has the missing verb/resource.
kubectl describe role <role-name> -n app
kubectl describe clusterrole <clusterrole-name>
Common Gotchas
- RoleBinding to ClusterRole is valid: it grants that ClusterRole's permissions only inside the RoleBinding namespace.
- ClusterRoleBinding is broad: it grants access across the cluster. Avoid it for app team namespace access.
- Secrets need explicit access: many read-only roles intentionally exclude
secretsbecause Secret read is sensitive. - Subresources matter:
pods,pods/log,pods/exec, anddeployments/scaleare different permissions. - Namespace deletion stuck: check finalizers and unavailable API services before force-removing finalizers.