Skip to main content
emnode / learn
Compliance High severity

AWS Security Hub · ECS

ECS.4: A container runs in privileged mode

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub ECS.4 check?

ECS.4 looks at the latest active revision of each task definition and fails if any container definition has privileged set to true. A privileged container is given elevated, root-like access to the host container instance; the secure state is privileged false or absent.

Why does ECS.4 matter?

A non-privileged container that gets compromised is, at worst, a compromised application fenced inside its namespaces. A privileged one that gets compromised is a compromised host — the attacker can tamper with every other task on the instance, read the instance metadata service, and pivot into the account. The same vulnerability has a wildly different cost depending on this one boolean.

How do I fix ECS.4?

  1. Set privileged to false (or remove it) in every container definition and register the new revision.
  2. Where a workload needs specific kernel features, grant only the minimal Linux capabilities via linuxParameters instead of full privilege.
  3. Add a guardrail so new revisions cannot reintroduce privileged: true.

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.4 a false positive?

ECS.4 is distinct from ECS.20: dropping to a non-root user inside the container does not satisfy it. privileged is a host-level escape hatch that must be false regardless of the container user.

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.5 A container has a writable root filesystem
  • 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