FR
live

Kubernetes 1.37 finally locks down volumes with noexec, nosuid, and an emptyDir mode

Kubernetes v1.37 introduces two storage security settings, still in alpha: bind mount options (noexec, nosuid, nodev) and a permission mode on emptyDir volumes. Enable the VolumeBindMountOptions and EmptyDirVolumeMode feature gates to replace init-container workarounds.

A row of metal storage cage doors held slightly ajar, a single amber padlock hanging from one of the latches.

September 16, 2026. Kubernetes v1.37 documents two container-storage hardening features, both in alpha: bind mount options (noexec, nosuid, nodev) and a permission mode on emptyDir volumes. September 16. The Red Hat engineers behind the feature lay out the “why” in a post on the official blog. September 20. No cluster applies them by default yet: two feature gates must be enabled. Why it matters: a compromised process in a container has always been able to execute a binary from any writable volume — even with a read-only root filesystem — because noexec was never set by default.

The gap these two settings close

The starting point is a detail of the kubelet and the container runtimes that few teams measure: when a volume is bind-mounted into a container, it is mounted without the noexec, nosuid, or nodev flags. In other words, every writable volume — an emptyDir, a PersistentVolume, a hostPath mount — is mounted with permission to execute whatever sits on it.

The consequence is direct. An attacker who gains code execution inside a container, even one running with readOnlyRootFilesystem: true, can download a binary onto the writable volume, mark it executable with chmod +x, and then run it. The root filesystem is locked, but the volume is not: the bypass becomes an exercise in convenience. The gap has been documented for a long time — the Kubernetes 1.24 security audit (NCC-E003660-7HM) already flagged the inability to mount an emptyDir with noexec as a security failure, and issue #48912 sat open for years.

The second gap concerns emptyDir volume permissions. By default, an emptyDir is created with a hard-coded 0777 mode: any process that discovers the volume can read, write, and delete everything on it, regardless of ownership. The existing workaround — an init container running a chmod — works, but it adds complexity, is hard to audit for compliance, and does not cover the sticky bit (mode 01777), expected on any shared directory worthy of /tmp, nor restricted permissions like 0750.

Two entry points, two levers

1.37 answers both problems with two distinct and complementary mechanisms.

The first, bindMountOptions, is added to a volumeMount specification. It accepts exactly the three Linux VFS flags you expect from a hardened mount: noexec (forbid execution of binaries), nosuid (neutralize setuid/setgid bits), and nodev (ignore character or block device files). This is the native answer to hardening benchmarks — CIS, NSA/CISA, PCI-DSS — that explicitly require these flags on writable mounts.

The second, mode, is added to the emptyDir volume source. It sets the directory’s creation permission: 01777 for a shared workspace that behaves like /tmp, 0750 for a directory restricted to its owner and group. The sticky bit in 01777 changes the game in multi-container pods: a compromised process in one container can no longer delete artifacts produced by another container in the same pod, since only a file’s owner (or root) can remove it.

The two features are independent and can be combined on the same mount — for example a shared emptyDir, mounted noexec,nosuid, with a 01777 mode.

How to enable them today

Both settings sit behind alpha feature gates: VolumeBindMountOptions and EmptyDirVolumeMode, enabled on the API server and the kubelet. In 1.37 they are off by default, and their alpha status demands caution: the syntax may evolve, and they should be validated on a test cluster before broad rollout.

The minimal manifest to mount an emptyDir at /tmp with the hardening flags:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: hardened-bindmount-pod
spec:
  os:
    name: linux
  containers:
    - name: hardened-app
      image: alpine:latest
      command: ["sleep", "3600"]
      securityContext:
        readOnlyRootFilesystem: true
      volumeMounts:
        - name: temp-storage
          mountPath: /tmp
          bindMountOptions:
            - noexec
            - nosuid
  volumes:
    - name: temp-storage
      emptyDir: {}

And the variant that enforces a sticky bit on a shared directory by setting the creation mode:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: sticky-tmp-pod
spec:
  containers:
    - name: app
      image: alpine:latest
      volumeMounts:
        - name: tmp
          mountPath: /tmp
  volumes:
    - name: tmp
      emptyDir:
        mode: "01777"

The second manifest replaces the init container that ran chmod 1777 /tmp before the app started: one declarative line instead of a workaround sidecar.

What it changes in practice

The use cases the authors spell out are precise, and they speak directly to a platform team.

  • Blocking privilege escalation on writable mounts. A temporary workspace mounted noexec,nosuid guarantees that, even if the application is compromised and a payload is downloaded, the payload can neither execute nor exploit a setuid bit to escalate on the node.
  • Securing shared space in multi-container pods. A CI/CD pod with a builder container and a logger sidecar can share a volume in 01777 mode: each writes freely, but a compromised process can no longer destroy a neighbor’s artifacts.
  • Applying least privilege to data. A database whose temporary storage is mounted 0750 is readable only by the expected user and group, denying access to any other sidecar in the pod.

The common thread across all three: security is declared in the manifest, not in an init script. That is exactly the direction the community has been heading for several releases — treating security posture as auditable, reproducible configuration rather than a pile of startup patches.

Rolling out alpha gates without breaking the node

Because both features are alpha, a few operational details matter before you flip the switch. The gates must be enabled on the kubelet and the API server, which means a rolling restart of the control plane and the node pool — plan for it rather than discovering it mid-change. Existing volumes are unaffected: the new fields only apply to pods you explicitly annotate with bindMountOptions or an emptyDir.mode, so enabling the gates is not a cluster-wide behavior change for workloads you leave untouched.

The real work is verification. A pod that silently drops the noexec request would be worse than a pod that never asked, so confirm the flags actually reach the runtime — for example by attempting to execute a binary from the mounted volume inside a test pod and expecting the attempt to fail. And because some CSI drivers and container runtimes translate mount options inconsistently, test against the exact driver and runtime you run in production before migrating anything sensitive.

Verdict

If you run multi-tenant clusters, CI/CD pods, or workloads under a hardening benchmark, enable VolumeBindMountOptions and EmptyDirVolumeMode on a test cluster starting with 1.37, and begin migrating your writable mounts to noexec,nosuid with an explicit mode — it is the cleanest way to close an execution hole most clusters have left open all along. If you are still on 1.36 or earlier, the init-container workaround with chmod and a correct securityContext remains the only path, but document it: compliance is proven, not guessed. If you do neither, at minimum set noexec at the node level on volume directories and add readOnlyRootFilesystem mounts — a partial posture is always better than a writable, executable-by-default volume.

References

The cyber brief, every Tuesday

The flaws that matter and the patches to apply, in a ten-minute read.

No spam. One-click unsubscribe.
read next

On the same topic

A Kubernetes backup is not disaster recovery

On September 10, 2026 two CNCF ambassadors published three reproducible failure scenarios that separate having backups from being able to actually recover. Test full restore into a cluster that has never run the workload, validate data against an expected result, and time the whole thing.

CrowdSec loses 170 private repos to the TanStack npm chain and a botched offboarding

On September 18, 2026, CrowdSec revealed that an attacker copied about 170 private GitHub repositories in May using the account of a former employee whose access had never been revoked. The initial compromise came from TanStack’s malicious npm packages, which also hit Mistral AI and OpenAI.

← Back to the feed

Type at least two characters.

navigate open esc dismiss