Lesson 12 NetworkPolicies
Theme:

Lab Guide: NetworkPolicies

Three exercises that build from default-deny to a fully templated Helm NetworkPolicy.

Prerequisites

A running Kubernetes cluster with a CNI that enforces NetworkPolicies (Calico, Cilium, or Weave). If using Minikube, start with minikube start --cni=calico. If using kind, install Calico after cluster creation.

Exercise 1 — Default-Deny and Selective Allow

In this exercise you will deploy two applications, lock down the namespace with a default-deny policy, and then create an allow rule to restore specific connectivity.

Step 1 — Create a test namespace and deploy two apps

# Create the namespace
kubectl create namespace netpol-lab

# Deploy a simple API server
kubectl create deployment api \
  --image=hashicorp/http-echo \
  --namespace=netpol-lab \
  -- -text="Hello from API" -listen=:8080
kubectl expose deployment api \
  --port=8080 --namespace=netpol-lab

# Deploy a frontend
kubectl create deployment frontend \
  --image=busybox:1.36 \
  --namespace=netpol-lab \
  -- sleep 3600
kubectl label deployment frontend app=frontend -n netpol-lab --overwrite

Step 2 — Verify connectivity (before any policy)

# Wait for pods to be ready
kubectl wait --for=condition=ready pod -l app=api -n netpol-lab --timeout=60s
kubectl wait --for=condition=ready pod -l app=frontend -n netpol-lab --timeout=60s

# Test: frontend can reach api
kubectl exec -n netpol-lab deploy/frontend -- \
  wget -qO- --timeout=3 http://api:8080
# Expected output: "Hello from API"

Step 3 — Apply a default-deny ingress policy

Create a file called default-deny.yaml:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: netpol-lab
spec:
  podSelector: {}
  policyTypes:
    - Ingress
kubectl apply -f default-deny.yaml

Step 4 — Verify connectivity is now blocked

kubectl exec -n netpol-lab deploy/frontend -- \
  wget -qO- --timeout=3 http://api:8080
# Expected: timeout / connection refused

Step 5 — Create a policy to allow frontend to reach api

Create allow-frontend.yaml:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: netpol-lab
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080
kubectl apply -f allow-frontend.yaml

# Test again -- should work now
kubectl exec -n netpol-lab deploy/frontend -- \
  wget -qO- --timeout=3 http://api:8080
# Expected output: "Hello from API"

Step 6 — Confirm other pods are still blocked

# Run a pod without the frontend label
kubectl run rogue --rm -it \
  --image=busybox:1.36 \
  --restart=Never -n netpol-lab -- \
  wget -qO- --timeout=3 http://api:8080
# Expected: timeout (rogue pod is not labeled app=frontend)

Checkpoint

You have a default-deny ingress policy in place. Only pods labeled app: frontend can reach the API on port 8080. All other ingress is blocked.

Exercise 2 — Egress Lockdown with DNS Allow

Lock down egress traffic and ensure DNS still works.

Step 1 — Apply a default-deny egress policy

Create deny-egress.yaml:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: netpol-lab
spec:
  podSelector: {}
  policyTypes:
    - Egress
kubectl apply -f deny-egress.yaml

# Test: frontend cannot resolve or reach anything
kubectl exec -n netpol-lab deploy/frontend -- \
  wget -qO- --timeout=3 http://api:8080
# Expected: DNS resolution failure or timeout

Step 2 — Allow DNS and specific egress

Create allow-egress.yaml:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-egress
  namespace: netpol-lab
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
    - Egress
  egress:
    # Allow DNS
    - ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    # Allow traffic to the API
    - to:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - protocol: TCP
          port: 8080
kubectl apply -f allow-egress.yaml

# Test: frontend can reach api again
kubectl exec -n netpol-lab deploy/frontend -- \
  wget -qO- --timeout=3 http://api:8080
# Expected: "Hello from API"

# Test: frontend cannot reach external sites
kubectl exec -n netpol-lab deploy/frontend -- \
  wget -qO- --timeout=3 http://example.com
# Expected: timeout

Step 3 — Inspect all policies

kubectl get networkpolicy -n netpol-lab
kubectl describe networkpolicy frontend-egress -n netpol-lab

Checkpoint

The frontend pod can resolve DNS and reach the API, but cannot make any other outbound connections.

Exercise 3 — Helm-Templated NetworkPolicy

Create a Helm chart that includes a conditional, configurable NetworkPolicy.

Step 1 — Scaffold the chart

helm create netpol-app
cd netpol-app

Step 2 — Add a NetworkPolicy template

Create templates/networkpolicy.yaml:

{{- if .Values.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ include "netpol-app.fullname" . }}
  labels:
    {{- include "netpol-app.labels" . | nindent 4 }}
spec:
  podSelector:
    matchLabels:
      {{- include "netpol-app.selectorLabels" . | nindent 6 }}
  policyTypes:
  {{- range .Values.networkPolicy.policyTypes }}
    - {{ . }}
  {{- end }}
  {{- with .Values.networkPolicy.ingress }}
  ingress:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- with .Values.networkPolicy.egress }}
  egress:
    {{- toYaml . | nindent 4 }}
  {{- end }}
{{- end }}

Step 3 — Add default values

Add the following to the end of values.yaml:

networkPolicy:
  enabled: false
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 80
  egress:
    - ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Step 4 — Test with helm template

# Default: networkPolicy.enabled=false, no output expected
helm template myrelease . | grep -A 30 "NetworkPolicy"
# Expected: no output

# Override to enable:
helm template myrelease . \
  --set networkPolicy.enabled=true | grep -A 30 "NetworkPolicy"
# Expected: full NetworkPolicy manifest

Step 5 — Install and verify

# Install with policy enabled
helm install myrelease . \
  --namespace netpol-lab \
  --set networkPolicy.enabled=true

# Check the policy was created
kubectl get networkpolicy -n netpol-lab

# Describe it
kubectl describe networkpolicy myrelease-netpol-app -n netpol-lab

Step 6 — Clean up

helm uninstall myrelease -n netpol-lab
kubectl delete namespace netpol-lab

Checkpoint

You have a Helm chart that conditionally creates a NetworkPolicy based on values. The policy is fully configurable via values.yaml or --set overrides.