← Back to DevBytes

Fargate Security: IAM Policies and Network Security

Introduction to Fargate Security

AWS Fargate is a serverless compute engine for containers that works with both Amazon ECS and Amazon EKS. When you run containers on Fargate, AWS manages the underlying infrastructure—you don't see or control the EC2 instances. This abstraction brings tremendous operational benefits, but it also shifts the security model in important ways. Security in Fargate revolves around two primary pillars: IAM Policies (who and what your containers can access) and Network Security (how your containers communicate with the world and each other). Understanding both is essential to running secure, production-grade container workloads.

Why Fargate Security Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

With Fargate, you relinquish direct control over the host OS and instance-level firewall rules. This means traditional host-based security approaches no longer apply. Instead, security becomes entirely identity-based and network-based. A misconfigured IAM role can grant a compromised container access to your entire AWS account. An improperly configured security group can expose your internal services to the internet. Given that containers often run microservices handling sensitive data, the blast radius of a single vulnerability can be catastrophic. Properly configuring IAM policies and network controls is not optional—it is the foundation of your defense-in-depth strategy on Fargate.

IAM Policies for Fargate: The Two-Role Model

Fargate uses two distinct IAM roles per task, and confusing them is a common source of security issues:

Separating these two concerns follows the principle of least privilege and limits the damage if application code is compromised.

Task Execution Role Example

Below is a minimal but complete task execution role policy. It allows Fargate to pull images from ECR and write logs to CloudWatch. Notice that it does not grant access to sensitive data stores like DynamoDB or S3—that belongs to the task role.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}

Attach this policy to a role named something like fargate-task-execution-role. You can also add statements for Secrets Manager if your tasks reference sensitive environment variables stored there.

Task Role Example: Least Privilege for Application Code

Your application code likely needs to interact with other AWS services. Here is an example task role policy for a container that reads from an S3 bucket and writes to a DynamoDB table. Notice the use of specific resource ARNs rather than wildcards:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-bucket",
        "arn:aws:s3:::my-app-bucket/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:UpdateItem",
        "dynamodb:Query"
      ],
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable"
    }
  ]
}

By scoping permissions to exact resources, you ensure that even if an attacker gains code execution within the container, they cannot enumerate or access other buckets or tables in your account.

Attaching Roles in a Task Definition

In your ECS task definition JSON, you reference both roles. Here's a snippet showing how:

