

# 使用 Guard 验证模板
<a name="cloudformation-guard"></a>

Amazon CloudFormation Guard（`cfn-guard`）是一种策略即代码工具。您可以使用 Guard 语言编写描述必需或禁止配置的规则，然后根据这些规则检查 JSON 或 YAML 数据。例如，您可以要求模板中的每个 Amazon S3 存储桶都使用加密，或者屏蔽向互联网开放端口 22 的安全组规则。

Guard 适用于任何 JSON 或 YAML 数据。除了 CloudFormation 模板外，您还可以查看 CloudFormation 更改集和 Amazon Config 配置项目。本主题侧重于检查模板。

有关发布说明和完整的命令参考，请参阅 GitHub 上的 [Guard](https://github.com/aws-cloudformation/cloudformation-guard)。有关完整的语言参考，请参阅《[Guard 用户指南](https://docs.amazonaws.cn/cfn-guard/latest/ug/what-is-guard.html)》。

有关本地验证的限制以及部署前需采取的步骤，请参阅[了解验证范围](template-guide.md#template-validation-scope)。

**Topics**
+ [安装 Guard](#cloudformation-guard-install)
+ [了解 Guard 语言](#cloudformation-guard-language)
+ [编写规则](#cloudformation-guard-write-rule)
+ [验证模板](#cloudformation-guard-validate)
+ [测试规则](#cloudformation-guard-test)
+ [从模板生成规则](#cloudformation-guard-rulegen)
+ [向您的工作流添加 Guard](#cloudformation-guard-integrate)

## 安装 Guard
<a name="cloudformation-guard-install"></a>

在 macOS 上，使用 Homebrew 安装 Guard：

```
brew install cloudformation-guard
```

在 Linux 或 macOS 上，您可以改为运行安装脚本。该脚本将最新版本安装到 `~/.guard/bin`。脚本完成后，请将该目录添加到您的 `PATH`。

```
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.sh | sh
```

如果您有 Rust 工具链，也可以通过 Cargo 安装 Guard：

```
cargo install cfn-guard
```

要确认安装，请输出已安装的版本：

```
cfn-guard --version
```

有关 Windows 安装、Docker 映像和版本验证的信息，请参阅 [Setting up Guard](https://docs.amazonaws.cn/cfn-guard/latest/ug/setting-up.html)。

## 了解 Guard 语言
<a name="cloudformation-guard-language"></a>

现在 Guard 已安装完毕，您可以开始编写规则了。Guard 规则由以下部分构成：
+ **查询**：使用虚线路径从数据中选择值，例如 `Resources.MyBucket.Properties.BucketName`。使用 `*` 选择映射中的每个值，使用 `[*]` 选择列表中的每个项目。在方括号中添加筛选条件以缩小选择范围。例如，`Resources.*[ Type == 'AWS::S3::Bucket' ]` 选择每个 Amazon S3 存储桶。
+ **子句**：做出判定为真或假的断言。子句将查询与运算符（例如 `==`、`!=`、`>=`、`in`、`exists`、`empty` 或 `is_string`）组合在一起。位于不同行上的子句必须全部通过。要接受两个子句中的任何一个，请将其与 `or` 联接。
+ **变量**：使用 `let` 存储查询结果，并在以后使用 `%` 前缀引用它。变量允许您一次性选择一组资源，并在多条规则中重复使用它。
+ **命名规则**：使用名称对 `rule` 块中的子句进行分组。添加一个 `when` 条件，以便在规则不适用时将其跳过，例如，当模板没有 Amazon S3 存储桶时。规则可以引用其他规则，因此您可以从小部分构建复杂的策略。
+ **自定义消息**：在子句后的 `<<` 和 `>>` 之间添加文本，以解释输出中的失败。

有关完整语法，请参阅 [Writing Guard rules](https://docs.amazonaws.cn/cfn-guard/latest/ug/writing-rules.html)。

## 编写规则
<a name="cloudformation-guard-write-rule"></a>

将规则保存为扩展名为 `.guard` 的文件。`rules.guard` 中的以下示例要求模板中的每个 Amazon S3 存储桶都必须指定存储桶加密并屏蔽公共访问权限：

```
let s3_buckets = Resources.*[ Type == 'AWS::S3::Bucket' ]

rule S3_BUCKET_ENCRYPTED when %s3_buckets !empty {
  %s3_buckets.Properties.BucketEncryption exists
  <<
    Violation: S3 buckets must specify BucketEncryption.
    Fix: Add a BucketEncryption property with ServerSideEncryptionConfiguration.
  >>
}

rule S3_BUCKET_PUBLIC_ACCESS_BLOCKED when %s3_buckets !empty {
  %s3_buckets.Properties.PublicAccessBlockConfiguration {
    BlockPublicAcls == true
    BlockPublicPolicy == true
    IgnorePublicAcls == true
    RestrictPublicBuckets == true
  }
}
```

第一行将每个 Amazon S3 存储桶存储在 `s3_buckets` 变量中。仅当模板包含至少一个存储桶时，每条规则才会运行。第一条规则检查 `BucketEncryption` 属性是否存在，并包含一条在检查失败时由 Guard 输出的消息。第二条规则检查 `PublicAccessBlockConfiguration` 内部的四个属性。由于子句位于不同的行中，因此所有四个子句均须通过。

您可以将多条规则保存在同一个文件中，也可以按主题将规则拆分到不同的文件中，例如，将存储规则存入一个文件，将网络规则存入另一个文件。当您将目录传递给命令时，Guard 会评估该目录中的每个 `.guard` 文件。

## 验证模板
<a name="cloudformation-guard-validate"></a>

将规则文件传递给 `--rules`，将模板传递给 `--data`。添加 `--type CFNTemplate` 以便 Guard 按资源逻辑 ID 报告调查发现。

```
cfn-guard validate --rules rules.guard --data template.yaml --type CFNTemplate
```

模板通过后，Guard 会输出 `PASS` 状态并返回退出代码 `0`。当规则未通过时，Guard 会列出未通过的规则；对于每个不合规的资源，它还会列出未通过的检查、属性路径以及原因。对于存储桶缺失 `BucketEncryption` 的模板，其输出以如下内容开头：

```
template.yaml Status = FAIL
FAILED rules
rules.guard/S3_BUCKET_ENCRYPTED    FAIL
---
Evaluating data template.yaml against rules rules.guard
Number of non-compliant resources 1
Resource = MyBucket {
  Type      = AWS::S3::Bucket
  Rule = S3_BUCKET_ENCRYPTED {
    ...
```

当一条或多条规则未通过时，Guard 会返回退出代码 `19`；当无法解析规则或数据文件时，则返回退出代码 `5`。您可以使用这些退出代码来停止自动化构建。

在验证模板时，以下选项非常有用：
+ 多次传递 `--rules` 或 `--data`，或传递一个目录，以在一次运行中检查多个规则文件或模板。传递到 `--data` 的目录必须仅包含数据文件，而传递到 `--rules` 的目录必须仅包含规则文件。
+ 将 `--show-summary` 与 `pass`、`fail`、`skip`、`all` 或 `none` 配合使用以控制摘要表中显示哪些规则。默认值为 `fail`。
+ 将 `--output-format` 与 `json`、`yaml`、`junit` 或 `sarif` 配合使用，为其他工具生成输出。`junit` 和 `sarif` 格式适用于大多数构建系统和代码扫描工具。
+ 使用 `--input-parameters` 传递包含规则所引用的值的 JSON 或 YAML 文件，例如已批准的安全组 ID 列表。这使得环境特定的值不会出现在您的规则中。

有关所有选项，请参阅《Guard 用户指南》**中的 [validate](https://docs.amazonaws.cn/cfn-guard/latest/ug/cfn-guard-validate.html)。

## 测试规则
<a name="cloudformation-guard-test"></a>

在自动化工作流中使用规则之前，请利用内置的单元测试支持对其进行测试。测试文件列出了示例输入以及针对每条规则所预期的结果：`PASS`、`FAIL` 或 `SKIP`。`rules_tests.yaml` 中的以下示例测试了上一个示例中的 `S3_BUCKET_ENCRYPTED` 规则：

```
---
- input:
    Resources: {}
  expectations:
    rules:
      S3_BUCKET_ENCRYPTED: SKIP
- input:
    Resources:
      MyBucket:
        Type: AWS::S3::Bucket
        Properties: {}
  expectations:
    rules:
      S3_BUCKET_ENCRYPTED: FAIL
- input:
    Resources:
      MyBucket:
        Type: AWS::S3::Bucket
        Properties:
          BucketEncryption:
            ServerSideEncryptionConfiguration:
              - ServerSideEncryptionByDefault:
                  SSEAlgorithm: aws:kms
  expectations:
    rules:
      S3_BUCKET_ENCRYPTED: PASS
```

使用 `test` 命令运行测试：

```
cfn-guard test --rules-file rules.guard --test-data rules_tests.yaml
```

Guard 为每个期望值输出 `PASS` 或 `FAIL`，并在任何期望失败时返回退出代码 `7`。有关更多信息，请参阅 [Testing Guard rules](https://docs.amazonaws.cn/cfn-guard/latest/ug/testing-rules.html)。

## 从模板生成规则
<a name="cloudformation-guard-rulegen"></a>

若想快速上手，您可以根据已满足您要求的模板生成规则。`rulegen` 命令会读取模板，并为需要模板中的属性值的每种资源类型编写规则：

```
cfn-guard rulegen --template template.yaml --output generated.guard
```

检查生成的文件，只保留代表您策略的检查。生成的规则是一个起点，通常需要源模板中的精确值，这对于其他模板可能过于严苛。

## 向您的工作流添加 Guard
<a name="cloudformation-guard-integrate"></a>

您可以在以下位置运行 Guard：
+ **在您的编辑器中**：CloudFormation 语言服务器会在您编辑模板时运行 Guard 规则包，并以内联方式显示失败。有关设置，请参阅[CloudFormation 语言服务器](ide-extension.md)。
+ **在 Git 提交之前**：Guard 提供了一个 `pre-commit` 钩子，可以在更改的模板上运行 `validate` 或在更改的规则上运行 `test`。请将以下内容添加到 `.pre-commit-config.yaml` 文件中，并将路径替换为您自己的路径：

  ```
  repos:
    - repo: https://github.com/aws-cloudformation/cloudformation-guard
      rev: pre-commit-v0.0.2
      hooks:
        - id: cfn-guard
          args:
            - --operation=validate
            - --rules=rules/
          files: ^templates/.*
  ```
+ **在自动化构建中**：将 `cfn-guard validate` 作为构建步骤运行，并使用退出代码使构建失败。使用 `--output-format junit` 或 `--output-format sarif` 发布结果。此外还提供了一个 GitHub 操作和一个容器映像。请从 `public.ecr.aws/aws-cloudformation/cloudformation-guard` 提取映像。
+ **使用 `cloudformation-validate`**：`cfn-validate` 命令和 Amazon CDK 验证插件可以运行支持的 Guard 规则子集及其内置检查。有关更多信息，请参阅 [使用 `cloudformation-validate` 验证模板](cloudformation-validate.md)。
+ **堆栈操作期间**：要在 CloudFormation 或 Cloud Control API 创建、更新或删除资源时强制执行规则，请使用 Guard 钩子。有关更多信息，请参阅 [Guard Hooks](https://docs.amazonaws.cn/cloudformation-cli/latest/hooks-userguide/guard-hooks.html)。

有关涵盖常见安全和合规控制的即用型规则，请参阅 GitHub 上的 [Amazon Guard Rules Registry](https://github.com/aws-cloudformation/aws-guard-rules-registry)。