Common Misconfiguration
By default, Kubernetes allows all pods to communicate with each other without restrictions. Missing NetworkPolicies create a flat network where compromised pods can access any other pod or service in the cluster.Vulnerable Example
Copy
# Vulnerable: No NetworkPolicies defined
apiVersion: apps/v1
kind: Deployment
metadata:
name: database
namespace: production
spec:
replicas: 1
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: app
image: webapp:latest
ports:
- containerPort: 8080
# Problem: Any pod in any namespace can connect to the database
# No network segmentation or access control
Secure Example
Copy
# Secure: Zero-trust network with explicit policies
# 1. Default deny-all policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# 2. Database NetworkPolicy - only allow specific apps
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress
ingress:
- from:
# Only allow from web-app pods in this namespace
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 5432
egress:
# Allow DNS resolution
- to:
- namespaceSelector:
matchLabels:
name: kube-system # Use correct label for kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow responses back to web-app (optional, as egress is stateful)
- to:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
---
# 3. Web application NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-app-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
- Egress
ingress:
# Allow from ingress controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx # Use the namespace's label
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 8080
# Allow from monitoring
- from:
- namespaceSelector:
matchLabels:
name: monitoring # Use the namespace's label
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090 # Port for scraping
egress:
# Allow to database
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
# Allow to cache
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow external HTTPS for APIs
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # Block metadata service
- 10.0.0.0/8
- 192.168.0.0/16
- 172.16.0.0/12
ports:
- protocol: TCP
port: 443
---
# 4. Redis cache NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
- Egress
ingress:
- from:
# Only allow from web-app pods in this namespace
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 6379
egress:
# Only allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
Advanced Network Segmentation
Copy
# Namespace labeling for NetworkPolicies
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
name: production
environment: prod
compliance: pci
---
apiVersion: v1
kind: Namespace
metadata:
name: development
labels:
name: development
environment: dev
---
# Cross-namespace communication policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-dev-to-prod-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
tier: public
policyTypes:
- Ingress
ingress:
- from:
# Allow from development namespace
- namespaceSelector:
matchLabels:
environment: dev
# But only from specific pods
- podSelector:
matchLabels:
app: integration-tests
ports:
- protocol: TCP
port: 8080
---
# Egress to specific external services
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-apis
namespace: production
spec:
podSelector:
matchLabels:
requires: external-apis
policyTypes:
- Egress
egress:
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# Allow specific external IP ranges
- to:
- ipBlock:
cidr: 52.94.0.0/20 # AWS S3 IP range (example)
ports:
- protocol: TCP
port: 443
- to:
- ipBlock:
cidr: 104.16.0.0/12 # Cloudflare IP range (example)
ports:
- protocol: TCP
port: 443
Cilium NetworkPolicy Example (Advanced)
Copy
# Using Cilium for L7 network policies
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: web-app-l7-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: web
ingress:
- fromEndpoints:
- matchLabels:
app: ingress-nginx
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/public/.*"
- method: POST
path: "/api/auth/.*"
headers:
- 'Content-Type: application/json'
egress:
- toEndpoints:
- matchLabels:
app: database
toPorts:
- ports:
- port: "5432"
protocol: TCP
- toFQDNs:
- matchPattern: "*.amazonaws.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
- toServices:
- k8sService:
serviceName: kubernetes
namespace: default
Testing NetworkPolicies
Copy
# Test pod for network connectivity verification
apiVersion: v1
kind: Pod
metadata:
name: netpol-test
namespace: production
labels:
app: test
spec:
containers:
- name: test
image: nicolaka/netshoot:latest
command: ["/bin/bash"]
args: ["-c", "while true; do sleep 3600; done"]
---
# Test commands to run inside the pod:
kubectl exec -it netpol-test -n production -- bash
# Test DNS:
nslookup kubernetes.default
# Test database connection (should fail due to policies):
nc -zv database-service 5432
# Test web app (should fail):
curl http://web-service:8080

