Kubernetes Security: The Defaults Will Get You
The container migration was a success. The monolith is decomposed. The CI/CD pipeline builds images and pushes them to the cluster automatically. Engineering is shipping faster than they have in years. And somewhere in that cluster, a pod is running as root, with a service account that has cluster-admin privileges, in a network where every pod can reach every other pod by default, and the secrets the application needs are sitting in environment variables that anyone who can read the pod spec can see.
Kubernetes does not ship with secure defaults. It ships with defaults that prioritize getting things running. The gap between "the cluster is up" and "the cluster is secure" is large, poorly documented relative to its importance, and full of specific misconfigurations that appear in every cloud security assessment run against containerized environments.
Root Is the Default
Containers do not, by default, run as root. But they also do not, by default, run as anything else. If the container image does not specify a non-root user and the pod spec does not enforce one, the process inside the container runs as root within the container's namespace. On a correctly configured system, that root is isolated from the host. On a system with a container escape vulnerability, it is exactly the same root that owns the underlying node.
Container escape vulnerabilities exist and are discovered regularly. The path from "container running as root" to "control of the underlying host" is not theoretical. It is documented, exploited in the wild, and substantially easier when the process inside the container already has the highest possible privilege level.
Pod Security Admission, the replacement for the deprecated PodSecurityPolicy, provides a mechanism to enforce that containers run as non-root. Most clusters have not configured it. The rationale is usually that existing workloads would break, which is sometimes true and sometimes means the workloads were built assuming root access they should not have had. Both of those are problems. Neither justifies leaving enforcement disabled indefinitely.
Service Accounts and the Privilege They Carry
Every pod in Kubernetes gets a service account. If you do not specify one, it gets the default service account for its namespace. That default service account has no special permissions out of the box, and its credential token is automatically mounted into every pod in the namespace unless you explicitly disable the automount.
The token matters because RBAC permissions attach to service accounts, and those permissions are frequently broader than anyone intended. A service account created for one purpose accumulates permissions over time. Someone needed the pod to read configmaps across namespaces. Someone else needed it to update deployments during a migration. The grants were added, the migration finished, and the grants stayed because removing them felt risky and nobody had a ticket for it.
Cluster-admin bound to a service account that runs in a production pod is not a hypothetical misconfiguration. It is a finding that appears in real assessments with uncomfortable regularity. An attacker who compromises a pod carrying a cluster-admin token has administrative access to everything in the cluster - every namespace, every secret, every workload, every node.
Fixing this requires a real access review: which service accounts exist, what role bindings they carry, and which of those bindings are actually necessary for the workload to function. That review is tedious and requires understanding the current state of the cluster rather than its intended state, which often differ significantly. Most teams defer it because there is always something higher priority until there is not.
The Flat Network Problem, Container Edition
Traditional network segmentation argues that not everything should be able to reach everything else. Kubernetes implements this principle through NetworkPolicy resources. An absence of NetworkPolicy means every pod in the cluster can initiate connections to every other pod, across namespaces, without restriction.
By default, every Kubernetes cluster is a flat network. A compromised pod in the frontend namespace can connect to the database in the backend namespace, to the monitoring agent in the platform namespace, to the internal APIs in the integrations namespace. The segmentation that the network team spent months building outside the cluster does not exist inside it.
NetworkPolicy enforcement also requires a container network interface plugin that supports it. Some commonly used CNI plugins do not. Installing a NetworkPolicy in a cluster whose CNI ignores it does nothing, produces no errors, and creates a false sense of control that is genuinely worse than knowing the gap exists.
Retrofitting network policies onto a running cluster requires mapping every communication path between services, which most teams do not have documented. The documentation that exists is usually aspirational rather than descriptive. The accurate picture comes from traffic analysis, which is a separate project that competes with everything else on the platform team's roadmap.
Secrets Are Not Actually Secret
Kubernetes Secrets exist to store sensitive configuration: database credentials, API tokens, TLS certificates. The name is somewhat misleading in practice. By default, Secrets are stored unencrypted in etcd, the cluster's key-value store. Anyone with direct etcd access has access to every Secret in the cluster in plaintext. Etcd access in a self-managed cluster is not heavily restricted by default, and the etcd endpoint is not always given the same security treatment as production databases holding equivalent data.
Encryption at rest for etcd requires explicit configuration and is not enabled in default cluster deployments. It is a few lines of configuration and a restart of the API server. It is also skipped in a large proportion of clusters because nobody is specifically responsible for verifying it is enabled.
The environment variable pattern compounds the exposure. Secrets injected as environment variables are visible to every process in the container, appear in process listings under the right conditions, and end up in application logs when verbose or debug output is enabled. A secret that never lives in the cluster at all, managed instead through an external secrets operator that integrates with a dedicated secrets manager, is a more defensible architecture. It adds operational complexity, which is why most clusters rely on Kubernetes Secrets and accept the tradeoff without fully accounting for the risk.
What a Reasonable Baseline Actually Requires
Pod Security Admission configured in enforce mode for production namespaces, requiring non-root containers, non-privileged security contexts, and read-only root filesystems where applications support it. Default-deny NetworkPolicy applied to each namespace, with explicit allow rules for the specific connections that actually need to happen. Service account token automounting disabled at the namespace level, re-enabled only for pods that have a documented reason to call the Kubernetes API. Etcd encryption at rest enabled and verified, not assumed. RBAC audit on a quarterly cadence to identify role bindings that have outlived their purpose.
None of this is complicated in isolation. All of it is absent from default cluster configurations, and all of it requires someone who understands both what the settings do and what the workloads actually need, because getting least-privilege right requires knowing the current requirements, not the intended ones.
The organizations that build secure clusters from the start have a much easier path than the ones retrofitting controls onto a running environment that has never had them. The retrofit is harder, slower, and requires more coordination with the teams who own the workloads. It is also the situation most organizations are in, because the cluster went live before anyone finished the security review.
The cluster is running. That part worked. The part that keeps someone from walking through the pod network with a stolen service account token is a separate project. Schedule it before it gets scheduled for you.