Safeguard
Cloud Security•

Default-deny egress for ingress controllers: a NetworkPolicy starter

When ingress-nginx code execution is the risk, outbound limits decide what an attacker can steal. A short NetworkPolicy starter with the DNS gotcha.

Safeguard Security Team
2 min read

The 9 March 2026 advisory for CVE-2026-3288 says an Ingress annotation can inject nginx configuration and lead to arbitrary code execution in the ingress-nginx controller, with disclosure of Secrets the controller can read. Patching fixes that bug. A NetworkPolicy decides what a future bug of the same kind can do afterwards, because stolen data still has to leave the pod.

What's affected

NetworkPolicy only works if your network plugin enforces it. The Kubernetes docs say plainly that policies are implemented by the network plugin, and a cluster without an enforcing plugin accepts the objects and does nothing. Pods are open to all traffic until at least one policy selects them, and a policy that selects a pod for egress denies everything it does not allow.

What to do now

  1. Confirm your CNI enforces NetworkPolicy in its documentation, then test with a throwaway namespace.
  2. Apply the default-deny policy from the Kubernetes docs to a test namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
  1. Add DNS back. The docs warn that default-deny egress also blocks DNS, so allow the cluster DNS service. The control plane sets the immutable label kubernetes.io/metadata.name on every namespace, which lets you select kube-system by name:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
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
  1. Prove the block works from a test pod:
kubectl run -n netpol-test probe --rm -it --restart=Never --image=busybox:1.36 -- wget -qO- -T 3 http://example.com
  1. For the ingress controller namespace, add explicit egress only to what it needs: DNS, the Kubernetes API server, and the backend namespaces it routes to. Roll that out in a staging cluster first, since a controller with no route to its backends serves errors.

Check the DNS pod label on your distribution before relying on the example above. Some clusters label CoreDNS differently.

Quick check

Why does a default-deny egress policy often break name resolution?

Egress to the cluster DNS service is denied like all other egress, so you must add a separate policy that allows it.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.