CME-713

Read-Only Root Filesystem Enforcement

Description

Mount the container root filesystem as read-only, forcing all writable data into explicitly declared volumes or tmpfs mounts. Prevents attackers who achieve code execution inside a container from modifying binaries, planting webshells, or persisting backdoors on the filesystem. Combined with NoNewPrivileges (CME-707) and Dropped Capabilities (CME-705), eliminates post-exploitation persistence mechanisms within the container layer.

CVSS Vector Impacts

Metric Transition Rationale
Integrity (I) H → L Runtime filesystem modification is blocked; attacker cannot persist malicious artifacts, modify binaries, or plant webshells on the root filesystem
Attack Complexity (AC) L → H Root filesystem modification is blocked; exploitation chain requires identifying a writable volume mount (tmpfs, emptyDir, PVC) to stage payloads — complexity increase is strongest when writable mounts are minimized to essential paths only

CWE Relationships

Verification

Verify container root filesystem is mounted read-only

$ kubectl get pod <pod> -o jsonpath='{range .spec.containers[*]}{"ro="}{.securityContext.readOnlyRootFilesystem}{"\n"}{end}' 2>/dev/null | grep 'ro=' | grep -c -v 'ro=true'
# Expected: 0
Platform: kubernetes
$ podman inspect <ctr> --format '{{.HostConfig.ReadonlyRootfs}}'
# Expected: true
Platform: linux
$ docker inspect <ctr> --format '{{.HostConfig.ReadonlyRootfs}}'
# Expected: true
Platform: linux
$ kubectl get pod <pod> -o jsonpath='{range .spec.containers[*].volumeMounts[*]}{.mountPath}{"\t"}{.readOnly}{"\n"}{end}' 2>/dev/null | grep -v 'true$' | grep -c -E '^/(usr|bin|sbin|lib|etc|opt|srv|var/www)'
# Expected: 0
Platform: kubernetes
$ kubectl get clusterpolicy,constrainttemplate -o json 2>/dev/null | grep -c -i 'readOnlyRootFilesystem'
# Expected: >=1
Platform: kubernetes
$ kubectl exec <pod> -c <ctr> -- sh -c 'touch /cme-write-test 2>&1' | grep -c -i 'read-only'
# Expected: 1
Platform: kubernetes

Remediation

kubernetes Kubernetes readOnlyRootFilesystem with tmpfs for writable paths

kubectl patch deployment <deployment> -n <namespace> --type=json -p '[{"op":"add","path":"/spec/template/spec/containers/0/securityContext/readOnlyRootFilesystem","value":true}]'
kubectl patch deployment <deployment> -n <namespace> --type=json -p '[{"op":"add","path":"/spec/template/spec/volumes/-","value":{"name":"tmp","emptyDir":{"medium":"Memory","sizeLimit":"64Mi"}}},{"op":"add","path":"/spec/template/spec/containers/0/volumeMounts/-","value":{"name":"tmp","mountPath":"/tmp"}}]'
kubectl label namespace <namespace> pod-security.kubernetes.io/enforce=restricted --overwrite
linux Podman/Docker read-only root filesystem with tmpfs mounts

podman run --read-only --tmpfs /tmp:rw,size=64m --tmpfs /var/run:rw,size=16m <image>
printf '[Container]\nReadOnly=true\nTmpfs=/tmp:rw,size=64m\nTmpfs=/var/run:rw,size=16m\n' > /etc/containers/systemd/<service>.container.d/readonly.conf && systemctl daemon-reload && systemctl restart <service>
← CME-712: Container Image Signing and Provenance Verification (Cosign/Sigstore) CME-801: Multi-Factor Authentication (MFA) →