apisix-authz is an authorization plugin for APISIX based on lua-casbin.
Ensure you have Casbin's system dependencies installed by:
sudo apt install gcc libpcre3 libpcre3-dev
Install Casbin's latest release from LuaRocks by:
sudo luarocks install casbin
Then clone this repo and copy the apisix-authz.lua file to your plugin directory (modify the plugin according to your situation if you wish to):
git clone https://github.com/apache/casbin-lua-apisix-authz cp casbin-lua-apisix-authz/apisix-authz.lua path_to_apisix/plugins/apisix-authz.lua
And finally append the plugins section of your conf/config.yaml with apisix-authz to something like this (more on this here):
plugins: # plugin list ... - apisix-authz
You can add this plugin globally or on any route/service by sending a request through the Admin API. For example, for adding it to some route:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/*",
"plugins": {
"apisix-authz": {
"model_path": "/path/to/model_path.conf",
"policy_path": "/path/to/policy_path.csv",
"username": "user"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"example.com": 1
}
}
}'
For adding this globally:
curl -X PUT \
https://127.0.0.1:9080/apisix/admin/global_rules/1 \
-H 'Content-Type: application/json' \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-d '{
"plugins": {
"apisix-authz": {
"model_path": "/path/to/model_path.conf",
"policy_path": "/path/to/policy_path.csv",
"username": "user"
}
}
}'
As per the current configuration, if the request is authorized, the execution will proceed normally. While if it is not authorized, it will return “Access Denied” message with the 403 exit code and stop any further execution.
Different routes can use different models and policies. The plugin keeps one Casbin Enforcer per model_path + policy_path pair, so a route is always enforced against the model and policy it was configured with:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/service1/*",
"plugins": {
"apisix-authz": {
"model_path": "/path/to/service1_model.conf",
"policy_path": "/path/to/service1_policy.csv",
"username": "user"
}
},
"upstream": {"type": "roundrobin", "nodes": {"service1.com": 1}}
}'
curl http://127.0.0.1:9080/apisix/admin/routes/2 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/service2/*",
"plugins": {
"apisix-authz": {
"model_path": "/path/to/service2_model.conf",
"policy_path": "/path/to/service2_policy.csv",
"username": "user"
}
},
"upstream": {"type": "roundrobin", "nodes": {"service2.com": 1}}
}'
Routes that share the same model_path and policy_path share a single Enforcer, so a policy added at runtime through the plugin API is visible to all of them.
The policy management APIs (/apisix/plugin/casbin/add, remove, has, get and save) act on one Enforcer, selected with the model_path and policy_path headers:
curl -i -X POST \ --url http://127.0.0.1:9080/apisix/plugin/casbin/add \ --header 'model_path: /path/to/service1_model.conf' \ --header 'policy_path: /path/to/service1_policy.csv' \ --header 'type: p' \ --header 'subject: alice' \ --header 'object: /service1/data' \ --header 'action: GET'
These two headers may be omitted only when a single Enforcer exists. Otherwise the request is answered with a 400 status code, since the Enforcer to act on would be ambiguous.
If you wish to customize this according to your scenario, you can do so by customizing the apisix-authz.lua file from your plugins directory.
The authorization determines a request based on {subject, object, action}, which means what subject can perform what action on what object. In this plugin, the meanings are:
subject: the logged-in username as passed in the headerobject: the URL path for the web resource like “dataset1/item1”action: HTTP method like GET, POST, PUT, DELETE, or the high-level actions you defined like “read-file”, “write-blog” For how to write authorization policy and other details, please refer to the Casbin's documentation.An example of policy file and model file is given in the examples directory of this repo.
The example model file is:
[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)
And the example policy file is:
p, *, /, GET p, admin, *, * g, alice, admin
This means that all users can access the homepage / but only users with admin permissions like alice can access other pages and other HTTP request methods.
Now if you send a request (using the example model/policy files) as:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/*",
"plugins": {
"apisix-authz": {
"model_path": "/path/to/authz_model.conf",
"policy_path": "/path/to/authz_policy.csv",
"username": "user"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"example.com": 1
}
},
"host": "example.com"
}'
This will configure model and policy on this route on all URIs (/*). Now if you send a request for the first time as:
curl -i -X GET \ --url http://127.0.0.1:9080/ \ --header 'user: anonymous' \ --header 'host: example.com'
When run for the first time, it will create a Casbin Enforcer using the model path and policy path. If this returns a 200 (OK) status code, then the configuration is good to go otherwise please check the error.log file in your logs directory of APISIX.
But if you the send request as:
curl -i -X POST \ --url http://127.0.0.1:9080/ \ --header 'user: anonymous' \ --header 'host: example.com'
This will result in a 403 error, since as per the policy configuration any non-admin user can not use any non-GET request methods. If you change the ‘user’ header to alice, this will be an authorized request then.
This project is under the Apache 2.0 License.