创建 Terraform 限制条件

准备工作

限制条件框架

gcloud beta terraform vet 使用限制条件框架政策,其中包括限制条件限制条件模板。这两者的区别如下:

  • 限制条件模板类似于函数声明,用于定义规则并选择性地将变量作为输入。
  • 限制条件是引用限制条件模板的文件,该文件定义要使用的模板值。

创建限制条件模板

1.收集样本数据

要编写限制条件模板,您需要具有可供操作的样本数据。基于 Terraform 的限制条件可处理资源变更数据,该数据来自 Terraform 方案 JSONresource_changes 键。

例如,您的 JSON 可能如下所示:

// tfplan.json
{
  "format_version": "0.2",
  "terraform_version": "1.0.10",
  "resource_changes": [
    {
      "address": "google_compute_address.internal_with_subnet_and_address",
      "mode": "managed",
      "type": "google_compute_address",
      "name": "internal_with_subnet_and_address",
      "provider_name": "registry.terraform.io/hashicorp/google",
      "change": {
        "actions": [
          "create"
        ],
        "before": null,
        "after": {
          "address": "10.0.42.42",
          "address_type": "INTERNAL",
          "description": null,
          "name": "my-internal-address",
          "network": null,
          "prefix_length": null,
          "region": "us-central1",
          "timeouts": null
        },
        "after_unknown": {
          "creation_timestamp": true,
          "id": true,
          "network_tier": true,
          "project": true,
          "purpose": true,
          "self_link": true,
          "subnetwork": true,
          "users": true
        },
        "before_sensitive": false,
        "after_sensitive": {
          "users": []
        }
      }
    }
  ],
  // other data
}

2.编写 Rego

获得样本数据后,您便可以使用 Rego 编写限制条件模板的逻辑。您的 Rego 必须具有 violations 规则。待审核的资源变更作为 input.review 提供。限制条件参数作为 input.parameters 提供。例如,如需要求 google_compute_address 资源必须具有允许的 address_type,则请编写如下代码:

# validator/tf_compute_address_address_type_allowlist_constraint_v1.rego
package templates.gcp.TFComputeAddressAddressTypeAllowlistConstraintV1

violation[{
  "msg": message,
  "details": metadata,
}] {
  resource := input.review
  resource.type == "google_compute_address"

  allowed_address_types := input.parameters.allowed_address_types
  count({resource.after.address_type} & allowed_address_types) >= 1
  message := sprintf(
    "Compute address %s has a disallowed address_type: %s",
    [resource.address, resource.after.address_type]
  )
  metadata := {"resource": resource.name}
}

为限制条件模板命名

上述示例使用名称 TFComputeAddressAddressTypeAllowlistConstraintV1。这是每个限制条件模板的唯一标识符。我们建议您遵循以下命名准则:

  • 常规格式:TF{resource}{feature}Constraint{version}。使用 CamelCase 格式。(换句话说,每个单词的首字母都要大写)。
  • 对于单资源限制条件,请遵循 Terraform 提供程序的产品命名惯例。例如,对于 google_tags_tag,产品名称为 tags,即使 API 名称为 resourcemanager
  • 如果模板适用于多种类型的资源,请省略资源部分并仅包含功能(例如:“TFAddressTypeAllowlistConstraintV1”)。
  • 版本号不遵循 semver 格式;它只是一个数字。这实际上会使模板的每个版本都成为唯一的模板。

我们建议您使用与限制条件模板名称匹配的 rego 文件名,但要使用 snake_case 格式。换句话说,将名称转换为小写字母并使用 _ 分隔每个单词。对于上文中的示例,建议的文件名为 tf_compute_address_address_type_allowlist_constraint_v1.rego

3.测试 Rego

您可以使用 Rego Playground 手动测试 Rego。请务必使用非敏感数据。

我们建议您编写自动化测试。将收集的样本数据放入 validator/test/fixtures/<constraint filename>/resource_changes/data.json 中,并在测试文件中引用这些数据,如下所示:

# validator/tf_compute_address_address_type_allowlist_constraint_v1_test.rego
package templates.gcp.TFComputeAddressAddressTypeAllowlistConstraintV1

