changeset 2370:5fe483b73fd2

mod_firewall: Rate limiting: Document 'entries' and add option to allow overflowing when full
author Matthew Wild <>
date Tue, 15 Nov 2016 21:55:12 +0000
parents 2fb11d34087e
children b41957bf4fd3
files mod_firewall/README.markdown mod_firewall/definitions.lib.lua
diffstat 2 files changed, 14 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/mod_firewall/README.markdown	Tue Nov 15 21:02:46 2016 +0000
+++ b/mod_firewall/README.markdown	Tue Nov 15 21:55:12 2016 +0000
@@ -302,6 +302,18 @@
 For more information on expressions, see the section later in this document.
+Each value of 'EXPRESSION' has to be tracked individually in a table, which uses a small amount of memory. To prevent
+memory exhaustion, the number of tracked values is limited to 1000 by default. You can override this by setting the
+maximum number of table entries when you define the rate:
+    %RATE normal: 2 (burst 3) (entries 4096)
+Old values are automatically removed from the tracking table. However if the tracking table becomes full, new entries
+will be rejected - it will behave as if the rate limit was reached, even for values that have not been seen before. Since
+this opens up a potential denial of service (innocent users may be affected if malicious users can fill up the tracking
+table within the limit period). You can choose to instead "fail open", and allow the rate limit to be temporarily bypassed
+when the table is full. To choose this behaviour, add `(allow overflow)` to the RATE definition.
 ### Session marking
 It is possible to 'mark' sessions (see the MARK_ORIGIN action below). To match stanzas from marked sessions, use the
--- a/mod_firewall/definitions.lib.lua	Tue Nov 15 21:02:46 2016 +0000
+++ b/mod_firewall/definitions.lib.lua	Tue Nov 15 21:55:12 2016 +0000
@@ -31,14 +31,14 @@
 			local rate = assert(tonumber(line:match("([%d.]+)")), "Unable to parse rate");
 			local burst = tonumber(line:match("%(%s*burst%s+([%d.]+)%s*%)")) or 1;
 			local max_throttles = tonumber(line:match("%(%s*entries%s+([%d]+)%s*%)")) or multirate_cache_size;
+			local deny_when_full = not line:match("%(allow overflow%)");
 			return {
 				single = function ()
 					return new_throttle(rate*burst, burst);
 				multi = function ()
-					local cache = require "util.cache".new(max_throttles, evict_only_unthrottled);
+					local cache = require "util.cache".new(max_throttles, deny_when_full and evict_only_unthrottled or nil);
 					return {
 						poll_on = function (_, key, amount)
 							assert(key, "no key");