Module 2.1: RBAC Deep Dive
Complexity:
[MEDIUM]- Core security skillTime to Complete: 45-50 minutes
Prerequisites: CKA RBAC knowledge, ServiceAccounts basics
What You’ll Be Able to Do
Section titled “What You’ll Be Able to Do”After completing this module, you will be able to:
- Audit RBAC configurations to identify over-permissioned roles and privilege escalation paths
- Implement least-privilege Roles and ClusterRoles for specific workload requirements
- Trace effective permissions for any user or ServiceAccount through RoleBindings
- Diagnose RBAC-related access denials and fix them without granting excessive permissions
Why This Module Matters
Section titled “Why This Module Matters”RBAC is the access control mechanism for Kubernetes. CKA taught you to create Roles and RoleBindings. CKS goes deeper: you must audit RBAC for over-permissioned accounts, understand escalation paths, and implement least privilege.
Misconfigured RBAC is a top Kubernetes vulnerability.
RBAC Review
Section titled “RBAC Review”┌─────────────────────────────────────────────────────────────┐│ RBAC COMPONENTS │├─────────────────────────────────────────────────────────────┤│ ││ Role/ClusterRole ││ └── Defines WHAT actions are allowed ││ ├── apiGroups: ["", "apps", "batch"] ││ ├── resources: ["pods", "deployments"] ││ └── verbs: ["get", "list", "create", "delete"] ││ ││ RoleBinding/ClusterRoleBinding ││ └── Defines WHO gets the permissions ││ ├── subjects: [users, groups, serviceaccounts] ││ └── roleRef: [Role or ClusterRole] ││ ││ Scope: ││ ├── Role + RoleBinding = namespace-scoped ││ ├── ClusterRole + ClusterRoleBinding = cluster-wide ││ └── ClusterRole + RoleBinding = reusable in namespace ││ │└─────────────────────────────────────────────────────────────┘Dangerous RBAC Patterns
Section titled “Dangerous RBAC Patterns”Pattern 1: Wildcard Permissions
Section titled “Pattern 1: Wildcard Permissions”# DANGEROUS: Allows everythingapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: too-permissiverules:- apiGroups: ["*"] resources: ["*"] verbs: ["*"]
# WHY IT'S BAD:# - Equivalent to cluster-admin# - Can access secrets, modify RBAC, delete anything# - Violates least privilegePattern 2: Secrets Access
Section titled “Pattern 2: Secrets Access”# DANGEROUS: Can read all secretsapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: secret-readerrules:- apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "watch"]
# WHY IT'S BAD:# - Secrets contain passwords, tokens, certificates# - One secret can compromise entire applications# - Should be tightly scoped to specific secretsPattern 3: RBAC Escalation
Section titled “Pattern 3: RBAC Escalation”# DANGEROUS: Can modify RBACapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: rbac-modifierrules:- apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterroles", "clusterrolebindings"] verbs: ["create", "update", "patch"]
# WHY IT'S BAD:# - Can grant themselves cluster-admin# - Privilege escalation attack# - Only admins should modify RBACPattern 4: Pod Creation with Privileges
Section titled “Pattern 4: Pod Creation with Privileges”# DANGEROUS: Can create pods (potential escalation)apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: pod-creatorrules:- apiGroups: [""] resources: ["pods"] verbs: ["create"]
# WHY IT'S BAD:# - Can create privileged pods# - Can mount service account tokens# - Can escape container to node# - Needs Pod Security to be safeStop and think: A developer has a Role that allows
createonpodsbut nothing else. They claim they can’t do anything dangerous. But what if they create a pod withserviceAccountName: cluster-admin-saandautomountServiceAccountToken: true? How does pod creation become a privilege escalation vector?
Least Privilege Examples
Section titled “Least Privilege Examples”Good: Specific Resource Access
Section titled “Good: Specific Resource Access”apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: pod-viewer namespace: productionrules:- apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"]- apiGroups: [""] resources: ["pods/log"] verbs: ["get"]Good: Resource Names Restriction
Section titled “Good: Resource Names Restriction”apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: specific-configmap-reader namespace: apprules:- apiGroups: [""] resources: ["configmaps"] resourceNames: ["app-config", "feature-flags"] # Only these! verbs: ["get"]Good: Subresources Only
Section titled “Good: Subresources Only”apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: pod-executor namespace: debugrules:- apiGroups: [""] resources: ["pods/exec"] # Only exec, not full pod access verbs: ["create"]Auditing RBAC
Section titled “Auditing RBAC”Find Overpermissive Roles
Section titled “Find Overpermissive Roles”# List all ClusterRoles with wildcard permissionskubectl get clusterroles -o json | jq -r ' .items[] | select(.rules[]? | (.verbs[]? == "*") or (.resources[]? == "*") or (.apiGroups[]? == "*") ) | .metadata.name'
# Find roles that can read secretskubectl get clusterroles -o json | jq -r ' .items[] | select(.rules[]? | (.resources[]? | contains("secrets")) and ((.verbs[]? == "get") or (.verbs[]? == "*")) ) | .metadata.name'
# Find roles that can modify RBACkubectl get clusterroles -o json | jq -r ' .items[] | select(.rules[]? | (.apiGroups[]? == "rbac.authorization.k8s.io") and ((.verbs[]? == "create") or (.verbs[]? == "update") or (.verbs[]? == "*")) ) | .metadata.name'Check User Permissions
Section titled “Check User Permissions”# What can a specific user do?kubectl auth can-i --list --as=system:serviceaccount:default:myapp
# Can user create privileged pods?kubectl auth can-i create pods --as=developer
# Can user read secrets?kubectl auth can-i get secrets --as=system:serviceaccount:app:backend
# In specific namespacekubectl auth can-i delete deployments -n production --as=developerFind Bindings to Dangerous Roles
Section titled “Find Bindings to Dangerous Roles”# Who has cluster-admin?kubectl get clusterrolebindings -o json | jq -r ' .items[] | select(.roleRef.name == "cluster-admin") | "\(.metadata.name): \(.subjects[]?.name // "unknown")"'
# List all ClusterRoleBindingskubectl get clusterrolebindings -o wide
# Describe suspicious bindingkubectl describe clusterrolebinding suspicious-bindingRBAC Escalation Prevention
Section titled “RBAC Escalation Prevention”┌─────────────────────────────────────────────────────────────┐│ RBAC ESCALATION PATHS │├─────────────────────────────────────────────────────────────┤│ ││ Direct Escalation: ││ ───────────────────────────────────────────────────────── ││ 1. Create/update ClusterRoleBindings ││ → Bind self to cluster-admin ││ ││ 2. Create/update ClusterRoles ││ → Add * permissions ││ ││ Indirect Escalation: ││ ───────────────────────────────────────────────────────── ││ 3. Create pods in any namespace ││ → Mount privileged ServiceAccount ││ ││ 4. Create pods with node access ││ → Read kubelet credentials ││ ││ 5. Impersonate users ││ → Act as cluster-admin ││ ││ Prevention: ││ ───────────────────────────────────────────────────────── ││ • Never give RBAC modification rights loosely ││ • Use Pod Security Admission ││ • Audit escalation verbs regularly ││ │└─────────────────────────────────────────────────────────────┘What would happen if: You find a ClusterRoleBinding that grants
cluster-adminto a ServiceAccount calledmonitoring-agentin themonitoringnamespace. The monitoring team says they need it to “see everything.” What’s the risk if an attacker compromises a pod running as that ServiceAccount?
The Escalate and Bind Verbs
Section titled “The Escalate and Bind Verbs”# The 'bind' verb allows creating bindings to roles# even without permissions the role grants- apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterrolebindings"] verbs: ["create"] # Plus...- apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterroles"] verbs: ["bind"] # ...this allows binding to any role!
# The 'escalate' verb allows granting permissions# that the user doesn't have- apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterroles"] verbs: ["escalate"] # Can add any permissions to roles!Best Practices
Section titled “Best Practices”┌─────────────────────────────────────────────────────────────┐│ RBAC BEST PRACTICES │├─────────────────────────────────────────────────────────────┤│ ││ 1. Least Privilege ││ └── Only grant what's needed ││ └── Prefer Roles over ClusterRoles ││ └── Use resourceNames when possible ││ ││ 2. No Wildcards ││ └── Never use "*" in production ││ └── List specific resources and verbs ││ ││ 3. Audit Regularly ││ └── Review cluster-admin bindings ││ └── Check for secret access ││ └── Monitor RBAC changes ││ ││ 4. Namespace Isolation ││ └── One ServiceAccount per application ││ └── Roles scoped to namespace ││ ││ 5. Protect RBAC Resources ││ └── Only cluster admins modify RBAC ││ └── Audit bind/escalate verbs ││ │└─────────────────────────────────────────────────────────────┘Real Exam Scenarios
Section titled “Real Exam Scenarios”Scenario 1: Reduce Permissions
Section titled “Scenario 1: Reduce Permissions”# Given: ServiceAccount with too many permissions# Task: Reduce to only get/list pods
# Check current permissionskubectl auth can-i --list --as=system:serviceaccount:app:backend -n app
# Find the rolebindingkubectl get rolebindings -n app -o wide
# Check the rolekubectl get role backend-role -n app -o yaml
# Create restricted rolecat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: backend-role namespace: apprules:- apiGroups: [""] resources: ["pods"] verbs: ["get", "list"]EOF
# Verifykubectl auth can-i delete pods --as=system:serviceaccount:app:backend -n app# Should return "no"Scenario 2: Find and Remove Dangerous Binding
Section titled “Scenario 2: Find and Remove Dangerous Binding”# Find who has cluster-adminkubectl get clusterrolebindings -o json | jq -r ' .items[] | select(.roleRef.name == "cluster-admin") | .metadata.name'
# Remove inappropriate bindingkubectl delete clusterrolebinding developer-adminScenario 3: Create Least-Privilege Role
Section titled “Scenario 3: Create Least-Privilege Role”# Requirement: App needs to read configmaps and create eventscat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: app-role namespace: myapprules:- apiGroups: [""] resources: ["configmaps"] verbs: ["get", "list", "watch"]- apiGroups: [""] resources: ["events"] verbs: ["create"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: app-binding namespace: myappsubjects:- kind: ServiceAccount name: myapp-sa namespace: myapproleRef: kind: Role name: app-role apiGroup: rbac.authorization.k8s.ioEOFPause and predict: You run
kubectl auth can-i --list --as=system:serviceaccount:default:defaultand see permissions toget,list, andwatchsecrets cluster-wide. You didn’t create any RoleBindings for the default ServiceAccount. Where are these permissions coming from?
RBAC Debugging
Section titled “RBAC Debugging”# Test as specific userkubectl auth can-i create pods --as=jane
# Test as ServiceAccountkubectl auth can-i get secrets --as=system:serviceaccount:default:myapp
# List all permissionskubectl auth can-i --list --as=jane
# Why can/can't user do something?kubectl auth can-i create pods --as=jane -v=5
# Check who can do somethingkubectl auth who-can create podskubectl auth who-can delete secrets -n productionDid You Know?
Section titled “Did You Know?”-
Kubernetes doesn’t have a ‘deny’ rule. RBAC is purely additive—you can only grant permissions, not explicitly deny them. To restrict access, simply don’t grant it.
-
The ‘system:masters’ group is hardcoded to have cluster-admin. You can’t remove it via RBAC. If someone is in this group, they have full access.
-
‘escalate’ and ‘bind’ verbs were added specifically to prevent privilege escalation. Before Kubernetes 1.12, anyone who could create RoleBindings could bind to cluster-admin!
-
Aggregated ClusterRoles (like admin, edit, view) automatically include rules from other roles labeled with the aggregation label. This is how CRDs extend built-in roles.
Common Mistakes
Section titled “Common Mistakes”| Mistake | Why It Hurts | Solution |
|---|---|---|
| Giving cluster-admin to developers | Full access to everything | Use edit or custom roles |
| Using ClusterRoles when Role works | Excessive scope | Prefer namespace-scoped |
| Wildcards in production | No access control | List specific permissions |
| Not auditing bindings | Unknown who has access | Regular RBAC reviews |
| Ignoring ServiceAccount defaults | Default SA may have permissions | Disable auto-mount, use specific SA |
-
During a security audit, you discover a ClusterRole with
apiGroups: ["*"], resources: ["*"], verbs: ["*"]bound to a ServiceAccount calleddeploy-botin theci-cdnamespace. The CI/CD team says they need broad access to deploy applications. How do you reduce the risk while keeping their pipeline functional?Answer
A wildcard ClusterRole is effectively `cluster-admin` -- if the CI/CD pipeline is compromised, an attacker controls the entire cluster. Replace it with a scoped Role (not ClusterRole) in the target namespaces, granting only the specific resources and verbs the pipeline needs: typically `create`, `update`, `patch` on `deployments`, `services`, `configmaps`, and `secrets` in specific namespaces. Use `resourceNames` where possible. The pipeline should never need access to RBAC resources, nodes, or cluster-wide secrets. Audit with `kubectl auth can-i --list` before and after to verify the reduction. -
A penetration tester reports they escalated from a compromised application pod to cluster-admin. The pod’s ServiceAccount only had
getandlistonpods. Investigation reveals the SA also hadcreateonpodsin a namespace where a ServiceAccount withcluster-adminbinding existed. Explain the escalation path.Answer
The attacker created a new pod with `serviceAccountName` set to the cluster-admin ServiceAccount and `automountServiceAccountToken: true`. When the pod started, the cluster-admin token was mounted at `/var/run/secrets/kubernetes.io/serviceaccount/token`. The attacker exec'd into the pod and used the token to call the API with full cluster-admin privileges. This is why pod creation is a dangerous permission -- it's an indirect escalation path. Prevention requires Pod Security Admission to restrict ServiceAccount usage, and the `bind` verb should be tightly controlled. -
Your SOC team detects unusual API calls: someone is listing secrets across all namespaces using a ServiceAccount from the
monitoringnamespace. The monitoring team says their tools only need pod metrics. How do you trace the source of these permissions and fix it?Answer
Trace the permissions: run `kubectl get clusterrolebindings -o json | jq '.items[] | select(.subjects[]?.name == "")' ` to find which ClusterRoleBinding grants the access. Then inspect the referenced ClusterRole with `kubectl get clusterrole -o yaml`. Likely someone bound the SA to `view` or `cluster-admin` instead of creating a custom role. Fix by deleting the overpermissive binding and creating a new Role with only `get` and `list` on `pods` and `pods/metrics` in the namespaces the monitoring tool actually needs. Verify with `kubectl auth can-i get secrets --as=system:serviceaccount:monitoring: ` -- it should return "no." -
A junior admin creates a ClusterRole that includes
verbs: ["create"]onclusterrolebindingsin therbac.authorization.k8s.ioAPI group and assigns it to a developer. The admin says “it’s fine, they can only create bindings, not modify existing ones.” Why is this a critical security misconfiguration?Answer
The ability to create ClusterRoleBindings is one of the most dangerous permissions in Kubernetes. The developer can create a new ClusterRoleBinding that binds themselves (or any ServiceAccount) to the `cluster-admin` ClusterRole, instantly gaining full cluster control. Kubernetes prevents this with the `bind` verb -- but if the developer also has `bind` on ClusterRoles, the escalation is trivial. Even without `bind`, creating RoleBindings to existing powerful roles is dangerous. Only cluster administrators should ever have write access to RBAC resources. Audit `escalate` and `bind` verbs regularly.
Hands-On Exercise
Section titled “Hands-On Exercise”Task: Audit and fix overpermissive RBAC.
# Setup: Create overpermissive configurationkubectl create namespace rbac-testkubectl create serviceaccount admin-app -n rbac-test
cat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: overpermissiverules:- apiGroups: ["*"] resources: ["*"] verbs: ["*"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: admin-app-bindingsubjects:- kind: ServiceAccount name: admin-app namespace: rbac-testroleRef: kind: ClusterRole name: overpermissive apiGroup: rbac.authorization.k8s.ioEOF
# Task 1: Audit the permissionskubectl auth can-i --list --as=system:serviceaccount:rbac-test:admin-app
# Task 2: Check if it can read secrets (it shouldn't!)kubectl auth can-i get secrets --as=system:serviceaccount:rbac-test:admin-app
# Task 3: Create a restricted role (only pods in namespace)cat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: pod-manager namespace: rbac-testrules:- apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch", "create", "delete"]EOF
# Task 4: Replace the ClusterRoleBinding with RoleBindingkubectl delete clusterrolebinding admin-app-binding
cat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: admin-app-binding namespace: rbac-testsubjects:- kind: ServiceAccount name: admin-app namespace: rbac-testroleRef: kind: Role name: pod-manager apiGroup: rbac.authorization.k8s.ioEOF
# Task 5: Verify permissions are now restrictedkubectl auth can-i get secrets --as=system:serviceaccount:rbac-test:admin-app# Should return "no"
kubectl auth can-i get pods --as=system:serviceaccount:rbac-test:admin-app -n rbac-test# Should return "yes"
kubectl auth can-i get pods --as=system:serviceaccount:rbac-test:admin-app -n default# Should return "no" (namespace-scoped)
# Cleanupkubectl delete namespace rbac-testkubectl delete clusterrole overpermissiveSuccess criteria: ServiceAccount can only manage pods in its own namespace.
Summary
Section titled “Summary”RBAC Security Principles:
- Least privilege always
- No wildcards in production
- Prefer Role over ClusterRole
- Use resourceNames when possible
Dangerous Patterns:
- Wildcard permissions (*, *)
- Secrets access without need
- RBAC modification rights
- bind/escalate verbs
Auditing Commands:
kubectl auth can-i --list --as=...kubectl auth who-can <verb> <resource>- Check ClusterRoleBindings to cluster-admin
Exam Tips:
- Know how to reduce permissions
- Practice finding overpermissive roles
- Understand escalation paths
Next Module
Section titled “Next Module”Module 2.2: ServiceAccount Security - Hardening ServiceAccounts and token management.