Mercurial > prosody-modules
diff mod_firewall/trace.lib.lua @ 6538:de1f17ed4dac
mod_firewall: Add support for tracing rule evaluation in real-time
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Mon, 11 May 2026 13:37:19 +0100 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_firewall/trace.lib.lua Mon May 11 13:37:19 2026 +0100 @@ -0,0 +1,93 @@ +local async = require "prosody.util.async"; + +local tracers = {}; + +local trace_mt = { + __close = function (t) + for session, options in pairs(tracers) do + if not options.tag or options.tag == (t.tag or "untagged") then + session.print(("\n## Evaluating [%s::%s] (%s)"):format(t.filename, t.chain, t.tag or "untagged")); + local stanza_log = options.stanzas; + if stanza_log == "full" then + session.print(t.stanza); + elseif not stanza_log or stanza_log == "header" then + session.print(t.stanza:top_tag()); + end + + for rule_n, rule_trace in ipairs(t.rules) do + local source = t.source[rule_n].source; + for cond_n, matched in ipairs(rule_trace) do + session.print( + ("%s %s"):format( + matched and "✔" or "✖", + source.conditions[cond_n] or ("Rule %d condition %d"):format(rule_n, cond_n) + ) + ); + end + if rule_trace.matched then + for _, action in ipairs(source.actions) do + session.print("➜ "..action); + end + end + end + end + end + end; +}; + +local function init(filename, chain_name, stanza, tag, source_rules) + local rule_trace = {}; + local trace = setmetatable({ + filename = filename; + chain = chain_name; + rules = rule_trace; + stanza = stanza; + tag = tag; + source = source_rules; + }, trace_mt); + return trace, function (rule_n, cond_n, result) + local rule = rule_trace[rule_n]; + if not rule then + rule = {}; + rule_trace[rule_n] = rule; + end + + if cond_n then + rule[cond_n] = result; + return result; + else + -- Rule finished + rule.matched = result; + end + end; +end + + +module:add_item("shell-command", { + section = "firewall"; + section_desc = "mod_firewall commands"; + name = "trace"; + desc = "Trace executions"; + args = { + { name = "host", type = "string" }; + }; + host_selector = "host"; + flags = { + short_params = { t = "tag" }; + value_params = { tag = true, stanzas = true }; + }; + handler = function(self, host, opts) --luacheck: ignore 212/self 212/host + self.session.print("# Tracing mod_firewall on "..host); + tracers[self.session] = opts or {}; + + while self.session.is_connected() do + async.sleep(3); + end + + tracers[self.session] = nil; + return true; + end; +}); + + +return { init = init };
