What is a NetworkPolicy, and what's the default network behavior without one?

7 minadvancednetworkpolicysecuritynetworking

Quick Answer

By default, every Pod in a Kubernetes cluster can reach every other Pod (and be reached by every other Pod) across the whole cluster, regardless of namespace — there's no network isolation unless you add it. A NetworkPolicy is a namespace-scoped set of rules that restricts which Pods can send traffic to (egress) or receive traffic from (ingress) a given set of Pods, based on label selectors — but it only has any effect if the cluster's CNI plugin actually implements NetworkPolicy enforcement, since the object itself is just configuration.

Detailed Answer

The default: flat, fully-open networking

Out of the box, Kubernetes's networking model guarantees every Pod can reach every other Pod's IP directly, cluster-wide, with no NAT and no default restriction — this "flat network" model is a deliberate simplicity choice in the base Kubernetes networking design, but it means a compromised or misbehaving Pod in one namespace can, by default, reach any Pod in any other namespace, including ones it has no legitimate business talking to.

Restricting traffic with a NetworkPolicy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-allow-from-frontend-only
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend-api          # this policy applies to Pods labeled app=backend-api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend       # only allow traffic FROM pods labeled app=frontend
      ports:
        - protocol: TCP
          port: 8080

This says: Pods labeled app: backend-api in the production namespace only accept inbound traffic on port 8080, and only from Pods labeled app: frontend — traffic from any other Pod (including other Pods in the same namespace not labeled frontend, and every Pod in every other namespace) is rejected.

The default-deny pattern

An empty podSelector: {} with no ingress/egress rules matches all Pods in the namespace and, since no rules are specified, denies all traffic of that type — a common, deliberate first step for hardening a namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Applied alone, this blocks all traffic in/out of every Pod in the namespace — then additional, more permissive NetworkPolicies are layered on top to explicitly allow only the specific traffic patterns actually needed (frontend → backend, backend → database, everything → DNS). This "default deny, then explicitly allow" approach is the standard, security-recommended pattern rather than trying to enumerate every disallowed path from an otherwise-open default.

The critical caveat: NetworkPolicy needs CNI support

A NetworkPolicy object is just declarative configuration — like any Kubernetes object, it does nothing on its own unless something in the cluster actually enforces it. NetworkPolicy enforcement depends entirely on the cluster's CNI plugin (see that question) supporting it — Calico, Cilium, and several others implement NetworkPolicy enforcement; some simpler CNI plugins (certain Flannel configurations, in particular) do not, meaning NetworkPolicy objects you create are silently accepted by the API server but have zero actual effect on traffic. Verifying your specific CNI plugin actually enforces NetworkPolicies is an essential, easy-to-overlook step — a false sense of security from an unenforced NetworkPolicy is worse than no policy at all, since it looks secured but isn't.

For any cluster running genuinely multi-tenant workloads, or handling sensitive data, treat NetworkPolicies (backed by a CNI plugin that actually enforces them) as a baseline security control, not an optional extra — combined with a default-deny starting posture per namespace and explicit allow rules for legitimate traffic paths only.