Posted May 4May 4 This blog post was authored by Olly Pomeroy, Senior Container Specialist Solution Architect at AWS. As organizations grow, enforcing governance and securing workloads becomes increasingly complex. Security and platform teams face the challenge of implementing consistent security controls, enforcing defaults, and applying best practices without slowing down application teams. Organizations need a comprehensive approach to security, governance, and compliance that can scale with their containerized workloads. Amazon Elastic Container Service (Amazon ECS) addresses these challenges through deep integration with AWS security and governance services. For security and access control, Amazon ECS integrates with AWS Identity and Access Management (AWS IAM), enabling organizations to control who has access to Amazon ECS APIs and what resources they can access. For governance and compliance, Amazon ECS customers can deploy with AWS CloudFormation, and secure with AWS Config and AWS Security Hub. In this blog post, we explore recent AWS ECS and AWS CloudFormation launches that help organizations secure their containerized workloads more effectively. IAM Context Keys AWS IAM policies control access to AWS resources. When you attach a policy to an identity or resource, it defines what actions they can perform and on which resources. Each time someone makes a request, AWS evaluates these policies to determine if the request should be allowed. AWS IAM JSON Policy contain an Action (the API call) and a Resource (a list of AWS resources to which the actions apply). IAM Policies can be scoped down further with a Condition and IAM context keys. For example, the policy below enforces that the IAM principal can create ECS Cluster’s but only if they have a tag owner:sharks. { "Effect": "Allow", "Resource": "*", "Action": [ "ecs:CreateCluster", "ecs:TagResource" ], "Condition": { "StringEquals": { "aws:RequestTag/owner": "sharks" } } } For more information, on enforcing tags on ECS resources using IAM policies see this previous blog post by Kriti. We recently expanded these capabilities by launching new IAM context keys for Amazon ECS resources. The new context keys can enforce standards when defining an ECS Task Definition and deploying an ECS service (whats new post). IAM context keys help organizations enforce best practices regardless of how they create resources, for example applying enforcement for both infrastructure as code and the AWS Command Line Interface (AWS CLI). Task Definition A workload is defined for Amazon ECS in an ECS Task Definition. With the new ECS IAM context keys, organizations can enforce the size of the Tasks developers can define in a Task Definition, a means of enforcing cost controls. Additionally, you can enforce a best practice that privileged containers cannot be defined in a Task Definition. The example below shows how an organization can create an IAM policy to restrict developers to only create 1 vCPU Tasks with either 2GB or 4GB of memory and no privileged containers. { "Effect": "Allow", "Resource": "*", "Action": [ "ecs:RegisterTaskDefinition" ], "Condition": { "Bool": { "ecs:privileged": "false" }, "NumericEquals": { "ecs:task-cpu": [ "1024" ], "ecs:task-memory": [ "2048", "4096" ] } } } ECS Service An ECS Service typically represents an application; identical ECS Tasks deployed from the same ECS Task Definition. When creating an ECS Service you define how the application should run, for example which VPC Subnets the ECS Task should use. Following the recent launch, you can now enforce more compliance controls on to an ECS Service. Restricting which subnets ECS Tasks can be deployed on to, whether a public IP address can be attached or not, and finally to mandate tags are propagated down. Propagating tags down to the ECS Task and leveraging ECS Managed Tasks, are required for granular billing in Cost and Usage Reports. The example policy below restricts ECS Services to specific subnets and enforces tag propagation, it also prevents these rules being overridden when a service is updated. { "Effect": "Allow", "Resource": "*", "Action": [ "ecs:CreateService", "ecs:UpdateService" ], "Condition": { "BoolIfExists": { "ecs:enable-ecs-managed-tags": "true" }, "StringEqualsIfExists": { "ecs:propagate-tags": "SERVICE" }, "ForAllValues:StringEquals": { "ecs:subnet": [ "subnet-0123456789", "subnet-9876543210" ] } } } For more information about the new ECS context keys, check out the documentation. Enforcing policies in Infrastructure as Code (IaC) Using IAM context keys to enforce standards and best practices provides a robust way to implement rules. However, it is common for enterprises to implement checks at multiple places. Implementing policies within infrastructure as code tools helps to shift enforcement left, closer to the development cycle. Application teams defining workloads in AWS CloudFormation or AWS Cloud Development Kit (AWS CDK) can take advantage of CloudFormation Hooks to enforce standards and best practices. CloudFormation Hooks are a proactive control step in the deployment process where customers can evaluate their configuration against policies. CloudFormation Hooks can prevent a resource from being created if any non-compliance is found. In late 2024, CloudFormation Hooks had a number of releases to extend its functionality, making it easier to define policies and support a wider range of use cases. In this blog post we explore Guard Hooks, and how they can enforce best practices to ECS resources. AWS CloudFormation Guard is an open-source, general-purpose, policy-as-code evaluation tool. The cfn-guard command line interface (CLI) provides a simple-to-use and declarative domain-specific language (DSL) that you can use to express policy as code. Previously you have been able to define policies and validate resources through the cfn-guard CLI on your workstation or in your pipeline, but following the Guard Hooks launch in CloudFormation, you can now enforce these policies at deployment time. There are a number of example CloudFormation Guard policies available on containersonaws.com, including examples to restrict privileged ECS Tasks and enforce non-blocking mode with the awslogs logging driver. In this blog post we provide a walk through on how to set up a new CloudFormation Guard Hook to enforce that all container images must be stored in Amazon Elastic Container Registry (Amazon ECR). Walk Through Pre-Requisites A workstation with access to the AWS CLI. The relevant AWS permissions to create Amazon Simple Storage Service (Amazon S3) Buckets, IAM Policies and CloudFormation Hooks. Implementing CloudFormation Guard Hooks 1. First we create an AWS CloudFormation template that contains: an S3 Bucket to hold the Gaurd Rules, an S3 bucket to store CloudFormation Hook logs, an IAM Rule for the CloudFormation service to access these buckets, and finally a CloudFormation Guard Hook. cat << EOF > hooks.cloudformation.yaml AWSTemplateFormatVersion: '2010-09-09' Resources: RulesBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub "\${AWS::StackName}-rules-bucket" HooksLogBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub "\${AWS::StackName}-logs-bucket" GuardHookRole: Type: AWS::IAM::Role Properties: RoleName: GuardHookExecutionRole AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: hooks.cloudformation.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: S3AccessPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion - s3:ListBucket Resource: - !Sub "arn:\${AWS::Partition}:s3:::\${RulesBucket}/*" - !Sub "arn:\${AWS::Partition}:s3:::\${RulesBucket}" - Effect: Allow Action: - s3:PutObject Resource: - !Sub "arn:\${AWS::Partition}:s3:::\${HooksLogBucket}/*" ECRImageHook: Type: AWS::CloudFormation::GuardHook Properties: Alias: Private::ECS::ContainerImage ExecutionRole: !GetAtt GuardHookRole.Arn FailureMode: FAIL HookStatus: ENABLED LogBucket: !Ref HooksLogBucket RuleLocation: Uri: !Sub s3://\${RulesBucket}/imagecheck.guard TargetOperations: - RESOURCE TargetFilters: Actions: - CREATE - UPDATE Outputs: RulesBucket: Value: !Ref RulesBucket Description: Bucket to hold the rules Export: Name: RuleBucket LogsBucket: Value: !Ref HooksLogBucket Description: Bucket to hold the logs Export: Name: LogBucket EOF To deploy this template with the AWS CLI: aws cloudformation create-stack \ --stack-name ecrimagehook \ --template-body file://hooks.cloudformation.yaml \ --capabilities CAPABILITY_NAMED_IAM 2. Next, we create a CloudFormation Guard rule that enforces that container images must come from Amazon ECR and that container images are not using the tag latest. REGION_NAME=$(aws configure get region) ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) cat << EOF > imagecheck.guard let task_defs = Resources.*[ Type == "AWS::ECS::TaskDefinition" ] rule CHECK_IMAGE_SOURCE { %task_defs.Properties { let containers = ContainerDefinitions[*] %containers { Image exists Image is_string Image == /^$ACCOUNT_ID\.dkr\.ecr\.$REGION_NAME\.amazonaws\.com\/.+$/ << "Container image must use ECR repository from this account and this region" >> Image != /^$ACCOUNT_ID\.dkr\.ecr\.$REGION_NAME\.amazonaws\.com\/.+:latest$/ << "Container image must not use the latest tag" >> } } } EOF We then copy this to the S3 bucket using the AWS CLI. BUCKET_NAME=$(aws cloudformation describe-stacks --stack-name ecrimagehook --query 'Stacks[0].Outputs[?OutputKey==`RulesBucket`].OutputValue' --output text) aws s3 cp imagecheck.guard s3://$BUCKET_NAME/imagecheck.guard 3. Thirdly, we define an ECS Task Definition with AWS CloudFormation. Within this ECS Task Definition we attempt to use a container image that is not stored in Amazon ECR. cat << EOF > task.cloudformation.yaml AWSTemplateFormatVersion: 2010-09-09 Description: ECS Task Definition CloudFormation Template Resources: TaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "\${AWS::StackName}-ecsTaskExecutionRole" AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ecs-tasks.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy Path: / TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: Family: !Sub "\${AWS::StackName}-taskdef" NetworkMode: awsvpc RequiresCompatibilities: - FARGATE Memory: 1024 Cpu: 512 ExecutionRoleArn: !Ref TaskExecutionRole ContainerDefinitions: - Name: containerone Image: !Sub "\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/demo:v1" - Name: containertwo Image: !Sub "\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/demo:latest" - Name: containerthree Image: mysecretrepository/demo:v1 EOF We can then deploy this stack using the AWS CLI. aws cloudformation create-stack \ --stack-name ecs-test \ --template-body file://task.cloudformation.yaml \ --capabilities CAPABILITY_NAMED_IAM 4. To verify that the deployment failed, you can visit the AWS CloudFormation Console or describe the Stack Events using the AWS CLI. aws cloudformation describe-stack-events --stack-name ecs-test --query 'StackEvents[?ResourceStatus==`CREATE_FAILED`].[LogicalResourceId, ResourceStatusReason]' [ [ "TaskDefinition", "The following hook(s) failed: [Private::ECS::ContainerImage]" ] ] You can also retrieve the Guard Hooks log from the previously created S3 Bucket. LOG_BUCKET_NAME=$(aws cloudformation describe-stacks --stack-name ecrimagehook --query 'Stacks[0].Outputs[?OutputKey==`LogsBucket`].OutputValue' --output text) S3_OBJECT_KEY=$(aws s3api list-objects --bucket $LOG_BUCKET_NAME --prefix cfn-guard-validate-report/AWS--ECS--TaskDefinition-TaskDefinition --query 'Contents[-1].Key' --output text) aws s3api \ get-object \ --bucket $LOG_BUCKET_NAME \ --key $S3_OBJECT_KEY \ logs.json cat logs.json | jq -r '.[0].not_compliant[0].Rule.checks | .[].Clause.Binary.messages' Clean Up To clean up the CloudFormation Guard Hook and the remaining resources run the following commands: RULE_BUCKET_NAME=$(aws cloudformation describe-stacks --stack-name ecrimagehook --query 'Stacks[0].Outputs[?OutputKey==`RulesBucket`].OutputValue' --output text) aws s3 rm --recursive s3://$RULE_BUCKET_NAME/ LOG_BUCKET_NAME=$(aws cloudformation describe-stacks --stack-name ecrimagehook --query 'Stacks[0].Outputs[?OutputKey==`LogsBucket`].OutputValue' --output text) aws s3 rm --recursive s3://$LOG_BUCKET_NAME/ aws cloudformation delete-stack --stack-name ecs-test aws cloudformation delete-stack --stack-name ecrimagehook Conclusion Amazon ECS provides powerful tools to enforce standards and best practices across your containerized workloads. The new IAM context keys enable you to implement consistent security controls at both the ECS Task Definition and ECS Service levels, ensuring compliance regardless of how your teams deploy applications. Additionally, AWS CloudFormation Guard hooks offer flexible, customizable rules to enforce your organization’s specific best practices during infrastructure deployment. Ready to strengthen your container security posture? Here’s how to get started: Explore the Amazon ECS documentation to learn more about implementing IAM context keys Visit the AWS CloudFormation Guard documentation to create Guard Hooks for your organization. View the full article
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.