Skip to content

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

AspectRoleClusterRole
ScopeSingle namespaceCluster-wide
Can grant access toNamespaced resources in one namespaceNamespaced resources across all namespaces, or cluster-scoped resources
Bound byRoleBindingClusterRoleBinding or RoleBinding
Use caseNamespace-specific permissionsCluster-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.

yaml
# 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 role

DANGER

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.

yaml
# 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.

yaml
# 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:

bash
# Impersonating another user
kubectl get pods --as=system:admin
kubectl get pods --as-group=system:masters

4. Wildcard (*) Permissions

yaml
# 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

yaml
# DANGEROUS: Can read all secrets (including SA tokens, TLS certs, passwords)
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get", "list", "watch"]

6. Pod Exec / Attach

yaml
# 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:

yaml
# 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

bash
# 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

bash
# 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 production

Find Roles with Dangerous Permissions

bash
# 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:

yaml
# 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

yaml
# 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.io

Read-Only Access Pattern

yaml
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.

bash
# Check the default service account
kubectl get sa default -n production -o yaml

Disable 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

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app
  namespace: production
automountServiceAccountToken: false    # Pods using this SA won't get tokens

At the Pod Level

yaml
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:latest

Priority

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

yaml
# 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
yaml
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.namespace

RBAC Verification Commands

bash
# 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-admin

Common RBAC Patterns for CKS

Pattern 1: Restrict User to Specific Namespace

bash
# 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=alice

Pattern 2: Grant Read-Only Cluster Access

bash
# Use the built-in view ClusterRole
kubectl create clusterrolebinding alice-viewer \
  --clusterrole=view \
  --user=alice

Pattern 3: Allow CI/CD Pipeline Deployments Only

yaml
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 operations

Exam Strategy

When auditing RBAC in the exam:

  1. Use kubectl auth can-i --list --as=<user> to see all permissions
  2. Check for wildcard (*) in verbs, resources, or apiGroups
  3. Look for bindings to cluster-admin
  4. Verify service accounts have automountServiceAccountToken: false where appropriate
  5. Ensure no unnecessary ClusterRoleBindings exist

Released under the MIT License.