Project Lab 08: RBAC Governance & Admission Control
Authentication identifies the user, but Authorization defines their boundaries. In this lab, we will transition from a wide-open cluster to a "Zero-Trust" environment where every action is audited and governed by policy.
Reference Material:
docs/07-authorization/1-authorization-mechanisms.mddocs/07-authorization/2-rbac-service-accounts.mddocs/07-authorization/3-admission-controllers.md
1. OBJECTIVE: SECURITY GOVERNANCE
The goal is to implement three layers of security:
- User Authorization: Grant Seema (the Auditor from Lab 07) read-only access to a specific namespace.
- Workload Identity: Create a ServiceAccount for a "Namespace Watcher" app with minimal permissions.
- Admission Policy: Ensure no one (including Seema) can deploy insecure "Root" containers in the production namespace.
2. PHASE 1: RBAC FOR THE AUDITOR (USER SEEMA)
We will use the Reusable ClusterRole pattern. We define the permissions once at the cluster level but restrict them to a specific namespace for Seema.
2.1 Create the ClusterRole
This role allows viewing common resources but blocks access to sensitive data like Secrets.
# 01-auditor-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: auditor-read-only
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
2.2 Bind Seema to the 'finance' Namespace
Even though we used a ClusterRole, a RoleBinding confines Seema's power to just one namespace.
# 02-auditor-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: seema-finance-audit
namespace: finance
subjects:
- kind: User
name: seema # Matches the CN from Lab 07
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: auditor-read-only
apiGroup: rbac.authorization.k8s.io
3. PHASE 2: WORKLOAD IDENTITY (SERVICEACCOUNTS)
Applications should never use human credentials. We will create a ServiceAccount for a custom Python script that monitors Pod changes.
3.1 Create ServiceAccount and specialized Role
kubectl create namespace monitoring-system
kubectl create serviceaccount pod-watcher-sa -n monitoring-system
3.2 Implement Token Hardening
In production, we disable automounting if the app doesn't need to talk to the API. Here, our app does need it, but we want to restrict it.
# 03-sa-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-viewer-role
namespace: finance
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: watcher-binding
namespace: finance
subjects:
- kind: ServiceAccount
name: pod-watcher-sa
namespace: monitoring-system
roleRef:
kind: Role
name: pod-viewer-role
apiGroup: rbac.authorization.k8s.io
4. PHASE 3: ADMISSION CONTROL (POD SECURITY STANDARDS)
Even if Seema has RBAC permissions, we want to block her from creating dangerous Pods. We will use the built-in PodSecurity admission controller via namespace labels.
4.1 Enforce "Restricted" Profile on Finance
kubectl label namespace finance \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest
5. VERIFICATION & AUDIT (NINJA COMMANDS)
5.1 Test User Authorization
Verify Seema can list pods in finance but NOT in default.
# Verify access in Finance (using 'as' for impersonation)
kubectl auth can-i list pods -n finance --as=seema
# Output: yes
# Verify access in Default
kubectl auth can-i list pods -n default --as=seema
# Output: no
# Verify Secret protection
kubectl auth can-i get secrets -n finance --as=seema
# Output: no
5.2 Test Admission Control (The "Policy Block")
Try to deploy a non-compliant pod (running as root) into the finance namespace as Seema.
kubectl run root-pod --image=nginx -n finance --as=seema
Expected Output:
Error from server (Forbidden): pods "root-pod" is forbidden: violates PodSecurity "restricted:latest":
allowPrivilegeEscalation != false, runAsNonRoot != true, seccompProfile.type != "RuntimeDefault"...
Success: The Admission Controller blocked the request after authorization but before persistence to etcd.
5.3 Technical Insight: TokenReview API
How does the API Server internally verify these requests? When a ServiceAccount sends a token, the API Server uses the TokenReview API. You can simulate this:
# 1. Get the ServiceAccount Token
TOKEN=$(kubectl create token pod-watcher-sa -n monitoring-system)
# 2. Review the token (Self-Review)
kubectl get --raw /apis/authentication.k8s.io/v1/tokenreviews -d "{\"spec\":{\"token\":\"$TOKEN\"}}" | jq .
Observation: The JSON output will show the authenticated: true status and the specific groups the ServiceAccount belongs to.
6. TROUBLESHOOTING
6.1 Identify Admission Rejections
If a deployment fails to create Pods but the Deployment object looks fine, check the events:
kubectl get events -n finance --sort-by='.lastTimestamp'
Look for "FailedCreate" from the ReplicaSet controller.
6.2 Debugging RBAC Mismatches
If a user should have access but gets 403 Forbidden, check for typos in the subjects.name or apiGroup.
kubectl describe rolebinding seema-finance-audit -n finance
7. ARCHITECT'S KEY TAKEAWAYS
- RBAC is Additive: You start with zero permissions. Every Rule adds permission. You cannot "Deny" in RBAC.
- RoleBinding + ClusterRole = Scoped Power: This is the most efficient way to manage permissions in multi-tenant clusters.
- Admission Control is Content-Aware: RBAC checks "Who," but Admission checks "What." A secure cluster requires both.
- ServiceAccount Isolation: Always disable
automountServiceAccountToken: trueunless the Pod explicitly requires API access.