Casbin authorization plugin for APISIX

Clone this repo:
  1. 121c94f ci: pin OpenResty 1.19.9.1 for APISIX 2.7 compatibility (#13) by Ziyang Dong · 3 weeks ago master
  2. aaa8dd2 ci: fetch APISIX 2.7 tarball from archive.apache.org by Yang Luo · 3 weeks ago
  3. e63f746 chore: update URLs and CI paths for apache org by Yang Luo · 3 weeks ago
  4. 46261c6 docs: expand README badges by Yang Luo · 3 weeks ago
  5. 13a8484 ci: add tag-triggered manual release workflow by Yang Luo · 3 weeks ago

Use this instead: https://github.com/apache/apisix/blob/master/docs/en/latest/plugins/authz-casbin.md

apisix-authz

GitHub Action Release LuaRocks License Discord

Generic badge Generic badge APISIX Casbin Sourcegraph

apisix-authz is an authorization plugin for APISIX based on lua-casbin.

Installation

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

Configuration

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.

Multiple models and policies

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.

Development

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.

Documentation

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:

  1. subject: the logged-in username as passed in the header
  2. object: the URL path for the web resource like “dataset1/item1”
  3. 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.

Example

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.

Getting Help

License

This project is under the Apache 2.0 License.