14.1 Default Allow-All and Why It Is Dangerous
Right out of the box, your Kubernetes cluster is a model of reckless optimism. It operates on a principle of “default allow-all.” This is the networking equivalent of leaving your front door wide open with a sign that says, “All valuables are in the upstairs safe, which is also unlocked.” Every pod can talk to every other pod, and every pod can initiate egress traffic to the entire internet. It’s convenient for getting started, but it’s a security nightmare waiting to happen.
Why is this the case? Because Kubernetes is designed first and foremost to get your applications running. The network plugins (like Calico, Cilium, or WeaveNet) are there to provide connectivity, not to be security gatekeepers by default. Their primary job is to make sure the “IP-per-pod” model works and that pods can talk to each other across nodes. Imposing restrictions is a secondary, explicit step you have to take. It’s a classic case of convenience over security, and it’s your job to fix it.
The Anatomy of a Default-Deny Policy
The first and most critical step is to enact a “default-deny” posture. This flips the entire security model on its head. Instead of “allow everything unless we say otherwise,” it becomes “deny everything unless we explicitly allow it.” This is the cornerstone of a zero-trust network inside your cluster.
You implement this using two NetworkPolicy resources: one for ingress (traffic coming to the pod) and one for egress (traffic leaving from the pod). Here’s what that looks like:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: your-namespace
spec:
podSelector: {} # The magic. This selects ALL pods in the namespace.
policyTypes:
- Ingress
# No 'ingress' array specified means "allow no incoming traffic."
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: your-namespace
spec:
podSelector: {} # Again, applies to all pods.
policyTypes:
- Egress
# No 'egress' array specified means "allow no outgoing traffic."
Apply these to a namespace, and you’ve just thrown that namespace into a state of digital solitary confinement. Nothing can talk to the pods inside, and they can’t talk to anything outside. It might seem drastic, but it’s the only sane starting point. Now you can surgically add rules to allow only the necessary communication.
The Immediate Aftermath of Lockdown
The moment you apply these policies, you will break things. Spectacularly. And that’s the point! You’re about to discover all the implicit connections your applications were relying on that you never knew about.
- Your application might fail because it can no longer talk to the Kubernetes API server to get service account tokens or discover other services. (This is a big one, often missed).
- That nifty sidecar that exports metrics? It can’t phone home to Prometheus anymore.
- Your app can’t resolve DNS queries because it’s blocked from talking to
kube-dns(the CoreDNS pods in thekube-systemnamespace).
This pain is a feature, not a bug. It forces you to explicitly document and allow every single required network flow. You’re moving from “it works by accident” to “it works because we designed it to.”
Allowing the Essential Plumbing
Before your apps can function, you need to allow some fundamental cluster traffic. The most critical is DNS. Without it, almost nothing works, as service discovery grinds to a halt. Here’s how you allow egress to DNS. Note the use of a namespace selector targeting the kube-system namespace where CoreDNS usually lives.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: your-namespace
spec:
podSelector: {} # Applies to all pods
policyTypes:
- Egress
egress:
# Allow egress to TCP/UDP port 53 for DNS
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
You’ll likely also need to allow egress traffic to the API server, which usually lives in the default namespace with the label component=apiserver. This is why reading the labels on your system pods is a crucial skill.
The Most Common Pitfall: Forgetting the Traffic Flow
Here’s where everyone gets tripped up. Network Policies are stateful for the traffic they allow. This is a brilliant design choice that saves you a world of pain.
If a pod initiates an outbound connection (egress) to another pod, the return traffic from that destination pod is automatically allowed. You don’t need an explicit ingress rule for the response. It just works. The reverse is also true: if you allow ingress to a port, the response packets for that connection are automatically allowed for egress.
The pitfall is assuming you need symmetric rules. You don’t. If your front-end pod needs to talk to your back-end pod, you only need an egress rule on the front-end or an ingress rule on the back-end. You choose which side to put the policy on based on who “owns” the security requirement. Does the back-end need to protect itself from unauthorized access? Then put an ingress policy on the back-end. Does the front-end need to be restricted in what it can talk to? Then put egress policies on the front-end. Often, you’ll use a combination.
The dangerous “default allow-all” is a relic of a simpler time. By starting with default-deny, you’re not just securing your cluster; you’re formally documenting the network architecture of your application, one explicit rule at a time. It’s a pain, but it’s the kind of pain that separates a hobbyist setup from a production-grade one.