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:
- Security hardening — preventing unauthorized users from accessing cluster resources.
- 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:
| Stage | Question It Answers | Example |
|---|---|---|
| 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:
- x509 client certificates
- Cloud identity providers — Azure Active Directory, AWS IAM
- Static token files
- 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 Type | Who Uses It | Managed By |
|---|---|---|
| User Account | Human operators, CI/CD pipelines, external tools | External identity provider (Azure AD, etc.) |
| Service Account | Pods and components running inside the cluster | Kubernetes 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:
- A Role defines a set of permissions — what actions are allowed on which resources.
- 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:
| Resource | Scope | Manages Access To |
|---|---|---|
Role | Namespace | Resources within a single Namespace (Pods, Services, ConfigMaps, etc.) |
RoleBinding | Namespace | Binds a Role (or ClusterRole) to identities within a Namespace |
ClusterRole | Cluster-wide | Cluster-level resources (Nodes, Namespaces, PersistentVolumes) or any namespace |
ClusterRoleBinding | Cluster-wide | Binds 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:
| Verb | HTTP Method | Description |
|---|---|---|
create | POST | Create a new resource. |
delete | DELETE | Delete an existing resource. |
get | GET | Retrieve a single resource. |
list | GET | Retrieve a collection of resources. |
patch | PATCH | Partially update an existing resource. |
update | PUT | Fully replace an existing resource. |
watch | GET | Stream real-time updates to a resource. |
proxy | GET | Connect 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:
| ClusterRole | What It Grants |
|---|---|
cluster-admin | Full control over the entire cluster — every resource, every action. |
admin | Full control within a single Namespace (used with a RoleBinding). |
edit | Read and write most resources in a Namespace (cannot manage RBAC itself). |
view | Read-only access to most resources in a Namespace. |
You can view all built-in ClusterRoles with:
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:
You can also check subresources like logs or port-forwarding:
Checking Another User's Permissions
Cluster administrators can impersonate any user to test their access:
Listing Roles and Bindings
Describing a Role
To see the exact rules defined in a Role:
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:
To preview changes without applying them:
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:
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:
Apply it:
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:
Apply it:
Let's break down the key fields in the rules section:
apiGroups— which API group the resources belong to. An empty string ("") means the core API group, which includes Pods, Services, and ConfigMaps. Theappsgroup covers Deployments, ReplicaSets, and StatefulSets.resources— the resource types this rule applies to.pods/logis a subresource of Pods.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:
Apply it:
The subjects list supports three kinds of identity:
ServiceAccount— a Kubernetes-managed identity for Pods.User— a human user authenticated via an external provider.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:
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:
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:
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:
Confirm she cannot delete Pods (which is not in the Role):
Check that diana cannot access a different Namespace (the Role is namespace-scoped):
Verify that the Service Account has the expected permissions:
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:
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:
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:
- Auditability — a full history of who was granted what and when.
- Accountability — changes can be reviewed in pull requests.
- 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:
Always preview changes with --dry-run before applying:
Step 11: Clean Up
Summary
RBAC is the primary mechanism Kubernetes uses to control who can do what in your cluster. Here is what we covered:
- Every API request is first authenticated (who are you?) then authorized (are you allowed?). RBAC handles authorization.
- 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.
- RBAC has four resource types arranged in two pairs:
Role+RoleBinding— namespace-scoped.ClusterRole+ClusterRoleBinding— cluster-wide.- A Role defines permissions as a list of
apiGroups,resources, and allowedverbs. A RoleBinding attaches that Role to users, groups, or Service Accounts. - Use
kubectl auth can-ito test permissions for yourself or for other identities using--as. - Kubernetes ships with four built-in end-user ClusterRoles:
cluster-admin,admin,edit, andview. - Store RBAC configuration in source control and apply it with
kubectl auth reconcilefor safe merging. - 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.