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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET 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?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
