Skip to content
intermediate Phase 17 · Container and Kubernetes Security

Kubernetes RBAC

Scope what users and service accounts can do inside the cluster.

55m
0 problems
Topic Progress 0%

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

0 / 2 solved
Design RBAC for a Multi-Team Namespace

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.
Audit and Fix Over-Permissive Service Account

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?

Question 1 options

2. What is the difference between a Role and a ClusterRole?

Question 2 options

3. Why is it dangerous to bind `cluster-admin` to a service account used by a CI/CD pipeline?

Question 3 options

Flashcards

Question

What are the four RBAC object types in Kubernetes?

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?

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?

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?

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/patch on service accounts (privilege escalation)
  • get on secrets (credential theft)
  • create on pods (backdoor workloads)
  • cluster-admin binding (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.