Skip to main content
11 min read

hostNetwork Is Still a Footgun: What CVE-2026-32193 Teaches Every Cluster

hostNetwork Is Still a Footgun: What CVE-2026-32193 Teaches Every Cluster

Microsoft published CVE-2026-32193 in June 2026: a remote code execution flaw in Azure Kubernetes Service rated CVSS 8.8. The one-line version is short and uncomfortable. An attacker who can run an untrusted container configured with hostNetwork could send specially crafted requests to a host-level service that was never meant to take unauthenticated calls, exploit a path-traversal bug in it, and break out of the container onto the worker node.

Azure has patched the specific service. If you run AKS, the fix shipped in node image 2026-02-13.5, and you should roll it out. But fixating on the Azure-specific bug misses the point. The reason a container could reach a privileged node service at all is hostNetwork: true, and that switch exists on every Kubernetes distribution. The CVE is a fresh reminder of an old truth: the moment you give a pod the host's network namespace, a pile of "internal only" services on that node stop being internal.

This post walks through what hostNetwork actually changes, why "it only listens on localhost" is not a security boundary, the NetworkPolicy gotcha that catches people out, and the concrete controls that stop this class of escape regardless of which cloud you run on.

TL;DR

  • hostNetwork: true drops the pod's network namespace. The pod shares the node's network stack, so it can reach anything listening on the node's loopback (127.0.0.1) and link-local addresses, including cloud metadata endpoints.
  • Many node-local daemons and cloud agents bind to localhost with no authentication because they assume only the node can reach them. hostNetwork breaks that assumption. CVE-2026-32193 is one instance of the pattern.
  • Kubernetes NetworkPolicy does not apply to hostNetwork pods. Your egress rules will not save you here.
  • The fix is policy, not patching: forbid host namespaces for normal workloads with Pod Security Admission or an admission controller, audit who already has them, and block workload access to the metadata endpoint.
  • Reserve hostNetwork for the few system components that genuinely need it (CNI agents, kube-proxy, some node exporters) and keep them out of namespaces where application teams deploy.

Prerequisites

  • A working knowledge of Kubernetes pods and namespaces
  • kubectl access to a cluster you can audit (read access to pods across namespaces)
  • Familiarity with Linux namespaces at a high level
  • Optional: a policy engine such as Kyverno or Gatekeeper, or Pod Security Admission enabled on your namespaces

What CVE-2026-32193 actually was

The advisory is terse, so here is what the published metadata tells us and what it does not.

  • Class: CWE-22, improper limitation of a pathname to a restricted directory (path traversal).
  • Score: CVSS 8.8, vector AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H.
  • Affected: Azure Kubernetes Service before node image 2026-02-13.5.
  • Condition: the attacker can schedule or run an untrusted container with hostNetwork enabled.

Two parts of that vector matter for the lesson. AV:L (local) and PR:L (low privileges) mean the attacker is already inside a container on the node, not on the public internet. That is a normal day for a multi-tenant cluster or any cluster that runs partially trusted workloads, CI jobs, or customer code. And S:C (scope changed) is the headline: the compromise crosses a security boundary. A process confined to a container ends up controlling the node, which is a different security authority than the workload that started it.

Warning

If you run AKS, confirm your node images are at 2026-02-13.5 or later. Check with kubectl get nodes -o wide and compare the node image version, or review the AKS security bulletins. Patching closes this specific service, but the rest of this post is about the door it came through.

The path-traversal flaw itself is Azure's to fix and they fixed it. What you own is the condition that exposed it.

What hostNetwork actually does

A normal pod gets its own network namespace. It has its own loopback interface, its own set of listening sockets, and its own view of the network. When that pod talks to 127.0.0.1, it reaches itself, not the node.

Setting hostNetwork: true removes that boundary. The pod runs in the node's network namespace instead of its own.

Normal pod                          hostNetwork: true
+-------------------------+         +-------------------------+
|  pod netns              |         |  (no pod netns)         |
|   lo -> the pod itself  |         |   shares the NODE netns |
|   eth0 via CNI          |         |                         |
+-----------+-------------+         +-----------+-------------+
            |                                   |
            | CNI / NetworkPolicy applies       | talks to the node's
            v                                   v stack directly
