Kubernetes Security Deep Dive
Go beyond container basics: RBAC misconfiguration, admission control, NetworkPolicy, container-escape vectors, and why Kubernetes Secrets aren't encrypted by default.
Learning Objectives
- → Explain Kubernetes RBAC and how an over-permissioned ServiceAccount enables lateral movement
- → Explain admission controllers as the enforcement point for cluster-wide security policy
- → Apply NetworkPolicy to restrict pod-to-pod traffic beyond Kubernetes' default allow-all networking
- → Explain the risks of privileged containers and hostPath mounts as container-escape vectors
- → Describe why 'stored as a Secret' isn't the same as 'encrypted' in Kubernetes
Beyond "Container Security 101"
"Container Security: Docker & Kubernetes" introduced the basics. This lesson goes deeper into Kubernetes-specific attack surface: RBAC misconfiguration, admission control, network policy, and the container-escape vectors that turn a single compromised pod into full cluster compromise.
Kubernetes RBAC: Roles, Bindings, and Over-Permissioned ServiceAccounts
Kubernetes RBAC grants permissions via Role/ClusterRole objects bound to a subject (user, group, or ServiceAccount) through a RoleBinding/ClusterRoleBinding. Every pod runs as a ServiceAccount by default, and its associated token is automatically mounted into the pod's filesystem — meaning compromising one over-permissioned pod can hand an attacker that ServiceAccount's full RBAC permissions across the cluster, not just that one pod.
# Dangerously broad: effectively cluster-admin
kind: ClusterRoleBinding
subjects:
- kind: ServiceAccount
name: my-app-sa
roleRef:
kind: ClusterRole
name: cluster-admin
Admission Controllers: The Cluster's Policy Enforcement Point
Admission controllers intercept requests to the Kubernetes API after authentication/authorization but before an object is persisted, and can reject or mutate it. This is where cluster-wide security policy actually gets enforced — e.g. rejecting any pod spec that requests privileged mode, or requiring images to come from an approved registry. Pod Security Standards (the modern replacement for the deprecated PodSecurityPolicy) are typically enforced this way.
NetworkPolicy: Kubernetes Defaults to Allow-All
Without any NetworkPolicy defined, every pod in a cluster can reach every other pod, by default — flat, unrestricted networking. A NetworkPolicy resource restricts this explicitly, similar in spirit to security groups/firewall rules but scoped to pod labels and namespaces rather than IP addresses:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
podSelector:
matchLabels: { app: payments }
ingress:
- from:
- podSelector:
matchLabels: { app: api-gateway }
This example allows only pods labeled api-gateway to reach the payments pod — everything else is denied.
Container Escape Vectors
| Vector | Risk |
|---|---|
Privileged containers (privileged: true) |
Grants nearly all host capabilities — a compromised privileged container is close to a compromised host |
hostPath volume mounts |
Mounting the host's filesystem into a container can let a compromise write to sensitive host paths directly |
Docker socket mount (/var/run/docker.sock) |
Grants full control over the host's container runtime — equivalent to root on the host |
etcd and Secrets: Not Encrypted by Default
Kubernetes stores all cluster state, including Secret objects, in etcd — and by default, Secrets are stored in etcd as base64-encoded plaintext, not encrypted. Base64 is an encoding, not encryption; anyone with etcd read access (or a backup of it) can trivially decode every Secret in the cluster. Encryption at rest for etcd must be explicitly enabled.
Common Pitfalls
- Binding
cluster-adminto a ServiceAccount "just to get something working," then never revisiting it - Assuming Kubernetes Secrets are encrypted simply because they're a distinct object type from ConfigMaps
- Deploying without any NetworkPolicy and relying entirely on application-layer authentication for pod-to-pod security
- Allowing privileged containers or Docker socket mounts anywhere outside of narrowly justified infrastructure tooling
Every pod's ServiceAccount token is automatically mounted into its filesystem by default, extending its blast radius far beyond one pod.
Why does compromising one over-permissioned pod matter beyond that single pod?
Kubernetes networking is flat and unrestricted until you explicitly define otherwise.
Without any NetworkPolicy defined, how can pods in a Kubernetes cluster communicate by default?
Secrets get a distinct object type in the Kubernetes API, but that alone says nothing about how they're actually stored.
By default, how are Kubernetes Secret values stored in etcd?
💪 Exercises & Challenges
Kubernetes Security Deep Dive MCQ
Test your understanding of Kubernetes Security Deep Dive.
Scope Down an Over-Permissioned ServiceAccount
A pod's ServiceAccount is currently bound to cluster-admin via a ClusterRoleBinding, but the application only needs to read ConfigMaps in its own namespace. Write the correct Role and RoleBinding (nam
Trace the Cluster-Wide Secret Exposure
A cluster compromise investigation finds: an attacker gained code execution in a single pod running an internet-facing web application. That pod's ServiceAccount was bound to cluster-admin via a Clust