Back to Checks
kubernetes

Containers should run with a read-only root filesystem

core_readonly_root_filesystem_enabled
Service: coreSeverity: medium
OfficialBuilt-inVerified
Categories:container-security
Resource Type:Pod

Kubernetes Pods are evaluated to ensure every container sets readOnlyRootFilesystem: true in its securityContext. A writable root filesystem lets an attacker who gains code execution modify binaries, drop tools, or persist malicious files inside the container.

Risk

Without a read-only root filesystem an attacker with code execution inside a container can tamper with binaries or libraries (integrity), write credential files or exfiltration tools (confidentiality), and persist across process restarts (availability).

Run this check with Prowler

Run in Prowler Cloud
Run
prowler kubernetes --checks core_readonly_root_filesystem_enabled

Recommendation

Set readOnlyRootFilesystem: true for every regular, init, and ephemeral container that is part of a Pod spec. Mount writable emptyDir volumes only at the specific paths that genuinely need write access (e.g., /tmp, /var/cache). Enforce this at admission time with custom admission policies such as ValidatingAdmissionPolicy or OPA/Gatekeeper.

Remediation

CLI

kubectl patch deployment/<workload-name> -n <namespace> -p '{"spec":{"template":{"spec":{"containers":[{"name":"<container-name>","securityContext":{"readOnlyRootFilesystem":true}}]}}}}'
# Deployment template example for regular containers. For init containers, use initContainers instead of containers. Ephemeral containers are added through the ephemeralcontainers subresource; remove or recreate debug containers with a compliant securityContext.

Terraform

resource "kubernetes_pod" "main" {
  metadata {
    name = "<example_resource_name>"
  }
  spec {
    container {
      name  = "app"
      image = "nginx"
      security_context {
        read_only_root_filesystem = true # Critical: enforce a read-only root filesystem
      }
    }
  }
}

Other

1Open your Kubernetes Dashboard (or your cloud provider's Kubernetes console) and locate the workload managing the failing Pod (Deployment/StatefulSet/DaemonSet)
2Click Edit to modify the manifest (YAML)
3For each container missing `securityContext.readOnlyRootFilesystem: true`, add or set it to `true`
4If the container needs write access, mount a writable `emptyDir` or `persistentVolumeClaim` at the specific paths that require writes instead of making the entire root filesystem writable
5Save/Apply the changes to trigger a rollout
6Verify new Pods have `securityContext.readOnlyRootFilesystem` set to `true`

Check Code

from prowler.lib.check.models import Check, Check_Report_Kubernetes
from prowler.providers.kubernetes.services.core.core_client import core_client


class core_readonly_root_filesystem_enabled(Check):
    """Check whether every container in each Pod has readOnlyRootFilesystem set to true."""

    def execute(self) -> list[Check_Report_Kubernetes]:
        """Execute the Kubernetes read-only root filesystem check.

        Returns:
            List of check reports for Kubernetes pods.
        """
        findings = []
        for pod in core_client.pods.values():
            report = Check_Report_Kubernetes(metadata=self.metadata(), resource=pod)
            report.status = "PASS"
            report.status_extended = f"Pod {pod.name} has read-only root filesystem enabled for all containers."

            for containers in (
                pod.containers,
                pod.init_containers,
                pod.ephemeral_containers,
            ):
                for container in (containers or {}).values():
                    if (
                        container.security_context.get("read_only_root_filesystem")
                        is not True
                    ):
                        report.status = "FAIL"
                        report.status_extended = f"Pod {pod.name} container {container.name} does not have readOnlyRootFilesystem set to true."
                        break
                if report.status == "FAIL":
                    break

            findings.append(report)

        return findings