RBAC Fundamentals: Roles, RoleBindings, and Scope
RBAC Fundamentals\n\nKubernetes RBAC controls who can do what inside a cluster. Every API request — creating a pod, reading a secret, deleting a deployment — is an RBAC authorization decision. If RBAC is misconfigured, any user, service account, or compromised workload can perform destructive actions.\n\n#### The four RBAC objects\n\nKubernetes RBAC has four object types that work together:\n\n1. Role — defines a set of permissions within a specific namespace:\n\nyaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n namespace: production\n name: pod-reader\nrules:\n- apiGroups: [\"\"]\n resources: [\"pods\"]\n verbs: [\"get\", \"list\", \"watch\"]\n\n\n2. RoleBinding — grants the permissions defined in a Role to a user, group, or service account within that namespace:\n\nyaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n name: read-pods\n namespace: production\nsubjects:\n- kind: User\n name: jane.doe@company.com\n apiGroup: rbac.authorization.k8s.io\nroleRef:\n kind: Role\n name: pod-reader\n apiGroup: rbac.authorization.k8s.io\n\n\n3. ClusterRole — defines permissions across the entire cluster (or on cluster-scoped resources):\n\nyaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name: node-reader\nrules:\n- apiGroups: [\"\"]\n resources: [\"nodes\"]\n verbs: [\"get\", \"list\"]\n\n\n4. ClusterRoleBinding — grants ClusterRole permissions across the entire cluster:\n\nyaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n name: read-nodes\nsubjects:\n- kind: ServiceAccount\n name: monitoring-agent\n namespace: monitoring\nroleRef:\n kind: ClusterRole\n name: node-reader\n apiGroup: rbac.authorization.k8s.io\n\n\n#### Namespace-scoped vs. cluster-scoped\n\nThe most important design decision in RBAC is scope:\n\n- Roles are namespace-scoped. They grant permissions on namespaced resources (pods, services, configmaps, secrets) within one namespace.\n- ClusterRoles are cluster-scoped. They grant permissions on cluster-scoped resources (nodes, persistent volumes, namespaces) or across all namespaces.\n\nyaml\n# ClusterRole that can read pods in ALL namespaces\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n name: global-pod-reader\nrules:\n- apiGroups: [\"\"]\n resources: [\"pods\"]\n verbs: [\"get\", \"list\", \"watch\"]\n\n\nA ClusterRole bound via ClusterRoleBinding grants permissions across the entire cluster. The same ClusterRole bound via RoleBinding grants permissions only within that namespace — this is a powerful pattern for reusing role definitions.\n\n#### The verbs\n\nRBAC verbs map to HTTP methods and actions:\n\n| Verb | Action |\n|---|---|\n| get | Read a single resource |\n| list | List all resources of a type |\n| watch | Watch for changes (streaming) |\n| create | Create a new resource |\n| update | Modify an existing resource |\n| patch | Partially modify a resource |\n| delete | Delete a resource |\n\nThe most dangerous verbs are create, patch, and delete on sensitive resources like secrets, service accounts, and RBAC objects themselves.\n\n#### Aggregated ClusterRoles\n\nKubernetes uses aggregated ClusterRoles to combine multiple roles. The default view, edit, and admin ClusterRoles are aggregated from multiple component roles:\n\nbash\n# View the default aggregated ClusterRoles\nkubectl get clusterroles view -o yaml\nkubectl get clusterroles edit -o yaml\nkubectl get clusterroles admin -o yaml\n\n\nThese default roles are convenient but often too permissive. The edit role allows creating and deleting most resources in a namespace, which is more than most teams need.
Service Accounts and Workload Identity
Service Accounts and Workload Identity\n\nEvery pod in Kubernetes runs under a service account. The service account's token is mounted into the pod and used to authenticate API requests. If a pod is compromised, the attacker inherits the service account's permissions — making service account RBAC critical for containing compromises.\n\n#### How service accounts work\n\nyaml\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: app-backend\n namespace: production\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: backend\n namespace: production\nspec:\n template:\n spec:\n serviceAccountName: app-backend\n containers:\n - name: app\n image: myapp:latest\n\n\nThe pod automatically mounts the service account token at /var/run/secrets/kubernetes.io/serviceaccount/token. The application uses this token to authenticate API requests.\n\n#### The default service account problem\n\nEvery namespace has a default service account. If you don't specify a service account in the pod spec, the pod uses default. In older Kubernetes versions, the default service account had broad permissions. In modern clusters (1.24+), the default token is not auto-mounted, but the service account still exists.\n\nbash\n# Check what the default service account can do\nkubectl auth can-i --list --as=system:serviceaccount:production:default -n production\n\n\n#### Creating least-privilege service accounts\n\nEach workload should have its own service account with only the permissions it needs:\n\nyaml\n# Service account for a backend app that only needs to read configmaps\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: backend-reader\n namespace: production\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n name: configmap-reader\n namespace: production\nrules:\n- apiGroups: [\"\"]\n resources: [\"configmaps\"]\n verbs: [\"get\", \"list\"]\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n name: backend-reads-config\n namespace: production\nsubjects:\n- kind: ServiceAccount\n name: backend-reader\n namespace: production\nroleRef:\n kind: Role\n name: configmap-reader\n apiGroup: rbac.authorization.k8s.io\n\n\n#### Disabling auto-mounted tokens\n\nFor pods that don't need to call the API server, disable token auto-mounting:\n\nyaml\napiVersion: v1\nkind: Pod\nmetadata:\n name: no-api-access\nspec:\n automountServiceAccountToken: false\n containers:\n - name: app\n image: myapp:latest\n\n\nThis is a simple but effective hardening: if the pod doesn't need API access, don't give it a token. If the pod is compromised, the attacker can't use the token to explore the cluster.\n\n#### Auditing service account permissions\n\nRegularly audit what each service account can do:\n\nbash\n# Check permissions for a specific service account\nkubectl auth can-i --list \\\n --as=system:serviceaccount:production:backend-reader \\\n -n production\n\n# Check if a service account can create pods (dangerous)\nkubectl auth can-i create pods \\\n --as=system:serviceaccount:production:backend-reader \\\n -n production\n\n# Check if a service account can read secrets (very dangerous)\nkubectl auth can-i get secrets \\\n --as=system:serviceaccount:production:backend-reader \\\n -n production\n\n\n#### The cluster-admin trap\n\nThe cluster-admin ClusterRole grants full control over the cluster. It should be extremely rare:\n\nbash\n# Find all cluster-admin bindings\nkubectl get clusterrolebindings -o json | \\\n jq -r '.items[] | select(.roleRef.name==\"cluster-admin\") | .subjects[] | \"\\\(.kind): \\\(.namespace)/\\(.name)\"'\n\n\nCommon sources of cluster-admin:\n- system:masters group (cluster creators)\n- cluster-admin bound to service accounts for tools that don't need it\n- Default bindings in managed Kubernetes services (EKS, GKE, AKS)\n\nEvery cluster-admin binding should be justified, documented, and reviewed regularly. A compromised cluster-admin token means full cluster compromise.
Practice Problems
Three teams share a namespace: frontend (needs to manage deployments and services), backend (needs to manage pods and configmaps), and ops (needs full access). Create Roles and RoleBindings for each team.
Solution
```yaml
# Frontend team: manage deployments and services
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: frontend-deployer
namespace: shared
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: frontend-team
namespace: shared
subjects:
- kind: Group
name: frontend-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: frontend-deployer
apiGroup: rbac.authorization.k8s.io
---
# Backend team: manage pods and configmaps
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: backend-manager
namespace: shared
rules:
- apiGroups: [""]
resources: ["pods", "configmaps"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: backend-team
namespace: shared
subjects:
- kind: Group
name: backend-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: backend-manager
apiGroup: rbac.authorization.k8s.io
---
# Ops team: full access (use existing admin role)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ops-team
namespace: shared
subjects:
- kind: Group
name: ops-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin
apiGroup: rbac.authorization.k8s.io
```
Note: The ops team uses the `admin` ClusterRole bound via RoleBinding, which grants admin permissions only within the `shared` namespace — not cluster-wide. A service account `deploy-bot` in the `ci-cd` namespace has a ClusterRoleBinding to `cluster-admin` because 'it was needed for a deployment once.' The deployment no longer needs cluster-admin. How do you audit and fix this?
Solution
1. **Audit current permissions**: `kubectl auth can-i --list --as=system:serviceaccount:ci-cd:deploy-bot -n ci-cd`
2. **Identify actual needs**: The deployment only needs to create deployments and configmaps in the `ci-cd` namespace.
3. **Create a least-privilege Role**:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: ci-cd
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update", "patch"]
```
4. **Create a RoleBinding**:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deploy-bot
namespace: ci-cd
subjects:
- kind: ServiceAccount
name: deploy-bot
namespace: ci-cd
roleRef:
kind: Role
name: deployer
apiGroup: rbac.authorization.k8s.io
```
5. **Remove the ClusterRoleBinding**: `kubectl delete clusterrolebinding deploy-bot-cluster-admin`
6. **Verify**: `kubectl auth can-i --list --as=system:serviceaccount:ci-cd:deploy-bot -n ci-cd` Quiz
1. A pod runs under service account `app-sa` in namespace `production`. The service account has a RoleBinding to a Role that allows `get` and `list` on pods. An attacker compromises the pod. What can the attacker do with the service account token?
2. What is the difference between a Role and a ClusterRole?
3. Why is it dangerous to bind `cluster-admin` to a service account used by a CI/CD pipeline?
Flashcards
Question
What are the four RBAC object types in Kubernetes?
Click to reveal answer
Answer
Role (namespace-scoped permissions), RoleBinding (grants Role to a subject in a namespace), ClusterRole (cluster-scoped permissions), ClusterRoleBinding (grants ClusterRole across the cluster).
Question
What is the most important design decision in Kubernetes RBAC?
Click to reveal answer
Answer
Scope — namespace-scoped (Role/RoleBinding) vs. cluster-scoped (ClusterRole/ClusterRoleBinding). Prefer namespace-scoped permissions and use cluster-scoped only when necessary.
Question
Why is service account RBAC critical for security?
Click to reveal answer
Answer
Every pod runs under a service account whose token is mounted into the pod. If the pod is compromised, the attacker inherits the service account's permissions. Least-privilege service accounts contain compromises.
Question
What does `automountServiceAccountToken: false` do?
Click to reveal answer
Answer
It prevents the pod from automatically mounting the service account token. If the pod doesn't need API server access, this removes the token entirely, preventing attackers from using it if the pod is compromised.
Revision Notes
Key Takeaways
- 1. Roles are namespace-scoped; ClusterRoles are cluster-scoped — prefer namespace scope for least privilege
- 2. Service account tokens are mounted into pods — least-privilege service accounts contain compromises
- 3. cluster-admin should be extremely rare and regularly audited
- 4. Disable auto-mounted tokens for pods that don't need API server access
Interview Tips
- • Use a concrete example: 'We found a CI/CD service account with cluster-admin — if the pipeline was compromised, the attacker would own the entire cluster.'
- • Explain the scope decision: 'We use namespace-scoped Roles for all workload permissions and reserve ClusterRoles for cluster-level resources like nodes.'
- • Discuss the service account pattern: 'Each workload has its own service account with only the permissions it needs — if compromised, the attacker can't move laterally.'
Cheat Sheet
Kubernetes RBAC Cheat Sheet
Phase: Container and Kubernetes Security | Level: intermediate
Four RBAC objects:
- Role: namespace-scoped permissions
- RoleBinding: grants Role to a subject in a namespace
- ClusterRole: cluster-scoped permissions
- ClusterRoleBinding: grants ClusterRole across the cluster
Scope decision:
- Prefer namespace-scoped (Role/RoleBinding) — least privilege
- Use ClusterRole only for cluster-scoped resources (nodes, PVs)
- ClusterRole + RoleBinding = namespace-scoped permissions from a reusable ClusterRole
Service account security:
- Each workload gets its own service account with minimal permissions
- Disable auto-mounted tokens for pods that don't need API access
- Audit with:
kubectl auth can-i --list --as=system:serviceaccount:NS:SA -n NS - Never bind cluster-admin to CI/CD service accounts
Dangerous permissions:
create/patchon service accounts (privilege escalation)geton secrets (credential theft)createon pods (backdoor workloads)cluster-adminbinding (full cluster control)
Audit commands:
# Check what a service account can do
kubectl auth can-i --list --as=system:serviceaccount:prod:app -n prod
# Find all cluster-admin bindings
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin")'
Continue Learning
Before You Start
- No prerequisites listed.