Terraform-Einschränkungen erstellen

Vorbereitung

Einschränkungs-Framework

gcloud beta terraform vet verwendet Constraint Framework-Richtlinien, die aus Einschränkungen und Einschränkungsvorlagen bestehen. So unterscheiden sich die beiden:

  • Eine Einschränkungsvorlage ist wie eine Funktionsdeklaration. Sie definiert eine Regel in Rego und kann optional Eingabeparameter verwenden.
  • Eine Einschränkung ist eine Datei, die auf eine Einschränkungsvorlage verweist und die Eingabeparameter definiert, die an sie und die von der Richtlinie abgedeckten Ressourcen übergeben werden.

Damit können Sie Wiederholungen vermeiden. Sie können eine Einschränkungsvorlage mit einer allgemeinen Richtlinie erstellen und dann eine beliebige Anzahl von Einschränkungen schreiben, die unterschiedliche Eingabeparameter oder unterschiedliche Regeln für den Ressourcenabgleich enthalten.

Einschränkungsvorlage erstellen

So erstellen Sie eine Einschränkungsvorlage:

  1. Erfassen Sie Beispieldaten.
  2. Schreiben Sie Rego.
  3. Testen Sie Ihr Rego.
  4. Richten Sie eine Basis-Einschränkungsvorlage ein.
  5. Binden Sie Ihr Rego ein.
  6. Richten Sie eine Einschränkung ein.

Beispieldaten erfassen

Um eine Einschränkungsvorlage schreiben zu können, benötigen Sie Beispieldaten. Terraform-basierte Einschränkungen arbeiten mit Daten zu Ressourcenänderungen, die aus dem Schlüssel resource_changes von Terraform-Plan JSON stammen.

Ihre JSON-Datei könnte beispielsweise so aussehen:

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

Rego schreiben

Nachdem Sie Beispieldaten haben, können Sie die Logik für Ihre Einschränkungsvorlage in Rego schreiben. Ihr Rego muss eine violations-Regel enthalten. Die geprüfte Ressourcenänderung ist als input.review verfügbar. Einschränkungsparameter sind als input.parameters verfügbar. Wenn Sie beispielsweise anfordern möchten, dass google_compute_address-Ressourcen einen zulässigen address_type haben, schreiben Sie:

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

Einschränkungsvorlage benennen

Im vorherigen Beispiel wird der Name TFComputeAddressAddressTypeAllowlistConstraintV1 verwendet. Dies ist eine eindeutige Kennung für jede Einschränkungsvorlage. Wir empfehlen, diese Richtlinien für Namen zu verwenden:

  • Allgemeines Format: TF{resource}{feature}Constraint{version}. CamelCase verwenden. (Beginnen Sie also jedes neue Wort mit einem Großbuchstaben.)
  • Beachten Sie bei Einschränkungen für einzelne Ressourcen die Konventionen für Terraform-Anbieter für die Produktbenennung. Bei google_tags_tag lautet der Produktname beispielsweise tags, obwohl der API-Name resourcemanager lautet.
  • Wenn eine Vorlage für mehr als einen Ressourcentyp gilt, lassen Sie den Ressourcenteil weg und fügen Sie nur das Feature ein (Beispiel: "TFAddressTypeAllowlistConstraintV1").
  • Die Versionsnummer folgt nicht der semver-Form. nur eine einzige Zahl. Dadurch wird jede Version einer Vorlage zu einer eindeutigen Vorlage.

Wir empfehlen, für Ihre Rego-Datei einen Namen zu verwenden, der mit dem Namen der Einschränkungsvorlage übereinstimmt, aber "snake_case" verwendet. Mit anderen Worten, konvertieren Sie den Namen mit _ in Kleinbuchstaben. Im obigen Beispiel lautet der empfohlene Dateiname tf_compute_address_address_type_allowlist_constraint_v1.rego

Rego testen

Sie können Ihr Rego manuell mit Rego Playground testen. Verwenden Sie nicht vertrauliche Daten.

Wir empfehlen, automatisierte Tests zu schreiben. Fügen Sie Ihre gesammelten Beispieldaten in validator/test/fixtures/<constraint filename>/resource_changes/data.json ein und verweisen Sie in Ihrer Testdatei so darauf:

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

