Active Nerds
Module #9 · advanced Article

NetworkPolicies: Micro-Segmentation and Pod Traffic Filtering with Calico or Cilium

NetworkPolicies are pod-level firewalls that whitelist allowed ingress and egress traffic — start with a default-deny-all policy per namespace, then explicit...

6 min read+20 XPPublished 2026-07-07

Quick Answer: NetworkPolicies are pod-level firewalls that whitelist allowed ingress and egress traffic — start with a default-deny-all policy per namespace, then explicitly allow only the traffic paths you need.

Detailed Answer:

# STEP 1: Default deny all — apply to every production namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}                   # Applies to ALL pods in namespace
  policyTypes:
  - Ingress
  - Egress
  # No ingress or egress rules = deny everything

---
# STEP 2: Allow DNS egress (without this, nothing resolves)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  podSelector: {}                   # All pods need DNS
  policyTypes:
  - Egress
  egress:
  - to: []                         # Any destination
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

---
# STEP 3: Allow specific traffic paths
# Frontend → API only (not directly to DB)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api                      # This policy TARGETS the API pods
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend             # Only allow from frontend pods
    ports:
    - protocol: TCP
      port: 8080

---
# API → Database only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-to-database
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres                 # Targets postgres pods
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api                  # Only allow from API pods
    ports:
    - protocol: TCP
      port: 5432

---
# Allow monitoring namespace to scrape metrics from all pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-prometheus-scrape
  namespace: production
spec:
  podSelector: {}                   # All pods
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring          # From monitoring namespace
    ports:
    - protocol: TCP
      port: 9090                   # Prometheus metrics port

---
# Allow ingress controller to reach pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-controller
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: ingress-nginx
      podSelector:
        matchLabels:
          app.kubernetes.io/name: ingress-nginx
    ports:
    - protocol: TCP
      port: 3000

Testing Network Policies:

# Verify NetworkPolicy is enforced (requires CNI support)
# Deploy test pods
kubectl run test-frontend --image=nicolaka/netshoot \
  --labels="app=frontend" -n production
kubectl run test-attacker --image=nicolaka/netshoot \
  --labels="app=attacker" -n production

# Test allowed path: frontend → api (should succeed)
kubectl exec -it test-frontend -n production -- \
  curl -m 5 http://api-svc:8080/health
# Expected: 200 OK

# Test blocked path: attacker → database (should fail/timeout)
kubectl exec -it test-attacker -n production -- \
  curl -m 5 http://postgres-svc:5432
# Expected: timeout (connection blocked by NetworkPolicy)

# Check which CNI is installed (must support NetworkPolicy)
kubectl get pods -n kube-system | grep -E "calico|cilium|weave"

# View all NetworkPolicies
kubectl get networkpolicies -A
kubectl describe networkpolicy default-deny-all -n production

# Cilium — advanced policy visualization
cilium policy get
hubble observe --namespace production --verdict DROP

Advanced Cilium Policy (eBPF-based — more powerful than basic NetworkPolicy):

# Cilium NetworkPolicy — L7 HTTP-aware policies
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-http-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: frontend
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:                       # L7 HTTP rules — only allow specific paths
        - method: GET
          path: /api/v1/.*
        - method: POST
          path: /api/v1/orders
        # Block: DELETE /api/v1/.* even from frontend

Common Mistakes:

  • Forgetting to allow DNS egress — breaks all service discovery silently
  • Not labeling namespaces — namespaceSelector without labels matches nothing
  • Assuming NetworkPolicy works without a supporting CNI — flannel ignores policies silently
  • Creating ingress policy without egress policy — traffic blocked in one direction still fails

Key Takeaway: Always start with default-deny-all, then explicitly allow — and always add DNS egress first or you'll be confused why nothing resolves after applying your "allow" policies.


Finished reading?

Mark as complete to claim your +20 XP and track progress.