+-------------------------+         +-------------------------+
|  node network namespace |         |  node network namespace |
|   127.0.0.1:<kubelet>   |  <----  |   127.0.0.1:<kubelet>   |
|   127.0.0.1:<agents>    |  reach  |   127.0.0.1:<agents>    |
|   169.254.169.254 IMDS  |  these  |   169.254.169.254 IMDS  |
+-------------------------+         +-------------------------+

Now 127.0.0.1 from inside the pod is the node's loopback. Every service bound to the node's loopback is one connection away. So is the cloud metadata endpoint at 169.254.169.254 and any other link-local address the node can reach. The pod did not gain a new capability in the Linux sense. It gained reach.

You can confirm the effect quickly. A hostNetwork pod sees the node's interfaces and hostname:

# A pod that shares the node's network stack.
apiVersion: v1
kind: Pod
metadata:
  name: hostnet-demo
spec:
  hostNetwork: true          # the footgun
  containers:
    - name: shell
      image: nicolaka/netshoot
      command: ['sleep', 'infinity']
kubectl exec hostnet-demo -- hostname        # prints the NODE's hostname
kubectl exec hostnet-demo -- ss -lntp         # lists sockets the NODE is listening on
kubectl exec hostnet-demo -- curl -s http://127.0.0.1:10248/healthz   # kubelet healthz, on the node's loopback

That last command is harmless on its own. The problem is everything else that also listens on the node's loopback and assumes nobody untrusted can connect.

"It only listens on localhost" is not a boundary

A huge amount of software binds to 127.0.0.1 and treats that as authentication. The mental model is "only code running on this machine can reach me, and code running on this machine is already trusted." On a single-tenant VM that is roughly true. On a Kubernetes node running mixed workloads it is not, because hostNetwork lets a pod become "code running on this machine" for network purposes.

The list of things that commonly listen on a node's loopback or link-local addresses is long:

  • Kubelet's read-only and healthz endpoints
  • Cloud provider node agents that broker bootstrap credentials and node identity
  • The instance metadata service (IMDS) at 169.254.169.254, which hands out the node's cloud identity and, on many setups, tokens for it
  • Local proxies, log shippers, and CSI or CNI helper sockets
  • Debug and admin endpoints that developers assumed were unreachable

CVE-2026-32193 is the cloud-agent case: a node-local service that brokers privileged operations trusted its callers and had a path-traversal bug. With hostNetwork, an untrusted pod reached it and turned a parsing flaw into node control. This is the same shape as the IMDS credential-theft problem that has bitten cloud Kubernetes for years. If a workload can reach 169.254.169.254, it can often assume the node's cloud identity, and on a node that means the kubelet's permissions and any IAM role attached to the node pool.

The takeaway is not "this one Azure service was buggy." It is that a node runs a small fleet of privileged local services that were designed assuming the network namespace boundary holds. hostNetwork removes the boundary, so any bug in any of them becomes a node takeover.

The NetworkPolicy gotcha

Here is the part that surprises people. You might assume a default-deny egress NetworkPolicy would stop a hostNetwork pod from reaching the metadata endpoint or a node-local service. It does not.

Kubernetes NetworkPolicy is implemented by the CNI plugin against pod network namespaces. A hostNetwork pod has no pod network namespace. Its traffic originates from the node's stack, which the CNI does not police the same way. The upstream documentation is explicit that NetworkPolicy behavior for hostNetwork pods is undefined, and in practice most CNIs do not enforce policy on them.

# This will NOT reliably stop a hostNetwork pod from reaching IMDS.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: app
spec:
  podSelector: {}
  policyTypes: ['Egress']
  # No egress rules = deny all egress... for pods with their own netns.
  # hostNetwork pods bypass this.

So the defense cannot be "lock down egress with NetworkPolicy." If a pod has hostNetwork, you have already lost the network-layer control. The defense has to be earlier: do not let untrusted workloads set hostNetwork in the first place.

Step 1: find out who already has it

