Network Policies
The idea in one sentence
Section titled “The idea in one sentence”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.
The default: a flat, open network
Section titled “The default: a flat, open network”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/v1kind: NetworkPolicymetadata: name: allow-frontend-to-api namespace: shopspec: podSelector: matchLabels: app: api policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080This 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.
kubectl get networkpolicy -n shopkubectl describe networkpolicy allow-frontend-to-api -n shopNetworkPolicy 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.
The default-deny pattern
Section titled “The default-deny pattern”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/v1kind: NetworkPolicymetadata: name: default-deny-ingress namespace: shopspec: 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/v1kind: NetworkPolicymetadata: name: allow-monitoring-scrape namespace: shopspec: podSelector: matchLabels: app: api policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: monitoring podSelector: matchLabels: app: prometheus ports: - protocol: TCP port: 9090That 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