]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Rework ratelimit plugin
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 30 Nov 2015 16:14:47 +0000 (16:14 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 30 Nov 2015 16:14:47 +0000 (16:14 +0000)
- Switch to `rates` instead of old and stupid strings to setup
- Check if a bucket is zero and disable the corresponding limits
- Turn off all buckets by default
- Check either `rcpt` or `user` buckets, not all together
- Document new `rates` and `symbol` options
- Inform user about what buckets are used in the configuration

conf/modules.d/ratelimit.conf
doc/markdown/modules/ratelimit.md
src/plugins/lua/ratelimit.lua

index c6f24370849153e01a8a89dd925a08505e032e20..317957ac48ed4b5b604d3ec142e4cd1621da9e1f 100644 (file)
@@ -1,11 +1,21 @@
 ratelimit {
     .include(try=true,priority=1) "${DBDIR}/dynamic/ratelimit.conf"
-    limit = "to:100:0.033333333";
-    limit = "to_ip:30:0.025";
-    limit = "to_ip_from:20:0.01666666667";
-    limit = "bounce_to:10:0.000555556";
-    limit = "bounce_to_ip:5:0.000277778";
-    limit = "user:20:0.01666666667";
+    rates {
+        # Limit for all mail per recipient (burst 100, rate 2 per minute)
+        to = [100, 0.033333333];
+        # Limit for all mail per one source ip (burst 30, rate 1.5 per minute)
+        to_ip = [30, 0.025];
+        # Limit for all mail per one source ip and from address (burst 20, rate 1 per minute)
+        to_ip_from = [20, 0.01666666667];
+        # Limit for all bounce mail (burst 10, rate 2 per hour)
+        bounce_to = [10, 0.000555556];
+        # Limit for bounce mail per one source ip (burst 5, rate 1 per hour)
+        bounce_to_ip = [5, 0.000277778];
+        # Limit for all mail per authenticated user (burst 20, rate 1 per minute)
+        user = [20, 0.01666666667];
+    }
+    # If symbol is specified, then it is inserted instead of setting result
+    #symbol = "R_RATELIMIT";
     whitelisted_rcpts = "postmaster,mailer-daemon";
     max_rcpt = 5;
-}
\ No newline at end of file
+}
index 209d9f42faa1bd30e67eb5177e349b5212ffe743..dc0ac3ae04702baf54f477b5d25a8ade75903fce 100644 (file)
@@ -39,26 +39,25 @@ Each bucket has two parameters:
 For example, a bucket with capacity `100` and leak `1` can accept up to 100 messages but then
 will accept not more than a message per second.
 
-By default, ratelimit module has the following settings:
+By default, ratelimit module has the following settings which disable all limits:
 
 ~~~lua
 -- Default settings for limits, 1-st member is burst, second is rate and the third is numeric type
 local settings = {
   -- Limit for all mail per recipient (burst 100, rate 2 per minute)
-  to = {[1] = 100, [2] = 0.033333333, [3] = 1},
+  to = {0, 0.033333333},
   -- Limit for all mail per one source ip (burst 30, rate 1.5 per minute)
-  to_ip = {[1] = 30, [2] = 0.025, [3] = 2},
+  to_ip = {0, 0.025},
   -- Limit for all mail per one source ip and from address (burst 20, rate 1 per minute)
-  to_ip_from = {[1] = 20, [2] = 0.01666666667, [3] = 3},
+  to_ip_from = {0, 0.01666666667},
 
   -- Limit for all bounce mail (burst 10, rate 2 per hour)
-  bounce_to = {[1] = 10, [2] = 0.000555556, [3] = 4},
+  bounce_to = {0, 0.000555556},
   -- Limit for bounce mail per one source ip (burst 5, rate 1 per hour)
-  bounce_to_ip = {[1] = 5 , [2] = 0.000277778, [3] = 5},
+  bounce_to_ip = {0, 0.000277778},
 
   -- Limit for all mail per user (authuser) (burst 20, rate 1 per minute)
-  user = {[1] = 20, [2] = 0.01666666667, [3] = 6}
-
+  user = {0, 0.01666666667}
 }
 ~~~
 
@@ -73,9 +72,9 @@ the value of this option is 'postmaster, mailer-daemon'
 - `whitelisted_ip` - a map of ip addresses or networks whitelisted
 - `max_rcpts` - do not apply ratelimit if it contains more than this value of recipients (5 by default). This
 option allows to avoid too many work for setting buckets if there are a lot of recipients in a message).
-- `limit` - allows to set limit for a specific category. This option should be in the following form:
+- `rates` - a table of allowed rates in form:
 
-    type:burst:leak
+    type = [burst,leak];
 
 Where `type` is one of:
 
@@ -86,4 +85,6 @@ Where `type` is one of:
 - `bounce_to_ip`
 
 `burst` is a capacity of a bucket and `leak` is a rate in messages per second.