{
  "family": "my-app-task",
  "networkMode": "awsvpc",
  "executionRoleArn": "arn:aws:iam::123456789012:role/fargate-task-execution-role",
  "taskRoleArn": "arn:aws:iam::123456789012:role/fargate-task-role",
  "containerDefinitions": [
    {
      "name": "my-app-container",
      "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
      "essential": true,
      "memory": 512,
      "cpu": 256,
      "environment": [
        {
          "name": "BUCKET_NAME",
          "value": "my-app-bucket"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/my-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

The executionRoleArn handles setup, and taskRoleArn is what your application uses at runtime. Always verify that sensitive environment variables and secrets are not accessible via the task execution role alone if they are meant for application use.

Using Secrets Manager with Fargate

When injecting secrets, you must grant the task execution role permission to retrieve the secret, and then reference it in the container definition. Here is an extended task execution role policy snippet and the corresponding container definition update:

// Additional statement for task execution role:
{
  "Effect": "Allow",
  "Action": [
    "secretsmanager:GetSecretValue",
    "kms:Decrypt"
  ],
  "Resource": [
    "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-app-secret-*",
    "arn:aws:kms:us-east-1:123456789012:key/your-kms-key-id"
  ]
}

// Container definition secrets block:
"secrets": [
  {
    "name": "DATABASE_PASSWORD",
    "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-app-secret-db"
  }
]

This ensures secrets are injected as environment variables at container startup without hardcoding them in the task definition. The task execution role must have decrypt permission on the KMS key if the secret is encrypted with a customer-managed key.

Network Security for Fargate Tasks

Fargate tasks always run in the awsvpc network mode. Each task gets its own elastic network interface (ENI) with a private IP address. This model provides powerful isolation and fine-grained network control, but it requires deliberate configuration.

VPC, Subnets, and Security Groups

Your Fargate tasks must run inside a VPC. You specify which subnets and security groups to associate with each task. The fundamental decision is whether to place tasks in public or private subnets:

Security Group Configuration

Security groups act as a stateful virtual firewall for each ENI. Here is how to set up a typical three-tier architecture using CloudFormation snippet to demonstrate the security group rules:

# ALB Security Group (public facing)
ALBSecurityGroup:
  Type: AWS::EC2::SecurityGroup
  Properties:
    GroupDescription: Allow HTTP and HTTPS from anywhere
    VpcId: !Ref VPCId
    SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 443
        ToPort: 443
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0

# Fargate Service Security Group
FargateServiceSG:
  Type: AWS::EC2::SecurityGroup
  Properties:
    GroupDescription: Allow traffic from ALB only
    VpcId: !Ref VPCId
    SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 8080
        ToPort: 8080
        SourceSecurityGroupId: !Ref ALBSecurityGroup
    SecurityGroupEgress:
      - IpProtocol: tcp
        FromPort: 3306
        ToPort: 3306
        DestinationSecurityGroupId: !Ref DatabaseSG

# Database Security Group
DatabaseSG:
  Type: AWS::EC2::SecurityGroup
  Properties:
    GroupDescription: Allow MySQL from Fargate service only
    VpcId: !Ref VPCId
    SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 3306
        ToPort: 3306
        SourceSecurityGroupId: !Ref FargateServiceSG

This configuration ensures that the ALB accepts internet traffic on ports 80/443, the Fargate service only accepts traffic from the ALB on port 8080, and the database only accepts traffic from the Fargate service on port 3306. No other ingress paths exist.

Network ACLs as a Secondary Boundary

Network ACLs (NACLs) are stateless and act at the subnet level. While security groups are the primary control, NACLs can provide an additional layer of defense. For example, you might configure a NACL on your private subnet to explicitly deny outbound traffic to known malicious IP ranges or restrict ephemeral port ranges:

# Example NACL for a private subnet
PrivateSubnetNACL:
  Type: AWS::EC2::NetworkAcl
  Properties:
    VpcId: !Ref VPCId
    Tags:
      - Key: Name
        Value: private-subnet-nacl

# Inbound rule: Allow only from VPC CIDR
PrivateSubnetNACLInbound:
  Type: AWS::EC2::NetworkAclEntry
  Properties:
    NetworkAclId: !Ref PrivateSubnetNACL
    RuleNumber: 100
    Protocol: -1
    RuleAction: allow
    CidrBlock: 10.0.0.0/16
    PortRange:
      From: 0
      To: 65535

# Outbound rule: Allow only to VPC CIDR and specific required endpoints
PrivateSubnetNACLOutbound:
  Type: AWS::EC2::NetworkAclEntry
  Properties:
    NetworkAclId: !Ref PrivateSubnetNACL
    RuleNumber: 100
    Protocol: -1
    RuleAction: allow
    CidrBlock: 10.0.0.0/16
    PortRange:
      From: 0
      To: 65535

# Explicit deny for all other traffic (default rule)
# Additional explicit rules can block specific CIDRs

Remember that NACLs are stateless—you must allow both inbound and outbound ephemeral ports for return traffic. Security groups handle this automatically, which is why they are preferred as the primary mechanism.

VPC Endpoints for AWS Services

Fargate tasks in private subnets need access to AWS APIs (ECR, CloudWatch, S3, etc.). Without a NAT Gateway, they cannot reach the public endpoints. VPC endpoints bring AWS services directly into your VPC over the AWS backbone, eliminating internet exposure entirely. Here is how to create endpoints for ECR and CloudWatch (required for Fargate to function) and S3:

# VPC Endpoints for Fargate essential services
ECREndpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    VpcId: !Ref VPCId
    ServiceName: com.amazonaws.us-east-1.ecr.api
    VpcEndpointType: Interface
    SubnetIds: !Ref PrivateSubnetIds
    SecurityGroupIds: [!Ref VpcEndpointSG]
    PrivateDnsEnabled: true

ECRDockerEndpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    VpcId: !Ref VPCId
    ServiceName: com.amazonaws.us-east-1.ecr.dkr
    VpcEndpointType: Interface
    SubnetIds: !Ref PrivateSubnetIds
    SecurityGroupIds: [!Ref VpcEndpointSG]
    PrivateDnsEnabled: true

CloudWatchEndpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    VpcId: !Ref VPCId
    ServiceName: com.amazonaws.us-east-1.logs
    VpcEndpointType: Interface
    SubnetIds: !Ref PrivateSubnetIds
    SecurityGroupIds: [!Ref VpcEndpointSG]
    PrivateDnsEnabled: true

S3Endpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    VpcId: !Ref VPCId
    ServiceName: com.amazonaws.us-east-1.s3

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles