在南墙Web应用防火墙(uuWAF),看到一个比较有意思的功能:需要对外暴露一些api,允许用户写一些简单lua逻辑调用api来配置规则。根据需求,可以执行lua代码,但是用户又不能随意调用系统函数。如果要基于openresty的环境来实现这个功能,找到lua5.1中的函数setenv()很适合实现这个功能。先来熟悉一下这个函数。

原型:setfenv(f, table)

解释:设置参数f所指定函数使用的当前环境,参数f可以是一个Lua函数也可以是一个指定函数调用层级的数字,当参数为1时,表示正在调用函数getfenv()函数的函数。这个函数的返回值是参数f所指定的函数。有一种特殊情况就是当参数f是0的时候来改变函数所处的运行环境,这个函数setfenv不返回任何值

按照这个函数的用法,只需要传不同参数f进去就可以与当前环境隔离,直接看代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
--首先创建waf_api.lua文件,里面包括方法
local ngx = ngx
local setmetatable = setmetatable
local waf_api = {}
function waf_api.print_test(str)
    ngx.say(str)
    ngx.exit(ngx.HTTP_OK)
end

function waf_api.new(ngx_var)
    return setmetatable({
        --这样声明后就可以在规则里面通过waf_api.ip调用这个变量
        ip = ngx_var.remote_addr
    }, { __index = waf_api })
end
return waf_api
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
--下面就是调用api的代码,可以在access_by_lua阶段测试
local code = [[
waf_api.print_test(waf_api.ip)
]]

local f,err = assert(loadstring(code))
if err ~= nil then
    ngx.say(err)
end
local ngx_var = ngx.var
--注意waf_api里面的变量需要从外面传进去,不能在waf_api直接调用ngx.var
local env = {
waf_api = require("waf_api").new(ngx_var)
}
setfenv(f, env)
--这个可以有返回值的
return f()