How do NetworkPolicies work -- what is the default behavior and how do you implement zero-trust networking?
Quick Answer
By default, Kubernetes allows all Pod-to-Pod traffic with no restrictions. NetworkPolicies are namespace-scoped resources that select Pods via labels and define allowed ingress and egress rules. Implementing zero-trust requires a default-deny policy in every namespace followed by explicit allow rules for each legitimate communication path.
Detailed Answer
Think of a Kubernetes cluster without NetworkPolicies as an open-plan office with no walls or doors -- anyone can walk up to anyone's desk and start a conversation. NetworkPolicies are like installing walls, doors, and a badge-access system. A default-deny policy locks all doors, and then you install specific badge readers that allow only authorized personnel to enter specific rooms. Zero-trust networking means starting from 'deny everything' and explicitly granting only the access that is proven necessary.
By default in Kubernetes, every Pod can communicate with every other Pod across all namespaces -- there are no network restrictions. This is the flat network model. NetworkPolicies are the mechanism to restrict this open access. They are Kubernetes-native resources (networking.k8s.io/v1) that are enforced by the CNI plugin (Calico, Cilium, Weave Net, etc.) -- the critical caveat is that not all CNI plugins support NetworkPolicies. Flannel, for example, does not enforce them at all, meaning you can create NetworkPolicy objects that are silently ignored. Always verify your CNI plugin supports policy enforcement.
A NetworkPolicy selects target Pods using spec.podSelector (label-based). Once a Pod is selected by any NetworkPolicy, it becomes isolated for the direction (ingress, egress, or both) specified in policyTypes. Traffic that does not match an explicit allow rule in any selecting NetworkPolicy is denied. If no NetworkPolicy selects a Pod, it remains non-isolated and accepts all traffic. Ingress rules define which sources can send traffic to the selected Pods (by podSelector, namespaceSelector, ipBlock, or combinations). Egress rules define which destinations the selected Pods can send traffic to. Each rule can specify ports and protocols. The rules are additive: if multiple NetworkPolicies select the same Pod, the union of all their allow rules applies -- there are no deny rules or precedence ordering.
Implementing zero-trust requires a layered approach. First, deploy a default-deny-all NetworkPolicy in every namespace that selects all Pods ({}) and specifies both Ingress and Egress in policyTypes with no rules -- this blocks everything. Then, create specific allow policies for each known communication path. For example, allow the checkout-api to reach the payments-api on port 8080, allow the payments-api to reach the inventory-db on port 5432, allow all Pods to reach CoreDNS on port 53 (often forgotten, breaking all DNS resolution), and allow the monitoring namespace to scrape metrics from all namespaces. Use namespaceSelector with labels to scope cross-namespace rules. For egress to external services, use ipBlock with CIDR ranges.
A non-obvious gotcha is that blocking egress without allowing DNS (UDP port 53 to the kube-system namespace where CoreDNS runs) silently breaks all Service name resolution, causing applications to fail with connection errors that look like network issues rather than DNS issues. Another trap: NetworkPolicies do not apply to traffic through the host network. Pods with hostNetwork: true bypass NetworkPolicies entirely because they share the node's network namespace. Additionally, NetworkPolicy enforcement happens at the Pod level, not the Service level -- if a Service load-balances to Pods in different namespaces, each backing Pod needs its own ingress rules. Finally, there is no audit mode for NetworkPolicies in vanilla Kubernetes; deploying a restrictive policy can break production traffic with no warning. Cilium offers a policy audit mode that logs violations without blocking, which is essential for safely rolling out zero-trust in existing clusters.