Kubernetes NetworkPolicy: Default Deny Without Breaking DNS - 夜莺博客

Kubernetes NetworkPolicy: Default Deny Without Breaking DNS

By default a Kubernetes cluster is a flat network: any pod can reach any pod in any namespace, and a single compromised container can talk to your database, your metadata service and your control plane. NetworkPolicy is the fix, but it fails in two quiet ways — a CNI that silently ignores the policy, and a deny-all that nobody paired with a DNS exception. This guide covers the mental model, the patterns that cover most production cases, and how to verify enforcement is real.

The mental model, in three sentences

  1. Without a policy, everything is allowed.
  2. Once a policy selects a pod, everything not explicitly allowed is denied — policies are whitelists.
  3. Multiple policies on the same pod are additive; they never contradict, they union their allows.

Does your CNI actually enforce policies?

Plugin Behaviour
Flannel Ignores NetworkPolicy completely, with no warning.
kindnet Accept the YAML, never enforce it (default in kind clusters).
AWS VPC CNI Ignored until the Network Policy Controller is enabled (v1.14+).
Azure CNI Requires Azure NPM or Calico to be activated.
GKE default Enforcement must be chosen at cluster creation (Dataplane V2 / Calico).
Cilium, Calico, Antrea, Weave Enforce.
kubectl get pods -n kube-system -l k8s-app=calico-node
kubectl get pods -n kube-system -l k8s-app=cilium
kubectl describe daemonset aws-node -n kube-system | grep ENABLE_NETWORK_POLICY

A successful kubectl apply proves nothing. Verify the CNI, then test from a pod.

Pattern 1: default deny, both directions

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}          # every pod in the namespace
  policyTypes: [Ingress, Egress]

No ingress: or egress: block means no exceptions. Deploy this to every namespace before adding application rules — retrofitting deny-all after the fact means a long list of “why is this service broken” tickets.

Pattern 2: the DNS rule nobody remembers

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
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

Deny-all egress without this rule breaks service discovery, which breaks almost everything, and the failure looks like an application bug rather than a network policy.

The AND-versus-OR trap

# AND: pods in a namespace labelled env=production
ingress:
- from:
  - podSelector: {matchLabels: {app: frontend}}
    namespaceSelector: {matchLabels: {env: production}}

# OR: any pod named frontend, or any pod in env=production
ingress:
- from:
  - podSelector: {matchLabels: {app: frontend}}
  - namespaceSelector: {matchLabels: {env: production}}

One list item with both selectors is an AND. Two list items are ORed. This single distinction is the source of most “my policy allows more than I intended” findings, especially when a namespace is not labelled at all — an empty match on a namespace selector behaves differently from what people expect.

Patterns worth having ready

  • Allow Prometheus scraping from the monitoring namespace on 9090/8080 — monitoring is almost always forgotten and then disabled “because it stopped working”.
  • Allow the ingress controller. External traffic through an Ingress still crosses the pod network, so the controller namespace must be allowed explicitly or all inbound traffic is dropped at the policy layer.
  • Block the cloud metadata endpoint (169.254.169.254) on egress to limit credential theft from a compromised pod.
  • Per-namespace policies, labels not names. Policies match labels; naming conventions that drift create silent holes.

Roll out in this order: deny-all plus DNS, then the ingress controller and monitoring, then per-service rules, testing with kubectl exec ... -- nc -zv between namespaces after each step.

Related reading: Calico, Cilium and Flannel compared, Docker networking drivers and nginx reverse proxy configuration.

原文链接:https://cheveo.de/en/blog/network-policies-cheatsheet