APISIX网关OpenResty插件开发

Posted crazycode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了APISIX网关OpenResty插件开发相关的知识,希望对你有一定的参考价值。


APISIX 是基于云原生的微服务 API 网关,它是所有业务流量的入口,可以处理传统的南北向流量,也可以处理服务间的东西向流量, APISIX 通过插件机制,提供动态负载平衡、身份验证、限流限速等功能,并且支持你自己开发的插件.


下面介绍自定义APISIX插件开发流程,我们以基于IP黑名单限制插件为例



1. OpenResty脚本开发


插件主要功能基于Redis实现管理黑名单IP池,并利用openresty进行IP限制访问


1. 基于Redis的动态IP黑名单池

2. 插件定时从Redis 获取最新IP黑名单 

3. 匹配IP限制访问返回403


*ip-redis-restriction.lua*

local ipairs = ipairslocal core = require("apisix.core")

-- 插件名称local plugin_name = "ip-redis-restriction"
-- 插件参数schemalocal schema = { type = "object", properties = {
}, additionalProperties = false}

-- 插件的配置信息local _M = { version = 0.1, priority = 3000, name = plugin_name, schema = schema,}

-- 需要实现 check_schema(conf) 方法,完成配置参数的合法性校验。function _M.check_schema(conf) return core.schema.check(schema, conf)end
-- redis IP黑名单键值local key = "ip_blacklist"-- ipmatcherlocal ipmatcher = require("resty.ipmatcher")local blacklist_matcher = nil
local lastUpdateTime = ngx.now()
-- 关闭redislocal function close_redis(red) if not red then return end -- 释放连接池设置 local pool_max_idle_time = 10000 --单位毫秒 local pool_size = 100 --连接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then core.log.error("set keepalive error : ", err) endend

local function rebuild_ip_cache()
local iptable = {} --引入redis模块 local redis = require "resty.redis" --创建一个对象,注意是用冒号调用的 local red = redis:new() --设置超时(毫秒) red:set_timeout(3000) local ip = "127.0.0.1" local port = 6379 local passwd = "12345678" local ok, err = red:connect(ip,port)
if not ok then core.log.error("red connect error: ", err) return end
local count, err = red:get_reused_times()
----count> 0 从连接池中获取连接,无需再次认证密码 if 0 == count then ----新建连接,需要认证密码 ok, err = red:auth(passwd) if not ok then core.log.error("failed to auth: ", err) return end elseif err then core.log.error("failed to get reused times: ", err) return end

local ip_blacklist, err = red:smembers(key)
if err then core.log.error("limit ip smembers ",err) else for i,bip in ipairs(ip_blacklist) do -- cache:set(bip,1) table.insert(iptable,bip) end end -- 关闭redis释放回连接池 close_redis(red)
local ip,err = ipmatcher.new(iptable) -- core.log.warn("blacklist_matcher update") if not ip then core.log.error("blacklist_matcher update failed",err) end blacklist_matcher = ip
end
-- 检测是否需要重新从redis拿IP数据缓存local function check_cache_need_update()
-- check缓存TTL过期,过期了则从redis取数据
if lastUpdateTime == nil then lastUpdateTime = ngx.now() core.log.warn('get_ip_backlist lastUpdateTime is nil ') end
local diffTime = ngx.now() - lastUpdateTime
-- 每过1分钟了,重新从redis取最新IP黑名单数据 if diffTime >= 60 or blacklist_matcher == nil then rebuild_ip_cache() core.log.warn('rebuild_ip_cache begin') lastUpdateTime = ngx.now() core.log.warn('rebuild_ip_cache success') else -- core.log.warn('no need update cache ',diffTime) end end
-- 执行阶段function _M.access(conf,ctx)        core.log.warn('access conf ',core.json.encode(conf))        local remote_addr = ctx.var.http_x_forwarded_for if remote_addr == nil then --取不到就用remote_addr remote_addr = ctx.var.remote_addr end
check_cache_need_update()
if blacklist_matcher == nil then core.log.warn('blacklist_matcher is nill') return end
    local data,err = blacklist_matcher:match(remote_addr)
if data then core.log.warn('access cache hit blacklist ',core.json.encode(data),remote_addr) -- 不能在 rewrite 和 access 阶段调用 ngx.exit 或者 core.respond.exit。如果确实需要退出,只需要 return 状态码和正文,插件引擎将使用返回的状态码和正文进行退出 return 403, {error_msg = "Forbidden"} -- return core.response.exit(403) endendreturn _M


2. 配置插件


  • 1. 插件代码开发后我们在./apisix/plugins/ 目录下增加 ip-redis-restriction.lua

  • 2. 在文件./conf/config-defaut.yaml 配置新增插件名,如下


    plugins:   # plugin list (sorted in alphabetical order) - api-breaker - authz-keycloak - basic-auth - batch-requests - consumer-restriction .... - ip-redis-restriction

*插件热加载生效*

curl http://127.0.0.1:9080/apisix/admin/plugins/reload -H 'X-API-KEY: xxxxxxxx' -X PUT


  • 3.更新dashboard插件信息


执行以下命令

curl 127.0.0.1:9090/v1/schema > schema.json


将schema.json拷贝到 dashboard的工作目录conf下,重启管理API, 让控制台能显示插件,方便我们在dashboard界面上配置管理.


[root@test conf]# lsconf.yaml schema.json

4. 配置API路由插件


在配置路由设置时插件配置阶段找到ip-redis-restriction插件,启用并保存提交



5. 验证效果


curl验证下插件是否生效.   将58.243.49.20加入IP池,从结果可以判断插件已经生效返回了403

[root@test plugins]# curl -v http://127.0.0.1:9080/test-api/v1/test -H 'x-forwarded-for:58.243.49.20'.....< HTTP/1.1 403 Forbidden< Date: Sat, 17 Jul 2021 06:17:14 GMT< Content-Type: text/plain; charset=utf-8< Transfer-Encoding: chunked< Connection: keep-alive< Server: APISIX/2.6<{"error_msg":"Forbidden"}



6. 文档


  • http://apisix.apache.org/zh/docs/apisix/plugin-develop

  • https://github.com/apache/apisix-dashboard/blob/master/docs/en/latest/FAQ.md


以上是关于APISIX网关OpenResty插件开发的主要内容,如果未能解决你的问题,请参考以下文章

结合 casbin 为 APISIX 开发一个接口权限校验插件

国产微服务网关 APISIX,有点意思,直接开锤,换掉家门口的 Nginx

Apache Apisix 安全漏洞(CVE-2020-13945)

Apache Apisix 安全漏洞(CVE-2020-13945)

Apache Apisix 安全漏洞(CVE-2020-13945)

再谈 APISIX 高性能实践