RBAC Deep Dive
Beyond CKA-Level RBAC
The CKS exam expects a deeper understanding of RBAC than the CKA. While CKA tests basic Role and RoleBinding creation, CKS focuses on identifying dangerous permissions, understanding privilege escalation vectors, securing service accounts, and implementing least privilege at scale.
CKS Exam Relevance
Expect questions that ask you to audit existing RBAC configurations, identify overly permissive roles, restrict service account permissions, and create tightly scoped roles. You should be able to spot dangerous permissions like escalate, bind, and impersonate.
RBAC Object Relationships
Key Differences: Role vs ClusterRole
| Aspect | Role | ClusterRole |
|---|---|---|
| Scope | Single namespace | Cluster-wide |
| Can grant access to | Namespaced resources in one namespace | Namespaced resources across all namespaces, or cluster-scoped resources |
| Bound by | RoleBinding | ClusterRoleBinding or RoleBinding |
| Use case | Namespace-specific permissions | Cluster-wide permissions or reusable templates |
ClusterRole + RoleBinding
A ClusterRole bound by a RoleBinding grants permissions only within the RoleBinding's namespace. This is a common pattern for creating reusable permission templates. A ClusterRole bound by a ClusterRoleBinding grants permissions across all namespaces.
Dangerous RBAC Permissions
The Most Dangerous Verbs
1. escalate Verb
The escalate verb on roles or clusterroles allows a user to grant permissions they do not themselves hold. This is essentially privilege escalation.
# DANGEROUS: Allows privilege escalation
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-escalator
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "clusterroles"]
verbs: ["escalate"] # Can grant any permission to any roleDANGER
A user with escalate on roles can modify any role to include any permission, including cluster-admin level access. This should only be granted to cluster administrators.
2. bind Verb
The bind verb on rolebindings or clusterrolebindings allows binding any role to any subject, regardless of whether the binder holds those permissions.
# DANGEROUS: Can bind any role to any user
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-binder
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings", "clusterrolebindings"]
verbs: ["bind"]3. impersonate Verb
The impersonate verb allows a user to act as another user, group, or service account.
# DANGEROUS: Can impersonate any user
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonator
rules:
- apiGroups: [""]
resources: ["users", "groups", "serviceaccounts"]
verbs: ["impersonate"]Usage:
# Impersonating another user
kubectl get pods --as=system:admin
kubectl get pods --as-group=system:masters4. Wildcard (*) Permissions
# DANGEROUS: Full access to everything
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: super-admin
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]Audit for Wildcards
Any role with * in apiGroups, resources, or verbs should be scrutinized. The built-in cluster-admin ClusterRole uses wildcards -- ensure it is bound only to trusted administrators.
5. Secrets Access
# DANGEROUS: Can read all secrets (including SA tokens, TLS certs, passwords)
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]6. Pod Exec / Attach
# DANGEROUS: Can execute commands in any pod
rules:
- apiGroups: [""]
resources: ["pods/exec", "pods/attach"]
verbs: ["create"]7. Create Pods (Indirect Escalation)
Creating pods allows mounting service account tokens, host paths, and running privileged containers:
# Potentially dangerous: Can create pods with any SA or host access
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create"]Auditing Existing RBAC Permissions
Find All ClusterRoleBindings for a Subject
# Find all ClusterRoleBindings for a specific user
kubectl get clusterrolebindings -o json | \
jq -r '.items[] | select(.subjects[]? | .name == "jane") | .metadata.name'
# Find all RoleBindings in all namespaces for a user
kubectl get rolebindings -A -o json | \
jq -r '.items[] | select(.subjects[]? | .name == "jane") | "\(.metadata.namespace)/\(.metadata.name)"'Check What a User Can Do
# Check if a user can perform a specific action
kubectl auth can-i create pods --as=jane
kubectl auth can-i delete secrets --as=jane -n production
kubectl auth can-i '*' '*' --as=jane # Check for wildcard access
# List all permissions for a user
kubectl auth can-i --list --as=jane
kubectl auth can-i --list --as=jane -n productionFind Roles with Dangerous Permissions
# Find ClusterRoles with wildcard verb access
kubectl get clusterroles -o json | \
jq -r '.items[] | select(.rules[]? | .verbs[]? == "*") | .metadata.name'
# Find ClusterRoles that can access secrets
kubectl get clusterroles -o json | \
jq -r '.items[] | select(.rules[]? | .resources[]? == "secrets") | .metadata.name'
# Find roles with escalate permission
kubectl get clusterroles -o json | \
jq -r '.items[] | select(.rules[]? | .verbs[]? == "escalate") | .metadata.name'
# Find all subjects bound to cluster-admin
kubectl get clusterrolebindings -o json | \
jq -r '.items[] | select(.roleRef.name == "cluster-admin") | .subjects[]'Least Privilege in Practice
Principle: Grant Only What Is Needed
Instead of broad roles, create specific roles for each use case:
# BAD: Overly broad
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: production
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
---
# GOOD: Specific and minimal
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: production
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update"]Namespace-Scoped Access Only
# Grant access ONLY within a specific namespace using RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-binding
namespace: development # Only grants access in "development" namespace
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.ioRead-Only Access Pattern
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: read-only
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "events", "namespaces", "nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "daemonsets", "statefulsets", "replicasets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["networkpolicies", "ingresses"]
verbs: ["get", "list", "watch"]
# NOTE: Deliberately excludes "secrets"Service Account Token Security
Default Service Account Risks
Every namespace has a default service account. By default, pods get this service account's token mounted automatically.
# Check the default service account
kubectl get sa default -n production -o yamlDisable Automounting Service Account Tokens
Security Best Practice
Most pods do not need to communicate with the Kubernetes API. Disable automatic token mounting at the ServiceAccount or Pod level.
At the ServiceAccount Level
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: production
automountServiceAccountToken: false # Pods using this SA won't get tokensAt the Pod Level
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: production
spec:
serviceAccountName: my-app
automountServiceAccountToken: false # Override at pod level
containers:
- name: app
image: my-app:latestPriority
The pod-level setting takes precedence over the ServiceAccount-level setting. If a pod explicitly sets automountServiceAccountToken: true, the token will be mounted even if the ServiceAccount says false.
Create Dedicated Service Accounts
# Step 1: Create a dedicated service account
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader
namespace: production
automountServiceAccountToken: true # Only because this SA needs API access
---
# Step 2: Create a minimal role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader-role
namespace: production
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
# Step 3: Bind the role to the service account
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader-binding
namespace: production
subjects:
- kind: ServiceAccount
name: pod-reader
namespace: production
roleRef:
kind: Role
name: pod-reader-role
apiGroup: rbac.authorization.k8s.io
---
# Step 4: Use the service account in a pod
apiVersion: v1
kind: Pod
metadata:
name: reader-pod
namespace: production
spec:
serviceAccountName: pod-reader
containers:
- name: reader
image: bitnami/kubectl:latest
command: ["sleep", "3600"]Bound Service Account Tokens (Kubernetes 1.24+)
Since Kubernetes 1.24, service account tokens are no longer auto-created as secrets. Instead, bound tokens are projected into pods with:
- Time-limited expiration
- Bound to specific pod and audience
spec:
containers:
- name: app
volumeMounts:
- name: kube-api-access
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
volumes:
- name: kube-api-access
projected:
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
fieldPath: metadata.namespaceRBAC Verification Commands
# Test permissions for current user
kubectl auth can-i create deployments
kubectl auth can-i delete pods -n production
# Test permissions for a specific user
kubectl auth can-i create pods --as=jane -n development
# Test permissions for a service account
kubectl auth can-i list secrets \
--as=system:serviceaccount:production:pod-reader \
-n production
# List all permissions for a service account
kubectl auth can-i --list \
--as=system:serviceaccount:production:pod-reader \
-n production
# Describe a role to see its rules
kubectl describe role developer -n production
# Describe a clusterrole
kubectl describe clusterrole cluster-adminCommon RBAC Patterns for CKS
Pattern 1: Restrict User to Specific Namespace
# Create namespace
kubectl create namespace team-a
# Create role with required permissions
kubectl create role team-a-developer \
--namespace=team-a \
--verb=get,list,watch,create,update,delete \
--resource=pods,deployments,services
# Bind role to user
kubectl create rolebinding team-a-developer-binding \
--namespace=team-a \
--role=team-a-developer \
--user=alicePattern 2: Grant Read-Only Cluster Access
# Use the built-in view ClusterRole
kubectl create clusterrolebinding alice-viewer \
--clusterrole=view \
--user=alicePattern 3: Allow CI/CD Pipeline Deployments Only
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update"]
# No access to secrets, pods/exec, or delete operationsExam Strategy
When auditing RBAC in the exam:
- Use
kubectl auth can-i --list --as=<user>to see all permissions - Check for wildcard (
*) in verbs, resources, or apiGroups - Look for bindings to
cluster-admin - Verify service accounts have
automountServiceAccountToken: falsewhere appropriate - Ensure no unnecessary ClusterRoleBindings exist