Kubernetes Security
Secure every layer of the cluster: RBAC and service accounts, pod security standards, network policies, secrets, and image admission control. Kubernetes is only as safe as its weakest policy.
What is this?
Kubernetes security spans four planes: the API server (identity, RBAC, admission), the control plane (certificates, encryption at rest), the data plane (pod security context, runtime), and the network (network policies). Defaults are permissive — hardening means being explicit about who can do what, what pods may run, and what can reach what.
Why do you need it?
- The API server is the crown jewel: anyone with create pods can typically run a privileged container and own the node.
- Default RBAC is loose and many clusters run bindings nobody can explain — a permanent standing vulnerability.
- Network policies are the cluster firewall; without them every pod can talk to every pod.
- Pod Security Standards give you a repeatable, policy-as-code bar for what images may run.
- Secrets and TLS material in etcd must be encrypted and rotation-friendly.
Mental model
A hotel with a master key
Cluster-admin on a Kubernetes cluster
The general manager has a master key, but they do not hand it to the cleaning staff. Cluster-admin is the master key — grant it rarely, audit it, and scope everyone else to their floor.
How it works
- The API server authenticates requests (users, kubelet, service accounts) and authorizes them via RBAC role bindings.
- Admission controllers intercept every create/update and can mutate or reject requests — policy engines live here.
- Service accounts are the identity of pods; their tokens are what the pod uses to talk to the API.
- Pod Security Standards (privileged, baseline, restricted) are enforced by labels or admission policies.
- Network policies select pods by labels and define allowed ingress/egress with IP blocks and ports.
The cluster hardening stack, from API to workload.
Key commands
kubectl auth can-i create pods --as=system:serviceaccount:app:deployerTest whether a service account may create pods before granting it.
kubectl get rolebindings,clusterrolebindings -AList every binding to audit who holds which power.
kubectl get networkpolicy -AShow all network policies to confirm default-deny is in force.
kubectl label ns prod pod-security.kubernetes.io/enforce=restrictedEnforce the restricted Pod Security Standard on the prod namespace.
kubectl get secrets -AInventory secrets so you know what encryption-at-rest must protect.
Examples
A hardened pod spec
apiVersion: v1kind: Podmetadata: name: appspec: serviceAccountName: app-sa securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: app image: myapp:1.0 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"]The pod runs as its own service account, refuses root, blocks privilege escalation, uses the default seccomp profile, keeps the filesystem read-only and drops every capability — the restricted baseline.
In production
- RBAC is managed as code and reviewed quarterly; cluster-admin is a short-lived, break-glass role.
- Pod Security Standards are enforced by namespace label and validated by admission policies in CI.
- Default-deny network policies are pushed per namespace, with explicit allow rules per workload.
- Workload identity (Workload Identity Federation / IRSA) gives pods scoped cloud permissions without static keys.
- Runtime security (Falco) and image admission scanning (Kyverno, OPA) catch violations at runtime and admission.
Best practices
- Enable RBAC and never grant more than a binding proves necessary.
- Use distinct, least-privilege service accounts per workload, not the default one.
- Enforce Pod Security Standards at restricted where possible and review exceptions.
- Set a default-deny network policy and allow explicitly.
- Encrypt etcd, restrict who may read secrets, and rotate certificates.
Common mistakes
- Running everything under the default service account with wide bindings.
- Using hostNetwork and hostPath with privileged containers for convenience.
- Granting cluster-admin to solve an RBAC design problem once, then forgetting it.
- Copying permissive network policy examples without trimming them.
- Storing secrets in plaintext ConfigMaps or relying on defaults that never rotate.
Common issues
Forbidden: unable to create podsThe workload's service account lacks create permissions or a policy denied admission
Check kubectl auth can-i create pods --as=<sa> and review admission policy results.
Pod rejected by Pod Security StandardThe spec violates restricted/baseline requirements (root, capabilities, hostPath)
Apply the hardened securityContext or move the workload to a namespace with an allowed level.
Workload cannot reach its databaseA default-deny network policy now blocks egress
Add an explicit NetworkPolicy allowing egress to the database's pod selector and port.
Debugging tips
- Use kubectl auth can-i --list to see exactly what an identity can do.
- Check admission rejections in the API server audit log or the policy engine's results.
- Test network policy changes with a canary pod: kubectl run nettest --rm -it --image=busybox.
- Use kubectl describe pod to surface ImagePullBackOff, CreateContainerConfigError and admission denials.
Interview checkpoint
Hands-on lab
- Inspect the cluster RBAC surface
- Enforce Pod Security Standards by namespace
- Watch admission reject an insecure pod
- Verify RBAC allows and denies the right actions
Enforce Pod Security and RBAC
35 min · advanced
Quick quiz
Which resource maps a permission set to a set of subjects?
Which Pod Security Standard is the most restrictive?
Without any NetworkPolicy, every pod can communicate with every other pod by default.
Cheat sheet
kubectl auth can-i get podsTest current permissionskubectl get rolebindings -AAudit who holds whatkubectl create serviceaccount appDedicated workload identity--as=system:serviceaccount:ns:saAct as a service accountpod-security.kubernetes.io/enforce=restrictedEnforce PSS on a namespacerunAsNonRoot: trueReject root containerscapabilities: drop: [ALL]Strip privilegesNetworkPolicy default-denyClose the mesh by default