Prima di iniziare
Framework dei vincoli
gcloud beta terraform vet
utilizza
policy Constraint Framework, che consistono in vincoli e modelli di vincolo. La
differenza tra i due è la seguente:
- Un modello di vincolo è come una dichiarazione di funzione: definisce una regola in Rego e, facoltativamente, accetta parametri di input.
- Un vincolo è un file che fa riferimento a un modello di vincolo e definisce i parametri di input da passare e le risorse coperte dal criterio.
In questo modo puoi evitare ripetizioni. Puoi scrivere un modello di vincolo con una policy generica, quindi scrivere un numero qualsiasi di vincoli che forniscono parametri di input diversi o regole di corrispondenza delle risorse diverse.
Crea un modello di vincolo
Per creare un modello di vincolo:
- Raccogli i dati di esempio.
- Scrivi Rego.
- Testa la tua Rego.
- Configura uno scheletro del modello di vincolo.
- Incorpora Rego.
- Configura un vincolo.
Raccogliere dati di esempio
Per scrivere un modello di vincolo, devi disporre di dati di esempio su cui operare. I vincoli basati su Terraform operano sui dati di modifica delle risorse, che provengono
dalla chiave resource_changes
del
JSON del piano Terraform.
Ad esempio, il file 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
}
Scrivere Rego
Dopo aver ottenuto 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 di 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.change.after.address_type} & allowed_address_types) >= 1
message := sprintf(
"Compute address %s has a disallowed address_type: %s",
[resource.address, resource.change.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. Ti consigliamo di seguire queste linee guida per la denominazione:
- Formato generale:
TF{resource}{feature}Constraint{version}
. Utilizza la notazione CamelCase. In altre parole, metti in maiuscolo ogni nuova parola. - Per i vincoli a una sola risorsa, segui le convenzioni del
provider Terraform
per la denominazione dei prodotti. Ad esempio, per
google_tags_tag
il nome del prodotto ètags
anche se il nome dell'API èresourcemanager
. - Se un modello si applica a più di un tipo di risorsa, ometti la parte della risorsa e includi solo la funzionalità (ad esempio: "TFAddressTypeAllowlistConstraintV1").
- Il numero di versione non segue il formato semver, ma è un singolo numero. In questo modo, ogni versione di un modello diventa un modello unico.
Ti consigliamo di utilizzare un nome per il file Rego che corrisponda al nome del modello di vincolo, ma utilizzando snake_case. In altre parole, converti il nome in
minuscolo e separa le parole con _
. Per l'esempio precedente, il nome file consigliato è tf-compute-address-address-type-allowlist-constraint-v1.rego
Testare Rego
Puoi testare Rego manualmente con Rego Playground. Assicurati di utilizzare dati non sensibili.
Ti consigliamo di scrivere
test automatizzati.
Inserisci i dati di esempio raccolti in validator/test/fixtures/<constraint
filename>/resource_changes/data.json
e fai riferimento a questi nel file di test come segue:
# 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 Rego e il test nella cartella validator
della libreria dei criteri.
Configurare lo scheletro di un modello di vincolo
Dopo aver creato e testato una regola Rego funzionante, devi pacchettizzarla come modello di vincolo. Constraint Framework utilizza le definizioni di risorse personalizzate di Kubernetes come contenitore per le norme Rego.
Il modello di vincolo definisce anche quali parametri sono consentiti come input dai vincoli, utilizzando lo schema OpenAPI V3.
Utilizza lo stesso nome per lo scheletro che hai utilizzato per Rego. In particolare:
- Utilizza lo stesso nome file del tuo Rego. Esempio:
tf-compute-address-address-type-allowlist-constraint-v1.yaml
spec.crd.spec.names.kind
deve contenere il nome del modellometadata.name
deve contenere il nome del modello, ma in minuscolo
Inserisci lo scheletro del modello di vincolo in policies/templates
.
Ad esempio:
# 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
Incorpora Rego
A questo punto, seguendo l'esempio precedente, il layout della directory è il seguente:
| 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 della libreria di criteri fornito da Google,
puoi eseguire make build
per aggiornare automaticamente i modelli di vincolo in
policies/templates
con Rego definito in validator
.
Configurare un vincolo
I vincoli contengono tre informazioni che gcloud beta terraform vet
deve
avere per applicare e segnalare correttamente le violazioni:
severity
:low
,medium
ohigh
match
: parametri per determinare se un vincolo si applica a una risorsa specifica. Sono supportati i seguenti parametri di corrispondenza:addresses
: Un elenco di indirizzi delle risorse da includere utilizzando la corrispondenza in stile globexcludedAddresses
: (facoltativo) un elenco di indirizzi delle risorse da escludere utilizzando la corrispondenza in stile glob.
parameters
: valori per i 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 indirizzo 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 di corrispondenza:
Corrispondenza indirizzo | Descrizione |
---|---|
module.** |
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 l'indirizzo di una risorsa corrisponde ai valori di addresses
e excludedAddresses
, viene escluso.
Limitazioni
I dati del piano Terraform forniscono la migliore rappresentazione disponibile dello stato effettivo dopo l'applicazione. Tuttavia, in molti casi, lo stato dopo l'applicazione potrebbe non essere noto perché viene calcolato lato server.
La creazione di percorsi di discendenza CAI fa parte della procedura di convalida delle norme. Utilizza
il progetto predefinito fornito per aggirare gli ID progetto sconosciuti. Nel caso in cui non venga fornito un progetto predefinito, il percorso di discendenza è impostato su organizations/unknown
.
Puoi vietare l'ascendenza sconosciuta aggiungendo il seguente vincolo:
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: {}
Risorse supportate
Puoi creare vincoli di modifica delle risorse per qualsiasi risorsa Terraform da qualsiasi provider Terraform.