BlogKubernetes

Kubernetes network policies: default-deny, keep DNS up

Lock down pod-to-pod traffic with a default-deny NetworkPolicy, then re-allow DNS and the flows your apps actually need.

Jul 3, 2026·11 min readAdvanced

By default a Kubernetes cluster is a flat network: every pod can reach every other pod, in any namespace. A NetworkPolicy is the cluster's firewall, but it only takes effect once your CNI enforces it (Calico, Cilium, and most managed CNIs do). The safe pattern is default-deny, then allow — and the very first thing default-deny breaks is DNS, so we fix that in the same breath.

bash — a flat network by defaultlive
kubectl exec -n shop web-0 -- curl -s --max-time 3 db:5432
(connected) — any pod can reach any pod
kubectl get networkpolicy -n shop
No resources found in shop namespace.
convenient for you, and exactly what an attacker wants

Step 1: default-deny the namespace

This policy selects every pod (podSelector: {}) and allows no ingress or egress. Apply it and the namespace goes dark.

default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: shop
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
You just broke DNS
Egress deny includes UDP/53 to CoreDNS, so every name lookup fails and apps hang before they ever connect. Re-allow DNS next — this trips up almost everyone.

Step 2: allow DNS back

allow-dns.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: shop
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels: { k8s-app: kube-dns }
ports:
- { protocol: UDP, port: 53 }
- { protocol: TCP, port: 53 }

Step 3: allow only the flows you need

Now open the specific paths. This lets web pods reach db on 5432 — and nothing else can.

web-to-db.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-to-db
namespace: shop
spec:
podSelector:
matchLabels: { app: db }
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels: { app: web }
ports:
- { protocol: TCP, port: 5432 }
What a request now traverses
1
web pod
egress allowed
2
DNS lookup
UDP/53 to CoreDNS
3
db ingress
from app=web only
4
anything else
dropped

Verify it actually denies

bash — test the negative caselive
kubectl exec -n shop web-0 -- curl -s --max-time 3 db:5432
web -> db:5432 OK
kubectl exec -n shop test-0 -- curl -s --max-time 3 db:5432
timed out — denied, as intended

Always test the denied path too. A policy that allows the happy path but forgets to deny the rest is a policy that does nothing.

Go deeper in a courseKubernetes security & hardeningNetwork policy, RBAC, Pod Security and the rest of CKS, hands-on.View course

Related posts