kubectl auth can-i <verb> <resource> --as=<user> [--namespace=<ns>]*) in verbs or resources grant access to all current AND future resources, including secrets and admission webhooks.rbac.authorization.k8s.io/v1 is the only supported API version in modern clusters).roleRef in RoleBinding/ClusterRoleBinding is IMMUTABLE after creation -- delete and recreate the binding to change the referenced role [src1]system:masters group -- it bypasses ALL RBAC authorization checks and cannot be restricted [src2]escalate and bind verbs allow privilege escalation -- restrict these to cluster administrators only [src2]create on pods/deployments implicitly grants access to any Secret, ConfigMap, or ServiceAccount mountable in that namespace [src2]| Resource | Scope | Purpose | API Group | Key Fields |
|---|---|---|---|---|
Role | Namespace | Define permissions within a single namespace | rbac.authorization.k8s.io/v1 | rules[].apiGroups, resources, verbs |
ClusterRole | Cluster | Define cluster-wide or cross-namespace permissions | rbac.authorization.k8s.io/v1 | rules[], aggregationRule |
RoleBinding | Namespace | Bind Role or ClusterRole to subjects in one namespace | rbac.authorization.k8s.io/v1 | subjects[], roleRef (immutable) |
ClusterRoleBinding | Cluster | Bind ClusterRole to subjects cluster-wide | rbac.authorization.k8s.io/v1 | subjects[], roleRef (immutable) |
ServiceAccount | Namespace | Identity for pods; bound to Roles via RoleBinding | v1 | automountServiceAccountToken |
| Verb | Type | Description |
|---|---|---|
get | Read | Retrieve a single resource by name |
list | Read | List all resources of a type (exposes secret values!) |
watch | Read | Stream changes to resources (exposes secret values!) |
create | Write | Create a new resource |
update | Write | Replace an entire resource |
patch | Write | Modify specific fields of a resource |
delete | Write | Delete a single resource |
deletecollection | Write | Delete all resources of a type |
escalate | Dangerous | Modify RBAC rules beyond your own permissions |
bind | Dangerous | Create bindings to roles with higher permissions |
impersonate | Dangerous | Act as another user, group, or service account |
| Default ClusterRole | Permissions | Use Case |
|---|---|---|
cluster-admin | Full access to ALL resources | Emergency break-glass only |
admin | Full access within a namespace (no ResourceQuota/Namespace) | Namespace administrators |
edit | Read/write most resources (no Roles/RoleBindings) | Developers deploying workloads |
view | Read-only most resources (no Secrets) | Auditors, read-only dashboards |
START
├── Need cluster-wide access to non-namespaced resources (nodes, namespaces, PVs)?
│ ├── YES → Use ClusterRole + ClusterRoleBinding
│ └── NO ↓
├── Need the same permissions across multiple namespaces?
│ ├── YES → Define a ClusterRole + RoleBinding per namespace
│ └── NO ↓
├── Need permissions in a single namespace only?
│ ├── YES → Use Role + RoleBinding (most secure, preferred)
│ └── NO ↓
├── Need to extend a built-in role (admin/edit/view)?
│ ├── YES → Use aggregated ClusterRole with matching labels
│ └── NO ↓
├── Subject is a pod/workload?
│ ├── YES → Create dedicated ServiceAccount + Role + RoleBinding
│ │ Set automountServiceAccountToken: false on SA
│ └── NO ↓
├── Subject is a human user?
│ ├── YES → Bind to Group (not individual User) for manageability
│ └── NO ↓
└── DEFAULT → Start with the built-in "view" ClusterRole; add permissions incrementally
Every application should have its own ServiceAccount rather than using the default ServiceAccount. Disable automatic token mounting unless the pod needs Kubernetes API access. [src2] [src3]
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp-sa
namespace: production
automountServiceAccountToken: false
Verify: kubectl get sa myapp-sa -n production → should show the ServiceAccount
Specify exact apiGroups, resources, and verbs. Use resourceNames to restrict access to specific objects when possible. Never use wildcards. [src1] [src2]
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myapp-role
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["myapp-config"]
verbs: ["get", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
Verify: kubectl describe role myapp-role -n production → should show the rules
Create a RoleBinding that connects the ServiceAccount to the Role. The roleRef is immutable -- to change the role reference, delete and recreate the binding. [src1]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myapp-binding
namespace: production
subjects:
- kind: ServiceAccount
name: myapp-sa
namespace: production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: myapp-role
Verify: kubectl auth can-i get configmaps --as=system:serviceaccount:production:myapp-sa -n production → yes
Reference the ServiceAccount in your pod spec. If the pod does need API access, set automountServiceAccountToken: true in the pod spec. [src3]
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
serviceAccountName: myapp-sa
automountServiceAccountToken: true
containers:
- name: myapp
image: myapp:1.2.3
Verify: kubectl get pods -n production -o jsonpath='{.items[0].spec.serviceAccountName}' → myapp-sa
Always verify both positive (allowed) and negative (denied) access after configuring RBAC. [src1]
# Should succeed (allowed by the Role)
kubectl auth can-i get configmaps --as=system:serviceaccount:production:myapp-sa -n production
# Should fail (not granted)
kubectl auth can-i create deployments --as=system:serviceaccount:production:myapp-sa -n production
# Check all permissions for a subject
kubectl auth can-i --list --as=system:serviceaccount:production:myapp-sa -n production
Verify: Positive tests return yes, negative tests return no
Use kubectl auth reconcile for idempotent RBAC application, especially in CI/CD pipelines. [src1]
kubectl auth reconcile -f rbac-manifests/ --dry-run=server
kubectl auth reconcile -f rbac-manifests/
Verify: kubectl get role,rolebinding -n production → should list all created objects
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: development
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-binding
namespace: development
subjects:
- kind: Group
name: dev-team
apiGroup: rbac.authorization.k8s.io
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: developer
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: metrics-reader
labels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list"]
apiVersion: v1
kind: ServiceAccount
metadata:
name: cicd-deployer
namespace: staging
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: staging
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["apps"]
resources: ["deployments/rollback"]
verbs: ["create"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cicd-deployer-binding
namespace: staging
subjects:
- kind: ServiceAccount
name: cicd-deployer
namespace: staging
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: deployer
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: auditor
rules:
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: auditor-binding
subjects:
- kind: Group
name: security-auditors
apiGroup: rbac.authorization.k8s.io
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: auditor
# BAD -- grants ALL permissions on ALL resources, including secrets,
# admission webhooks, and any future resources added to the cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: super-user
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
# GOOD -- grants only the specific permissions needed
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-manager
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list"]
# BAD -- if any pod using this SA is compromised, the attacker has
# full cluster-admin access to the entire cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: app-admin
subjects:
- kind: ServiceAccount
name: default
namespace: production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
# GOOD -- dedicated SA with minimum permissions, namespace-scoped
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp-sa
namespace: production
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myapp-binding
namespace: production
subjects:
- kind: ServiceAccount
name: myapp-sa
namespace: production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: myapp-role
# BAD -- the default SA is shared by all pods in the namespace;
# any RBAC granted to it applies to every pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: myapp:latest
# GOOD -- each workload has its own SA with tailored permissions
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
serviceAccountName: myapp-sa
automountServiceAccountToken: true
containers:
- name: myapp
image: myapp:1.2.3
# BAD -- list and watch on secrets exposes ALL secret values
# in plaintext, not just metadata
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-viewer
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
# GOOD -- access only specific secrets in one namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-secret-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["myapp-tls", "myapp-db-credentials"]
verbs: ["get"]
# BAD -- allows the subject to grant themselves any permission,
# effectively making them a cluster-admin
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: rbac-manager
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "clusterroles"]
verbs: ["get", "list", "create", "update", "patch", "escalate"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings", "clusterrolebindings"]
verbs: ["get", "list", "create", "update", "patch", "bind"]
# GOOD -- only cluster-admin (via break-glass) should modify RBAC objects
# Regular users should request RBAC changes through GitOps/PR workflow
# Do NOT create custom roles with escalate/bind verbs
list and watch on secrets only show metadata. In reality, they return full secret data in plaintext. Fix: Never grant list/watch on secrets; use get with resourceNames for specific secrets only. [src2]automountServiceAccountToken: false on the ServiceAccount and only enable it in the pod spec for pods that need it. [src3]system:authenticated grants access to anyone with a valid identity token, including external users. Fix: Never bind roles to system:authenticated or system:unauthenticated. [src3]rbac.authorization.k8s.io/aggregate-to-admin: "true" to a ClusterRole automatically merges its rules into the built-in admin role for all namespace admins. Fix: Audit aggregation labels carefully and test with kubectl auth can-i. [src1]# Check if a specific user/SA can perform an action
kubectl auth can-i get pods --as=jane -n production
# Check if a ServiceAccount can perform an action
kubectl auth can-i create deployments --as=system:serviceaccount:staging:cicd-deployer -n staging
# List all permissions for a subject in a namespace
kubectl auth can-i --list --as=system:serviceaccount:production:myapp-sa -n production
# List all Roles and RoleBindings in a namespace
kubectl get roles,rolebindings -n production
# List all ClusterRoles and ClusterRoleBindings
kubectl get clusterroles,clusterrolebindings
# Describe a specific Role to see its rules
kubectl describe role myapp-role -n production
# Find all bindings referencing cluster-admin
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name'
# Audit: find all subjects with cluster-admin access
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin") | {name: .metadata.name, subjects: .subjects}'
# Dry-run RBAC reconciliation (safe for CI/CD)
kubectl auth reconcile -f rbac-manifests/ --dry-run=server
# Check which API groups/resources are available
kubectl api-resources --verbs=list -o wide
| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| K8s 1.32+ | Current | No RBAC-specific changes | -- |
| K8s 1.30 | Current | ValidatingAdmissionPolicy GA -- supplements RBAC | Use VAP for complex admission rules beyond RBAC |
| K8s 1.24 | Supported | BoundServiceAccountTokenVolume GA; auto secret-based tokens removed | Tokens now auto-rotated via TokenRequest API |
| K8s 1.22 | Minimum recommended | rbac.authorization.k8s.io/v1beta1 REMOVED | Update all manifests to rbac.authorization.k8s.io/v1 |
| K8s 1.8 | Historical | RBAC promoted to GA (rbac.authorization.k8s.io/v1) | -- |
| K8s 1.6 | Historical (EOL) | RBAC introduced as beta | -- |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Controlling who can access which Kubernetes API resources | Need network-level traffic control between pods | NetworkPolicy |
| Restricting ServiceAccount permissions for workloads | Need to enforce pod security constraints (no privileged, no hostPath) | Pod Security Admission / Pod Security Standards |
| Granting namespace-scoped developer access | Need complex conditional policies (e.g., "allow only images from approved registries") | OPA/Gatekeeper or ValidatingAdmissionPolicy |
| Setting up CI/CD pipeline access to deploy | Need to restrict API access based on source IP or time of day | Admission webhooks or API server audit policies |
| Auditor read-only access across namespaces | Need data-level encryption or secret management | External secrets management (Vault, AWS Secrets Manager) |
system:masters group is hardcoded in the Kubernetes API server and bypasses all RBAC checks. There is no way to restrict a user in this group through RBAC.