auditing-network-policy-segmentation-gaps · git:20260828.ce55026 · 2026-08-28 · sha256 c71e2c719ce1419a
auditing-network-policy-segmentation-gaps git:20260828.ce55026A
Immutable. This exact content is served forever at /api/v1/blob/c71e2c719ce1419a.
--- name: auditing-network-policy-segmentation-gaps description: >- Audit cluster network segmentation for the reachability a workload should not have: a namespace with no default-deny so every pod can reach every other pod, a missing egress policy that lets a compromised pod call out to the internet or the cloud metadata endpoint, an overly broad selector that admits more sources than intended, and a policy that governs one direction while the other stays open. Covers Kubernetes network policies and equivalent segmentation where pod-to-pod, pod-to-service, and pod-to-external reachability is meant to be constrained. Use when network policy is the control limiting lateral movement and egress in a cluster. The reachable source pod is the source, the pod, service, or external endpoint it can reach is the sink, and the segmentation gap that permits the reach is the bug. license: MIT --- # Auditing network segmentation gaps: when every pod can reach every pod Kubernetes networking is open by default: without a policy, every pod can talk to every other pod, every service, and often the internet and the cloud metadata endpoint. Network policy is what closes that down, so segmentation is not a property the cluster has, it is a property each namespace earns by having policies that actually constrain reachability in both directions. The gaps are predictable: a namespace with no default-deny where the whole open default still applies, a missing egress rule that lets a compromised pod exfiltrate or reach the node metadata endpoint, a selector broad enough to admit sources it never meant to, and a policy that locks ingress while egress stays wide open. Lateral movement and egress are exactly what an attacker does after a foothold, and segmentation is what limits both. You audit this by checking, per namespace and per workload, what can actually reach what. ## When to use - Network policy is the control meant to limit pod-to-pod, pod-to-service, or pod-to-external reachability. - Namespaces may lack a default-deny, so the open-by-default reachability still applies. - Egress may be unconstrained, letting a compromised pod reach the internet or the cloud metadata endpoint. ## Scope check Test segmentation only on clusters you own or are authorized to assess, on non-production namespaces. Probing reachability sends traffic between real pods and possibly outbound, so keep probes benign and inside the authorized namespaces, and do not exfiltrate through any egress gap you find. If you can't name the authorization, stop. ## The loop 1. **Establish the intended reachability first.** Name, per workload, what it should be able to reach and what should be able to reach it: which services, which namespaces, what egress. This is the false-positive killer: a namespace with a default-deny and explicit allow rules that match exactly the intended reachability is correctly segmented. Name the intended graph, then compare actual reachability to it. 2. **Check for a default-deny per namespace.** Determine whether each namespace has a default-deny for ingress and egress, or whether the open default applies. A namespace without default-deny gives every pod full reachability regardless of any specific allow rules, so the absence of default-deny is the first and biggest gap. 3. **Audit egress explicitly.** For each workload, check whether egress is constrained. Unconstrained egress lets a compromised pod call out to the internet to exfiltrate or fetch a payload, and reach the cloud metadata endpoint to grab node credentials. Egress is frequently forgotten while ingress is locked; confirm both directions. 4. **Check selector breadth on allow rules.** For each allow policy, read the pod, namespace, and IP selectors and confirm they admit only the intended sources or destinations. A selector broader than needed, an empty selector that matches everything, or a namespace label an attacker workload can wear, admits more than intended. 5. **Reconcile both directions and cross-namespace paths.** Confirm that ingress and egress policies agree and that cross-namespace reachability matches intent: a service meant to be internal is not reachable from a namespace that should be walled off, and a policy governing one direction is not undone by the other staying open. Map the effective reachability graph, not each rule in isolation. 6. **Confirm and record.** Confirm by reaching, from a source pod, a pod, service, or external endpoint it should not be able to reach, on non-production namespaces and without exfiltrating. Kill the lead if every namespace has a default-deny, egress is constrained (including to the metadata endpoint), selectors admit only intended sources, and both directions agree with the intended graph. Record the source pod, the reached sink, and the segmentation gap that permitted it. ## Where segmentation gaps leak - **No default-deny means fully open.** A namespace without default-deny keeps the open-by-default reachability, so specific allow rules add nothing. - **Unconstrained egress enables exfiltration and metadata theft.** A pod with open egress can reach the internet and the cloud metadata endpoint after a compromise. - **A broad selector admits extra sources.** An overly wide or empty selector, or one keyed on an attacker-wearable label, lets in more than the intended source set. - **One-directional policy leaves the other open.** Locking ingress while egress stays open, or vice versa, leaves half the reachability unconstrained. - **Cross-namespace reachability is the lateral path.** An internal service reachable from a namespace that should be isolated is the move an attacker makes after a foothold. ## Worked example (a confirm and a kill) > **Confirm.** An application namespace has ingress policies on its front-end service but no default-deny and no > egress policy. A pod in that namespace, standing in for a compromised workload, reaches the cloud metadata > endpoint and an internal database in another namespace that the front end never needed. **Confirmed** > segmentation gap enabling lateral reach and metadata access, `high`, remediation = apply a default-deny for > ingress and egress in the namespace, add explicit egress rules that exclude the metadata endpoint and the > internet, and scope cross-namespace allow rules to the exact services required. > > **Kill.** Every namespace has a default-deny for both directions, egress is restricted to the specific > destinations each workload needs with the metadata endpoint blocked, allow selectors match only the intended > pods and namespaces on labels the cluster controls, and ingress and egress agree with the intended graph. A > source pod reaches only its permitted destinations. **Killed**, `kill_reason` = "default-deny in every > namespace with constrained egress, metadata blocked, and tight selectors in both directions; no pod reaches > anything outside the intended reachability graph." ## Rationalizations to reject - *"We have network policies."* → Confirm a default-deny exists; without it the open default applies and your allow rules only add to a fully open baseline. - *"Ingress is locked down."* → Check egress too; an open egress lets a compromised pod exfiltrate and reach the metadata endpoint regardless of ingress. - *"The cluster is internal."* → Internal still has lateral movement and a metadata endpoint; segmentation limits an attacker who already has one foothold. - *"The selector targets our app."* → Confirm the selector cannot be matched by another workload's labels; a broad or wearable selector admits more than your app. - *"Nothing sensitive is in that namespace."* → Cross-namespace reachability is the lateral path; a reachable internal service is a target even if the source namespace looks unimportant. ## Executing this in practice You need, per namespace, whether a default-deny exists; per workload, the ingress and egress allow rules and their selectors; the egress posture toward the internet and the metadata endpoint; and the intended reachability graph. For each workload, compute what it can actually reach in both directions and compare to intent. Reading the policies shows the intended segmentation; reaching a should-be-unreachable endpoint from a source pod shows whether it holds. ## Related - `mapping-pod-to-cloud-credential-reach` - open egress to the metadata endpoint is how a pod reaches the node role; segmentation and credential reach compound here. - `auditing-namespace-as-tenant-boundary` - network policy is one of the controls a namespace-as-tenant boundary depends on; audit segmentation as part of that boundary. - `auditing-service-mesh-mtls-and-authz-trust` - mesh authorization is the layer-7 companion to layer-3/4 network policy; the two together define who can reach whom. - `mapping-attack-surface` - use it to enumerate the services and external endpoints reachable from a namespace before auditing the policies that should constrain them. - [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - source = the reachable source pod, sink = the pod, service, or external endpoint it reaches, evidence = the segmentation gap that permitted the reach.