Before you enforce anything, see what would break. Audit every pod and workload template that sets host namespaces or other escape-prone fields. This command lists running pods using hostNetwork, hostPID, or hostIPC:

Expect to see your CNI agent, kube-proxy, and node exporters. Those are legitimate and we will handle them in a moment. What you are hunting for is application workloads in team namespaces that picked up hostNetwork because someone copied a Helm values file or wanted to avoid a Service. Those are the accounts that turn a node-local bug into an incident.

Audit the templates too, not just running pods, since a Deployment can sit at zero replicas:

# Scan workload templates for host namespaces and privileged settings.
kubectl get deploy,daemonset,statefulset -A -o json \
  | jq -r '.items[]
      | select(.spec.template.spec.hostNetwork==true
            or .spec.template.spec.hostPID==true
            or any(.spec.template.spec.containers[].securityContext // {}; .privileged==true))
      | "\(.kind)\t\(.metadata.namespace)/\(.metadata.name)"'

Step 2: forbid host namespaces for normal workloads

The cleanest control ships with Kubernetes. Pod Security Admission's baseline profile already forbids host namespaces (hostNetwork, hostPID, hostIPC), hostPath volumes, and privileged containers. Enforcing baseline on the namespaces where teams deploy stops this class of escape without writing a single custom rule.

You can apply the same intent three ways depending on what you run. All three reject the demo pod above.

Roll it out in stages. Set warn and audit first so you can see which workloads would be rejected, fix or exempt them, then flip enforce. Flipping straight to enforce on a busy cluster is how you find out at 2am that a DaemonSet you forgot about needed hostNetwork.

Step 3: cut off the metadata endpoint

Even with host namespaces locked down, normal pods can often still reach IMDS at 169.254.169.254 through the regular CNI path, and that is its own credential-theft route. Close it:

  • On AKS, GKE, and EKS, use the provider guardrails. EKS users should move to IRSA or EKS Pod Identity and block IMDS access from pods. GKE has Workload Identity and metadata concealment. AKS has Workload Identity so pods stop needing the node's identity at all.
  • Where the CNI does enforce policy (normal pods), add an explicit egress deny to 169.254.169.254/32.
  • Prefer per-workload cloud identity (Workload Identity / IRSA / Pod Identity) over node-attached roles, so a node compromise is worth less.

None of these help a hostNetwork pod, which is exactly why step 2 comes first. Defense in depth means the metadata block catches the ordinary pods and the host-namespace ban catches the dangerous ones.

When hostNetwork is legitimately needed

hostNetwork is not evil. A handful of components need it because they operate on the node's networking itself:

  • CNI agents (Cilium, Calico) that program the node's dataplane
  • kube-proxy, which manages node-level service routing
  • Node exporters and some observability agents that read host-level network stats
  • A few ingress and load-balancer setups that bind directly to node ports for performance

The rule is not "never use hostNetwork." It is "only system components use it, and they live where application teams cannot deploy." Keep those workloads in kube-system or a dedicated, locked-down namespace, exempt only that namespace from the policy, and treat any request to add hostNetwork to an application namespace as a security review, not a config tweak. If a team wants hostNetwork to expose a port, they almost always want a Service or a properly scoped hostPort instead.

Key takeaways

  • hostNetwork: true is a reach amplifier. It does not add Linux capabilities, it removes the network namespace boundary, and that boundary is what keeps untrusted pods away from privileged node-local services and the metadata endpoint.
  • CVE-2026-32193 is one bug in one Azure service, but the pattern is universal. Every node runs local daemons that trust local callers, so any one of them becomes a node takeover once a pod shares the host network.
  • NetworkPolicy does not apply to hostNetwork pods. Do not rely on egress rules to contain them.
  • Enforce Pod Security Admission baseline (or an equivalent admission policy) on application namespaces, audit what already uses host namespaces, and block workload access to IMDS with per-workload cloud identity.
  • Patch your nodes, then fix the door: keep hostNetwork for the few system components that need it and out of every namespace where untrusted or application code runs.
Published: 2026-06-30|Last updated: 2026-06-30T09:00:00Z

Found an issue?

Also worth your time on this topic