Kubernetes Network Policies: Pod-to-Pod Security Guide
Lock down pod communication with Kubernetes NetworkPolicies. Learn ingress and egress rules, label selectors, and namespace isolation.
What you'll learn
- ✓How NetworkPolicies control traffic between pods
- ✓Writing ingress and egress rules with label and namespace selectors
- ✓Default deny patterns and namespace isolation strategies
Prerequisites
- •Basic Kubernetes knowledge — see Pods and Services
- •Understanding of Kubernetes labels and namespaces
By default, every pod in a Kubernetes cluster can talk to every other pod. There are no firewalls, no access controls, nothing stopping a compromised frontend pod from connecting directly to your database. NetworkPolicies fix this by letting you define exactly which pods can communicate with which other pods.
How NetworkPolicies Work
A NetworkPolicy is a Kubernetes resource that selects pods using labels and defines allowed ingress (incoming) and egress (outgoing) traffic rules. Once you apply any NetworkPolicy that selects a pod, all traffic not explicitly allowed by a policy is denied for that pod.
Important: NetworkPolicies require a CNI plugin that supports them. Calico, Cilium, and Weave Net support NetworkPolicies. The default kubenet and some versions of Flannel do not. If your CNI does not support them, the policies are accepted by the API server but have no effect.
Default Deny All Traffic
The first step in securing a namespace is to deny all traffic by default, then allow specific flows:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
The empty podSelector: {} matches all pods in the namespace. With no ingress or egress rules defined, all traffic is blocked. This includes DNS resolution, so pods cannot resolve service names.
Allow DNS so services can still resolve names:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Ingress Rules: Controlling Incoming Traffic
Allow the API to receive traffic only from the frontend:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 3000
This policy says: pods labeled app: api accept TCP traffic on port 3000 only from pods labeled app: frontend in the same namespace.
Egress Rules: Controlling Outgoing Traffic
Restrict the API to only connect to the database and Redis:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-egress
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
# Allow connections to the database
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# Allow connections to Redis
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
Cross-Namespace Policies
To allow traffic from pods in a different namespace, combine namespaceSelector with podSelector:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow-api-from-staging
namespace: production
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
environment: staging
podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 5432
Note the indentation. When namespaceSelector and podSelector are at the same level under a single from entry, they act as AND — the pod must match both selectors. If they are separate entries in the from array, they act as OR.
# AND: pod must be in staging namespace AND have label app=api
- from:
- namespaceSelector:
matchLabels:
environment: staging
podSelector:
matchLabels:
app: api
# OR: any pod in staging namespace OR any pod with label app=api
- from:
- namespaceSelector:
matchLabels:
environment: staging
- podSelector:
matchLabels:
app: api
This is a common source of misconfiguration. Always double-check whether you want AND or OR logic.
CIDR-Based Rules
Allow egress to external services by IP range:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-external
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
# Allow HTTPS to specific external APIs
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
# Allow all internal cluster traffic
- to:
- ipBlock:
cidr: 10.0.0.0/8
except:
- 10.0.5.0/24
The except field excludes a subnet from the allowed range. Use this to allow broad cluster traffic while blocking specific sensitive subnets.
A Complete Three-Tier Example
Here is a full set of policies for a frontend, API, and database architecture:
# 1. Default deny everything
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: myapp
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
# 2. Allow DNS for all pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: myapp
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
---
# 3. Frontend: accept external traffic, connect to API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: myapp
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes: [Ingress, Egress]
ingress:
- ports:
- protocol: TCP
port: 80
egress:
- to:
- podSelector:
matchLabels:
tier: api
ports:
- protocol: TCP
port: 3000
---
# 4. API: accept from frontend, connect to database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
namespace: myapp
spec:
podSelector:
matchLabels:
tier: api
policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 3000
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- protocol: TCP
port: 5432
---
# 5. Database: accept from API only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: myapp
spec:
podSelector:
matchLabels:
tier: database
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
tier: api
ports:
- protocol: TCP
port: 5432
Testing Network Policies
Use a temporary pod to test connectivity:
# Test if frontend can reach the API
kubectl run test-frontend --rm -it --image=busybox \
--labels="tier=frontend" -n myapp -- wget -qO- http://api:3000/health
# Test if frontend can reach the database (should fail)
kubectl run test-frontend --rm -it --image=busybox \
--labels="tier=frontend" -n myapp -- wget -qO- --timeout=5 http://postgres:5432
If a connection that should work is blocked, check your label selectors, namespace labels, and port numbers.
Summary
NetworkPolicies are essential for production Kubernetes clusters. Start with a default deny policy in each namespace, then add specific allow rules for the traffic flows your application needs. Always remember to allow DNS egress, be careful with AND vs OR logic in selectors, and test your policies with temporary pods. The patterns here — default deny, three-tier isolation, cross-namespace controls — cover the majority of real-world use cases.
Related articles
- Kubernetes RBAC in Kubernetes: A Practical Guide
A practical guide to Kubernetes RBAC: Roles, ClusterRoles, RoleBindings, ClusterRoleBindings, ServiceAccounts, and real-world patterns for securing cluster access.
- Kubernetes Kubernetes cert-manager: Automate TLS Certificates
Set up cert-manager in Kubernetes to automate TLS certificate provisioning with Let's Encrypt. Covers Issuers, Certificates, Ingress integration.
- Kubernetes Kubernetes DaemonSets Explained with Practical Examples
Learn how DaemonSets run exactly one pod per node for logging, monitoring, and networking agents. Includes tolerations and update strategies.
- Kubernetes Kubernetes Persistent Volumes and Storage Classes
Understand PersistentVolumes, PersistentVolumeClaims, and StorageClasses to give your Kubernetes workloads reliable, portable storage.