LearnKubernetes
Kubernetes6. Security·EXPERT·6 min read

Network Policies

TL;DR

  • By default, Kubernetes networking is a flat, fully-open mesh — any pod can reach any other pod cluster-wide
  • NetworkPolicies are the only native mechanism to restrict traffic; they only work if the CNI plugin enforces them (Calico, Cilium, Weave Net do; basic Flannel does not)
  • Applying NetworkPolicy to a pod immediately denies all traffic in that direction unless explicitly allowed
  • Standard pattern: apply default-deny-all, then layer explicit allow rules
  • Always remember to allow egress to CoreDNS (port 53 UDP/TCP) when restricting egress, or DNS breaks for all affected pods
  • Namespaces provide zero network isolation by default — only NetworkPolicies or service meshes provide traffic segmentation
  • Selector logic in ingress/egress rules: combining podSelector and namespaceSelector in the same entry uses AND logic; separate entries use OR

Core Concepts

The Default-Allow Reality

Without any NetworkPolicy objects, every pod can send traffic to every other pod and receive traffic from every other pod, cluster-wide. This default-allow behavior is often surprising to teams migrating from traditional segmented networks where network isolation is the default.

Critical dependency on CNI: NetworkPolicy is only enforced if the CNI plugin supports it. Flannel in its basic mode does not enforce NetworkPolicies at all — Calico, Cilium, and Weave Net do. Applying a NetworkPolicy on a cluster with a non-enforcing CNI does nothing and creates a dangerous false sense of security.

Default-Deny: The Essential First Policy

The moment a pod is selected by any NetworkPolicy (for a given direction), all traffic in that direction is denied unless explicitly allowed by some policy. The standard pattern is: apply a default-deny-all policy, then layer explicit allow rules on top.

Default-deny policies select all pods in a namespace and block ingress and/or egress until allow rules are added:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: payments
spec:
  podSelector: {}          # selects ALL pods in the namespace
  policyTypes:
  - Ingress
  - Egress

Ingress and Egress Rules

NetworkPolicies define rules for incoming traffic (ingress) and outgoing traffic (egress). Each rule specifies:

  • from/to selectors: which pods/namespaces/CIDR ranges the rule applies to
  • ports: specific protocols and port numbers to allow

Selector types:

  • podSelector — other pods, scoped to the same namespace unless combined with namespaceSelector
  • namespaceSelector — all pods in namespaces matching the label
  • ipBlock — CIDR ranges, for traffic to/from outside the cluster

Selector combination logic: Combining podSelector AND namespaceSelector in a single from/to entry means “pods matching X, in namespaces matching Y” (AND logic). Separate entries in the same list mean OR logic.

DNS and NetworkPolicy: A Common Gotcha

When egress is restricted, pods cannot reach CoreDNS (port 53 UDP) unless explicitly allowed. DNS failures after applying egress rules are extremely common because DNS resolution is just another egress traffic type. Every namespace that applies egress restrictions needs an explicit allow rule permitting egress to CoreDNS (typically via a namespaceSelector matching kube-system, port 53 UDP/TCP).

Network Isolation is Not Automatic

Namespaces provide zero network isolation by default — namespace boundaries are purely organizational and RBAC constructs. Without explicit NetworkPolicies (or a service mesh enforcing mTLS and authorization), a compromised pod in one namespace can freely reach any pod’s IP:port in any other namespace across the entire cluster.

Comparison Table

Aspect Default Behavior With NetworkPolicy With Service Mesh
Pod-to-pod traffic Fully allowed cluster-wide Denied unless explicitly allowed Denied unless authorized
Cross-namespace isolation None If policies exist If policies + mTLS enabled
DNS resolution Works (no restrictions) Must be explicitly allowed Works with mTLS
CNI dependency None Required (plugins vary) Independent of NetworkPolicy
CIDR blocking Not possible natively Via ipBlock rules Via policies + mesh rules
Complexity Minimal Medium High

Command Reference

List all NetworkPolicies in a namespace:

kubectl get networkpolicy -n payments

Describe a policy to see its exact ingress/egress rules:

kubectl describe networkpolicy allow-frontend-to-api -n payments

Apply a network policy manifest:

kubectl apply -f network-policy.yaml

Test connectivity directly to diagnose a policy issue:

kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -qO- http://payments-api:8080 --timeout=3

Check whether the cluster’s CNI supports NetworkPolicy enforcement:

kubectl get pods -n kube-system | grep -Ei "calico|cilium|weave"

View a complete NetworkPolicy definition:

kubectl get networkpolicy allow-frontend-to-api -n payments -o yaml

Delete a NetworkPolicy:

kubectl delete networkpolicy allow-frontend-to-api -n payments

Common Pitfalls

Pitfall — Forgetting to Allow CoreDNS Egress After applying an egress-deny rule, pods suddenly cannot resolve any DNS names, causing cascading failures in applications.

  • Why: Default-deny blocks all egress, including UDP port 53 traffic to CoreDNS, which is not obviously related to application traffic.
  • Fix: Always include an explicit allow rule for egress to CoreDNS (typically namespaceSelector matching kube-system, port 53 UDP and TCP).

Pitfall — Assuming NetworkPolicy Exists When CNI Doesn’t Support It A NetworkPolicy is created with correct selectors and rules, but traffic remains unrestricted with zero indication of the problem.

  • Why: NetworkPolicy is purely an API object; enforcement is entirely delegated to the CNI. Basic Flannel silently accepts the object without enforcing it.
  • Fix: Verify the cluster’s CNI plugin actually supports NetworkPolicy enforcement before relying on it for security (Calico, Cilium, Weave Net do; basic Flannel does not).

Pitfall — Namespace Boundaries Providing Network Isolation Assuming workloads in different namespaces are automatically isolated from each other’s network traffic.

  • Why: Namespaces are organizational constructs only; they have no impact on network routing or traffic filtering.
  • Fix: Use explicit NetworkPolicies to enforce cross-namespace isolation, or use a service mesh that enforces mTLS and authorization policies.

Pitfall — Misunderstanding Selector Combination Logic Combining podSelector and namespaceSelector in the same ingress/egress rule, expecting OR logic when the actual behavior is AND.

  • Why: The Kubernetes API interprets combining these selectors in a single entry as AND logic, not OR.
  • Fix: Use separate entries in the list for OR logic; place selectors in the same entry only when both conditions must match.

Interview Questions

Q: Explain the default pod-to-pod traffic behavior in Kubernetes without any NetworkPolicies applied.

Q: Why does a default-deny-all NetworkPolicy often break DNS resolution, and how do you fix it?

Q: A NetworkPolicy looks correctly configured, but traffic is still unrestricted. What is the first thing to verify?

Q: Walk through the difference between AND logic and OR logic when combining selectors in NetworkPolicy rules.

Q: Design a network isolation strategy for a multi-tenant cluster where teams should not be able to reach each other’s workloads.

Q: Can namespaces isolate network traffic without NetworkPolicies? Explain.

Q: What are the differences in traffic restrictions provided by NetworkPolicies versus a service mesh with mTLS?

Q: When would using ipBlock rules in a NetworkPolicy be necessary versus just using pod/namespace selectors?

🔒 Locked
Sign in and unlock the full syllabus to keep reading.
Sign in to continue
Knowledge check
5 scenario questions on this topic
Take the quiz →
Related in 6. Security
SSL/TLS Fundamentals
EXPERT
TLS in Kubernetes
EXPERT
Authentication & Authorization
EXPERT
RBAC
EXPERT