Full lesson
Explore the full explanation, examples, and visuals at your own pace.
default-deny-egress.yaml applied to namespace shop
Apply this one policy, and every service call in shop starts failing. The empty podSelector selects every Pod in the namespace, and policyTypes lists Egress with no rules, so nothing may leave. That's true even though we separately allowed the frontend to reach backend. Something else is being blocked first.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: shop
spec:
podSelector: {}
policyTypes:
- EgressBefore the policy
Before the policy, a call to backend is really two connections. First, the frontend asks CoreDNS on port 53 to resolve the name, and gets back 10.96.0.12. Only then does it open HTTP to the backend on 8080 and receive a 200 OK. Notice that the DNS lookup is itself egress traffic, leaving the frontend Pod toward kube-system.
After the policy: DNS dropped
Now the policy is on. Frontend sends its lookup to CoreDNS on port 53, but no egress rule matches that traffic, so it's dropped silently. No rejection comes back. The retry fails the same way, and the lookup times out. Backend never gets a single packet, even though we allowed port 8080. To the application, it just looks like a connectivity failure.
Selected for egress? Only explicitly allowed egress leaves the Pod.
Here's the rule. Once any policy selects a Pod for egress, everything not explicitly allowed in that direction is denied. DNS included.
Allowing backend does not allow DNS
The allow-backend rule is narrow. It lets the frontend reach backend Pods on port 8080, and nothing else. CoreDNS on port 53 is a different destination, in another namespace, so that rule never matches it. Policies only add up as a union of allowed destinations. DNS needs its own rule.
allow-dns.yaml
This policy lets every Pod in shop reach CoreDNS on port 53. Notice namespaceSelector and podSelector sit in one list item, so they combine as AND: kube-system and k8s-app kube-dns. We include TCP because large responses fall back to it. Check those labels on your own cluster.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: shop
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53Fix applied: lookup, then connect
With both rules in place, the original flow works again. Frontend asks CoreDNS to resolve backend on port 53, and gets 10.96.0.12 back. Then it opens the HTTP connection to Backend on 8080 and receives a 200 OK. Two connections, each with its own allow rule. And default-deny still blocks everything else, so you haven't given up the security.
Test from inside the frontend Pod
Test from inside the frontend Pod, and test in two steps. nslookup isolates DNS. wget by name exercises both lookup and connection, and a bad address error points straight at DNS. If the image has no tools, kubectl debug attaches a busybox container. These outputs are illustrative.
# 1. Name resolution
kubectl -n shop exec deploy/frontend -- nslookup backend
# broken: connection timed out; no servers could be reached
# 2. Connectivity by name
kubectl -n shop exec deploy/frontend -- wget -qO- -T 3 http://backend:8080
# broken: bad address 'backend'
# 3. No tools in the image? Attach a debug container
kubectl -n shop debug -it deploy/frontend --image=busybox -- shSame YAML, different clusters
One caveat. The API server accepts a NetworkPolicy no matter what, so applying it proves nothing. On an enforcing CNI, the DNS query gets stopped. On a non-enforcing one, the same YAML is simply ignored and traffic flows. Enforcement belongs to the plugin, so check what yours supports.
Takeaways
Every Service call starts with a DNS call, so allow both. Default-deny egress blocks port 53 too, so open UDP and TCP to kube-dns, and make sure your CNI enforces policy. Next time, run nslookup before blaming the backend rule.
- Default-deny egress also blocks DNS on port 53
- Allow UDP and TCP 53 to kube-dns Pods
- Confirm your CNI enforces NetworkPolicy

