<h2>O que é OPA Gatekeeper?</h2>
<p>OPA (Open Policy Agent) Gatekeeper é um validador de admissão (admission controller) nativo do Kubernetes que implementa o conceito de "Políticas como Código". Em vez de confiar em regras estáticas e difíceis de manter no cluster, o Gatekeeper permite que você defina políticas de segurança, conformidade e governança através de código declarativo usando a linguagem Rego. Essas políticas são aplicadas automaticamente no momento em que um recurso tenta ser criado ou modificado no cluster, bloqueando operações que não estejam em conformidade.</p>
<p>O Gatekeeper atua como intermediário entre a requisição do cliente (kubectl apply, por exemplo) e a persistência no etcd do Kubernetes. Quando você tenta fazer deploy de um Pod, Deployment ou qualquer outro recurso, o Gatekeeper analisa aquele recurso contra todas as políticas registradas. Se alguma política rejeitar o recurso, a operação é bloqueada antes mesmo de chegar ao cluster. Isso elimina a necessidade de revisar manualmente cada deploy e permite que você defina regras complexas de uma forma que é versionável, testável e auditável.</p>
<h2>Instalação e Configuração Inicial</h2>
<h3>Instalando o Gatekeeper no seu cluster</h3>
<p>A instalação do Gatekeeper é straightforward através do Helm ou dos manifestos YAML fornecidos pela comunidade OPA. Começaremos com a abordagem mais direta usando os manifestos oficiais:</p>
<pre><code class="language-bash">kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.14/deploy/gatekeeper.yaml</code></pre>
<p>Esse comando cria um novo namespace chamado <code>gatekeeper-system</code> e implanta os componentes necessários: o webhook de validação, o webhook de mutação e o controlador OPA. Verifique se os pods estão rodando:</p>
<pre><code class="language-bash">kubectl get pods -n gatekeeper-system</code></pre>
<p>Você deve ver algo como <code>gatekeeper-audit-<em></code> e <code>gatekeeper-controller-</em></code> rodando com status Running. Se preferir usar Helm (recomendado para ambientes de produção), adicione o repositório Helm da Gatekeeper e faça a instalação:</p>
<pre><code class="language-bash">helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install gatekeeper/gatekeeper --name-template=gatekeeper --namespace gatekeeper-system --create-namespace</code></pre>
<h3>Verificando a instalação</h3>
<p>Para confirmar que o Gatekeeper está funcionando corretamente, verifique se os webhooks de validação estão registrados:</p>
<pre><code class="language-bash">kubectl get validatingwebhookconfigurations | grep gatekeeper</code></pre>
<p>Você deve ver a saída <code>gatekeeper-validating-webhook-configuration</code>. Isso significa que o Gatekeeper está pronto para interceder nas requisições de API do Kubernetes e avaliar as políticas.</p>
<h2>Compreendendo Rego e a Linguagem de Políticas</h2>
<h3>Fundamentos da linguagem Rego</h3>
<p>Rego é uma linguagem declarativa de consulta criada especificamente para políticas. Diferentemente de linguagens imperativas tradicionais, Rego funciona através de lógica: você descreve <em>o que</em> deveria ser verdadeiro, não <em>como</em> computar. Uma política em Rego geralmente responde a uma pergunta simples: "Esta operação deveria ser permitida?"</p>
<p>Um programa Rego básico consiste em regras. Uma regra define uma condição que, quando verdadeira, faz algo acontecer (permitir ou negar). Vamos ver um exemplo simples que nega qualquer Pod que não possua um label específico:</p>
<pre><code class="language-rego">package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
not input.request.object.metadata.labels.app
msg := "Pod deve ter o label 'app'"
}</code></pre>
<p>Quebrando esse código: <code>package kubernetes.admission</code> define o escopo; <code>deny[msg]</code> é a regra que gera mensagens de negação; a seção entre chaves <code>{}</code> são as condições que devem ser atendidas para a negação ser acionada. <code>input.request.kind.kind == "Pod"</code> verifica se o recurso é um Pod. <code>not input.request.object.metadata.labels.app</code> verifica se o Pod <em>não</em> tem o label <code>app</code>. Se ambas as condições forem verdadeiras, o Pod é rejeitado com a mensagem especificada.</p>
<h3>Estrutura de uma ConstraintTemplate</h3>
<p>No Gatekeeper, você não escreve diretamente arquivos Rego avulsos. Em vez disso, você cria um <code>ConstraintTemplate</code>, que é um CRD (Custom Resource Definition) do Kubernetes que contém a lógica Rego e define os parâmetros que sua política aceita. Esse design permite que você crie restrições reutilizáveis e parametrizáveis.</p>
<p>Aqui está a estrutura típica de um ConstraintTemplate:</p>
<pre><code class="language-yaml">apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
description: "Lista de labels obrigatórios"
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.runAsNonRoot
msg := sprintf("Container '%v' deve rodar como usuário não-root", [container.name])
}</code></pre>
<p>Neste exemplo, o <code>ConstraintTemplate</code> define um CRD chamado <code>K8sRequiredLabels</code> com um parâmetro <code>labels</code> que é uma lista de strings. A lógica Rego dentro da seção <code>targets</code> então usa esses parâmetros para validar recursos.</p>
<h2>Implementando Políticas Práticas</h2>
<h3>Política 1: Garantir que imagens vêm de um registro confiável</h3>
<p>Começaremos com uma política comum em ambientes de produção: garantir que todas as imagens de container usadas no cluster venham de registros confiáveis. Isso evita que imagens maliciosas ou não auditadas sejam implantadas.</p>
<pre><code class="language-yaml">apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8sallowedregistries
spec:
crd:
spec:
names:
kind: K8sAllowedRegistries
validation:
openAPIV3Schema:
type: object
properties:
allowedRegistries:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sallowedregistries
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
image := container.image
not image_from_allowed_registry(image)
msg := sprintf("Imagem '%v' não é de um registro permitido", [image])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
image := container.image
not image_from_allowed_registry(image)
msg := sprintf("Imagem init '%v' não é de um registro permitido", [image])
}
image_from_allowed_registry(image) {
registry := split(image, "/")[0]
registry == input.parameters.allowedRegistries[_]
}
image_from_allowed_registry(image) {
not contains(image, "/")
}</code></pre>
<p>Agora você precisa criar uma <code>Constraint</code> (uma instância específica dessa política) para aplicá-la ao seu cluster:</p>
<pre><code class="language-yaml">apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRegistries
metadata:
name: require-allowed-registries
spec:
parameters:
allowedRegistries:
- "gcr.io"
- "docker.io"
- "registry.example.com"
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces: ["gatekeeper-system", "kube-system"]</code></pre>
<p>Após aplicar ambos os YAMLs, qualquer tentativa de criar um Pod com uma imagem de um registro não permitido será bloqueada. Teste isso:</p>
<pre><code class="language-bash">kubectl run test-pod --image=unauthorized-registry.com/malicious:latest
Erro: admission webhook denied the request</code></pre>
<h3>Política 2: Exigir resource limits e requests</h3>
<p>Clusters sem limites de recursos podem sofrer de "resource starvation", onde um único Pod consome todos os recursos disponíveis. Esta política garante que todo container defina requests e limits:</p>
<pre><code class="language-yaml">apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredresources
spec:
crd:
spec:
names:
kind: K8sRequiredResources
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredresources
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.requests.memory
msg := sprintf("Container '%v' deve definir requests.memory", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.requests.cpu
msg := sprintf("Container '%v' deve definir requests.cpu", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container '%v' deve definir limits.memory", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container '%v' deve definir limits.cpu", [container.name])
}</code></pre>
<p>Aplique a constraint:</p>
<pre><code class="language-yaml">apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredResources
metadata:
name: require-resource-limits
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet", "DaemonSet"]</code></pre>
<h3>Política 3: Bloquear containers rodando como root</h3>
<p>Containers rodando como root representam um risco de segurança significativo. Esta política garante que todos os containers rodem com um usuário não-root:</p>
<pre><code class="language-yaml">apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8snoroot
spec:
crd:
spec:
names:
kind: K8sNoRoot
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8snoroot
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.runAsNonRoot
msg := sprintf("Container '%v' deve ter runAsNonRoot: true", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.runAsUser == 0
msg := sprintf("Container '%v' não pode rodar com UID 0 (root)", [container.name])
}</code></pre>
<p>A constraint:</p>
<pre><code class="language-yaml">apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sNoRoot
metadata:
name: no-root-containers
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet"]
excludedNamespaces: ["gatekeeper-system"]</code></pre>
<h2>Testando, Monitorando e Troubleshooting</h2>
<h3>Testando suas políticas</h3>
<p>Antes de aplicar políticas em produção, você deve testá-las para garantir que não bloqueiam recursos legítimos. O Gatekeeper fornece um modo "audit" que registra violações sem bloqueá-las:</p>
<pre><code class="language-yaml">apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRegistries
metadata:
name: test-allowed-registries
spec:
enforcementAction: "audit" # Em vez de "deny"
parameters:
allowedRegistries:
- "gcr.io"</code></pre>
<p>Com <code>enforcementAction: audit</code>, violações são registradas no objeto <code>Constraint</code> mas não bloqueiam o deploy. Você pode então verificar as violações:</p>
<pre><code class="language-bash">kubectl describe k8sallowedregistries test-allowed-registries</code></pre>
<p>Procure pela seção <code>Status.Total Violations</code>. Quando estiver seguro, mude para <code>enforcementAction: deny</code>.</p>
<h3>Verificando violações</h3>
<p>Cada Constraint mantém um histórico de violações. Para ver quais recursos estão violando qual política:</p>
<pre><code class="language-bash">kubectl get constraints
kubectl describe k8snoroot no-root-containers</code></pre>
<p>Você também pode fazer query diretamente:</p>
<pre><code class="language-bash">kubectl get k8snoroot -o json | jq '.items[].status.totalViolations'</code></pre>
<h3>Debugging de políticas</h3>
<p>Se sua política não está funcionando como esperado, existem algumas técnicas úteis. Primeiro, verifique se o ConstraintTemplate foi criado corretamente:</p>
<pre><code class="language-bash">kubectl get constrainttemplates</code></pre>
<p>Depois, verifique se há erros de compilação Rego:</p>
<pre><code class="language-bash">kubectl describe constrainttemplate k8snoroot</code></pre>
<p>Se houver erros de sintaxe Rego, eles aparecerão em <code>Status</code>. Para testar a lógica Rego localmente antes de aplicar ao cluster, use o playground OPA em https://play.openpolicyagent.org ou instale o OPA localmente:</p>
<pre><code class="language-bash">opa run -s
Isso inicia um servidor interativo onde você pode testar suas policies</code></pre>
<p>Você também pode verificar os logs do Gatekeeper:</p>
<pre><code class="language-bash">kubectl logs -n gatekeeper-system -l gatekeeper.sh/system=yes -f</code></pre>
<h2>Conclusão</h2>
<p>Aprendemos que o OPA Gatekeeper transforma a governança de clusters Kubernetes de um processo manual e propenso a erros em um sistema automatizado, auditável e versionável através de "Políticas como Código". Primeiro, entendemos que políticas são definidas em ConstraintTemplates usando a linguagem Rego, que permite expressões lógicas complexas de forma declarativa e legível. Segundo, implementamos três políticas práticas (validação de registros, resource limits e não-root) que cobrem casos de uso reais em produção. Finalmente, descobrimos que testar políticas através do modo audit antes de ativar enforcement é uma prática essencial que previne bloqueios inesperados.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.openpolicyagent.org/docs/latest/" target="_blank" rel="noopener noreferrer">Open Policy Agent - Documentação Oficial</a></li>
<li><a href="https://github.com/open-policy-agent/gatekeeper" target="_blank" rel="noopener noreferrer">Gatekeeper - GitHub Repository</a></li>
<li><a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/" target="_blank" rel="noopener noreferrer">Kubernetes Admission Controllers</a></li>
<li><a href="https://www.openpolicyagent.org/docs/latest/policy-language/" target="_blank" rel="noopener noreferrer">Rego Language Documentation</a></li>
<li><a href="https://kubernetes.io/docs/concepts/security/" target="_blank" rel="noopener noreferrer">Policy as Code: A Practical Introduction to Kubernetes Security</a></li>
</ul>
<p><!-- FIM --></p>