Créer des contraintes CAI

Avant de commencer

Framework de contrainte

gcloud beta terraform vet utilise les règles du framework de contrainte, constitué de contraintes et de modèles de contrainte. La différence entre les deux est la suivante :

  • Un modèle de contrainte est semblable à une déclaration de fonction : il définit une règle dans Rego et accepte éventuellement les paramètres d'entrée.
  • Une contrainte est un fichier qui référence un modèle de contrainte et définit les paramètres d'entrée à lui transmettre et les ressources couvertes par la règle.

Cela vous permet d'éviter la redondance. Vous pouvez écrire un modèle de contrainte avec une règle générique, puis écrire un nombre illimité de contraintes fournissant des paramètres d'entrée différents ou des règles de correspondance des ressources différentes.

Éléments CAI et ressources Terraform

Les éléments d'inventaire des éléments cloud constituent un format standard d'exportation de données Google disponible pour de nombreuses ressources Google Cloud. Les données CAI ne sont généralement disponibles qu'après la création ou la mise à jour d'une ressource. Toutefois, en convertissant les modifications de ressources Terraform en données d'éléments CAI, gcloud beta terraform vet vous permet d'écrire une stratégie une fois et de l'utiliser avant l'application et en tant que vérification d'audit avec des outils compatibles.

Outils compatibles

Les outils suivants ne sont pas des produits Google officiels et ne sont pas compatibles. Cependant, ils peuvent être compatibles avec des règles écrites pour gcloud beta terraform vet :

Créer un modèle de contrainte

Avant de développer votre modèle de contrainte, vérifiez que l'élément pour lequel vous souhaitez écrire une règle est compatible avec l'inventaire des éléments cloud et gcloud beta terraform vet.

La procédure de création d'un modèle de contrainte est la suivante :

  1. Collecter des exemples de données
  2. Écrire dans Rego
  3. Tester votre Rego
  4. Configurer le squelette d'un modèle de contrainte
  5. Intégrer votre Rego
  6. Configurer une contrainte

Collecter des exemples de données

Pour écrire un modèle de contrainte, vous devez disposer d'exemples de données à utiliser. Les contraintes basées sur CAI fonctionnent sur des données d'éléments CAI. Collectez des exemples de données en créant des ressources du type approprié et en les exportant au format JSON, comme décrit dans le guide de démarrage rapide de CAI.

Voici un exemple d'exportation au format JSON pour une adresse de calcul:

[
  {
    "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"
      }
    }
  },
]

Écrire dans Rego

Une fois que vous disposez d'exemples de données, vous pouvez écrire la logique de votre modèle de contrainte dans Rego. Votre Rego doit être associé à une règle violations. L'élément en cours d'examen est disponible sous la forme input.review. Les paramètres de contrainte sont disponibles sous la forme input.parameters. Par exemple, pour exiger que les éléments compute.googleapis.com/Address aient un addressType autorisé, écrivez:

# 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}
}

Attribuer un nom à votre modèle de contrainte

L'exemple précédent utilise le nom GCPComputeAddressAddressTypeAllowlistConstraintV1. Il s'agit d'un identifiant unique pour chaque modèle de contrainte. Nous vous recommandons de suivre ces consignes sur l'attribution de noms :

  • Format général : GCP{resource}{feature}Constraint{version}. Utilisez CamelCase. (En d'autres termes, utilisez une majuscule pour chaque nouveau mot.)
  • Pour les contraintes de ressource unique, suivez les noms de groupe gcloud pour la dénomination de ressources. Par exemple, utilisez "compute" au lieu de "gce", "sql" au lieu de "cloud-sql" et "container-cluster" au lieu de "gke".
  • Si un modèle s'applique à plusieurs types de ressources, omettez la partie ressource et n'incluez que la caractéristique (exemple: "GCPAddressTypeAllowlistConstraintV1").
  • Le numéro de version ne suit pas le format semver. C'est un nombre unique. Ainsi, chaque version d'un modèle est unique.

Nous vous recommandons d'utiliser un nom pour votre fichier Rego qui correspond au nom du modèle de contrainte, mais en utilisant snake_case. En d'autres termes, convertissez le nom en mots distincts en minuscules avec _. Pour l'exemple précédent, le nom de fichier recommandé est gcp_compute_address_address_type_allowlist_constraint_v1.rego.

Tester votre Rego

Vous pouvez tester votre Rego manuellement avec Rego Playground. Veillez à utiliser des données non sensibles.

Nous vous recommandons d'écrire des tests automatisés. Placez les exemples de données collectés dans validator/test/fixtures/<constraint filename>/assets/data.json et référencez-les dans votre fichier de test comme suit :

# 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
}

