Skip to main content

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

# 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

# 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

# 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)

# 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

# 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