Kubernetes Entry Created: 10 Mar 2026 Updated: 10 Mar 2026

Role-Based Access Control for Kubernetes

Almost every Kubernetes cluster you encounter today has Role-Based Access Control (RBAC) enabled. You may have already bumped into it — perhaps you could not access your cluster until someone ran a magic command to create a RoleBinding. But understanding why that worked, and how to design access rules yourself, requires understanding what RBAC is and how its pieces fit together.

RBAC provides a mechanism for restricting who can do what with which resources in the Kubernetes API. It serves two equally important purposes:

  1. Security hardening — preventing unauthorized users from accessing cluster resources.
  2. Accident prevention — stopping a developer from accidentally deleting production workloads when they meant to delete their test environment.

Every request to the Kubernetes API server goes through two stages before it is processed: authentication (who are you?) and authorization (are you allowed to do this?). RBAC is the most common authorization mechanism in use today.

Core Concepts

Authentication vs Authorization

These two terms are often confused. Here is the distinction:

StageQuestion It AnswersExample
Authentication"Who are you?"The requester is user diana from the warehouse-devs group.
Authorization"Are you allowed to do this?"Is diana allowed to delete Pods in the warehouse Namespace?

Kubernetes does not have a built-in identity store. Instead, it integrates with external identity providers. Common authentication providers include:

  1. x509 client certificates
  2. Cloud identity providers — Azure Active Directory, AWS IAM
  3. Static token files
  4. Authentication webhooks

Once identity is established, RBAC takes over for authorization. If authorization fails, the API server returns an HTTP 403 Forbidden response.

User Identities vs Service Accounts

Kubernetes distinguishes two kinds of identity:

Identity TypeWho Uses ItManaged By
User AccountHuman operators, CI/CD pipelines, external toolsExternal identity provider (Azure AD, etc.)
Service AccountPods and components running inside the clusterKubernetes itself

A best practice is to give each application its own dedicated identity. Your production frontend, production backend, and staging services should all have distinct identities. Never share identities between applications — this limits the blast radius if one identity is compromised.

Roles and Role Bindings — The Core Building Blocks

RBAC is built from two types of objects that always work as a pair:

  1. A Role defines a set of permissions — what actions are allowed on which resources.
  2. A RoleBinding attaches a Role to one or more identities (users, groups, or service accounts).

Think of a Role as a job description and a RoleBinding as the act of assigning that job description to a person.

Namespace-Scoped vs Cluster-Scoped

Kubernetes provides two pairs of RBAC resources depending on scope:

ResourceScopeManages Access To
RoleNamespaceResources within a single Namespace (Pods, Services, ConfigMaps, etc.)
RoleBindingNamespaceBinds a Role (or ClusterRole) to identities within a Namespace
ClusterRoleCluster-wideCluster-level resources (Nodes, Namespaces, PersistentVolumes) or any namespace
ClusterRoleBindingCluster-wideBinds a ClusterRole to identities across the entire cluster

Use Role + RoleBinding when you want to grant access to resources within a specific Namespace. Use ClusterRole + ClusterRoleBinding when you need access to cluster-wide resources (like listing Nodes) or when you want to grant the same permissions in every Namespace.

RBAC Verbs

When writing a Role, you must specify which verbs (actions) are allowed. Verbs map roughly to HTTP methods:

VerbHTTP MethodDescription
createPOSTCreate a new resource.
deleteDELETEDelete an existing resource.
getGETRetrieve a single resource.
listGETRetrieve a collection of resources.
patchPATCHPartially update an existing resource.
updatePUTFully replace an existing resource.
watchGETStream real-time updates to a resource.
proxyGETConnect to a resource via a streaming WebSocket proxy.

Built-In Cluster Roles

Kubernetes ships with a set of pre-built ClusterRoles for common use cases. Four of them are designed for end users:

ClusterRoleWhat It Grants
cluster-adminFull control over the entire cluster — every resource, every action.
adminFull control within a single Namespace (used with a RoleBinding).
editRead and write most resources in a Namespace (cannot manage RBAC itself).
viewRead-only access to most resources in a Namespace.

You can view all built-in ClusterRoles with:

kubectl get clusterroles

Warning: The built-in ClusterRoles are auto-reconciled by the Kubernetes API server on restart. If you modify a built-in ClusterRole, your changes will be overwritten the next time the API server restarts (e.g., during an upgrade). To prevent this, set the rbac.authorization.kubernetes.io/autoupdate: "false" annotation on the ClusterRole before making changes.

Hands-On: Kubernetes Commands

Checking Your Own Permissions

