Governança de infraestrutura como código com políticas baseadas em padrões

O problema que política como código resolve

Manter requisitos de segurança e conformidade aplicados de forma consistente em toda a infraestrutura de nuvem é um desafio real para a maioria das organizações. Um workload pode acabar sendo implantado em uma Região da AWS que nunca foi aprovada para aquela classe de dado. Em outro ambiente, um security group pode permitir acesso mais amplo do que o pretendido. Tags obrigatórias podem estar faltando. Criptografia pode ser assumida, mas não configurada.

Esses gaps criam risco, aumentam o esforço de revisão e tornam auditorias mais difíceis do que precisam ser. Revisão manual ajuda, mas não escala quando a entrega acelera e mais times provisionam infraestrutura diretamente.

É aqui que política como código entra. Ela transforma a intenção de controle em verificações preventivas que rodam no fluxo de entrega. Um modelo baseado em padrões torna essas verificações mais simples de revisar, manter e explicar.

Organizando políticas em torno de padrões recorrentes

Times às vezes constroem regras serviço a serviço, o que pode tornar as bibliotecas de política como código difíceis de revisar e expandir à medida que crescem. Uma abordagem baseada em padrões organiza as políticas em torno da intenção de controle recorrente, e não em verificações específicas por serviço. Isso facilita a cobertura, a explicação e a evolução das políticas conforme a infraestrutura muda.

O conjunto prático de padrões inclui:

  • Metadados obrigatórios — para tags e outros campos usados para identificar propriedade, suporte, alocação de custos e automação.
  • Configuração permitida — para Regiões aprovadas, limites de implantação aceitos e outras configurações autorizadas.
  • Restrição de exposição — para configurações que tornam a infraestrutura mais acessível do que o pretendido, como ingresso público ou recursos voltados para a internet no ambiente errado.
  • Aplicação de proteção — para salvaguardas de linha de base como criptografia, logging ou proteção contra exclusão.
  • Restrição de privilégio — para definições do Gerenciamento de Identidade e Acesso da AWS (IAM) e padrões de acesso que precisam de validação mais rigorosa.
Imagem original — fonte: Aws

Um engenheiro de conformidade pode descrever um requisito como “metadados obrigatórios”. Um engenheiro de nuvem pode descrever o mesmo requisito como “padrão de tagging”. A estrutura de padrões ajuda os dois times a falarem sobre a mesma coisa.

Onde o OPA se encaixa em um modelo de governança em camadas

O Open Policy Agent (OPA) atua na camada preventiva — ele valida que as mudanças de infraestrutura estão alinhadas com as expectativas antes do deploy. O OPA avalia entradas estruturadas (o JSON do plano do HashiCorp Terraform) contra a lógica de política.

Ele não substitui os serviços de governança da AWS que fornecem guardrails organizacionais, monitoramento contínuo e aplicação em nível de recurso após os recursos existirem. A divisão de responsabilidades funciona assim:

  • OPA — verifica as mudanças propostas antes do deploy.
  • AWS Organizations e AWS Control Tower — estabelecem guardrails organizacionais.
  • AWS Config e AWS Security Hub — fornecem visibilidade e monitoramento após os recursos existirem.
  • Proteções em nível de serviço — aplicam configurações na fronteira do recurso.
Imagem original — fonte: Aws

Como integrar a validação de política no pipeline de CI/CD

O fluxo de implementação segue uma sequência clara dentro do pipeline:

  1. Enviar uma mudança via pull request ou merge request.
  2. Executar validações iniciais: formatação, validação de sintaxe e verificações de dependência.
  3. Gerar um plano do Terraform e convertê-lo para o formato JSON.
  4. Avaliar o plano (em formato JSON) contra a biblioteca compartilhada de políticas OPA.
  5. Publicar o relatório de validação como um artefato.
  6. Executar verificações adicionais de qualidade automatizadas conforme necessário.
  7. Usar o artefato de validação nas decisões de aprovação para ambientes de maior risco.
  8. Implantar as mudanças aprovadas.
  9. Continuar o monitoramento pós-deploy pelos serviços nativos de governança da AWS.

Os quality gates fornecem resultados automatizados de aprovação ou reprovação com base em critérios definidos. Os approval gates controlam se uma mudança avança para um ambiente protegido. Essa separação importa — a aprovação manual não é o primeiro lugar onde alguém percebe tags faltando, uma Região AWS não permitida ou ingresso público. As verificações automatizadas identificam esses problemas mais cedo. O OPA pertence à camada de gate automatizado, e sua saída também alimenta o processo de aprovação.

