Learning Kubernetes - Episode 36 - Introduction and Explanation of Network Policy

Learning Kubernetes - Episode 36 - Introduction and Explanation of Network Policy

In this episode, we'll discuss Kubernetes NetworkPolicy for fine-grained network traffic control. We'll learn how to implement network segmentation, zero-trust networking, and best practices for securing Pod communication.

Arman Dwi Pangestu
Arman Dwi PangestuApril 11, 2026
0 views
7 min read

Introduction

Note

If you want to read the previous episode, you can click the Episode 35 thumbnail below

Episode 35Episode 35

In the previous episode, we explored Affinity and Anti-Affinity, which control Pod placement across nodes. Now we'll dive into NetworkPolicy, which controls network traffic between Pods and external networks.

Note: Here I'll be using a Kubernetes Cluster installed through K3s.

By default, Kubernetes allows all traffic between Pods (flat network model). NetworkPolicy lets you implement network segmentation and zero-trust networking. Think of NetworkPolicy like firewall rules for your cluster - without it, all Pods can talk to each other. With it, you can restrict communication to only what's necessary.

Understanding NetworkPolicy

NetworkPolicy is a Kubernetes resource that defines how Pods communicate with each other and with external networks. It operates at Layer 3 (IP) and Layer 4 (TCP/UDP) of the OSI model. It doesn't inspect application-level protocols or content.

Default Network Behavior

By default, Kubernetes has no network restrictions:

  • All Pods can communicate with all other Pods
  • All Pods can communicate with external networks
  • All external traffic can reach Pods

This is convenient for development but dangerous for production. NetworkPolicy changes this behavior.

NetworkPolicy Basics

A NetworkPolicy consists of:

  1. Pod Selector - Which Pods the policy applies to
  2. Policy Types - Ingress, Egress, or both
  3. Rules - What traffic is allowed
KubernetesBasic NetworkPolicy Structure
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-web
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: client
    ports:
    - protocol: TCP
      port: 80

Ingress Policies

Ingress policies control incoming traffic to Pods.

Allow All Ingress

KubernetesAllow All Ingress Traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - {}

An empty ingress rule allows all traffic.

Allow from Specific Pods

KubernetesAllow from Specific Pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-frontend
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

This allows traffic to app=backend Pods only from app=frontend Pods on port 8080.

Allow from Specific Namespace

KubernetesAllow from Specific Namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 9090

This allows traffic from any Pod in the monitoring namespace.

Allow from Specific IP Block

KubernetesAllow from IP Block
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-external
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 203.0.113.0/24
        except:
        - 203.0.113.5/32
    ports:
    - protocol: TCP
      port: 443

This allows traffic from the CIDR block 203.0.113.0/24 except 203.0.113.5.

Multiple Ingress Rules

KubernetesMultiple Ingress Rules
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-ingress
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 5432
  - from:
    - podSelector:
        matchLabels:
          app: migration
    ports:
    - protocol: TCP
      port: 5432
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 9090

Multiple rules are combined with OR logic - traffic matching any rule is allowed.

Egress Policies

Egress policies control outgoing traffic from Pods.

Allow All Egress

KubernetesAllow All Egress Traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - {}

Allow to Specific Pods

KubernetesAllow Egress to Specific Pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-to-database
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432

This allows app=backend Pods to send traffic only to app=database Pods on port 5432.

Allow DNS Egress

KubernetesAllow DNS Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - protocol: UDP
      port: 53

This allows DNS queries to the kube-system namespace.

Allow External Egress

KubernetesAllow External Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-external
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32  # Block AWS metadata service
    ports:
    - protocol: TCP
      port: 443

This allows external HTTPS traffic but blocks the AWS metadata service.

Combining Ingress and Egress

KubernetesCombined Ingress and Egress Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - protocol: UDP
      port: 53

Deny All Policies

A common pattern is to deny all traffic by default, then allow specific traffic.

Deny All Ingress

KubernetesDeny All Ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

An empty ingress list denies all traffic.

