1. O problema: erros que só aparecem depois do apply
No artigo anterior desta série, vimos como evitar que segredos vazem em texto puro. Mas existe uma categoria de erro igualmente comum e mais sutil: um terraform apply tecnicamente correto — sem erro de sintaxe, sem segredo exposto — que ainda assim cria um recurso inseguro: um bucket S3 público, um security group liberando 0.0.0.0/0 na porta 22, um disco sem criptografia. Nada disso quebra o plan. Só um revisor humano atento (ou um incidente) descobre depois.
Revisão manual de código não escala e é falível — ninguém lê 40 arquivos .tf de um PR grande linha a linha procurando um cidr_blocks errado. É exatamente esse problema que o Policy as Code resolve: transformar regras de segurança em código executável, que roda automaticamente antes do apply e bloqueia o que violar a política — sem depender de alguém lembrar de olhar.
2. O que é policy as code
Policy as Code é a prática de escrever regras de governança (segurança, compliance, convenções de nomenclatura, tags obrigatórias) como código versionado, testável e executado automaticamente em algum ponto do pipeline — geralmente entre o plan e o apply. As duas ferramentas mais usadas no ecossistema de IaC são:
- OPA (Open Policy Agent), um motor de políticas open source e agnóstico de ferramenta, mantido pela CNCF, com sua própria linguagem de regras chamada Rego.
- Sentinel, a solução proprietária da HashiCorp, disponível no Terraform Cloud/Enterprise, com integração nativa ao plano do Terraform.
Vamos focar no OPA por ser open source, gratuito e utilizável em qualquer pipeline (GitHub Actions, GitLab CI, Jenkins), independente de estar ou não no Terraform Cloud.
3. Rego: a linguagem de políticas do OPA
Rego é uma linguagem declarativa focada em fazer perguntas sobre dados estruturados (tipicamente JSON). Uma política Rego não "executa passos" — ela declara condições que, se verdadeiras, geram uma violação (deny) ou uma decisão (allow). O bloco básico:
package terraform.security
deny[msg] {
# condição que, se verdadeira, gera uma mensagem de violação
msg := "exemplo de violação"
}
O padrão mais comum ao avaliar Terraform é gerar o plano em JSON e alimentar essa estrutura para o OPA avaliar contra as políticas.
4. Exemplo prático: bloquear bucket S3 público
Considere este recurso, que qualquer pessoa pode escrever por engano ao copiar um exemplo de blog desatualizado:
resource "aws_s3_bucket" "logs" {
bucket = "app-logs-prod"
}
resource "aws_s3_bucket_public_access_block" "logs" {
bucket = aws_s3_bucket.logs.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
Uma política Rego para bloquear esse padrão, avaliando o plano em JSON gerado por terraform show -json:
package terraform.security
import future.keywords.in
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket_public_access_block"
change := resource.change.after
some field in ["block_public_acls", "block_public_policy",
"ignore_public_acls", "restrict_public_buckets"]
change[field] == false
msg := sprintf(
"recurso '%s' permite acesso público (%s = false)",
[resource.address, field],
)
}
5. Rodando a política com Conftest
O Conftest é uma CLI que facilita testar arquivos estruturados (JSON, YAML, planos de Terraform) contra políticas Rego, sem precisar montar toda a infraestrutura de servidor do OPA:
# Gera o plano em formato binário e depois em JSON
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
# Avalia o plano contra as políticas no diretório policy/
conftest test --policy policy/ tfplan.json
Se a política do passo anterior for violada, a saída é algo como:
FAIL - tfplan.json - terraform.security - recurso 'aws_s3_bucket_public_access_block.logs' permite acesso público (block_public_acls = false)
1 test, 0 passed, 1 failed, 0 warnings
O código de saída não-zero do conftest é o que permite usar esse comando como um gate de CI: se falhar, o pipeline para antes de chegar no apply.
6. Sentinel como alternativa
Para quem já usa Terraform Cloud/Enterprise, o Sentinel oferece o mesmo conceito com integração nativa — as políticas rodam automaticamente entre plan e apply, sem precisar montar o pipeline de conftest manualmente:
import "tfplan/v2" as tfplan
s3_buckets_publicos = filter tfplan.resource_changes as _, rc {
rc.type is "aws_s3_bucket_public_access_block" and
rc.change.after.block_public_acls is false
}
main = rule {
length(s3_buckets_publicos) is 0
}
A sintaxe é diferente do Rego, mas a ideia é idêntica: inspecionar o plano e recusar o apply se alguma condição de segurança não for satisfeita. A escolha entre OPA e Sentinel tende a ser menos sobre capacidade técnica e mais sobre onde o time já está: fora do Terraform Cloud, OPA/Conftest é a opção sem lock-in; dentro dele, o Sentinel remove a etapa de integração.
7. Onde rodar as políticas no pipeline
O ganho de Policy as Code só se realiza se a checagem for obrigatória, não opcional. Um pipeline típico de GitHub Actions:
# .github/workflows/terraform.yml
jobs:
plan-and-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init
- run: terraform plan -out=tfplan.binary
- run: terraform show -json tfplan.binary > tfplan.json
- name: Instalar conftest
run: |
curl -fsSL https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_linux_x86_64.tar.gz | tar xz
sudo mv conftest /usr/local/bin
- name: Avaliar políticas de segurança
run: conftest test --policy policy/ tfplan.json
# O apply só roda se o passo anterior não falhar
- run: terraform apply -auto-approve tfplan.binary
Colocar a checagem antes do apply, como um passo que pode falhar o job, é o que transforma a política de "documentação que ninguém lê" em "barreira que ninguém consegue pular sem querer".
8. Conclusão
Policy as Code fecha uma lacuna que revisão manual de código não cobre bem: garantir, de forma automática e repetível, que todo recurso criado por IaC respeita as regras de segurança da organização — sem depender da atenção de quem está revisando o PR. OPA com Rego (via Conftest) e Sentinel resolvem o mesmo problema com integrações diferentes; o que importa é que a checagem rode como gate obrigatório no pipeline, não como sugestão. No próximo e último artigo desta série, entramos em outro pilar de governança: como desenhar permissões de IAM com o mínimo privilégio necessário — e como auditar, de forma automatizada, quando isso deixa de ser verdade.
Imagem de capa: Logo oficial da HashiCorp, mantenedora do Vault — Wikimedia Commons
Referências:
Top comments (0)