创建 CAI 限制条件

准备工作

限制条件框架

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

  • 限制条件模板类似于函数声明;它在 Rego 中定义规则,并选择性地接受输入参数。
  • 限制条件是一个文件,它引用限制条件模板并定义要传递给它的输入参数以及政策涵盖的资源。

这样可以避免重复。您可以使用通用政策编写限制条件模板,然后编写提供不同输入参数或不同资源匹配规则的任意数量的限制条件。

CAI 资产与 Terraform 资源

Cloud Asset Inventory 资产是一种标准 Google 数据导出格式,适用于许多 Google Cloud 资源。CAI 数据通常仅在资源创建或更新后可用。但是,在将 Terraform 资源变更转换为 CAI 资产数据后,您便可以通过 gcloud beta terraform vet 进行一次性的政策编写,这样除了在应用之前使用该政策之外,还可将该政策用作对兼容工具的审核检查。

兼容的工具

以下工具不是 Google 官方产品且不受支持。但是,它们可能与为 gcloud beta terraform vet 编写的政策兼容。

创建限制条件模板

在开发限制条件模板之前,请先验证 Cloud Asset Inventorygcloud beta terraform vet 是否均支持您要为其编写政策的资产。

创建限制条件模板需要执行以下步骤:

  1. 收集样本数据。
  2. 编写 Rego
  3. 测试 Rego。
  4. 设置限制条件模板框架。
  5. 内嵌 Rego。
  6. 设置限制条件。

收集样本数据

要编写限制条件模板,您需要具有可供操作的样本数据。基于 CAI 的限制条件会处理 CAI 资产数据。如 CAI 快速入门中所述,通过创建相应类型的资源并将这些资源导出为 JSON 来收集样本数据。

以下是一个计算地址的 JSON 导出示例:

[
  {
    "name": "//compute.googleapis.com/projects/789/regions/us-central1/addresses/my-internal-address",
    "asset_type": "compute.googleapis.com/Address",
    "ancestors: [
      "organization/123",
      "folder/456",
      "project/789"
    ],
    "resource": {
      "version": "v1",
      "discovery_document_uri": "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
      "discovery_name": "Address",
      "parent": "//cloudresourcemanager.googleapis.com/projects/789",
      "data": {
        "address": "10.0.42.42",
        "addressType": "INTERNAL",
        "name": "my-internal-address",
        "region": "projects/789/global/regions/us-central1"
      }
    }
  },
]

编写 Rego

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

# validator/gcp_compute_address_address_type_allowlist_constraint_v1.rego
package templates.gcp.GCPComputeAddressAddressTypeAllowlistConstraintV1

