Network Policies
Video: Day 26/40 — Kubernetes Network Policies Explained • https://www.youtube.com/watch?v=eVtnevr3Rao • Duration: ~26 min
Key terms
| Term | Meaning |
|---|---|
| NetworkPolicy | Firewall rules for pod traffic |
| Ingress | Allowed inbound traffic |
| Egress | Allowed outbound traffic |
| podSelector | The pods a policy applies to |
| namespaceSelector | Select peer pods by namespace |
| Default-deny | Baseline policy blocking all traffic |
| CNI | Plugin that must enforce policy (e.g. Calico) |
Problem & solution
By default every pod can reach every other pod, so a single compromised pod can talk to anything in the cluster. We need a pod-level firewall to restrict which traffic is allowed in and out.
Solution: Apply NetworkPolicies (enforced by a capable CNI) to firewall pod-to-pod traffic with default-deny plus explicit allow rules.
The analogy
Inside our port, leaving every shed and gate wide open means anyone who slips in can wander into any building and load any cargo. So the operators put up internal fences and checkpoints that decide which sheds may talk to which, and post a guard who inspects each cart's permit before waving it through. A Kubernetes NetworkPolicy is exactly those fences and checkpoints, declaring which pods may exchange traffic, and the enforcing CNI such as Calico is the guard that lets matching connections through and silently drops everyone else.
Where this fits in the cluster
The same cluster entities appear in every day's notes; the diagram below shows where this day's topic fits.
The default: everything can talk to everything
By default Kubernetes networking is flat and open — any pod can reach any other pod. A NetworkPolicy is a firewall for pods that restricts this.
You need a CNI that ENFORCES policy
NetworkPolicy objects are inert unless the CNI plugin implements them.
The default kindnet does NOT — use Calico (or Cilium, etc).
# kind cluster with default CNI disabled, so we can install Calico
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
networking:
disableDefaultCNI: true
podSubnet: 192.168.0.0/16
Then install Calico (docs.tigera.io/calico .../kind). Without an enforcing CNI, your policies are silently ignored.
Direction: Ingress vs Egress
A policy controls inbound traffic, outbound traffic, or both, declared in
policyTypes.
Ingress -> traffic INTO the selected pods
Egress -> traffic OUT of the selected pods
policyTypes lists which directions this policy controls
How selection works
A policy uses label selectors to pick the target pods and to describe which peers and ports are allowed.
podSelector -> which pods THIS policy applies to (the target)
from / to -> the allowed peers (podSelector / namespaceSelector / ipBlock)
ports -> which ports/protocols are allowed
Key rule: as soon as a pod is selected by ANY policy for a direction, it becomes default-deny for that direction — only the listed traffic is allowed.
Example: only backend may reach Postgres on 5432
This ingress policy targets the Postgres pods (app=postgres) and allows
traffic only from pods labelled role=backend on port 5432.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-allow-backend
spec:
podSelector:
matchLabels:
app: postgres # applies to the Postgres pods
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: backend # only pods labelled role=backend
ports:
- port: 5432
Result: backend -> postgres:5432 allowed; frontend -> postgres blocked.
Default-deny everything (common baseline)
Selecting all pods with no rules creates a deny-all baseline you then open up with additive allow policies.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {} # all pods in the namespace
policyTypes:
- Ingress
# no ingress rules = deny all inbound
Test it
Exec into pods on either side and curl the db to confirm allowed traffic
connects and blocked traffic hangs.
# from a backend pod (should work), then a frontend pod (should hang/fail)
kubectl exec -it backend -- nc -zv -w 3 postgres 5432
kubectl exec -it frontend -- nc -zv -w 3 postgres 5432
End-to-end example: who may reach postgres:5432
The CNI allows the backend pod and silently drops everyone else.
Graph legend — each step maps to a concrete policy field or component:
| Graph step | Maps to | What it does |
|---|---|---|
| TCP connect to postgres port 5432 | a client connection to the DB | The backend opens the Postgres port |
| Evaluates NetworkPolicies selecting app=postgres | spec.podSelector.matchLabels app=postgres | Finds every policy targeting the Postgres pods |
| Rule allow from podSelector role=backend on 5432 | ingress.from.podSelector + ingress.ports | The single allowed source + port |
| backend matches the rule, ALLOWED | the CNI's allow decision (Calico) | Permits the connection to Postgres |
| frontend tries port 5432, no rule matches | default-deny for the selected direction | No matching from rule for the frontend |
| packet DROPPED, connection times out | the CNI's silent drop | Frontend traffic never reaches Postgres |
End-to-end flow
The CNI evaluates policies per packet, allowing matching peers and dropping the rest.
Graph legend — each node maps to a concrete policy field or decision:
| Graph node | Maps to | What it does |
|---|---|---|
| backend/frontend pod connects to postgres:5432 | client pods opening the DB port | The two candidate sources |
| CNI evaluates NetworkPolicies selecting app=postgres | spec.podSelector match on Postgres | Gathers the policies that protect Postgres |
| Source matches allow rule role=backend on 5432? | ingress.from.podSelector + ports | The allow test the CNI applies per packet |
| Packet allowed to postgres:5432 | the allow branch | Backend's connection reaches Postgres |
| Packet dropped, connection times out | the default-deny branch | Frontend's connection is silently dropped |
Key takeaways
- Pods are open by default; NetworkPolicy adds a firewall.
- Needs an enforcing CNI (Calico) — kindnet ignores policies.
podSelectorpicks the target;from/to+portsallow specific peers.- Selecting a pod for a direction makes it default-deny for that direction.
- Policies are additive (allow-only); combine them per namespace.
Checklist
- [ ] Created a kind cluster with default CNI disabled + installed Calico
- [ ] Deployed frontend/backend/postgres and confirmed open connectivity
- [ ] Applied the
postgres-allow-backendpolicy and verified only backend reaches postgres - [ ] Tried a
default-deny-ingressbaseline and saw traffic blocked