Skip to main content
emnode / learn
Compliance High severity

AWS Security Hub · ECS

ECS.5: A container has a writable root filesystem

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub ECS.5 check?

ECS.5 passes only when every container definition in a task definition sets readonlyRootFilesystem to true. It fails when the parameter is false or simply omitted, which is the default — leaving the container's root filesystem writable at runtime.

Why does ECS.5 matter?

A writable root filesystem is the first thing an attacker reaches for after landing a foothold: they can drop a reverse-shell binary, install reconnaissance tooling, or overwrite the application's own binary so malicious behaviour survives a restart. Making the filesystem read-only takes that whole post-exploitation playbook off the table — the foothold becomes ephemeral and dies with the next task recycle.

How do I fix ECS.5?

  1. Set readonlyRootFilesystem to true in each container definition.
  2. Identify paths the app genuinely writes to (temp, cache, scratch) and back them with explicit read-write volume mounts.
  3. Register the new revision, redeploy, and confirm the workload still starts cleanly.

Remediation script · bash

# Inventory: flag containers running as root or with a writable root filesystem.
for fam in $(aws ecs list-task-definition-families --status ACTIVE \
    --query 'families[]' --output text); do
  aws ecs describe-task-definition --task-definition "$fam" \
    --query "taskDefinition.containerDefinitions[?user==null || user=='root' || user=='0' || readonlyRootFilesystem!=\`true\`].{Family:'$fam',Name:name,User:user,ReadOnly:readonlyRootFilesystem}" \
    --output text
done

# Harden at the source. Dockerfile:
#   RUN addgroup -S app && adduser -S -G app appuser
#   USER appuser
# Task definition: non-root user, read-only root with one narrow tmpfs, secrets via ARN.
#   "user": "1000:1000",
#   "readonlyRootFilesystem": true,
#   "mountPoints": [{ "sourceVolume": "scratch", "containerPath": "/tmp", "readOnly": false }],
#   "secrets": [{ "name": "DB_PASSWORD",
#     "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/checkout/db-AbCdEf" }]

# Register the hardened revision and roll it out (tasks only update on redeploy).
aws ecs register-task-definition --cli-input-json file://checkout-api-hardened.json
aws ecs update-service --cluster prod --service checkout-api \
  --task-definition checkout-api --force-new-deployment

Full walkthrough (console steps, edge cases and verification) in the lesson Harden ECS container workloads.

Is ECS.5 a false positive?

Teams assume an app that writes to /tmp can never be read-only. Mounting a writable tmpfs or volume at those specific paths satisfies the app while keeping the root filesystem read-only, so the control still passes.

Part of the learning path Lock down access
  • ECS.2 An ECS service auto-assigns public IPs to tasks
  • ECS.3 A task definition shares the host PID namespace
  • ECS.4 A container runs in privileged mode
  • ECS.8 Secrets are passed as plaintext container env vars
  • ECS.9 A task definition has no logging configuration
  • ECS.10 Fargate services should run latest platform version
  • ECS.12 ECS clusters should use Container Insights
  • ECS.16 An ECS task set auto-assigns public IPs
  • ECS.18 ECS task defs should encrypt EFS volumes in transit
  • ECS.19 Capacity providers managed termination protection
  • ECS.20 Linux containers should run as non-root users
  • ECS.21 Windows containers should run as non-admin users