Introdução
APISIX é um gateway de API nativo da nuvem de alta performance e escalável baseado em Nginx e etcd. É um projeto de código aberto da Apache Software Foundation. Além disso, o que torna o APISIX tão bom é o suporte de muitos ótimos plugins integrados que podem ser usados para implementar recursos como autenticação, monitoramento, roteamento, etc. E o fato de que os plugins no APISIX são recarregados em tempo real (sem reinicializações) o torna muito dinâmico.
Mas ao usar o APISIX, podem surgir cenários em que você queira adicionar lógica de autorização complexa em sua aplicação. É aqui que o authz-casbin pode ajudá-lo, authz-casbin é um plugin do APISIX baseado no Lua Casbin que permite uma autorização poderosa baseada em vários modelos de controle de acesso. Casbin é uma biblioteca de autorização que suporta modelos de controle de acesso como ACL, RBAC, ABAC. Originalmente escrito em Go, foi portado para muitas linguagens e Lua Casbin é a implementação em Lua do Casbin. O desenvolvimento do authz-casbin começou quando propusemos um novo plugin para autorização no repositório do APISIX (#4674) ao qual os membros principais concordaram. E após as revisões úteis que levaram a algumas mudanças importantes e melhorias, o PR (#4710) foi finalmente mesclado.
Neste blog, usaremos o plugin authz-casbin para mostrar como você pode implementar um modelo de autorização baseado em Controle de Acesso Baseado em Funções (RBAC) no APISIX.
NOTA: Você precisará usar algum outro plugin ou fluxo de trabalho personalizado para autenticar o usuário, já que o Casbin fará apenas autorização e não autenticação.
Criando um modelo
O plugin usa três parâmetros para autorizar qualquer solicitação - sujeito, objeto e ação. Aqui, sujeito é o valor do cabeçalho do nome de usuário, que poderia ser algo como [username: alice]
. Então, o objeto é o caminho da URL que está sendo acessado e a ação é o método de solicitação usado.
Vamos dizer que queremos criar um modelo com três recursos nos caminhos - /
, /res1
e /res2
. E queremos ter um modelo assim:
Isso significaria que todos os usuários (*
) como por exemplo jack
podem acessar a página inicial (/
). E usuários com permissões de admin
como alice
e bob
podem acessar todas as páginas e recursos (como res1
e res2
). Além disso, vamos restringir usuários sem quaisquer permissões de admin a usar apenas o método de solicitação GET
. Para este cenário, poderíamos definir o modelo como:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)
Criando uma política
Do cenário acima, a política seria:
p, *, /, GET
p, admin, *, *
g, alice, admin
g, bob, admin
O matcher do modelo significa:
(g(r.sub, p.sub) || keyMatch(r.sub, p.sub))
: Ou o sujeito da solicitação tem um papel como o sujeito da política ou o sujeito da solicitação corresponde ao sujeito da política emkeyMatch
.keyMatch
é uma função integrada no Lua Casbin, você pode dar uma olhada na descrição da função e em mais funções que podem ser úteis aqui.keyMatch(r.obj, p.obj)
: O objeto da solicitação corresponde ao objeto da política (caminho da URL aqui).keyMatch(r.act, p.act)
: A ação da solicitação corresponde à ação da política (método de solicitação HTTP aqui).
Habilitando o plugin em uma rota
Uma vez que você tenha criado o modelo e a política, você pode habilitá-los em uma rota usando a API Admin do APISIX. Para habilitá-los usando caminhos de arquivos de modelo e política:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"authz-casbin": {
"model_path": "/path/to/model.conf",
"policy_path": "/path/to/policy.csv",
"username": "username"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/*"
}'
Aqui, o campo de nome de usuário é o nome do cabeçalho que você usará para passar o sujeito. Por exemplo, se você for passar o cabeçalho de nome de usuário como user: alice
, você usaria "username": "user"
.
Para usar texto de modelo/política em vez de arquivos, você pode usar os campos model
e policy
em vez disso:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"authz-casbin": {
"model": "[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",
"policy": "p, *, /, GET
p, admin, *, *
g, alice, admin
g, bob, admin",
"username": "username"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/*"
}'
Habilitando o plugin usando um modelo/política global
Pode haver situações em que você queira usar uma única configuração de modelo e política em várias rotas. Você pode fazer isso primeiro enviando uma solicitação PUT
para adicionar a configuração de modelo e política aos metadados do plugin por:
curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/authz-casbin -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
{
"model": "[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)",
"policy": "p, *, /, GET
p, admin, *, *
g, alice, admin
g, bob, admin"
}'
E então para habilitar a mesma configuração em alguma rota, envie uma solicitação usando a API Admin:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"authz-casbin": {
"username": "username"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/route1/*"
}'
Isso adicionará a configuração de metadados do plugin à rota. Você também pode facilmente atualizar a configuração de metadados do plugin reenviando a solicitação aos metadados do plugin com a configuração de modelo e política atualizada, o plugin atualizará automaticamente todas as rotas usando a configuração de metadados do plugin.
Casos de Uso
- O principal caso de uso deste plugin seria na implementação de autorização em suas APIs. Você pode facilmente adicionar este plugin em qualquer rota de API que você esteja usando com seu modelo de autorização e configuração de política.
- Se você quiser ter um único modelo de autorização para todas as suas APIs, você pode usar o método de modelo/política global. Isso facilita a atualização da política para todas as rotas, já que você só precisa atualizar os metadados no etcd.
- Enquanto se você gostaria de usar um modelo diferente para cada rota diferente, você pode usar o método de rota. Isso é útil quando diferentes rotas de API têm diferentes conjuntos de permissões de usuário. Você também pode usar isso quando estiver lidando com políticas maiores, pois tornará a autorização mais rápida quando filtrada em várias rotas.