The default is fully open
In Kubernetes, every Pod in every namespace can connect to every other Pod. If a frontend Pod is compromised, the attacker goes straight to the database, Redis or an internal dashboard. A NetworkPolicy is a label-based firewall at layers 3–4.
NetworkPolicy only works when the CNI supports it: Calico, Cilium, Antrea, Azure NPM/Cilium on AKS, the VPC CNI with network policy on EKS. Plain Flannel does not enforce it.
How it works
podSelectorchooses the Pods the policy applies to.- Once a Pod is selected by at least one policy for
Ingress(orEgress), all traffic in that direction is blocked unless a rule allows it. - Policies are additive; there is no separate "deny" rule.
Default deny, then open step by step
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny, namespace: app }
spec:
podSelector: {} # every Pod in the namespace
policyTypes: [Ingress, Egress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: api-from-web, namespace: app }
spec:
podSelector: { matchLabels: { app: api } }
policyTypes: [Ingress]
ingress:
- from:
- podSelector: { matchLabels: { app: web } }
ports: [{ protocol: TCP, port: 8080 }]
When blocking egress, remember to allow DNS to CoreDNS, or every name lookup fails:
egress:
- to:
- namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: kube-system } }
podSelector: { matchLabels: { k8s-app: kube-dns } }
ports: [{ protocol: UDP, port: 53 }, { protocol: TCP, port: 53 }]
flowchart LR
W["web"] -- "TCP 8080 ✓" --> A["api"]
A -- "TCP 5432 ✓" --> D[("db")]
W -. "✗ blocked" .-> D
X["Unknown Pod in another namespace"] -. "✗" .-> A
Common traps
- A
fromwith two items (- namespaceSelectorand- podSelector) is an OR; one item containing both selectors is an AND. Getting this wrong can open a whole namespace. - Applying default-deny egress and forgetting DNS.
- Believing a policy blocks traffic when the CNI does not support it.
kubectl get networkpolicy -n app
kubectl describe networkpolicy api-from-web -n app
kubectl run tmp -n app --rm -it --image=busybox:1.36 -- wget -qO- -T 2 http://db:5432
The last command tests a connection from a temporary Pod to confirm the policy behaves as intended. Cilium and Calico also offer layer-7 policies (HTTP paths, DNS names) and traffic observability tools such as Hubble.