DEV Community

Cover image for Segurança e governança em infraestrutura - policy as code com OPA
Rafael Dutra for apsis-cc

Posted on

Segurança e governança em infraestrutura - policy as code com OPA

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"
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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],
    )
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Open Policy Agent — Documentation
  2. Conftest — Documentation
  3. HashiCorp Sentinel — Documentation

Top comments (0)