2026-09-10 · 7 min read
Kubernetes Network Policies: Locking Down Pod Traffic
By default every pod can talk to every other pod. Network Policies fix that. A practical guide to default-deny, ingress/egress rules, and zero-trust networking in Kubernetes.

Kubernetes Network Policies: Locking Down Pod Traffic
Here's something that surprises people new to Kubernetes: by default, every pod can reach every other pod in the cluster. No firewall, no segmentation. A compromised frontend pod can talk straight to your database, your internal admin service, and everything else. Network Policies are how you close that down, and they're criminally underused.
The default is wide open
Kubernetes networking is flat. Any pod can initiate a connection to any other pod by IP, across namespaces, with nothing in the way. For a single trusted app this is convenient. For a real multi-service cluster, it means one popped container has lateral movement to everything.
Network Policies let you declare which pods can talk to which: a per-pod firewall expressed as Kubernetes resources, enforced by your CNI.
You need a CNI that enforces them
A critical gotcha: NetworkPolicy objects are inert unless your CNI plugin enforces them. Calico,
Cilium, and Weave do. The default kubenet and some basic setups do not: your policies will
apply with no effect and a false sense of security. Check your CNI first.
Start with default-deny
The right foundation is default-deny, then allow what's needed: zero trust at the network layer. This policy denies all ingress to every pod in a namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: app
spec:
podSelector: {} # all pods in the namespace
policyTypes:
- Ingress
An empty podSelector selects every pod; listing Ingress with no ingress: rules means "allow
nothing in." Now nothing can reach these pods until you explicitly permit it. Do the same for egress
once you're confident.
Then allow specific paths
Let the frontend reach the API, and only the API reach the database:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-from-frontend
namespace: app
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow-from-api
namespace: app
spec:
podSelector:
matchLabels: { app: postgres }
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels: { app: api }
ports:
- { protocol: TCP, port: 5432 }
Now the frontend physically cannot reach the database, even if it's compromised. That's the win: blast-radius containment.
Don't forget egress and DNS
Egress policies control what pods can reach outbound. They're powerful for stopping data exfiltration, but there's a classic trap: lock down egress and you'll break DNS unless you explicitly allow it. Always permit DNS to kube-dns:
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- { protocol: UDP, port: 53 }
- { protocol: TCP, port: 53 }
Cross-namespace rules
Use namespaceSelector to allow traffic from another namespace (e.g. let monitoring scrape your
pods):
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
Practical rollout advice
- Label namespaces and pods consistently: policies select on labels, so a labeling convention is a prerequisite.
- Roll out in audit/log mode first (Cilium and Calico support this) so you see what would be blocked before you actually block it.
- Start with default-deny ingress per namespace, add allow rules, then tackle egress once stable.
- Test from inside the cluster:
kubectl run tmp --rm -it --image=nicolaka/netshootand try to reach things that should and shouldn't be reachable.
Where it fits in defense-in-depth
Network Policies are one layer. Combine them with:
- RBAC for the API server (who can do what).
- Pod Security Standards (what containers can do on the node).
- A service mesh for mTLS and L7 policy if you need it (Istio/Linkerd/Consul comparison).
Network Policies handle L3/L4 segmentation cheaply and natively, they should be the baseline on every real cluster. See the devsecops-starter-kit for where this sits in a hardened setup.
The short version
- Default Kubernetes networking is flat, every pod can reach every pod.
- Confirm your CNI (Calico/Cilium) actually enforces NetworkPolicy.
- Adopt default-deny, then allow specific paths, zero trust at L3/L4.
- Always allow DNS when you restrict egress.
- Roll out in audit mode first, label consistently, test from inside.
Hardening Kubernetes clusters is part of my DevSecOps consulting, reach out.