Isolate Namespaces with Deny-All and Allow-From NetworkPolicies
First, create a NetworkPolicy named deny-policy in the prod namespace to block all ingress traffic. The prod namespace is labeled env:prod.
Live lab access
This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.
Create an account to continue →
On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
Context
You must implement NetworkPolicies controlling the traffic flow of existing Deployments across namespaces.
Task
First, create a NetworkPolicy named deny-policy in the prod namespace to block all ingress traffic. The prod namespace is labeled env:prod.
Next, create a NetworkPolicy named allow-from-prod in the data namespace to allow ingress traffic only from Pods in the prod namespace. Use the label of the prod namespace to allow traffic. The data namespace is labeled env:data.
> Do not modify or delete any namespaces or Pods. Only create the required NetworkPolicies.
Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. Its network plugin must enforce NetworkPolicy (for example Calico or Cilium); without one, policies are accepted but change nothing.
Isolate a namespace, then let one other namespace back in
Namespace isolation is built from two NetworkPolicy ideas. A default-deny policy selects every Pod in a namespace and allows nothing, which switches those Pods to "isolated for ingress". An allow policy then adds sources back, and because policies are additive, the result is the union of everything allowed. The CKS twist is selecting the source by namespace, and knowing that one YAML indentation level decides whether two selectors mean AND or OR.
prepium-batch (role=exporter) ──┐
prepium-batch (role=other) ──┼── TCP/80 ──→ prepium-tutorial: api (app=api)
prepium-guest (role=exporter) ──┘ default-deny + allow rules
1. Build the namespaces and prove open access
for ns in prepium-tutorial prepium-batch prepium-guest; do kubectl create namespace $ns; done
kubectl -n prepium-tutorial run api --image=nginx:1.27-alpine --labels=app=api
kubectl -n prepium-batch run exporter --image=busybox:1.36 --labels=role=exporter -- sleep 3600
kubectl -n prepium-batch run other --image=busybox:1.36 --labels=role=other -- sleep 3600
kubectl -n prepium-guest run exporter --image=busybox:1.36 --labels=role=exporter -- sleep 3600
API=$(kubectl -n prepium-tutorial get pod api -o jsonpath='{.status.podIP}')
probe() { kubectl -n "$1" exec "$2" -- wget -T 3 -qO /dev/null "http://$API" && echo "$1/$2 ok" || echo "$1/$2 blocked"; }
probe prepium-batch exporter; probe prepium-batch other; probe prepium-guest exporter
All three should report ok. With no policy selecting a Pod, it accepts everything, so this baseline is what makes later "blocked" results meaningful.
2. Default-deny ingress for the whole namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: lockdown
namespace: prepium-tutorial
spec:
podSelector: {}
policyTypes:
- Ingress
An empty podSelector selects every Pod in the policy's namespace, including Pods created later. Listing Ingress with no ingress rules allows no sources. Egress is untouched. Apply it and run the three probes again: all are now blocked.
3. Allow a namespace by its name label
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: from-batch
namespace: prepium-tutorial
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: prepium-batch
NetworkPolicy has no field for a namespace name, but the control plane puts an immutable kubernetes.io/metadata.name label on every namespace, so selecting on it is safe. If a namespace carries a team label such as tier=batch, that works too; check with kubectl get ns --show-labels first. After applying, both prepium-batch probes succeed and the guest stays blocked.
4. AND versus OR
Now narrow access to exporters inside prepium-batch. The two selectors must sit in the same list entry:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: prepium-batch
podSelector: # no dash: same entry, so AND
matchLabels:
role: exporter
Put a dash before podSelector and it becomes a second entry. Entries are ORed, and a podSelector on its own matches Pods in the policy's own namespace, so you would allow every Pod in prepium-batch plus any role=exporter Pod in prepium-tutorial. Run kubectl describe networkpolicy from-batch: it prints how the API understood the rule, which is the fastest way to catch this.
Common mistakes
- Testing only the allowed path. Without a blocked probe you have not shown the deny works.
- Expecting a default-deny in one namespace to block traffic leaving it. It only isolates ingress to its own Pods.
- Selecting a namespace label that does not exist. The policy is valid, matches nothing, and the allowed client stays blocked.
- Adding a deny-all egress policy while testing. It also blocks DNS, so name-based tests fail for an unrelated reason.
| Example check | Evidence |
|---|---|
| Namespace isolated | Policy with podSelector: {} and policyTypes: [Ingress] |
| Source namespace allowed | namespaceSelector matches a label the source namespace really has |
| AND, not OR | describe networkpolicy shows one peer combining both selectors |
| Behaviour proven | Allowed probe succeeds, other probes time out |
Cleanup: kubectl delete namespace prepium-tutorial prepium-batch prepium-guest. References: Network Policies, kubernetes.io/metadata.name. Next, try a Pod-to-Pod allow rule or label troubleshooting.
What this tests
Restrict RBAC and service accounts, tighten API server flags, and keep the cluster patched. On the CKS exam, Cluster Hardening tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.
Practice it for real
The public description lets you study the task before signing up. Full access includes this live lab; the selected Task of the Week is free with an account. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.