Compliance as code: OPA/Gatekeeper para políticas de segurança

1. Introdução ao Compliance as Code no Ecossistema Kubernetes

Em ambientes Kubernetes dinâmicos e multi-inquilinos, garantir conformidade com políticas de segurança torna-se um desafio complexo. Times de DevOps precisam assegurar que cada workload respeite regras específicas — desde restrições de privilégios até controle de registros de imagens — sem depender de revisões manuais ou scripts frágeis.

O conceito de "Compliance as Code" surge como resposta a essa necessidade: políticas de segurança são escritas como código, versionadas, testadas e aplicadas automaticamente. Diferente de políticas de rede (NetworkPolicies) ou RBAC, que controlam comunicação e acesso, as políticas de segurança declarativas atuam no momento da criação ou atualização de recursos, validando configurações antes que sejam persistidas.

O Open Policy Agent (OPA) é um motor de políticas open-source que permite escrever regras em uma linguagem declarativa chamada Rego. O Gatekeeper, por sua vez, é um admissão controller nativo do Kubernetes que integra o OPA ao cluster, interceptando requisições à API e tomando decisões de allow/deny com base nas políticas definidas.

2. Arquitetura e Componentes do OPA/Gatekeeper

A arquitetura do OPA/Gatekeeper segue um fluxo claro:

  1. Um usuário ou automatização envia uma requisição ao API Server (ex.: kubectl apply -f pod.yaml)
  2. O API Server encaminha a requisição ao Gatekeeper via webhook de admissão
  3. O Gatekeeper consulta o OPA, que avalia as políticas Rego configuradas
  4. O OPA retorna uma decisão: allow (permitir) ou deny (negar)
  5. O API Server aplica ou rejeita a requisição conforme a decisão

O OPA funciona como um motor genérico — pode ser usado fora do Kubernetes — enquanto o Gatekeeper fornece a integração específica para clusters, expondo as políticas como Custom Resource Definitions (CRDs). As políticas são definidas em dois níveis:

  • ConstraintTemplate: define a regra Rego e os parâmetros que ela aceita
  • Constraint: instancia o template com valores específicos e escopo (namespaces, labels, etc.)

3. Escrevendo Políticas com Rego: Sintaxe e Padrões Essenciais

Rego é uma linguagem declarativa baseada em lógica de predicados. Abaixo, um exemplo de política que proíbe containers com privileged: true:

package k8ssecurity

violation[{"msg": msg}] {
    container := input.review.object.spec.containers[_]
    container.securityContext.privileged == true
    msg := sprintf("Container %v é privilegiado, o que não é permitido", [container.name])
}

Para exigir readOnlyRootFilesystem, usamos:

package k8ssecurity

violation[{"msg": msg}] {
    container := input.review.object.spec.containers[_]
    not container.securityContext.readOnlyRootFilesystem == true
    msg := sprintf("Container %v deve ter readOnlyRootFilesystem habilitado", [container.name])
}

Funções auxiliares como every permitem validar todos os elementos de uma lista:

package k8ssecurity

violation[{"msg": msg}] {
    container := input.review.object.spec.containers[_]
    every env in container.env {
        env.name != "SECRET_TOKEN"
    }
    msg := sprintf("Container %v não define a variável SECRET_TOKEN", [container.name])
}

O Rego também suporta validação de labels e annotations, usando input.review.object.metadata.labels ou input.review.object.metadata.annotations.

4. Instalação e Configuração do Gatekeeper no Cluster

A instalação pode ser feita via Helm ou manifestos YAML oficiais. Com Helm:

helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper/gatekeeper --namespace gatekeeper-system --create-namespace

Após a instalação, criamos um ConstraintTemplate. Exemplo para bloquear imagens de registros não aprovados:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8sallowedrepos
spec:
  crd:
    spec:
      names:
        kind: K8sAllowedRepos
      validation:
        openAPIV3Schema:
          type: object
          properties:
            repos:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sallowedrepos
        violation[{"msg": msg}] {
            container := input.review.object.spec.containers[_]
            image := container.image
            not startswith(image, input.parameters.repos[_])
            msg := sprintf("Container %v usa imagem %v de registro não aprovado", [container.name, image])
        }

