Skip to main content
emnode / learn
Compliance High severity

AWS Security Hub · ECS

ECS.16: An ECS task set auto-assigns public IPs

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub ECS.16 check?

ECS.16 checks the networkConfiguration of an ECS task set — the object created per variant during EXTERNAL or blue-green deployments — and fails when its assignPublicIp is ENABLED. It passes when the flag is DISABLED.

Why does ECS.16 matter?

A task set with a public IP in a public subnet gives every task a routable address, so a backend container that should only be reachable through an internal load balancer becomes directly addressable from the internet. Any admin port, debug endpoint, or unauthenticated metrics route is then exposed to internet-wide scanning. Because ECS.16 maps to PCI DSS 1.4.4, it is also a control gap an assessor will write up.

How do I fix ECS.16?

  1. Set assignPublicIp to DISABLED in the task set's awsvpcConfiguration.
  2. Launch task sets into private subnets with NAT or VPC endpoints for any required egress.
  3. Recreate the affected task set if the flag cannot be changed in place, then verify the blue-green cutover.

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

Teams harden the ECS service and security groups but never look at the task set. The task set carries its own network configuration during a blue-green cutover, so ECS.16 is a separate control from ECS.2 and must be fixed independently.

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.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.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