NetworkPolicy: who can talk to whom
How to lock a Pod down from unwanted traffic with label-based rules, and where the one-dash mistake hides.
What a NetworkPolicy is
A NetworkPolicy is a Kubernetes object that sets rules for network traffic between Pods. A Pod is the smallest unit Kubernetes runs; one or more containers run inside a Pod. NetworkPolicy works like a firewall, but it selects Pods by their labels, not by IP addresses.
The NetworkPolicy object itself blocks nothing. The rules are enforced by the CNI plugin (Container Network Interface, the cluster component responsible for Pod networking). Calico, Cilium and Kube-router support NetworkPolicy, but simpler plugins like flannel don't: the rules get created in the API and are silently ignored. You need to check a policy's effect on the exact cluster where it will run.
kind's default network plugin is kindnet, and starting with kind v0.24.0 it enforces NetworkPolicy. This lesson's labs run on it.
Everything is allowed by default
If a namespace has no NetworkPolicy at all, traffic is open. Any Pod can reach anywhere. A namespace is an area inside the cluster that separates one set of objects from another.
As soon as a NetworkPolicy is attached to a Pod, a rule kicks in for it: everything not explicitly allowed is denied. After that, the policy lists which traffic gets through.
If a Pod is selected by several policies, their permissions add up. There's no order among policies, and one policy can't revoke what another allowed.
Anatomy of a manifest
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-only-from-allowed
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: allowed
ports:
- protocol: TCP
port: 8080
podSelector is which Pods the rules apply to. Here it's Pods with the
label app: web. An empty {} means every Pod in the namespace.
policyTypes is which traffic the policy governs. Ingress is incoming
traffic, Egress is outgoing. You can list one type or both.
ingress.from is where incoming traffic is allowed from. A source is set
through podSelector (Pods with these labels), namespaceSelector (Pods
from a namespace with these labels) or ipBlock (an IP range).
A podSelector inside from with no namespaceSelector next to it only
looks for Pods in the policy's own namespace. To select a namespace by
name, the label kubernetes.io/metadata.name is handy: Kubernetes sets it
on every namespace itself, with the value equal to the namespace's name.
ports is which ports are open. Without this block, all ports are open.
AND and OR in selectors
Inside one from list element, selectors are combined with AND: a Pod
with this label AND in a namespace with that label. Different list
elements are combined with OR.
AND, one element, two selectors (a Pod with label role: allowed AND in a
namespace with label team: a):
ingress:
- from:
- podSelector:
matchLabels:
role: allowed
namespaceSelector:
matchLabels:
team: a
OR, two elements (a Pod with label role: allowed OR any Pod from a
namespace with label team: a):
ingress:
- from:
- podSelector:
matchLabels:
role: allowed
- namespaceSelector:
matchLabels:
team: a
The difference is one dash. It's the single most common mistake in NetworkPolicy.
What's in from | Logic | Who gets through |
|---|---|---|
| two selectors in one list element | AND | a Pod with label role: allowed from namespace team: a |
| two separate list elements | OR | a Pod with label role: allowed, or any Pod from team: a |
Checking the effect
The most reliable way to check a policy is to try connecting from both
sides. For example, next to Pod web (fronted by Service web) there are
two clients on image busybox:1.36.1: client-allowed with label
role: allowed, and client-blocked with label role: blocked.
From the allowed client:
kubectl exec client-allowed -- nc -zv -w 2 web 8080
web (10.96.12.34:8080) open
From the blocked one:
kubectl exec client-blocked -- nc -zv -w 2 web 8080
nc: web (10.96.12.34:8080): Connection timed out
command terminated with exit code 1
The allowed client connects right away, the blocked one waits 2 seconds
(-w 2) and gets a timeout: packets are silently dropped, and no
"connection refused" comes back. Notice the port: the policy opens 8080, so that's what
gets checked, not 80. A new policy doesn't apply instantly, it takes a few
seconds, so a probe right after apply is worth repeating.
Three commands help figure out what actually applied:
# List of policies in the namespace
kubectl get networkpolicy
# How the policy breaks down into selectors and ports
kubectl describe networkpolicy web-only-from-allowed
# Pod labels, to see what matchLabels catches
kubectl get pods --show-labels
Common mistakes
- An extra dash in
from: instead of "a Pod with this label AND from this namespace" you get "a Pod with this label OR any Pod from this namespace". Check withkubectl describe networkpolicy. - A
podSelectorinfromwith nonamespaceSelector, meant to target a Pod in another namespace: it only looks in the policy's own namespace. - The policy was created in the wrong namespace:
podSelectoronly selects Pods where the policy itself lives. - Checking a port that
portsdoesn't open. - A cluster with a CNI that doesn't support NetworkPolicy: the object exists, but nothing gets filtered.
Homework
Practice the theory on your own cluster: 2 steps with a check. Set up the cluster with the Kubernetes locally track, the tasks live in the k8s-practice repository.
./run.sh start k8s-network-policy # set up the starting state
./run.sh verify k8s-network-policy # check the current step
In short
- NetworkPolicy selects Pods by labels, not by IP, and lives inside a single namespace.
- While there are no policies, traffic is open. The first policy switches the selected Pods into "deny everything except what's listed" mode.
- The rules are enforced by the CNI plugin. Without plugin-side support, the object gets created but filters nothing.
- Selectors inside one
fromelement work as AND, different list elements as OR. The difference is one dash. - Without a
portsblock, all ports of the selected Pods are open.
What you'll need for the tasks
kubectl config current-context: confirm you're working in the lab cluster.kubectl -n <ns> exec <pod> -- nc -zv -w 2 <address> <port>: probe a connection from a client.kubectl get networkpolicy,kubectl describe networkpolicyand-o yaml: what's written in the policy.kubectl get pod --show-labelsandkubectl get namespace --show-labels: the labels selectors act on.kubectl apply -fandkubectl edit: create or fix a policy.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson k8s-network-06-network-policy
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress