Network Policies
By default, Kubernetes is an "Open Network."
- Any Pod can talk to any other Pod.
- A hacked web server in the
defaultnamespace can port-scan your database in thesecurenamespace.
NetworkPolicies are the firewall rules of Kubernetes. They let you enforce a Zero Trust environment where traffic is blocked unless explicitly allowed.
1. The "Isolation" Switch
Network Policies work differently than traditional firewalls.
- No Policy: The Pod is "Non-Isolated." It accepts traffic from anywhere.
- Policy Exists: As soon as a NetworkPolicy selects a Pod, that Pod becomes "Isolated."
- It immediately blocks ALL traffic that isn't explicitly allowed by the policy.
Visualizing the Switch
graph TD
subgraph "Scenario A: No Policy"
Web1[Web Pod] -->|Allowed| DB1[DB Pod]
Hacker1[Hacker Pod] -->|Allowed!| DB1
end
subgraph "Scenario B: With NetworkPolicy"
Web2[Web Pod] -->|Allowed by Rule| DB2[DB Pod]
Hacker2[Hacker Pod] -- "Blocked (Default Deny)" --> DB2
NP[NetworkPolicy] -.->|Selects| DB2
end
style DB2 stroke:#ff0000
2. The "Default Deny" (Start Here)
The best practice for security is to start by locking everything down, and then punching holes for what you need.
This policy acts as a "Catch-All" to block all incoming and outgoing traffic for every Pod in the namespace.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: default
spec:
podSelector: {} # Selects ALL pods in this namespace
policyTypes:
- Ingress
- Egress
Once you apply this, your application will stop working. Now you must explicitly allow traffic.
3. Allow Specific Traffic (Ingress)
Let's say we want to allow our backend to receive traffic from the frontend, but nothing else.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
spec:
podSelector:
matchLabels:
app: backend # The target (Who gets protected?)
ingress:
- from:
- podSelector:
matchLabels:
app: frontend # The source (Who can knock?)
ports:
- port: 8080
protocol: TCP
4. The "Namespace" Gotcha
A common mistake is trying to allow traffic from a different namespace using podSelector. podSelector only works within the same namespace.
To allow traffic from any Pod in the monitoring namespace:
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring # Requires the namespace to have this label!
Label Your Namespaces!
namespaceSelector selects based on Labels, not the Namespace name. You must run:
kubectl label namespace monitoring name=monitoring
5. Egress & The DNS Trap
If you block Egress (outbound traffic), you block DNS lookups too.
If your Pod can't reach the DNS server (CoreDNS), it can't resolve google.com or my-db, and your app will crash.
Always allow UDP port 53 if you restrict Egress.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
spec:
podSelector: {} # Apply to all pods
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system # Where CoreDNS lives
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
6. Combining Selectors (AND vs OR)
Be careful with your YAML indentation. It changes the logic completely.
Logic: OR (Any of these)
Traffic allowed from frontend OR from monitoring.
ingress:
- from:
- podSelector: { matchLabels: { app: frontend } }
- namespaceSelector: { matchLabels: { name: monitoring } }
Logic: AND (Must match BOTH)
Traffic allowed only from a pod labeled frontend running inside the monitoring namespace.
ingress:
- from:
- podSelector: { matchLabels: { app: frontend } }
namespaceSelector: { matchLabels: { name: monitoring } }
Summary
- NetworkPolicies require a CNI that supports them (Calico, Cilium, Antrea).
- They act as a firewall for Pods.
- Default Behavior: Allow All.
- Selected Behavior: Deny Everything Else.
- Best Practice: Apply a "Default Deny" policy first.
- Critical Gotcha: If you block Egress, remember to Allow DNS (Port 53), or nothing will work.