The most useful debugging command for RBAC is kubectl auth can-i. It tells you whether the current user is authorized to perform a specific action:

kubectl auth can-i create pods
kubectl auth can-i delete deployments -n warehouse

You can also check subresources like logs or port-forwarding:

kubectl auth can-i get pods --subresource=logs

Checking Another User's Permissions

Cluster administrators can impersonate any user to test their access:

kubectl auth can-i list pods --as=diana -n warehouse
kubectl auth can-i list pods --as-group=warehouse-devs -n warehouse

Listing Roles and Bindings

kubectl get roles -n warehouse
kubectl get rolebindings -n warehouse
kubectl get clusterroles
kubectl get clusterrolebindings

Describing a Role

To see the exact rules defined in a Role:

kubectl describe role inventory-api-role -n warehouse

Applying RBAC from Source Control

RBAC resources are plain YAML and should be version-controlled. Use kubectl auth reconcile instead of kubectl apply for RBAC resources — it safely merges permissions without destroying existing bindings:

kubectl auth reconcile -f rbac-config.yaml

To preview changes without applying them:

kubectl auth reconcile -f rbac-config.yaml --dry-run=client

Step-by-Step Example

In this walkthrough, we will build a realistic access control setup for a warehouse management system. A .NET Inventory API runs in a warehouse Namespace. We will define a Service Account for the application, a namespaced Role for developers, and a cluster-wide ClusterRole for listing Namespaces and Nodes.

Step 1: Create the Namespace

First, create a Namespace to isolate all warehouse resources:

kubectl create namespace warehouse

Step 2: Create a Service Account for the Application

Every application running in Kubernetes should have its own dedicated Service Account. Not only does this follow the least-privilege principle, it also makes audit logs easier to read — you can see exactly which application made which API requests. Save this as inventory-api-serviceaccount.yaml:

apiVersion: v1
kind: ServiceAccount
metadata:
name: inventory-api-sa
namespace: warehouse
labels:
app: inventory-api
team: warehouse

Apply it:

kubectl apply -f inventory-api-serviceaccount.yaml

By default, Pods run under the default Service Account in their Namespace. Assigning a dedicated Service Account lets you grant only the permissions that specific application needs.

Step 3: Create a Namespaced Role

A Role defines what operations are permitted on which resources, scoped to a single Namespace. This Role allows viewing Pods, Services, ConfigMaps, and reading Pod logs — the minimum permissions a developer needs to debug the Inventory API. Save this as inventory-api-role.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: warehouse
name: inventory-api-role
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]

Apply it:

kubectl apply -f inventory-api-role.yaml

Let's break down the key fields in the rules section:

  1. apiGroups — which API group the resources belong to. An empty string ("") means the core API group, which includes Pods, Services, and ConfigMaps. The apps group covers Deployments, ReplicaSets, and StatefulSets.
  2. resources — the resource types this rule applies to. pods/log is a subresource of Pods.
  3. verbs — the allowed actions. This Role only allows reading (get, list, watch) — not creating or deleting.

Step 4: Create a RoleBinding

A Role by itself grants nothing. You must bind it to one or more identities using a RoleBinding. This binding attaches the Role to the Service Account, to the developer diana, and to the entire warehouse-devs group. Save this as inventory-api-rolebinding.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: warehouse
name: inventory-api-rolebinding
subjects:
- kind: ServiceAccount
name: inventory-api-sa
namespace: warehouse
- kind: User
name: diana
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: warehouse-devs
apiGroup: rbac.authorization.k8s.io
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: inventory-api-role

Apply it:

kubectl apply -f inventory-api-rolebinding.yaml

The subjects list supports three kinds of identity:

  1. ServiceAccount — a Kubernetes-managed identity for Pods.
  2. User — a human user authenticated via an external provider.
  3. Group — a group of users. This is the most scalable approach: you add users to a group in your identity provider rather than editing the RoleBinding every time someone joins or leaves the team.

The roleRef field is immutable. Once a RoleBinding is created, you cannot change which Role it points to. To change the Role, you must delete and recreate the RoleBinding.

Step 5: Attach the Service Account to a Deployment

A Service Account only takes effect when a Pod uses it. Reference the Service Account in your Deployment template using serviceAccountName:

apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory-api
namespace: warehouse
labels:
app: inventory-api
spec:
replicas: 2
selector:
matchLabels:
app: inventory-api
template:
metadata:
labels:
app: inventory-api
spec:
serviceAccountName: inventory-api-sa
containers:
- name: inventory-api
image: mcr.microsoft.com/dotnet/aspnet:10.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 20

