How to Use Role-Based Access Control (RBAC) in Kubernetes

How to Use Role-Based Access Control (RBAC) in Kubernetes

If you’ve ever managed a Kubernetes cluster with more than one person on the team, you’ve probably run into the question: “Why does this developer have permission to delete production Pods?” That question is exactly what Role-Based Access Control (RBAC) exists to answer — and to prevent. In this guide, I’ll break down how RBAC works in Kubernetes from the ground up, and walk through practical examples you can apply immediately.

Why RBAC Matters

Kubernetes clusters often host multiple teams, multiple environments, and multiple layers of automation (CI/CD pipelines, controllers, monitoring agents) all talking to the same API server. Without access control, any authenticated user or service account could do anything — read secrets, delete deployments, modify RBAC itself. RBAC solves this by letting you define who can do what, on which resources, in which namespaces.

Kubernetes Architecture Context

Every request to Kubernetes — whether from kubectl, a controller, or a CI pipeline — goes through three stages at the API server:

  1. Authentication: Who are you? (certificates, tokens, OIDC, service accounts)
  2. Authorization: Are you allowed to do this? (this is where RBAC lives)
  3. Admission Control: Should this specific request be allowed/modified? (webhooks, policies)

RBAC is the authorization layer. It doesn’t care who you are beyond your identity (user or service account) and group memberships — it just checks whether a Role permits the verb and resource you’re requesting.

The Four RBAC Objects

RBAC in Kubernetes is built from four API objects:

  • Role: A set of permissions (verbs on resources) scoped to a single namespace.
  • ClusterRole: The same thing, but scoped cluster-wide (or reusable across namespaces).
  • RoleBinding: Grants a Role to a user, group, or service account within a namespace.
  • ClusterRoleBinding: Grants a ClusterRole cluster-wide.

The key mental model: Roles/ClusterRoles define permissions; Bindings assign those permissions to someone. A Role by itself does nothing until it’s bound.

Step 1: Create a Namespace-Scoped Role

Let’s say a developer needs to view and manage Pods in the staging namespace, but nothing else.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: staging
  name: pod-manager
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Apply it:

kubectl apply -f pod-manager-role.yaml

Step 2: Bind the Role to a User

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-manager-binding
  namespace: staging
subjects:
  - kind: User
    name: jane.doe
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-manager
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f pod-manager-binding.yaml

Now Jane can manage Pods only inside staging — nothing else, and nowhere else.

Step 3: Cluster-Wide Permissions with ClusterRole

Suppose you have a monitoring service account that needs to read node metrics across the whole cluster.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-reader
rules:
  - apiGroups: [""]
    resources: ["nodes", "nodes/metrics"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: node-reader-binding
subjects:
  - kind: ServiceAccount
    name: monitoring-agent
    namespace: monitoring
roleRef:
  kind: ClusterRole
  name: node-reader
  apiGroup: rbac.authorization.k8s.io

Understanding Verbs and Resources

Common verbs: get, list, watch, create, update, patch, delete, deletecollection. Resources can be scoped further using resourceNames to restrict access to specific named objects:

rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["app-config"]
    verbs: ["get", "update"]

This grants access only to the app-config ConfigMap — nothing else in that resource type.

Aggregated ClusterRoles

For larger clusters, Kubernetes supports aggregated ClusterRoles, which combine multiple ClusterRoles using label selectors. This is how the built-in admin, edit, and view roles work internally, and it’s a clean pattern for building modular permission sets:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-aggregate
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
rules: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
rules:
  - apiGroups: [""]
    resources: ["endpoints", "services", "pods"]
    verbs: ["get", "list", "watch"]

Checking Permissions

Before you go debugging why something isn’t working, use kubectl auth can-i:

kubectl auth can-i delete pods --namespace staging --as jane.doe
# yes

kubectl auth can-i create deployments --namespace production --as jane.doe
# no

This is invaluable for both testing and troubleshooting RBAC issues without waiting for a user to hit an error.

RBAC for Service Accounts (CI/CD and Automation)

Every Pod runs with a ServiceAccount, and by default it’s the default service account in its namespace, which typically has minimal permissions. For a CI/CD pipeline deploying to a namespace:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-deployer
  namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: production
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-deployer-binding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: ci-deployer
    namespace: production
roleRef:
  kind: Role
  name: deployer
  apiGroup: rbac.authorization.k8s.io

Your pipeline (GitHub Actions, GitLab CI, Argo CD) then authenticates using this ServiceAccount’s token, scoped to exactly the deployments it needs to touch.

Security Best Practices

  • Principle of least privilege: Never grant cluster-admin unless absolutely necessary. Audit who has it with kubectl get clusterrolebindings -o json | jq.
  • Avoid wildcard rules (resources: ["*"], verbs: ["*"]) in production; they’re a common source of privilege escalation.
  • Use groups, not individual users, for bindings when integrating with an identity provider (OIDC) — it scales much better as teams grow.
  • Separate namespaces per environment/team and scope Roles accordingly, rather than relying on a handful of broad ClusterRoles.
  • Rotate and audit service account tokens, especially long-lived ones; prefer projected, time-bound tokens (BoundServiceAccountToken) which are the default in modern Kubernetes.
  • Watch for privilege escalation via RBAC itself — a user with create on rolebindings and bind verb capability could grant themselves broader access; Kubernetes has built-in escalation checks, but review carefully.

Common Mistakes

  • Binding a ClusterRole via a RoleBinding to try to limit it to a namespace — this actually works and is a valid pattern, but it’s easy to confuse with a ClusterRoleBinding, which grants access everywhere.
  • Forgetting pods/log and pods/exec are separate subresources from pods — granting get on pods doesn’t let someone kubectl exec into them.
  • Applying RBAC changes without testing with kubectl auth can-i --as, leading to either broken pipelines or unnoticed over-permissioning.

Troubleshooting RBAC Issues

When a request is denied, the API server returns a clear Forbidden error naming the exact verb, resource, and namespace it evaluated:

Error from server (Forbidden): pods is forbidden: User "jane.doe" cannot list resource "pods" in API group "" in the namespace "production"

Read this message literally — it tells you exactly which Role or ClusterRoleBinding you’re missing.

Summary

RBAC is the backbone of multi-tenant, secure Kubernetes operations. By combining Roles and ClusterRoles (what’s allowed) with RoleBindings and ClusterRoleBindings (who gets it), you can build precise, auditable access control for humans, CI pipelines, and controllers alike. Start from least privilege, use kubectl auth can-i liberally, and treat every wildcard rule as a red flag worth double-checking.

References

Total
6
Shares

Leave a Reply

Previous Post
How to Set Up Pod Disruption Budgets with Loki in Kubernetes

How to Set Up Pod Disruption Budgets with Loki in Kubernetes

Next Post
How to Set Up Custom Controllers in Kubernetes

How to Set Up Custom Controllers in Kubernetes

Related Posts