AWS CloudFormation Guard

1# 什么是AWS CloudFormation Guard?

AWS CloudFormation Guard(cfn-guard) 为合规性管理员提供了一种简单的策略即代码语言,用于定义规则,检查要求的和强制的资源配置。它使开发人员能够根据这些规则验证他们的 CloudFormation 模板。

cfn-guard 可帮助企业最大限度地降低与运营成本超支、安全漏洞、法律问题等相关的风险。例如,管理员可以创建规则,确保开发人员始终创建加密的亚马逊 S3 存储桶。cfn-guard 采用轻量级声明式语法,管理员无需学习编程语言即可快速定义规则。开发人员可以在本地编辑模板时使用 cfn-guard,也可以作为 CI/CD 管道的一部分自动使用 cfn-guard,以停止部署不合规的资源。如果模板中的资源不符合规则,cfn-guard 会为开发人员提供信息,帮助识别不符合规则的资源。

  • 它允许对 CloudFormation 模板资源进行部署前安全检查。您既可以要求包含设置,也可以禁止以前曾导致问题的配置。

  • 可以嵌入到CI/CD的流程中。

  • 可以在本地运行,也可在 AWS 账户中作为 lambdas 运行,以便与其他 AWS 服务集成。

  • 上手很容易。你可以使用 cfn-guard 的规则生成功能,从现有的已知良好模板中提取你想要的规则。

2# 如何使用AWS CloudFormation Guard?

cfn-guard 是一个开源命令行界面(CLI),可使用简单的策略即代码声明式语言检查 CloudFormation 模板是否符合策略。安装后,您可以使用给定的模板输入和规则集运行命令行,cfn-guard 将根据模板评估规则并反馈结果。

  1. 安装最新版的cfn-guard

参考Github的安装说明:https://github.com/aws-cloudformation/cloudformation-guard/tree/Guard1.0?tab=readme-ov-file#installation

  1. 假设添加安全检查策略用于检查创建EC2的IMDS版本,是否是IMDSv2的:

IMDS 提供对经常轮换的临时凭证的访问,因此您无需手动或以编程方式将敏感凭证分发到实例。 IMDS 本地连接到每个 EC2 实例。它运行在特殊的IP地址169.254.169.254上。该 IP 地址仅可由实例上运行的软件访问。

IMDSv2 增加了对可用于访问 IMDS 的漏洞的保护,IMDSv1没有进行任何校验,因此很容易被攻击者获取EC2上的AKSK。IMDSv2引入了token,可以避免攻击者获取AKSK。

验证成功的模板

Within the LaunchTemplateData property, provide a MetadataOptions configuration and set the value of HttpTokens to required.以下分别是JSON和YAML格式的样例:

JSON 样例

{
    "EC2LaunchTemplate": {
        "Type": "AWS::EC2::LaunchTemplate",
        "Properties": {
            "LaunchTemplateData": {
                "InstanceType": "t3.micro",
                "ImageId": {
                    "Ref": "LatestAmiId"
                },
                "MetadataOptions": {
                    "HttpTokens": "required"
                }
            }
        }
    }
}

YAML 样例

Parameters:
  LatestAmiId:
    Description: Region specific latest AMI ID from the Parameter Store
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
Resources:
  EC2LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        InstanceType: t3.micro
        ImageId:
          Ref: LatestAmiId
        MetadataOptions:
          HttpTokens: required

创建一个CloudFormation文件pass_template.yaml,填写上面的内容后保存:

再创建一个文件ec2_imds2_rule_set.guard,填写下面的内容后保存。

# ###################################
##       Rule Specification        ##
#####################################
# 
# Rule Identifier:
#   ec2_launch_template_imdsv2_check
# 
# Description:
#   This control checks whether your Amazon EC2 launch templates are configured with Instance Metadata Service Version 2 (IMDSv2).
# 
# Reports on:
#   AWS::EC2::LaunchTemplate
# 
# Evaluates:
#   AWS CloudFormation, AWS CloudFormation hook
# 
# Rule Parameters:
#   None
# 
# Scenarios:
#   Scenario: 1
#     Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document
#       And: The input document does not contain any EC2 launch template resources
#      Then: SKIP
#   Scenario: 2
#     Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document
#       And: The input document contains an EC2 launch template resource
#       And: 'LaunchTemplateData' has not been provided or 'LaunchTemplateData.MetadataOptions.HttpEndpoint' has
#             been provided and is equal to 'disabled'
#      Then: SKIP
#   Scenario: 3
#     Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document
#       And: The input document contains an EC2 launch template resource
#       And: 'LaunchTemplateData' has been provided
#       And: 'MetadataOptions.HttpEndpoint' in 'LaunchTemplateData' has not been provided or has been provided and
#            is equal to 'enabled'
#       And: 'MetadataOptions.HttpTokens' in 'LaunchTemplateData' has not been provided
#      Then: FAIL
#   Scenario: 4
#     Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document
#       And: The input document contains an EC2 launch template resource
#       And: 'LaunchTemplateData' has been provided
#       And: 'MetadataOptions.HttpEndpoint' in 'LaunchTemplateData' has not been provided or has been provided and
#            is equal to 'enabled'
#       And: 'MetadataOptions.HttpTokens' in 'LaunchTemplateData' has been provided and set to a value other than 'required'
#      Then: FAIL
#   Scenario: 5
#     Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document
#       And: The input document contains an EC2 launch template resource
#       And: 'LaunchTemplateData' has been provided
#       And: 'MetadataOptions.HttpEndpoint' in 'LaunchTemplateData' has not been provided or has been provided and
#            is equal to 'enabled'
#       And: 'MetadataOptions.HttpTokens' in 'LaunchTemplateData' has been provided and set to 'required'
#      Then: PASS

