On the CKS exam, network policy is where a lot of candidates quietly lose points. The mechanics look simple — a NetworkPolicy is just a few selectors and rules — but the exam doesn’t test whether you can copy a manifest. It tests whether you understand the security model: that Kubernetes pods accept traffic from anywhere until you say otherwise, that a policy is additive, and that locking down egress is what actually stops a compromised pod from phoning home or stealing cloud credentials.
This guide takes the security-engineer view the CKS rewards. If you want the pure mechanics of ingress and egress selectors first — the CKA-level “how do the fields fit together” walkthrough — read the Kubernetes NetworkPolicy for the CKA exam guide, then come back here for the hardening angle. This article assumes you know the syntax and focuses on what a hardened cluster’s policies look like and why. It sits inside the CKS Cluster Hardening and Minimize Microservice Vulnerabilities domains — the same territory covered in the minimize microservice vulnerabilities guide. For the full exam picture, start with the CKS exam guide for 2026 and sequence your prep with the CKS study plan.
The Default That Fails Open
Here is the single most important sentence for the exam: a pod with no NetworkPolicy selecting it accepts all traffic, from anywhere, on every port. Kubernetes networking fails open. Every pod can reach every other pod, across namespaces, by default. There is no implicit firewall.
That default is the attack surface. If an attacker compromises one pod — an unpatched web front end, a vulnerable dependency — they land in a flat network where they can scan and connect to every database, cache, and internal API in the cluster. Lateral movement is trivial. NetworkPolicy is how you replace “fail open” with “least privilege on the wire.”
A second rule that trips people up: the moment a NetworkPolicy selects a pod for a direction (ingress or egress), that pod switches to deny-by-default for that direction, and only the listed rules are allowed. Policies are purely additive — there is no deny verb, no ordering, no priority. You allow things; everything you didn’t allow is denied once at least one policy applies. Understanding this “select to isolate, then allow back” behaviour is the whole game.
Prerequisite: Your CNI Must Enforce Policy
NetworkPolicy is an API that the API server will happily accept — but it does nothing unless your CNI plugin enforces it. This is a favourite exam trap: you write a perfect default-deny policy, traffic still flows, and you assume the manifest is wrong. It isn’t; the CNI just doesn’t implement policy.
| CNI plugin | Enforces NetworkPolicy? |
|---|---|
| Calico | Yes |
| Cilium | Yes |
| Weave Net | Yes |
| Antrea | Yes |
| Flannel (alone) | No |
| kubenet | No |
On the CKS exam the cluster uses a policy-capable CNI (typically Calico or Cilium), so you can trust that policies take effect. In the real world, confirm enforcement before you rely on it. A quick sanity check is to apply a default-deny and verify a previously-working connection now times out.
Step One: The Default-Deny-All Baseline
The correct hardening pattern is deny everything, then allow what each workload genuinely needs. You establish that baseline per namespace with a policy that selects every pod (podSelector: {}) and lists no allow rules for both directions:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {} # selects every pod in the namespace
policyTypes:
- Ingress
- Egress
# no ingress/egress rules => nothing is allowed
podSelector: {} matches all pods in payments. Naming both Ingress and Egress in policyTypes with no rules means every pod is now isolated in both directions. Apply this first to any namespace you’re hardening, then layer specific allow-policies on top. If you only default-deny ingress, a compromised pod can still open outbound connections — which is exactly how data exfiltration and credential theft happen. For the exam and for real security, deny egress too.
A common softer variant denies ingress but leaves egress open:
spec:
podSelector: {}
policyTypes:
- Ingress
Know the difference cold: the version above isolates inbound only. If a question says “ensure pods cannot make outbound connections except to X,” you must include Egress in policyTypes.
Step Two: Allow Only What the Workload Needs (Ingress)
With default-deny in place, add targeted allow policies. Suppose an api pod should receive traffic only from the web pods in the same namespace, on port 8080:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-api
namespace: payments
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8080
Because a policy already isolated these pods, this one adds back a single allowed source. Anything not matching app: web on TCP/8080 is still denied.
The namespaceSelector vs podSelector trap
The most-tested subtlety in NetworkPolicy is how selectors combine inside a single from entry:
ingress:
- from:
- namespaceSelector:
matchLabels:
team: frontend
podSelector:
matchLabels:
app: web
Two selectors in one list item are ANDed: traffic must come from a pod labelled app: web that lives in a namespace labelled team: frontend. Contrast that with two separate list items:
ingress:
- from:
- namespaceSelector:
matchLabels:
team: frontend
- podSelector:
matchLabels:
app: web
These are ORed: allow any pod from team: frontend namespaces, or any pod named app: web in the policy’s own namespace. That single dash changes the security boundary entirely. On the exam, read the indentation carefully — this is where wrong answers hide.
Remember too that a bare podSelector (without a namespaceSelector) only matches pods in the same namespace as the policy. To allow cross-namespace traffic you must include a namespaceSelector.
Step Three: Egress Control — The CKS Differentiator
Ingress rules protect your services. Egress rules protect the world from your compromised pod. This is where CKS content diverges sharply from CKAD/CKA: reducing the blast radius of a breach.
The problem is that if you default-deny egress, you also break DNS, because pods resolve names through CoreDNS on UDP/TCP 53. A default-deny-egress policy with no DNS allowance makes every pod unable to resolve anything.svc.cluster.local, and applications fail in confusing ways. So the canonical hardened egress baseline allows DNS and nothing else:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: payments
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
namespaceSelector: {} with a podSelector matches the CoreDNS pods in whatever namespace they run (kube-system). Now add exactly the egress destinations a workload legitimately needs — for example, letting api reach a database pod:
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
Everything else outbound — arbitrary internet hosts, other namespaces, internal admin endpoints — stays blocked. That is what stops a breached container from downloading a second-stage payload or exfiltrating data.
Blocking the Cloud Metadata Endpoint
This deserves its own section because it is a classic CKS scenario and a real-world compromise path. On AWS, GCP, and Azure the node’s instance metadata service lives at the link-local address 169.254.169.254. A pod that can reach it may be able to read the node’s IAM/instance credentials and escalate straight out of the cluster into the cloud account.
You block it with an egress policy that allows everything the pod needs except that address, using an ipBlock with an except:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-metadata-egress
namespace: payments
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32
ports:
- protocol: TCP
port: 443
The cidr: 0.0.0.0/0 with except: 169.254.169.254/32 says “you may egress to any IP on 443 except the metadata endpoint.” In a real hardened cluster you’d narrow the allowed CIDR far more tightly and keep the DNS allowance from earlier — but knowing the ipBlock/except pattern for the metadata address is exam-critical. Note that ipBlock matches on IP, so it’s the right tool for infrastructure endpoints that have no pod or namespace labels.
Proving It Works: Test, Don’t Trust
Never submit a policy task without verifying it. The exam gives you a running cluster; use it. The fastest test is an ephemeral pod and a timeout-bounded connection attempt:
# Should SUCCEED: allowed path
kubectl run tester --rm -it --image=busybox:1.36 --labels="app=web" \
-n payments --restart=Never -- \
wget -qO- --timeout=3 http://api:8080
# Should FAIL (time out): blocked path
kubectl run intruder --rm -it --image=busybox:1.36 --labels="app=evil" \
-n payments --restart=Never -- \
wget -qO- --timeout=3 http://api:8080
# Should FAIL: metadata endpoint blocked
kubectl run tester --rm -it --image=busybox:1.36 --labels="app=api" \
-n payments --restart=Never -- \
wget -qO- --timeout=3 http://169.254.169.254/latest/meta-data/
A blocked connection hangs and times out — it does not return “connection refused.” That timeout is your proof the policy denied the traffic. Also inspect what’s actually applied:
kubectl get networkpolicy -n payments
kubectl describe networkpolicy default-deny-all -n payments
describe prints the resolved pod selector and the allowed peers, which is the quickest way to catch a selector that matches the wrong pods.
Common Mistakes That Cost Points
| Mistake | Why it fails | Fix |
|---|---|---|
| Default-deny ingress only | Compromised pod can still exfiltrate | Add Egress to policyTypes |
| Egress deny with no DNS rule | Name resolution breaks; app errors | Allow UDP/TCP 53 to CoreDNS |
podSelector without namespaceSelector for cross-ns | Only matches same namespace | Add namespaceSelector |
Selectors in one from item when you meant OR | AND is far stricter than intended | Split into separate list items |
| Assuming policy works on Flannel | CNI doesn’t enforce it | Use a policy-capable CNI |
Forgetting ipBlock for metadata/IP endpoints | Label selectors can’t match an IP | Use ipBlock with except |
A Realistic Hardening Sequence
Put together, hardening a namespace on the exam looks like this, in order:
- Apply
default-deny-all(both directions) to the namespace. - Add
allow-dns-egressso name resolution keeps working. - Add tight ingress allow-policies per service (who may connect in).
- Add tight egress allow-policies per workload (what it may connect out to).
- Add
deny-metadata-egresswhere pods run with any node-level cloud identity. - Test every allowed and every denied path with ephemeral pods.
This mirrors real zero-trust network design and is exactly the muscle memory the exam rewards. If you also need the cluster-level controls that sit around these policies — API server flags, RBAC, CIS remediation — the CKS cluster setup and hardening guide covers that layer.
Practicing Against Live Clusters
Reading manifests is not the same as writing them under a 15-minute-per-task clock against a cluster you didn’t build. The fastest way to make default-deny, DNS carve-outs, and metadata blocks automatic is to break and repair real clusters repeatedly. Sailor.sh’s Certified Kubernetes Security Specialist (CKS) Mock Exam Bundle gives you full-length, browser-based performance exams against live clusters, with tasks in exactly this style — apply a default-deny baseline, allow a single source, block the metadata endpoint, and prove it — so the workflow is second nature by exam day.
Frequently Asked Questions
Do NetworkPolicies apply to traffic that never leaves the node?
Yes. NetworkPolicy is about pod-to-pod and pod-to-endpoint traffic regardless of whether the peers are on the same node. Enforcement is handled by the CNI’s dataplane, which applies rules for local traffic too.
Why does my app still work after I applied a default-deny policy?
Most often the CNI doesn’t enforce NetworkPolicy (e.g. plain Flannel), or the policy’s podSelector doesn’t actually match your pods. Run kubectl describe networkpolicy to see the resolved selector, and confirm your CNI supports policy enforcement.
How do I allow a pod to reach the internet but not the cloud metadata endpoint?
Use an egress rule with ipBlock: { cidr: 0.0.0.0/0, except: [169.254.169.254/32] }. Keep a separate egress rule allowing DNS to CoreDNS, otherwise name resolution breaks.
Are NetworkPolicies namespaced?
Yes. A NetworkPolicy only affects pods in its own namespace, and a bare podSelector peer matches only same-namespace pods. Cross-namespace allow rules require a namespaceSelector. To harden a whole cluster you apply baseline policies per namespace.
Can I express “deny” explicitly in a NetworkPolicy?
No. There is no deny rule. You isolate pods by selecting them, and everything not explicitly allowed is denied. “Default deny” is achieved by selecting pods with no allow rules for a direction.
Does egress filtering break Services and DNS?
It can. Blocking egress without allowing UDP/TCP port 53 to CoreDNS breaks name resolution, and blocking egress to a Service’s backend pods breaks the connection. Always allow DNS first, then the specific backends each workload needs.
Key Takeaways
- Pods fail open: with no policy, all ingress and egress is allowed.
- Selecting a pod with a policy flips it to deny-by-default for that direction; policies are additive, never subtractive.
- Start every hardened namespace with a default-deny-all covering both directions.
- Egress control is the CKS differentiator — always allow DNS back, then only the destinations a workload needs.
- Block the cloud metadata endpoint (
169.254.169.254) with anipBlock/exceptegress rule to stop credential theft. - Your CNI must enforce policy, and you must test allowed and denied paths before moving on.
Master these six ideas and NetworkPolicy shifts from a point-losing topic to one of the most reliable sections of your CKS score. Pair the theory with repeated hands-on reps, and default-deny becomes your reflex — on the exam and in production.