diff --git a/rootfs/etc/nginx/lua/plugins.lua b/rootfs/etc/nginx/lua/plugins.lua new file mode 100644 index 000000000..d634bc2f4 --- /dev/null +++ b/rootfs/etc/nginx/lua/plugins.lua @@ -0,0 +1,48 @@ +local string_format = string.format +local new_tab = require "table.new" +local ngx_log = ngx.log +local INFO = ngx.INFO +local ERR = ngx.ERR + +local _M = {} +local MAX_NUMBER_OF_PLUGINS = 10000 +-- TODO: is this good for a dictionary? +local plugins = new_tab(MAX_NUMBER_OF_PLUGINS, 0) + +local function load_plugin(name) + local path = string_format("plugins.%s.main", name) + + local ok, plugin = pcall(require, path) + if not ok then + ngx_log(ERR, string_format("error loading plugin \"%s\": %s", path, plugin)) + return + end + + plugins[name] = plugin +end + +function _M.init(names) + for _, name in ipairs(names) do + load_plugin(name) + end +end + +function _M.run() + local phase = ngx.get_phase() + + for name, plugin in pairs(plugins) do + if plugin[phase] then + ngx_log(INFO, string_format("running plugin \"%s\" in phase \"%s\"", name, phase)) + + -- TODO: consider sandboxing this, should we? + -- probably yes, at least prohibit plugin from accessing env vars etc + -- but since the plugins are going to be installed by ingress-nginx operator they can be assumed to be safe also + local ok, err = pcall(plugin[phase]) + if not ok then + ngx_log(ERR, string_format("error while running plugin \"%s\" in phase \"%s\": %s", name, phase, err)) + end + end + end +end + +return _M diff --git a/rootfs/etc/nginx/lua/plugins/hello_world/main.lua b/rootfs/etc/nginx/lua/plugins/hello_world/main.lua new file mode 100644 index 000000000..799daa33e --- /dev/null +++ b/rootfs/etc/nginx/lua/plugins/hello_world/main.lua @@ -0,0 +1,15 @@ +local _M = {} + +function _M.rewrite() + ngx.req.set_header("x-hello-world", "1") +end + +function _M.access() + local ua = ngx.var.http_user_agent + + if ua == "hello" then + ngx.exit(403) + end +end + +return _M diff --git a/rootfs/etc/nginx/template/nginx.tmpl b/rootfs/etc/nginx/template/nginx.tmpl index f2b45b39b..f64463b5b 100644 --- a/rootfs/etc/nginx/template/nginx.tmpl +++ b/rootfs/etc/nginx/template/nginx.tmpl @@ -101,6 +101,15 @@ http { certificate = res end {{ end }} + + ok, res = pcall(require, "plugins") + if not ok then + error("require failed: " .. tostring(res)) + else + plugins = res + end + -- load all plugins that'll be used here + plugins.init({}) } init_worker_by_lua_block { @@ -109,6 +118,8 @@ http { {{ if $all.EnableMetrics }} monitor.init_worker() {{ end }} + + plugins.run() } {{/* Enable the real_ip module only if we use either X-Forwarded headers or Proxy Protocol. */}} @@ -976,11 +987,11 @@ stream { rewrite_by_lua_block { lua_ingress.rewrite({{ locationConfigForLua $location $server $all }}) balancer.rewrite() + plugins.run() } - {{ if shouldConfigureLuaRestyWAF $all.Cfg.DisableLuaRestyWAF $location.LuaRestyWAF.Mode }} - access_by_lua_block { + {{ if shouldConfigureLuaRestyWAF $all.Cfg.DisableLuaRestyWAF $location.LuaRestyWAF.Mode }} local lua_resty_waf = require("resty.waf") local waf = lua_resty_waf:new() @@ -1021,8 +1032,10 @@ stream { {{ end }} waf:exec() + {{ end }} + + plugins.run() } - {{ end }} header_filter_by_lua_block { {{ if shouldConfigureLuaRestyWAF $all.Cfg.DisableLuaRestyWAF $location.LuaRestyWAF.Mode }} @@ -1030,6 +1043,8 @@ stream { local waf = lua_resty_waf:new() waf:exec() {{ end }} + + plugins.run() } body_filter_by_lua_block { {{ if shouldConfigureLuaRestyWAF $all.Cfg.DisableLuaRestyWAF $location.LuaRestyWAF.Mode }} @@ -1049,6 +1064,8 @@ stream { {{ if $all.EnableMetrics }} monitor.call() {{ end }} + + plugins.run() } {{ if (and (not (empty $server.SSLCert.PemFileName)) $all.Cfg.HSTS) }}