Skip to main content
cd ../config-traps
risk/register/kyverno-blocks-docker-sock-misses-containerd-sock.html
Kubernetes Runtime Securitycritical severityKubernetes

Kyverno Blocks docker.sock, Misses containerd.sock

Severity
critical
Reviewed
12 Jul 2026
Remediation
~20 minutes
Overview

A hostPath denylist written for Docker-era clusters checks for /var/run/docker.sock by exact string but never learned the containerd or CRI-O equivalents, so migrating the runtime silently reopens the same node-root escape the policy was written to close.

Operational summary

At a glance

Symptom
A pod requests a hostPath mount at /run/containerd/containerd.sock or /var/run/crio/crio.sock after a CRI migration, evading the outdated policy.
Likely cause
A hostPath denylist policy matches only the literal string /var/run/docker.sock and ignores containerd or CRI-O socket paths.
Impact
Direct containerd or CRI-O API access lets an attacker spawn a new container with host namespaces and root filesystem access, compromising the node and its scheduled workloads.
Verification signal
Re-run the detection test and a controlled negative-path test.
Safe correction
Replace literal socket path denylists with regex-based ValidatingAdmissionPolicy or Kyverno rules covering all known CRI socket variants, and restrict socket mounts to dedicated node DaemonSets.
Rollback or recovery
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

The Trap

Runtime-socket hostPath denylisting by literal path string, rather than by regex or by capability class, in Kyverno, OPA Gatekeeper, or PodSecurity Admission exemptions.

The Default State

Most published Kyverno and Gatekeeper policy packs for blocking dangerous hostPath mounts were written when Docker was the default CRI and contain a hard-coded string match against /var/run/docker.sock. When a cluster migrates to containerd or CRI-O (as every cluster running Kubernetes 1.24+ has, following dockershim removal), the actual runtime socket moves to /run/containerd/containerd.sock or /var/run/crio/crio.sock. Nobody updates the policy, because the CI pipeline that validated it against docker.sock still passes. The denylist author assumed the socket path is the threat; the socket path is just one instance of it.

The Blast Radius

A pod that requests a hostPath volume at /run/containerd/containerd.sock is admitted cleanly because the string never matches the policy’s blocked-paths array. Inside the container, the containerd ttrpc client (or a statically compiled ctr binary) talks to that socket directly, bypassing Docker entirely. From there an attacker calls the containerd Tasks service to create and start a new container with host PID, host network, and a bind mount of the node’s root filesystem — no privileged: true flag required, because the exploit runs through the runtime API, not through kernel capabilities the pod spec declares. PodSecurity Admission’s restricted profile does not inspect hostPath target strings for semantic risk; it only checks that hostPath is present at all if you’ve configured that rule, and most clusters exempt kube-system or CI namespaces from restricted enforcement anyway. Once the shim is reachable, the attacker owns the node: kubelet client certs, every Secret volume mounted into pods scheduled there, and lateral movement into the API server via the node’s own service account token.

The Lead Mechanic Fix

Replace literal path matching with a regex-based Kyverno validate rule: pattern spec.volumes[].hostPath.path matched against ^/(var/)?run/(docker|containerd|crio|dockershim)(/.*)?.sock$, or better, deny all hostPath volumes outside an explicit allowlist enforced via a ValidatingAdmissionPolicy CEL expression that checks every volume’s hostPath.path against a maintained regex constant, not a per-runtime string list. Mount CRI sockets only inside node-level DaemonSets running under a dedicated ServiceAccount with no API server RBAC beyond what the CNI or CSI driver needs, and gate that DaemonSet’s namespace with a restricted PodSecurity label with no exemption. Audit existing clusters with kubectl get pods -A -o json | jq for any hostPath.path containing ‘.sock’ to find current exposure before writing the rule.

03

Apply the safer control

Before you change production

Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.

Control to implement

Replace literal socket path denylists with regex-based ValidatingAdmissionPolicy or Kyverno rules covering all known CRI socket variants, and restrict socket mounts to dedicated node DaemonSets.

Validate the vendor-specific syntax in official documentation before applying it.

04

Verify, roll back or escalate

Verify

Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.

Rollback

Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

Escalate

Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.

After remediation

Further reading stays below the corrective workflow and is selected by platform, category and shared technical keywords.

Discover more

Connected KBY resources