Kubernetes Network Policies zijn een van die features waarvan iedereen weet dat ze het moeten gebruiken maar weinigen echt begrijpen. De YAML ziet er intimiderend uit, het gedrag is niet-intuïtief, en het mentale model kost tijd om te ontwikkelen.
Ik heb uren besteed aan het debuggen van policies die “zouden moeten werken” maar het niet deden. Laat me je die pijn besparen met een visuele benadering van Network Policies.
De Default: Alles Praat met Alles
Standaard staat Kubernetes alle pod-naar-pod communicatie toe. Elke pod kan elke andere pod bereiken over elke namespace. Dit is handig om te starten maar verschrikkelijk voor security.
flowchart TD
subgraph cluster["Cluster"]
Pod1["Pod"] <--> Pod2["Pod"]
Pod2 <--> Pod3["Pod"]
Pod3 <--> Pod4["Pod"]
Pod1 <--> Pod3
Pod1 <--> Pod4
Pod2 <--> Pod4
end
note["Iedereen kan praten"]
Als een aanvaller één pod compromitteert, kan deze elke andere pod in het cluster bereiken. Dit schendt zero trust principes — we moeten breach aannemen en blast radius beperken.
Network Policies: De Firewall voor Pods
Een NetworkPolicy is een specificatie van hoe pods mogen communiceren. Zie het als een firewall regel die van toepassing is op groepen pods.
Kernconcepten:
- Ingress: Inkomend verkeer naar een pod
- Egress: Uitgaand verkeer van een pod
- Selectors: Hoe pods te identificeren (via labels)
- Policy Types: Of je ingress, egress, of beide controleert
De Belangrijkste Regel: Default Deny
De eerste policy die je moet toepassen op elke namespace is default deny. Dit blokkeert al het verkeer, dan sta je expliciet toe wat nodig is.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Leeg = alle pods
policyTypes:
- Ingress
- Egress
flowchart TD
subgraph production["production namespace"]
Pod1["Pod"] x--x Pod2["Pod"]
Pod2 x--x Pod3["Pod"]
Pod3 x--x Pod4["Pod"]
end
note["Al het verkeer standaard geblokkeerd"]
Nu kan niets communiceren. We bouwen op vanaf nul.
Ingress Toestaan: Wie Mag NAAR Deze Pod Praten?
Stel we hebben een frontend die verkeer moet ontvangen:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-ingress
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
- podSelector:
matchLabels:
app: ingress-controller
ports:
- protocol: TCP
port: 8080
flowchart LR
subgraph ingress-nginx["ingress-nginx ns"]
IC["ingress controller"]
end
subgraph production["production ns"]
FE["frontend<br/>app:frontend"]
Other["andere pods"]
end
IC -->|"✓"| FE
Other x--x|"✗"| FE
De frontend kan alleen verkeer ontvangen van de ingress controller op poort 8080.
Egress Toestaan: Waar Mag Deze Pod NAAR Praten?
De frontend moet praten met de backend API:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-egress
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 3000
- to: # DNS toestaan
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
flowchart TD
subgraph production["production namespace"]
FE["frontend"] -->|"✓"| BE["backend"]
BE x--x|"✗"| DB["database"]
FE -->|"✓ alleen DNS"| DNS["kube-dns"]
end
De frontend kan alleen de backend bereiken (poort 3000) en DNS. Het kan niet direct bij de database.
Het Complete Three-Tier Voorbeeld
Hier is een realistische setup: ingress → frontend → backend → database.
# Frontend policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress
ports:
- port: 80
egress:
- to:
- podSelector:
matchLabels:
tier: backend
ports:
- port: 8080
- to: # DNS
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
---
# Backend policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- port: 5432
- to: # DNS
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
---
# Database policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: backend
ports:
- port: 5432
egress: [] # Database hoeft geen verbindingen te initiëren
flowchart LR
Internet --> Ingress
Ingress --> Frontend
Frontend --> Backend
Backend --> Database
Frontend -.->|"Alleen DNS"| DNS["kube-dns"]
Backend -.->|"Alleen DNS"| DNS
Elke tier kan alleen praten met de aangrenzende tier. De frontend kan de database niet bereiken. De database kan het internet niet bereiken.
Veelvoorkomende Valkuilen
Valkuil 1: Vergeet DNS Niet
Je pods hebben DNS nodig om service namen te resolven. Sta altijd egress naar kube-dns toe:
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
Zonder dit kunnen je pods backend.production.svc.cluster.local niet resolven.
Valkuil 2: Policies Zijn Additief
Meerdere policies op dezelfde pod worden gecombineerd met OR logica. Als policy A verkeer van X toestaat, en policy B verkeer van Y toestaat, ontvangt de pod verkeer van zowel X als Y.
Je kunt geen “deny” policy maken die een “allow” policy overschrijft. De enige manier om te denyen is niet toestaan.
Valkuil 3: Lege Selector Betekent “Alles”
podSelector: {} # Alle pods in deze namespace
namespaceSelector: {} # Alle namespaces
Dit vangt veel mensen. Een lege selector betekent niet “niets” — het betekent “alles.”
Valkuil 4: Policies Beïnvloeden Alleen Geselecteerde Pods
Een NetworkPolicy beïnvloedt alleen pods die matchen met zijn podSelector. Pods zonder policies die ze selecteren hebben geen restricties.
Dit is waarom default-deny belangrijk is: het zorgt dat elke pod minstens één policy heeft.
Network Policies Testen
Test altijd je policies. Hier is een snelle manier:
# Maak een test pod
kubectl run test-pod --image=busybox --rm -it -- sh
# Probeer een andere service te bereiken
wget -qO- --timeout=2 http://backend.production:8080/health
# Probeer de database direct te bereiken (zou moeten falen)
nc -zv database.production 5432
Of gebruik een tool zoals netshoot:
kubectl run netshoot --image=nicolaka/netshoot -it --rm -- bash
CNI Vereisten
Niet alle CNIs ondersteunen Network Policies. Je hebt een CNI nodig die de NetworkPolicy API implementeert:
- Calico: Volledige ondersteuning, plus uitbreidingen met meer features
- Cilium: Volledige ondersteuning, plus L7 policies
- Weave: Volledige ondersteuning
- Flannel: Geen ondersteuning (heeft Calico erbovenop nodig)
Check je CNI voordat je op Network Policies vertrouwt.
Voorbij Basis Policies
Voor meer geavanceerde use cases, overweeg:
- Cilium Network Policies: L7 (HTTP) filtering, DNS-based policies
- Calico Network Policies: Host bescherming, global policies
- Service Mesh: Istio/Linkerd voor mutual TLS en L7 policies
De Kubernetes NetworkPolicy API is opzettelijk simpel. Complexere vereisten hebben vaak CNI-specifieke uitbreidingen nodig.
Mijn Aanbeveling
Start simpel:
- Pas default-deny toe op elke namespace
- Voeg policies toe die alleen noodzakelijke communicatie toestaan
- Test grondig voordat je naar productie gaat
- Gebruik tools zoals Cilium’s policy editor om te visualiseren
Network Policies zijn een van de beste security verbeteringen die je kunt maken in Kubernetes. Ze zijn gratis (geen extra infrastructuur), ze zijn declaratief (infrastructure as code), en ze beperken blast radius dramatisch.
Het mentale model kost oefening, maar zodra het klikt vraag je je af hoe je ooit een cluster draaide zonder.
Network Policies implementeren het principe van least privilege op netwerkniveau. Elke connectie die niet hoeft te bestaan zou niet moeten bestaan.
