> For the complete documentation index, see [llms.txt](https://aws-gcr-wwso-security.gitbook.io/an-quan-zui-jia-shi-jian/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://aws-gcr-wwso-security.gitbook.io/an-quan-zui-jia-shi-jian/an-quan-ti-xi-jian-she/cong-devops-dao-devsecops/iac-an-quan-jian-cha/aws-cloudformation-guard.md).

# 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>

2. 假设添加安全检查策略用于检查创建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 样例**

<pre><code><strong>{
</strong>    "EC2LaunchTemplate": {
        "Type": "AWS::EC2::LaunchTemplate",
        "Properties": {
            "LaunchTemplateData": {
                "InstanceType": "t3.micro",
                "ImageId": {
                    "Ref": "LatestAmiId"
                },
                "MetadataOptions": {
                    "HttpTokens": "required"
                }
            }
        }
    }
}
</code></pre>

**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，填写下面的内容后保存。

<pre><code><strong># ###################################
</strong>##       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)
        &#x3C;&#x3C;
        [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)
        &#x3C;&#x3C;
        [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
}

    
</code></pre>

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

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

可以看到以下执行结果：

pass\_template.yaml Status = <mark style="color:green;">PASS</mark>

PASS rules

ec2\_imds2\_rule\_set.guard/ec2\_launch\_template\_imdsv2\_check    <mark style="color:green;">PASS</mark>

### 验证失败的模板

如果模板中使用IMDSv1版本则使用以下内容创建文件fail\_template.yaml：

```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](https://docs.aws.amazon.com/controltower/latest/userguide/ec2-rules.html#ct-ec2-pr-1-description)

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

1. **使用Control Tower**

[Control Tower](https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html)预置了很多常用的[Proactive controls](https://docs.aws.amazon.com/controltower/latest/userguide/proactive-controls.html)，底层基于 [AWS CloudFormation hooks](https://docs.aws.amazon.com/cloudformation-cli/latest/hooks-userguide/what-is-cloudformation-hooks.html)。不需要写策略和进行繁琐的配置，激活使用即可。

2. **使用AWS CloudFormation Hooks**

如何使用AWS CloudFormation Hooks可以参见[官方的帮助文档](https://docs.aws.amazon.com/cloudformation-cli/latest/hooks-userguide/what-is-cloudformation-hooks.html)。

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

## 参考资料

* [CloudFormation Guard CLI ](https://github.com/aws-cloudformation/cloudformation-guard#installation)
* [CloudFormation Guard ](https://aws.amazon.com/about-aws/whats-new/2020/10/aws-cloudformation-guard-an-open-source-cli-for-infrastructure-compliance-is-now-generally-available/#:~:text=Customer%20Enablement-,AWS%20CloudFormation%20Guard%20%E2%80%93%20an%20open%2Dsource%20CLI%20for%20infrastructure,compliance%20%E2%80%93%20is%20now%20generally%20available\&text=Cfn%2Dguard%20is%20an%20open,as%2Dcode%2C%20declarative%20language.)
* [cfn-guard GitHub repository](https://github.com/aws-cloudformation/cloudformation-guard)
* [CloudFormation Guard Examples](https://github.com/aws-cloudformation/cloudformation-guard/tree/main/guard-examples)