Em seguida, criamos a Constraint que aplica a regra ao cluster:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: block-unauthorized-repos
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    repos:
      - "docker.io/minhaempresa/"
      - "gcr.io/minhaempresa/"

5. Estratégias de Teste e Simulação de Políticas

Testar políticas Rego offline é essencial para evitar surpresas em produção. Use opa eval para simular avaliações:

opa eval --data policy.rego --input input.json "data.k8ssecurity.violation"

Para testes unitários, crie arquivos .rego com casos de teste:

package k8ssecurity

test_privileged_container {
    input := {"review": {"object": {"spec": {"containers": [{"name": "test", "securityContext": {"privileged": true}}]}}}}
    count(violation) == 1
}

test_non_privileged_container {
    input := {"review": {"object": {"spec": {"containers": [{"name": "test", "securityContext": {"privileged": false}}]}}}}
    count(violation) == 0
}

Execute com opa test . -v. Ferramentas como gatekeeper-policy-manager e kubectl gatekeeper ajudam a visualizar e simular políticas diretamente no cluster.

Em pipelines CI/CD, integre o OPA para validar manifestos antes do deploy:

- name: Validar políticas com OPA
  run: |
    opa eval --data policies/ --input manifests/deployment.yaml "data.k8ssecurity.violation"

6. Integração com Fluxos DevOps e GitOps

Armazene políticas como código em um repositório Git dedicado. Estruture assim:

policies/
├── templates/
│   └── k8sallowedrepos.yaml
├── constraints/
│   └── block-unauthorized-repos.yaml
├── tests/
│   └── policy_test.rego
└── README.md

Com ArgoCD ou Flux, sincronize automaticamente o repositório com o cluster. O Git se torna a única fonte da verdade para políticas de segurança.

Estratégias de rollout incluem dois modos:

  • Auditoria (dry-run): as políticas registram violações mas não bloqueiam requisições
  • Enforcement (deny): as políticas bloqueiam requisições que violam as regras

Para ativar o modo auditoria, use a anotação gatekeeper.sh/audit: "true" na Constraint. Monitore os resultados com kubectl get constraint e verifique a coluna TOTAL VIOLATIONS.

7. Monitoramento, Auditoria e Troubleshooting

O Gatekeeper expõe logs detalhados no namespace gatekeeper-system. Para identificar violações:

kubectl logs -n gatekeeper-system -l app=gatekeeper --tail=100 | grep "denied"

Métricas Prometheus estão disponíveis na porta 8888 do serviço gatekeeper-controller-manager-metrics. Principais métricas:

  • gatekeeper_constraints: total de constraints ativas
  • gatekeeper_violations: violações por constraint
  • gatekeeper_request_duration_seconds: latência das requisições

Para depuração, descreva a constraint e verifique eventos:

kubectl describe constraint K8sAllowedRepos block-unauthorized-repos
kubectl get events --field-selector involvedObject.kind=K8sAllowedRepos

Se uma política estiver gerando falsos positivos, ajuste o escopo da constraint ou adicione exceções via match.namespaces ou match.labelSelector.

8. Casos de Uso Avançados e Considerações de Produção

Em ambientes multi-cluster, o OPA pode operar em modo sidecar ou federado, compartilhando políticas entre clusters via GitOps. Ferramentas como Kyverno oferecem abordagem similar, enquanto Falco foca em detecção de runtime e Polaris em auditoria de configurações.

Limitações importantes para produção:

  • Performance: políticas complexas com loops aninhados podem aumentar a latência. Use every com moderação e evite regras que percorrem todos os namespaces
  • Mutações vs. Validações: O Gatekeeper suporta mutações (v3.7+), mas mutações complexas podem causar loops. Prefira validações sempre que possível
  • Exceções: para casos legítimos que precisam burlar políticas, use match.excludedNamespaces ou labels específicas nas constraints

Para tratamento de exceções, crie uma constraint separada que permita namespaces específicos:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: allow-legacy-repos
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces:
      - "legacy-system"
  parameters:
    repos:
      - "docker.io/antigo-registro/"

O Compliance as Code com OPA/Gatekeeper transforma políticas de segurança em ativos gerenciáveis, testáveis e auditáveis, alinhando-se perfeitamente com práticas DevOps e GitOps. Ao tratar compliance como código, você ganha rastreabilidade, consistência e agilidade para adaptar regras conforme o ecossistema evolui.

Referências