Skip to main content

ECS Task Definitions Have Containers with Read-Only Root Filesystems

Overview

This check verifies that all containers in your Amazon ECS task definitions have read-only root filesystems enabled. When a container's root filesystem is read-only, it cannot modify its own system files at runtime, which significantly reduces the attack surface.

Risk

A writable root filesystem creates serious security vulnerabilities:

  • Malware installation: Attackers can download and install malicious software
  • Binary tampering: System binaries can be modified to create backdoors
  • Persistence: Attackers can modify files to maintain access after restarts
  • Data exfiltration: Sensitive files like logs or secrets could be accessed or modified

Making the root filesystem read-only prevents these attacks by ensuring containers run in an immutable state.

Remediation Steps

Prerequisites

You need:

  • Access to the AWS Console with permissions to modify ECS task definitions
  • Knowledge of which directories your application needs to write to (commonly /tmp, /var/run, or application-specific paths)
CLI and IaC tool setup

For command-line remediation:

  • AWS CLI v2 installed and configured
  • Appropriate IAM permissions for ecs:RegisterTaskDefinition and ecs:DescribeTaskDefinition

For infrastructure as code:

  • Terraform 1.0+ with AWS provider 5.0+
  • Or AWS CloudFormation access

AWS Console Method

  1. Open the Amazon ECS console at https://console.aws.amazon.com/ecs/
  2. In the left navigation, click Task definitions
  3. Select the task definition family you need to update
  4. Click Create new revision
  5. Scroll down to the Container definitions section
  6. Click on the container name to expand its settings
  7. Under Storage and Logging, find Read only root file system
  8. Toggle this setting to On
  9. If your application needs to write to specific directories:
    • Scroll to the Storage section at the task level
    • Add a volume (e.g., name it tmp-volume)
    • Back in the container settings, under Mount points, add a mount:
      • Source volume: tmp-volume
      • Container path: /tmp
      • Read only: Leave unchecked
  10. Repeat steps 6-9 for each container in the task definition
  11. Click Create to register the new revision
  12. Update any services using this task definition to use the new revision
AWS CLI

Step 1: Get the current task definition

aws ecs describe-task-definition \
--task-definition <your-task-family> \
--region us-east-1 \
--query 'taskDefinition' > task-def.json

Step 2: Modify the container definitions

Edit task-def.json to add "readonlyRootFilesystem": true to each container, and add volume mounts for directories that need write access:

{
"containerDefinitions": [
{
"name": "app-container",
"image": "your-image:tag",
"readonlyRootFilesystem": true,
"mountPoints": [
{
"sourceVolume": "tmp-volume",
"containerPath": "/tmp",
"readOnly": false
}
]
}
],
"volumes": [
{
"name": "tmp-volume"
}
]
}

Step 3: Remove read-only fields before registering

The describe output includes fields that cannot be included when registering. Remove these fields:

  • taskDefinitionArn
  • revision
  • status
  • requiresAttributes
  • compatibilities
  • registeredAt
  • registeredBy

Step 4: Register the new task definition

aws ecs register-task-definition \
--cli-input-json file://task-def.json \
--region us-east-1

Step 5: Update services (if applicable)

aws ecs update-service \
--cluster <your-cluster> \
--service <your-service> \
--task-definition <your-task-family> \
--region us-east-1
CloudFormation
AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Task Definition with read-only root filesystem

Parameters:
TaskFamily:
Type: String
Description: The family name for the task definition
Default: my-secure-task
ContainerImage:
Type: String
Description: The container image to use
Default: nginx:latest

Resources:
ECSTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref TaskFamily
RequiresCompatibilities:
- FARGATE
NetworkMode: awsvpc
Cpu: '256'
Memory: '512'
ContainerDefinitions:
- Name: app-container
Image: !Ref ContainerImage
ReadonlyRootFilesystem: true
Essential: true
PortMappings:
- ContainerPort: 80
Protocol: tcp
MountPoints:
- SourceVolume: tmp-volume
ContainerPath: /tmp
ReadOnly: false
- SourceVolume: var-run-volume
ContainerPath: /var/run
ReadOnly: false
Volumes:
- Name: tmp-volume
- Name: var-run-volume

Outputs:
TaskDefinitionArn:
Description: The ARN of the task definition
Value: !Ref ECSTaskDefinition

Deploy with:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name ecs-secure-task \
--parameter-overrides TaskFamily=my-secure-task ContainerImage=nginx:latest \
--region us-east-1
Terraform
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "us-east-1"
}

variable "task_family" {
description = "The family name for the task definition"
type = string
default = "my-secure-task"
}

variable "container_image" {
description = "The container image to use"
type = string
default = "nginx:latest"
}

resource "aws_ecs_task_definition" "secure_task" {
family = var.task_family
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "256"
memory = "512"

container_definitions = jsonencode([
{
name = "app-container"
image = var.container_image
readonlyRootFilesystem = true
essential = true
portMappings = [
{
containerPort = 80
protocol = "tcp"
}
]
mountPoints = [
{
sourceVolume = "tmp-volume"
containerPath = "/tmp"
readOnly = false
},
{
sourceVolume = "var-run-volume"
containerPath = "/var/run"
readOnly = false
}
]
}
])

volume {
name = "tmp-volume"
}

volume {
name = "var-run-volume"
}
}

output "task_definition_arn" {
description = "The ARN of the task definition"
value = aws_ecs_task_definition.secure_task.arn
}

Apply with:

terraform init
terraform apply

Verification

After updating your task definition:

  1. Go to ECS > Task definitions in the AWS Console
  2. Select your task definition and click on the latest revision
  3. Expand each container definition
  4. Confirm that Read only root file system shows as Yes or true
CLI verification
aws ecs describe-task-definition \
--task-definition <your-task-family> \
--region us-east-1 \
--query 'taskDefinition.containerDefinitions[*].{Name:name,ReadOnlyRootFilesystem:readonlyRootFilesystem}'

Expected output shows true for all containers:

[
{
"Name": "app-container",
"ReadOnlyRootFilesystem": true
}
]

Additional Resources

Notes

  • Application compatibility: Some applications expect to write to the root filesystem. Test thoroughly in a non-production environment before applying this change to production workloads.

  • Common writable paths: Applications often need write access to:

    • /tmp - Temporary files
    • /var/run - Runtime data (PID files, sockets)
    • /var/log - Log files (consider using CloudWatch Logs instead)
    • Application-specific data directories
  • Layered security: For maximum protection, combine read-only root filesystems with:

    • Running containers as non-root users
    • Dropping unnecessary Linux capabilities
    • Using immutable container images from trusted registries
  • Existing services: After registering a new task definition revision, you must update any running services to use the new revision. The old tasks will continue running until replaced.