Mercurial > prosody-modules
comparison mod_firewall/mod_firewall.lua @ 971:53e158e44a44
mod_firewall: Add rate limiting capabilities, and keep zones and throttle objects in shared tables
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Sat, 06 Apr 2013 22:20:59 +0100 |
| parents | a88f33fe6970 |
| children | aeb11522a44f |
comparison
equal
deleted
inserted
replaced
| 970:adcb751f22f3 | 971:53e158e44a44 |
|---|---|
| 1 | 1 |
| 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 add_filter = require "util.filters".add_filter; | 6 local add_filter = require "util.filters".add_filter; |
| 6 | 7 local new_throttle = require "util.throttle".create; |
| 7 zones = {}; | 8 |
| 8 local zones = zones; | 9 local zones, throttles = module:shared("zones", "throttles"); |
| 9 setmetatable(zones, { | 10 local active_zones, active_throttles = {}, {}; |
| 10 __index = function (zones, zone) | |
| 11 local t = { [zone] = true }; | |
| 12 rawset(zones, zone, t); | |
| 13 return t; | |
| 14 end; | |
| 15 }); | |
| 16 | 11 |
| 17 local chains = { | 12 local chains = { |
| 18 preroute = { | 13 preroute = { |
| 19 type = "event"; | 14 type = "event"; |
| 20 priority = 0.1; | 15 priority = 0.1; |
| 32 deliver_remote = { | 27 deliver_remote = { |
| 33 type = "event"; "route/remote"; | 28 type = "event"; "route/remote"; |
| 34 priority = 0.1; | 29 priority = 0.1; |
| 35 }; | 30 }; |
| 36 }; | 31 }; |
| 32 | |
| 33 local function idsafe(name) | |
| 34 return not not name:match("^%a[%w_]*$") | |
| 35 end | |
| 37 | 36 |
| 38 -- Dependency locations: | 37 -- Dependency locations: |
| 39 -- <type lib> | 38 -- <type lib> |
| 40 -- <type global> | 39 -- <type global> |
| 41 -- function handler() | 40 -- function handler() |
| 71 global_code = [[local group_contains = module:depends("groups").group_contains]]; | 70 global_code = [[local group_contains = module:depends("groups").group_contains]]; |
| 72 }; | 71 }; |
| 73 is_admin = { global_code = [[local is_admin = require "core.usermanager".is_admin]]}; | 72 is_admin = { global_code = [[local is_admin = require "core.usermanager".is_admin]]}; |
| 74 core_post_stanza = { global_code = [[local core_post_stanza = prosody.core_post_stanza]] }; | 73 core_post_stanza = { global_code = [[local core_post_stanza = prosody.core_post_stanza]] }; |
| 75 zone = { global_code = function (zone) | 74 zone = { global_code = function (zone) |
| 76 assert(zone:match("^%a[%w_]*$"), "Invalid zone name: "..zone); | 75 assert(idsafe(zone), "Invalid zone name: "..zone); |
| 77 return ("local zone_%s = zones[%q] or {};"):format(zone, zone); | 76 return ("local zone_%s = zones[%q] or {};"):format(zone, zone); |
| 78 end }; | 77 end }; |
| 79 date_time = { global_code = [[local os_date = os.date]]; local_code = [[local current_date_time = os_date("*t");]] }; | 78 date_time = { global_code = [[local os_date = os.date]]; local_code = [[local current_date_time = os_date("*t");]] }; |
| 80 time = { local_code = function (what) | 79 time = { local_code = function (what) |
| 81 local defs = {}; | 80 local defs = {}; |
| 82 for field in what:gmatch("%a+") do | 81 for field in what:gmatch("%a+") do |
| 83 table.insert(defs, ("local current_%s = current_date_time.%s;"):format(field, field)); | 82 table.insert(defs, ("local current_%s = current_date_time.%s;"):format(field, field)); |
| 84 end | 83 end |
| 85 return table.concat(defs, " "); | 84 return table.concat(defs, " "); |
| 86 end, depends = { "date_time" }; }; | 85 end, depends = { "date_time" }; }; |
| 86 throttle = { | |
| 87 global_code = function (throttle) | |
| 88 assert(idsafe(throttle), "Invalid rate limit name: "..throttle); | |
| 89 assert(throttles[throttle], "Unknown rate limit: "..throttle); | |
| 90 return ("local throttle_%s = throttles.%s;"):format(throttle, throttle); | |
| 91 end; | |
| 92 }; | |
| 87 }; | 93 }; |
| 88 | 94 |
| 89 local function include_dep(dep, code) | 95 local function include_dep(dep, code) |
| 90 local dep, dep_param = dep:match("^([^:]+):?(.*)$"); | 96 local dep, dep_param = dep:match("^([^:]+):?(.*)$"); |
| 91 local dep_info = available_deps[dep]; | 97 local dep_info = available_deps[dep]; |
| 186 local zone_member_list = {}; | 192 local zone_member_list = {}; |
| 187 for member in zone_members:gmatch("[^, ]+") do | 193 for member in zone_members:gmatch("[^, ]+") do |
| 188 zone_member_list[#zone_member_list+1] = member; | 194 zone_member_list[#zone_member_list+1] = member; |
| 189 end | 195 end |
| 190 zones[zone_name] = set.new(zone_member_list)._items; | 196 zones[zone_name] = set.new(zone_member_list)._items; |
| 197 table.insert(active_zones, zone_name); | |
| 198 elseif not(state) and line:match("^RATE ") then | |
| 199 local name = line:match("^RATE ([^:]+)"); | |
| 200 assert(idsafe(name), "Invalid rate limit name: "..name); | |
| 201 local rate = assert(tonumber(line:match(":%s*([%d.]+)")), "Unable to parse rate"); | |
| 202 local burst = tonumber(line:match("%(%s*burst%s+([%d.]+)%s*%)")) or 1; | |
| 203 throttles[name] = new_throttle(rate*burst, burst); | |
| 204 table.insert(active_throttles, name); | |
| 191 elseif line:match("^[^%s:]+[%.=]") then | 205 elseif line:match("^[^%s:]+[%.=]") then |
| 192 -- Action | 206 -- Action |
| 193 if state == nil then | 207 if state == nil then |
| 194 -- This is a standalone action with no conditions | 208 -- This is a standalone action with no conditions |
| 195 rule = new_rule(ruleset, chain); | 209 rule = new_rule(ruleset, chain); |
| 263 .."\n end\n"; | 277 .."\n end\n"; |
| 264 end | 278 end |
| 265 table.insert(code, rule_code); | 279 table.insert(code, rule_code); |
| 266 end | 280 end |
| 267 | 281 |
| 268 local code_string = [[return function (zones, fire_event, log) | 282 local code_string = [[return function (zones, throttles, fire_event, log) |
| 269 ]]..table.concat(code.global_header, "\n")..[[ | 283 ]]..table.concat(code.global_header, "\n")..[[ |
| 270 local db = require 'util.debug' | 284 local db = require 'util.debug' |
| 271 return function (event) | 285 return function (event) |
| 272 local stanza, session = event.stanza, event.origin; | 286 local stanza, session = event.stanza, event.origin; |
| 273 | 287 |
| 289 return nil, "Error compiling (probably a compiler bug, please report): "..err; | 303 return nil, "Error compiling (probably a compiler bug, please report): "..err; |
| 290 end | 304 end |
| 291 local function fire_event(name, data) | 305 local function fire_event(name, data) |
| 292 return module:fire_event(name, data); | 306 return module:fire_event(name, data); |
| 293 end | 307 end |
| 294 chunk = chunk()(zones, fire_event, logger(filename)); -- Returns event handler with 'zones' upvalue. | 308 chunk = chunk()(zones, throttles, fire_event, logger(filename)); -- Returns event handler with 'zones' upvalue. |
| 295 return chunk; | 309 return chunk; |
| 296 end | 310 end |
| 297 | 311 |
| 312 local function cleanup(t, active_list) | |
| 313 local unused = set.new(it.to_array(it.keys(t))) - set.new(active_list); | |
| 314 for k in unused do t[k] = nil; end | |
| 315 end | |
| 316 | |
| 298 function module.load() | 317 function module.load() |
| 318 active_zones, active_throttles = {}, {}; | |
| 299 local firewall_scripts = module:get_option_set("firewall_scripts", {}); | 319 local firewall_scripts = module:get_option_set("firewall_scripts", {}); |
| 300 for script in firewall_scripts do | 320 for script in firewall_scripts do |
| 301 script = resolve_relative_path(prosody.paths.config, script); | 321 script = resolve_relative_path(prosody.paths.config, script); |
| 302 local chain_functions, err = compile_firewall_rules(script) | 322 local chain_functions, err = compile_firewall_rules(script) |
| 303 | 323 |
| 320 module:hook("firewall/chains/"..chain, handler); | 340 module:hook("firewall/chains/"..chain, handler); |
| 321 end | 341 end |
| 322 end | 342 end |
| 323 end | 343 end |
| 324 end | 344 end |
| 325 end | 345 -- Remove entries from tables that are no longer in use |
| 346 cleanup(zones, active_zones); | |
| 347 cleanup(throttles, active_throttles); | |
| 348 end |
