Skip to content
UNRESOVED

Kubernetes Security

Multi-Layered Hardening for Kubernetes: Why Each Layer Has to Be Tested on Its Own Terms

Defense in depth is usually described as a stack of layers. In practice it behaves more like six separate disciplines that happen to share a cluster, each with its own failure modes.

Hope Akpabio
Hope Akpabio·17 September 2026·13 min read
Kubernetes Security

The assumption

"Defense in depth" is usually drawn as a stack: perimeter at the bottom, identity above it, network policy above that, workload configuration, supply chain, runtime detection, each layer sitting on top of the one before it like a wall built brick by brick. The image is reassuring because it implies redundancy. If one brick cracks, the others are still standing.

This was the working model behind most of the Kubernetes hardening research I did for my dissertation, and it took a fair amount of hands-on cluster testing to unlearn it. The layers in a real cluster are not stacked. They are closer to six separate disciplines that happen to share the same cluster and the same YAML files, each with its own threat model, its own failure modes, and almost no ability to compensate for a gap in any of the others.

The key idea

Defense in depth is not a stack where each layer backstops the one above it. It is six largely independent disciplines, and a cluster can be hardened at five of them while remaining completely exposed at the sixth.

The problem with the stack metaphor

A stack implies that if the top layer fails, the layer beneath it catches the fall. That is a reasonable description of how a network firewall and a host firewall interact: redundant controls, similar threat model, meaningful overlap. It is a poor description of how Kubernetes' security surfaces relate to each other.

Consider a cluster with excellent Pod Security Standards enforcement (nothing privileged, no host namespace access, mandatory non-root) and completely flat RBAC (every service account can read every secret in every namespace). The workload layer is hardened. The identity layer is not. A compromised pod cannot escalate to the node, because Pod Security Standards did its job, but it can read every credential in the cluster, because RBAC never did. The workload layer's strength provided zero compensation for the identity layer's weakness. They were never stacked. They were parallel.

This is the pattern that shows up across the K8s security experiments elsewhere on this site: a strong perimeter control says nothing about RBAC scope; a correct admission policy says nothing about audit visibility; a managed control plane says nothing about workload configuration. Multi-layered hardening only works as intended if each layer is actually tested against its own specific failure modes, not assumed to be covered because a neighboring layer looks strong.

Why the metaphor persists anyway

The stack metaphor survives because it maps cleanly onto how hardening work gets organized: one team, one sprint, one checklist, moving top to bottom. Compliance frameworks reinforce it further by presenting controls as a flat, numbered list, which reads like a sequence even when the underlying controls have no sequential relationship at all. A CIS Benchmark score of "92% passing" sounds like a stack that is 92% built. It is closer to six unrelated dials, some turned further than others, none of which know the other five exist. The number is real. The implied structure behind it is not.

There is also a simpler, more human reason the metaphor sticks: a stack is easier to report upward. "We are hardened, with two remaining gaps" fits in a slide. "We have six independent postures, three strong, one partially tested, one unverified, and one absent" does not fit in a slide, even though it is the more accurate sentence. Most of the pressure toward the stack framing is a reporting problem wearing a technical costume.

The six layers, tested separately

1. Control plane and cluster configuration. This is the layer most hardening guides start with: API server flags, etcd encryption at rest, audit logging scope, kubelet authentication. The CIS Kubernetes Benchmark and the NSA/CISA hardening guidance both treat this as foundational, and it is, but it is also the layer most affected by whether you run a managed or self-managed control plane. On managed Kubernetes, most of this is handled for you. The mistake is assuming that means the layer is fully covered. Audit log scope and retention is frequently still the operator's decision even on a fully managed control plane, and it is exactly the kind of setting that determines whether an incident three months from now is reconstructable at all.

A quick way to see how much of this layer is actually within your control, versus assumed to be handled by the provider, is to check what audit policy is actually shipping requests to storage:

kubectl get configmap audit-policy -n kube-system -o yaml 2>/dev/null || \
  echo "no cluster-managed audit policy found; check provider console for audit trail scope"

On more than one managed cluster I've audited, the answer was that audit logging existed, was billed, and was retained for exactly the default window the console shipped with, which nobody had revisited since the cluster's creation.

