Skip to content

Network Policies

By default every Pod in a cluster can talk to every other Pod, and a NetworkPolicy is what lets you restrict that to only the traffic you explicitly allow.

Out of the box, Kubernetes networking has no isolation at all. Any Pod can send traffic to any other Pod, regardless of namespace, and — depending on how Services are exposed — Pods can often be reached from outside the cluster too. There is no default-deny anywhere. This is simple to reason about but means a compromised or misconfigured Pod can reach far more of the cluster than it should ever need to.

NetworkPolicy: selecting Pods, then allowing traffic

Section titled “NetworkPolicy: selecting Pods, then allowing traffic”

A NetworkPolicy (API group networking.k8s.io/v1) selects a set of Pods with a podSelector, then defines the ingress and/or egress rules allowed for exactly those Pods:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: shop
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080

This policy applies only to Pods labeled app: api in the shop namespace, and it allows inbound traffic only from Pods labeled app: frontend, only on TCP port 8080. Everything else to those Pods is implicitly denied once at least one policy selects them.

Terminal window
kubectl get networkpolicy -n shop
kubectl describe networkpolicy allow-frontend-to-api -n shop

NetworkPolicy only works if the CNI enforces it

Section titled “NetworkPolicy only works if the CNI enforces it”

This is the single most important caveat: a NetworkPolicy object is inert unless the cluster’s CNI plugin actually implements NetworkPolicy enforcement. Kubernetes accepts and stores the object either way — kubectl apply will succeed — but if the CNI does not enforce policies, nothing actually changes about the traffic. Always confirm the installed CNI supports NetworkPolicy before relying on it for isolation.

A common point of confusion: Pods that are not selected by any NetworkPolicy remain completely open (allow-all) for that traffic direction. Isolation is opt-in per Pod, not cluster-wide. To lock a namespace down, you deliberately create a policy that selects all Pods but allows nothing:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []

podSelector: {} matches every Pod in the namespace, and an empty ingress: [] list allows nothing in. Once this exists, every Pod in shop is ingress-isolated by default, and you then add further NetworkPolicy objects to selectively allow the traffic you actually want — such as the allow-frontend-to-api example above.

Combining podSelector and namespaceSelector

Section titled “Combining podSelector and namespaceSelector”

Putting both a podSelector and a namespaceSelector in the same from entry narrows the rule to specific Pods in specific namespaces, rather than “any Pod in this namespace” or “this label anywhere in the cluster”:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring-scrape
namespace: shop
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090

That allows traffic only from Pods labeled app: prometheus that are running specifically inside the monitoring namespace — a precise, cross-namespace access rule.

flowchart LR
  subgraph before["Before: default flat network"]
    a1["Pod A"] --- a2["Pod B"]
    a2 --- a3["Pod C"]
    a1 --- a3
  end
  subgraph after["After: NetworkPolicy applied"]
    b1["frontend"] -->|"allowed"| b2["api"]
    b3["random Pod"] -.->|"blocked"| b2
  end
Default flat all-to-all Pod network versus after a NetworkPolicy restricts allowed paths
With no NetworkPolicy objects created at all, how do Pods in a cluster communicate by default
What is required for a NetworkPolicy object to actually restrict traffic
How do you express deny-all-ingress for a set of Pods with NetworkPolicy
What does combining namespaceSelector and podSelector in a single NetworkPolicy rule achieve