๐Ÿ“– Read-only preview โ€” lessons, diagrams, and quizzes only. The hands-on labs, live grading, and progress tracking require running the real course locally against a Kubernetes cluster.Run it locally โ†’

Module 11 ยท Lesson 11.2

Pod Security Standards & NetworkPolicies

RBAC controls who can change the cluster's desired state. These two features control something different: what a running container is actually allowed to do, and who it's allowed to talk to.

Pod Security Standards: constraining the container itself

Kubernetes defines three standard security profiles: Privileged (no restrictions โ€” effectively opt-out), Baseline (blocks known privilege-escalation paths, a sane minimum), and Restricted (hardened: no root, no privilege escalation, a locked-down syscall filter). You apply one by labeling a namespace:

pod-security.kubernetes.io/enforce: restricted

No separate controller to install โ€” this is built into API admission. Once set, any pod spec that violates the profile is rejected at kubectl apply time, not silently allowed and flagged later. You'll see this directly in the lab: applying a pod without the right securityContext into a restricted namespace never creates anything โ€” the rejection message lists exactly which constraints it violated (running as root, missing seccomp profile, privilege escalation allowed).

Linux aside: most of what "restricted" enforces maps to real Linux primitives โ€” runAsNonRoot is about the process's Linux UID (don't run as UID 0), seccompProfile restricts which syscalls the process can make at all, and dropping capabilities removes specific root-like powers (like binding privileged ports) without granting full root. Containers don't have their own permission model โ€” they inherit Linux's.

NetworkPolicies: constraining who a pod can talk to

By default, every pod in a cluster can reach every other pod โ€” namespaces don't restrict this (Module 2). A NetworkPolicy changes that, for the pods it selects, using the same label-selector pattern you've used since Module 2: podSelector picks which pods the policy applies to, then ingress/egress rules say what's allowed in or out โ€” selected by pod labels, not IP addresses, so the rule keeps working as pods are replaced.

Default-deny, then allow by label

client-arole: untrusted
client-brole: trusted
โœ“
โœ“
๐Ÿ–ฅ serverapp: server
No NetworkPolicy yet โ€” every pod can reach the server, regardless of label.

An empty podSelector: {} with policyTypes: [Ingress] and no ingress rules is a classic default-deny: it matches every pod in the namespace and allows nothing in. From there you add narrow ingress rules naming exactly which pods (by label) may reach which other pods โ€” the same default-deny-then-allow shape as a security group, just selector-based instead of IP/CIDR-based.

One real caveat worth knowing: NetworkPolicy objects always exist as API objects, but enforcement is the CNI plugin's job, not the API server's. This cluster runs Calico specifically because it enforces NetworkPolicy; some CNIs (including kind's own default) silently accept the objects and do nothing. Always confirm your CNI actually enforces before trusting a policy in production.

The infrastructure analogy

A NetworkPolicy is a security group that follows the workload instead of an IP โ€” you write "allow from anything labeled role: trusted" instead of "allow from 10.0.4.0/24." Pod Security Standards are closest to a hardened AMI/container baseline enforced at admission instead of discovered at runtime.

In the lab you'll trip a Restricted-namespace rejection and fix it, then prove a NetworkPolicy actually blocks (and selectively allows) real traffic โ€” not just that the YAML applied.

๐Ÿงช Lab: lab-23-pod-security-and-networkpolicies

Preview only

Goal

Trip a Pod Security admission rejection and fix it, then write a NetworkPolicy that actually blocks (and selectively allows) real traffic โ€” not just YAML that applies cleanly.

Tasks

Part 1 โ€” Pod Security Standards

  1. Apply the starting manifests: kubectl apply -f manifests/
  2. Notice pod/app is rejected outright โ€” read the error message, it lists every constraint it violates.
  3. Edit manifests/app-pod.yaml: add a pod-level and container-level securityContext that satisfies restricted (non-root user, a seccomp profile, no privilege escalation, all capabilities dropped).
  4. Re-apply: kubectl apply -f manifests/app-pod.yaml โ€” it should now be created and reach Running.

Part 2 โ€” NetworkPolicy

  1. server, client-a, and client-b should already be Running (they were compliant from the start). Confirm client-a can currently reach server โ€” get the server's pod IP (kubectl get pod server -n lab-23-pod-security-and-networkpolicies -o jsonpath='{.status.podIP}') and from client-a: kubectl exec -n lab-23-pod-security-and-networkpolicies client-a -- wget -q -T 3 -O- http://<server-ip>:8080
  2. Write a NetworkPolicy selecting app: server that denies all ingress except from pods labeled role: trusted. Add it as a new file under manifests/ and apply it.
  3. Re-run the step 5 command from client-a โ€” it should now fail/timeout.
  4. client-b is currently labeled role: untrusted too (that's a bug โ€” it's supposed to be the allowed one). Fix its label: kubectl label pod client-b -n lab-23-pod-security-and-networkpolicies role=trusted --overwrite
  5. Run the same wget command from client-b instead โ€” it should succeed.

Check

Run the check once both parts are done.

This lab runs against a real local Kubernetes cluster with an automated grader โ€” clone the repo and run make start to do it for real.

๐Ÿ“ Quiz

1. You label a namespace `pod-security.kubernetes.io/enforce: restricted`. A pod spec that violates it is applied. What happens?

2. Which of these does the 'restricted' Pod Security Standard typically require? (select all that apply)

3. What does `podSelector: {}` with `policyTypes: [Ingress]` and no ingress rules do?

4. You apply a default-deny NetworkPolicy on `kind`'s default kindnet CNI (not Calico) and test that traffic is blocked. What should you expect?scenario

5. A NetworkPolicy allows traffic 'from pods labeled role: trusted' rather than from a specific IP range. Why is that generally better inside a cluster?

Progress isn't saved in this preview โ€” run the course locally to track completion and grade labs for real.