Safeguard
Container Security

What Are Kubernetes Network Policies

Kubernetes clusters default to flat, all-to-all pod networking. Here's how NetworkPolicy objects, CNI enforcement, and default-deny rules actually stop lateral movement.

Vikram Iyer
Cloud Security Engineer
7 min read

Kubernetes ships with a flat network by default: every pod can reach every other pod across every namespace, on every port, with zero authentication. A NetworkPolicy is a namespace-scoped Kubernetes API object (networking.k8s.io/v1, GA since Kubernetes 1.7 in June 2017) that uses label selectors to define which pods may talk to which other pods, and over which ports and protocols. It is not enforced by the Kubernetes API server or kubelet — enforcement is delegated entirely to the cluster's CNI plugin, which means a NetworkPolicy object can exist in etcd and do absolutely nothing if the underlying network plugin doesn't implement the spec. That distinction trips up more teams than any other part of Kubernetes networking. Understanding what network policies actually control, which plugins enforce them, and where the common misconfigurations hide is the difference between a segmented cluster and one where a single compromised pod can reach your entire workload estate.

What Problem Do Kubernetes Network Policies Solve?

Kubernetes network policies solve the lateral movement problem created by Kubernetes' default all-to-all pod connectivity model. Without a policy, a pod running a public-facing API in namespace frontend can open a TCP connection directly to a pod running a payments database in namespace backend, and nothing in the cluster will block or even log it. This is the exact pattern attackers use once they land a foothold: the 2018 Tesla incident, where an exposed, unauthenticated Kubernetes dashboard let attackers pivot into the cluster's pods and mine cryptocurrency, is a frequently cited example of what an unsegmented cluster network allows once initial access is achieved. A cluster with default-deny network policies limits that same compromised pod to only the connections its workload legitimately needs — often just one or two destination services — turning a cluster-wide breach into a contained one.

How Do Kubernetes Network Policies Work?

Kubernetes network policies work by matching a podSelector to a set of pods and then applying ingress and egress rules that whitelist allowed traffic by source/destination pod selector, namespace selector, IP block (CIDR), and port. A policy with an empty podSelector: {} applies to every pod in its namespace. Once at least one policy selects a pod for a given direction (ingress or egress), that direction switches from default-allow to default-deny, and only traffic matching an explicit rule is permitted — everything else is dropped. Policies are additive: if two policies both select the same pod, the pod is allowed the union of both rule sets, not the intersection. A common oversight is assuming a single restrictive policy will "tighten" an existing permissive one; it will not, because Kubernetes always applies the most permissive combination of all matching policies.

Which CNI Plugins Actually Enforce Network Policies?

Only CNI plugins that implement the NetworkPolicy controller — Calico, Cilium, Weave Net, and Antrea are the most widely deployed — actually enforce these rules; plugins like plain Flannel or the default AWS VPC CNI on EKS silently accept NetworkPolicy objects without applying any of them. This matters directly on managed clusters: Amazon EKS requires installing Calico or the Cilium add-on separately before policies take effect, Azure AKS requires selecting "Calico" or "Azure" as the network policy engine at cluster creation time (it cannot be added after the fact without recreating node pools in most configurations), and Google GKE requires explicitly enabling "NetworkPolicy" enforcement, which provisions Calico under the hood, when the cluster is created or updated via gcloud container clusters update --update-addons=NetworkPolicy=ENABLED. Teams that write and commit NetworkPolicy YAML without confirming CNI support end up with manifests that pass kubectl apply cleanly and provide zero actual protection — a gap that's invisible in kubectl get networkpolicies output but obvious the first time an incident responder tries to trace how a breach spread.

What Does a Default-Deny Network Policy Look Like?

A default-deny network policy is a policy that selects all pods in a namespace and specifies an empty rule set, blocking every connection until explicit allow rules are added on top. The minimal version looks like this:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: payments
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

From there, teams layer in specific allow rules, for example permitting the payments-api pod to reach the payments-db pod on port 5432 and nothing else:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-db
  namespace: payments
spec:
  podSelector:
    matchLabels:
      app: payments-db
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: payments-api
      ports:
        - protocol: TCP
          port: 5432
  policyTypes:
    - Ingress

CNCF's 2023 annual survey found that a majority of respondents running Kubernetes in production had not implemented default-deny policies cluster-wide, which is consistent with what shows up in cluster audits: individual services get locked down reactively after an incident, while the namespace as a whole stays open.

What Are the Most Common Kubernetes Network Policy Mistakes?

The most common mistake is forgetting that network policies are namespace-scoped, so a default-deny policy applied in payments does nothing to protect pods running in staging or default — each namespace needs its own policy set. The second most common mistake is blocking egress DNS traffic: a default-deny egress policy that doesn't explicitly allow UDP/TCP port 53 to kube-dns or coredns will break service discovery for every pod it applies to, since pods can no longer resolve payments-db.payments.svc.cluster.local to an IP. A third is assuming policies protect against traffic within the same pod's containers or against node-level access — network policies govern pod-to-pod traffic on the CNI overlay only; they don't restrict hostNetwork: true pods, don't apply to traffic that never leaves the pod, and don't protect the API server or kubelet ports, which need separate controls like RBAC and node firewall rules. Finally, teams frequently write policies against pod labels that aren't actually applied consistently by their deployment tooling, so a policy that looks correct in the YAML silently matches zero pods after a Helm chart upgrade renames a label.

How Do Network Policies Compare to a Service Mesh?

Kubernetes network policies operate at Layer 3/4 (IP and port), while a service mesh like Istio or Linkerd adds Layer 7 controls — HTTP method/path-based authorization, mutual TLS, and traffic encryption — on top of, not instead of, network policies. A service mesh sidecar can enforce that payments-api may only call GET /v1/balance on payments-db-proxy, something a NetworkPolicy cannot express since it has no visibility into application-layer payloads. The two are complementary rather than competing: network policies provide the coarse-grained, CNI-enforced perimeter that stops unauthorized connections before they're established, while a mesh provides fine-grained authorization and encryption for the connections that are allowed. Cilium is the notable exception that blurs this line — its eBPF-based dataplane can enforce L7 HTTP-aware policies natively without a sidecar mesh, which is why it's increasingly deployed specifically to get mesh-like controls with lower per-pod overhead.

How Safeguard Helps

Writing correct network policies requires knowing which services actually talk to each other in production, not which connections look plausible on a whiteboard — and that's exactly the gap Safeguard's reachability analysis closes by tracing real, exploitable call paths between workloads so teams can scope default-deny policies to what's genuinely needed instead of guessing. Griffin AI, Safeguard's autonomous triage engine, correlates cluster topology, SBOM data, and live traffic patterns to flag namespaces running without enforced segmentation and to prioritize which missing policies matter most given actual exposure. Safeguard ingests and generates SBOMs across your container images so vulnerable, network-adjacent components are visible before they ship, and where a gap is found — a missing default-deny policy, an over-broad ingress rule, an unenforced CNI configuration — Safeguard can open an auto-fix pull request with the corrected NetworkPolicy YAML ready for review. That turns network segmentation from a periodic audit finding into a continuously enforced, version-controlled part of the deployment pipeline.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.