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.

45 min 2 hands-on tasksNetworking · 20% of the exam

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 podSelector chooses 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 (or Egress), 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.
  • policyTypes says which directions the policy isolates. If you omit it, Ingress is always included, and Egress only if the policy has an egress section. 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.
The three baseline policies
# 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

Peer in from / toSelects
podSelector aloneMatching Pods in the policy's own namespace
namespaceSelector aloneEvery Pod in namespaces whose labels match
namespaceSelector + podSelector in one entryMatching Pods in matching namespaces (AND)
ipBlock (cidr, except)IP ranges, meant for traffic to or from outside the cluster; Pod IPs change, so do not use it for Pods
One dash changes the meaning
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: monitor

Every 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.

frontend may reach backend on 8080, nothing else may
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: 8080

Egress 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.

Egress: DNS plus one destination
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: 8080
Prove it: one allowed client, one denied client
kubectl -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

Hands-on task 1 of 2nw-np-1
~7 min6% weightNetworking

Default deny, then allow one client

In namespace 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. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-1/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-1/cleanup | bash
Hands-on task 2 of 2nw-np-2
~7 min6% weightNetworking

Lock down egress except DNS and one Service

In namespace 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. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-2/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-np-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 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.