-Both these attributes are floating point values.
\ No newline at end of file
+Both these attributes are floating point values.
+
+- `symbol` - if this option is specified, then `ratelimit` plugin just adds the corresponding symbol instead of setting pre-result, the value is scaled as $$ 2 * tanh(\frac{bucket}{threshold * 2}) $$, where `tanh` is the hyperbolic tanhent function
index cb7394e2fe33e25f6f60bdd3df22a2c220f47a64..2c80c01398a23a7a05b291076490cd492386dd34 100644 (file)
@@ -31,20 +31,19 @@ local default_port = 6379
 -- Default settings for limits, 1-st member is burst, second is rate and the third is numeric type
 local settings = {
   -- Limit for all mail per recipient (burst 100, rate 2 per minute)
-  to = {[1] = 100, [2] = 0.033333333, [3] = 1},
+  to = {0, 0.033333333},
   -- Limit for all mail per one source ip (burst 30, rate 1.5 per minute)
-  to_ip = {[1] = 30, [2] = 0.025, [3] = 2},
+  to_ip = {0, 0.025},
   -- Limit for all mail per one source ip and from address (burst 20, rate 1 per minute)
-  to_ip_from = {[1] = 20, [2] = 0.01666666667, [3] = 3},
+  to_ip_from = {0, 0.01666666667},
 
   -- Limit for all bounce mail (burst 10, rate 2 per hour)
-  bounce_to = {[1] = 10, [2] = 0.000555556, [3] = 4},
+  bounce_to = {0, 0.000555556},
   -- Limit for bounce mail per one source ip (burst 5, rate 1 per hour)
-  bounce_to_ip = {[1] = 5 , [2] = 0.000277778, [3] = 5},
+  bounce_to_ip = {0, 0.000277778},
 
   -- Limit for all mail per user (authuser) (burst 20, rate 1 per minute)
-  user = {[1] = 20, [2] = 0.01666666667, [3] = 6}
-
+  user = {0, 0.01666666667}
 }
 -- Senders that are considered as bounce
 local bounce_senders = {'postmaster', 'mailer-daemon', '', 'null', 'fetchmail-daemon', 'mdaemon'}
@@ -259,24 +258,32 @@ local function rate_test_set(task, func)
   end
   -- Get user (authuser)
   local auser = task:get_user()
-  if auser then
+  if auser and settings['user'] > 0 then
     table.insert(args, {settings['user'], make_rate_key (auser, '<auth>', nil)})
   end
 
   local is_bounce = check_bounce(from_user)
 
-  if rcpts then
+  if rcpts and not auser then
     _.each(function(r)
       if is_bounce then
-        table.insert(args, {settings['bounce_to'], make_rate_key ('<>', r['addr'], nil)})
-        if ip then
-          table.insert(args, {settings['bounce_to_ip'], make_rate_key ('<>', r['addr'], ip)})
+        if settings['bounce_to'][1] > 0 then
+          table.insert(args, { settings['bounce_to'], make_rate_key('<>', r['addr'], nil) })
+        end
+        if ip and settings['bounce_to_ip'][1] > 0 then
+          table.insert(args, { settings['bounce_to_ip'], make_rate_key('<>', r['addr'], ip) })
         end
       end
-      table.insert(args, {settings['to'], make_rate_key (nil, r['addr'], nil)})
+      if settings['to'][1] > 0 then
+        table.insert(args, { settings['to'], make_rate_key(nil, r['addr'], nil) })
+      end
       if ip then
-        table.insert(args, {settings['to_ip'], make_rate_key (nil, r['addr'], ip)})
-        table.insert(args, {settings['to_ip_from'], make_rate_key (from_addr, r['addr'], ip)})
+        if settings['to_ip'][1] > 0 then
+          table.insert(args, { settings['to_ip'], make_rate_key(nil, r['addr'], ip) })
+        end
+        if settings['to_ip_from'][1] > 0 then
+          table.insert(args, { settings['to_ip_from'], make_rate_key(from_addr, r['addr'], ip) })
+        end
       end
     end, rcpts)
   end
@@ -362,6 +369,22 @@ if opts then
     parse_limit(rates)
   end
 
+  if opts['rates'] and type(opts['rates']) == 'table' then
+    -- new way of setting limits
+    _.each(function(t, lim)
+      if type(lim) == 'table' and settings[t] then
+        settings[t] = lim
+      else
+        rspamd_logger.errx(rspamd_config, 'bad rate: %s: %s', t, lim)
+      end
+    end, opts['rates'])
+  end
+
+  local enabled_limits = _.totable(_.map(function(t, lim)
+    return t
+  end, _.filter(function(t, lim) return lim[1] > 0 end, settings)))
+  rspamd_logger.infox(rspamd_config, 'enabled rate buckets: %s', enabled_limits)
+
   if opts['whitelisted_rcpts'] and type(opts['whitelisted_rcpts']) == 'string' then
     whitelisted_rcpts = split(opts['whitelisted_rcpts'], ',')
   elseif type(opts['whitelisted_rcpts']) == 'table' then