RBAC (Role-Based Access Control) is the authorization layer that answers: "Can this user/SA do this action on this resource?" It's the most important security mechanism in Kubernetes — every API request passes through RBAC after authentication. Mastering it is essential for CKA, CKS, and production cluster security.

1. The RBAC Model — Four Resources

WHO User / Group / ServiceAccount BINDING RoleBinding / ClusterRoleBinding WHAT Role / ClusterRole (verbs + resources) subjects roleRef Binding connects WHO (subjects) to WHAT (role with permissions) Role = "what can be done" | Binding = "who can do it"
ResourceScopePurpose
RoleNamespacedDefines permissions within a specific namespace
ClusterRoleCluster-wideDefines permissions cluster-wide OR reusable across namespaces
RoleBindingNamespacedGrants a Role (or ClusterRole) to subjects within a namespace
ClusterRoleBindingCluster-wideGrants a ClusterRole to subjects across the entire cluster
RBAC is additive (allow-only). There are no "deny" rules. If no binding grants a permission, it's implicitly denied. Multiple bindings can grant different permissions to the same subject — they add up. You can't take away a permission without removing the binding.

Combination Rules

Role TypeBinding TypeResult
RoleRoleBindingPermissions in one namespace ✓
ClusterRoleRoleBindingClusterRole's permissions scoped to one namespace ✓
ClusterRoleClusterRoleBindingPermissions across all namespaces ✓
RoleClusterRoleBinding❌ Invalid (can't bind a namespaced Role cluster-wide)
ClusterRole + RoleBinding = reusable permission template. Define a ClusterRole once (e.g., "pod-reader"), then bind it in each namespace separately. Each RoleBinding scopes the ClusterRole's permissions to its own namespace. This avoids duplicating Roles across namespaces.

2. Roles — Defining Permissions

Role (Namespaced)

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
  - apiGroups: [""]              # Core group (Pods, Services, etc.)
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/log"]      # Subresource
    verbs: ["get"]

ClusterRole (Cluster-Wide)

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-reader              # No namespace (cluster-scoped)
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list"]

The Verb Matrix

VerbHTTP MethodMeaning
getGET (single)Read one resource by name
listGET (collection)List all resources (filtered)
watchGET (streaming)Watch for changes (long-poll)
createPOSTCreate a new resource
updatePUTReplace an existing resource
patchPATCHPartially modify a resource
deleteDELETE (single)Delete one resource
deletecollectionDELETE (collection)Delete all resources matching criteria

Rule Building Blocks

# Multiple resources in one rule (AND between apiGroups+resources, OR between verbs):
rules:
  - apiGroups: ["", "apps"]                     # Core AND apps groups
    resources: ["pods", "deployments"]
    verbs: ["get", "list", "create", "delete"]

# Restrict to specific resource names:
  - apiGroups: [""]
    resources: ["secrets"]
    resourceNames: ["db-creds", "api-key"]      # Only these specific secrets
    verbs: ["get"]

# Subresources:
  - apiGroups: [""]
    resources: ["pods/exec"]                     # Pod exec
    verbs: ["create"]                            # exec requires "create" verb
  - apiGroups: ["apps"]
    resources: ["deployments/scale"]             # Scale subresource
    verbs: ["get", "update"]

# Non-resource URLs (health endpoints, metrics):
  - nonResourceURLs: ["/healthz", "/metrics"]
    verbs: ["get"]
CKA exam: generate Roles imperatively for speed:
kubectl create role pod-reader --verb=get,list,watch --resource=pods -n production --dry-run=client -o yaml
kubectl create clusterrole node-reader --verb=get,list --resource=nodes --dry-run=client -o yaml

3. Bindings — Connecting WHO to WHAT

RoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: production
subjects:                           # WHO gets the permissions
  - kind: User
    name: jane
    apiGroup: rbac.authorization.k8s.io
  - kind: Group
    name: developers
    apiGroup: rbac.authorization.k8s.io
  - kind: ServiceAccount
    name: ci-sa
    namespace: ci-cd               # SA namespace (can be different!)
roleRef:                            # WHAT permissions (immutable after creation!)
  kind: Role                        # or ClusterRole
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cluster-admin-binding      # No namespace (cluster-scoped)
subjects:
  - kind: User
    name: admin@company.com
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin              # Built-in superuser role
  apiGroup: rbac.authorization.k8s.io

Subject Types

KindName FormatExample
UserUsername from authentication (cert CN, OIDC claim)jane, admin@company.com
GroupGroup from authentication (cert O, OIDC groups)developers, system:masters
ServiceAccountSA name + namespaceci-sa in namespace ci-cd
roleRef is IMMUTABLE. Once a binding is created, you cannot change which Role it references. To change the role, you must delete and recreate the binding. This prevents accidental privilege escalation through binding modification.
# Imperative commands (fast for exams):
kubectl create rolebinding read-pods \
  --role=pod-reader \
  --user=jane \
  --serviceaccount=ci-cd:ci-sa \
  -n production

kubectl create clusterrolebinding admin-binding \
  --clusterrole=cluster-admin \
  --user=admin@company.com
CKA exam speed: kubectl create rolebinding and kubectl create clusterrolebinding support --user, --group, and --serviceaccount=ns:name flags. Use imperative creation, then verify with kubectl auth can-i.

4. Testing RBAC — kubectl auth can-i

# Check your own permissions:
kubectl auth can-i create deployments -n production
# yes

kubectl auth can-i delete nodes
# no

# Check as another user (requires admin):
kubectl auth can-i get secrets -n production --as=jane
# no

kubectl auth can-i get pods -n production --as=system:serviceaccount:ci-cd:ci-sa
# yes

# List ALL permissions for a user:
kubectl auth can-i --list --as=jane -n production
# Resources   Non-Resource URLs   Resource Names   Verbs
# pods        []                  []               [get list watch]
# pods/log    []                  []               [get]
kubectl auth can-i is your verification tool. After creating a Role + Binding, always test: kubectl auth can-i <verb> <resource> --as=<user> -n <namespace>. This proves the RBAC is working correctly before moving to the next question.

5. Built-in ClusterRoles

ClusterRolePermissionsUse Case
cluster-adminEverything (wildcard *)Cluster operators (danger!)
adminFull access within a namespace (no quota/RBAC modification)Namespace owner
editRead/write most resources in a namespace (no Roles/Bindings)Developers
viewRead-only access to most resources (no Secrets)Monitoring, auditors
# Give a developer full edit access in their namespace:
kubectl create rolebinding dev-edit \
  --clusterrole=edit \
  --user=jane \
  -n team-alpha

# ClusterRole "edit" used with RoleBinding → scoped to namespace team-alpha
# Jane can create/delete Pods, Deployments, Services in team-alpha only
Built-in roles use ClusterRole + RoleBinding pattern. The view, edit, and admin ClusterRoles are designed to be bound per-namespace via RoleBinding. You don't need to create your own Roles for common use cases — just bind the built-in ones. Only create custom Roles for specific fine-grained needs.

6. Common Patterns

# Pattern: CI/CD pipeline — create/update Deployments only
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: production
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services"]
    verbs: ["get", "list", "create", "update"]
# NO: delete, pods/exec, secrets — principle of least privilege
# Pattern: Monitoring — read everything, write nothing
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-reader
rules:
  - apiGroups: ["", "apps", "batch"]
    resources: ["*"]
    verbs: ["get", "list", "watch"]     # Read only, across all namespaces
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: []                            # Explicitly NO access to Secrets

Summary

ConceptKey Point
RoleNamespaced permissions (verbs + apiGroups + resources)
ClusterRoleCluster-wide permissions (or reusable template for namespaces)
RoleBindingGrants Role or ClusterRole to subjects in one namespace
ClusterRoleBindingGrants ClusterRole to subjects across all namespaces
Additive onlyNo deny rules. No binding = no permission.
roleRef immutableCan't change which Role a Binding references (delete + recreate)
Verbsget, list, watch, create, update, patch, delete, deletecollection
resourceNamesRestrict to specific named resources (fine-grained)
Subresourcespods/exec, deployments/scale, pods/log — separate permissions
Testingkubectl auth can-i — verify before moving on

📝 Quiz: RBAC Fundamentals

Q1: You create a Role in namespace "dev" and a ClusterRoleBinding referencing it. Does this work?

No. A ClusterRoleBinding can only reference a ClusterRole (not a namespaced Role). This is invalid — the binding must use a RoleBinding to reference a namespace-scoped Role. The valid combinations are: Role+RoleBinding, ClusterRole+RoleBinding, ClusterRole+ClusterRoleBinding.

Q2: A user has a RoleBinding in namespace "dev" granting "get pods" AND a ClusterRoleBinding granting "get nodes." Can they get pods in namespace "prod"?

No. The RoleBinding only grants "get pods" in the "dev" namespace — not cluster-wide. The ClusterRoleBinding grants "get nodes" (cluster-scoped resource), but doesn't help with Pods. To get pods in "prod," they'd need a separate RoleBinding in "prod" or a ClusterRoleBinding with pod permissions.

Q3: You need a CI/CD ServiceAccount to deploy apps but NOT read Secrets. How do you configure this?

Create a custom Role that lists only the needed resources:
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get","list","create","update","patch"]
  - apiGroups: [""]
    resources: ["services","configmaps"]
    verbs: ["get","list","create","update"]
Don't include secrets in any rule. RBAC is additive — if Secrets aren't granted, they're denied by default. Bind this Role to the CI SA via RoleBinding.

Q4: What's the difference between using ClusterRole "edit" with a RoleBinding vs a ClusterRoleBinding?

RoleBinding: Grants "edit" permissions only in the binding's namespace. User can edit resources in that one namespace.
ClusterRoleBinding: Grants "edit" permissions in ALL namespaces. User can edit resources everywhere in the cluster. Always prefer RoleBinding for least privilege — only use ClusterRoleBinding when cluster-wide access is truly needed.

Q5: A user can get pods but cannot kubectl exec into a Pod. What's missing?

They need permission for the pods/exec subresource with the create verb:
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create"]
kubectl exec creates a new exec session (POST to /api/v1/.../pods/name/exec), which requires create on the pods/exec subresource — separate from get pods.

Q6: How do you verify that ServiceAccount "deployer" in namespace "ci" can create Deployments in namespace "production"?

kubectl auth can-i create deployments -n production --as=system:serviceaccount:ci:deployer
The --as flag impersonates the SA. The full format for SAs is system:serviceaccount:<namespace>:<name>. If it returns "yes," the RBAC is correct.