Deny privileged container mode in Amazon ECS with CloudFormation Guard policy as code

Nathan Peck profile picture
Nathan Peck
Senior Developer Advocate at AWS

About

Amazon Elastic Container Service (ECS) is a container orchestrator that launches and manages container deployments on your behalf.

CloudFormation Guard is an open-source, general-purpose, policy-as-code evaluation tool. It helps you define policies that you can use to enforce best practice standards in your infrastructure as code.

This pattern will show you how to use CloudFormation Guard to enforce that developers who have access to deploy on your AWS account can't deploy Amazon ECS containers in privileged mode. Any CloudFormation infrastructure as code that defines a privileged container will be caught, and can be blocked in CI/CD process prior to deployment.

Why?

A privileged container is missing most of the normal protections that are used to keep containers isolated from each other and isolated from the underlying host. This is because the privileged container is inheriting the capabilities of the container runtime process that launched it. That parent process is typically going to have quite a lot of privileges, because container runtimes need to hook into many of the operating system fundamentals in order to setup a container.

Generally speaking you should not launch a container with a privileged mode turned on unless you have a very specific use case that requires it.

Dependencies

This pattern uses CloudFormation Guard, which can be installed with the following command:

Language: sh
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.sh | sh
export PATH=~/.guard/bin:$PATH
cfn-guard --version

You can also see the install instructions for other systems.

CloudFormation Guard Rule

File: privileged-tasks.guardLanguage: guard

let task_defs = Resources.*[Type == 'AWS::ECS::TaskDefinition']

#
# Verify that ECS tasks are not being given privileged mode
#
rule tasks_denied_privileged_condition {
  when %task_defs !empty {
    %task_defs.Properties.ContainerDefinitions[*] {
      Privileged !exists or Privileged != true
    }
  }
}

Sample Templates

The following sample CloudFormation templates can be used to verify that this rule works:

  • Non-privileged ECS task
  • Privileged ECS task
File: safe-task-def.ymlLanguage: yml
AWSTemplateFormatVersion: '2010-09-09'
Description: An example task definition with no special privileges
Resources:
  SafeTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: alpine
      Cpu: 256
      Memory: 128
      ContainerDefinitions:
        - Name: alpine
          Image: public.ecr.aws/docker/library/alpine:latest
          Essential: true

  OtherSafeTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: alpine
      Cpu: 256
      Memory: 128
      ContainerDefinitions:
        - Name: alpine
          Image: public.ecr.aws/docker/library/alpine:latest
          Essential: true
          Privileged: false

Usage

You can validate the sample CloudFormation templates against the CloudFormation guard rule using the following command:

Language: sh
cfn-guard validate --data *.yml --rules .

You will see output similar to this:

Language: txt
privileged-task-def.yml Status = FAIL
FAILED rules
privileged-tasks.guard/tasks_denied_privileged_condition    FAIL
---
Evaluating data privileged-task-def.yml against rules privileged-tasks.guard
Number of non-compliant resources 1
Resource = PrivilegedTaskDefinition {
  Type      = AWS::ECS::TaskDefinition
  Rule = tasks_denied_privileged_condition {
    ALL {
      ANY {
        Check =  Privileged not EXISTS   {
          ComparisonError {
            Error            = Check was not compliant as property [/Resources/PrivilegedTaskDefinition/Properties/ContainerDefinitions/0/Privileged[L:14,C:22]] existed.
            PropertyPath    = /Resources/PrivilegedTaskDefinition/Properties/ContainerDefinitions/0/Privileged[L:14,C:22]
            Operator        = NOT EXISTS
            Code:
                 12.        - Name: alpine
                 13.          Image: public.ecr.aws/docker/library/alpine:latest
                 14.          Essential: true
                 15.          Privileged: true

          }
        }
        Check =  Privileged not EQUALS  true {
          ComparisonError {
            Error            = Check was not compliant as property value [Path=/Resources/PrivilegedTaskDefinition/Properties/ContainerDefinitions/0/Privileged[L:14,C:22] Value=true] equal to value [Path=[L:0,C:0] Value=true].
            PropertyPath    = /Resources/PrivilegedTaskDefinition/Properties/ContainerDefinitions/0/Privileged[L:14,C:22]
            Operator        = NOT EQUAL
            Value           = true
            ComparedWith    = true
            Code:
                 12.        - Name: alpine
                 13.          Image: public.ecr.aws/docker/library/alpine:latest
                 14.          Essential: true
                 15.          Privileged: true

          }
        }
      }
    }
  }
}

The cfn-guard process will also exit with a non-zero exit code. In a typical CI/CD server, this exceptional exit will stop the release process, and return an error. This allows you to use CloudFormation guard as a gate that blocks privileged containers from being released to your infrastructure.

See Also

More policy as code patterns: