Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"severity": "HIGH",
"category": "Encryption",
"descriptionText": "Ensure that AWS ECS clusters are encrypted. Data encryption at rest, prevents unauthorized users from accessing sensitive data on your AWS ECS clusters and associated cache storage systems.",
"descriptionUrl": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html",
"descriptionUrl": "https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-instance.html#cfn-ec2-instance-blockdevicemappings",
"platform": "CloudFormation",
"descriptionID": "512ea20d",
"cloudProvider": "aws",
"cwe": "311"
"cwe": "312"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,130 @@ package Cx
import data.generic.common as common_lib
import data.generic.cloudformation as cf_lib


CxPolicy[result] {
# Case of undefined field(s) during path checking - EC2::LaunchTemplate
resource := input.document[i].Resources
elem := resource[key]
elem.Type == "AWS::ECS::Service"
elem.Properties.Cluster
taskdefinitionkey := getTaskDefinitionName(elem)
taskDefinition := resource[taskdefinitionkey]
elem.Type == "AWS::EC2::LaunchTemplate"
template_data := elem.Properties.LaunchTemplateData
path := check_valid_path(template_data,key)
not path.value

result := {
"documentId": input.document[i].id,
"resourceType": elem.Type,
"resourceName": cf_lib.get_resource_name(resource, key),
"searchKey": sprintf("Resources.%s.Properties.LaunchTemplateData%s", [key,path.path_tail]),
"issueType": "MissingAttribute",
"keyExpectedValue": sprintf("Resources.%s.Properties.LaunchTemplateData.BlockDeviceMappings.Ebs.Encrypted should be defined and true", [key]),
"keyActualValue": sprintf("%s is not defined.", [path.missing_resource]),
"searchLine": common_lib.build_search_line(path.searchLine,[]),
}
}

count(taskDefinition.Properties.ContainerDefinitions) > 0
res := is_transit_encryption_disabled(taskDefinition, taskdefinitionkey)
CxPolicy[result] {
# Case of "encrypted" defined but set to false - EC2::LaunchTemplate
resource := input.document[i].Resources
elem := resource[key]
elem.Type == "AWS::EC2::LaunchTemplate"
template_data := elem.Properties.LaunchTemplateData
path := check_valid_path(template_data,key)
path.value

cf_lib.isCloudFormationFalse(template_data.BlockDeviceMappings[path.index].Ebs.Encrypted)

result := {
"documentId": input.document[i].id,
"resourceType": elem.Type,
"resourceName": cf_lib.get_resource_name(resource, key),
"searchKey": sprintf("Resources.%s.Properties.Volumes", [taskdefinitionkey]),
"issueType": res["issueT"],
"keyExpectedValue": res["kev"],
"keyActualValue": res["kav"],
"searchKey": sprintf("Resources.%s.Properties.LaunchTemplateData.BlockDeviceMappings.Ebs.Encrypted", [key]),
"issueType": "IncorrectValue",
"keyExpectedValue": sprintf("Resources.%s.Properties.LaunchTemplateData.BlockDeviceMappings.Ebs.Encrypted should be defined and true", [key]),
"keyActualValue": "Encrypted is set to false.",
"searchLine": common_lib.build_search_line(path.searchLine,[]),
}
}

