Module 01 · Lesson 01
Network Security Policies
Restrict cluster-level access with default-deny and targeted allow rules.
- Explain why selecting a pod flips it to deny-by-default.
- Write a policy that allows one workload to reach another, and nothing else.
- Spot the indentation trap that turns an AND into an OR.
The default is wide open
A fresh cluster applies no network restrictions at all. Every pod can reach every other pod, in every namespace. A four-tier application with a frontend, an API, a database and a monitoring stack has, by default, a direct path from the public web tier to the database.
A NetworkPolicy changes that, but not in the way a firewall rule would. It has
no deny action and no ordering, and understanding why is most of this topic.
Anatomy of a policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-api
namespace: backend # 1. policies are NAMESPACED
spec:
podSelector: # 2. which pods THIS policy protects
matchLabels:
app: api
policyTypes: # 3. which directions it governs
- Ingress
ingress:
- from: # 4. who is allowed
- namespaceSelector:
matchLabels:
tier: frontend
podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 80
Read it as: in namespace backend, for pods labelled app=api, deny
all ingress except TCP 80 from pods labelled app=web in namespaces labelled
tier=frontend.
Four rules that decide every outcome
A pod with no policy selecting it allows everything. The moment
any policy with Ingress in policyTypes selects
it, all ingress is denied except what that policy, and any other policy selecting it,
explicitly allows.
Two policies selecting the same pod produce the union of their allows. There is no deny rule, no priority and no ordering. If traffic is getting through and you do not want it to, you are looking for the policy that allows it, not writing one that blocks it.
policyTypes: [Ingress] leaves egress completely untouched and wide open.
policyTypes: [Ingress, Egress] locks both to what you allow. Omitting
Egress is the most common way people believe they have isolated a workload
when they have not.
Rule 4: one dash changes the meaning entirely
This is the exam's favourite trap. Compare the indentation:
# AND: pods labelled app=web that are IN namespaces labelled tier=frontend
- from:
- namespaceSelector: {matchLabels: {tier: frontend}}
podSelector: {matchLabels: {app: web}}
# OR: ANY pod in tier=frontend namespaces, PLUS any app=web pod in THIS namespace
- from:
- namespaceSelector: {matchLabels: {tier: frontend}}
- podSelector: {matchLabels: {app: web}}
One leading dash turns an intersection into a union. In the second form, every pod in every frontend namespace is allowed, which is almost never what the task asked for. Check the indentation every single time.
Check the labels you are selecting on
Selectors match labels. A wrong label matches zero pods and the policy becomes a silent no-op, which looks identical to a policy that is working.
Kubernetes sets kubernetes.io/metadata.name on every namespace
automatically, so you can always select one by name without labelling anything yourself:
namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: frontend}}.
The moment you add Egress to policyTypes, the pod can no longer
reach CoreDNS, so every name lookup fails. The symptom is not "connection refused", it is
a hang followed by a resolution error, and it looks nothing like a network policy
problem. Any egress policy needs an explicit allow to kube-system on UDP and
TCP 53.
A pod is selected by two policies. The first allows port 80 from the frontend, the second allows port 443 from monitoring. What reaches the pod?
There is no ordering and no conflict resolution, because there is nothing to resolve. Every policy selecting a pod contributes allows, and the pod permits the union of all of them.
You add an egress policy to a workload and it immediately stops working, with name resolution errors. What did you most likely forget?
Adding Egress denies all outbound traffic including DNS, and CoreDNS is a pod like any other. This is the single most common self-inflicted outage in this topic.
Traffic you want blocked is still arriving at a pod that several policies select. What do you do?
NetworkPolicy has no deny action and no priority field. Traffic arriving means something is allowing it, so the work is auditing every policy that selects the pod.
Selecting a pod flips it to deny-by-default for the directions named in
policyTypes. Policies only add allows, never deny. Watch the leading dash
that turns AND into OR, and remember that egress rules break DNS unless you allow it.
Next: reviewing configuration against the CIS Benchmark.