Imagem original — fonte: Aws

Estrutura da biblioteca de políticas

Uma estrutura de biblioteca baseada em padrões mantém o modelo de política mais próximo de como os times falam sobre controles. O exemplo de estrutura de diretórios abaixo ilustra essa organização:

opa-policies/
├── patterns/
│   ├── baseline/     # Segurança fundamental
│   ├── tagging/      # Tags obrigatórias
│   ├── networking/   # Controles de rede
│   ├── logging/      # Habilitação de logging
│   ├── encryption/   # Criptografia em repouso e em trânsito
│   └── iam/          # Boas práticas de IAM
├── shared/
│   ├── helpers.rego
│   └── messages.rego
├── tests/
├── fixtures/
└── docs/

Exemplos práticos de políticas OPA

Exemplo 1: Aplicar transporte seguro para o Amazon S3

Este exemplo implementa o padrão de aplicação de proteção para o Amazon Simple Storage Service (Amazon S3). O objetivo é verificar se o acesso ao bucket S3 está protegido em trânsito, exigindo uma política de bucket que negue requisições quando aws:SecureTransport estiver definido como false. A política verifica duas coisas: se a política do bucket S3 inclui uma instrução de negação que bloqueia requisições não criptografadas, e se o bucket S3 tem alguma política de bucket correspondente. A regra avalia tanto ações de criação quanto de atualização no JSON do plano do Terraform.

package compliance.amazon_s3.ssl

import future.keywords.in
import future.keywords.contains
import future.keywords.if

# Deny: S3 bucket policy missing SecureTransport deny statement
deny contains msg if {
    resource := input.resource_changes[_]
    resource.type == "aws_s3_bucket_policy"
    is_create_or_update(resource.change.actions)
    policy_value := resource.change.after.policy
    policy := json.unmarshal(policy_value)
    not has_secure_transport_deny(policy)
    msg := sprintf(
        "[S3-OPA-1] Resource '%s' does not enforce SSL/TLS. Bucket policy must include a Deny statement with Condition Bool aws:SecureTransport set to \"false\".",
        [resource.address]
    )
}

# Deny: S3 bucket created without any corresponding bucket policy
deny contains msg if {
    resource := input.resource_changes[_]
    resource.type == "aws_s3_bucket"
    is_create_or_update(resource.change.actions)
    bucket_name := resource.change.after.bucket
    not has_bucket_policy(bucket_name)
    msg := sprintf(
        "[S3-OPA-1] Resource '%s' (bucket '%s') has no bucket policy. A bucket policy with a Deny statement for aws:SecureTransport \"false\" is required.",
        [resource.address, bucket_name]
    )
}

is_create_or_update(actions) if { actions[_] == "create" }
is_create_or_update(actions) if { actions[_] == "update" }

has_bucket_policy(bucket_name) if {
    bp := input.resource_changes[_]
    bp.type == "aws_s3_bucket_policy"
    is_create_or_update(bp.change.actions)
    bp.change.after.bucket == bucket_name
}

has_secure_transport_deny(policy) if {
    stmt := policy.Statement[_]
    stmt.Effect == "Deny"
    stmt.Condition.Bool["aws:SecureTransport"] == "false"
    stmt.Principal == "*"
    action := stmt.Action
    action == "s3:*"
}

Exemplo 2: Restringir ingresso público em portas sensíveis

Este exemplo implementa o padrão de restrição de exposição. O objetivo é identificar configurações de security group do Amazon Virtual Private Cloud (Amazon VPC) que permitem ingresso público em portas sensíveis antes que essas regras sejam implantadas. A política avalia tanto recursos aws_security_group com regras de ingresso inline quanto recursos aws_security_group_rule independentes, pois repositórios de clientes frequentemente usam os dois estilos.

package compliance.amazon_vpc.ingress

import future.keywords.in
import future.keywords.contains
import future.keywords.if

# Sensitive ports that must not be open to the internet
sensitive_ports := {22, 3389, 5432}

# Deny: aws_security_group with inline ingress open to 0.0.0.0/0 on sensitive ports
deny contains msg if {
    resource := input.resource_changes[_]
    resource.type == "aws_security_group"
    is_create_or_update(resource.change.actions)
    ingress := resource.change.after.ingress[_]
    ingress.cidr_blocks[_] == "0.0.0.0/0"
    port := sensitive_ports[_]
    ingress.from_port <= port
    ingress.to_port >= port
    msg := sprintf(
        "[VPC-OPA-1] Resource '%s' allows ingress from 0.0.0.0/0 on port %d. Restrict access to specific CIDR ranges.",
        [resource.address, port]
    )
}