Platzieren Sie den Rego und den Test im Ordner validator Ihrer Richtlinienbibliothek.

Basis-Einschränkungsvorlage einrichten

Nachdem Sie eine funktionierende und getestete Rego-Regel haben, müssen Sie diese als Einschränkungsvorlage verpacken. Constraint Framework verwendet benutzerdefinierte Kubernetes-Ressourcendefinitionen als Container für den Richtlinien-Rego.

Die Einschränkungsvorlage definiert mithilfe des Schemas OpenAPI V3 auch, welche Parameter als Eingaben von Einschränkungen zulässig sind.

Verwenden Sie für das Basisschema denselben Namen wie für den Rego. Und zwar:

  • Verwenden Sie denselben Dateinamen wie für Ihren Rego. Beispiel: tf_compute_address_address_type_allowlist_constraint_v1.yaml
  • spec.crd.spec.names.kind muss den Vorlagennamen enthalten
  • metadata.name muss den Vorlagennamen enthalten, muss aber kleingeschrieben werden

Platzieren Sie das Basisschema der Einschränkungsvorlage in policies/templates.

Für das obige Beispiel:

# 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

Ihr Rego einbinden

In Anlehnung an das vorherige Beispiel sieht Ihr Verzeichnislayout nun folgendermaßen aus:

| 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

Wenn Sie das von Google bereitgestellte Repository für die Richtlinienbibliothek geklont haben, können Sie make build ausführen, um Ihre Einschränkungsvorlagen in policies/templates automatisch mit den in validator definierten Rego zu aktualisieren.

Eine Einschränkung einrichten

Einschränkungen enthalten drei Informationen, die gcloud beta terraform vet ordnungsgemäß erzwingen und Verstöße melden muss:

  • severity: low, medium oder high
  • match: Parameter, um zu bestimmen, ob eine Einschränkung für eine bestimmte Ressource gilt. Die folgenden Übereinstimmungsparameter werden unterstützt:
    • addresses: Eine Liste der Ressourcenadressen, die mithilfe des Glob-Stils einbezogen werden sollen
    • excludedAddresses: (Optional) Eine Liste von Ressourcenadressen, die mithilfe des Glob-Stils ausgeschlossen werden sollen.
  • parameters: Werte für die Eingabeparameter der Einschränkungsvorlage.

Achten Sie darauf, dass kind den Namen der Einschränkungsvorlage enthält. Wir empfehlen, metadata.name auf eine beschreibende Slug festzulegen.

Wenn Sie beispielsweise nur INTERNAL-Adresstypen mit der vorherigen Beispieleinschränkungsvorlage zulassen möchten, schreiben Sie:

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

Beispiele für Übereinstimmungen:

Adress-Matcher Beschreibung
"module.**" Alle Ressourcen in einem beliebigen Modul
"module.my_module.**" Alles im Modul "my_module"
`**.google_compute_global_forwarding_rule.*` Alle google_compute_global_forwarding_rule-Ressourcen in einem beliebigen Modul
`module.my_module.google_compute_global_forwarding_rule.*` Alle google_compute_global_forwarding_rule-Ressourcen in "my_module"

Wenn eine Ressourcenadresse mit den Werten in addresses und excludedAddresses übereinstimmt, wird sie ausgeschlossen.

Beschränkungen

Die Daten eines Terraform-Plans liefern nach dem Anwenden die beste verfügbare Darstellung des tatsächlichen Status. In vielen Fällen ist der Zustand nach dem Anwenden jedoch möglicherweise nicht bekannt, da er serverseitig berechnet wird.

Das Erstellen von CAI-Herkunftspfaden ist Teil des Prozesses, wenn Richtlinien validiert werden. Dabei wird das Standardprojekt verwendet, um unbekannte Projekt-IDs zu umgehen. Wenn kein Standardprojekt angegeben ist, wird der Herkunftspfad standardmäßig auf organizations/unknown gesetzt.

Sie können unbekannte Herkunft verbieten, indem Sie die folgende Einschränkung hinzufügen:

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

Unterstützte Ressourcen

Sie können Einschränkungen für die Ressourcenänderung für jede Terraform-Ressource von jedem Terraform-Anbieter erstellen.