Principle of least privilege: Grant only the minimum permissions needed. Never use cluster-admin for applications. Don't bind ClusterRoles to ServiceAccounts unless absolutely necessary. Audit RBAC regularly.
Security & RBAC
Secure your cluster with Role-Based Access Control, service accounts, network policies, and pod security.
🧒 Simple Explanation (ELI5)
Imagine a building with many rooms. RBAC is the keycard system: you get a keycard (Role) that opens only the rooms you need. An intern's card opens the lobby; a manager's opens the executive floor. Service Accounts are keycards for automated systems (elevators, HVAC) — they only have access to what they need to function. Network Policies are the walls and doors themselves — controlling who can even walk to which rooms.
🔧 Technical Explanation
RBAC Components
| Resource | Scope | Purpose |
|---|---|---|
| Role | Namespace | Defines permissions (verbs on resources) within a namespace |
| ClusterRole | Cluster-wide | Defines permissions across all namespaces or for cluster-scoped resources |
| RoleBinding | Namespace | Binds a Role/ClusterRole to a user/group/service account in a namespace |
| ClusterRoleBinding | Cluster-wide | Binds a ClusterRole to a user/group/service account cluster-wide |
API Request Flow
Every API request goes through: Authentication (who are you?) → Authorization (are you allowed?) → Admission Control (any last checks?) → Execute.
📊 Visual: RBAC Model
⌨️ Hands-on
Create a Role and RoleBinding
# role.yaml - Read-only access to pods in "dev" namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: dev
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list"]
# rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: dev
subjects:
- kind: User
name: alice
apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
name: monitoring-sa
namespace: dev
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Service Accounts
# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: deploy-bot
namespace: production
---
# Give it permission to manage deployments
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-manager
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deploy-bot-binding
namespace: production
subjects:
- kind: ServiceAccount
name: deploy-bot
namespace: production
roleRef:
kind: Role
name: deployment-manager
apiGroup: rbac.authorization.k8s.io
Use Service Account in a Pod
apiVersion: v1
kind: Pod
metadata:
name: deploy-bot-pod
namespace: production
spec:
serviceAccountName: deploy-bot
automountServiceAccountToken: true
containers:
- name: bot
image: bitnami/kubectl:latest
command: ["sleep", "3600"]
Network Policy
# network-policy.yaml - Only allow traffic from app pods to database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow-app-only
namespace: production
spec:
podSelector:
matchLabels:
role: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: app
ports:
- protocol: TCP
port: 5432
# Test RBAC permissions kubectl auth can-i get pods --as=alice -n dev # yes kubectl auth can-i delete pods --as=alice -n dev # no kubectl auth can-i get pods --as=alice -n production # no # Check what a service account can do kubectl auth can-i --list --as=system:serviceaccount:production:deploy-bot -n production
🐛 Debugging Scenarios
Scenario 1: "Forbidden" / "Permission Denied"
# Error: "User 'alice' cannot list pods in namespace 'production'" # Debug: Check what permissions alice has kubectl auth can-i --list --as=alice -n production # Check existing RoleBindings in the namespace kubectl get rolebindings -n production -o wide # Check if there's a ClusterRoleBinding kubectl get clusterrolebindings -o wide | grep alice # Fix: Create appropriate RoleBinding kubectl create rolebinding alice-pods \ --role=pod-reader \ --user=alice \ -n production
Scenario 2: Pod Can't Access Kubernetes API
Symptom: Application in pod gets 403 when calling the K8s API.
- Check ServiceAccount:
kubectl get pod my-pod -o jsonpath='{.spec.serviceAccountName}' - Check if mount is enabled:
automountServiceAccountToken: true - Check the SA's permissions:
kubectl auth can-i --list --as=system:serviceaccount:ns:sa-name - Create/fix the Role and RoleBinding for the ServiceAccount
Scenario 3: Network Policy Blocking Traffic
# Check network policies in namespace kubectl get networkpolicies -n production # Describe to see rules kubectl describe networkpolicy db-allow-app-only -n production # Test connectivity kubectl exec app-pod -- curl db-service:5432 # If blocked, check pod labels match the policy's ingress "from" selector # Common issue: Default deny policy exists but no allow policy for your traffic # Fix: Add matching labels or create appropriate allow policy
🎯 Interview Questions
Beginner
RBAC (Role-Based Access Control) is a method of regulating access to Kubernetes resources based on the roles assigned to users or service accounts. It uses four objects: Role (namespace permissions), ClusterRole (cluster-wide permissions), RoleBinding (assigns Role to subjects in a namespace), ClusterRoleBinding (assigns ClusterRole cluster-wide).
A Service Account provides an identity for pods to interact with the Kubernetes API. Each namespace has a "default" SA. When pods need to call the K8s API (e.g., a CI/CD tool managing deployments), they use a Service Account with appropriate RBAC permissions bound to it.
Role: Scoped to a specific namespace. Can only grant permissions within that namespace. ClusterRole: Cluster-wide. Can grant permissions across all namespaces or for cluster-scoped resources (nodes, PVs, namespaces themselves). A ClusterRole can be used with a RoleBinding to grant its permissions in a single namespace.
A Network Policy controls traffic flow to/from pods at the IP/port level. By default, all pods can communicate with all other pods. Network Policies restrict this — e.g., "only app pods can talk to database pods on port 5432." Requires a CNI plugin that supports Network Policies (Calico, Cilium, Azure CNI).
It checks if a user or service account has permission to perform an action. kubectl auth can-i create deployments checks for the current user. kubectl auth can-i get pods --as=alice -n dev checks for another user. kubectl auth can-i --list shows all permissions. Essential for RBAC debugging.
Intermediate
PSA replaced PodSecurityPolicy (removed in K8s 1.25). It enforces pod security standards at the namespace level using labels. Three profiles: Privileged (unrestricted), Baseline (prevents known privilege escalations), Restricted (heavily restricted, follows hardening best practices). three modes: enforce, audit, warn.
Grant the minimum permissions required. Don't use cluster-admin for apps. Use namespace-scoped Roles instead of ClusterRoles. Don't mount service account tokens unless needed (automountServiceAccountToken: false). Use specific resource names where possible. Run containers as non-root. Drop all capabilities and add only what's needed.
1) Enable RBAC (default since 1.8). 2) Use strong authentication (certificates, OIDC, not basic auth). 3) Enable audit logging. 4) Restrict anonymous access. 5) Use admission controllers (OPA Gatekeeper, Kyverno) for policy enforcement. 6) Encrypt etcd at rest. 7) Rotate credentials regularly. 8) Use network policies to limit who can reach the API server.
securityContext defines privilege and access control for pods/containers. Key settings: runAsNonRoot: true, runAsUser: 1000, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, capabilities: drop: [ALL]. Pod-level securityContext applies to all containers; container-level overrides per-container.
Admission controllers intercept API requests after auth but before persistence. They can validate (reject bad requests) or mutate (modify requests). Built-in examples: LimitRanger, ResourceQuota, PodSecurity. External: OPA Gatekeeper, Kyverno — enforce custom policies like "all containers must have resource limits" or "no latest tag allowed".
Scenario-Based
Create a Role with specific verbs: verbs: ["get", "list", "watch"] on pods, pods/log; verbs: ["create"] on pods/exec (exec requires create verb on the subresource). Bind it to the developer via RoleBinding in the staging namespace. They can view and exec but can't create, update, or delete any resources.
1) Create a dedicated ServiceAccount in the production namespace. 2) Create a Role with only the needed permissions (create/update deployments, get pods). 3) Bind the Role to the SA. 4) Generate a token for the SA and store it securely in the CI/CD system (as a protected secret). 5) Never give cluster-admin. 6) Audit pipeline actions via K8s audit logs. 7) Use short-lived tokens (TokenRequest API) instead of long-lived ones.
Risk: If the pod is compromised, the attacker has full cluster access — can read all secrets, create privileged pods, access other namespaces, and even destroy the cluster. Fix: 1) Remove the ClusterRoleBinding immediately. 2) Create a scoped Role with only needed permissions. 3) Audit if anything was exploited during the exposure. 4) Implement OPA/Kyverno policies to prevent cluster-admin bindings to ServiceAccounts. 5) Set up alerts for ClusterRoleBinding creation.
Create a default-deny NetworkPolicy in both namespaces: podSelector: {} with empty ingress/egress (denies all). Then create specific allow policies for legitimate traffic. For namespace A: allow ingress only from namespace A pods. For namespace B: same. Cross-namespace traffic requires explicit namespaceSelector in the NetworkPolicy. Requires a CNI that supports Network Policies (Calico, Cilium).
Multiple layers: 1) Pod Security Admission: Label namespaces with pod-security.kubernetes.io/enforce: restricted. 2) OPA Gatekeeper/Kyverno: Create a policy that rejects pods without runAsNonRoot: true and runAsUser > 0. 3) Best practice in images: Use USER 1000 in Dockerfiles. 4) Audit existing pods: kubectl get pods -A -o json | jq '.items[] | select(.spec.securityContext.runAsNonRoot != true)'.
🌍 Real-World Use Case
A healthcare platform (HIPAA-compliant) implements defense-in-depth security:
- RBAC: Each team gets a namespace with scoped roles. No one has cluster-admin except the platform team.
- Service Accounts: Each microservice has its own SA with minimal permissions.
- Network Policies: Default-deny in all namespaces. Explicit allow rules for each service dependency.
- Pod Security: Restricted PSA enforced. All containers run as non-root, read-only filesystem, no privilege escalation.
- OPA Gatekeeper: Enforces policies — no latest tags, mandatory resource limits, mandatory labels, only approved registries.
- Audit logging: All API calls logged to SIEM for compliance and threat detection.
📝 Summary
- RBAC controls who can do what: Role + RoleBinding (namespace), ClusterRole + ClusterRoleBinding (cluster)
- Service Accounts provide pod identity for API access
- Network Policies control pod-to-pod traffic at the network level
- Pod Security: runAsNonRoot, readOnlyRootFilesystem, drop capabilities
- Principle of least privilege everywhere — minimal permissions, scoped access