Placez votre Rego et votre test dans le dossier validator de votre bibliothèque de règles.

Configurer le squelette d'un modèle de contrainte

Une fois que vous disposez d'une règle Rego opérationnelle et testée, vous devez l'empaqueter en tant que modèle de contrainte. Le framework des contraintes utilise les définitions de ressources personnalisées Kubernetes en tant que conteneur pour la règle Rego.

Le modèle de contrainte définit également les paramètres qui sont autorisés en tant qu'entrées de contraintes, à l'aide du schéma OpenAPI V3.

Utilisez le même nom pour le squelette que celui que vous avez utilisé pour votre Rego. En particulier :

  • Utilisez le même nom de fichier que pour votre Rego. Exemple : gcp_compute_address_address_type_allowlist_constraint_v1.yaml
  • spec.crd.spec.names.kind doit contenir le nom du modèle
  • metadata.name doit contenir le nom du modèle, mais en minuscules.

Placez le squelette de modèle de contrainte dans policies/templates.

Pour l'exemple ci-dessus:

# 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

Intégrer votre Rego

À ce stade, après l'exemple précédent, la disposition de votre répertoire ressemble à ceci:

| 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

Si vous avez cloné le Dépôt de bibliothèques de règles fourni par Google, vous pouvez exécuter make build pour mettre à jour automatiquement vos modèles de contraintes dans policies/templates avec le Rego défini dans le fichier validator.

Configurer une contrainte

Les contraintes contiennent trois informations que gcloud beta terraform vet doit appliquer et signaler correctement les violations:

  • severity : low, medium ou high.
  • match: paramètres permettant de déterminer si une contrainte s'applique à une ressource particulière. Les paramètres de correspondance suivants sont acceptés :
    • ancestries : liste des chemins d'accès ancêtres à inclure à l'aide de la correspondance de style glob.
    • excludedAncestries (facultatif) : liste des chemins d'accès ancêtres à exclure à l'aide de la correspondance de style glob.
  • parameters: valeurs des paramètres d'entrée du modèle de contrainte.

Assurez-vous que kind contient le nom du modèle de contrainte. Nous vous recommandons de définir metadata.name sur un slug descriptif.

Par exemple, pour n'autoriser que les types d'adresses INTERNAL à l'aide de l'exemple de modèle de contrainte précédent, écrivez:

# 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"

Exemples de correspondance :

Outil de mise en correspondance des chemins d'accès ancêtres Description
organizations/** Toutes les organisations
organizations/123/** Tous les éléments de l'organisation 123
organizations/123/folders/** Tous les éléments d'un dossier de l'organisation 123
organizations/123/folders/456 Tous les éléments du dossier 456 de l'organisation 123
organizations/123/folders/456/projects/789 Tous les éléments du projet 789 dans le dossier 456 de l'organisation 123

Si une adresse de ressource correspond aux valeurs de ancestries et excludedAncestries, elle est exclue.

Limites

Les données du plan Terraform fournissent la meilleure représentation disponible de l'état réel après application. Toutefois, dans de nombreux cas, l'état après l'application peut ne pas être connu, car il est calculé côté serveur. Dans ces cas, les données ne sont pas non plus disponibles dans les éléments CAI convertis.

La création de chemins d'accès ancêtre CAI fait partie du processus de validation des stratégies. Ce processus utilise le projet par défaut fourni pour contourner les ID de projet inconnus. Dans le cas où aucun projet par défaut n'est fourni, le chemin d'accès ancêtre est défini par défaut sur organizations/unknown.

Vous pouvez interdire les ancêtres inconnus en ajoutant la contrainte suivante :

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: {}

Ressources compatibles

Si vous souhaitez que gcloud beta terraform vet soit compatible avec une ressource qui ne figure pas dans cette liste, ouvrez une demande d'amélioration ; vous pouvez si vous le souhaitez contribuer à la création du code.

La liste des ressources compatibles dépend de la version de gcloud beta terraform vet installée. Voici la liste actuelle des ressources acceptées :

Ressource Terraform Éléments de l'inventaire des éléments 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