# ################################### ## Rule Specification ## ##################################### # # Rule Identifier: # cloudfront_origin_access_identity_enabled_check # # Description: # This control checks whether Amazon CloudFront distributions backed by Amazon S3 are configured with an origin access identity. # # Reports on: # AWS::CloudFront::Distribution # # Evaluates: # AWS CloudFormation, AWS CloudFormation hook # # Rule Parameters: # None # # Scenarios: # Scenario: 1 # Given: The input document is an AWS CloudFormation or CloudFormation hook document # And: The input document does not contain any CloudFront distribution resources # Then: SKIP # Scenario: 2 # Given: The input document is an AWS CloudFormation or CloudFormation hook document # And: The input document contains a CloudFront distribution resource # And: No S3 backed 'Origins' are provided on the CloudFront distribution resource or 'Origins' is not present on # the CloudFront distribution resource or is present and an empty list # Then: SKIP # Scenario: 3 # Given: The input document is an AWS CloudFormation or CloudFormation hook document # And: The input document contains a CloudFront distribution resource # And: 'S3Origin' is present on the CloudFront distribution resource # Then: FAIL # Scenario: 4 # Given: The input document is an AWS CloudFormation or CloudFormation hook document # And: The input document contains a CloudFront distribution resource # And: One or more S3 backed 'Origins' are configured on the CloudFront distribution resource # And: 'OriginAccessIdentity' is not present or is an empty string in the 'S3OriginConfig' property or invalid # local reference # Then: FAIL # Scenario: 5 # Given: The input document is an AWS CloudFormation or CloudFormation hook document # And: The input document contains a CloudFront distribution resource # And: One or more S3 backed 'Origins' are provided on the CloudFront distribution resource # And: 'S3OriginConfig' is present with an 'OriginAccessIdentity for each S3 backed 'Origin' on the # CloudFront distribution resource that is a non-empty string or valid local reference # Then: PASS # # Constants # let CLOUDFRONT_DISTRIBUTION_TYPE = "AWS::CloudFront::Distribution" let S3_BUCKET_DNS_NAME_PATTERN = /(.*)\.s3(-external-\d|[-\.][a-z]*-[a-z]*-[0-9])?\.amazonaws\.com(\.cn)?$/ let INPUT_DOCUMENT = this # # Assignments # let cloudfront_distributions = Resources.*[ Type == %CLOUDFRONT_DISTRIBUTION_TYPE ] # # Primary Rules # rule cloudfront_origin_access_identity_enabled_check when is_cfn_template(%INPUT_DOCUMENT) %cloudfront_distributions not empty { check(%cloudfront_distributions.Properties) << [CT.CLOUDFRONT.PR.2]: Require any Amazon CloudFront distributions with Amazon S3 backed origins to have an origin access identity configured [FIX]: Configure Amazon S3 backed origins by means of the 'Origins' property. For each origin backed by Amazon S3, configure an origin access identity by means of the 'OriginAccessIdentity' property within an 'S3OriginConfig' configuration. >> } rule cloudfront_origin_access_identity_enabled_check when is_cfn_hook(%INPUT_DOCUMENT, %CLOUDFRONT_DISTRIBUTION_TYPE) { check(%INPUT_DOCUMENT.%CLOUDFRONT_DISTRIBUTION_TYPE.resourceProperties) << [CT.CLOUDFRONT.PR.2]: Require any Amazon CloudFront distributions with Amazon S3 backed origins to have an origin access identity configured [FIX]: Configure Amazon S3 backed origins by means of the 'Origins' property. For each origin backed by Amazon S3, configure an origin access identity by means of the 'OriginAccessIdentity' property within an 'S3OriginConfig' configuration. >> } # # Parameterized Rules # rule check(cloudfront_distribution) { %cloudfront_distribution[ filter_cloudfront_distribution_with_legacy_s3_origins(this) ] { DistributionConfig { # Scenario 3 S3Origin not exists } } %cloudfront_distribution[ # Scenario 2 filter_cloudfront_distribution_with_origins(this) ] { DistributionConfig { # Scenario 4 Origins [ DomainName == %S3_BUCKET_DNS_NAME_PATTERN or check_origin_domain_name_get_att(DomainName) ] { S3OriginConfig exists S3OriginConfig is_struct S3OriginConfig { # Scenario 3 and 5 OriginAccessIdentity exists check_is_string_and_not_empty(OriginAccessIdentity) or check_local_oai(OriginAccessIdentity) } } } } } rule check_origin_domain_name_get_att(domain) { %domain { 'Fn::GetAtt' { this is_list this not empty this[1] == "DomainName" or this[1] == "RegionalDomainName" } check_local_references(%INPUT_DOCUMENT, this, "AWS::S3::Bucket") } } rule check_local_oai(oai) { %oai { 'Fn::Join' { this[1] exists this[1] is_list this[1] not empty some this[1].* { check_local_references(%INPUT_DOCUMENT, this, "AWS::CloudFront::CloudFrontOriginAccessIdentity") } } or 'Fn::Sub' { when this is_list { this[1] exists this[1] is_struct some this[1].* { check_local_references(%INPUT_DOCUMENT, this, "AWS::CloudFront::CloudFrontOriginAccessIdentity") } } when this is_string { check_is_string_and_not_empty(this) } } } } rule filter_cloudfront_distribution_with_origins(cloudfront_distribution) { %cloudfront_distribution { DistributionConfig exists DistributionConfig is_struct DistributionConfig { Origins exists Origins is_list Origins not empty } } } rule filter_cloudfront_distribution_with_legacy_s3_origins(cloudfront_distribution) { %cloudfront_distribution { DistributionConfig exists DistributionConfig is_struct DistributionConfig { S3Origin exists } } } # # Utility Rules # rule check_is_string_and_not_empty(value) { %value { this is_string this != /\A\s*\z/ } } rule is_cfn_template(doc) { %doc { AWSTemplateFormatVersion exists or Resources exists } } rule is_cfn_hook(doc, RESOURCE_TYPE) { %doc.%RESOURCE_TYPE.resourceProperties exists } rule check_local_references(doc, reference_properties, referenced_resource_type) { %reference_properties { 'Fn::GetAtt' { query_for_resource(%doc, this[0], %referenced_resource_type) <> } or Ref { query_for_resource(%doc, this, %referenced_resource_type) <> } } } rule query_for_resource(doc, resource_key, referenced_resource_type) { let referenced_resource = %doc.Resources[ keys == %resource_key ] %referenced_resource not empty %referenced_resource { Type == %referenced_resource_type } }