26

Network Policies

Video: Day 26/40 — Kubernetes Network Policies Explained • https://www.youtube.com/watch?v=eVtnevr3Rao • Duration: ~26 min

Key terms

TermMeaning
NetworkPolicyFirewall rules for pod traffic
IngressAllowed inbound traffic
EgressAllowed outbound traffic
podSelectorThe pods a policy applies to
namespaceSelectorSelect peer pods by namespace
Default-denyBaseline policy blocking all traffic
CNIPlugin 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 stepMaps toWhat it does
TCP connect to postgres port 5432a client connection to the DBThe backend opens the Postgres port
Evaluates NetworkPolicies selecting app=postgresspec.podSelector.matchLabels app=postgresFinds every policy targeting the Postgres pods
Rule allow from podSelector role=backend on 5432ingress.from.podSelector + ingress.portsThe single allowed source + port
backend matches the rule, ALLOWEDthe CNI's allow decision (Calico)Permits the connection to Postgres
frontend tries port 5432, no rule matchesdefault-deny for the selected directionNo matching from rule for the frontend
packet DROPPED, connection times outthe CNI's silent dropFrontend 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 nodeMaps toWhat it does
backend/frontend pod connects to postgres:5432client pods opening the DB portThe two candidate sources
CNI evaluates NetworkPolicies selecting app=postgresspec.podSelector match on PostgresGathers the policies that protect Postgres
Source matches allow rule role=backend on 5432?ingress.from.podSelector + portsThe allow test the CNI applies per packet
Packet allowed to postgres:5432the allow branchBackend's connection reaches Postgres
Packet dropped, connection times outthe default-deny branchFrontend's connection is silently dropped

Key takeaways

  • Pods are open by default; NetworkPolicy adds a firewall.
  • Needs an enforcing CNI (Calico) — kindnet ignores policies.
  • podSelector picks the target; from/to + ports allow 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-backend policy and verified only backend reaches postgres
  • [ ] Tried a default-deny-ingress baseline and saw traffic blocked