From ab54c8b26b92e7861cff8bb9781cd4aaefc8e658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=B0=C3=97=C3=94=C3=B3=C6=BD?= Date: Wed, 8 Dec 2021 11:44:30 +0800 Subject: [PATCH] feat: add opa plugin --- apisix/core/request.lua | 19 ++++++-- apisix/plugins/opa.lua | 87 +++++++++++++++++++++++++++++++++++ apisix/plugins/opa/helper.lua | 45 ++++++++++++++++++ 3 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 apisix/plugins/opa.lua create mode 100644 apisix/plugins/opa/helper.lua diff --git a/apisix/core/request.lua b/apisix/core/request.lua index 95d84b95b4f66..88dbd87cb66fc 100644 --- a/apisix/core/request.lua +++ b/apisix/core/request.lua @@ -18,14 +18,16 @@ local lfs = require("lfs") local log = require("apisix.core.log") local io = require("apisix.core.io") +local str = require("apisix.core.string") local ngx = ngx local get_headers = ngx.req.get_headers local clear_header = ngx.req.clear_header -local tonumber = tonumber -local error = error -local type = type -local str_fmt = string.format +local tonumber = tonumber +local error = error +local type = type +local str_fmt = string.format local str_lower = string.lower +local str_sub = string.sub local req_read_body = ngx.req.read_body local req_get_body_data = ngx.req.get_body_data local req_get_body_file = ngx.req.get_body_file @@ -269,6 +271,15 @@ function _M.get_port(ctx) end +function _M.get_path(ctx, original) + if not ctx then + ctx = ngx.ctx.api_ctx + end + + return ctx.var.uri or '' +end + + function _M.get_http_version() return ngx.req.http_version() end diff --git a/apisix/plugins/opa.lua b/apisix/plugins/opa.lua new file mode 100644 index 0000000000000..fb169ea6ca56c --- /dev/null +++ b/apisix/plugins/opa.lua @@ -0,0 +1,87 @@ +local core = require("apisix.core") +local http = require("resty.http") +local helper = require("apisix.plugins.opa.helper") + +local schema = { + type = "object", + properties = { + host = {type = "string"}, + ssl_verify = { + type = "boolean", + default = true, + }, + package = {type = "string"}, + decision = {type = "string", maxLength = 256}, + timeout = { + type = "integer", + minimum = 1, + maximum = 60000, + default = 3000, + description = "timeout in milliseconds", + }, + keepalive = {type = "boolean", default = true}, + keepalive_timeout = {type = "integer", minimum = 1000, default = 60000}, + keepalive_pool = {type = "integer", minimum = 1, default = 5} + }, + required = {"host", "package", "decision"} +} + + +local _M = { + version = 0.1, + priority = 2001, + name = "opa", + schema = schema, +} + + +function _M.check_schema(conf) + local ok, err = core.schema.check(schema, conf) + if not ok then + return false, err + end + + return true +end + + +function _M.access(conf, ctx) + local body = helper.build_opa_input(conf, ctx, "http") + local params = { + method = "POST", + body = body, + headers = { + ["Content-Type"] = "application/json", + }, + keepalive = conf.keepalive, + ssl_verify = conf.ssl_verify + } + + if conf.keepalive then + params.keepalive_timeout = conf.keepalive_timeout + params.keepalive_pool = conf.keepalive_pool + end + + local endpoint = conf.host .. "/v1/data/" .. conf.package .. "/" .. conf.decision + + local httpc = http.new() + httpc:set_timeout(conf.timeout) + + local res, err = httpc:request_uri(endpoint, params) + + -- block by default when decision is unavailable + if not res or err then + core.log.error("failed to process OPA decision, err: ", err) + return 403 + end + + -- parse the results of the decision + local ret = core.json.decode(res.body).result + + if not ret then + return 403 + end +end + + +return _M diff --git a/apisix/plugins/opa/helper.lua b/apisix/plugins/opa/helper.lua new file mode 100644 index 0000000000000..8a3fddf0878fe --- /dev/null +++ b/apisix/plugins/opa/helper.lua @@ -0,0 +1,45 @@ +local core = require("apisix.core") +local ngx_var = ngx.var +local ngx_time = ngx.time + +local _M = {} + + +local function build_var(conf, ctx) + return { + server_addr = ngx_var.server_addr, + server_port = ngx_var.server_port, + remote_addr = ngx_var.remote_addr, + remote_port = ngx_var.remote_port, + timestamp = ngx_time(), + } +end + + +local function build_http_request(conf, ctx) + return { + scheme = core.request.get_scheme(ctx), + method = core.request.get_method(ctx), + host = core.request.get_host(ctx), + port = core.request.get_port(ctx), + path = core.request.get_path(ctx, true), + header = core.request.headers(ctx), + query = core.request.get_uri_args(ctx), + } +end + + +function _M.build_opa_input(conf, ctx, subsystem) + local request = build_http_request(conf, ctx) + + local data = { + type = subsystem, + request = request, + var = build_var(conf, ctx) + } + + return core.json.encode({input = data}) +end + + +return _M