CxPolicy[result] {
CxPolicy[result] {
# Case of undefined field(s) during path checking - EC2::Instance
resource := input.document[i].Resources
elem := resource[key]
elem.Type == "AWS::ECS::Service"
elem.Properties.Cluster
taskdefinitionkey := getTaskDefinitionName(elem)
not common_lib.valid_key(resource, taskdefinitionkey)
elem.Type == "AWS::EC2::Instance"
template_data := elem.Properties
path := check_valid_path(template_data,key)
not path.value

searchLine := [x | x := path.searchLine[_]; x != "LaunchTemplateData"]

result := {
"documentId": input.document[i].id,
"resourceType": elem.Type,
"resourceName": cf_lib.get_resource_name(resource, key),
"searchKey": sprintf("Resources.%s", [taskdefinitionkey]),
"resourceName": cf_lib.get_resource_name(resource, key),
"searchKey": sprintf("Resources.%s.Properties%s", [key,path.path_tail]),
"issueType": "MissingAttribute",
"keyExpectedValue": sprintf("Resources.%s should be defined", [taskdefinitionkey]),
"keyActualValue": sprintf("Resources.%s is not defined.", [taskdefinitionkey]),
"keyExpectedValue": sprintf("Resources.%s.Properties.BlockDeviceMappings.Ebs.Encrypted should be defined and true", [key]),
"keyActualValue": sprintf("%s is not defined.", [path.missing_resource]),
"searchLine": common_lib.build_search_line(searchLine,[]),
}
}

is_transit_encryption_disabled(taskDefinition, taskdefinitionkey) = res {
volume := taskDefinition.Properties.Volumes[j]
common_lib.valid_key(volume.EFSVolumeConfiguration, "TransitEncryption")
volume.EFSVolumeConfiguration.TransitEncryption == "DISABLED"
res := {
"issueT": "IncorrectValue",
"kev": sprintf("Resources.%s.Properties.Volumes[%d].EFSVolumeConfiguration.TransitEncryption should be enabled", [taskdefinitionkey, j]),
"kav": sprintf("Resources.%s.Properties.Volumes[%d].EFSVolumeConfiguration.TransitEncryption is disabled", [taskdefinitionkey, j]),
}
} else = res {
volume := taskDefinition.Properties.Volumes[j]
efsVolumeConfiguration := volume.EFSVolumeConfiguration
not common_lib.valid_key(efsVolumeConfiguration, "TransitEncryption")
res := {
"issueT": "MissingAttribute",
"kev": sprintf("Resources.%s.Properties.Volumes[%d].EFSVolumeConfiguration.TransitEncryption should be defined", [taskdefinitionkey, j]),
"kav": sprintf("Resources.%s.Properties.Volumes[%d].EFSVolumeConfiguration.TransitEncryption is not defined (set to DISABLED by default)", [taskdefinitionkey, j]),
}
}
CxPolicy[result] {
# Case of "encrypted" defined but set to false - EC2::Instance
resource := input.document[i].Resources
elem := resource[key]
elem.Type == "AWS::EC2::Instance"
template_data := elem.Properties
path := check_valid_path(template_data,key)
path.value

getTaskDefinitionName(resource) := name {
name := resource.Properties.TaskDefinition
not common_lib.valid_key(name, "Ref")
} else := name {
name := resource.Properties.TaskDefinition.Ref
cf_lib.isCloudFormationFalse(template_data.BlockDeviceMappings[path.index].Ebs.Encrypted)

searchLine := [x | x := path.searchLine[_]; x != "LaunchTemplateData"]

result := {
"documentId": input.document[i].id,
"resourceType": elem.Type,
"resourceName": cf_lib.get_resource_name(resource, key),
"searchKey": sprintf("Resources.%s.Properties.BlockDeviceMappings.Ebs.Encrypted", [key]),
"issueType": "IncorrectValue",
"keyExpectedValue": sprintf("Resources.%s.Properties.BlockDeviceMappings.Ebs.Encrypted should be defined and true", [key]),
"keyActualValue": "Encrypted is set to false.",
"searchLine": common_lib.build_search_line(searchLine,[]),
}
}

check_valid_path(template_data,key) = path {
common_lib.valid_key(template_data.BlockDeviceMappings[i].Ebs,"Encrypted")
path := {
"value": true,
"searchLine": ["Resources",key,"Properties","LaunchTemplateData","BlockDeviceMappings",i,"Ebs","Encrypted"],
"path_tail": sprintf(".BlockDeviceMappings[%d].Ebs.Encrypted",[i]),
"index": i
}
} else = path {
common_lib.valid_key(template_data.BlockDeviceMappings[i],"Ebs")
path := {
"value": false,
"searchLine": ["Resources",key,"Properties","LaunchTemplateData","BlockDeviceMappings",i,"Ebs"],
"path_tail": sprintf(".BlockDeviceMappings[%d].Ebs.Encrypted",[i]),
"missing_resource": "Encrypted"
}
} else = path {
common_lib.valid_key(template_data,"BlockDeviceMappings")
path := {
"value": false,
"searchLine": ["Resources",key,"Properties","LaunchTemplateData","BlockDeviceMappings"],
"path_tail": ".BlockDeviceMappings[x].Ebs",
"missing_resource": "Ebs"
}
} else = path {
path := {
"value": false,
"searchLine": ["Resources",key,"Properties","LaunchTemplateData"],
"path_tail": ".BlockDeviceMappings",
"missing_resource": "BlockDeviceMappings"
}
}


Original file line number Diff line number Diff line change
@@ -1,133 +1,19 @@
AWSTemplateFormatVersion: 2010-09-09
Description: Creating ECS service
Parameters:
AppName:
Type: String
Description: Name of app requiring ELB exposure
Default: simple-app
AppContainerPort:
Type: Number
Description: Container port of app requiring ELB exposure
Default: '80'
AppHostPort:
Type: Number
Description: Host port of app requiring ELB exposure
Default: '80'
ServiceName:
Type: String
LoadBalancerName:
Type: String
HealthCheckGracePeriodSeconds:
Type: String
Resources:
cluster:
Type: AWS::ECS::Cluster
taskdefinition:
Type: AWS::ECS::TaskDefinition
ECSLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
ContainerDefinitions:
- Name: !Ref AppName
MountPoints:
- SourceVolume: my-vol
ContainerPath: /var/www/my-vol
Image: amazon/amazon-ecs-sample
Cpu: '10'
PortMappings:
- ContainerPort: !Ref AppContainerPort
HostPort: !Ref AppHostPort
EntryPoint:
- /usr/sbin/apache2
- '-D'
- FOREGROUND
Memory: '500'
Essential: true
- Name: busybox
Image: busybox
Cpu: '10'
EntryPoint:
- sh
- '-c'
Memory: '500'
Command:
- >-
/bin/sh -c "while true; do /bin/date > /var/www/my-vol/date; sleep
1; done"
Essential: false
VolumesFrom:
- SourceContainer: !Ref AppName
Volumes:
- Host:
SourcePath: /var/lib/docker/vfs/dir/
Name: my-vol
EFSVolumeConfiguration:
TransitEncryption: ENABLED
TransitEncryptionPort: 8080
LaunchTemplateName: ECS-Encrypted-LT
LaunchTemplateData:
ImageId: ami-xxxxxxxxxxxxxx # ECS-optimized AMI
InstanceType: t3.micro
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 30
VolumeType: gp2
Encrypted: true
UserData:
Fn::Base64: !Sub |
#!/bin/bash
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config

service:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref cluster
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
DesiredCount: 0
HealthCheckGracePeriodSeconds: !Ref HealthCheckGracePeriodSeconds
LoadBalancers:
- ContainerName: !Ref AppName
ContainerPort: !Ref AppContainerPort
LoadBalancerName: !Ref elb
PlacementStrategies:
- Type: binpack
Field: memory
- Type: spread
Field: host
PlacementConstraints:
- Type: memberOf
Expression: 'attribute:ecs.availability-zone != us-east-1d'
- Type: distinctInstance
TaskDefinition: !Ref taskdefinition
ServiceName: !Ref ServiceName
Role: !Ref Role
elb:
Type: AWS::ElasticLoadBalancing::LoadBalancer
Properties:
LoadBalancerName: !Ref LoadBalancerName
Listeners:
- InstancePort: !Ref AppHostPort
LoadBalancerPort: '80'
Protocol: HTTP
Subnets:
- !Ref Subnet1
DependsOn: GatewayAttachment
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/24
Subnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/25
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2008-10-17
Statement:
- Sid: ''
Effect: Allow
Principal:
Service: ecs.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole'
Outputs:
Cluster:
Value: !Ref cluster
Loading
Loading