RBAC — Least Privilege
Create a service account with cluster-admin and prove it can delete nodes, read any secret, and escalate privileges. Then create a properly scoped service account that can only read ConfigMaps and Secrets in one namespace — nothing else.
The Mistake: cluster-admin on a Service Account
This is one of the most common Kubernetes misconfigurations in real clusters. A developer needed "admin" access for convenience — and gave a service account full cluster control.
kubectl create namespace rbac-demo
kubectl create serviceaccount over-privileged-sa -n rbac-demo
kubectl create clusterrolebinding over-privileged-binding \
--clusterrole=cluster-admin \
--serviceaccount=rbac-demo:over-privileged-sa
Test what this account can do
SA="system:serviceaccount:rbac-demo:over-privileged-sa"
kubectl auth can-i list secrets -n kube-system --as=$SA
kubectl auth can-i delete nodes --as=$SA
kubectl auth can-i create clusterrolebindings --as=$SA
kubectl auth can-i '*' '*' --as=$SA
Expected: yes / yes / yes / yes
A compromised pod using this service account has full cluster control — it can delete nodes, escalate its own privileges, and read every secret.
The Fix: Least Privilege
Scenario: A backend API needs to read ConfigMaps and Secrets in its own namespace. Nothing else.
kubectl create serviceaccount backend-api-sa -n rbac-demo
cat > backend-role.yaml << 'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: backend-api-role
namespace: rbac-demo
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: backend-api-binding
namespace: rbac-demo
subjects:
- kind: ServiceAccount
name: backend-api-sa
namespace: rbac-demo
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: backend-api-role
EOF
kubectl apply -f backend-role.yaml
Verify least-privilege permissions
SA_BACKEND="system:serviceaccount:rbac-demo:backend-api-sa"
echo "=== Allowed ==="
kubectl auth can-i list configmaps -n rbac-demo --as=$SA_BACKEND
kubectl auth can-i get secrets -n rbac-demo --as=$SA_BACKEND
echo "=== Blocked ==="
kubectl auth can-i delete pods -n rbac-demo --as=$SA_BACKEND
kubectl auth can-i list secrets -n kube-system --as=$SA_BACKEND
kubectl auth can-i create clusterrolebindings --as=$SA_BACKEND
Expected: yes / yes / no / no / no
Audit: Find All cluster-admin Accounts
kubectl get clusterrolebindings -o json | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for item in data['items']:
if item.get('roleRef', {}).get('name') == 'cluster-admin':
for s in item.get('subjects', []):
print(f\" {s.get('kind')}/{s.get('namespace','cluster')}/{s.get('name')} via {item['metadata']['name']}\")
"
Run this periodically in production to find over-privileged accounts before attackers do.
Cleanup
kubectl delete namespace rbac-demo
kubectl delete clusterrolebinding over-privileged-binding
- Previous lesson
- Mutual TLS with Istio Service Mesh
- Next lesson
- Kubernetes Audit Logging