Network Policies
TL;DR
- By default, Kubernetes networking is a flat, fully-open mesh — any pod can reach any other pod cluster-wide
- NetworkPolicies are the only native mechanism to restrict traffic; they only work if the CNI plugin enforces them (Calico, Cilium, Weave Net do; basic Flannel does not)
- Applying NetworkPolicy to a pod immediately denies all traffic in that direction unless explicitly allowed
- Standard pattern: apply default-deny-all, then layer explicit allow rules
- Always remember to allow egress to CoreDNS (port 53 UDP/TCP) when restricting egress, or DNS breaks for all affected pods
- Namespaces provide zero network isolation by default — only NetworkPolicies or service meshes provide traffic segmentation
- Selector logic in ingress/egress rules: combining podSelector and namespaceSelector in the same entry uses AND logic; separate entries use OR
Core Concepts
The Default-Allow Reality
Without any NetworkPolicy objects, every pod can send traffic to every other pod and receive traffic from every other pod, cluster-wide. This default-allow behavior is often surprising to teams migrating from traditional segmented networks where network isolation is the default.
Critical dependency on CNI: NetworkPolicy is only enforced if the CNI plugin supports it. Flannel in its basic mode does not enforce NetworkPolicies at all — Calico, Cilium, and Weave Net do. Applying a NetworkPolicy on a cluster with a non-enforcing CNI does nothing and creates a dangerous false sense of security.
Default-Deny: The Essential First Policy
The moment a pod is selected by any NetworkPolicy (for a given direction), all traffic in that direction is denied unless explicitly allowed by some policy. The standard pattern is: apply a default-deny-all policy, then layer explicit allow rules on top.
Default-deny policies select all pods in a namespace and block ingress and/or egress until allow rules are added:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {} # selects ALL pods in the namespace
policyTypes:
- Ingress
- Egress
Ingress and Egress Rules
NetworkPolicies define rules for incoming traffic (ingress) and outgoing traffic (egress). Each rule specifies:
- from/to selectors: which pods/namespaces/CIDR ranges the rule applies to
- ports: specific protocols and port numbers to allow
Selector types:
podSelector— other pods, scoped to the same namespace unless combined with namespaceSelectornamespaceSelector— all pods in namespaces matching the labelipBlock— CIDR ranges, for traffic to/from outside the cluster
Selector combination logic: Combining podSelector AND namespaceSelector in a single from/to entry means “pods matching X, in namespaces matching Y” (AND logic). Separate entries in the same list mean OR logic.
DNS and NetworkPolicy: A Common Gotcha
When egress is restricted, pods cannot reach CoreDNS (port 53 UDP) unless explicitly allowed. DNS failures after applying egress rules are extremely common because DNS resolution is just another egress traffic type. Every namespace that applies egress restrictions needs an explicit allow rule permitting egress to CoreDNS (typically via a namespaceSelector matching kube-system, port 53 UDP/TCP).
Network Isolation is Not Automatic
Namespaces provide zero network isolation by default — namespace boundaries are purely organizational and RBAC constructs. Without explicit NetworkPolicies (or a service mesh enforcing mTLS and authorization), a compromised pod in one namespace can freely reach any pod’s IP:port in any other namespace across the entire cluster.
Comparison Table
| Aspect | Default Behavior | With NetworkPolicy | With Service Mesh |
|---|---|---|---|
| Pod-to-pod traffic | Fully allowed cluster-wide | Denied unless explicitly allowed | Denied unless authorized |
| Cross-namespace isolation | None | If policies exist | If policies + mTLS enabled |
| DNS resolution | Works (no restrictions) | Must be explicitly allowed | Works with mTLS |
| CNI dependency | None | Required (plugins vary) | Independent of NetworkPolicy |
| CIDR blocking | Not possible natively | Via ipBlock rules | Via policies + mesh rules |
| Complexity | Minimal | Medium | High |
Command Reference
List all NetworkPolicies in a namespace:
kubectl get networkpolicy -n payments
Describe a policy to see its exact ingress/egress rules:
kubectl describe networkpolicy allow-frontend-to-api -n payments
Apply a network policy manifest:
kubectl apply -f network-policy.yaml
Test connectivity directly to diagnose a policy issue:
kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -qO- http://payments-api:8080 --timeout=3
Check whether the cluster’s CNI supports NetworkPolicy enforcement:
kubectl get pods -n kube-system | grep -Ei "calico|cilium|weave"
View a complete NetworkPolicy definition:
kubectl get networkpolicy allow-frontend-to-api -n payments -o yaml
Delete a NetworkPolicy:
kubectl delete networkpolicy allow-frontend-to-api -n payments
Common Pitfalls
Pitfall — Forgetting to Allow CoreDNS Egress After applying an egress-deny rule, pods suddenly cannot resolve any DNS names, causing cascading failures in applications.
- Why: Default-deny blocks all egress, including UDP port 53 traffic to CoreDNS, which is not obviously related to application traffic.
- Fix: Always include an explicit allow rule for egress to CoreDNS (typically
namespaceSelectormatchingkube-system, port 53 UDP and TCP).
Pitfall — Assuming NetworkPolicy Exists When CNI Doesn’t Support It A NetworkPolicy is created with correct selectors and rules, but traffic remains unrestricted with zero indication of the problem.
- Why: NetworkPolicy is purely an API object; enforcement is entirely delegated to the CNI. Basic Flannel silently accepts the object without enforcing it.
- Fix: Verify the cluster’s CNI plugin actually supports NetworkPolicy enforcement before relying on it for security (Calico, Cilium, Weave Net do; basic Flannel does not).
Pitfall — Namespace Boundaries Providing Network Isolation Assuming workloads in different namespaces are automatically isolated from each other’s network traffic.
- Why: Namespaces are organizational constructs only; they have no impact on network routing or traffic filtering.
- Fix: Use explicit NetworkPolicies to enforce cross-namespace isolation, or use a service mesh that enforces mTLS and authorization policies.
Pitfall — Misunderstanding Selector Combination Logic Combining podSelector and namespaceSelector in the same ingress/egress rule, expecting OR logic when the actual behavior is AND.
- Why: The Kubernetes API interprets combining these selectors in a single entry as AND logic, not OR.
- Fix: Use separate entries in the list for OR logic; place selectors in the same entry only when both conditions must match.
Interview Questions
Q: Explain the default pod-to-pod traffic behavior in Kubernetes without any NetworkPolicies applied.
Q: Why does a default-deny-all NetworkPolicy often break DNS resolution, and how do you fix it?
Q: A NetworkPolicy looks correctly configured, but traffic is still unrestricted. What is the first thing to verify?
Q: Walk through the difference between AND logic and OR logic when combining selectors in NetworkPolicy rules.
Q: Design a network isolation strategy for a multi-tenant cluster where teams should not be able to reach each other’s workloads.
Q: Can namespaces isolate network traffic without NetworkPolicies? Explain.
Q: What are the differences in traffic restrictions provided by NetworkPolicies versus a service mesh with mTLS?
Q: When would using ipBlock rules in a NetworkPolicy be necessary versus just using pod/namespace selectors?