A widespread mental model holds that containers isolate workloads the way virtual machines do. They do not, and the difference determines how far an attacker gets after compromising a single application.
A virtual machine has its own kernel, and the hypervisor enforces separation in hardware. Containers on the same host share the host kernel. Isolation comes from kernel features — namespaces controlling what a process can see, cgroups controlling what it can consume, capabilities controlling what it may do — and every one of those is a software boundary that configuration can weaken or remove entirely.
The configuration that dissolves the boundary
Most container escapes do not exploit a kernel vulnerability. They exploit a container that was granted what it asked for.
- Privileged mode disables essentially all isolation. A privileged container can access host devices and is generally one step from full host compromise. It exists for legitimate reasons and is used far more widely than those reasons justify.
- The host filesystem mounted in. Mounting
/or sensitive host paths gives the container the ability to modify the host directly. - The container runtime socket mounted in. Access to the Docker socket is equivalent to root on the host, because the container can instruct the runtime to start a new privileged container. This appears constantly in CI runners and monitoring agents.
- Host namespaces shared. Sharing the host’s process, network or IPC namespace removes the separation those namespaces provided.
- Running as root. The default in many images. Combined with any other weakness, it removes the last obstacle.
- Excess capabilities. Capabilities such as those permitting arbitrary mounts or kernel module loading are escape primitives.
The practical baseline is the inverse of that list: run as a non-root user, drop all capabilities and add back only what is required, use a read-only root filesystem, and never mount the runtime socket into a workload.
Images: what you inherit
Every image inherits everything in its base layers, including packages the application never uses. A large general-purpose base image ships hundreds of binaries, and each is available to an attacker who achieves execution — the container equivalent of living off the land.
Minimal and distroless images address this directly by removing the shell and package manager entirely. An attacker who gains execution in a container with no shell has a substantially harder time, because the tooling they would ordinarily rely on is simply absent.
Two habits matter as much as base image choice. Pin images by digest rather than by tag, since tags are mutable and :latest means the artefact you tested is not necessarily the artefact you deployed. And treat images as immutable and short-lived: rebuild to patch rather than updating packages inside a running container, which produces state that exists in no repository.
Kubernetes: the API server is the boundary
In a cluster, the security boundary that matters most is not the container but the API server, because it controls everything. Compromising a workload is meaningful largely to the extent that it grants access to that API.
The mechanism is usually the service account token mounted into every pod by default. If that account has broad permissions, an application vulnerability becomes cluster compromise. Two settings deserve immediate review: disable automatic token mounting for workloads that do not call the API, and scope roles narrowly for those that do.
Specific permissions are more dangerous than they appear. The ability to create pods is close to cluster-admin, because a pod can be created with host mounts and privileged flags. Reading secrets cluster-wide exposes every credential. The ability to create token requests or modify role bindings permits escalation. Reviewing role bindings for these specific verbs finds more real risk than a generic audit.
Network: flat by default
Kubernetes networking permits any pod to reach any other pod across all namespaces unless a network policy says otherwise. Most clusters run with no policies at all, which means a single compromised pod has unrestricted internal reachability — the ideal position for lateral movement.
The remedy is a default-deny policy per namespace, followed by explicit allowances for the flows that must exist. This is ordinary work with an unusually high return, since it converts a flat internal network into a segmented one.
Egress deserves equal attention. Restricting outbound traffic limits an attacker’s ability to reach command-and-control infrastructure or exfiltrate, and it should include blocking pod access to the cloud metadata endpoint, which is otherwise a direct route to node credentials.
Network policies and cluster CIDR planning are subnet arithmetic, and image verification starts with a digest.
Secrets are encoded, not encrypted
Kubernetes Secrets are Base64-encoded by default, which is an encoding and provides no confidentiality. Without encryption at rest configured, they are stored in plain form in the cluster datastore, and anyone able to read that datastore or read secrets through the API obtains them directly.
Enable encryption at rest, restrict who can read secrets through role bindings, prefer mounting secrets as files over environment variables since environment variables leak readily into logs and crash dumps, and use an external secret manager with short-lived credentials where the platform supports it.
Admission control and runtime detection
Admission controllers enforce policy at the moment a workload is created, which is the point where prevention is cheapest. Pod Security Standards provide a baseline — the restricted profile blocks privileged containers, host namespace sharing and running as root — and policy engines allow organisation-specific rules such as requiring signed images or forbidding mutable tags. Enforcing at admission is considerably more reliable than scanning for violations afterwards.
Runtime detection covers what admission cannot anticipate. The signals worth alerting on are behavioural: a shell process starting inside a container that should never have one, unexpected outbound connections from a workload, writes to sensitive host paths, and API calls from a service account that has never made them before. Because containers are supposed to be immutable and single-purpose, deviation from their normal behaviour is a stronger signal than it would be on a general-purpose host.
Priorities
For teams securing an existing cluster, the order that removes the most risk per unit of effort:
- Eliminate privileged containers and mounted runtime sockets.
- Disable automatic service account token mounting where not required.
- Apply default-deny network policies per namespace.
- Enforce Pod Security Standards at admission.
- Enable encryption at rest for secrets and restrict who can read them.
- Move to minimal base images and pin by digest.
- Audit role bindings for pod creation, secret reading and escalation verbs.
- Block pod access to the cloud metadata endpoint.
The consistent theme is that containers provide isolation only to the degree the configuration preserves it. The technology is not inherently weak, but its defaults favour convenience, and an attacker’s first question after compromising a workload is always which of those defaults is still in place.
Image provenance and admission
Everything discussed about supply chain security applies with particular force to containers, because an image is a complete filesystem assembled from sources most teams never inspect. The control that makes this tractable is admission-time verification: the cluster refuses to run an image that is not signed by an expected identity, or whose provenance does not reference an approved builder and source repository.
This changes the security question from “did anyone scan this image?” to “can this image prove where it came from?”, and the second is enforceable. Pair it with restricting which registries the cluster may pull from at all, since an image reference pointing at an arbitrary public registry is an unreviewed dependency introduced by a single line of YAML.
Vulnerability scanning remains worthwhile but should be understood for what it is: a check for known CVEs in packaged software. It will not identify a malicious image, and a clean scan says nothing about provenance.
The limits of namespace multi-tenancy
Namespaces are an organisational boundary that is often mistaken for a security boundary. They separate names and provide a scope for policy, but workloads in different namespaces still share the same nodes, the same kernel and the same control plane. A container escape crosses namespace boundaries without difficulty, because from the kernel’s perspective there was never a boundary there at all.
For workloads with genuinely different trust levels — customer-supplied code, environments with different regulatory obligations, production alongside development — the honest options are stronger. Separate node pools with scheduling constraints reduce shared-kernel exposure. Sandboxed runtimes that provide a per-workload kernel, or lightweight virtual machines, restore a hardware-enforced boundary at some performance cost. Separate clusters remain the strongest and are frequently the right answer despite the operational overhead.
Deciding this deliberately matters more than the specific choice. The common failure is assuming namespaces provide isolation they were never designed to deliver, and discovering otherwise during an incident.
Responding to a compromise in a cluster
Container incident response differs from host response in ways that catch teams out, and the differences are worth rehearsing before they matter.
The first is that evidence is ephemeral by design. Killing a compromised pod destroys its filesystem and memory, and the replica set will cheerfully recreate a fresh one — which looks like remediation while destroying the investigation and leaving the underlying cause untouched. Before terminating anything, capture what you need: process state, filesystem changes and, where tooling permits, memory.
The second is that containment has a specific shape. Isolating a pod usually means applying a network policy that denies all traffic to and from it while leaving it running, and simultaneously cordoning the node so nothing new schedules there. Scaling the workload to zero is the equivalent of powering off — sometimes correct, always destructive of evidence.
The third is credential scope. Assume the pod’s service account token is compromised and evaluate exactly what it could reach. If the workload ran on a node whose identity has broader cloud permissions, and the pod could reach the metadata endpoint, the blast radius extends to the node’s role rather than the pod’s.
Finally, remember that the compromised artefact is usually the image rather than the instance. Rebuilding from the same image reproduces the vulnerability, which is why identifying and fixing the image — and then rolling it out — is the actual remediation rather than restarting the workload.