What are Kubernetes network policies and how do you set default deny?

A Kubernetes network policy is a namespaced NetworkPolicy object that acts as a firewall for Pods: it selects Pods by label and lists the ingress and egress traffic they may send or receive. Without any policy, every Pod can reach every other Pod and any external address. A default deny is a policy with an empty podSelector and no allow rules, which isolates every Pod in the namespace until other policies open specific paths.

How network policies work

A Pod is non-isolated until a policy selects it. Once any policy selects a Pod for ingress or egress, only the traffic that some policy allows in that direction gets through. The Kubernetes documentation is explicit that policies “do not conflict; they are additive”: the result is the union of all rules that apply, and evaluation order does not matter.

Each policy has three parts:

  • podSelector chooses the Pods the policy applies to. An empty selector {} selects every Pod in the namespace.
  • policyTypes says whether the policy covers Ingress, Egress or both. If you leave it out, Ingress is always set and Egress is set only when the policy has egress rules.
  • ingress and egress rules list allowed peers (Pod labels, namespace labels or IP blocks) and ports.

A connection between two Pods needs both sides to agree: the source Pod’s egress policy and the destination Pod’s ingress policy must each allow it.

Enforcement depends on the CNI plugin

The API server stores NetworkPolicy objects, but the network plugin enforces them. In the words of the docs, “creating a NetworkPolicy resource without a controller that implements it will have no effect.” Flannel, for example, does not enforce network policies on its own and needs a separate policy controller. Cilium, Calico and Antrea enforce them. Check this before you rely on a policy, because an unenforced policy fails silently. The article on choosing a CNI plugin compares the common options.

How to set a default deny

Start every protected namespace with one policy that denies both directions:

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

This blocks DNS too. Every name lookup now fails, which surfaces as timeouts that look unrelated to networking. Add an egress rule to the cluster DNS service right away:

yaml
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: 53

Then add one narrow policy per real traffic path, for example “Pods labelled app: api accept TCP 8080 from Pods labelled app: frontend”. The kubernetes.io/metadata.name label is set on every namespace automatically, so you can select namespaces by name without labelling them yourself. Check the labels of your DNS Pods, because k8s-app: kube-dns is a convention and not every distribution uses it.

What the core API cannot do

The upstream list of things NetworkPolicy does not support includes:

NeedCore NetworkPolicyWhere it comes from instead
Allow egress to a domain name such as api.stripe.comNo, IP blocks onlyCNI extensions, such as Cilium toFQDNs rules
Rules on HTTP methods or paths (layer 7)NoCNI extensions or a service mesh
Explicit deny rulesNo, allow rules onlyCNI policies with deny actions, ClusterNetworkPolicy
One policy for all namespacesNo, namespaced onlyCNI cluster-wide policies, ClusterNetworkPolicy
Logging of allowed or dropped connectionsNoCNI observability tooling
Encryption or TLSNoNode-to-node encryption or a service mesh

CNI plugins fill these gaps with their own resources. Cilium has CiliumNetworkPolicy and the cluster-scoped CiliumClusterwideNetworkPolicy, with layer 7 HTTP and DNS-based rules. Calico has its own NetworkPolicy and GlobalNetworkPolicy resources. These resources are not portable between plugins, so a switch of CNI means rewriting them.

Cluster-wide guardrails

Namespace owners can write their own NetworkPolicies, which makes them a poor tool for rules the platform team must guarantee, such as “no Pod reaches the cloud metadata endpoint”. SIG Network’s Network Policy API project addresses this with ClusterNetworkPolicy, a custom resource in the policy.networking.k8s.io/v1alpha2 API. It has two tiers: an Admin tier evaluated before namespace policies, which namespace owners cannot override, and a Baseline tier evaluated after them, which sets defaults they can override. Rules use Accept, Deny or Pass actions.

It succeeds the earlier AdminNetworkPolicy and BaselineAdminNetworkPolicy APIs, which the project lists as no longer actively developed. As of November 2026 the API version is alpha, and the project’s implementations page lists Calico, Kube-OVN and kube-network-policies. Until your CNI supports it, use the CNI’s own cluster-wide policy resource.

How to test that a policy blocks traffic

A policy that applies without errors is not proof that it works. Test each path from inside the cluster:

  1. Start a temporary Pod in the source namespace with the same labels as the real client, for example kubectl run probe --rm -it --image=busybox --labels=app=frontend -n shop -- sh.
  2. From that Pod, connect to an allowed destination and confirm it succeeds, then connect to a denied one and confirm it times out.
  3. Repeat with a Pod that has different labels, to confirm the selector is as narrow as you meant.
  4. If the CNI exposes policy verdicts, such as Cilium’s Hubble, check that dropped flows show the policy you expect.

Network policies are one layer. For untrusted code, combine them with a sandboxed runtime as described in how to isolate untrusted workloads, and for tenants that share a cluster, with the controls in Kubernetes multi-tenancy.

Network policies on Cloudfleet

Cloudfleet Kubernetes Engine (CFKE) uses Cilium to enforce NetworkPolicy, CiliumNetworkPolicy and CiliumClusterwideNetworkPolicy on every node, in every cloud and on self-managed nodes, with nothing to enable. The network policies guide shows the common patterns.

On this page