Your first policy: Kubernetes NetworkPolicy
Your first policy: Kubernetes NetworkPolicy
Every pod in a fresh cluster can talk to every other pod - a flat network where
one compromised workload can reach all the others. This track walks up the four
policy types that fix that, from the one your cluster already supports to
Calico's cluster-wide controls. We start at the bottom rung: the upstream
Kubernetes NetworkPolicy (networking.k8s.io/v1).
What you'll learn
- That a Kubernetes NetworkPolicy is namespaced and picks the pods it
governs with
spec.podSelector. - How selecting a pod with a
policyTypes: [Ingress]policy flips it to default-deny - noDenyrule required. - That only the pods you select change; everything else stays open.
What is a Kubernetes NetworkPolicy?
A NetworkPolicy (networking.k8s.io/v1) is the policy type built into every
Kubernetes cluster - the portable baseline the rest of this track builds on. It
is namespaced and allow-list only: you can't write a deny, you can only
name what's permitted, and the act of selecting a pod flips it to default-deny.
Its entire field surface is small:
| Field | Purpose |
|---|---|
spec.podSelector |
which pods in this namespace the policy governs (matchLabels/matchExpressions); empty selects every pod in the namespace |
spec.policyTypes |
Ingress, Egress, or both - the directions this policy default-denies |
spec.ingress[].from / spec.egress[].to |
the allowed peers: podSelector, namespaceSelector, or ipBlock (a CIDR) |
spec.ingress[].ports / egress[].ports |
allowed protocol + port (plus endPort for a range) |
Two properties define it: it is namespace-scoped (a bare podSelector only
sees local pods - crossing namespaces needs a namespaceSelector), and it has
no action, no order, no tiers - every rule is an implicit allow. Those
exact gaps are what the cluster-scoped and Calico kinds later in this track fill.
The policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: prod
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
metadata.namespace: prod- a KNP only ever acts inside its own namespace.devis a different world, untouched by this object.podSelector: {app: backend}- the policy governs theprod/backendpod and nothing else.ingress.from.podSelector: {app: frontend}- the one admitted source.- Because a policy now selects
prod/backend, that pod is default-deny: every inbound connection is refused unless this rule allows it.
What to observe
Allowed
prod/frontend → prod/backend:8080- the one path the rule names.
Denied
prod/database → prod/backend- a real pod, but notapp == frontend.dev/frontend → prod/backend-podSelectoris namespace-local; thisfrontendlives indev, so it isn't matched (more on crossing namespaces in the next lesson).
Untouched (still open)
prod/frontend → prod/database-databaseis selected by no policy, so it keeps the default-allow behaviour.
The trap is thinking you "denied" the database. You didn't. A KNP only constrains the pods its
podSelectormatches. Until something selectsprod/database, it accepts everyone. Default-deny is per-selected-pod, not per-namespace.
Recap
A Kubernetes NetworkPolicy is namespaced, selects pods by label, and the act of selecting flips those pods to default-deny - you then add back the flows you trust. Next: reaching across namespaces, and the limits that make you want a bigger tool.