When this Pod runs, any code within the container that calls the Kubernetes API (via the in-cluster client) will authenticate as inventory-api-sa and be subject to the permissions defined in the Role.

Step 6: Create a ClusterRole for Cross-Namespace Read Access

Some resources exist at the cluster level — Nodes and Namespaces are not inside any Namespace. To grant developers read-only access to these, you need a ClusterRole. Save this as namespace-reader-clusterrole.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-reader
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list"]
kubectl apply -f namespace-reader-clusterrole.yaml

Step 7: Bind the ClusterRole to All Developers

A ClusterRoleBinding grants the permissions cluster-wide. Here we give the all-developers group the ability to list Namespaces and Nodes in the entire cluster. Save this as namespace-reader-clusterrolebinding.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: namespace-reader-binding
subjects:
- kind: Group
name: all-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: namespace-reader
kubectl apply -f namespace-reader-clusterrolebinding.yaml

Step 8: Verify Access with can-i

After creating the bindings, verify that the intended permissions work as expected.

Check that diana can list Pods in the warehouse Namespace:

kubectl auth can-i list pods --as=diana -n warehouse

Confirm she cannot delete Pods (which is not in the Role):

kubectl auth can-i delete pods --as=diana -n warehouse

Check that diana cannot access a different Namespace (the Role is namespace-scoped):

kubectl auth can-i list pods --as=diana -n production

Verify that the Service Account has the expected permissions:

kubectl auth can-i get pods \
--as=system:serviceaccount:warehouse:inventory-api-sa \
-n warehouse

The format for impersonating a Service Account is: system:serviceaccount:<namespace>:<serviceaccount-name>.

Step 9: Using Built-In Roles as a Shortcut

Instead of writing custom Roles for common patterns, you can use the built-in ClusterRoles directly. For example, to give a new developer read-only access to the warehouse Namespace:

kubectl create rolebinding warehouse-viewer \
--clusterrole=view \
--user=bob \
-n warehouse

Notice that we are creating a RoleBinding (namespace-scoped) that references the built-in view ClusterRole. This is a common pattern: define the permissions once in a ClusterRole and bind it in specific Namespaces with RoleBindings.

To give a team lead full control over the warehouse Namespace:

kubectl create rolebinding warehouse-admin \
--clusterrole=admin \
--user=alice \
-n warehouse

Step 10: Managing RBAC in Source Control

RBAC resources are plain YAML and should be stored in version control alongside your application manifests. This provides:

  1. Auditability — a full history of who was granted what and when.
  2. Accountability — changes can be reviewed in pull requests.
  3. Rollback — if a permission change causes problems, you can revert it.

Use kubectl auth reconcile to apply RBAC from source control. Unlike kubectl apply, it handles subtle merge semantics correctly for Role and RoleBinding resources:

kubectl auth reconcile -f inventory-api-role.yaml
kubectl auth reconcile -f inventory-api-rolebinding.yaml

Always preview changes with --dry-run before applying:

kubectl auth reconcile -f inventory-api-role.yaml --dry-run=client

Step 11: Clean Up

kubectl delete clusterrolebinding namespace-reader-binding
kubectl delete clusterrole namespace-reader
kubectl delete rolebinding inventory-api-rolebinding -n warehouse
kubectl delete role inventory-api-role -n warehouse
kubectl delete serviceaccount inventory-api-sa -n warehouse
kubectl delete namespace warehouse

Summary

RBAC is the primary mechanism Kubernetes uses to control who can do what in your cluster. Here is what we covered:

  1. Every API request is first authenticated (who are you?) then authorized (are you allowed?). RBAC handles authorization.
  2. Kubernetes distinguishes User Accounts (humans and external tools) from Service Accounts (Pods and in-cluster components). Every application should use its own dedicated Service Account.
  3. RBAC has four resource types arranged in two pairs:
  4. Role + RoleBinding — namespace-scoped.
  5. ClusterRole + ClusterRoleBinding — cluster-wide.
  6. A Role defines permissions as a list of apiGroups, resources, and allowed verbs. A RoleBinding attaches that Role to users, groups, or Service Accounts.
  7. Use kubectl auth can-i to test permissions for yourself or for other identities using --as.
  8. Kubernetes ships with four built-in end-user ClusterRoles: cluster-admin, admin, edit, and view.
  9. Store RBAC configuration in source control and apply it with kubectl auth reconcile for safe merging.
  10. RBAC is a critical but not sufficient security layer. Pods running arbitrary code can still escalate privileges. Combine RBAC with proper Pod isolation (namespaces, network policies, Pod security standards) for strong multi-tenant security.


Share this lesson: