Skip to main content
cd ../config-traps
risk/register/namespace-migration-erases-networkpolicy-isolation.html
Kubernetes Networkinghigh severityKubernetes

Namespace Migration Erases NetworkPolicy Isolation

Severity
high
Reviewed
12 Jul 2026
Remediation
~20 minutes
Overview

Namespace renames or GitOps cutovers leave NetworkPolicy objects bound to the old namespace name, so the new namespace has none. Kubernetes defaults to allow-all networking, silently exposing PCI-scoped pods to unauthenticated traffic from any namespace, with no admission error to flag it.

Operational summary

At a glance

Symptom
Namespace rename via kustomize overlay, Argo CD app rename, or Helm cutover leaves policy orphaned in old namespace
Likely cause
NetworkPolicy manifests hardcode metadata.namespace instead of deriving it from the namespace generator
Impact
Pods in the new namespace accept unauthenticated L3/L4 traffic from any pod on the CNI overlay, bypassing mesh mTLS entirely
Verification signal
Re-run the detection test and a controlled negative-path test.
Safe correction
Generate default-deny-all NetworkPolicy per namespace via Kyverno with synchronize:true, plus CI gate checking policy presence pre-cutover
Rollback or recovery
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

The Trap

Namespace-scoped NetworkPolicy objects orphaned during a namespace rename or recreation, silently restoring the cluster’s native default-allow networking posture for every pod that lands in the new namespace.

The Default State

Kubernetes ships with no built-in network isolation. Without a NetworkPolicy object whose podSelector matches a given pod, every CNI that implements the NetworkPolicy API — Calico, Cilium, Weave — permits all ingress and egress for that pod by default. Platform teams write these policies with a literal metadata.namespace field, for example payments-prod. When the workload migrates to payments-prod-v2 via a kustomize overlay rename, an Argo CD application rename, or a Helm release cutover, the NetworkPolicy manifest frequently isn’t re-templated to the new namespace because the pipeline treats it as a static resource rather than a value derived from the namespace generator. kubectl get networkpolicy -n payments-prod-v2 returns nothing, and no admission controller in a stock cluster requires one to exist.

The Blast Radius

Pods in payments-prod-v2 are now reachable from any other pod on the same CNI overlay, including lower-trust namespaces sharing the same IPAM range. PCI-scoped services that were previously locked behind explicit allow lists in payments-prod now accept unauthenticated TCP from anywhere in the cluster, and there is no signal to catch it: kube-apiserver logs nothing, because an absent policy isn’t a denied request, it’s just the platform’s default state. A compromised debug pod in an unrelated namespace can reach the database service directly on port 5432, bypassing service mesh mTLS entirely because that enforcement sits at L7 and never inspects raw L3/L4 socket permissions. Compliance drift accumulates for weeks before an auditor, not a monitoring system, notices the isolation boundary is gone.

The Lead Mechanic Fix

Stop treating NetworkPolicy as a manifest that travels with the workload. Bind a default-deny-all policy to namespace creation itself using a Kyverno generate rule with synchronize: true, so the policy is stamped out automatically and can’t be removed without deleting the namespace:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-default-deny-netpol
spec:
  rules:
  – name: default-deny-all
    match:
      resources:
        kinds: [Namespace]
    generate:
      kind: NetworkPolicy
      synchronize: true
      data:
        spec:
          podSelector: {}
          policyTypes: [Ingress, Egress]

Layer explicit allow rules on top as separate objects, then add a CI gate that runs before any migration cutover: kubectl get networkpolicy –all-namespaces -o json piped through jq to confirm every namespace carries at least one deny-all baseline plus the expected allow rules, failing the pipeline if the new namespace comes up naked.

03

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.

Control to implement

Generate default-deny-all NetworkPolicy per namespace via Kyverno with synchronize:true, plus CI gate checking policy presence pre-cutover

Validate the vendor-specific syntax in official documentation before applying it.

04

Verify, roll back or escalate

Verify

Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.

Rollback

Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

Escalate

Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.

After remediation

Further reading stays below the corrective workflow and is selected by platform, category and shared technical keywords.

Discover more

Connected KBY resources