#
# Constants
#
let EC2_LAUNCH_TEMPLATE_TYPE = "AWS::EC2::LaunchTemplate"
let INPUT_DOCUMENT = this

#
# Assignments
#
let ec2_launch_templates = Resources.*[ Type == %EC2_LAUNCH_TEMPLATE_TYPE ]

#
# Primary Rules
#
rule ec2_launch_template_imdsv2_check when is_cfn_template(%INPUT_DOCUMENT)
                                           %ec2_launch_templates not empty {
    check(%ec2_launch_templates.Properties)
        <<
        [CT.EC2.PR.1]: Require an Amazon EC2 launch template to have IMDSv2 configured
            [FIX]: Within the 'LaunchTemplateData' property, provide a 'MetadataOptions' configuration and set the value of 'HttpTokens' to 'required'.
        >>
}

rule ec2_launch_template_imdsv2_check when is_cfn_hook(%INPUT_DOCUMENT, %EC2_LAUNCH_TEMPLATE_TYPE) {
    check(%INPUT_DOCUMENT.%EC2_LAUNCH_TEMPLATE_TYPE.resourceProperties)
        <<
        [CT.EC2.PR.1]: Require an Amazon EC2 launch template to have IMDSv2 configured
            [FIX]: Within the 'LaunchTemplateData' property, provide a 'MetadataOptions' configuration and set the value of 'HttpTokens' to 'required'.
        >>
}

#
# Parameterized Rules
#
rule check(launch_template) {
    %launch_template [
        # Scenario 2
        filter_launch_template_imds_enabled(this)
    ] {
        LaunchTemplateData exists
        LaunchTemplateData is_struct

        LaunchTemplateData {
            # Scenario 3, 4 and 5
            MetadataOptions exists
            MetadataOptions is_struct
            MetadataOptions {
                HttpTokens exists
                HttpTokens == "required"
            }
        }
    }
}

rule filter_launch_template_imds_enabled(launch_template) {
    %launch_template {
        LaunchTemplateData exists
        LaunchTemplateData is_struct
        LaunchTemplateData {
            MetadataOptions not exists or
            filter_metadata_options_imds_enabled(this)
        }
    }
}

rule filter_metadata_options_imds_enabled(metadata_options) {
    %metadata_options {
        MetadataOptions is_struct
        MetadataOptions {
            HttpEndpoint not exists or
            HttpEndpoint == "enabled"
        }
    }
}

#
# Utility Rules
#
rule is_cfn_template(doc) {
    %doc {
        AWSTemplateFormatVersion exists  or
        Resources exists
    }
}

rule is_cfn_hook(doc, RESOURCE_TYPE) {
    %doc.%RESOURCE_TYPE.resourceProperties exists
}

    

进入保存了这两个文件的文件夹,执行以下命令:

cfn-guard validate -d pass_template.yaml -r ec2_imds2_rule_set.guard --show-summary all

可以看到以下执行结果:

pass_template.yaml Status = PASS

PASS rules

ec2_imds2_rule_set.guard/ec2_launch_template_imdsv2_check PASS

验证失败的模板

如果模板中使用IMDSv1版本则使用以下内容创建文件fail_template.yaml:

Parameters:
  LatestAmiId:
    Description: Region specific latest AMI ID from the Parameter Store
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
Resources:
  EC2LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        InstanceType: t3.micro
        ImageId:
          Ref: LatestAmiId
        MetadataOptions:
          HttpTokens: optional

然后执行以下命令:

cfn-guard validate -d fail_template.yaml -r ec2_imds2_rule_set.guard --show-summary all

将看到如下执行结果。

fail_template.yaml Status = FAIL
FAILED rules
ec2_imds2_rule_set.guard/ec2_launch_template_imdsv2_check    FAIL
---
Evaluating data fail_template.yaml against rules ec2_imds2_rule_set.guard
Number of non-compliant resources 1
Resource = EC2LaunchTemplate {
  Type      = AWS::EC2::LaunchTemplate
  Rule = ec2_launch_template_imdsv2_check {
    ALL {
      Rule = check {
        Message {
          [CT.EC2.PR.1]: Require an Amazon EC2 launch template to have IMDSv2 configured
          [FIX]: Within the 'LaunchTemplateData' property, provide a 'MetadataOptions' configuration and set the value of 'HttpTokens' to 'required'.
        }
        ALL {
          Check =  HttpTokens EQUALS  "required" {
            ComparisonError {
              Error            = Check was not compliant as property value [Path=/Resources/EC2LaunchTemplate/Properties/LaunchTemplateData/MetadataOptions/HttpTokens[L:14,C:22] Value="optional"] not equal to value [Path=[L:0,C:0] Value="required"].
              PropertyPath    = /Resources/EC2LaunchTemplate/Properties/LaunchTemplateData/MetadataOptions/HttpTokens[L:14,C:22]
              Operator        = EQUAL
              Value           = "optional"
              ComparedWith    = "required"
              Code:
                   12.        ImageId:
                   13.          Ref: LatestAmiId
                   14.        MetadataOptions:
                   15.          HttpTokens: optional

            }
          }
        }
      }
    }
  }
}

参考资料:[CT.EC2.PR.1] Require an Amazon EC2 launch template to have IMDSv2 configured

#3 如何嵌入到生产部署中

  1. 使用Control Tower

Control Tower预置了很多常用的Proactive controls,底层基于 AWS CloudFormation hooks。不需要写策略和进行繁琐的配置,激活使用即可。

  1. 使用AWS CloudFormation Hooks

如何使用AWS CloudFormation Hooks可以参见官方的帮助文档

  1. 创建Lambda,CI/CD流程中引用这些lambda先检查,检查通过后才允许发版。

参考资料

最后更新于