AWS Container Security with ECS and EKS

AWS Container Security with ECS and EKS

Amazon Web Services offers two primary container orchestration services: Elastic Container Service (ECS) for AWS-native orchestration and Elastic Kubernetes Service (EKS) for managed Kubernetes. Both services integrate deeply with AWS security services, providing comprehensive security capabilities when properly configured. Understanding these integrations enables organizations to build secure, scalable container deployments.

ECS task definitions serve as security boundaries, defining IAM roles, network configurations, and resource limits. Task-level IAM roles provide fine-grained AWS permissions without embedding credentials. AWS Fargate eliminates host management, reducing attack surface but limiting some security controls. EC2-based ECS provides more control but requires host hardening. Choosing between Fargate and EC2 impacts security architecture decisions.

# Example: Secure ECS task definition with AWS security integrations
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Secure ECS Task Definition with comprehensive AWS security controls'

Parameters:
  Environment:
    Type: String
    Default: production
    AllowedValues: [development, staging, production]

Resources:
  # KMS key for encryption
  ContainerEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: KMS key for container encryption
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'
          - Sid: Allow ECS to use the key
            Effect: Allow
            Principal:
              Service:
                - ecs-tasks.amazonaws.com
                - logs.amazonaws.com
            Action:
              - 'kms:Decrypt'
              - 'kms:GenerateDataKey'
            Resource: '*'

  # Task execution role
  TaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
      Policies:
        - PolicyName: SecretAccess
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - 'secretsmanager:GetSecretValue'
                Resource:
                  - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${Environment}/*'
              - Effect: Allow
                Action:
                  - 'kms:Decrypt'
                Resource:
                  - !GetAtt ContainerEncryptionKey.Arn

  # Task role with minimal permissions
  TaskRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: 'sts:AssumeRole'
            Condition:
              StringEquals:
                'aws:SourceAccount': !Ref 'AWS::AccountId'
      Policies:
        - PolicyName: ApplicationPermissions
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - 's3:GetObject'
                  - 's3:PutObject'
                Resource:
                  - !Sub 'arn:aws:s3:::${Environment}-app-data/*'
              - Effect: Allow
                Action:
                  - 'dynamodb:GetItem'
                  - 'dynamodb:PutItem'
                  - 'dynamodb:Query'
                Resource:
                  - !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${Environment}-app-table'

  # Security group for containers
  ContainerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ECS containers
      VpcId: !ImportValue VPCId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          SourceSecurityGroupId: !ImportValue ALBSecurityGroupId
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
          Description: HTTPS outbound for API calls
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          DestinationSecurityGroupId: !ImportValue DatabaseSecurityGroupId
          Description: PostgreSQL database access

  # ECS Task Definition
  SecureTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub '${Environment}-secure-app'
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      Cpu: '512'
      Memory: '1024'
      ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
      TaskRoleArn: !GetAtt TaskRole.Arn
      ContainerDefinitions:
        - Name: secure-app
          Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Environment}-app:latest'
          Essential: true
          ReadonlyRootFilesystem: true
          User: '10001:10001'
          LinuxParameters:
            InitProcessEnabled: true
            Capabilities:
              Drop:
                - ALL
          Environment:
            - Name: ENVIRONMENT
              Value: !Ref Environment
            - Name: AWS_DEFAULT_REGION
              Value: !Ref 'AWS::Region'
          Secrets:
            - Name: DATABASE_URL
              ValueFrom: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${Environment}/database-url'
            - Name: API_KEY
              ValueFrom: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${Environment}/api-key'
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref 'AWS::Region'
              awslogs-stream-prefix: ecs
          HealthCheck:
            Command:
              - CMD-SHELL
              - 'wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1'
            Interval: 30
            Timeout: 5
            Retries: 3
            StartPeriod: 60
          MountPoints:
            - SourceVolume: tmp-volume
              ContainerPath: /tmp
            - SourceVolume: cache-volume
              ContainerPath: /app/cache
      Volumes:
        - Name: tmp-volume
          Host: {}
        - Name: cache-volume
          Host: {}

  # CloudWatch Log Group with encryption
  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/ecs/${Environment}-secure-app'
      RetentionInDays: 30
      KmsKeyId: !GetAtt ContainerEncryptionKey.Arn

  # EventBridge rule for security monitoring
  SecurityMonitoringRule:
    Type: AWS::Events::Rule
    Properties:
      Description: Monitor ECS task security events
      EventPattern:
        source:
          - aws.ecs
        detail-type:
          - ECS Task State Change
        detail:
          lastStatus:
            - STOPPED
          stoppedReason:
            - anything-but: "Scaling activity initiated by (deployment ecs-svc/*)"
      State: ENABLED
      Targets:
        - Arn: !ImportValue SecuritySNSTopicArn
          Id: "1"

EKS security leverages Kubernetes-native controls with AWS integrations. IAM roles for service accounts (IRSA) provide pod-level AWS permissions. AWS VPC CNI enables native VPC networking with security groups. AWS ALB ingress controller integrates with WAF for application protection. These integrations require careful configuration to maintain security while enabling functionality.