How do Kubernetes NetworkPolicies control pod-to-pod traffic, and what happens without any policy?
Quick Answer
Without any NetworkPolicy, Kubernetes uses a flat network where every pod can communicate with every other pod across all namespaces. NetworkPolicies act as pod-level firewalls: once any policy selects a pod, all non-matching traffic is denied by default. Policies whitelist specific ingress and egress traffic based on pod labels, namespace labels, or CIDR blocks.
Detailed Answer
Think of an open office floor plan versus one with locked rooms. By default, Kubernetes networking is the open floor plan: any pod can talk to any other pod on any port across any namespace. This is the flat network model. NetworkPolicies add walls and locked doors. Once you put one wall around a pod, everything that is not explicitly allowed through a door is blocked. But pods without any wall remain completely open.
Kubernetes networking follows a fundamental rule: all pods can communicate with all other pods without NAT. This is the flat network requirement that every CNI plugin (Calico, Cilium, Weave, AWS VPC CNI) must implement. While this simplifies service discovery and connectivity, it means a compromised pod in the staging namespace can reach the database pods in the production namespace. This is where NetworkPolicies provide segmentation.
A NetworkPolicy is a namespaced resource that selects pods using labels (podSelector) and defines allowed ingress (incoming) and egress (outgoing) traffic rules. Each rule can specify allowed sources or destinations by pod labels (podSelector), namespace labels (namespaceSelector), or IP ranges (ipBlock with CIDR notation). The critical behavior to understand is that NetworkPolicies are additive for the selected pods: if a pod matches any policy, only traffic explicitly allowed by all applicable policies is permitted. There is no deny rule — you achieve deny-all by creating a policy that selects pods but specifies no allowed traffic.
The standard production pattern is defense in depth. First, apply a default deny-all policy to each sensitive namespace that blocks all ingress and egress for all pods. Then layer specific allow policies on top: the payments-api can receive traffic from the ingress controller and the API gateway on port 8080, can send traffic to payments-db on port 5432, and can reach external payment processors on specific CIDR ranges. This ensures that even if a vulnerability is exploited in one service, lateral movement is restricted.
In production, NetworkPolicies are only enforced if the CNI plugin supports them. AWS VPC CNI alone does not enforce NetworkPolicies — you need to add Calico or Cilium as a policy engine. Kubenet (the default in some environments) also does not support them. Teams have deployed NetworkPolicy YAMLs assuming they were protected, only to discover during a security audit that the CNI never enforced them. Always verify enforcement by testing that a blocked connection actually fails.
The non-obvious gotcha is DNS resolution. A default deny-all egress policy blocks DNS queries to CoreDNS (port 53), which breaks service discovery for all pods in the namespace. You must explicitly allow egress to the kube-system namespace on port 53 (TCP and UDP) in your deny-all policy, or all pods will fail to resolve service names. Another surprise is that NetworkPolicies are namespace-scoped: a policy in namespace A cannot directly reference pods in namespace B by name, only by namespace labels. If namespace labels are not set correctly, cross-namespace rules silently fail to match.