Skip to main content
5 min read·802 words

Securing Container Volume Mounts with Bind Mount Options

Kubernetes v1.37 introduces two alpha storage hardening features — bind mount options (bindMountOptions) and emptyDir permission modes — that close a persistent gap in container isolation: writable volumes that allow binary execution even when readOnlyRootFilesystem: true is set.


The Security Problem​

Setting readOnlyRootFilesystem: true in a container's security context prevents writes to the container's root overlay. However, it has no effect on volumes that are mounted into the container. By default, volumes are bind-mounted without noexec, nosuid, or nodev flags. A compromised process can write a binary to any writable volume — including emptyDir, PersistentVolumes, ConfigMap, or Secret volumes — make it executable with chmod +x, and run it directly from the mount point.

The emptyDir volume type compounds this: its directory is created with a hardcoded 0777 mode, so any process that discovers the volume path can read, write, or delete its contents.

These gaps were tracked in kubernetes/kubernetes#48912 and kubernetes/kubernetes#119627, the latter citing a 1.24 security audit.

A common workaround — using an init container to run chmod — adds complexity and is difficult to verify at admission time.


1. Bind Mount Options on Volume Mounts​

Kubernetes v1.37 adds a bindMountOptions field to volumeMounts. The field accepts a list of Linux bind mount flags:

FlagEffect
noexecBlocks direct execution of binaries on the mount
nosuidIgnores set-user-ID and set-group-ID bits
nodevIgnores character and block special devices

Issue: Without bind mount flags, a process writing to an emptyDir or any other writable volume can execute arbitrary binaries from that path, even with readOnlyRootFilesystem: true.
Fix: Set bindMountOptions on the volumeMount entry to apply noexec, nosuid, and nodev at the container level.

Supported volume types​

bindMountOptions works with emptyDir, PersistentVolumes, CSI volumes, projected volumes, ConfigMap volumes, and Secret volumes. Image volumes are explicitly unsupported.

Feature gates​

Both the API server and every kubelet must have the VolumeBindMountOptions feature gate enabled. The container runtime must also support the mount_options field in the CRI Mount message and advertise that support via runtimeFeatures. If a pod reaches a node whose container runtime does not advertise support for the mount_options CRI field, the kubelet rejects the pod — there is no silent degradation.

Enable on the API server and kubelets:

--feature-gates=VolumeBindMountOptions=true

Example: applying noexec and nosuid to an emptyDir​

apiVersion: v1
kind: Pod
metadata:
name: hardened-bindmount-pod
namespace: default
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: {}

Verification​

After applying the pod, confirm the mount flags are in effect:

kubectl exec hardened-bindmount-pod -- mount | grep /tmp

The output should include noexec and nosuid in the mount options.

Confirm that binary execution is blocked:

kubectl exec hardened-bindmount-pod -- sh -c 'cp /bin/ls /tmp/ls && /tmp/ls'

Expected output:

sh: /tmp/ls: Permission denied

2. emptyDir Permission Modes​

A companion alpha feature (EmptyDirVolumeMode, KEP-5502) adds a mode field to emptyDir volume definitions. This sets the Unix creation permission mode on the emptyDir directory itself.

Issue: emptyDir directories are always created with mode 0777, allowing any process that discovers the path to read, write, or delete any file inside.
Fix: Set emptyDir.mode to an explicit permission mode such as 01777 (world-writable with sticky bit) or 0750 (owner and group only).

ModeEffect
01777Sticky bit — files can only be deleted by their owner or root; standard for shared /tmp-style directories
0750Accessible only by the volume owner and their group

Feature gate: EmptyDirVolumeMode. Must be enabled on all relevant cluster components.

fsGroup interaction: If fsGroup is set in the pod security context, its group permissions override mode, consistent with how defaultMode behaves on Secret and ConfigMap volumes.

This feature has no effect on Windows nodes.

Example: shared scratch space with sticky bit​

apiVersion: v1
kind: Pod
metadata:
name: hardened-emptydir-pod
namespace: default
spec:
os:
name: linux
containers:
- name: app-container
image: alpine:latest
command: ["sleep", "3600"]
volumeMounts:
- name: shared-tmp
mountPath: /tmp
volumes:
- name: shared-tmp
emptyDir:
mode: 01777

Verification​

Confirm the sticky bit prevents one user from deleting another user's files:

kubectl exec hardened-emptydir-pod -- sh -c 'touch /tmp/guest_file && chmod 600 /tmp/guest_file'
kubectl exec hardened-emptydir-pod -- rm /tmp/guest_file

With the sticky bit set and the file owned by a different UID, the deletion should fail:

rm: can't remove '/tmp/guest_file': Operation not permitted

3. Relationship to Existing Controls​

PersistentVolume mountOptions vs bindMountOptions​

PersistentVolume.spec.mountOptions and StorageClass.mountOptions apply filesystem-level flags at the node via the CSI driver. They do not reliably become bind mount flags inside the container. bindMountOptions on a volumeMount operates at the container level and is independent of — and complementary to — any node-level mountOptions.

Read-only root filesystem​

readOnlyRootFilesystem: true prevents writes to the container image layers and overlay root. It does not affect volumes mounted into the container. bindMountOptions closes this gap on the volume side.

Pod Security Standards​

Pod Security Standards (Baseline and Restricted) do not currently require or recommend bindMountOptions — the feature is alpha and the PSS definitions have not been updated to reference it. Apply bindMountOptions independently of PSS enforcement.


4. Version Skew and Cluster Compatibility​

Both features are Alpha in Kubernetes v1.37 and disabled by default. Alpha features are not available on most managed Kubernetes offerings (EKS, GKE, AKS) until a subsequent release cycle promotes them to beta or stable. Check your managed provider's supported feature gate list before attempting to enable these flags.

Enabling VolumeBindMountOptions has no effect on Windows nodes — the field is ignored on non-Linux pods.


Audit: Identify Pods with Writable Volumes and No bindMountOptions​

Find all pods with emptyDir volumes that do not set bindMountOptions:

kubectl get pods -A -o json | jq -r '
.items[] |
. as $pod |
.spec.volumes[]? |
select(.emptyDir != null) |
[$pod.metadata.namespace, $pod.metadata.name, .name] |
@tsv
'

This returns a list of namespaces, pod names, and volume names where emptyDir is used. Cross-reference with spec.containers[*].volumeMounts to verify whether bindMountOptions is set on each mount.


References​

This article is based on information from the following official sources:

  1. Set Bind Mount Options on Volume Mounts - Kubernetes Documentation
  2. Kubernetes v1.37: Hardening Container Storage with Bind Mount Options and EmptyDir Permissions - Kubernetes Blog