RBAC in Kubernetes: A Practical Guide
A practical guide to Kubernetes RBAC: Roles, ClusterRoles, RoleBindings, ClusterRoleBindings, ServiceAccounts, and real-world patterns for securing cluster access.
What you'll learn
- ✓How RBAC authorization works in Kubernetes
- ✓Creating Roles and ClusterRoles with specific permissions
- ✓Binding roles to users, groups, and ServiceAccounts
- ✓ServiceAccount patterns for pods and automation
- ✓Real-world RBAC configurations for teams and CI/CD
Prerequisites
- •Kubernetes fundamentals (Pods, Deployments, Namespaces)
- •Basic kubectl usage
- •Understanding of authentication vs authorization concepts
RBAC (Role-Based Access Control) controls who can do what in your Kubernetes cluster. Without RBAC, anyone with cluster access can create, modify, or delete any resource. With RBAC, you define precise permissions: this user can only read pods in this namespace, that service account can only create deployments in that namespace. Getting RBAC right is fundamental to running Kubernetes securely.
RBAC concepts
RBAC has four key resources:
- Role — Defines permissions within a single namespace
- ClusterRole — Defines permissions cluster-wide or across namespaces
- RoleBinding — Grants a Role to a user, group, or ServiceAccount within a namespace
- ClusterRoleBinding — Grants a ClusterRole cluster-wide
The relationship is simple: a Role says “what can be done,” and a RoleBinding says “who can do it.”
Role (what) + RoleBinding (who) = Permission in a namespace
ClusterRole (what) + ClusterRoleBinding (who) = Permission cluster-wide
ClusterRole (what) + RoleBinding (who) = Permission in a namespace (reusable role)
Creating a Role
A Role specifies a list of rules. Each rule grants access to specific API resources with specific verbs.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: development
rules:
- apiGroups: [""] # Core API group (pods, services, etc.)
resources: ["pods"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
kubectl apply -f role.yaml
kubectl get roles -n development
kubectl describe role pod-reader -n development
Understanding the rules
apiGroups — The API group containing the resource. Core resources (pods, services, configmaps) use "". Apps resources (deployments, statefulsets) use "apps". Batch resources (jobs, cronjobs) use "batch".
resources — The resource type. Use kubectl api-resources to see all available resources.
verbs — The allowed operations:
get— Read a single resourcelist— List all resourceswatch— Stream changescreate— Create new resourcesupdate— Modify existing resourcespatch— Partially modify resourcesdelete— Delete resourcesdeletecollection— Delete multiple resources
# A more comprehensive developer role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: development
rules:
# Full access to common workload resources
- apiGroups: ["", "apps", "batch"]
resources:
- pods
- deployments
- services
- configmaps
- jobs
- cronjobs
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Read-only access to secrets (can view but not modify)
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
# Can view logs and exec into pods (for debugging)
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
# Can view events
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list", "watch"]
Creating a ClusterRole
ClusterRoles work like Roles but are not namespaced. They define permissions that can be applied cluster-wide or reused across namespaces.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-admin
rules:
# Manage most resources within any namespace (when bound with RoleBinding)
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
resources: ["*"]
verbs: ["*"]
# But NOT cluster-scoped resources like nodes, namespaces, PVs
# Read-only ClusterRole for monitoring
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-viewer
rules:
- apiGroups: [""]
resources:
- pods
- services
- endpoints
- namespaces
- nodes
- persistentvolumes
- persistentvolumeclaims
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources:
- deployments
- replicasets
- statefulsets
- daemonsets
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "watch"]
RoleBinding
A RoleBinding grants the permissions defined in a Role to specific subjects (users, groups, or service accounts).
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: development
subjects:
# A user
- kind: User
name: alice
apiGroup: rbac.authorization.k8s.io
# A group
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
kubectl apply -f rolebinding.yaml
kubectl get rolebindings -n development
kubectl describe rolebinding read-pods -n development
Binding a ClusterRole in a namespace
You can bind a ClusterRole with a RoleBinding to limit it to a specific namespace. This is useful for reusing role definitions across namespaces.
# Reuse the cluster-viewer ClusterRole, but only in the development namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-viewer
namespace: development
subjects:
- kind: Group
name: junior-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-viewer
apiGroup: rbac.authorization.k8s.io
ClusterRoleBinding
Grants permissions cluster-wide.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admins
subjects:
- kind: Group
name: platform-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin # Built-in superuser role
apiGroup: rbac.authorization.k8s.io
Be very cautious with ClusterRoleBindings. They grant permissions across ALL namespaces, including kube-system. The built-in cluster-admin ClusterRole has full access to everything.
ServiceAccounts
ServiceAccounts provide identities for Pods. Every Pod runs with a ServiceAccount, and RBAC rules determine what that ServiceAccount can do.
# Create a ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
namespace: production
---
# Create a Role for the ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-role
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
resourceNames: ["app-config"] # Only this specific secret
---
# Bind the Role to the ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-role-binding
namespace: production
subjects:
- kind: ServiceAccount
name: app-service-account
namespace: production
roleRef:
kind: Role
name: app-role
apiGroup: rbac.authorization.k8s.io
---
# Use the ServiceAccount in a Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
template:
spec:
serviceAccountName: app-service-account
automountServiceAccountToken: true
containers:
- name: app
image: my-app:latest
resourceNames for fine-grained access
The resourceNames field restricts access to specific named resources.
rules:
# Can only read the "app-config" secret, not any other secret
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
resourceNames: ["app-config"]
# Can only delete specific deployments
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["delete"]
resourceNames: ["canary-deployment"]
CI/CD ServiceAccount
A common pattern: a ServiceAccount for CI/CD that can deploy to a specific namespace.
apiVersion: v1
kind: ServiceAccount
metadata:
name: ci-deployer
namespace: staging
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer-role
namespace: staging
rules:
# Can manage deployments and services
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# Can manage configmaps and secrets for app config
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "create", "update", "patch"]
# Can view pod status (for deploy verification)
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
# Can manage ingress
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ci-deployer-binding
namespace: staging
subjects:
- kind: ServiceAccount
name: ci-deployer
namespace: staging
roleRef:
kind: Role
name: deployer-role
apiGroup: rbac.authorization.k8s.io
# Create a token for the CI/CD ServiceAccount
kubectl create token ci-deployer -n staging --duration=1h
Testing RBAC permissions
# Check if a user/SA can perform an action
kubectl auth can-i get pods --namespace=development --as=alice
# yes
kubectl auth can-i delete deployments --namespace=production --as=alice
# no
# Check as a ServiceAccount
kubectl auth can-i get secrets --namespace=production \
--as=system:serviceaccount:production:app-service-account
# yes (if bound)
# List all permissions for a user
kubectl auth can-i --list --as=alice --namespace=development
Built-in ClusterRoles
Kubernetes ships with several default ClusterRoles:
kubectl get clusterroles | grep -E '^(cluster-admin|admin|edit|view)\s'
- cluster-admin — Full access to everything. Use sparingly.
- admin — Full access within a namespace (when bound with RoleBinding). Can manage most resources except ResourceQuotas and the namespace itself.
- edit — Read/write access to most resources in a namespace. No RBAC or role management.
- view — Read-only access to most resources. No secrets access.
# Give a developer edit access to a namespace using a built-in role
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-edit
namespace: development
subjects:
- kind: User
name: bob
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit
apiGroup: rbac.authorization.k8s.io
Aggregated ClusterRoles
Aggregated ClusterRoles combine rules from multiple ClusterRoles using label selectors.
# Base role that other roles aggregate into
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-view
labels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups: ["monitoring.coreos.com"]
resources: ["prometheusrules", "servicemonitors"]
verbs: ["get", "list", "watch"]
Any ClusterRole with the label rbac.authorization.k8s.io/aggregate-to-view: "true" automatically has its rules merged into the built-in view ClusterRole. This is how CRD operators extend the default roles.
Common mistakes
- Using cluster-admin for everything. Start with the minimum required permissions and add more as needed.
- Not setting
automountServiceAccountToken: falseon Pods that do not need API access. Every Pod mounts the default ServiceAccount token, which can be exploited. - Granting wildcard permissions (
resources: ["*"],verbs: ["*"]). Be explicit about what you are granting. - Forgetting namespace isolation. A RoleBinding only grants permissions in its own namespace. Users cannot accidentally access other namespaces unless you create ClusterRoleBindings.
- Not auditing RBAC regularly. Permissions accumulate over time. Review and prune them periodically.
# Disable auto-mounting for pods that don't need API access
apiVersion: v1
kind: Pod
metadata:
name: web-server
spec:
automountServiceAccountToken: false
containers:
- name: nginx
image: nginx:1.27
RBAC is the gatekeeper for your entire cluster. Follow the principle of least privilege: grant the minimum permissions needed, scope them to the narrowest namespace possible, and audit regularly. Start with the built-in view and edit ClusterRoles, then create custom Roles only when you need finer control.
Related articles
- Kubernetes Kubernetes Network Policies: Pod-to-Pod Security Guide
Lock down pod communication with Kubernetes NetworkPolicies. Learn ingress and egress rules, label selectors, and namespace isolation.
- Kubernetes Kubernetes cert-manager: Automate TLS Certificates
Set up cert-manager in Kubernetes to automate TLS certificate provisioning with Let's Encrypt. Covers Issuers, Certificates, Ingress integration.
- Kubernetes Kubernetes DaemonSets Explained with Practical Examples
Learn how DaemonSets run exactly one pod per node for logging, monitoring, and networking agents. Includes tolerations and update strategies.
- Kubernetes Kubernetes Persistent Volumes and Storage Classes
Understand PersistentVolumes, PersistentVolumeClaims, and StorageClasses to give your Kubernetes workloads reliable, portable storage.