Kubernetes Network Policies Controlling Pod Communication

By default, every Pod in a Kubernetes cluster can communicate with every other Pod — regardless of namespace. This open communication model is convenient for learning but dangerous in production. Network Policies let you define exactly which Pods can talk to which, creating a security layer inside your cluster.

The Default Open Network Problem

Imagine an office building where every door is unlocked and every employee can walk into any room. The finance team's files sit in a room anyone can enter. Network Policies are the locks you put on specific doors — only authorized people (Pods) get in.

Without Network Policy:
Pod A (frontend) → Pod B (payment-service) ✓  (unintended!)
Pod A (frontend) → Pod C (backend-api)    ✓  (intended)
Pod A (frontend) → Pod D (database)       ✓  (dangerous!)

With Network Policy:
Pod A (frontend) → Pod C (backend-api)    ✓  (allowed)
Pod A (frontend) → Pod B (payment-service) ✗  (blocked)
Pod A (frontend) → Pod D (database)       ✗  (blocked)

How Network Policies Work

A Network Policy is a Kubernetes resource that targets a set of Pods (using label selectors) and defines which ingress (incoming) and egress (outgoing) traffic is allowed. Any traffic not explicitly allowed is denied once a policy targets a Pod.

Network Policies require a compatible CNI (Container Network Interface) plugin. Common CNI plugins that support Network Policies include Calico, Cilium, and Weave Net. The default Minikube setup does not enforce Network Policies — install Calico or use a cloud provider's Kubernetes service for testing.

Writing Your First Network Policy

Allow only the frontend Pods to reach the backend-api Pods on port 8080:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend-api        # This policy targets backend-api Pods
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend       # Only pods labeled app=frontend can connect
    ports:
    - protocol: TCP
      port: 8080

Once applied, the backend-api Pods accept connections only from Pods labeled app=frontend on port 8080. All other incoming connections are dropped.

Deny All Traffic First, Then Allow Specifically

The recommended security pattern is to deny all traffic to a namespace by default, then add specific allow rules:

# Step 1: Deny all ingress and egress in the namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: production
spec:
  podSelector: {}       # Empty selector = targets ALL pods in namespace
  policyTypes:
  - Ingress
  - Egress
# Step 2: Allow frontend pods to reach backend-api
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend-api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - port: 8080

Egress Policies: Controlling Outbound Traffic

Egress policies control traffic going out of a Pod. For example, restrict the database Pods so they can only respond to the backend-api and cannot make any outbound connections themselves:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-egress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: backend-api
    ports:
    - port: 5432

Cross-Namespace Rules

You can allow traffic from Pods in a different namespace using namespaceSelector:

ingress:
- from:
  - namespaceSelector:
      matchLabels:
        environment: staging
    podSelector:
      matchLabels:
        app: test-runner

This allows Pods labeled app: test-runner in any namespace labeled environment: staging to connect. Label your namespaces:

kubectl label namespace staging environment=staging

Allowing DNS Traffic

If you use a deny-all egress policy, your Pods cannot resolve DNS names (since DNS runs in the kube-system namespace). Always add a DNS egress exception:

egress:
- to:
  - namespaceSelector:
      matchLabels:
        kubernetes.io/metadata.name: kube-system
  ports:
  - port: 53
    protocol: UDP
  - port: 53
    protocol: TCP

Network Policy Visualization

Namespace: production

[frontend Pods]
    ↓ allowed (port 8080)
[backend-api Pods]
    ↓ allowed (port 5432)
[database Pods]

frontend → database: ✗ blocked
frontend → payment: ✗ blocked
backend-api → external internet: ✗ blocked (egress deny-all)

Inspecting Network Policies

kubectl get networkpolicies -n production
kubectl describe networkpolicy allow-frontend-to-backend -n production

Key Points

  • By default, all Pods in a cluster can communicate freely — Network Policies add traffic controls.
  • A Network Policy targets Pods using label selectors and defines which ingress/egress traffic is allowed.
  • Start with a deny-all policy, then add specific allow rules for required communication paths.
  • Always allow egress to port 53 (DNS) when using deny-all egress policies.
  • Network Policies require a CNI plugin like Calico or Cilium — the default Minikube setup does not enforce them.

Leave a Comment