Deny All Egress

KubernetesDeny All Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

Deny All (Both Ingress and Egress)

KubernetesDeny All Traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Practical Examples

Three-Tier Application

KubernetesThree-Tier Application Network Policy
# Frontend can receive traffic from external
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-ingress
spec:
  podSelector:
    matchLabels:
      tier: frontend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - protocol: TCP
      port: 80
    - protocol: TCP
      port: 443
---
# Frontend can only talk to backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-to-backend
spec:
  podSelector:
    matchLabels:
      tier: frontend
  policyTypes:
  - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          tier: backend
    ports:
    - protocol: TCP
      port: 8080
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - protocol: UDP
      port: 53
---
# Backend can receive from frontend and talk to database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
spec:
  podSelector:
    matchLabels:
      tier: backend
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          tier: frontend
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - podSelector:
        matchLabels:
          tier: database
    ports:
    - protocol: TCP
      port: 5432
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - protocol: UDP
      port: 53
---
# Database can only receive from backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-policy
spec:
  podSelector:
    matchLabels:
      tier: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          tier: backend
    ports:
    - protocol: TCP
      port: 5432

Microservices with Monitoring

KubernetesMicroservices with Monitoring Access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-monitoring
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 9090

This allows Prometheus in the monitoring namespace to scrape metrics from all Pods.

Namespace Isolation

KubernetesNamespace Isolation
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-cross-namespace
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}
      namespaceSelector:
        matchLabels:
          name: production

This allows traffic only within the production namespace.

NetworkPolicy Selectors

Pod Selector

Selects Pods by label:

Kubernetesyaml
podSelector:
  matchLabels:
    app: web

Namespace Selector

Selects Pods in specific namespaces:

Kubernetesyaml
namespaceSelector:
  matchLabels:
    name: production

IP Block

Selects by IP CIDR:

Kubernetesyaml
ipBlock:
  cidr: 10.0.0.0/8
  except:
  - 10.1.0.0/16

Combined Selectors

Kubernetesyaml
from:
- podSelector:
    matchLabels:
      app: web
  namespaceSelector:
    matchLabels:
      name: production

This means: Pods with app=web label in the production namespace.

NetworkPolicy Limitations

1. No Layer 7 (Application) Filtering

NetworkPolicy works at Layer 3/4. It can't filter based on HTTP paths or methods.

2. No Egress to Pods in Other Clusters

NetworkPolicy only works within a cluster.

3. Requires Network Plugin Support

Not all network plugins support NetworkPolicy. Flannel doesn't, but Calico, Weave, and Cilium do.

4. No Logging Built-in

NetworkPolicy doesn't log denied traffic by default.

5. Performance Overhead

Complex policies can impact network performance.

Common Mistakes and Pitfalls

Mistake 1: Forgetting DNS Egress

Problem: Pods can't resolve DNS.

KubernetesMistake: No DNS Egress
# DON'T DO THIS - Pods can't resolve DNS
egress:
- to:
  - podSelector:
      matchLabels:
        app: database
  ports:
  - protocol: TCP
    port: 5432

Solution: Always allow DNS:

KubernetesCorrect: Include DNS
egress:
- to:
  - podSelector:
      matchLabels:
        app: database
  ports:
  - protocol: TCP
    port: 5432
- to:
  - namespaceSelector:
      matchLabels:
        name: kube-system
  ports:
  - protocol: UDP
    port: 53

Mistake 2: Blocking Kubernetes API Access

Problem: Pods can't access Kubernetes API.

KubernetesMistake: Blocking API Server
# DON'T DO THIS - Pods can't access Kubernetes API
egress:
- to:
  - podSelector:
      matchLabels:
        app: database

Solution: Allow API server access.

Mistake 3: Overly Restrictive Policies

Problem: Breaks legitimate traffic.

KubernetesMistake: Too Restrictive
# DON'T DO THIS - Breaks legitimate traffic
ingress:
- from:
  - podSelector:
      matchLabels:
        app: specific-app

