Here's a suggested patch to Gordon Messmer's ratelimit module for
pythonfilter.
It's a common practice these days among spammers to cycle through a
number of originating IP addresses when sending out a spam spew, which
tends to defeat per-IP rate limiting. Although there may be exceptions,
I've noted that these IP addresses are almost always in the same /24
address group.
This patch isolates the first three octets of the originating IP address
from the sender information returned by courier.control.getSendersMta()
and stores these in the connection history data structure. If block
awareness is enabled in the module, the connection count is based on a
match of these three-octet identifiers rather than the entire sender
information string.
One new configuration variable is introduced. use_blocks is a boolean
which determines whether ratelimit.py detects address group matches or
simply sender matches as is the case now.
I will welcome any corrections, improvements or comments on this code.
I'm aware that the three octet data stored in the connection history
structure doesn't constitute normalized related data, but my
understanding is that it's less expensive of processing time to store
this information once and recall it than to compute it from the stored
sender string with each incoming email.
I've tested this to determine that it works as expected with email from
a single IP address with use_blocks set both False and True. I haven't
tested it sending emails from a multi-homed platform, but if it works
from a single IP with use_blocks=True then it should work as expected to
rate limit email from a /24 address group.
--- ratelimit.py 2015-03-13 10:08:23.000000000 -0500
+++ ratelimit.py.new 2015-03-13 10:09:50.000000000 -0500
@@ -23,11 +23,13 @@
import thread
import time
import courier.control
+import re
# The rate is measured in messages / interval in minutes
maxConnections = 60
interval = 1
+use_blocks = True
# The senders lists will be scrubbed at the interval indicated in
# seconds. All records older than the "interval" number of minutes
@@ -43,6 +45,10 @@
global _sendersLock
global _senders
global _sendersLastPurged
+ global _pat
+ global _igroup
+ _pat = re.compile(".*?\[.*?(\d{1,3}\.\d{1,3}\.\d{1,3})\.\d{1,3}\].*$")
+ _igroup = lambda a: _pat.match(a).group(1)
_sendersLock = thread.allocate_lock()
_senders = {}
_sendersLastPurged = 0
@@ -78,14 +84,24 @@
if not _senders.has_key(now):
_senders[now] = {}
if not _senders[now].has_key(sender):
- _senders[now][sender] = 1
+ _senders[now][sender] = []
+ _senders[now][sender].append(1)
+ _senders[now][sender].append(_igroup(sender))
else:
- _senders[now][sender] = _senders[now][sender] + 1
+ _senders[now][sender][0] += 1
+
# Now count the number of connections from this sender
connections = 0
for i in range(0, interval):
- if _senders.has_key(now - i) and _senders[now - i].has_key(sender):
- connections = connections + _senders[now - i][sender]
+ if _senders.has_key(now - i):
+ if use_blocks:
+ for j in _senders[now - i]:
+ if _igroup(sender) == _senders[now - i][j][1]:
+ connections += _senders[now - i][j][0]
+ else:
+ if _senders[now - i].has_key(sender):
+ connections += _senders[now - i][sender][0]
+
# If the connection count is higher than the maxConnections setting,
# return a soft failure.
if connections > maxConnections:
--
Lindsay Haisley | "UNIX is user-friendly, it just
FMP Computer Services | chooses its friends."
512-259-1190 | -- Andreas Bogk
http://www.fmp.com |
------------------------------------------------------------------------------
Dive into the World of Parallel Programming The Go Parallel Website, sponsored
by Intel and developed in partnership with Slashdot Media, is your hub for all
things parallel software development, from weekly thought leadership blogs to
news, videos, case studies, tutorials and more. Take a look and join the
conversation now. http://goparallel.sourceforge.net/
_______________________________________________
courier-users mailing list
[email protected]
Unsubscribe: https://lists.sourceforge.net/lists/listinfo/courier-users