Crea vincoli Terraform

Prima di iniziare

Framework del vincolo

gcloud beta terraform vet utilizza i criteri di Vincolo di vincoli, che includono vincoli e modelli di vincolo. La differenza tra i due è la seguente:

  • Un modello di vincolo è come una dichiarazione di funzione; definisce una regola e, facoltativamente, accetta variabili come input.
  • Un vincolo è un file che fa riferimento a un modello di vincolo e definisce i valori da utilizzare.

Creare un modello di vincolo

1. Raccogli dati di esempio

Per scrivere un modello di vincolo, è necessario disporre di dati di esempio su cui operare. I vincoli basati su Terraform operano sui dati di modifica delle risorse, provenienti dalla chiave resource_changes del piano JSON di Teraform.

Ad esempio, il tuo JSON potrebbe avere il seguente aspetto:

// 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. Scrittura Rego

Dopo aver recuperato i dati di esempio, puoi scrivere la logica per il modello di vincolo in Rego. Il tuo Rego deve avere una regola violations. La modifica della risorsa in fase di revisione è disponibile come input.review. I parametri del vincolo sono disponibili come input.parameters. Ad esempio, per richiedere che le risorse google_compute_address abbiano un address_type consentito, scrivi:

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

Assegna un nome al modello di vincolo

L'esempio precedente utilizza il nome TFComputeAddressAddressTypeAllowlistConstraintV1. Si tratta di un identificatore univoco per ogni modello di vincolo. Consigliamo di seguire queste linee guida di denominazione:

  • Formato generale: TF{resource}{feature}Constraint{version}. Usa CamelCase. In altre parole, utilizza le maiuscole per ogni nuova parola.
  • Per i vincoli relativi a risorse singole, segui le convenzioni del fornitore Teraform per la denominazione dei prodotti. Ad esempio, per google_tags_tag il nome del prodotto è tags anche se il nome API è resourcemanager.
  • Se un modello è valido per più tipi di risorse, ometti la parte della risorsa e includi solo la funzionalità (ad esempio: &TFt;TFAddressTypeAllowlistConstraintV1").
  • Il numero di versione non segue il modulo semver; si tratta solo di un numero. In questo modo, ogni versione di un modello diventa un modello unico.

Ti consigliamo di utilizzare per il file rego un nome che corrisponda al nome del modello di vincolo, ma usi snake_case. In altre parole, converti il nome in parole minuscole con _. Per l'esempio precedente, il nome del file consigliato è tf_compute_address_address_type_allowlist_constraint_v1.rego

3. Testa la tua Rego

Puoi testare Rego manualmente con Rego Playground. Assicurati di utilizzare dati non sensibili.

Consigliamo di scrivere test automatici. Inserisci i dati di esempio raccolti in validator/test/fixtures/<constraint filename>/resource_changes/data.json e fa riferimento a questi nel file di test, in questo modo:

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

Inserisci il test e la verifica nella cartella validator della libreria dei criteri.

4. Configurare uno scheletro di un modello di vincolo

Dopo aver testato e verificato una regola Rego, devi comprimerla come modello di vincolo. Il framework del vincolo utilizza le definizioni delle risorse personalizzate di Kubernetes come contenitore del criterio Rego.

Il modello di vincolo definisce anche i parametri consentiti come input da vincoli, utilizzando lo schema OpenAPI V3.

Usa lo stesso nome dello scheletro che hai usato per il Rego. In particolare:

  • Utilizza lo stesso nome file che hai scelto per il tuo sistema. Esempio: tf_compute_address_address_type_allowlist_constraint_v1.yaml
  • spec.crd.spec.names.kind deve contenere il nome del modello
  • metadata.name deve contenere il nome del modello, ma con lettere minuscole

Inserisci lo scheletro del modello di vincolo in policies/templates.

Per l'esempio riportato sopra:

# 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. Integra la tua risorsa

A questo punto, seguendo l'esempio precedente, il layout della directory ha il seguente aspetto:

| 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

Se hai clonato il repository delle librerie di criteri fornito da Google, puoi eseguire make build per aggiornare automaticamente i tuoi modelli di vincolo in policies/templates con il rego definito in validator.

6. Impostare un vincolo

I vincoli contengono tre informazioni che gcloud beta terraform vet deve applicare correttamente e segnalare le violazioni:

  • severity: low, medium o high
  • match: parametri per determinare se un vincolo si applica a una particolare risorsa. Sono supportati i seguenti parametri di corrispondenza:
    • addresses: un elenco di indirizzi di risorse da includere utilizzando la corrispondenza in stile globo
    • (Facoltativo) excludedAddresses: un elenco di indirizzi delle risorse da escludere utilizzando la corrispondenza in stile globo.
  • parameters: valori dei parametri di input del modello di vincolo.

Assicurati che kind contenga il nome del modello di vincolo. Ti consigliamo di impostare metadata.name su uno slug descrittivo.

Ad esempio, per consentire solo i tipi di indirizzi INTERNAL utilizzando il modello di vincolo dell'esempio precedente, scrivi:

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

Esempi corrispondenti:

Matcher indirizzo Descrizione
modulo.** Tutte le risorse in qualsiasi modulo
module.my_module.** Tutto nel modulo "my_module"
**.google_compute_global_forwarding_rule.* Tutte le risorse google_compute_global_forwarding_rule in qualsiasi modulo
module.my_module.google_compute_global_forwarding_rule.*" Tutte le risorse google_compute_global_forwarding_rule in "my_module"

Se un indirizzo di risorsa corrisponde a valori in addresses e excludedAddresses, è escluso.

Limitazioni

I dati dei piani Terraform forniscono la migliore rappresentazione disponibile dello stato effettivo dopo l'applicazione; tuttavia, in molti casi lo stato successivo all'applicazione potrebbe non essere noto perché viene calcolato sul lato server.

Risorse supportate

Puoi creare vincoli per le modifiche alle risorse da qualsiasi provider di google e google-beta di Terraform.