2. Identity and access. RBAC, service account scoping, and (where relevant) integration with an external identity provider. The single most common failure here is not a missing Role, it is a ClusterRoleBinding created early in a project's life for convenience and never revisited. A quick audit worth running on any cluster:

kubectl get clusterrolebindings -o json | \
  jq -r '.items[] | select(.roleRef.name=="cluster-admin") |
  .subjects[]?.name' | sort -u

Anything returned here that is not a small, deliberate list of human administrators is worth a second look. Service accounts with cluster-admin are a common finding, usually inherited from a Helm chart's default RBAC template rather than chosen on purpose. Worth running alongside it is the inverse query, which surfaces the roles nobody remembers granting rather than the bindings everyone already suspects:

kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | \
  jq -r '.items[] | "\(.metadata.namespace // "cluster"): \(.metadata.name) -> \(.roleRef.name)"' | \
  sort | uniq -c | sort -rn | head -20

The bindings that show up once, attached to a service account whose workload was decommissioned two quarters ago, are the ones that never get caught by a review focused on "does this look dangerous," because on their own, in isolation, they don't.

3. Network segmentation. Kubernetes networking is flat by default. Every pod can reach every other pod across every namespace unless a NetworkPolicy says otherwise, and a NetworkPolicy with a subtly wrong selector can silently fail to enforce anything while still reporting as present and healthy, as tested directly in an earlier experiment. A service mesh with mutual TLS adds real value here, but it answers a different question (is this traffic authenticated and encrypted) than a NetworkPolicy does (should this traffic be permitted to exist at all), and hardening one is not a substitute for the other.

4. Workload and pod security. Pod Security Standards (or the admission controller enforcing them), seccomp profiles, read-only root filesystems, dropped Linux capabilities, mandatory non-root execution. This is the layer with the clearest, most mechanical checklist, which is precisely why it tends to get the most attention relative to its actual share of real-world incidents. A pod that cannot run as root and cannot mount the host filesystem is meaningfully safer, but it is not automatically safer at the identity or network layers, which is the parallel-not-stacked point again.

5. Supply chain. Image provenance, vulnerability scanning, signature verification, and admission-time enforcement of all three (Kyverno and OPA Gatekeeper are the common choices for the enforcement step). The dissertation research this article draws on spent real time here specifically because supply chain hardening is the layer most often implemented as a scanning report nobody reads rather than an enforced gate. A vulnerability scanner that produces a report is observability. A vulnerability scanner wired into an admission controller that blocks the deploy is enforcement. Only the second one is actually hardening.

6. Runtime detection. Falco, eBPF-based monitoring, or an equivalent, watching for anomalous process behavior, unexpected network connections, or file access patterns that indicate a workload has already been compromised despite the first five layers. This is the layer most often skipped entirely, usually because it is the least mechanical to implement and the hardest to validate without a real incident. It is also the layer that answers the question every other layer leaves open: what happens after something gets through anyway.

A worked scenario across all six

To see why parallel, rather than stacked, is the right description, it helps to walk a single compromise through all six layers rather than discussing them abstractly. Say an attacker gets remote code execution inside a single pod through a vulnerable application dependency, the ordinary starting point for most real incidents.

At the workload layer, a well-configured Pod Security Standard prevents that pod from running as root or mounting the host filesystem, which closes off the most direct path to node compromise. So far, so good, and this is the layer most teams would point to first. But the same pod's service account, at the identity layer, was granted get and list on secrets across its entire namespace because a Helm chart shipped that as a default and nobody trimmed it. The attacker doesn't need node access. They read every credential in the namespace directly through the Kubernetes API, using permissions the workload layer had no visibility into and no ability to restrict.

At the network layer, if segmentation is real, the attacker's next move (reaching out to other namespaces or an external command server) gets blocked, and the incident stays contained to one namespace's worth of leaked secrets. If segmentation is theatrical (a NetworkPolicy present but misapplied), the same compromised credentials now reach services in namespaces that had nothing to do with the original vulnerable dependency. Supply chain controls, at this point, are retrospective: they might tell you which image introduced the vulnerable dependency, but only if provenance and scanning were wired into an enforced gate rather than a dashboard, and only after the fact. And runtime detection is the only layer positioned to notice the compromise while it is happening, rather than explaining it afterward or preventing one specific escalation path.

