By default, every pod in your Kubernetes cluster can talk to every other pod. No questions asked. No restrictions. Complete internal network access from the moment a container starts.
Think about what that means. If an attacker gets remote code execution inside your frontend pod, they now have a free tour of your entire internal network. Your database. Your message broker. Your internal APIs. All reachable. All unprotected.
Network Policies are the fix. And almost nobody has them.
π€ The Situation (Real Talk)
I've done cluster security reviews where zero Network Policies exist in production. Not "some are missing." Zero. Every namespace. Open to everything.
Why? Because Network Policies don't show up in your application logs when they're missing. Nothing breaks. Nothing alerts. The attack surface just silently exists, waiting.
The real conversation that usually triggers action is: "Your SIEM flagged lateral movement between pods in different namespaces." At which point it's too late to be proactive.
π§ How Network Policies Actually Work
Think of pods as houses in a neighbourhood. By default, every door is unlocked, and anyone in the neighbourhood can walk into any house. Network Policies are the locks — you decide exactly which neighbours can knock, which doors they can use, and which ones stay permanently closed.
π Network Policies are implemented by your CNI plugin — Calico, Cilium, or WeaveNet. Flannel doesn't enforce them. If you're using Flannel, these policies silently do nothing. Check your CNI first.
⚙️ The Implementation Recipe That Actually Works
Step 1 — Default deny everything in production namespaces
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # empty selector = applies to ALL pods in namespace
policyTypes:
- Ingress
- Egress
This single policy makes the namespace a closed door. Nothing in, nothing out — until you explicitly allow it.
Step 2 — Add explicit allows for what actually needs to communicate
# Allow frontend to reach the API on port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
# Allow API to reach the database on 5432
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 5432
Step 3 — Allow DNS (this one people always forget)
# Without this, your pods can't resolve any DNS after default-deny
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
π Forgetting DNS egress is the number-one mistake when applying default-deny. Your pods immediately start failing with DNS resolution errors and you spend an hour debugging something that looks like a totally unrelated problem.
⚠️ Common Mistakes
- Forgetting that policies are additive — Multiple policies on the same pod are ORed together. You can't use one policy to "deny" something that another policy already allows.
- Using IP-based policies instead of label selectors — Pod IPs are ephemeral. Label selectors are stable. Always prefer
podSelectorandnamespaceSelectoroveripBlockfor internal traffic. - Not testing with a network policy validation tool — Apply a policy in a dev namespace first. Use
kubectl execto manually test connectivity before assuming it works in production. - Only applying ingress, forgetting egress — A pod with no egress policy can still call out to the internet. Data exfiltration doesn't need inbound access.
π₯ CloudChef Pro Tip
Use Cilium for Network Policies at scale.
π Standard Kubernetes NetworkPolicy covers L3/L4 (IP and port). Cilium extends to L7 — you can write policies that say "allow HTTP GET to /api/v1/health but deny POST to /admin." If you need that level of granularity, Cilium is the CNI you want.
Audit your existing policies.
# See all network policies in all namespaces
kubectl get networkpolicies --all-namespaces
# Check if any namespace has no policies at all
kubectl get namespaces -o name | while read ns; do
count=$(kubectl get networkpolicies -n ${ns#*/} 2>/dev/null | grep -c "^" || echo 0)
echo "${ns#*/}: $count policies"
done
π Continue Your CloudChef Journey
π References
- Kubernetes Network Policies Documentation
- Cilium Network Policy Documentation
- Calico Project Documentation
π Final Thoughts
Network Policies don't feel urgent until they are. The moment a pod is compromised, the difference between "we had network policies" and "we didn't" is the difference between one compromised container and a full lateral movement event.
π Apply a default-deny to your production namespace this week. Add explicit allows. Remember DNS. Test it. That's the whole recipe — and it's one of the highest-ROI security improvements you can make to a cluster in an afternoon.
No comments:
Post a Comment