import data.test.fixtures.tf_compute_address_address_type_allowlist_constraint_v1_test.resource_changes as resource_changes

test_violation_with_disallowed_address_type {
  parameters := {
    "allowed_address_types": "EXTERNAL"
  }
  violations := violation with input.review as resource_changes[_]
    with input.parameters as parameters
  count(violations) == 1
}

将 rego 和测试放在政策库的 validator 文件夹中。

4.设置限制条件模板框架

编写和测试 Rego 规则后,您必须将其封装为限制条件模板。限制条件框架使用 Kubernetes 自定义资源定义作为 Rego 政策的容器。

限制条件模板还会使用 OpenAPI V3 架构定义允许将哪些参数作为限制条件的输入。

对该框架使用与 Rego 相同的名称。特别是:

  • 使用与 Rego 相同的文件名。示例:tf_compute_address_address_type_allowlist_constraint_v1.yaml
  • spec.crd.spec.names.kind 必须包含模板名称
  • metadata.name 必须包含模板名称,但需用小写字母

将限制条件模板框架放置在 policies/templates 中。

对于上述示例,代码如下:

# policies/templates/tf_compute_address_address_type_allowlist_constraint_v1.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: tfcomputeaddressaddresstypeallowlistconstraintv1
spec:
  crd:
    spec:
      names:
        kind: TFComputeAddressAddressTypeAllowlistConstraintV1
      validation:
        openAPIV3Schema:
          properties:
            allowed_address_types:
              description: "A list of address_types allowed, for example: ['INTERNAL']"
              type: array
              items:
                type: string
  targets:
    - target: validation.resourcechange.terraform.cloud.google.com
      rego: |
            #INLINE("validator/tf_compute_address_address_type_allowlist_constraint_v1.rego")
            #ENDINLINE

5. 内嵌 Rego

如果按照上文的示例操作,此时您的目录布局应如下所示:

| policy-library/
|- validator/
||- tf_compute_address_address_type_allowlist_constraint_v1.rego
||- tf_compute_address_address_type_allowlist_constraint_v1_test.rego
|- policies
||- templates
|||- tf_compute_address_address_type_allowlist_constraint_v1.yaml

如果您克隆了 Google 提供的政策库代码库,您便可以运行 make build 来使用 validator 中定义的 Rego 自动更新 policies/templates 中的限制条件模板。

6.设置限制条件

限制条件包含 gcloud beta terraform vet 正确地强制执行政策并报告违规行为所需的三项信息:

  • severitylowmediumhigh
  • match:用于确定限制条件是否应用于特定资源的参数。支持以下匹配参数:
    • addresses:使用 glob 样式匹配时要包含的资源地址的列表
    • excludedAddresses:(可选)使用 glob 样式匹配时要排除的资源地址的列表。
  • parameters:限制条件模板的输入参数的值。

确保 kind 包含限制条件模板名称。我们建议将 metadata.name 设置为描述性 slug。

例如,要使用上文中的示例限制条件模板仅允许 INTERNAL 地址类型,请编写如下代码:

# policies/constraints/tf_compute_address_internal_only.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: TFComputeAddressAddressTypeAllowlistConstraintV1
metadata:
  name: tf_compute_address_internal_only
spec:
  severity: high
  match:
    addresses:
    - "**"
  parameters:
    allowed_address_types:
    - "INTERNAL"

匹配示例:

地址匹配器 说明
`module.**` 任何模块中的所有资源
`module.my_module.**` 模块“my_module”中的所有内容
`**.google_compute_global_forwarding_rule.*` 任何模块中的所有 google_compute_global_forwarding_rule 资源
`module.my_module.google_compute_global_forwarding_rule.*` “my_module”中的所有 google_compute_global_forwarding_rule 资源

如果资源地址与 addressesexcludedAddresses 中的值匹配,则排除该资源。

限制

应用后的 Terraform 方案数据能最好地反映实际状态;但是,在许多情况下,应用后的状态可能是未知的,因为它是在服务器端计算的。

支持的资源

您可以通过 Terraform 的 googlegoogle-beta 提供程序为任何资源创建资源变更限制条件。