
- Kubernetes Tutorial
- Kubernetes - Home
- Kubernetes - Overview
- Kubernetes - Architecture
- Kubernetes - Setup
- Kubernetes - Setup on Ubuntu
- Kubernetes - Images
- Kubernetes - Jobs
- Kubernetes - Labels & Selectors
- Kubernetes - Namespace
- Kubernetes - Node
- Kubernetes - Service
- Kubernetes - POD
- Kubernetes - Replication Controller
- Kubernetes - Replica Sets
- Kubernetes - Deployments
- Kubernetes - Volumes
- Kubernetes - Secrets
- Kubernetes - Network Policy
- Advanced Kubernetes
- Kubernetes - API
- Kubernetes - Kubectl
- Kubernetes - Kubectl Commands
- Kubernetes - Creating an App
- Kubernetes - App Deployment
- Kubernetes - Autoscaling
- Kubernetes - Dasard Setup
- Kubernetes - Helm Package Management
- Kubernetes - CI/CD Integration
- Kubernetes - Persistent Storage and PVCs
- Kubernetes - RBAC
- Kubernetes - Logging & Monitoring
- Kubernetes - Service Mesh with Istio
- Kubernetes - Backup and Disaster Recovery
- Managing ConfigMaps and Secrets
- Running Stateful Applications
- Multi-Cluster Management
- Security Best Practices
- Kubernetes CRDs
- Debugging Pods and Nodes
- K9s for Cluster Management
- Managing Taints and Tolerations
- Horizontal and Vertical Pod Autoscaling
- Minikube for Local Development
- Kubernetes in Docker
- Deploying Microservices
- Blue-Green Deployments
- Canary Deployments with Commands
- Troubleshooting Kubernetes with Commands
- Scaling Applications with Kubectl
- Advanced Scheduling Techniques
- Upgrading Kubernetes Clusters
- Kubernetes Useful Resources
- Kubernetes - Quick Guide
- Kubernetes - Useful Resources
- Kubernetes - Discussion
Securing Kubernetes with Network Policies
In Kubernetes, network communication between Pods is open by default. That means any Pod can talk to any other Pod without restrictions. While this is fine for early development, it becomes a huge security risk in production environments. We need a way to control "who can talk to whom." This is where Kubernetes Network Policies come in.
In this chapter, we'll learn what Network Policies are, why they're important, and how to use them. We'll also build a real-world example securing a frontend app and a MySQL database.
What are Kubernetes Network Policies?
Network Policies are Kubernetes resources that control traffic between Pods and other network endpoints based on rules.
With Network Policies, we can:
- Allow only specific Pods to communicate with each other.
- Restrict traffic based on Pod labels, namespaces, and ports.
- Secure sensitive services like databases.
- Build "Zero Trust" networking inside the cluster.
Think of them as firewalls for Kubernetes.
Important: Network Policies only take effect if your Kubernetes cluster supports them. Many CNI plugins like Calico, Cilium, Weave Net, and Kube-router support Network Policies.
Why Use Network Policies?
Without Network Policies:
- Any compromised Pod could move laterally and attack other services.
- Secrets, databases, and internal APIs are exposed.
With Network Policies:
- Services only talk to authorized clients.
- We can limit blast radius in case of a breach.
- Kubernetes apps become more secure and compliant.
How Network Policies Work
Network Policies define two main types of traffic:
- Ingress Traffic: Incoming traffic to the Pod.
- Egress Traffic: Outgoing traffic from the Pod.
Each Policy can:
- Select Pods by their labels.
- Allow/deny traffic from specific Pods, namespaces, or IP blocks.
- Specify allowed ports and protocols (TCP/UDP).
By default:
- If no Network Policy selects a Pod, all traffic is allowed.
- If one or more Network Policies select a Pod, the Pod only allows what the policies permit. All other traffic is denied by default.
Prerequisites
Before we proceed, ensure:
- You have a running Kubernetes cluster (Minikube, KIND, or real cluster).
- Your cluster has a CNI plugin that supports Network Policies (e.g., Calico, Cilium, or Weave Net).
Note: If you use Minikube, start with Calico like this:
$ minikube start --network-plugin=cni --cni=calico
Setting Up the Environment
Let's first deploy two basic apps to simulate communication.
Create a namespace for isolation:
$ kubectl create namespace secure-app
Output
namespace/secure-app created
Deploy a backend pod:
$ kubectl run backend --image=nginx --namespace=secure-app --labels="app=backend" --port=80
Output
pod/backend created
Expose it:
$ kubectl expose pod backend --port=80 --namespace=secure-app
Output
service/backend exposed
Deploy a frontend pod:
$ kubectl run frontend --image=nginx --namespace=secure-app --labels="app=frontend" --port=80
Output
pod/frontend created
Confirm if the pods are running:
$ kubectl get pods -n secure-app
Output
NAME READY STATUS RESTARTS AGE backend 1/1 Running 0 4m51s frontend 1/1 Running 0 3m59s
Test Pod-to-Pod Communication
By default, frontend can reach backend easily.
Open a shell inside the frontend pod:
$ kubectl exec -n secure-app -it frontend -- /bin/sh
Inside the pod, try to curl the backend service. We should get an HTML response from the nginx server:
# curl backend
Output
<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
Create a Basic Network Policy (Deny All)
Now let's lock everything down first.
Create a file called deny-all.yaml:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: secure-app spec: podSelector: {} policyTypes: - Ingress
What this does:
- Targets all pods (empty podSelector: {}).
- Denies all incoming traffic (Ingress) unless explicitly allowed.
Apply it:
$ kubectl apply -f deny-all.yaml
Output
networkpolicy.networking.k8s.io/deny-all created
Test Again
Try to curl backend from frontend again:
# curl backend
Output
curl: (7) Failed to connect to backend port 80: Connection refused
Allow Specific Communication
Now let's allow traffic only from certain Pods.
Create a file called allow-frontend-to-backend.yaml:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: secure-app spec: podSelector: matchLabels: app: backend ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 80
Explanation,
- Selects pods with label app=backend.
- Allows ingress from pods labeled app=frontend.
- Only on TCP port 80.
Apply it:
$ kubectl apply -f allow-frontend-to-backend.yaml
Output
networkpolicy.networking.k8s.io/allow-frontend-to-backend created
Test Again
Inside frontend pod, run:
# curl backend
Output
<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
Real-World Applications: Securing a Frontend and MySQL Backend
Let's go deeper. Suppose we have:
- A frontend app (app: frontend) running on port 3000.
- A MySQL database (app: mysql) running on port 3306.
We want:
- Only the frontend app to talk to MySQL.
- No other Pods should access MySQL.
Let's implement it step-by-step.
Deploy MySQL
Create a file called mysql-deployment.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: mysql spec: selector: matchLabels: app: mysql replicas: 1 template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD value: "password" ports: - containerPort: 3306
Apply it:
$ kubectl apply -f mysql-deployment.yaml
Output
deployment.apps/mysql created
Check that the MySQL deployment is running correctly:
$ kubectl get deployments
Output
NAME READY UP-TO-DATE AVAILABLE AGE mysql 1/1 1 1 5m42s
Check the pods to ensure MySQL is running:
$ kubectl get pods
Output:
NAME READY STATUS RESTARTS AGE mysql-6ddd846c5d-f6z5q 1/1 Running 0 6m5s
Deploy Frontend App
Create a file called frontend-deployment.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: frontend spec: selector: matchLabels: app: frontend replicas: 1 template: metadata: labels: app: frontend spec: containers: - name: frontend image: node:14 command: ["node", "-e", "require('http').createServer((req,res)=>res.end('Hello')).listen(3000)"] ports: - containerPort: 3000
Apply it:
$ kubectl apply -f frontend-deployment.yaml
Output
deployment.apps/frontend created
Confirm if the frontend deployment and pod is running correctly:
$ kubectl get deployments
Output
NAME READY UP-TO-DATE AVAILABLE AGE frontend 0/1 1 0 9s mysql 1/1 1 1 11m
$ kubectl get pods
Output
NAME READY STATUS RESTARTS AGE frontend-58b97ffdc4-lhgvb 1/1 Running 0 2m15s mysql-6ddd846c5d-f6z5q 1/1 Running 0 13m
Create Network Policy for MySQL
Create a file called mysql-network-policy.yaml:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: mysql-network-policy spec: podSelector: matchLabels: app: mysql policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 3306
What this does:
- Only Pods with label app=frontend can talk to Pods with label app=mysql on port 3306.
Apply it:
$ kubectl apply -f mysql-network-policy.yaml
Output:
networkpolicy.networking.k8s.io/mysql-network-policy created
To confirm the network policy is running:
$ kubectl get networkpolicies
Output
NAME POD-SELECTOR AGE mysql-network-policy app=mysql 40s
Egress Control (Optional But Important)
Ingress controls incoming traffic. Egress controls outgoing traffic.
Let's create a policy to restrict pods from accessing external internet:
Create a file called deny-egress.yaml: apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-egress namespace: secure-app spec: podSelector: {} policyTypes: - Egress
Apply it:
$ kubectl apply -f deny-egress.yaml
Output:
networkpolicy.networking.k8s.io/deny-egress created
Now inside the pod, trying to curl google.com will fail:
$ curl google.com
Output
curl: (7) Failed to connect to google.com port 80: Connection refused
Points to Note
Keep the following points in mind while applying Network Policies in Kubernetes -
- Network Policy is "allow only" once applied. Everything else is denied by default.
- Policy enforcement depends on the CNI plugin. Make sure your CNI supports it.
- Egress rules are critical to prevent Pods from talking to the internet.
- Use labels carefully. Policies depend heavily on labels.
- Start simple, then gradually build complex policies.
Best Practices for Network Policies
Here is a set of best practices for securing Kubernetes with network policies -
- Apply "default deny all ingress" and "default deny all egress" first.
- Allow traffic explicitly between required services.
- Use namespaces to further isolate environments (dev, staging, prod).
- Test policies carefully before rolling out to production.
- Monitor traffic to identify missing or redundant policies.
Conclusion
By using Network Policies, we add a strong layer of security to our Kubernetes applications. It's like building walls and gates inside the cluster. We control traffic precisely and prevent unwanted access.
In this chapter, we learned how Network Policies work, how to apply them, and showcased a real-world example protecting a frontend and MySQL backend. The next step is to practice by writing your own policies and tightening security in your Kubernetes clusters.