Kubernetes Network Policies
Network Policies provide fine-grained control over traffic flow between pods in a Kubernetes cluster. They define how pods can communicate with each other and external services, implementing a zero-trust networking model. This guide covers ingress and egress rules, namespace isolation, and implementations using Calico and Cilium CNI plugins.
Table of Contents
- Network Policy Fundamentals
- Network Policy Syntax
- Ingress Rules
- Egress Rules
- Namespace Isolation
- Calico Implementation
- Cilium Implementation
- Practical Examples
- Conclusion
Network Policy Fundamentals
What are Network Policies?
Network Policies are Kubernetes objects that specify how pods communicate with each other and with network endpoints. They work like firewall rules for pod-to-pod traffic.
Prerequisites
Network Policies require a network plugin that supports them. Default network plugins (like Flannel) don't support policies. You need:
- Calico: Fully-featured network policy support
- Cilium: Advanced policy with eBPF-based implementation
- Kube-router: Lightweight option
- Weave Net: Network policy support available
Policy Evaluation
When a Network Policy is defined, it creates a whitelist of allowed traffic. By default:
- All traffic is allowed (if no policies exist)
- Once a policy targets pods, only traffic matching that policy is allowed
- Multiple policies combine with OR logic (traffic allowed if ANY policy permits it)
Network Policy Syntax
Basic Structure
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: production
spec:
podSelector: {} # Applies to all pods in namespace
policyTypes:
- Ingress
- Egress
ingress: [] # No ingress traffic allowed
egress: [] # No egress traffic allowed
Pod Selection
Target specific pods:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
Policy Types
- Ingress: Controls incoming traffic to pods
- Egress: Controls outgoing traffic from pods
- Both: Restricts both directions
Ingress Rules
Allow All Ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {} # Empty rule allows all traffic
Allow from Specific Pods
Allow traffic from pods in the same namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 8080
Allow from Namespace
Allow traffic from any pod in another namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-monitoring
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 9090
Allow from External Networks
Allow traffic from specific external networks:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-api
namespace: production
spec:
podSelector:
matchLabels:
app: api-gateway
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 203.0.113.0/24 # External network
except:
- 203.0.113.5/32 # Exclude specific IP
ports:
- protocol: TCP
port: 443
Multiple Sources
Allow traffic from multiple sources:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-multiple-sources
namespace: production
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: backend
- podSelector:
matchLabels:
tier: admin
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 5432
- protocol: TCP
port: 9100 # Prometheus exporter
Egress Rules
Allow All Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- {} # Allow all egress
Restrict Egress to Specific Pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-egress
namespace: production
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 8080
Allow DNS and External Access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-external
namespace: production
spec:
podSelector:
matchLabels:
tier: app
policyTypes:
- Egress
egress:
# DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# External HTTPS
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # Block AWS metadata
ports:
- protocol: TCP
port: 443
Namespace Isolation
Deny All Traffic to Namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress: []
Apply to all namespaces:
# Create the policy in each namespace
for ns in production staging development; do
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: $ns
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF
done
Allow Traffic Within Namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # Any pod in same namespace
Allow Ingress from Specific Namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
Calico Implementation
Installing Calico
Install Calico as your CNI:
# Download Calico manifest
curl https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml -o tigera-operator.yaml
# Apply operator
kubectl apply -f tigera-operator.yaml
# Wait for operator to be ready
kubectl wait --for=condition=ready pod -l k8s-app=tigera-operator -n tigera-operator --timeout=300s
Install Calico custom resource:
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
calicoNetwork:
ipPools:
- blockSize: 26
cidr: 10.0.0.0/16
encapsulation: VXLAN
natOutgoing: Enabled
nodeSelector: all()
nodeAddressAutodetectionV4:
interface: eth0
Calico NetworkPolicy Extensions
Calico supports additional NetworkPolicy features:
apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
name: advanced-policy
namespace: production
spec:
selector: tier == "backend"
types:
- Ingress
- Egress
ingress:
- action: Allow
protocol: TCP
destination:
ports:
- 8080
egress:
- action: Allow
protocol: TCP
destination:
nets:
- 10.0.0.0/8
- action: Allow
protocol: UDP
destination:
ports:
- 53
Calico Policy Rules
Calico allows more granular control:
# View Calico workload endpoints
calicoctl get workloadendpoints -n production
# View Calico policies
calicoctl get networkpolicies -n production
# Describe a policy
calicoctl get networkpolicies production.allow-frontend -o yaml
Cilium Implementation
Installing Cilium
Install Cilium with Helm:
helm repo add cilium https://helm.cilium.io
helm repo update
helm install cilium cilium/cilium \
--namespace kube-system \
--set kubeProxyReplacement=disabled \
--set k8sServiceHost=10.0.0.10 \
--set k8sServicePort=6443 \
--set image.tag=v1.14.0
Wait for Cilium to be ready:
kubectl rollout status -n kube-system ds/cilium
Cilium Network Policies
Cilium supports standard Kubernetes NetworkPolicy plus extended features:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
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: {}
ports:
- protocol: UDP
port: 53
Cilium CLI for Debugging
# Install cilium CLI
curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
# Check Cilium status
cilium status
# Inspect connectivity between pods
cilium connectivity test
# View policy enforcement
cilium policy get
Practical Examples
Example: Three-Tier Application
Frontend, backend, and database isolation:
---
# Deny all by default
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
---
# Allow external traffic to frontend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-ingress
namespace: production
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
---
# Allow frontend to backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend
namespace: production
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 8080
---
# Allow backend to database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-database
namespace: production
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 5432
---
# Allow DNS for all pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
Example: Multi-Namespace Communication
Allow specific namespaces to communicate:
---
# Label namespaces
# kubectl label namespace production environment=prod
# kubectl label namespace monitoring environment=monitoring
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
environment: monitoring
ports:
- protocol: TCP
port: 9090
Example: Microservices Communication
Complex service mesh with specific rules:
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: order-service-policy
namespace: microservices
spec:
podSelector:
matchLabels:
service: order
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
service: api-gateway
ports:
- protocol: TCP
port: 8080
egress:
# To payment service
- to:
- podSelector:
matchLabels:
service: payment
ports:
- protocol: TCP
port: 8080
# To inventory service
- to:
- podSelector:
matchLabels:
service: inventory
ports:
- protocol: TCP
port: 8080
# To databases
- to:
- podSelector:
matchLabels:
service: postgres
ports:
- protocol: TCP
port: 5432
# DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
Conclusion
Network Policies are essential for implementing zero-trust networking in Kubernetes. By carefully planning your communication requirements and implementing deny-all-by-default policies with explicit allow rules, you create a secure network architecture. Whether using Calico for comprehensive network policy support or Cilium for advanced eBPF-based enforcement, proper network segmentation protects your applications on VPS and baremetal infrastructure. Start with liberal policies and gradually restrict traffic as you understand your application requirements, then maintain clear documentation of each policy's purpose and scope.