# Deny: aws_security_group_rule with type "ingress" open to 0.0.0.0/0 on sensitive ports
deny contains msg if {
    resource := input.resource_changes[_]
    resource.type == "aws_security_group_rule"
    is_create_or_update(resource.change.actions)
    resource.change.after.type == "ingress"
    resource.change.after.cidr_blocks[_] == "0.0.0.0/0"
    port := sensitive_ports[_]
    resource.change.after.from_port <= port
    resource.change.after.to_port >= port
    msg := sprintf(
        "[VPC-OPA-1] Resource '%s' allows ingress from 0.0.0.0/0 on port %d. Restrict access to specific CIDR ranges.",
        [resource.address, port]
    )
}

is_create_or_update(actions) if { actions[_] == "create" }
is_create_or_update(actions) if { actions[_] == "update" }

Exemplo 3: Aplicar política de confiança de menor privilégio para roles IAM

Este exemplo implementa o padrão de restrição de privilégio para políticas de confiança de roles IAM. O objetivo é identificar relacionamentos de confiança que permitem que principals excessivamente amplos assumam um role. A política inspeciona o documento assume_role_policy para recursos aws_iam_role e procura por principals com wildcard em três representações válidas.

package compliance.amazon_iam.trust

import future.keywords.in
import future.keywords.contains
import future.keywords.if

# Deny: IAM role with wildcard principal in trust policy
deny contains msg if {
    resource := input.resource_changes[_]
    resource.type == "aws_iam_role"
    is_create_or_update(resource.change.actions)
    policy_value := resource.change.after.assume_role_policy
    policy := json.unmarshal(policy_value)
    stmt := policy.Statement[_]
    stmt.Effect == "Allow"
    has_wildcard_principal(stmt)
    msg := sprintf(
        "[IAM-OPA-2] Resource '%s' has a wildcard principal in its trust policy. Specify explicit account ARNs, service principals, or federated providers instead of \"*\".",
        [resource.address]
    )
}

# Principal is directly "*"
has_wildcard_principal(stmt) if { stmt.Principal == "*" }

# Principal.AWS is "*"
has_wildcard_principal(stmt) if { stmt.Principal.AWS == "*" }

# Principal.AWS is an array containing "*"
has_wildcard_principal(stmt) if { stmt.Principal.AWS[_] == "*" }

is_create_or_update(actions) if { actions[_] == "create" }
is_create_or_update(actions) if { actions[_] == "update" }

Vale mencionar que a AWS Labs disponibiliza o IAM Policy Autopilot, uma ferramenta open-source de linha de comando e servidor MCP que ajuda a gerar políticas IAM de linha de base a partir do código da aplicação. Essa ferramenta é complementar ao padrão mostrado acima — o IAM Policy Autopilot auxilia na geração de políticas, enquanto o exemplo acima foca em validar se as políticas de confiança de roles IAM estão adequadamente escopadas nas mudanças de infraestrutura.

Exemplos de implementação em CI/CD

Os exemplos a seguir mostram o mesmo modelo operacional em dois sistemas de CI/CD comuns. A sintaxe muda, mas a sequência é a mesma: validar, planejar, avaliar política, reter o artefato e usar o resultado durante promoção e aprovação. Esses exemplos assumem que o OPA está instalado no ambiente de CI/CD, que o diretório opa-policies contém a biblioteca de políticas, e que o Terraform está configurado com credenciais apropriadas.

GitLab CI

stages:
  - validate
  - plan
  - policy_check

variables:
  TF_IN_AUTOMATION: "true"

terraform_validate:
  stage: validate
  script:
    - terraform fmt -check
    - terraform init
    - terraform validate

terraform_plan:
  stage: plan
  script:
    - terraform plan -out=tfplan
    - terraform show -json tfplan > tfplan.json
  artifacts:
    paths:
      - tfplan.json

opa_policy_check:
  stage: policy_check
  script:
    - opa eval --format pretty --data opa-policies --input tfplan.json "data.terraform.deny"
    - opa eval --format json --data opa-policies --input tfplan.json "data.terraform.deny" > policy-report.json
  artifacts:
    paths:
      - policy-report.json

