Como o OPA e o Istio simplificaram nosso controle de acesso em microserviços

- Introdução
- Cenário de uso
- Por que escolhemos o Open Policy Agent?
- Como o Istio se integra com o OPA
- Modos de instalação do OPA
- Validação de JWT e manipulação de requisições
- Multi-tenant: cada tenant com suas próprias roles
- Distribuição das regras
- Soluções complementares
- Configuração de integração do Istio com o Opa
- Conclusões
Introdução
Hoje vou apresentar uma ferramenta cloud native que está sob o guarda-chuva da CNCF como um projeto graduado, focado em segurança e compliance: o Open Policy Agent (OPA).
Há alguns anos tive meu primeiro contato com essa ferramenta, mas nunca cheguei a utilizá-la de fato em produção, mas sempre enxerguei muito valor no que ela se propõe a resolver. E foi somente neste mês que, finalmente, surgiu um caso real em que o uso do OPA fez todo o sentido e é justamente esse cenário que vou compartilhar aqui.
Cenário de uso
Nosso cenário começou quando percebemos que estávamos replicando lógicas de autorização diretamente no código dos nossos microserviços. Em um primeiro momento, isso funcionou bem: utilizávamos, por exemplo, o Spring Boot com Spring Security, definindo roles e permissões diretamente nos endpoints via anotações ou configurações estáticas. Contudo, à medida que o número de serviços cresceu e passamos a empregar diferentes linguagens e frameworks (incluindo Quarkus e até linguagens não-Java), manter a consistência de roles e permissões em cada repositório tornou-se desafiador. Além disso, surgiu a necessidade de:
-
Criar e atualizar roles dinamicamente, sem precisar alterar o código-fonte de cada microserviço.
-
Gerenciar acessos a recursos específicos de forma semelhante ao que vemos em soluções como AWS IAM (onde podemos restringir ações em recursos pontuais).
-
Suportar múltiplos tenants, cada um com suas próprias regras de acesso (roles, políticas, etc.).
Diante disso, optamos por externalizar o controle de autorização, concentrando-o em uma ferramenta especializada. Nesse caminho, adotamos duas tecnologias fundamentais:
-
Istio como Service Mesh para direcionar e controlar o tráfego entre microserviços em nosso cluster Kubernetes.
-
Open Policy Agent (OPA) para implementar a lógica de autorização de forma centralizada
Neste post, vou contar um pouco mais sobre esse processo, as motivações, a arquitetura escolhida e como construímos uma solução capaz de criar e atualizar regras de forma dinâmica, garantindo segurança e flexibilidade.
Por que escolhemos o Open Policy Agent?
O Open Policy Agent já é um projeto “graduated” da CNCF (Cloud Native Computing Foundation) como já mencionado anteriormente. Ele é amplamente utilizado no mercado para diferentes cenários de políticas (segurança, compliance, políticas de CI/CD etc.).
A principal vantagem do OPA é a possibilidade de desacoplar as regras de autorização do código das aplicações, substituindo-as por “políticas como código”, escritas em uma linguagem declarativa chamada Rego. Isso facilita:
-
Centralização da lógica de autorização (acesso unificado em múltiplos serviços).
-
Atualizações dinâmicas de papéis (roles) e permissões, sem precisar recompilar ou redeployar todos os microserviços.
-
Manutenção simplificada nos microserviços, pois não precisamos replicar a mesma regra em diferentes linguagens ou frameworks.
Embora tenhamos avaliado a possibilidade de criar um serviço de autorização proprietário ou até mesmo usar uma solução de algum provedor cloud (por exemplo, Amazon Verified Permissions, que utiliza a linguagem Cedar), decidimos pelo OPA devido ao fato de ele ser um padrão aberto, já amplamente adotado no mercado, e por Rego ser mais simples de dar manutenção nos scripts do que outras linguagens/padrões.
Como o Istio se integra com o OPA
Nosso cluster Kubernetes já utiliza o Istio como Service Mesh, o que oferece, entre outras funcionalidades, controle de tráfego L7, observabilidade, roteamento avançado e ext_authz para autenticação e autorização externas.
O fluxo de autorização funciona da seguinte forma:

