An entry in a scanner that says “Kubernetes vulnerability” invites a very tidy response: upgrade Kubernetes. CVE-2026-35469 is a good reason not to stop there. The high-severity issue is in github.com/moby/spdystream, whose SPDY/3 parser could allocate enormous amounts of memory from attacker-controlled frame fields; the fixed library release is v0.5.1. It matters to Kubernetes operators because the library is used along streaming paths, but it is not a substitute for identifying the binary that accepts the connection.
That distinction is more than taxonomy. The Kubernetes project’s official CVE feed is the right feed for Kubernetes project disclosures; dependency advisories and runtime-vendor releases are separate inputs. A self-hosted fleet has to join those inputs to its own topology. “We run a recent cluster” is helpful evidence, not the conclusion.
Here is the practical audit to run before treating CVE-2026-35469 as either a fleet-wide emergency or a false positive.
| What to inventory | The question that decides exposure | Evidence to collect | Owner of the fix |
|---|---|---|---|
API server, kubelet, and kubectl | Which streaming protocol is actually negotiated for exec, attach, and port-forward? | Kubernetes version, feature-gate configuration, and a verbose test from a non-production pod | Cluster platform team |
| CRI runtime on every node | Does the shipped runtime include an affected moby/spdystream version and accept a streaming connection? | Runtime package/image version, vendor SBOM or release note, and the endpoint's network boundary | Node-image or OS team |
| Management-cluster controllers and platform tools | Did any controller, proxy, dashboard, or custom tool compile the library? | SBOM plus go list -m all for source-built binaries | Service owner |
| Access policy | Who can reach a streaming endpoint, directly or through Kubernetes subresources? | RBAC review for pods/exec, pods/attach, and pods/portforward; host-firewall rules | Security and platform teams |
| Machine rollout | Can the fixed runtime reach every node without turning a patch into an availability incident? | Machine/MachineDeployment state, surge capacity, disruption budget, and rollback version | Cluster API operator |
The core rule is deliberately plain: a vulnerable dependency plus a reachable server-side parser is an exposure; a transitive entry in an SBOM is a lead to investigate. Do not collapse the two.
What the advisory fixed—and what it did not say
The GitHub advisory describes three unbounded allocation paths: a SETTINGS entry count, a header count, and individual header-field sizes. Because SPDY header blocks are compressed, a small crafted input can produce values that make a process allocate far more memory than the wire payload suggests. The consequence is denial of service, not remote code execution: the affected process can exhaust memory and crash.
The v0.5.1 fix checks the SETTINGS count against frame length, limits header count and field size, and closes the connection on invalid control frames. The upstream v0.5.1 release is the authoritative statement of the patched library version. If a vendor backports the patch without changing the apparent upstream version, the vendor advisory or its SBOM is the evidence that matters.
It is tempting to call this simply a Kubernetes CVE because Kubernetes code has carried moby/spdystream as an indirect dependency. But that shortcut hides both kinds of error: a team may miss an affected CRI-O or platform binary because it looked only at Kubernetes release notes, or it may schedule an emergency cluster upgrade even though its reachable servers are already patched. The official Kubernetes feed is still essential—just not sufficient for every dependency in a Kubernetes-shaped system.
Trace the stream before you patch the fleet
SPDY persists in Kubernetes because interactive container operations are not ordinary request-response calls. A typical kubectl exec, attach, or port-forward request first reaches the API server; the kubelet and container runtime then participate in creating and carrying a stream to the target container. The precise hop that parses frames depends on the Kubernetes release, runtime, and feature gates.
Kubernetes began moving these operations from SPDY to WebSockets in v1.31. Its transition announcement says kubectl defaults to WebSockets for the affected streaming commands when the client and server support it. That is progress, not a license to assume SPDY is absent. Current kubectl documentation still describes fallback to SPDY when a WebSocket upgrade fails, and older clusters or integrations may deliberately retain the earlier transport.
The runtime layer is why a protocol test and a dependency inventory belong together. Kubernetes’ own CRI streaming explainer shows that the runtime creates streaming endpoints for exec, attach, and port-forward. It also explains that port forwarding has its own framing requirements and may use moby/spdystream. A WebSocket between a workstation and the API server does not, by itself, prove that every downstream runtime hop is free of the vulnerable parser.
Use a disposable namespace and a deliberately boring workload to observe the path. Do not test this against a production database or a privileged workload.
# Record the API server and node versions first.
kubectl get --raw=/version
kubectl get nodes -o wide
# On a disposable pod, capture the attempted upgrade headers in client output.
kubectl -v=8 exec deploy/stream-audit -- date
kubectl -v=8 port-forward deploy/stream-audit 18080:8080The verbose output is an observation, not a universal proof. Record whether it attempted WebSockets, whether it fell back, the client version, the target cluster, and any proxy or gateway involved. Re-run it after a runtime or feature-gate change. The point is to catch the “it works on my laptop” gap created by a different client version or an intermediary that does not pass upgrades through.
A five-minute inventory that becomes an actionable ticket
Start with the management cluster and the workload clusters separately. Cluster API makes it easy to see machines as objects; it does not make their runtime bits identical. A management controller, a bootstrap image, and a worker node are different patch targets.
# Management cluster: find the machine rollout units and their providers.
kubectl get machinedeployments,machines -A -o wide
# On each node-image build or source checkout, identify the resolved Go module.
go list -m -f '{{.Path}} {{.Version}}' all \
| grep '^github.com/moby/spdystream ' || true
# On a node, inventory the installed runtime package. Use the package manager
# appropriate to the image; this names the artifact but does not prove its SBOM.
rpm -q cri-o 2>/dev/null || dpkg-query -W -f='${Package} ${Version}\n' cri-o 2>/dev/null
crio --version 2>/dev/null || trueFor a vendor-supplied binary, use the vendor’s SBOM, release metadata, or security bulletin to answer whether its build contains the fixed code. For an image you build, generate an SBOM in CI and retain it with the image digest. For Go services you build from source, the resolved module line above should show v0.5.1 or a documented patched replacement. A scanner finding deserves a ticket with four fields: artifact digest or package NEVRA, embedded dependency version, reachable listening path, and patch owner.
Then check who can trigger the path. The following commands are only safe if the caller is authorized to impersonate the identity being reviewed; otherwise inspect the relevant RoleBindings and ClusterRoleBindings instead.
kubectl auth can-i create pods/exec -n tenant-a --as=system:serviceaccount:tenant-a:app
kubectl auth can-i create pods/attach -n tenant-a --as=system:serviceaccount:tenant-a:app
kubectl auth can-i create pods/portforward -n tenant-a --as=system:serviceaccount:tenant-a:appAn allowed subresource is not automatically a CVE exploit path, and an RBAC denial does not make a publicly exposed runtime endpoint safe. Together with host firewall rules, kubelet authentication settings, and the API-server-to-node network boundary, these checks tell you which identities could cause the relevant streaming machinery to run. That is the real blast-radius statement a small platform team needs.
Patch in the order that preserves both evidence and availability
Once the matrix identifies an affected, reachable artifact, patch the artifact—not a similarly named component—and preserve a trail that says why the new one is fixed. The safest order is usually:
- Obtain the runtime, Kubernetes distribution, or platform-tool release that explicitly includes the v0.5.1 fix or a documented backport.
- Build or select a new immutable node image, record its digest and SBOM, and test
exec,attach, andport-forwardthrough the same proxies production uses. - Roll a small failure domain first. Drain one node only when replacement capacity and workload disruption budgets make that safe; verify the runtime and stream behavior after the node rejoins.
- Let the Cluster API
MachineDeploymentroll the remainder in bounded batches, watching unavailable replicas, control-plane quorum, and runtime health rather than only the Machine status. - Close the ticket only when the new artifact identity and the observed path are recorded. “The rollout finished” is not the same assertion as “the vulnerable parser is no longer reachable.”
If an immediate upgrade is not available, the temporary response should reduce access to the vulnerable endpoint: limit untrusted access to streaming subresources, remove unnecessary port-forward permission, and keep runtime streaming endpoints off untrusted networks. Those are containment measures, not a patch. They can also affect developer workflows, which is why every exception needs an owner and expiry.
Turn a dependency alert into a standing control-plane practice
CVE-2026-35469 is useful precisely because it crosses the boundaries that teams often split into separate backlogs: upstream Kubernetes, a runtime, node images, a Go dependency, and tenant-facing RBAC. The recurring control should cross them too.
For each management cluster and workload-cluster image, keep a short review cadence:
- Subscribe to the Kubernetes official CVE feed for project fixes and to runtime and distribution advisories for their bundled dependencies.
- Produce SBOMs for controller and node images, then map critical findings to an actual process and network listener before assigning severity or an upgrade window.
- Test streaming through the same load balancers, tunnels, and proxies that operators use—not only from inside the cluster network.
- Set a patch service-level objective with an explicit exception path, and rehearse a one-node Cluster API rollout before an advisory dictates the schedule.
Self-hosting does not mean that every vulnerability is urgent; it means the evidence cannot be delegated. That is a fair trade when the fleet makes its binaries, boundaries, and rollout behavior inspectable. A Cluster API control plane gives you machinery to replace machines predictably. The operational discipline is deciding, with evidence, which machine actually needs replacing.
Bex.co is the open-source, AI-native Render alternative: push a Git repository and run an HTTPS service on machines you own. For teams that want application delivery and fleet operations to share a declarative foundation, explore Bex on GitHub.



