Additive NetworkPolicy Semantics Let a Leftover Allow-All Rule Override a New Deny-All Policy
A new deny-all NetworkPolicy in Kubernetes does not override an existing allow-all policy for the same pods; the two combine additively, leaving the namespace exposed while dashboards show the restriction as active.
Operational summary
At a glance
- Symptom
- A namespace that the platform team believed was newly locked down by a deny-all NetworkPolicy still accepts ingress traffic from pods that were supposed…
- Likely cause
- Kubernetes NetworkPolicies for a given pod are combined additively, not by override or precedence.
- Impact
- Every pod in the affected namespace remains reachable from any source that the legacy allow-all policy permitted, even though the team's change record and dashboards show a deny-all…
- Verification signal
- Re-run the same connectivity test used during diagnosis and confirm it now fails in the way the deny-all policy intends.From the disposable test pod, attempt the same connection…
- Safe correction
- Remove or narrow the overlapping legacy policy so the deny-all policy is no longer contradicted by an existing allow-all rule for the same pods.Identify every NetworkPolicy whose podSelector…
- Rollback or recovery
- If removing or narrowing the legacy policy breaks traffic that other services depend on, restore the previous policy set exactly as it existed before the change.Re-apply the original…
Symptom
A namespace that the platform team believed was newly locked down by a deny-all NetworkPolicy still accepts ingress traffic from pods that were supposed to be cut off, and a connectivity check from an unauthorised test pod returns a successful HTTP response instead of a connection timeout.
The deny-all policy was applied, is present when listed with kubectl get networkpolicy, and shows no errors in its status. Yet traffic that the team expected to be blocked continues to flow, and no alert or admission error indicates that anything is wrong.
False Assumption
The team assumed that applying a NetworkPolicy with an empty ingress rule set to a pod selector behaves like a firewall change: the newest, most restrictive rule for a given pod supersedes any earlier, more permissive rule that also selects that pod.
This mental model is reasonable for many network appliances, where rule order or rule specificity determines the effective outcome. It does not hold for Kubernetes NetworkPolicy objects.
Root Cause
Kubernetes NetworkPolicies for a given pod are combined additively, not by override or precedence. If any NetworkPolicy selecting a pod permits a given ingress connection, that connection is allowed, regardless of how many other NetworkPolicies also select that pod and would otherwise deny it. A pod becomes “isolated” for a traffic direction only once at least one policy selects it for that direction, and the effective allow-list for that pod is the union of the allow rules from every matching policy.
In this trap, an existing NetworkPolicy with a broad podSelector: {} and an open ingress rule was already selecting every pod in the namespace and allowing ingress from all sources. A new, narrower deny-all policy (empty ingress: [] with the same or overlapping selector) was applied with the intention of tightening access. Because policies combine additively, the union of the two policies still includes the original allow-all rule, so ingress remains fully open for any pod matched by both.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: legacy-allow-all
namespace: payments
spec:
podSelector: {}
policyTypes: [Ingress]
ingress:
- {}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: new-deny-all
namespace: payments
spec:
podSelector: {}
policyTypes: [Ingress]
ingress: []
Impact
Every pod in the affected namespace remains reachable from any source that the legacy allow-all policy permitted, even though the team’s change record and dashboards show a deny-all policy as “applied and active”.
The practical consequence is a false sense of network isolation. Workloads that the team believes are segmented from other namespaces, or from the wider cluster network, continue to accept unrestricted ingress. Depending on what the affected pods run, this can expose internal APIs, databases or admin interfaces to any pod or external caller that the older policy still permits, without any corresponding alert.
Diagnosis
Confirm the trap before making further changes, using read-only inspection of the existing policy objects.
- List every NetworkPolicy in the affected namespace and note each one’s
podSelectorandpolicyTypes. - For each policy that selects the same pods as the new deny-all rule, inspect its ingress rules for a broad or empty match (such as an ingress entry with no
fromfield). - From a disposable test pod inside the cluster, attempt a connection to a representative pod in the namespace and record whether it succeeds or times out.
Correction
Remove or narrow the overlapping legacy policy so the deny-all policy is no longer contradicted by an existing allow-all rule for the same pods.
- Identify every NetworkPolicy whose
podSelectoroverlaps with the new deny-all policy’s selector. - Either delete the legacy allow-all policy if it is no longer required, or rewrite it with a scoped
podSelectorand explicitfromrules that match only the traffic that must still be permitted. - Re-apply the deny-all policy only after the overlapping permissive rule has been removed or scoped, so the union of active policies reflects the intended restriction.
Validation
Re-run the same connectivity test used during diagnosis and confirm it now fails in the way the deny-all policy intends.
- From the disposable test pod, attempt the same connection that previously succeeded; it should now time out or be refused.
- List NetworkPolicies again and confirm no remaining policy in the namespace grants an unscoped or empty ingress rule to the same pod selector.
- Confirm any traffic that must still be permitted (for example, from a monitoring namespace) is allowed only through an explicit, scoped rule, not through a leftover broad policy.
Rollback
If removing or narrowing the legacy policy breaks traffic that other services depend on, restore the previous policy set exactly as it existed before the change.
- Re-apply the original legacy allow-all NetworkPolicy manifest that was removed or edited, using the version stored in source control or captured before the change.
- Delete the new deny-all policy if its presence alongside the restored legacy policy causes confusion in change records, since it has no effect while the legacy rule remains broad.
- Confirm restored connectivity with the same test-pod connection check used in validation, expecting the pre-change result to return.
Prevention
Treat every new NetworkPolicy as an addition to an existing set of rules, not a replacement, and audit for overlap before relying on a new policy to tighten access.
- Before applying any new restrictive policy, list all existing policies that share a pod selector and review their combined effect, not just the new policy in isolation.
- Avoid broad
podSelector: {}allow rules in shared namespaces; scope allow rules to specific labels so they cannot silently combine with later deny-all attempts. - Add a namespace-level review step that lists NetworkPolicy objects and their effective combined ingress rules as part of any change that claims to restrict network access.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Fix commands and configuration
kubectl get networkpolicypodSelector: {}ingress: []Verify, roll back or escalate
Verify
Re-run the same connectivity test used during diagnosis and confirm it now fails in the way the deny-all policy intends.From the disposable test pod, attempt the same connection that previously succeeded; it should now time out or be refused.List NetworkPolicies…
Rollback
If removing or narrowing the legacy policy breaks traffic that other services depend on, restore the previous policy set exactly as it existed before the change.Re-apply the original legacy allow-all NetworkPolicy manifest that was removed or edited, using the version…
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.