-
Request → istio-proxy
-
A requisição chega primeiro no sidecar do Istio (o Envoy, chamado aqui de “istio-proxy”), que intercepta todo o tráfego de entrada e saída do pod.
-
-
istio-proxy → OPA (ext_authz)
-
Antes de encaminhar a chamada para a aplicação (app), o Envoy aciona o mecanismo de autorização externa (ext_authz).
-
Ele envia os dados da requisição (cabeçalhos, token JWT, etc.) para o OPA, que vai avaliar as regras definidas em Rego.
-
-
istio-proxy ↔ IstioD
-
O IstioD é o plano de controle (control plane) do Istio. É ele quem distribui a configuração das políticas de autorização (AuthorizationPolicy) para os proxies.
-
Assim, o istio-proxy sabe que deve chamar o OPA, e como (porta, protocolo, etc.), por causa dessa configuração enviada pelo IstioD.
-
-
istio-proxy → app
-
Depois que o OPA devolve a decisão de “permitir” ou “bloquear”, o proxy aplica essa decisão.
-
Se for permitido, a requisição segue para a aplicação (app).
-
Se for bloqueado, o proxy não encaminha a chamada para a aplicação.
-
-
AuthorizationPolicy
-
Essa é a CRD (Custom Resource Definition) do Istio onde você define quais serviços (ou workloads) devem ser protegidos via ext_authz, quem pode acessar, quais métodos são permitidos, etc.
-
O IstioD lê essas políticas e as repassa aos proxies para aplicarem em tempo de execução.
-
Modos de instalação do OPA
Para integrar OPA e Istio, existem dois modos comuns de deploy do OPA no Kubernetes:
-
Sidecar: o OPA é implantado dentro do mesmo Pod que o microserviço, em paralelo ao Envoy.
-
Deployment + Service: o OPA é instalado como um Deployment dentro do cluster, com um Service vinculado, e o Envoy envia as requisições de autorização para esse serviço (ou seja, o OPA se torna um serviço separado que atende a todo o cluster).
No nosso caso, optamos pelo segundo modelo, pois isso simplificou nossa topologia e facilitou a governança. Com essa abordagem, apontamos a configuração do Istio (no extensionProviders) para o Service do OPA, garantindo que todas as requisições de autorização passem por ele.
Validação de JWT e manipulação de requisições
Como parte da nossa solução, validamos o Bearer Token (JWT) que chega na requisição. Abrimos esse token e extraímos informações como:
-
Roles
-
Groups
-
Outros claims específicos conforme a necessidade do negócio
Usamos esses dados para checar, nas políticas Rego, se o usuário tem permissão para acessar determinado serviço, executar uma ação ou manipular certos recursos.
Por exemplo, podemos ter uma política que decodifica um token JWT para obter as claims
, verificar se o username
é gaspar
, e se o mesmo esta acessando um endpoint com method post
no path /admin
.
package envoy.http.jwt
default allow := false
allow if {
is_method_post
is_path_admin
claims.username == "gaspar"
}
is_method_post if input.attributes.request.http.method == "POST"
is_path_admin if input.attributes.request.http.path == "/admin"
claims := payload if {
io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")
[_, payload, _] := io.jwt.decode(bearer_token)
}
bearer_token := t if {
v := input.attributes.request.http.headers.authorization
startswith(v, "Bearer ")
t := substring(v, count("Bearer "), -1)
}
Caso a autorização seja positiva, o OPA pode devolver não apenas se o acesso é permitido, mas também inserir (ou remover) cabeçalhos que o microserviço poderá ler. Também é possível mudar o corpo da resposta ou o código HTTP, criando respostas de erro personalizadas (por exemplo, 403 com um JSON específico).
Multi-tenant: cada tenant com suas próprias roles
Uma das maiores motivações para migrarmos a lógica de autorização para fora do código foi a migração para um sistema multi-tenant. Cada cliente (tenant) pode ter regras de acesso diferentes:
-
Roles específicas.
-
Políticas de acesso a recursos distintos.
-
Perfis de segurança que não necessariamente precisam estar hardcoded em cada microserviço.
No modelo antigo (onde definíamos as roles dentro de cada aplicação), qualquer mudança para contemplar um novo tenant ou atualizar permissões era sinônimo de mexer no código e republicar o serviço. Com o OPA e o Istio, essa lógica foi totalmente centralizada e externalizada.
Distribuição das regras
Para facilitar a construção e a manutenção das políticas, desenvolvemos internamente uma aplicação com interface gráfica simplificada. Nela, o usuário de IAM (aquele que gerencia as roles) tem acesso a um formulário rico, com diversos campos de dropdown e autocomplete, algo como:
-
Seleciona o serviço desejado.
-
O sistema então lista as ações disponíveis.
-
Permite criar regras de allow ou deny.
-
Possibilita ate mesmo adicionar o IDs de recursos, se necessário.
Dessa forma, o usuário de IAM não escreve Rego diretamente. Ao final, o sistema monta automaticamente os scripts Rego associados àquela ação do serviço, adicionando as instruções necessárias conforme os dados fornecidos.
Essas políticas são persistidas em um banco de dados e também empacotadas em um arquivo bundle (um pacote contendo todas as regras Rego). Em seguida, configuramos o OPA para fazer pull desse bundle a cada 30 segundos.
Para otimizar o processo, utilizamos um header ETag que funciona como um hash. Quando o OPA faz o pull:
-
Se o ETag recebido for o mesmo que ele já possui, ele mantém as regras anteriores.
-
Se o ETag for diferente, significa que algo mudou nas políticas, então ele descompacta o arquivo bundle contendo as regras e as atualiza.
Soluções complementares
Open Policy Administration Layer
Existem soluções como o OPAL (Open Policy Administration Layer), que permitem atualizações das politicas em tempo real. O OPAL mantém uma conexão WebSocket entre sua aplicação e o OPA, onde a sua aplicação se conecta a um repositório externo de politicas como um bucket S3, repositório GIT, banco de dados e outras fontes. Quando alguma alteração for efetuada no repositório ele notifica o OPA imediatamente. Avaliamos essa alternativa, mas, no nosso caso, consideramos que atualizações a cada 30 segundos eram suficientes, então não adotamos o OPAL.
Open Policy Containers
Outra iniciativa interessante no ecossistema do OPA é o Open Policy Containers, que permite criar um workflow para políticas OPA, empacotando-as, versionando-as e testando-as de forma padronizada. É um complemento que pode ser extremamente útil em cenários de grande escala ou quando precisamos de um pipeline de CI/CD só para políticas, garantindo testes, lint, validações e rastreamento de versões.
Configuração de integração do Istio com o Opa
Para quem deseja um passo a passo mais detalhado de como configurar o Istio e o OPA, recomendo fortemente a leitura do post Can Your Platform Do Policy? Accelerate Teams With Platform L7 Policy Functionality no blog do Istio. Seguir esse exemplo foi de extrema utilidade e simplicidade para entendermos todo o fluxo de autorização e como adequar o Istio e o OPA às nossas necessidades.
Nesse guia, é demonstrado como configurar o ext_authz do Envoy para delegar a decisão de autorização ao OPA, bem como estruturar políticas Rego e lidar com aspectos de autenticação e manipulação do header, body e status code.
Conclusões
Ao externalizar nossas regras de autorização com o OPA integrado ao Istio, temos os seguintes ganhos:
-
Unificação da lógica de acesso em um único ponto, reduzindo duplicações e riscos de inconsistência.
-
Escalabilidade: podemos ter inúmeros microserviços (independentemente da linguagem), todos delegando autorização ao mesmo OPA.
-
Maior agilidade: criar e atualizar roles e permissões passou a ser uma tarefa que não exige alteração no código de cada aplicação.
-
Suporte a multi-tenant de forma natural, pois cada tenant pode ter seu próprio conjunto de regras versionadas e armazenadas no mesmo bundle ou em bundles específicos.
-
Menos risco de falhas de segurança: policies centralizadas facilitam auditoria, rastreabilidade (logs unificados) e testes de segurança.
O modelo “Policy as Code” com OPA integrado ao Istio se mostrou a melhor opção para atender os requisitos do nosso cenário. Se sua organização também sofre com problemas de replicação de regras de autorização entre microserviços, vale a pena avaliar essa abordagem.
Caso você precise de atualizações em tempo real, o OPAL pode ser um caminho. E se quiser empacotar, versionar e testar suas políticas de forma profissional, considere o Open Policy Containers para um pipeline de CI/CD de políticas.