Mercurial > prosody-modules
comparison mod_firewall/mod_firewall.lua @ 2578:6dbd07f9a868
mod_firewall: Various improvements allowing dynamic load/reload/unload of scripts
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Sat, 25 Feb 2017 16:54:52 +0000 |
| parents | f65c5927ee8e |
| children | 5e948d1392a5 |
comparison
equal
deleted
inserted
replaced
| 2577:00cef058df8d | 2578:6dbd07f9a868 |
|---|---|
| 1 | 1 |
| 2 local lfs = require "lfs"; | |
| 2 local resolve_relative_path = require "core.configmanager".resolve_relative_path; | 3 local resolve_relative_path = require "core.configmanager".resolve_relative_path; |
| 3 local logger = require "util.logger".init; | 4 local logger = require "util.logger".init; |
| 4 local it = require "util.iterators"; | 5 local it = require "util.iterators"; |
| 6 local set = require "util.set"; | |
| 5 | 7 |
| 6 local definitions = module:shared("definitions"); | 8 local definitions = module:shared("definitions"); |
| 7 local active_definitions = { | 9 local active_definitions = { |
| 8 ZONE = { | 10 ZONE = { |
| 9 -- Default zone that includes all local hosts | 11 -- Default zone that includes all local hosts |
| 547 script_path = script_path:match("^module:(.+)$"); | 549 script_path = script_path:match("^module:(.+)$"); |
| 548 end | 550 end |
| 549 return resolve_relative_path(relative_to, script_path); | 551 return resolve_relative_path(relative_to, script_path); |
| 550 end | 552 end |
| 551 | 553 |
| 554 -- [filename] = { last_modified = ..., events_hooked = { [name] = handler } } | |
| 555 local loaded_scripts = {}; | |
| 556 | |
| 552 function load_script(script) | 557 function load_script(script) |
| 553 script = resolve_script_path(script); | 558 script = resolve_script_path(script); |
| 554 local chain_functions, err = compile_firewall_rules(script) | 559 local last_modified = (lfs.attributes(script) or {}).modification or os.time(); |
| 560 if loaded_scripts[script] then | |
| 561 if loaded_scripts[script].last_modified == last_modified then | |
| 562 return; -- Already loaded, and source file hasn't changed | |
| 563 end | |
| 564 module:log("debug", "Reloading %s", script); | |
| 565 -- Already loaded, but the source file has changed | |
| 566 -- unload it now, and we'll load the new version below | |
| 567 unload_script(script, true); | |
| 568 end | |
| 569 local chain_functions, err = compile_firewall_rules(script); | |
| 555 | 570 |
| 556 if not chain_functions then | 571 if not chain_functions then |
| 557 module:log("error", "Error compiling %s: %s", script, err or "unknown error"); | 572 module:log("error", "Error compiling %s: %s", script, err or "unknown error"); |
| 558 else | 573 return; |
| 559 for chain, handler_code in pairs(chain_functions) do | 574 end |
| 560 local new_handler, err = compile_handler(handler_code, "mod_firewall::"..chain); | 575 |
| 561 if not new_handler then | 576 -- Loop through the chains in the script, and for each chain attach the compiled code to the |
| 562 module:log("error", "Compilation error for %s: %s", script, err); | 577 -- relevant events, keeping track in events_hooked so we can cleanly unload later |
| 563 else | 578 local events_hooked = {}; |
| 564 local chain_definition = chains[chain]; | 579 for chain, handler_code in pairs(chain_functions) do |
| 565 if chain_definition and chain_definition.type == "event" then | 580 local new_handler, err = compile_handler(handler_code, "mod_firewall::"..chain); |
| 566 local handler = new_handler(chain_definition.pass_return); | 581 if not new_handler then |
| 567 for _, event_name in ipairs(chain_definition) do | 582 module:log("error", "Compilation error for %s: %s", script, err); |
| 568 module:hook(event_name, handler, chain_definition.priority); | 583 else |
| 569 end | 584 local chain_definition = chains[chain]; |
| 570 elseif not chain:sub(1, 5) == "user/" then | 585 if chain_definition and chain_definition.type == "event" then |
| 571 module:log("warn", "Unknown chain %q", chain); | 586 local handler = new_handler(chain_definition.pass_return); |
| 572 end | 587 for _, event_name in ipairs(chain_definition) do |
| 573 module:hook("firewall/chains/"..chain, new_handler(false)); | 588 events_hooked[event_name] = handler; |
| 574 end | 589 module:hook(event_name, handler, chain_definition.priority); |
| 575 end | 590 end |
| 591 elseif not chain:sub(1, 5) == "user/" then | |
| 592 module:log("warn", "Unknown chain %q", chain); | |
| 593 end | |
| 594 local event_name, handler = "firewall/chains/"..chain, new_handler(false); | |
| 595 events_hooked[event_name] = handler; | |
| 596 module:hook(event_name, handler); | |
| 597 end | |
| 598 end | |
| 599 loaded_scripts[script] = { last_modified = last_modified, events_hooked = events_hooked }; | |
| 600 module:log("debug", "Loaded %s", script); | |
| 601 end | |
| 602 | |
| 603 function unload_script(script, is_reload) | |
| 604 script = resolve_script_path(script); | |
| 605 local script_info = loaded_scripts[script]; | |
| 606 if not script_info then | |
| 607 return; -- Script not loaded | |
| 608 end | |
| 609 local events_hooked = script_info.events_hooked; | |
| 610 for event_name, event_handler in pairs(events_hooked) do | |
| 611 module:unhook(event_name, event_handler); | |
| 612 events_hooked[event_name] = nil; | |
| 613 end | |
| 614 loaded_scripts[script] = nil; | |
| 615 if not is_reload then | |
| 616 module:log("debug", "Unloaded %s", script); | |
| 617 end | |
| 618 end | |
| 619 | |
| 620 -- Given a set of scripts (e.g. from config) figure out which ones need to | |
| 621 -- be loaded, which are already loaded but need unloading, and which to reload | |
| 622 function load_unload_scripts(script_list) | |
| 623 local wanted_scripts = script_list / resolve_script_path; | |
| 624 local currently_loaded = set.new(it.to_array(it.keys(loaded_scripts))); | |
| 625 local scripts_to_unload = currently_loaded - wanted_scripts; | |
| 626 for script in wanted_scripts do | |
| 627 -- If the script is already loaded, this is fine - it will | |
| 628 -- reload the script for us if the file has changed | |
| 629 load_script(script); | |
| 630 end | |
| 631 for script in scripts_to_unload do | |
| 632 unload_script(script); | |
| 576 end | 633 end |
| 577 end | 634 end |
| 578 | 635 |
| 579 function module.load() | 636 function module.load() |
| 580 if not prosody.arg then return end -- Don't run in prosodyctl | 637 if not prosody.arg then return end -- Don't run in prosodyctl |
| 581 active_definitions = {}; | |
| 582 local firewall_scripts = module:get_option_set("firewall_scripts", {}); | 638 local firewall_scripts = module:get_option_set("firewall_scripts", {}); |
| 583 for script in firewall_scripts do | 639 load_unload_scripts(firewall_scripts); |
| 584 load_script(script); | |
| 585 end | |
| 586 -- Replace contents of definitions table (shared) with active definitions | 640 -- Replace contents of definitions table (shared) with active definitions |
| 587 for k in it.keys(definitions) do definitions[k] = nil; end | 641 for k in it.keys(definitions) do definitions[k] = nil; end |
| 588 for k,v in pairs(active_definitions) do definitions[k] = v; end | 642 for k,v in pairs(active_definitions) do definitions[k] = v; end |
| 589 end | 643 end |
| 644 | |
| 645 function module.save() | |
| 646 return { active_definitions = active_definitions, loaded_scripts = loaded_scripts }; | |
| 647 end | |
| 648 | |
| 649 function module.restore(state) | |
| 650 active_definitions = state.active_definitions; | |
| 651 loaded_scripts = state.loaded_scripts; | |
| 652 end | |
| 653 | |
| 654 module:hook_global("config-reloaded", function () | |
| 655 load_unload_scripts(module:get_option_set("firewall_scripts", {})); | |
| 656 end); | |
| 590 | 657 |
| 591 function module.command(arg) | 658 function module.command(arg) |
| 592 if not arg[1] or arg[1] == "--help" then | 659 if not arg[1] or arg[1] == "--help" then |
| 593 require"util.prosodyctl".show_usage([[mod_firewall <firewall.pfw>]], [[Compile files with firewall rules to Lua code]]); | 660 require"util.prosodyctl".show_usage([[mod_firewall <firewall.pfw>]], [[Compile files with firewall rules to Lua code]]); |
| 594 return 1; | 661 return 1; |
