RBAC com API Gateway e Open Policy Agent (OPA)
May 15, 2023
Com diversos modelos de controle de acesso e métodos de implementação disponíveis, construir um sistema de autorização para APIs de serviços de backend ainda pode ser desafiador. No entanto, o objetivo final é garantir que a pessoa correta tenha acesso apropriado ao recurso relevante. Neste artigo, discutiremos como habilitar o modelo de autorização baseado em funções (RBAC) para sua API usando o gateway de API de código aberto Apache APISIX e o Open Policy Agent (OPA).
Objetivos de aprendizagem
Você aprenderá o seguinte ao longo do artigo:
- O que é RBAC e como ele funciona?
- O que é OPA e como ele funciona?
- Como implementar RBAC com OPA e Apache APISIX?
- Como definir e registrar a política no OPA.
- Como criar um upstream, rota e habilitar o plugin OPA.
- Como a função e a permissão do usuário são extraídas do payload do token JWT ou dos dados do consumidor.
O que é RBAC?
Controle de Acesso Baseado em Funções (RBAC) e Controle de Acesso Baseado em Atributos (ABAC) são dois modelos de controle de acesso comumente usados para gerenciar permissões e controlar o acesso a recursos em sistemas de computação. O RBAC atribui permissões aos usuários com base em suas funções dentro de uma organização. No RBAC, as funções são definidas com base nas funções ou responsabilidades dos usuários, e as permissões são atribuídas a essas funções. Os usuários são então atribuídos a uma ou mais funções e herdam as permissões associadas a essas funções. No contexto de APIs, por exemplo, uma função de desenvolvedor pode ter permissão para criar e atualizar recursos de API, enquanto uma função de usuário final pode ter permissão apenas para ler ou executar recursos de API.
Basicamente, o RBAC atribui permissões com base nas funções do usuário, enquanto o ABAC atribui permissões com base em atributos associados aos usuários e recursos.
No RBAC, uma política é definida pela combinação da função atribuída ao usuário, as ações que ele está autorizado a realizar e os recursos nos quais ele pode realizar essas ações.
O que é OPA?
OPA (Open Policy Agent) é um mecanismo de política e um conjunto de ferramentas que fornecem uma abordagem unificada para a aplicação de políticas em todo um sistema distribuído. Ele permite que você defina, gerencie e aplique políticas centralmente a partir de um único ponto. Ao definir políticas como código, o OPA facilita a revisão, edição e reversão de políticas, permitindo um gerenciamento eficiente de políticas.
O OPA fornece uma linguagem declarativa chamada Rego, que permite criar e aplicar políticas em toda a sua stack. Quando você solicita uma decisão de política ao OPA, ele usa as regras e os dados que você forneceu em um arquivo .rego
para avaliar a consulta e produzir uma resposta. O resultado da consulta é então enviado de volta como a decisão da política. O OPA armazena todas as políticas e os dados necessários em seu cache na memória. Como resultado, o OPA retorna resultados rapidamente. Aqui está um exemplo de um arquivo Rego simples do OPA:
package example
default allow = false
allow {
input.method == "GET"
input.path =="/api/resource"
input.user.role == "admin"
}
Neste exemplo, temos um pacote chamado "example" que define uma regra chamada "allow". A regra "allow" especifica que a solicitação é permitida se o método de entrada for "GET", o caminho solicitado for /api/resource
e a função do usuário for "admin". Se essas condições forem atendidas, a regra "allow" será avaliada como "true", permitindo que a solicitação prossiga.
Por que usar OPA e API Gateway para RBAC?
O API Gateway fornece um local centralizado para configurar e gerenciar APIs e consumidores de API. Ele pode ser usado como um gateway de autenticação centralizado ao evitar que cada serviço individual implemente a lógica de autenticação dentro do próprio serviço. Por outro lado, o OPA adiciona uma camada de autorização e desacopla a política do código, criando um benefício distinto para a autorização. Com essa combinação, você pode adicionar permissões para um recurso de API a uma função. Os usuários podem estar associados a uma ou mais funções de usuário. Cada função de usuário define um conjunto de permissões (GET, PUT, DELETE) em recursos RBAC (definidos por caminhos URI). Na próxima seção, vamos aprender como alcançar o RBAC usando esses dois.
Como implementar RBAC com OPA e Apache APISIX
No Apache APISIX, você pode configurar rotas e plugins para definir o comportamento da sua API. Você pode usar o plugin opa do APISIX para aplicar políticas RBAC, encaminhando solicitações ao OPA para tomada de decisão. Em seguida, o OPA toma uma decisão de autorização com base nas funções e permissões dos usuários em tempo real.
Suponha que temos uma API de Conferência onde você pode recuperar/editar sessões de eventos, tópicos e informações do palestrante. Um palestrante pode apenas ler suas próprias sessões e tópicos, enquanto o administrador pode adicionar/editar mais sessões e tópicos. Ou os participantes podem deixar seus comentários sobre a sessão do palestrante por meio de uma solicitação POST para /speaker/speakerId/session/feedback
, e o palestrante pode apenas ver solicitando o método GET do mesmo URI. O diagrama abaixo ilustra todo o cenário:
- O consumidor da API solicita uma rota no API Gateway com sua credencial, como um token JWT no cabeçalho de autorização.
- O API Gateway envia os dados do consumidor com um cabeçalho JWT para o mecanismo OPA.
- O OPA avalia se o consumidor tem o direito de acessar o recurso usando as políticas (funções e permissões) que especificamos no arquivo .rego.
- Se a decisão do OPA for permitida, a solicitação será encaminhada para o serviço de Conferência upstream.
A seguir, instalamos, configuramos o APISIX e definimos políticas no OPA.
Pré-requisitos
- Docker é usado para instalar o etcd e o APISIX em contêineres.
- curl é usado para enviar solicitações à API Admin do APISIX. Você também pode usar ferramentas como Postman para interagir com a API.
Passo 1: Instalar o Apache APISIX
O APISIX pode ser facilmente instalado e iniciado com o seguinte script de início rápido:
curl -sL https://run.api7.ai/apisix/quickstart | sh
Passo 2: Configurar o serviço backend (upstream)
Para encaminhar solicitações ao serviço backend da API de Conferência, você precisará configurá-lo adicionando um servidor upstream no Apache APISIX por meio da API Admin.
curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -X PUT -d '
{
"name":"Conferences API upstream",
"desc":"Register Conferences API as the upstream",
"type":"roundrobin",
"scheme":"https",
"nodes":{
"conferenceapi.azurewebsites.net:443":1
}
}'
Passo 3: Criar um consumidor de API
Em seguida, criamos um consumidor (um novo palestrante) com o nome de usuário jack
no Apache APISIX. Ele configura o plugin jwt-auth para o consumidor com a chave e o segredo especificados. Isso permitirá que o consumidor se autentique usando um JSON Web Token (JWT).
curl http://127.0.0.1:9180/apisix/admin/consumers -X PUT -d '
{
"username": "jack",
"plugins": {
"jwt-auth": {
"key": "user-key",
"secret": "my-secret-key"
}
}
}'
Passo 4: Criar um endpoint público para gerar um token JWT
Você também precisa configurar uma nova Rota que gera e assina o token usando o plugin public-api. Neste cenário, o API Gateway atua como um servidor de provedor de identidade para criar e verificar o token com a chave do nosso consumidor jack. O provedor de identidade também pode ser qualquer outro serviço de terceiros, como Google, Okta, Keycloak e Ory Hydra.
curl http://127.0.0.1:9180/apisix/admin/routes/jas -X PUT -d '
{
"uri": "/apisix/plugin/jwt/sign",
"plugins": {
"public-api": {}
}
}'
Passo 5: Solicitar um novo token JWT para o consumidor de API
Agora podemos obter um novo token para o nosso palestrante Jack a partir do API Gateway usando o endpoint público que criamos. O seguinte comando curl gera um novo token com as credenciais de Jack e atribui função e permissão no payload.
curl -G --data-urlencode 'payload={"role":"speaker","permission":"read"}' http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key -i
Depois de executar o comando acima, você receberá um token como resposta. Salve esse token em algum lugar, pois mais tarde vamos usá-lo para acessar nosso novo endpoint do API Gateway.
Passo 6: Criar uma nova configuração de plugin
Este passo envolve a configuração de 3 plugins do APISIX: proxy-rewrite, jwt-auth e opa.
curl "http://127.0.0.1:9180/apisix/admin/plugin_configs/1" -X PUT -d '
{
"plugins":{
"jwt-auth":{
},
"proxy-rewrite":{
"host":"conferenceapi.azurewebsites.net"
}
}
}'
- O plugin
proxy-rewrite
está configurado para encaminhar solicitações para o hostconferenceapi.azurewebsites.net
. - O plugin de autenticação OPA está configurado para usar o mecanismo de política OPA em execução em http://localhost:8181/v1/data/rbacExample. Além disso, o APISIX envia todas as informações relacionadas ao consumidor para o OPA. Adicionaremos esse arquivo de política
.rego
na seção de configuração do OPA.
Passo 7: Criar uma Rota para as sessões da Conferência
O passo final é criar uma nova rota para as sessões dos palestrantes da API de Conferência:
curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
"name":"Conferences API speaker sessions route",
"desc":"Create a new route in APISIX for the Conferences API speaker sessions",
"methods": ["GET", "POST"],
"uris": ["/speaker/*/topics","/speaker/*/sessions"],
"upstream_id":"1",
"plugin_config_id":1
}'
O payload contém informações sobre a rota, como seu nome, descrição, métodos, URIs, ID do upstream e ID da configuração do plugin. Neste caso, a rota está configurada para lidar com solicitações GET e POST para dois URIs diferentes, /speaker/topics
e /speaker/sessions
. O campo "upstream_id" especifica o ID do serviço upstream que lidará com as solicitações recebidas para esta rota, enquanto o campo "plugin_config_id" especifica o ID da configuração do plugin a ser usada para esta rota.
Passo 8: Testar a configuração sem o OPA
Até agora, configuramos todas as configurações necessárias para que o APISIX direcione as solicitações recebidas para os endpoints da API de Conferência, permitindo apenas consumidores de API autorizados. Agora, cada vez que um consumidor de API quiser acessar um endpoint, ele deve fornecer um token JWT para recuperar dados do serviço backend de Conferência. Você pode verificar isso acessando o endpoint e o endereço de domínio que estamos solicitando agora é nosso API Gateway personalizado, e não o serviço real de Conferência:
curl -i http://127.0.0.1:9080/speaker/1/topics -H 'Authorization: {API_CONSUMER_TOKEN}'
Passo 9: Executar o serviço OPA
Os outros dois passos são executar o serviço OPA usando Docker e carregar nossa definição de política usando sua API, que pode ser usada para avaliar políticas de autorização para solicitações recebidas.
docker run -d --network=apisix-quickstart-net --name opa -p 8181:8181 openpolicyagent/opa:latest run -s
Este comando Docker executa um contêiner da imagem OPA com a versão mais recente. Ele cria um novo contêiner na rede existente do APISIX apisix-quickstart-net
com o nome opa
e expõe a porta 8181
. Assim, o APISIX pode enviar solicitações de verificação de política diretamente ao OPA usando o endereço [http://opa:8181](http://opa:8181)
. Observe que o OPA e o APISIX devem estar na mesma rede Docker.
Passo 10: Definir e registrar a política
O segundo passo no lado do OPA é definir as políticas que serão usadas para controlar o acesso aos recursos da API. Essas políticas devem definir os atributos necessários para o acesso (quais usuários têm quais funções) e as permissões (quais funções têm quais permissões) que são permitidas ou negadas com base nesses atributos. Por exemplo, na configuração abaixo, estamos dizendo ao OPA para verificar na tabela user_roles
qual é a função de jack
. Essa informação é enviada pelo APISIX dentro de input.consumer.username
. Além disso, estamos verificando a permissão do consumidor lendo o payload do JWT e extraindo token.payload.permission
de lá. Os comentários descrevem os passos claramente.
curl -X PUT '127.0.0.1:8181/v1/policies/rbacExample' \
-H 'Content-Type: text/plain' \
-d 'package rbacExample
# Atribuindo funções de usuário
user_roles := {
"jack": ["speaker"],
"bobur":["admin"]
}
# Atribuições de permissões de função
role_permissions := {
"speaker": [{"permission": "read"}],
"admin": [{"permission": "read"}, {"permission": "write"}]
}
# Funções auxiliares do JWT
bearer_token := t {
t := input.request.headers.authorization
}
# Decodifica o token de autorização para obter uma função e permissão
token = {"payload": payload} {
[_, payload, _] := io.jwt.decode(bearer_token)
}
# Lógica que implementa o RBAC
default allow = false
allow {
# Procura a lista de funções para o usuário
roles := user_roles[input.consumer.username]
# Para cada função nessa lista
r := roles[_]
# Procura a lista de permissões para a função r
permissions := role_permissions[r]
# Para cada permissão
p := permissions[_]
# Verifica se a permissão concedida a r corresponde à solicitação do usuário
p == {"permission": token.payload.permission}
}'
Passo 11: Atualizar a configuração do plugin existente com o plugin OPA
Depois de definir as políticas no serviço OPA, precisamos atualizar a configuração do plugin existente para a rota para usar o plugin OPA. Especificamos no atributo policy
do plugin OPA.
curl "http://127.0.0.1:9180/apisix/admin/plugin_configs/1" -X PATCH -d '
{
"plugins":{
"opa":{
"host":"http://opa:8181",
"policy":"rbacExample",
"with_consumer":true
}
}
}'
Passo 12: Testar a configuração com o OPA
Agora podemos testar todas as configurações que fizemos com as políticas do OPA. Se você tentar executar o mesmo comando curl para acessar o endpoint do API Gateway, ele primeiro verificará o token JWT como parte do processo de autenticação e enviará os dados do consumidor e do token JWT para o OPA para verificar a função e a permissão como parte do processo de autorização. Qualquer solicitação sem um token JWT ou com funções não permitidas será negada.
curl -i http://127.0.0.1:9080/speaker/1/topics -H 'Authorization: {API_CONSUMER_TOKEN}'
Conclusão
Neste artigo, aprendemos como implementar o RBAC com OPA e Apache APISIX. Definimos uma lógica de política personalizada simples para permitir/negar o acesso a recursos da API com base na função e nas permissões do nosso consumidor de API. Além disso, este tutorial demonstrou como podemos extrair informações relacionadas ao consumidor da API no arquivo de política a partir do payload do token JWT ou do objeto do consumidor enviado pelo APISIX.
Recursos relacionados
- Política de Autorização do Apache APISIX: Proteja suas APIs
- Autenticação Centralizada com Plugins do Apache APISIX
- Gerenciar Consumidores de API com Apache APISIX