准备工作
限制条件框架
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 Inventory 和 gcloud beta terraform vet
是否均支持您要为其编写政策的资产。
创建限制条件模板需要执行以下步骤:
- 收集样本数据。
- 编写 Rego。
- 测试 Rego。
- 设置限制条件模板框架。
- 内嵌 Rego。
- 设置限制条件。
收集样本数据
要编写限制条件模板,您需要具有可供操作的样本数据。基于 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
正确地强制执行政策并报告违规行为所需的三项信息:
severity
:low
、medium
或high
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 的所有内容 |
如果资源地址与 ancestries
和 excludedAncestries
中的值匹配,则系统会排除该资源。
限制
应用后的 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 |