CAI 제약조건 만들기

시작하기 전에

제약조건 프레임워크

gcloud beta terraform vet제약조건제약조건 템플릿으로 구성된 제약조건 프레임워크 정책을 사용합니다. 두 구성요소의 차이점은 다음과 같습니다.

  • 제약조건 템플릿은 함수 선언과 유사합니다. Rego에서 규칙을 정의하고 선택적으로 입력 매개변수를 사용합니다.
  • 제약조건은 제약조건 템플릿을 참조하고 전달할 입력 매개변수와 정책이 적용되는 리소스를 정의하는 파일입니다.

이렇게 하면 반복을 방지할 수 있습니다. 일반 정책으로 제약조건 템플릿을 작성한 후 다른 입력 매개변수나 다른 리소스 일치 규칙을 제공하는 제약조건을 임의의 수만큼 작성할 수 있습니다.

CAI 애셋과 Terraform 리소스 비교

Cloud 애셋 인벤토리 애셋은 많은 Google Cloud 리소스에 사용할 수 있는 표준 Google 데이터 내보내기 형식입니다. CAI 데이터는 일반적으로 리소스가 생성되거나 업데이트된 후에만 사용할 수 있습니다. 하지만 Terraform 리소스 변경사항을 CAI 애셋 데이터로 변환하면 gcloud beta terraform vet을 통해 정책을 한 번 작성하고 호환되는 도구를 사용하여 적용하기 전과 감사 검사 모두에서 사용할 수 있습니다.

호환 도구

다음 도구는 공식 Google 제품이 아니며 지원되지 않습니다. 그러나 gcloud beta terraform vet용으로 작성된 정책과 호환될 수 있습니다.

제약조건 템플릿 만들기

제약조건 템플릿을 개발하기 전에 정책을 작성할 애셋이 Cloud 애셋 인벤토리gcloud beta terraform vet 모두에서 지원되는지 확인하세요.

제약조건 템플릿을 만들려면 다음 단계를 수행합니다.

  1. 샘플 데이터 수집
  2. Rego 쓰기
  3. Rego 테스트
  4. 제약조건 템플릿 스켈레톤 설정
  5. Rego 인라인 처리
  6. 제약조건 설정

샘플 데이터 수집

제약조건 템플릿을 작성하려면 작동할 샘플 데이터가 있어야 합니다. CAI 기반 제약조건은 CAI 애셋 데이터에 대해 작동합니다. CAI 빠른 시작에 설명된 대로 적절한 유형의 리소스를 만들고 이 리소스를 JSON으로 내보내 샘플 데이터를 수집합니다.

다음은 Compute Address용 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 그룹 이름을 따라 리소스 이름을 지정합니다. 예를 들어 'gce' 대신 'compute', 'cloud-sql' 대신 'sql', 'gke' 대신 'container-cluster'를 사용합니다.
  • 템플릿이 2개 이상의 리소스 유형에 적용되는 경우 리소스 부분을 생략하고 특성만 포함합니다(예: 'GCPAddressTypeAllowlistConstraintV1').
  • 버전 번호는 semver 양식을 따르지 않습니다. 단일 숫자일 뿐입니다. 이렇게 하면 효과적으로 템플릿의 모든 버전을 고유한 템플릿으로 만들 수 있습니다.

제약조건 템플릿 이름과 일치하지만 snake_case를 사용하는 Rego 파일의 이름을 사용하는 것이 좋습니다. 즉, _로 구분되는 이름을 소문자 단어로 변환합니다. 이전 예시에서 권장 파일 이름은 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를 실행하여 policies/templates의 제약조건 템플릿을 validator에서 정의된 Rego와 함께 자동으로 업데이트할 수 있습니다.

제약조건 설정

제약조건에는 gcloud beta terraform vet이 위반사항을 올바르게 시행하고 보고하는 데 필요한 세 가지 정보가 포함됩니다.

  • severity: low, medium 또는 high
  • match: 제약조건이 특정 리소스에 적용되는지 결정하기 위한 매개변수입니다. 지원되는 일치 매개변수는 다음과 같습니다.
    • ancestries: glob 스타일 일치를 사용하여 포함할 상위 경로 목록
    • excludedAncestries: (선택사항) glob 스타일 일치를 사용하여 제외할 상위 경로 목록
  • parameters: 제약조건 템플릿의 입력 매개변수 값

kind에 제약조건 템플릿 이름이 있는지 확인합니다. metadata.name을 설명이 포함된 슬러그로 설정하는 것이 좋습니다.

예를 들어 이전 예시 제약조건 템플릿을 사용하여 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 애셋 인벤토리 애셋
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