<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于APISIX的ABAC模型鑒權插件開發(fā)

          共 8225字,需瀏覽 17分鐘

           ·

          2022-04-20 21:27

          APISIX是一個非常優(yōu)秀的開源全流量網(wǎng)關,內置了很多插件。但如果要擴展實現(xiàn)自定義的插件,網(wǎng)上可參考的文章非常少。本文將以簡化版ABAC模型的鑒權需求為例介紹APISIX插件的開發(fā)。

          前置知識

          1. 如果不了解APISIX請先移步官網(wǎng):?https://apisix.apache.org/

          2. 如果不會lua請先學習基礎的語法:https://www.lua.org/start.html 或是 https://learnxinyminutes.com/docs/lua/

          3. 也許你還需要知道一點點perl語法:https://learnxinyminutes.com/docs/perl/

          4. 最后還需要先學習下APISIX官方的插件開發(fā)教程,核心的內容已經(jīng)講得明明白白了:https://apisix.apache.org/zh/docs/apisix/plugin-develop

          插件說明

          不同于主流的RBAC( https://en.wikipedia.org/wiki/Role-based_access_control )權限模型,ABAC( https://en.wikipedia.org/wiki/Attribute-based_access_control )具備各靈活的權限配置策略,該模型的介紹也可參見筆者過往的文章( http://www.idealworld.group/2021/01/18/iam-more-elegant-model-and-implementation/?)。

          目前筆者在構建的 BIOS( https://github.com/ideal-world/bios ,歡迎star)項目需要使用到全流量、高性能網(wǎng)關,選型為APISIX,在此基礎上需要擴展實現(xiàn)簡化版ABAC模型的鑒權插件。

          環(huán)境準備

          Tip

          如果覺得麻煩可嘗試使用筆者寫的一鍵安裝腳本:https://github.com/ideal-world/bios/blob/main/gateway/init-ubuntu.sh

          Tip

          下文基于ubuntu(含wsl2)介紹,如操作系統(tǒng)有差異請自行修改。
          1. 添加Openresty源

            wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
            sudo apt-get update
            sudo apt-get -y install software-properties-common
            sudo apt-get update
          2. 安裝Lua及各類開發(fā)類庫/工具

            # 注意lua必須為dev版本
            sudo apt-get -y install git curl liblua5.1-0-dev openresty openresty-openssl111-dev cpanminus
          3. 安裝并啟動ETCD

            wget https://github.com/etcd-io/etcd/releases/download/v3.4.13/etcd-v3.4.13-linux-amd64.tar.gz
            tar -xvf etcd-v3.4.13-linux-amd64.tar.gz
            rm etcd-v3.4.13-linux-amd64.tar.gz
            mv etcd-v3.4.13-linux-amd64 etcd
            cd etcd
            sudo cp -a etcd etcdctl /usr/bin/
            cd ..
            nohup etcd /dev/null 2>&1 &
          4. 安裝LuaRocks

            # 官網(wǎng)的腳本(https://raw.githubusercontent.com/apache/apisix/master/utils/linux-install-luarocks.sh)在筆者的環(huán)境并不能正常安裝(E.g. OPENRESTY_PREFIX="/usr/local/openresty" 位置不正確,缺少sudo等)
            curl https://raw.githubusercontent.com/ideal-world/bios/main/gateway/utils/linux-install-luarocks.sh -sL | bash -
          5. 下載APISIX

            # 可修改成需要的版本
            wget https://mirrors.bfsu.edu.cn/apache/apisix/2.8/apache-apisix-2.8-src.tgz
            tar -cvf apisix.tar apisix
            tar -xf apache-apisix-2.8-src.tgz -C apisix
            tar -xf apisix.tar
            rm apisix.tar
            rm apache-apisix-2.8-src.tgz
          6. 安裝依賴

            cd apisix
            make deps
          7. 下載test-nginx并安裝依賴

            git clone --depth=1 https://github.com/iresty/test-nginx.git
            rm -rf test-nginx/.git
            sudo cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
            export PERL5LIB=.:$PERL5LIB

          插件開發(fā)

          如果一切順利,接下來我們就可以開發(fā)插件了。

          APISIX的插件位于?apisix/plugins/?下,新建一個?auth-bios?的目錄及?auth-bios.lua?文件,前者存放的是核心邏輯,后者為插件入口。

          新插件還需要添加到?conf/config-default.yaml?的?plugins:?下:

          plugins:
          ...
          - serverless-post-function
          - ext-plugin-post-req
          - auth-bios

          Tip

          如果是流式插件需要添加到?stream_plugins?下。

          auth-bios.lua?作為插件定義文件需要注意幾個版式化的約定:

          -- 引入一堆依賴
          local core = require("apisix.core")
          local m_redis = require("apisix.plugins.auth-bios.redis")
          ...

          -- 標識插件名稱
          local plugin_name = "auth-bios"

          -- 定義插件配置參數(shù)
          local schema = {
          type = "object",
          properties = {
          -- 一堆配置參數(shù)
          redis_host = { type = "string" },
          redis_port = { type = "integer", default = 6379 },
          redis_password = { type = "string" },
          ...
          },
          -- 必選參數(shù)列表
          required = { "redis_host" }
          }

          -- 定義插件信息
          local _M = {
          version = 0.1,
          -- 優(yōu)先級,官方推薦1~99,過高的優(yōu)先級先搶奪一些基礎插件的優(yōu)先執(zhí)行權
          priority = 5001,
          -- 由于需要與consumer配合,故設置成auth,詳見:https://apisix.apache.org/zh/docs/apisix/architecture-design/consumer
          type = 'auth',
          name = plugin_name,
          schema = schema,
          }

          -- check_schema方法在安裝插件時調用,用于檢查插件是否合法
          function _M.check_schema(conf)
          -- 此方法會檢查插件配置是否合法(E.g. 是否缺少必選參考)
          local check_ok, check_err = core.schema.check(schema, conf)
          if not check_ok then
          core.log.error("Configuration parameter error")
          return false, check_err
          end
          -- 此方法亦可以用于全局初始化,E.g. 建立redis連接
          local _, redis_err = m_redis.init(conf.redis_host, conf.redis_port, conf.redis_database, conf.redis_timeout, conf.redis_password)
          if redis_err then
          core.log.error("Connect redis error", redis_err)
          return false, redis_err
          end
          ...
          -- 如果檢查通過返回true
          return true
          end

          -- rewrite方法在每次請求命中插件時調用,也是插件處理的核心邏輯
          function _M.rewrite(conf, ctx)
          -- 這里調用了 auth-bios 目錄下的各個具體處理邏輯
          -- 先根據(jù)請求入?yún)@取請求身份(誰發(fā)起的請求,可以是人、租戶、應用、設備等)及請求的資源(基于uri)
          local ident_code, ident_message = m_ident.ident(conf, ctx)
          if ident_code ~= 200 then
          return ident_code, ident_message
          end
          -- 然后判斷該身份有沒有訪問對應資源的權限
          local auth_code, auth_message = m_auth.auth(ctx.ident_info)
          if auth_code ~= 200 then
          return auth_code, auth_message
          end
          -- 如果有權限則組裝請求信息傳向目標服務
          core.request.set_header(ctx, conf.ident_flag, ngx_encode_base64(json.decode({
          res_action = ctx.ident_info.resource_action,
          res_uri = ctx.ident_info.resource_uri,
          app_id = ctx.ident_info.app_id,
          tenant_id = ctx.ident_info.tenant_id,
          account_id = ctx.ident_info.account_id,
          token = ctx.ident_info.token,
          token_kind = ctx.ident_info.token_kind,
          ak = ctx.ident_info.ak,
          roles = ctx.ident_info.roles,
          groups = ctx.ident_info.groups,
          })))
          end

          -- 返回插件信息
          return _M

          Tip

          完整文件見:https://github.com/ideal-world/bios/blob/main/gateway/apisix/apisix/plugins/auth-bios.lua

          auth-bios?目錄包含核心處理邏輯及一些輔助方法,與APISIX插件開發(fā)的規(guī)約關系不大,本文不展開介紹,有興趣的讀者可到對應的github工程下查看。

          插件單元測試

          Tip

          APISIX的測試基于?test-nginx,如果是熟悉Java、Go、Node、Rust等項目的開發(fā),那么對這種測試方案一定很難適應。筆者覺得并不優(yōu)雅且使用復雜,當然也許是沒有領會到其精髓。

          測試文件約定寫在?t/plugin/?下,我們創(chuàng)建同名的?auth-bios?目錄,這里舉一個簡單的例子。

          use t::APISIX 'no_plan';

          no_long_string();
          no_root_location();
          no_shuffle();
          run_tests;

          __DATA__
          === TEST 1: test redis
          --- config
          location /t {
          content_by_lua_block {
          local m_utils = require("apisix.plugins.auth-bios.utils")
          local m_redis = require("apisix.plugins.auth-bios.redis")
          local m_redis1 = require("apisix.plugins.auth-bios.redis")
          m_redis.init("127.0.0.1", 6379, 1, 1000, "123456")
          m_redis1.set("test", "測試1")
          ngx.say(m_redis.get("test"))
          m_redis.hset("test_hash","api://xx/?1","{\"a\":\"xx1\"}")
          m_redis.hset("test_hash","api://xx/?2","{\"a\":\"xx2\"}")
          m_redis.hset("test_hash","api://xx/?3","{\"a\":\"xx3\"}")
          m_redis.hset("test_hash","api://xx/?4","{\"a\":\"xx4\"}")
          m_redis.hset("test_hash","api://xx/?5","{\"a\":\"xx5\"}")
          m_redis.hscan("test_hash","*",2, function(k,v) ngx.say(k..":"..v) end)
          m_redis.hscan("not_exist","*",2, function(k,v) ngx.say(k..":"..v) end)
          }
          }
          --- request
          GET /t
          --- response_body
          測試1
          api://xx/?1:{"a":"xx1"}
          api://xx/?2:{"a":"xx2"}
          api://xx/?3:{"a":"xx3"}
          api://xx/?4:{"a":"xx4"}
          api://xx/?5:{"a":"xx5"}
          --- no_error_log
          [error]

          測試邏輯寫在?__DATA__?下,通過?ngx.say?返回數(shù)據(jù),然后在?response_body?中輸出并比對是否匹配。

          單元測試的方式如下:

          export PERL5LIB=.:$PERL5LIB

          TEST_NGINX_BINARY=/usr/bin/openresty prove -Itest-nginx/lib -r t/plugin/auth-bios/redis.t

          插件集成測試

          插件的集成測試需要先編譯并啟動APISIX,常用命令如下:

          # initialize NGINX config file and etcd
          make init

          # start Apache APISIX server
          make run

          # stop Apache APISIX server gracefully
          make quit

          # stop Apache APISIX server immediately
          make stop

          然后就可以發(fā)起測試,示例如下:

          # 添加upstream
          curl "http://127.0.0.1:9080/apisix/admin/upstreams/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
          {
          "type": "roundrobin",
          "nodes": {
          "httpbin.org:80": 1
          }
          }'

          # 添加route
          curl "http://127.0.0.1:9080/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
          {
          "uri": "/cache/**",
          "upstream_id": "1"
          }'

          # 測試成功
          curl -i -X GET "http://127.0.0.1:9080/cache/1"

          # 添加全局插件(需要開啟Redis)
          curl "http://127.0.0.1:9080/apisix/admin/global_rules/1" -H "Content-Type: application/json" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
          {
          "plugins": {
          "auth-bios": {
          "redis_host": "127.0.0.1",
          "redis_password": "123456",
          "redis_database": 1
          }
          }
          }'

          # 獲取全局插件列表
          curl http://127.0.0.1:9080/apisix/admin/global_rules -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'

          # 測試失敗,缺少 BIOS-Host
          curl -i -X GET "http://127.0.0.1:9080/cache/1"
          # 測試成功
          curl -i -X GET "http://127.0.0.1:9080/cache/1" -H 'BIOS-Host: app1.tenant1'

          # 測試失敗,Token錯誤
          curl -i -X GET "http://127.0.0.1:9080/cache/1" -H 'BIOS-Host: app1.tenant1' -H 'BIOS-Token: token001'
          # 測試成功(先執(zhí)行單元測試)
          curl -i -X GET "http://127.0.0.1:9080/cache/1" -H 'BIOS-Host: app1.tenant1' -H 'BIOS-Token: tokenxxx'

          小結

          APISIX的插件開發(fā)并不算復雜,但需要學習lua、perl及相關的知識,還是有一定的門檻,所以APISIX也推出了插件擴展機制,支持使用自己熟悉的語言開發(fā)插件。但從運行機制看畢竟有本地RPC交互,在性能上可能會有些損失。說到性能,插件質量的優(yōu)劣對APISIX的影響很大,本文示例的插件也有許多可優(yōu)化的地方,比如更少的序列化邏輯、使用pb代替json、計算復雜度高的邏輯使用C/Rust封裝等。


          瀏覽 77
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产女生被男生操网站 | 婷婷婷夜色 | 黄色强奸免费小视频网站 | 操操操操操操操操操操操操操操操操操逼 | 操逼勉费看 |