Antes de começar
Framework de restrições
gcloud beta terraform vet
usa políticas do
Constraint Framework, que consistem em restrições e modelos de restrições. A diferença entre os dois é a seguinte:
- Um modelo de restrição é como uma declaração de função; define uma regra em Rego e, opcionalmente, usa parâmetros de entrada.
- Uma restrição é um ficheiro que faz referência a um modelo de restrição e define os parâmetros de entrada a transmitir ao mesmo, bem como os recursos abrangidos pela política.
Isto permite-lhe evitar a repetição. Pode escrever um modelo de restrição com uma política genérica e, em seguida, escrever qualquer número de restrições que forneçam diferentes parâmetros de entrada ou regras de correspondência de recursos diferentes.
Crie um modelo de restrição
Para criar um modelo de restrição, siga estes passos:
- Recolha dados de amostra.
- Escreva Rego.
- Teste o Rego.
- Configure um esqueleto de modelo de restrição.
- Incorporar o Rego.
- Configure uma restrição.
Recolha dados de amostra
Para escrever um modelo de restrição, tem de ter dados de exemplo para operar. As restrições baseadas no Terraform operam em dados de alteração de recursos, que provêm
da chave resource_changes
do
JSON do plano do Terraform.
Por exemplo, o seu JSON pode ter o seguinte aspeto:
// 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
}
Write Rego
Depois de ter dados de exemplo, pode escrever a lógica para o modelo de restrição
em
Rego.
O seu Rego tem de ter uma regra violations
. A alteração de recurso que está a ser revista está
disponível como input.review
. Os parâmetros de restrição estão disponíveis como
input.parameters
. Por exemplo, para exigir que os recursos google_compute_address
tenham um address_type
permitido, escreva:
# 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}
}
Atribua um nome ao modelo de restrição
O exemplo anterior usa o nome
TFComputeAddressAddressTypeAllowlistConstraintV1
. Este valor é um identificador exclusivo
para cada modelo de restrição. Recomendamos que siga estas diretrizes de nomenclatura:
- Formato geral:
TF{resource}{feature}Constraint{version}
. Use CamelCase. (Por outras palavras, use maiúsculas em cada nova palavra.) - Para restrições de recursos únicos, siga as convenções de nomenclatura de produtos do fornecedor do Terraform. Por exemplo, para
google_tags_tag
, o nome do produto étags
, apesar de o nome da API serresourcemanager
. - Se um modelo se aplicar a mais do que um tipo de recurso, omita a parte do recurso e inclua apenas a funcionalidade (exemplo: "TFAddressTypeAllowlistConstraintV1").
- O número da versão não segue o formato semver. É apenas um único número. Isto torna efetivamente cada versão de um modelo num modelo único.
Recomendamos que use um nome para o ficheiro Rego que corresponda ao nome do modelo de restrição, mas que use snake_case. Por outras palavras, converta o nome em letras minúsculas e separe as palavras com _
. Para o exemplo anterior, o nome do ficheiro recomendado é tf-compute-address-address-type-allowlist-constraint-v1.rego
Teste o seu Rego
Pode testar o Rego manualmente com o Rego Playground. Certifique-se de que usa dados não confidenciais.
Recomendamos que escreva testes automatizados.
Coloque os dados de exemplo recolhidos em validator/test/fixtures/<constraint
filename>/resource_changes/data.json
e faça referência aos mesmos no ficheiro de teste da seguinte forma:
# 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
}
Coloque o Rego e o teste na pasta validator
na biblioteca de políticas.
Configure um esqueleto de modelo de restrição
Depois de ter uma regra Rego funcional e testada, tem de a agrupar como um modelo de restrição. A framework de restrições usa definições de recursos personalizados do Kubernetes como o contentor para a política Rego.
O modelo de restrição também define que parâmetros são permitidos como entradas de restrições, usando o esquema OpenAPI V3.
Use o mesmo nome para o esqueleto que usou para o Rego. Concretamente:
- Use o mesmo nome de ficheiro que usou para o Rego. Exemplo:
tf-compute-address-address-type-allowlist-constraint-v1.yaml
spec.crd.spec.names.kind
tem de conter o nome do modelometadata.name
tem de conter o nome do modelo, mas em letras minúsculas
Coloque o esqueleto do modelo de restrição em policies/templates
.
Por exemplo:
# 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
Incorporar o seu Rego
Neste ponto, seguindo o exemplo anterior, a disposição do diretório tem o seguinte aspeto:
| 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 clonou o
repositório da biblioteca de políticas fornecido pela Google,
pode executar make build
para atualizar automaticamente os modelos de restrições em
policies/templates
com o Rego definido em validator
.
Configure uma restrição
As restrições contêm três informações que o gcloud beta terraform vet
precisa para aplicar e comunicar violações corretamente:
severity
:low
,medium
ouhigh
match
: parâmetros para determinar se uma restrição se aplica a um recurso específico. Os seguintes parâmetros de correspondência são suportados:addresses
: uma lista de endereços de recursos a incluir através da correspondência no estilo globexcludedAddresses
: (Opcional) Uma lista de endereços de recursos a excluir usando a correspondência no estilo glob.
parameters
: valores dos parâmetros de entrada do modelo de restrição.
Certifique-se de que kind
contém o nome do modelo de restrição. Recomendamos que defina metadata.name
como um slug descritivo.
Por exemplo, para permitir apenas tipos de endereços INTERNAL
usando o modelo de restrição do exemplo anterior, escreva:
# 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"
Exemplos de correspondência:
Localizador de moradas | Descrição |
---|---|
module.** |
Todos os recursos em qualquer módulo |
module.my_module.**
|
Tudo no módulo my_module |
**.google_compute_global_forwarding_rule.*
|
Todos os recursos google_compute_global_forwarding_rule em qualquer módulo |
module.my_module.google_compute_global_forwarding_rule.* |
Todos os recursos google_compute_global_forwarding_rule em "my_module" |
Se um endereço de recurso corresponder aos valores em addresses
e excludedAddresses
, é excluído.
Limitações
Os dados do plano do Terraform oferecem a melhor representação disponível do estado real após a aplicação. No entanto, em muitos casos, o estado após a aplicação pode não ser conhecido, uma vez que é calculado no lado do servidor.
A criação de caminhos de ascendência da CAI faz parte do processo de validação de políticas. Usa o projeto predefinido fornecido para contornar IDs de projetos desconhecidos. No caso em que não é fornecido um projeto predefinido, o caminho de hierarquia é predefinido como organizations/unknown
.
Pode não permitir a ascendência desconhecida adicionando a seguinte restrição:
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: {}
Recursos suportados
Pode criar restrições de alteração de recursos para qualquer recurso do Terraform a partir de qualquer fornecedor do Terraform.