NetworkPolicies
How NetworkPolicies isolate Pods, how selectors, peers and ports combine, why egress policies break DNS, and how to prove a policy works instead of hoping.
What you will be able to do
- Write default-deny policies for ingress and egress and open exactly the flows a question asks for.
- Tell an AND peer from an OR peer at a glance, and select Pods in other namespaces by namespace label.
- Test a policy from both an allowed and a denied Pod, with timeouts, in under a minute.
Define and enforce Network Policies is one of the most reliable task types on the exam, and one of the easiest to get almost right. The API accepts any well-formed policy. Whether it does what the question meant depends on one dash in the YAML, on remembering DNS, and on whether you tested it. Policies only work if the CNI enforces them. Calico in this lab does; Flannel on its own silently ignores them.
The rules of isolation
- A NetworkPolicy is namespaced. Its
podSelectorchooses the Pods in its own namespace it applies to;podSelector: {}means every Pod in the namespace. - A Pod that no policy selects for a direction is non-isolated in that direction: everything is allowed. The moment one policy selects it for
Ingress(orEgress), only traffic that some policy explicitly allows gets through. - Policies are additive. There are no deny rules; the allowed set is the union of every policy that selects the Pod. Adding a policy can only open more traffic for a Pod that is already isolated.
policyTypessays which directions the policy isolates. If you omit it,Ingressis always included, andEgressonly if the policy has anegresssection. Set it explicitly.- Replies are allowed automatically: the policy is about who may open a connection. For Pod A to reach Pod B, A's egress and B's ingress must both allow it.
# Deny all incoming traffic to every Pod in the namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: nw-netpol
spec:
podSelector: {}
policyTypes: ["Ingress"]
---
# Deny all traffic in both directions (remember DNS, see below)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: nw-netpol
spec:
podSelector: {}
policyTypes: ["Ingress", "Egress"]
---
# Allow all incoming traffic (overrides an isolation for the Pods it selects)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
namespace: nw-netpol
spec:
podSelector: {}
policyTypes: ["Ingress"]
ingress:
- {}Peers: who is allowed
ingress:
- from:
- namespaceSelector: # ONE peer: Pods labeled role=monitor
matchLabels: # AND in namespace "observability"
kubernetes.io/metadata.name: observability
podSelector:
matchLabels:
role: monitor
---
ingress:
- from:
- namespaceSelector: # TWO peers: every Pod in "observability"
matchLabels:
kubernetes.io/metadata.name: observability
- podSelector: # OR Pods labeled role=monitor in this namespace
matchLabels:
role: monitorEvery namespace carries the immutable label kubernetes.io/metadata.name=<name>, set by the API server. Use it to select a namespace by name without labeling anything. If the question names a custom label (team=payments), check that the namespace has it with kubectl get ns --show-labels, and add it with kubectl label ns if the question says to.
Ports
ports sits beside from/to in the same rule. It matches the Pod's port, after kube-proxy has translated the Service address. A Service on port 80 with targetPort: 8080 needs a policy that allows 8080. protocol defaults to TCP; port can be a named container port; endPort opens a range. A rule with from but no ports allows every port, and a rule with ports but no from allows every source on those ports.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: nw-netpol
spec:
podSelector:
matchLabels:
app: backend # the policy protects the backend Pods
policyTypes: ["Ingress"]
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080Egress and the DNS exception
Once a Pod is isolated for egress, it cannot reach CoreDNS either, and every connection by name fails with bad address before it starts. Almost every egress policy therefore needs a rule that allows port 53 over UDP and TCP to the DNS Pods. Allowing it to any destination is simpler and also accepted, unless the question says otherwise.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: client-egress
namespace: nw-egress
spec:
podSelector:
matchLabels:
role: client
policyTypes: ["Egress"]
egress:
- to: # DNS, to the CoreDNS Pods only
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to: # the api Pods, on their container port
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 8080kubectl -n nw-netpol get netpol
kubectl -n nw-netpol describe netpol allow-frontend # reads the selectors back in plain English
# -T 3 makes a blocked connection fail in 3 seconds instead of hanging
kubectl -n nw-netpol exec frontend -- wget -qO- -T 3 http://backend:8080/hostname # expect a name
kubectl -n nw-netpol exec intruder -- wget -qO- -T 3 http://backend:8080/hostname # expect "download timed out"
# Blocked looks like a timeout; "connection refused" means the policy allowed it and nothing listens
kubectl -n nw-egress exec client -- nslookup api.nw-egress.svc.cluster.local # DNS still works?In the official docs· open on exam day too
- Network Policiessearch: network policies
- Declare Network Policysearch: declare network policy
- Well-Known Labels: kubernetes.io/metadata.namesearch: kubernetes.io/metadata.name
Default deny, then allow one client
nw-netpol, create a NetworkPolicy named default-deny-ingress that blocks all incoming traffic to every Pod in the namespace. Then create a NetworkPolicy named allow-frontend that lets Pods labeled app=frontend connect to Pods labeled app=backend on TCP port 8080, and nothing else. Afterwards frontend must reach http://backend:8080/hostname and intruder must not.Cluster:kubectl config use-context kind-cka
- 1
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-1/setup | bash - 2
Solve it in your terminal
Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under
/tmp/cka/. - 3
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-1/verify | bashGot FAIL? Read which check failed, fix it, run it again. - 4
Clean up (optional)
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-1/cleanup | bash
Lock down egress except DNS and one Service
nw-egress, create a NetworkPolicy named client-egress for Pods labeled role=client. Those Pods may make DNS lookups (UDP and TCP port 53) and may connect to the Pods behind Service api. All other outgoing traffic from them must be blocked. Afterwards Pod client must get an answer from http://api/hostname and must not reach http://other/hostname.Cluster:kubectl config use-context kind-cka
- 1
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-2/setup | bash - 2
Solve it in your terminal
Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under
/tmp/cka/. - 3
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-2/verify | bashGot FAIL? Read which check failed, fix it, run it again. - 4
Clean up (optional)
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-2/cleanup | bash
Finished this lesson?
0 of 2 tasks passed. Your readiness counts tasks, so run them before you move on.