Five of six layers can be individually excellent in this scenario and the incident still spreads, because none of the five is watching the thing that mattered: an overly broad Role on a service account that had nothing to do with pod security, image provenance, or the network's segment boundaries.

What changed my mind about testing this way

Treating these as one hardening project, executed once against a checklist, produces clusters that look complete and behave unevenly. Treating them as six separate disciplines, each requiring its own test plan, its own failure-mode analysis, and its own periodic re-verification, is slower and less satisfying to report on, but it is the only version that survives contact with an actual incident. A CIS Benchmark score does not tell you whether your NetworkPolicy selectors are correct. A green Pod Security Standards audit does not tell you whether your RBAC is scoped. Each layer has to be interrogated on its own terms, with its own specific test, because passing one tells you almost nothing about the others.

Testing cadence matters as much as test design

The other thing the dissertation work made obvious is that a one-time test, however rigorous, has a shelf life measured in one deploy cycle. A NetworkPolicy selector that was correct on the day it was tested can be silently invalidated by a label change in an unrelated deployment six weeks later, with no error, no failed CI step, and no signal that the policy's coverage has changed. RBAC drifts the same way: a ClusterRoleBinding added for a one-off migration script gets left in place because removing it isn't anyone's job. None of the six layers has a natural mechanism that alerts you when it silently regresses, which means the test has to be periodic, not a one-time gate passed at launch and never revisited. Six layers tested once at launch is a snapshot. Six layers tested on a recurring schedule, with drift detection built into the recurrence, is closer to an actual posture.

You might disagree

The strongest objection to this framing is practical: most teams do not have the resources to run six independent hardening disciplines with six independent test plans, and a checklist, imperfect as it is, at least ensures every layer gets some attention rather than the team's limited time concentrating entirely on whichever layer is most interesting or most visible. Six thorough layers is the ideal. Six superficial checkbox passes across every layer might, in practice, catch more real misconfigurations than one deeply tested layer and five untouched ones.

That is a fair prioritization argument, and for a small team, doing all six layers superficially is often the right call. But it is an argument about sequencing and resourcing, not about whether the layers actually reinforce each other. Even under a checklist-first approach, the checklist should be read as six separate pass/fail results, not one aggregate "hardened" status, precisely because a five-out-of-six average hides which one thing an attacker would actually find. There is a middle path worth naming here too: a small team without the bandwidth for six deep programs can still pick a rotation, one layer per quarter gets the deep pass while the other five get the superficial one, so that over roughly a year and a half every layer has actually been tested once, rather than never. That is slower than doing all six properly at once, but it is a real plan rather than a permanent deferral, and it beats the alternative of waiting for enough headcount to do all six thoroughly, which in most organizations I've seen never actually arrives.

What I think now

I no longer describe a cluster as "hardened" as a single adjective. I describe it by layer: control plane configuration is strong, identity is weak, network segmentation is untested, workload security is strong, supply chain enforcement exists but isn't gating deploys, runtime detection is absent. That sentence is longer and less quotable than "the cluster is hardened," and it is also the only version that is actually true, because it is the only version that reflects that these six things were tested independently rather than assumed to travel together.

This has also changed how I read other people's hardening documentation. A document that says "hardened per CIS Benchmark" now prompts a specific follow-up rather than reassurance: which of the six disciplines did that benchmark actually exercise, and which ones does it just assume, because the benchmark's own scope doesn't cover identity blast radius or network policy correctness the way it covers API server flags. The benchmark is real and worth running. It answers a narrower question than the phrase "hardened" implies, and the gap between those two things is exactly where I've seen incidents happen that nobody's documentation predicted.

The lesson learned

Multi-layered hardening is not a stack where strength in one layer compensates for weakness in another. It is six disciplines, control plane, identity, network, workload, supply chain, and runtime, that share infrastructure but not failure modes. A cluster's actual security posture is the weakest of the six, not the average, and the only way to know which one that is happens to be the same method this entire publication keeps returning to: test each layer on its own terms rather than trusting that a checklist completed once means the layer stays covered.

Join the conversation

Have a different perspective? Continue the discussion.

Discuss on LinkedIn