Software / code / prosody-modules
Comparison
mod_firewall/mod_firewall.lua @ 999:197af8440ffb
mod_firewall: Make defining objects generic (currently zones and rate limits), so more can easily be added. Also a syntax change... definition lines must begin with %
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Tue, 07 May 2013 10:33:49 +0100 |
| parent | 998:6fdcebbd2284 |
| child | 1001:c0850793b716 |
comparison
equal
deleted
inserted
replaced
| 998:6fdcebbd2284 | 999:197af8440ffb |
|---|---|
| 2 local resolve_relative_path = require "core.configmanager".resolve_relative_path; | 2 local resolve_relative_path = require "core.configmanager".resolve_relative_path; |
| 3 local logger = require "util.logger".init; | 3 local logger = require "util.logger".init; |
| 4 local set = require "util.set"; | 4 local set = require "util.set"; |
| 5 local it = require "util.iterators"; | 5 local it = require "util.iterators"; |
| 6 local add_filter = require "util.filters".add_filter; | 6 local add_filter = require "util.filters".add_filter; |
| 7 local new_throttle = require "util.throttle".create; | 7 |
| 8 | 8 local definitions = module:shared("definitions"); |
| 9 local zones, throttles = module:shared("zones", "throttles"); | 9 local active_definitions = {}; |
| 10 local active_zones, active_throttles = {}, {}; | |
| 11 | 10 |
| 12 local chains = { | 11 local chains = { |
| 13 preroute = { | 12 preroute = { |
| 14 type = "event"; | 13 type = "event"; |
| 15 priority = 0.1; | 14 priority = 0.1; |
| 84 return table.concat(defs, " "); | 83 return table.concat(defs, " "); |
| 85 end, depends = { "date_time" }; }; | 84 end, depends = { "date_time" }; }; |
| 86 throttle = { | 85 throttle = { |
| 87 global_code = function (throttle) | 86 global_code = function (throttle) |
| 88 assert(idsafe(throttle), "Invalid rate limit name: "..throttle); | 87 assert(idsafe(throttle), "Invalid rate limit name: "..throttle); |
| 89 assert(throttles[throttle], "Unknown rate limit: "..throttle); | 88 assert(active_definitions.RATE[throttle], "Unknown rate limit: "..throttle); |
| 90 return ("local throttle_%s = throttles.%s;"):format(throttle, throttle); | 89 return ("local throttle_%s = rates.%s;"):format(throttle, throttle); |
| 91 end; | 90 end; |
| 92 }; | 91 }; |
| 93 }; | 92 }; |
| 94 | 93 |
| 95 local function include_dep(dep, code) | 94 local function include_dep(dep, code) |
| 124 end | 123 end |
| 125 end | 124 end |
| 126 code.included_deps[dep] = true; | 125 code.included_deps[dep] = true; |
| 127 end | 126 end |
| 128 | 127 |
| 128 local definition_handlers = module:require("definitions"); | |
| 129 local condition_handlers = module:require("conditions"); | 129 local condition_handlers = module:require("conditions"); |
| 130 local action_handlers = module:require("actions"); | 130 local action_handlers = module:require("actions"); |
| 131 | 131 |
| 132 local function new_rule(ruleset, chain) | 132 local function new_rule(ruleset, chain) |
| 133 assert(chain, "no chain specified"); | 133 assert(chain, "no chain specified"); |
| 181 return nil, errmsg("Unknown chain: "..chain); | 181 return nil, errmsg("Unknown chain: "..chain); |
| 182 elseif chain_info.type ~= "event" then | 182 elseif chain_info.type ~= "event" then |
| 183 return nil, errmsg("Only event chains supported at the moment"); | 183 return nil, errmsg("Only event chains supported at the moment"); |
| 184 end | 184 end |
| 185 ruleset[chain] = ruleset[chain] or {}; | 185 ruleset[chain] = ruleset[chain] or {}; |
| 186 elseif not(state) and line:match("^ZONE ") then | 186 elseif not(state) and line:match("^%%") then -- Definition (zone, limit, etc.) |
| 187 local zone_name = line:match("^ZONE ([^:]+)"); | 187 local what, name = line:match("^%%%s*(%w+) +([^ :]+)"); |
| 188 if not zone_name:match("^%a[%w_]*$") then | 188 if not definition_handlers[what] then |
| 189 return nil, errmsg("Invalid character(s) in zone name: "..zone_name); | 189 return nil, errmsg("Definition of unknown object: "..what); |
| 190 end | 190 elseif not name or not idsafe(name) then |
| 191 local zone_members = line:match("^ZONE .-: ?(.*)"); | 191 return nil, errmsg("Invalid "..what.." name"); |
| 192 local zone_member_list = {}; | 192 end |
| 193 for member in zone_members:gmatch("[^, ]+") do | 193 |
| 194 zone_member_list[#zone_member_list+1] = member; | 194 local val = line:match(": ?(.*)$"); |
| 195 end | 195 if not val and line:match(":<") then -- Read from file |
| 196 zones[zone_name] = set.new(zone_member_list)._items; | 196 local fn = line:match(":< ?(.-)%s*$"); |
| 197 table.insert(active_zones, zone_name); | 197 if not fn then |
| 198 elseif not(state) and line:match("^RATE ") then | 198 return nil, errmsg("Unable to parse filename"); |
| 199 local name = line:match("^RATE ([^:]+)"); | 199 end |
| 200 assert(idsafe(name), "Invalid rate limit name: "..name); | 200 local f, err = io.open(fn); |
| 201 local rate = assert(tonumber(line:match(":%s*([%d.]+)")), "Unable to parse rate"); | 201 if not f then return nil, errmsg(err); end |
| 202 local burst = tonumber(line:match("%(%s*burst%s+([%d.]+)%s*%)")) or 1; | 202 val = f:read("*a"):gsub("\r?\n", " "):gsub("%s+5", ""); |
| 203 throttles[name] = new_throttle(rate*burst, burst); | 203 end |
| 204 table.insert(active_throttles, name); | 204 if not val then |
| 205 return nil, errmsg("No value given for definition"); | |
| 206 end | |
| 207 | |
| 208 local ok, ret = pcall(definition_handlers[what], name, val); | |
| 209 if not ok then | |
| 210 return nil, errmsg(ret); | |
| 211 end | |
| 212 | |
| 213 if not active_definitions[what] then | |
| 214 active_definitions[what] = {}; | |
| 215 end | |
| 216 active_definitions[what][name] = ret; | |
| 205 elseif line:match("^[^%s:]+[%.=]") then | 217 elseif line:match("^[^%s:]+[%.=]") then |
| 206 -- Action | 218 -- Action |
| 207 if state == nil then | 219 if state == nil then |
| 208 -- This is a standalone action with no conditions | 220 -- This is a standalone action with no conditions |
| 209 rule = new_rule(ruleset, chain); | 221 rule = new_rule(ruleset, chain); |
| 293 .."\n end\n"; | 305 .."\n end\n"; |
| 294 end | 306 end |
| 295 table.insert(code, rule_code); | 307 table.insert(code, rule_code); |
| 296 end | 308 end |
| 297 | 309 |
| 298 local code_string = [[return function (zones, throttles, fire_event, log) | 310 for name in pairs(definition_handlers) do |
| 311 table.insert(code.global_header, 1, "local "..name:lower().."s = definitions."..name..";"); | |
| 312 end | |
| 313 | |
| 314 local code_string = [[return function (definitions, fire_event, log) | |
| 299 ]]..table.concat(code.global_header, "\n")..[[ | 315 ]]..table.concat(code.global_header, "\n")..[[ |
| 300 local db = require 'util.debug' | 316 local db = require 'util.debug' |
| 301 return function (event) | 317 return function (event) |
| 302 local stanza, session = event.stanza, event.origin; | 318 local stanza, session = event.stanza, event.origin; |
| 303 | 319 |
| 319 return nil, "Error compiling (probably a compiler bug, please report): "..err; | 335 return nil, "Error compiling (probably a compiler bug, please report): "..err; |
| 320 end | 336 end |
| 321 local function fire_event(name, data) | 337 local function fire_event(name, data) |
| 322 return module:fire_event(name, data); | 338 return module:fire_event(name, data); |
| 323 end | 339 end |
| 324 chunk = chunk()(zones, throttles, fire_event, logger(filename)); -- Returns event handler with 'zones' upvalue. | 340 chunk = chunk()(active_definitions, fire_event, logger(filename)); -- Returns event handler with 'zones' upvalue. |
| 325 return chunk; | 341 return chunk; |
| 326 end | 342 end |
| 327 | 343 |
| 328 local function cleanup(t, active_list) | |
| 329 local unused = set.new(it.to_array(it.keys(t))) - set.new(active_list); | |
| 330 for k in unused do t[k] = nil; end | |
| 331 end | |
| 332 | |
| 333 function module.load() | 344 function module.load() |
| 334 active_zones, active_throttles = {}, {}; | 345 active_definitions = {}; |
| 335 local firewall_scripts = module:get_option_set("firewall_scripts", {}); | 346 local firewall_scripts = module:get_option_set("firewall_scripts", {}); |
| 336 for script in firewall_scripts do | 347 for script in firewall_scripts do |
| 337 script = resolve_relative_path(prosody.paths.config, script); | 348 script = resolve_relative_path(prosody.paths.config, script); |
| 338 local chain_functions, err = compile_firewall_rules(script) | 349 local chain_functions, err = compile_firewall_rules(script) |
| 339 | 350 |
| 356 module:hook("firewall/chains/"..chain, handler); | 367 module:hook("firewall/chains/"..chain, handler); |
| 357 end | 368 end |
| 358 end | 369 end |
| 359 end | 370 end |
| 360 end | 371 end |
| 361 -- Remove entries from tables that are no longer in use | 372 -- Replace contents of definitions table (shared) with active definitions |
| 362 cleanup(zones, active_zones); | 373 for k in it.keys(definitions) do definitions[k] = nil; end |
| 363 cleanup(throttles, active_throttles); | 374 for k,v in pairs(active_definitions) do definitions[k] = v; end |
| 364 end | 375 end |