Kubernetes Network Policies: Pod-Level Micro-Segmentation
By default, all pods can reach all other pods in Kubernetes. NetworkPolicy resources change this to explicit allow.
Default Deny All
Apply this to every namespace you care about:
YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Selects ALL pods
policyTypes:
- Ingress
- EgressAllow Frontend to Backend
YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080Allow DNS (Critical!)
Without this, pods can't resolve DNS — add it with default-deny:
YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCPNamespace Isolation
YAML
# Only allow traffic within the same namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-cross-namespace
namespace: production
spec:
podSelector: {}
ingress:
- from:
- podSelector: {} # Same namespace onlyDatabase Access Control
YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-access
namespace: production
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- podSelector:
matchLabels:
role: db-client
ports:
- protocol: TCP
port: 5432Testing Network Policies
BASH
# Deploy test pod
kubectl run test --image=busybox -it --rm -- /bin/sh
# Inside test pod
wget -qO- http://backend:8080/health # Should work
wget -qO- http://database:5432 # Should failAlways use a CNI plugin that enforces NetworkPolicy: Calico, Cilium, or Antrea.