violation[{
  "msg": message,
  "details": metadata,
}] {
  asset := input.review
  asset.asset_type == "compute.googleapis.com/Address"

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

为限制条件模板命名

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

  • 常规格式:GCP{resource}{feature}Constraint{version}。使用 CamelCase 格式。 (换句话说,每个单词的首字母都要大写)。
  • 对于单资源限制条件,请按照 gcloud 组名称进行资源命名。例如,使用“compute”而不是“gce”,使用“sql”而不是“cloud-sql”,使用“container-cluster”而不是“gke”。
  • 如果模板适用于多种类型的资源,请省略资源部分并仅包含功能(示例:“GCPAddressTypeAllowlistConstraintV1”)。
  • 版本号不遵循 semver 格式;它只是一个数字。这实际上会使模板的每个版本都成为唯一的模板。

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

测试 Rego

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

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

# validator/gcp_compute_address_address_type_allowlist_constraint_v1_test.rego
package templates.gcp.GCPComputeAddressAddressTypeAllowlistConstraintV1

import data.test.fixtures.gcp_compute_address_address_type_allowlist_constraint_v1_test.assets as assets

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

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

设置限制条件模板框架

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

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

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

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

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

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

# policies/templates/gcp_compute_address_address_type_allowlist_constraint_v1.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: gcpcomputeaddressaddresstypeallowlistconstraintv1
spec:
  crd:
    spec:
      names:
        kind: GCPComputeAddressAddressTypeAllowlistConstraintV1
      validation:
        openAPIV3Schema:
          properties:
            allowed_address_types:
              description: "A list of address_types allowed, for example: ['INTERNAL']"
              type: array
              items:
                type: string
  targets:
    - target: validation.gcp.forsetisecurity.org
      rego: |
            #INLINE("validator/gcp_compute_address_address_type_allowlist_constraint_v1.rego")
            #ENDINLINE

内嵌 Rego

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

| policy-library/
|- validator/
||- gcp_compute_address_address_type_allowlist_constraint_v1.rego
||- gcp_compute_address_address_type_allowlist_constraint_v1_test.rego
|- policies
||- templates
|||- gcp_compute_address_address_type_allowlist_constraint_v1.yaml

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

设置限制条件

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

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

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

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

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

匹配示例:

祖先实体路径匹配器 说明
organizations/** 所有单位
organizations/123/** 组织 123 中的所有内容
organizations/123/folders/** 文件夹下的组织 123 中的所有内容
organizations/123/folders/456 组织 123 中的 456 文件夹的所有内容
organizations/123/folders/456/projects/789 组织 123 的 456 文件夹中项目 789 的所有内容

如果资源地址与 ancestriesexcludedAncestries 中的值匹配,则系统会排除该资源。

限制

应用后的 Terraform 方案数据能最好地反映实际状态。但是,在许多情况下,应用后的状态可能是未知的,因为它是在服务器端计算的。在这些情况下,数据在转换后的 CAI 资产中也不可用。

在验证政策时,构建 CAI 祖先路径是流程的一部分。如果遇到未知的项目 ID,它会使用提供的默认项目。如果未提供默认项目,则祖先路径默认为 organizations/unknown

您可以添加以下限制条件来禁止未知的祖先路径:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: GCPAlwaysViolatesConstraintV1
metadata:
  name: disallow_unknown_ancestry
  annotations:
    description: |
      Unknown ancestry is not allowed; use --project=<project> to set a
      default ancestry
spec:
  severity: high
  match:
    ancestries:
    - "organizations/unknown"
  parameters: {}

支持的资源

如果您希望 gcloud beta terraform vet 添加对此列表中未列出的资源的支持,请提交增强请求并考虑贡献代码

受支持资源的列表取决于安装的 gcloud beta terraform vet 版本。受支持资源的最新列表如下:

Terraform 资源 Cloud Asset Inventory 资产
google_access_context_manager_access_policy_iam_binding accesscontextmanager.googleapis.com/AccessPolicy
google_access_context_manager_access_policy_iam_member accesscontextmanager.googleapis.com/AccessPolicy
google_access_context_manager_access_policy_iam_policy accesscontextmanager.googleapis.com/AccessPolicy
google_access_context_manager_service_perimeter accesscontextmanager.googleapis.com/ServicePerimeter
google_apigee_environment_iam_binding apigee.googleapis.com/Environment
google_apigee_environment_iam_member apigee.googleapis.com/Environment
google_apigee_environment_iam_policy apigee.googleapis.com/Environment
google_bigquery_dataset bigquery.googleapis.com/Dataset
google_bigquery_dataset_iam_binding bigquery.googleapis.com/Dataset
google_bigquery_dataset_iam_member bigquery.googleapis.com/Dataset
google_bigquery_dataset_iam_policy bigquery.googleapis.com/Dataset
google_bigquery_table bigquery.googleapis.com/Table
google_bigquery_table_iam_binding bigquery.googleapis.com/Table
google_bigquery_table_iam_member bigquery.googleapis.com/Table
google_bigquery_table_iam_policy bigquery.googleapis.com/Table
google_bigtable_instance bigtableadmin.googleapis.com/Cluster、bigtableadmin.googleapis.com/Instance
google_binary_authorization_attestor_iam_binding binaryauthorization.googleapis.com/Attestor
google_binary_authorization_attestor_iam_member binaryauthorization.googleapis.com/Attestor
google_binary_authorization_attestor_iam_policy binaryauthorization.googleapis.com/Attestor
google_cloud_run_domain_mapping run.googleapis.com/DomainMapping
google_cloud_run_service run.googleapis.com/Service
google_cloud_run_service_iam_binding run.googleapis.com/Service
google_cloud_run_service_iam_member run.googleapis.com/Service
google_cloud_run_service_iam_policy run.googleapis.com/Service
google_cloudfunctions_function cloudfunctions.googleapis.com/CloudFunction
google_cloudfunctions_function_iam_binding cloudfunctions.googleapis.com/CloudFunction
google_cloudfunctions_function_iam_member cloudfunctions.googleapis.com/CloudFunction
google_cloudfunctions_function_iam_policy cloudfunctions.googleapis.com/CloudFunction
google_compute_address compute.googleapis.com/Address
google_compute_backend_service_iam_binding compute.googleapis.com/BackendService
google_compute_backend_service_iam_member compute.googleapis.com/BackendService
google_compute_backend_service_iam_policy compute.googleapis.com/BackendService
google_compute_disk compute.googleapis.com/Disk
google_compute_disk_iam_binding compute.googleapis.com/Disk
google_compute_disk_iam_member compute.googleapis.com/Disk
google_compute_disk_iam_policy compute.googleapis.com/Disk
google_compute_firewall compute.googleapis.com/Firewall
google_compute_forwarding_rule compute.googleapis.com/ForwardingRule
google_compute_global_address compute.googleapis.com/GlobalAddress
google_compute_global_forwarding_rule compute.googleapis.com/GlobalForwardingRule
google_compute_image_iam_binding compute.googleapis.com/Image
google_compute_image_iam_member compute.googleapis.com/Image
google_compute_image_iam_policy compute.googleapis.com/Image
google_compute_instance compute.googleapis.com/Instance
google_compute_instance_iam_binding compute.googleapis.com/Instance
google_compute_instance_iam_member compute.googleapis.com/Instance
google_compute_instance_iam_policy compute.googleapis.com/Instance
google_compute_network compute.googleapis.com/Network
google_compute_region_backend_service_iam_binding compute.googleapis.com/RegionBackendService
google_compute_region_backend_service_iam_member compute.googleapis.com/RegionBackendService
google_compute_region_backend_service_iam_policy compute.googleapis.com/RegionBackendService
google_compute_region_disk_iam_binding compute.googleapis.com/RegionDisk
google_compute_region_disk_iam_member compute.googleapis.com/RegionDisk
google_compute_region_disk_iam_policy compute.googleapis.com/RegionDisk
google_compute_security_policy compute.googleapis.com/SecurityPolicy
google_compute_snapshot compute.googleapis.com/Snapshot
google_compute_ssl_policy compute.googleapis.com/SslPolicy
google_compute_subnetwork compute.googleapis.com/Subnetwork
google_compute_subnetwork_iam_binding compute.googleapis.com/Subnetwork
google_compute_subnetwork_iam_member compute.googleapis.com/Subnetwork
google_compute_subnetwork_iam_policy compute.googleapis.com/Subnetwork
google_container_cluster container.googleapis.com/Cluster
google_container_node_pool container.googleapis.com/NodePool
google_data_catalog_entry_group_iam_binding datacatalog.googleapis.com/EntryGroup
google_data_catalog_entry_group_iam_member datacatalog.googleapis.com/EntryGroup
google_data_catalog_entry_group_iam_policy datacatalog.googleapis.com/EntryGroup
google_data_catalog_tag_template_iam_binding datacatalog.googleapis.com/TagTemplate
google_data_catalog_tag_template_iam_member datacatalog.googleapis.com/TagTemplate
google_data_catalog_tag_template_iam_policy datacatalog.googleapis.com/TagTemplate
google_dns_managed_zone dns.googleapis.com/ManagedZone
google_dns_policy dns.googleapis.com/Policy
google_endpoints_service_consumers_iam_binding servicemanagement.googleapis.com/ServiceConsumers
google_endpoints_service_consumers_iam_member servicemanagement.googleapis.com/ServiceConsumers
google_endpoints_service_consumers_iam_policy servicemanagement.googleapis.com/ServiceConsumers
google_endpoints_service_iam_binding servicemanagement.googleapis.com/Service
google_endpoints_service_iam_member servicemanagement.googleapis.com/Service
google_endpoints_service_iam_policy servicemanagement.googleapis.com/Service
google_filestore_instance file.googleapis.com/Instance
google_folder_iam_binding cloudresourcemanager.googleapis.com/Folder
google_folder_iam_member cloudresourcemanager.googleapis.com/Folder
google_folder_iam_policy cloudresourcemanager.googleapis.com/Folder
google_folder_organization_policy cloudresourcemanager.googleapis.com/Folder
google_healthcare_consent_store_iam_binding healthcare.googleapis.com/ConsentStore
google_healthcare_consent_store_iam_member healthcare.googleapis.com/ConsentStore
google_healthcare_consent_store_iam_policy healthcare.googleapis.com/ConsentStore
google_iap_tunnel_iam_binding iap.googleapis.com/Tunnel
google_iap_tunnel_iam_member iap.googleapis.com/Tunnel
google_iap_tunnel_iam_policy iap.googleapis.com/Tunnel
google_iap_tunnel_instance_iam_binding iap.googleapis.com/TunnelInstance
google_iap_tunnel_instance_iam_member iap.googleapis.com/TunnelInstance
google_iap_tunnel_instance_iam_policy iap.googleapis.com/TunnelInstance
google_iap_web_iam_binding iap.googleapis.com/Web
google_iap_web_iam_member iap.googleapis.com/Web
google_iap_web_iam_policy iap.googleapis.com/Web
google_kms_crypto_key cloudkms.googleapis.com/CryptoKey
google_kms_crypto_key_iam_binding cloudkms.googleapis.com/CryptoKey
google_kms_crypto_key_iam_member cloudkms.googleapis.com/CryptoKey
google_kms_crypto_key_iam_policy cloudkms.googleapis.com/CryptoKey
google_kms_key_ring cloudkms.googleapis.com/KeyRing
google_kms_key_ring_iam_binding cloudkms.googleapis.com/KeyRing
google_kms_key_ring_iam_member cloudkms.googleapis.com/KeyRing
google_kms_key_ring_iam_policy cloudkms.googleapis.com/KeyRing
google_monitoring_alert_policy monitoring.googleapis.com/AlertPolicy
google_monitoring_notification_channel monitoring.googleapis.com/NotificationChannel
google_notebooks_instance_iam_binding notebooks.googleapis.com/Instance
google_notebooks_instance_iam_member notebooks.googleapis.com/Instance
google_notebooks_instance_iam_policy notebooks.googleapis.com/Instance
google_notebooks_runtime_iam_binding notebooks.googleapis.com/Runtime
google_notebooks_runtime_iam_member notebooks.googleapis.com/Runtime
google_notebooks_runtime_iam_policy notebooks.googleapis.com/Runtime
google_organization_iam_binding cloudresourcemanager.googleapis.com/Organization
google_organization_iam_custom_role iam.googleapis.com/Role
google_organization_iam_member cloudresourcemanager.googleapis.com/Organization
google_organization_iam_policy cloudresourcemanager.googleapis.com/Organization
google_organization_policy cloudresourcemanager.googleapis.com/Organization
google_privateca_ca_pool_iam_binding privateca.googleapis.com/CaPool
google_privateca_ca_pool_iam_member privateca.googleapis.com/CaPool
google_privateca_ca_pool_iam_policy privateca.googleapis.com/CaPool
google_privateca_certificate_template_iam_binding privateca.googleapis.com/CertificateTemplate
google_privateca_certificate_template_iam_member privateca.googleapis.com/CertificateTemplate
google_privateca_certificate_template_iam_policy privateca.googleapis.com/CertificateTemplate
google_project cloudbilling.googleapis.com/ProjectBillingInfo、cloudresourcemanager.googleapis.com/Project
google_project_iam_binding cloudresourcemanager.googleapis.com/Project
google_project_iam_custom_role iam.googleapis.com/Role
google_project_iam_member cloudresourcemanager.googleapis.com/Project
google_project_iam_policy cloudresourcemanager.googleapis.com/Project
google_project_organization_policy cloudresourcemanager.googleapis.com/Project
google_project_service serviceusage.googleapis.com/Service
google_pubsub_lite_reservation pubsublite.googleapis.com/Reservation
google_pubsub_lite_subscription pubsublite.googleapis.com/Subscription
google_pubsub_lite_topic pubsublite.googleapis.com/Topic
google_pubsub_schema pubsub.googleapis.com/Schema
google_pubsub_subscription pubsub.googleapis.com/Subscription
google_pubsub_subscription_iam_binding pubsub.googleapis.com/Subscription
google_pubsub_subscription_iam_member pubsub.googleapis.com/Subscription
google_pubsub_subscription_iam_policy pubsub.googleapis.com/Subscription
google_pubsub_topic pubsub.googleapis.com/Topic
google_pubsub_topic_iam_binding pubsub.googleapis.com/Topic
google_pubsub_topic_iam_member pubsub.googleapis.com/Topic
google_pubsub_topic_iam_policy pubsub.googleapis.com/Topic
google_redis_instance redis.googleapis.com/Instance
google_secret_manager_secret_iam_binding secretmanager.googleapis.com/Secret
google_secret_manager_secret_iam_member secretmanager.googleapis.com/Secret
google_secret_manager_secret_iam_policy secretmanager.googleapis.com/Secret
google_spanner_database spanner.googleapis.com/Database
google_spanner_database_iam_binding spanner.googleapis.com/Database
google_spanner_database_iam_member spanner.googleapis.com/Database
google_spanner_database_iam_policy spanner.googleapis.com/Database
google_spanner_instance spanner.googleapis.com/Instance
google_spanner_instance_iam_binding spanner.googleapis.com/Instance
google_spanner_instance_iam_member spanner.googleapis.com/Instance
google_spanner_instance_iam_policy spanner.googleapis.com/Instance
google_sql_database sqladmin.googleapis.com/Database
google_sql_database_instance sqladmin.googleapis.com/Instance
google_storage_bucket storage.googleapis.com/Bucket
google_storage_bucket_iam_binding storage.googleapis.com/Bucket
google_storage_bucket_iam_member storage.googleapis.com/Bucket
google_storage_bucket_iam_policy storage.googleapis.com/Bucket
google_vpc_access_connector vpcaccess.googleapis.com/Connector