Solution: Test policies before deploying to production.

Mistake 4: Not Considering Kube-DNS

Problem: DNS won't work.

KubernetesMistake: Blocking Kube-DNS
# DON'T DO THIS - DNS won't work
egress:
- to:
  - ipBlock:
      cidr: 10.0.0.0/8

Solution: Explicitly allow kube-dns:

KubernetesCorrect: Allow Kube-DNS
egress:
- to:
  - namespaceSelector:
      matchLabels:
        name: kube-system
  ports:
  - protocol: UDP
    port: 53

Mistake 5: Forgetting to Label Namespaces

Problem: Namespace selectors won't work.

KubernetesMistake: No Namespace Labels
# This won't work if namespace isn't labeled
namespaceSelector:
  matchLabels:
    name: production

Solution: Label namespaces:

Kubernetesbash
kubectl label namespace production name=production

Best Practices

Start with Deny All, Then Allow

KubernetesDeny All Pattern
# First, deny all
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
 
# Then, allow specific traffic
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-web
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - protocol: TCP
      port: 80

Use Namespace Isolation

KubernetesNamespace Isolation
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: namespace-isolation
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}
      namespaceSelector:
        matchLabels:
          name: production

Label Everything

Label Pods, namespaces, and nodes consistently:

Kubernetesbash
kubectl label pod web-1 app=web tier=frontend
kubectl label namespace production name=production

Document Policies

Add comments explaining policy intent:

KubernetesDocumented Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
  annotations:
    description: "Allow frontend to backend communication on port 8080"
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

Test Policies Thoroughly

Test policies in staging before production:

KubernetesTest NetworkPolicy
# Apply policy
kubectl apply -f network-policy.yaml
 
# Test connectivity
kubectl exec -it pod-1 -- curl http://pod-2:8080
 
# Check policy
kubectl get networkpolicy
kubectl describe networkpolicy policy-name

Use Network Policy Editors

Tools like Cilium and Calico provide UI for policy management.

Monitor Policy Violations

Use network monitoring tools to track denied traffic:

KubernetesMonitor with Calico
# Calico provides policy logs
kubectl logs -n calico-system -l k8s-app=calico-node

Checking NetworkPolicy

List Policies

Kubernetesbash
kubectl get networkpolicy
kubectl get networkpolicy -n production

Describe Policy

Kubernetesbash
kubectl describe networkpolicy allow-web

Get Policy YAML

Kubernetesbash
kubectl get networkpolicy allow-web -o yaml

Test Connectivity

Kubernetesbash
# Test if Pod can reach another Pod
kubectl exec -it pod-1 -- curl http://pod-2:8080
 
# Test DNS
kubectl exec -it pod-1 -- nslookup kubernetes.default

NetworkPolicy and Network Plugins

Not all network plugins support NetworkPolicy:

PluginNetworkPolicy Support
FlannelNo
CalicoYes
WeaveYes
CiliumYes
AWS VPC CNILimited
Azure CNIYes

Check your network plugin documentation for support.

Conclusion

In episode 36, we've explored NetworkPolicy in Kubernetes in depth. We've learned how to implement network segmentation and zero-trust networking by controlling traffic between Pods and external networks.

Key takeaways:

  • NetworkPolicy controls Layer 3/4 traffic
  • Ingress policies control incoming traffic
  • Egress policies control outgoing traffic
  • Always allow DNS and API server access
  • Start with deny-all, then allow specific traffic
  • Label Pods and namespaces consistently
  • Test policies thoroughly before production
  • Use network plugins that support NetworkPolicy
  • Monitor policy violations for security
  • Document policies for clarity
  • Combine with RBAC for complete security

NetworkPolicy is essential for securing Kubernetes clusters. By starting with deny-all policies and explicitly allowing necessary traffic, you can build secure, resilient clusters.

Note

If you want to continue to the next episode, you can click the Episode 37 thumbnail below

Episode 37Episode 37

Related Posts