GitHub Actions

name: Terraform Policy Check

on:
  pull_request:

jobs:
  policy-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - name: Terraform Format Check
        run: terraform fmt -check
      - name: Terraform Init
        run: terraform init
      - name: Terraform Validate
        run: terraform validate
      - name: Terraform Plan
        run: terraform plan -out=tfplan
      - name: Convert Plan to JSON
        run: terraform show -json tfplan > tfplan.json
      - name: Run OPA Policy Check
        run: |
          opa eval --format pretty --data opa-policies --input tfplan.json "data.terraform.deny"
          opa eval --format json --data opa-policies --input tfplan.json "data.terraform.deny" > policy-report.json
      - name: Upload Validation Artifact
        uses: actions/upload-artifact@v4
        with:
          name: policy-report
          path: policy-report.json

Retendo artefatos de validação para revisão e auditoria

Em fluxos de entrega maduros, os resultados de política não desaparecem nos logs do pipeline — eles são retidos como artefatos de validação. Esses artefatos ajudam os revisores a decidir se uma mudança está pronta para aprovação, suportam o tratamento de exceções mostrando quais controles falharam e por quê, e podem permanecer junto ao registro da mudança para discussões de auditoria posteriores.

No mínimo, o artefato identifica: a mudança ou execução do pipeline, o escopo avaliado, o pacote ou versão de política, as verificações executadas, e os resultados de aprovação ou reprovação.

Testando o modelo de política como software

As primeiras regras costumam ser diretas. O trabalho real começa quando a biblioteca cresce e múltiplos times dependem dela. Os testes devem incluir:

  • Casos de teste positivos e negativos — cada política tem casos que mostram entrada válida e casos que mostram falhas esperadas.
  • Cobertura de regressão — helpers compartilhados precisam de cobertura de regressão.
  • Fixtures realistas — fixtures de plano do Terraform devem se parecer com mudanças reais, não com amostras pequenas inventadas.
  • Análise de impacto — quando uma regra muda, os times conseguem identificar rapidamente o que mais pode ser afetado.

Se os desenvolvedores pararem de confiar nos resultados, eles param de tratar a política como um mecanismo útil.

Uma abordagem em fases para implementar as verificações

Não é necessário ter cobertura ampla desde o primeiro dia. Uma implantação em fases funciona melhor do que uma abordagem de aplicação imediata em tudo de uma vez.

Fase 1: Avaliar e pilotar

Comece no modo consultivo para que os times possam ver os resultados sem serem bloqueados. Identifique dois ou três padrões de alta confiança, como metadados obrigatórios, Regiões aprovadas ou restrições de exposição pública. Execute o OPA nos pipelines existentes e revise a saída para verificar a precisão.

Fase 2: Iniciar a aplicação

Aplique o pequeno conjunto de padrões de alta confiança após a saída estar estável e as falhas serem úteis. Integre os artefatos de validação ao fluxo de aprovação. Estabeleça processos de propriedade e tratamento de exceções para pacotes compartilhados.

Fase 3: Operacionalizar e expandir

Formalize o versionamento para pacotes de política compartilhados. Expanda a cobertura de padrões com base no feedback dos times e nas prioridades organizacionais. Conecte a validação pré-deploy com o monitoramento pós-deploy por meio do AWS Config, AWS Security Hub e AWS Organizations.

Conclusão

Política como código ajuda a reduzir a distância entre o que uma organização diz que espera e o que seu sistema de entrega verifica. Ao implementar esses padrões OPA nos pipelines de CI/CD, é possível construir uma camada preventiva que avalia as mudanças de infraestrutura antes do deploy. Com uma biblioteca baseada em padrões, artefatos de validação e propriedade clara, política como código se torna uma forma repetível de traduzir a intenção de controle na entrega do dia a dia — enquanto os serviços de governança da AWS continuam fornecendo visibilidade e monitoramento após os recursos existirem.

Para se aprofundar no tema, a AWS disponibiliza recursos como a documentação oficial do Open Policy Agent, o Guia do Usuário do AWS Security Hub, o Pilar de Segurança do AWS Well-Architected Framework e o Guia do Desenvolvedor do AWS Config. Quem precisar de suporte especializado pode entrar em contato com o AWS Security Assurance Services.

Fonte

Governing infrastructure as code using pattern-based policy as code (https://aws.amazon.com/blogs/security/governing-infrastructure-as-code-using-pattern-based-policy-as-code/)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *