From bdc2d07946d2b10ccf4634db6117e798ab079555 Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 2 May 2016 19:00:06 +0200 Subject: [PATCH 01/61] fix suhosin_log in common paths - log files should be separated using "\n": prevents to throw an error "File option must be 'head' or 'tail'", if jail suhosin will be enabled. --- config/paths-common.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/paths-common.conf b/config/paths-common.conf index e2f08325..9072136c 100644 --- a/config/paths-common.conf +++ b/config/paths-common.conf @@ -40,7 +40,8 @@ lighttpd_error_log = /var/log/lighttpd/error.log # http://www.hardened-php.net/suhosin/configuration.html#suhosin.log.syslog.facility # syslog_user is the default. Lighttpd also hooks errors into its log. -suhosin_log = %(syslog_user)s %(lighttpd_error_log)s +suhosin_log = %(syslog_user)s + %(lighttpd_error_log)s # defaults to ftp or local2 if ftp doesn't exist proftpd_log = %(syslog_ftp)s From 0c2eeee8c7fc4b3a26083ef2f04ebde0c96560c6 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 12 May 2016 12:53:42 +0200 Subject: [PATCH 02/61] BF: fail2ban-client can't unserialize IPAddr objects - added IPAddr pickle-handler, that simple wrap IPAddr to the str --- fail2ban/server/ipdns.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py index bca03428..880e3f30 100644 --- a/fail2ban/server/ipdns.py +++ b/fail2ban/server/ipdns.py @@ -233,6 +233,14 @@ class IPAddr(object): def __str__(self): return self.ntoa + + def __reduce__(self): + """IPAddr pickle-handler, that simple wrap IPAddr to the str + + Returns a string as instance to be pickled, because fail2ban-client can't + unserialize IPAddr objects + """ + return (str, (self.ntoa,)) @property def addr(self): From 4b5b16cd9fc5baa8b43415de072d7ba74377943e Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 12 May 2016 15:33:15 +0200 Subject: [PATCH 03/61] allow using of IPv6 address style mask (analog to the IPv4), for example: `2606:28ff::/ffff:ff80::` -> `2606:2880::/25` fast calculating of maskplen using map table MAP_ADDR2MASKPLEN, with pre-calculated addr->maskplen values; test cases extended; --- fail2ban/server/ipdns.py | 28 ++++++++++++++++++++-------- fail2ban/tests/filtertestcase.py | 13 +++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py index 880e3f30..8109c21a 100644 --- a/fail2ban/server/ipdns.py +++ b/fail2ban/server/ipdns.py @@ -171,7 +171,7 @@ class IPAddr(object): # IP address without CIDR mask if len(s) > 2: raise ValueError("invalid ipstr %r, too many plen representation" % (ipstr,)) - if "." in s[1]: # 255.255.255.0 style mask + if "." in s[1] or ":" in s[1]: # 255.255.255.0 resp. ffff:: style mask s[1] = IPAddr.masktoplen(s[1]) s[1] = long(s[1]) return s @@ -235,7 +235,7 @@ class IPAddr(object): return self.ntoa def __reduce__(self): - """IPAddr pickle-handler, that simple wrap IPAddr to the str + """IPAddr pickle-handler, that simply wraps IPAddr to the str Returns a string as instance to be pickled, because fail2ban-client can't unserialize IPAddr objects @@ -392,17 +392,29 @@ class IPAddr(object): return (self.addr & mask) == net.addr + # Pre-calculated map: addr to maskplen + def __getMaskMap(): + m6 = (1 << 128)-1 + m4 = (1 << 32)-1 + mmap = {m6: 128, m4: 32, 0: 0} + m = 0 + for i in xrange(0, 128): + m |= 1 << i + if i < 32: + mmap[m ^ m4] = 32-1-i + mmap[m ^ m6] = 128-1-i + return mmap + + MAP_ADDR2MASKPLEN = __getMaskMap() + @property def maskplen(self): mplen = 0 if self._maskplen is not None: return self._maskplen - maddr = self._addr - while maddr: - if not (maddr & 0x80000000): - raise ValueError("invalid mask %r, no plen representation" % (str(self),)) - maddr = (maddr << 1) & 0xFFFFFFFFL - mplen += 1 + mplen = IPAddr.MAP_ADDR2MASKPLEN.get(self._addr) + if mplen is None: + raise ValueError("invalid mask %r, no plen representation" % (str(self),)) self._maskplen = mplen return mplen diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index 5e493f83..1d4db3a2 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -1452,11 +1452,24 @@ class DNSUtilsNetworkTests(unittest.TestCase): self.assertEqual(IPAddr('93.184.0.1', 24).ntoa, '93.184.0.0/24') self.assertEqual(IPAddr('192.168.1.0/255.255.255.128').ntoa, '192.168.1.0/25') + self.assertEqual(IPAddr('93.184.0.1/32').ntoa, '93.184.0.1') + self.assertEqual(IPAddr('93.184.0.1/255.255.255.255').ntoa, '93.184.0.1') + self.assertEqual(str(IPAddr('2606:2800:220:1:248:1893:25c8::', 120)), '2606:2800:220:1:248:1893:25c8:0/120') self.assertEqual(IPAddr('2606:2800:220:1:248:1893:25c8::', 120).ntoa, '2606:2800:220:1:248:1893:25c8:0/120') self.assertEqual(str(IPAddr('2606:2800:220:1:248:1893:25c8:0/120')), '2606:2800:220:1:248:1893:25c8:0/120') self.assertEqual(IPAddr('2606:2800:220:1:248:1893:25c8:0/120').ntoa, '2606:2800:220:1:248:1893:25c8:0/120') + self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::', 25)), '2606:2880::/25') + self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/ffff:ff80::')), '2606:2880::/25') + self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/ffff:ffff:ffff:ffff:ffff:ffff:ffff::')), + '2606:28ff:220:1:248:1893:25c8:0/112') + + self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/128')), + '2606:28ff:220:1:248:1893:25c8:0') + self.assertEqual(str(IPAddr('2606:28ff:220:1:248:1893:25c8::/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')), + '2606:28ff:220:1:248:1893:25c8:0') + def testIPAddr_CIDR_Repr(self): self.assertEqual(["127.0.0.0/8", "::/32", "2001:db8::/32"], [IPAddr("127.0.0.0", 8), IPAddr("::1", 32), IPAddr("2001:db8::", 32)] From ec6032d934f72fc2a373a630e44499505b561519 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 12 May 2016 17:16:09 +0200 Subject: [PATCH 04/61] prevent to fail stock configs test case, if any jail custom config does not have own test log-file (perhaps not clean copy) --- fail2ban/tests/servertestcase.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 90f7aaf2..b79a5652 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1021,7 +1021,11 @@ class ServerConfigReaderTests(LogCaptureTestCase): cmd[2] = 'polling' # change log path to test log of jail (to prevent "Permission denied" on /var/logs/ for test-user): elif len(cmd) > 3 and cmd[0] == 'set' and cmd[2] == 'addlogpath': - cmd[3] = os.path.join(TEST_FILES_DIR, 'logs', cmd[1]) + fn = os.path.join(TEST_FILES_DIR, 'logs', cmd[1]) + # fallback to testcase01 if jail has not an own test log-file (currently should be no matter): + if not os.path.exists(fn): # pragma: no cover + fn = os.path.join(TEST_FILES_DIR, 'testcase01.log') + cmd[3] = fn # if fast add dummy regex to prevent too long compile of all regexp (we don't use it in this test at all): elif unittest.F2B.fast and ( len(cmd) > 3 and cmd[0] in ('set', 'multi-set') and cmd[2] == 'addfailregex' From 14c31d8c58ac1b5beb3974a8b3ecd82ab19d7a08 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 12 May 2016 18:52:20 -0400 Subject: [PATCH 05/61] DOC: minor PEP8ing and comments enhancements --- fail2ban/tests/clientreadertestcase.py | 2 +- fail2ban/tests/servertestcase.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/fail2ban/tests/clientreadertestcase.py b/fail2ban/tests/clientreadertestcase.py index 0edbc69e..5e66e25e 100644 --- a/fail2ban/tests/clientreadertestcase.py +++ b/fail2ban/tests/clientreadertestcase.py @@ -45,7 +45,7 @@ TEST_FILES_DIR_SHARE_CFG = {} from .utils import CONFIG_DIR CONFIG_DIR_SHARE_CFG = unittest.F2B.share_config -STOCK = os.path.exists(os.path.join('config','fail2ban.conf')) +STOCK = os.path.exists(os.path.join('config', 'fail2ban.conf')) IMPERFECT_CONFIG = os.path.join(os.path.dirname(__file__), 'config') IMPERFECT_CONFIG_SHARE_CFG = {} diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index b79a5652..214520a6 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -844,7 +844,7 @@ class TransmitterLogging(TransmitterBase): outCode=1, outValue=Exception('Failed to change log target'), repr_=True # Exceptions are not comparable apparently - ) + ) }[platform.system() in ('Linux',) and os.path.exists('/dev/log')] ) @@ -1001,7 +1001,8 @@ class ServerConfigReaderTests(LogCaptureTestCase): if STOCK: def testCheckStockJailActions(self): - jails = JailsReader(basedir=CONFIG_DIR, force_enable=True, share_config=self.__share_cfg) # we are running tests from root project dir atm + # we are running tests from root project dir atm + jails = JailsReader(basedir=CONFIG_DIR, force_enable=True, share_config=self.__share_cfg) self.assertTrue(jails.read()) # opens fine self.assertTrue(jails.getOptions()) # reads fine stream = jails.convert(allow_no_files=True) @@ -1019,14 +1020,17 @@ class ServerConfigReaderTests(LogCaptureTestCase): # change to the fast init backend: if cmd[0] == 'add': cmd[2] = 'polling' - # change log path to test log of jail (to prevent "Permission denied" on /var/logs/ for test-user): + # change log path to test log of the jail + # (to prevent "Permission denied" on /var/logs/ for test-user): elif len(cmd) > 3 and cmd[0] == 'set' and cmd[2] == 'addlogpath': fn = os.path.join(TEST_FILES_DIR, 'logs', cmd[1]) - # fallback to testcase01 if jail has not an own test log-file (currently should be no matter): - if not os.path.exists(fn): # pragma: no cover + # fallback to testcase01 if jail has no its own test log-file + # (should not matter really): + if not os.path.exists(fn): # pragma: no cover fn = os.path.join(TEST_FILES_DIR, 'testcase01.log') cmd[3] = fn - # if fast add dummy regex to prevent too long compile of all regexp (we don't use it in this test at all): + # if fast add dummy regex to prevent too long compile of all regexp + # (we don't use it in this test at all): elif unittest.F2B.fast and ( len(cmd) > 3 and cmd[0] in ('set', 'multi-set') and cmd[2] == 'addfailregex' ): @@ -1035,7 +1039,7 @@ class ServerConfigReaderTests(LogCaptureTestCase): # command to server, use cmdHandler direct instead of `transm.proceed(cmd)`: try: cmdHandler(cmd) - except Exception, e: # pragma: no cover + except Exception, e: # pragma: no cover self.fail("Command %r has failed. Received %r" % (cmd, e)) # jails = server._Server__jails @@ -1318,7 +1322,7 @@ class ServerConfigReaderTests(LogCaptureTestCase): 'ip4': (), 'ip6': (), 'start': ( '`echo "table persist counters" | pfctl -f-`', - '`echo "block proto tcp from to any port any" | pfctl -f-`', + '`echo "block proto tcp from to any port any" | pfctl -f-`', ), 'stop': ( '`pfctl -sr 2>/dev/null | grep -v f2b-j-w-pf | pfctl -f-`', From 3d3735706b71da567de00b0158beba8915da4c94 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 12 May 2016 20:21:42 +0200 Subject: [PATCH 06/61] invalid recursion check in substituteRecursiveTags: for example action `bsd-ipfw` produced ValueError('properties contain self referencing definitions and cannot be resolved...') test cases extended for exactly this case and for all stock actions; closes gh-1417 --- fail2ban/server/action.py | 14 +++++--- fail2ban/tests/actiontestcase.py | 7 ++++ fail2ban/tests/servertestcase.py | 57 +++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index cdd4e28c..ba3d4633 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -366,17 +366,22 @@ class CommandAction(ActionBase): value = str(tags[tag]) # search and replace all tags within value, that can be interpolated using other tags: m = t.search(value) - done = [] + done = {} + last_found = tag #logSys.log(5, 'TAG: %s, value: %s' % (tag, value)) while m: found_tag = m.group(1) #logSys.log(5, 'found: %s' % found_tag) - if found_tag == tag or found_tag in done: + curdone = done.get(last_found) + if curdone is None: + done[last_found] = curdone = [] + if found_tag == tag or found_tag in curdone: # recursive definitions are bad #logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) ) raise ValueError( "properties contain self referencing definitions " - "and cannot be resolved, fail tag: %s value: %s" % (tag, value)) + "and cannot be resolved, fail tag: %s, found: %s in %s, value: %s" % + (tag, found_tag, curdone, value)) repl = None if found_tag not in cls._escapedTags: repl = tags.get(found_tag + '?' + conditional) @@ -390,7 +395,8 @@ class CommandAction(ActionBase): continue value = value.replace('<%s>' % found_tag, repl) #logSys.log(5, 'value now: %s' % value) - done.append(found_tag) + curdone.append(found_tag) + last_found = found_tag m = t.search(value, m.start()) #logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value)) # was substituted? diff --git a/fail2ban/tests/actiontestcase.py b/fail2ban/tests/actiontestcase.py index f3778022..00a4d6be 100644 --- a/fail2ban/tests/actiontestcase.py +++ b/fail2ban/tests/actiontestcase.py @@ -30,6 +30,7 @@ import time import unittest from ..server.action import CommandAction, CallingMap +from ..server.actions import OrderedDict from ..server.utils import Utils from .utils import LogCaptureTestCase @@ -65,6 +66,12 @@ class CommandActionTest(LogCaptureTestCase): lambda: CommandAction.substituteRecursiveTags({'A': 'to= fromip=', 'C': '', 'B': '', 'D': ''})) self.assertRaises(ValueError, lambda: CommandAction.substituteRecursiveTags({'failregex': 'to= fromip=', 'sweet': '', 'honeypot': '', 'ignoreregex': ''})) + # No-recursion, just multiple replacement of tag , should be successful + if OrderedDict: # we need here an ordered, because the sequence of iteration is very important for this test + self.assertEqual(CommandAction.substituteRecursiveTags( + OrderedDict((('X', 'x=x'), ('T', '1'), ('Z', ' '), ('Y', 'y=y'))) + ), {'X': 'x=x1', 'T': '1', 'Y': 'y=y1', 'Z': 'x=x1 1 y=y1'} + ) # missing tags are ok self.assertEqual(CommandAction.substituteRecursiveTags({'A': ''}), {'A': ''}) self.assertEqual(CommandAction.substituteRecursiveTags({'A': ' ','X':'fun'}), {'A': ' fun', 'X':'fun'}) diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 214520a6..cc14e97f 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -998,6 +998,38 @@ class ServerConfigReaderTests(LogCaptureTestCase): self.assertTrue(IPAddr('192.0.2.1').isIPv4) self.assertTrue(IPAddr('2001:DB8::').isIPv6) + def _testExecActions(self, server): + jails = server._Server__jails + for jail in jails: + # print(jail, jails[jail]) + for a in jails[jail].actions: + action = jails[jail].actions[a] + logSys.debug('# ' + ('=' * 50)) + logSys.debug('# == %-44s ==', jail + ' - ' + action._name) + logSys.debug('# ' + ('=' * 50)) + # we can currently test only command actions: + if not isinstance(action, _actions.CommandAction): continue + # wrap default command processor, just log if (heavy)debug: + action.executeCmd = self._executeCmd + # test start : + logSys.debug('# === start ==='); self.pruneLog() + action.start() + # test ban ip4 : + logSys.debug('# === ban-ipv4 ==='); self.pruneLog() + action.ban({'ip': IPAddr('192.0.2.1')}) + # test unban ip4 : + logSys.debug('# === unban ipv4 ==='); self.pruneLog() + action.unban({'ip': IPAddr('192.0.2.1')}) + # test ban ip6 : + logSys.debug('# === ban ipv6 ==='); self.pruneLog() + action.ban({'ip': IPAddr('2001:DB8::')}) + # test unban ip6 : + logSys.debug('# === unban ipv6 ==='); self.pruneLog() + action.unban({'ip': IPAddr('2001:DB8::')}) + # test stop : + logSys.debug('# === stop ==='); self.pruneLog() + action.stop() + if STOCK: def testCheckStockJailActions(self): @@ -1046,6 +1078,10 @@ class ServerConfigReaderTests(LogCaptureTestCase): # for j in jails: # print(j, jails[j]) + # test default stock actions sepecified in all stock jails: + if not unittest.F2B.fast: + self._testExecActions(server) + def getDefaultJailStream(self, jail, act): act = act.replace('%(__name__)s', jail) actName, actOpt = JailReader.extractOptions(act) @@ -1061,6 +1097,25 @@ class ServerConfigReaderTests(LogCaptureTestCase): stream.extend(action.convert()) return stream + def testCheckStockAllActions(self): + unittest.F2B.SkipIfFast() + import glob + + server = TestServer() + transm = server._Server__transm + + for actCfg in glob.glob(os.path.join(CONFIG_DIR, 'action.d', '*.conf')): + act = os.path.basename(actCfg).replace('.conf', '') + # transmit artifical jail with each action to the server: + stream = self.getDefaultJailStream('j-'+act, act) + for cmd in stream: + # command to server: + ret, res = transm.proceed(cmd) + self.assertEqual(ret, 0) + # test executing action commands: + self._testExecActions(server) + + def testCheckStockCommandActions(self): # test cases to check valid ipv4/ipv6 action definition, tuple with (('jail', 'action[params]', 'tests', ...) # where tests is a dictionary contains: @@ -1347,7 +1402,7 @@ class ServerConfigReaderTests(LogCaptureTestCase): # for cmd in stream: # print(cmd) - # filter all start commands (we want not start all jails): + # transmit jail to the server: for cmd in stream: # command to server: ret, res = transm.proceed(cmd) From a4b8f6e49e81340f3fdc31d2dd92935ea22160f5 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 12 May 2016 20:21:42 +0200 Subject: [PATCH 07/61] [part. cherry-picked from 0.10] invalid recursion check in substituteRecursiveTags: for example action `bsd-ipfw` produced ValueError('properties contain self referencing definitions and cannot be resolved...') test cases extended for exactly this case; closes gh-1417 --- fail2ban/server/action.py | 11 ++++++++--- fail2ban/tests/actiontestcase.py | 7 +++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index de0c8efc..b1af659e 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -400,12 +400,16 @@ class CommandAction(ActionBase): value = str(tags[tag]) # search and replace all tags within value, that can be interpolated using other tags: m = t.search(value) - done = [] + done = {} + last_found = tag #logSys.log(5, 'TAG: %s, value: %s' % (tag, value)) while m: found_tag = m.group(1) #logSys.log(5, 'found: %s' % found_tag) - if found_tag == tag or found_tag in done: + curdone = done.get(last_found) + if curdone is None: + done[last_found] = curdone = [] + if found_tag == tag or found_tag in curdone: # recursive definitions are bad #logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) ) return False @@ -417,7 +421,8 @@ class CommandAction(ActionBase): continue value = value.replace('<%s>' % found_tag , tags[found_tag]) #logSys.log(5, 'value now: %s' % value) - done.append(found_tag) + curdone.append(found_tag) + last_found = found_tag m = t.search(value, m.start()) #logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value)) # was substituted? diff --git a/fail2ban/tests/actiontestcase.py b/fail2ban/tests/actiontestcase.py index 289d8896..e20a6341 100644 --- a/fail2ban/tests/actiontestcase.py +++ b/fail2ban/tests/actiontestcase.py @@ -29,6 +29,7 @@ import time import tempfile from ..server.action import CommandAction, CallingMap +from ..server.actions import OrderedDict from .utils import LogCaptureTestCase from .utils import pid_exists @@ -58,6 +59,12 @@ class CommandActionTest(LogCaptureTestCase): # Unresolveable substition self.assertFalse(CommandAction.substituteRecursiveTags({'A': 'to= fromip=', 'C': '', 'B': '', 'D': ''})) self.assertFalse(CommandAction.substituteRecursiveTags({'failregex': 'to= fromip=', 'sweet': '', 'honeypot': '', 'ignoreregex': ''})) + # No-recursion, just multiple replacement of tag , should be successful + if OrderedDict: # we need here an ordered, because the sequence of iteration is very important for this test + self.assertEqual(CommandAction.substituteRecursiveTags( + OrderedDict((('X', 'x=x'), ('T', '1'), ('Z', ' '), ('Y', 'y=y'))) + ), {'X': 'x=x1', 'T': '1', 'Y': 'y=y1', 'Z': 'x=x1 1 y=y1'} + ) # missing tags are ok self.assertEqual(CommandAction.substituteRecursiveTags({'A': ''}), {'A': ''}) self.assertEqual(CommandAction.substituteRecursiveTags({'A': ' ','X':'fun'}), {'A': ' fun', 'X':'fun'}) From cce63926ce9bb7b96fa1820e7893af67e0ac021a Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 13 May 2016 14:37:48 +0200 Subject: [PATCH 08/61] ChangeLog entry added --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 033cd9ec..9ac9a752 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,9 @@ ver. 0.9.5 (2016/XX/XXX) - wanna-be-released - failregex of previous monit version merged as single expression. * filter.d/postfix.conf, filter.d/postfix-sasl.conf - extended failregex daemon part, matching also `postfix/smtps/smtpd` now (gh-1391) + * fixed a grave bug within tags substitutions because of incorrect detection of recursion + in case of multiple inline substitutions of the same tag (affected actions: `bsd-ipfw`, etc). + Now tracks the actual list of the already substituted tags (per tag instead of single list) - New Features: * New Actions: From 3e49522b7ab75833e412ec38d05e5edc9645993b Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 13 May 2016 20:07:19 +0200 Subject: [PATCH 09/61] fixes unexpected extra regex-space in generic `__prefix_line` (gh-1405, misleadingly committed in d2a953756802bd7cf63f5f5f792371f52f5cba8c); all optional spaces normalized in generic include `common.conf` + test cases are extended (using new example pseudo-filter and test log `zzz-generic-example`); --- ChangeLog | 3 ++ config/filter.d/common.conf | 8 +++--- config/filter.d/zzz-generic-example.conf | 17 +++++++++++ fail2ban/tests/clientreadertestcase.py | 7 +++-- fail2ban/tests/files/logs/zzz-generic-example | 28 +++++++++++++++++++ 5 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 config/filter.d/zzz-generic-example.conf create mode 100644 fail2ban/tests/files/logs/zzz-generic-example diff --git a/ChangeLog b/ChangeLog index 9ac9a752..44ba9d11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,9 @@ ver. 0.9.5 (2016/XX/XXX) - wanna-be-released * fixed a grave bug within tags substitutions because of incorrect detection of recursion in case of multiple inline substitutions of the same tag (affected actions: `bsd-ipfw`, etc). Now tracks the actual list of the already substituted tags (per tag instead of single list) + * filter.d/common.conf + - unexpected extra regex-space in generic `__prefix_line` (gh-1405) + - all optional spaces normalized in `common.conf`, test covered now - New Features: * New Actions: diff --git a/config/filter.d/common.conf b/config/filter.d/common.conf index 3e35f1d8..115fa96c 100644 --- a/config/filter.d/common.conf +++ b/config/filter.d/common.conf @@ -26,11 +26,11 @@ __daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:? # extra daemon info # EXAMPLE: [ID 800047 auth.info] -__daemon_extra_re = (?:\[ID \d+ \S+\]) +__daemon_extra_re = \[ID \d+ \S+\] # Combinations of daemon name and PID # EXAMPLES: sshd[31607], pop(pam_unix)[4920] -__daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:?) +__daemon_combs_re = %(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:? # Some messages have a kernel prefix with a timestamp # EXAMPLES: kernel: [769570.846956] @@ -44,14 +44,14 @@ __md5hex = (?:[\da-f]{2}:){15}[\da-f]{2} # bsdverbose is where syslogd is started with -v or -vv and results in <4.3> or # appearing before the host as per testcases/files/logs/bsd/*. -__bsd_syslog_verbose = (<[^.]+\.[^.]+>) +__bsd_syslog_verbose = <[^.]+\.[^.]+> # Common line prefixes (beginnings) which could be used in filters # # [bsdverbose]? [hostname] [vserver tag] daemon_id spaces # # This can be optional (for instance if we match named native log files) -__prefix_line = \s*%(__bsd_syslog_verbose)s?\s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s%(__daemon_extra_re)s?\s* +__prefix_line = \s*(?:(?:%(__bsd_syslog_verbose)s)\s*)?(?:(?:%(__hostname)s)\s*)?(?:(?:%(__kernel_prefix)s)\s*)?(?:(?:@vserver_\S+)\s*)?(?:(?:%(__daemon_combs_re)s)\s*)?(?:(?:%(__daemon_extra_re)s)\s*)? # PAM authentication mechanism check for failures, e.g.: pam_unix, pam_sss, # pam_ldap diff --git a/config/filter.d/zzz-generic-example.conf b/config/filter.d/zzz-generic-example.conf new file mode 100644 index 00000000..421c117d --- /dev/null +++ b/config/filter.d/zzz-generic-example.conf @@ -0,0 +1,17 @@ +# Fail2Ban generic example resp. test filter +# +# Author: Serg G. Brester (sebres) +# + +[INCLUDES] + +# Read common prefixes. If any customizations available -- read them from +# common.local +before = common.conf + +[Definition] + +_daemon = test-demo + +failregex = ^%(__prefix_line)sF2B: failure from $ +ignoreregex = diff --git a/fail2ban/tests/clientreadertestcase.py b/fail2ban/tests/clientreadertestcase.py index 0a3734e5..b5c31c38 100644 --- a/fail2ban/tests/clientreadertestcase.py +++ b/fail2ban/tests/clientreadertestcase.py @@ -600,9 +600,10 @@ class JailsReaderTest(LogCaptureTestCase): self.assertTrue(jails.read()) # opens fine self.assertTrue(jails.getOptions()) # reads fine # grab all filter names - filters = set(os.path.splitext(os.path.split(a)[1])[0] - for a in glob.glob(os.path.join('config', 'filter.d', '*.conf')) - if not a.endswith('common.conf')) + filters = (os.path.splitext(os.path.split(flt)[1])[0] + for flt in glob.glob(os.path.join('config', 'filter.d', '*.conf')) + if not flt.endswith('common.conf')) + filters = set(filter(lambda flt: not flt.startswith('zzz-'), filters)) # get filters of all jails (filter names without options inside filter[...]) filters_jail = set( JailReader.extractOptions(jail.options['filter'])[0] for jail in jails.jails diff --git a/fail2ban/tests/files/logs/zzz-generic-example b/fail2ban/tests/files/logs/zzz-generic-example new file mode 100644 index 00000000..ecc5a1c6 --- /dev/null +++ b/fail2ban/tests/files/logs/zzz-generic-example @@ -0,0 +1,28 @@ +# -- _daemon with __pid_re, without __hostname -- +# failJSON: { "time": "2005-06-21T16:47:46", "match": true , "host": "192.0.2.1" } +Jun 21 16:47:46 machine test-demo[13709]: F2B: failure from 192.0.2.1 +# -- _daemon with __pid_re -- +# failJSON: { "time": "2005-06-21T16:47:48", "match": true , "host": "192.0.2.1" } +Jun 21 16:47:48 test-demo[13709]: F2B: failure from 192.0.2.1 + +# -- __kernel_prefix -- +# failJSON: { "time": "2005-06-21T16:47:50", "match": true , "host": "192.0.2.2" } +Jun 21 16:47:50 machine kernel: [ 970.699396] F2B: failure from 192.0.2.2 + +# -- _daemon_re with and without __pid_re -- +# failJSON: { "time": "2005-06-21T16:47:52", "match": true , "host": "192.0.2.3" } +Jun 21 16:47:52 machine [test-demo] F2B: failure from 192.0.2.3 +# failJSON: { "time": "2005-06-21T16:47:53", "match": true , "host": "192.0.2.3" } +Jun 21 16:47:53 machine [test-demo][13709] F2B: failure from 192.0.2.3 +# failJSON: { "time": "2005-06-21T16:50:00", "match": true , "host": "192.0.2.3" } +Jun 21 16:50:00 machine test-demo(pam_unix) F2B: failure from 192.0.2.3 +# failJSON: { "time": "2005-06-21T16:50:02", "match": true , "host": "192.0.2.3" } +Jun 21 16:50:02 machine test-demo(pam_unix)[13709] F2B: failure from 192.0.2.3 + + +# -- all common definitions together (bsdverbose hostname kernel_prefix vserver tag daemon_id space) -- +# failJSON: { "time": "2005-06-21T16:55:01", "match": true , "host": "192.0.2.3" } +Jun 21 16:55:01 machine kernel: [ 970.699396] @vserver_demo test-demo(pam_unix)[13709] [ID 255 test] F2B: failure from 192.0.2.3 +# -- the same as above with additional spaces around -- +# failJSON: { "time": "2005-06-21T16:55:02", "match": true , "host": "192.0.2.3" } +Jun 21 16:55:02 machine kernel: [ 970.699396] @vserver_demo test-demo(pam_unix)[13709] [ID 255 test] F2B: failure from 192.0.2.3 From e0924e0d1bb948141cb4efd494d188436355032e Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 13 May 2016 21:44:07 +0200 Subject: [PATCH 10/61] test case fix (always sort result of `DNSUtils.textToIp`, because order of result from `socket.getaddrinfo` is undefined (system depended) --- fail2ban/tests/filtertestcase.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py index 1d4db3a2..4f5dbb55 100644 --- a/fail2ban/tests/filtertestcase.py +++ b/fail2ban/tests/filtertestcase.py @@ -1357,9 +1357,11 @@ class DNSUtilsNetworkTests(unittest.TestCase): res = DNSUtils.textToIp('www.example.com', 'no') self.assertEqual(res, []) res = DNSUtils.textToIp('www.example.com', 'warn') - self.assertEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) + # sort ipaddr, IPv4 is always smaller as IPv6 + self.assertEqual(sorted(res), ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) res = DNSUtils.textToIp('www.example.com', 'yes') - self.assertEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) + # sort ipaddr, IPv4 is always smaller as IPv6 + self.assertEqual(sorted(res), ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) def testTextToIp(self): # Test hostnames @@ -1371,7 +1373,8 @@ class DNSUtilsNetworkTests(unittest.TestCase): for s in hostnames: res = DNSUtils.textToIp(s, 'yes') if s == 'www.example.com': - self.assertEqual(res, ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) + # sort ipaddr, IPv4 is always smaller as IPv6 + self.assertEqual(sorted(res), ['93.184.216.34', '2606:2800:220:1:248:1893:25c8:1946']) else: self.assertEqual(res, []) From cb280b817f89386546c58109f2138fea6e0cfc08 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 13 May 2016 22:16:35 +0200 Subject: [PATCH 11/61] csocket multi-set fix: prevent to convert `list`, `dict`, `set` during transfer (send), this offers a sending of 'multi-set' arrays (missed by cherry-picking from multi-set branch) --- fail2ban/client/csocket.py | 4 +++- fail2ban/server/server.py | 4 ++++ fail2ban/server/transmitter.py | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fail2ban/client/csocket.py b/fail2ban/client/csocket.py index 2e22e5ee..45d58cc6 100644 --- a/fail2ban/client/csocket.py +++ b/fail2ban/client/csocket.py @@ -44,7 +44,9 @@ class CSocket: def send(self, msg): # Convert every list member to string - obj = dumps([str(m) for m in msg], HIGHEST_PROTOCOL) + obj = dumps(map( + lambda m: str(m) if not isinstance(m, (list, dict, set)) else m, msg), + HIGHEST_PROTOCOL) self.__csock.send(obj + CSPROTO.END) return self.receive(self.__csock) diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index 7f75c347..def796a5 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -266,8 +266,10 @@ class Server: flt = self.__jails[name].filter if multiple: for value in value: + logSys.debug(" failregex: %r", value) flt.addFailRegex(value) else: + logSys.debug(" failregex: %r", value) flt.addFailRegex(value) def delFailRegex(self, name, index): @@ -280,8 +282,10 @@ class Server: flt = self.__jails[name].filter if multiple: for value in value: + logSys.debug(" ignoreregex: %r", value) flt.addIgnoreRegex(value) else: + logSys.debug(" ignoreregex: %r", value) flt.addIgnoreRegex(value) def delIgnoreRegex(self, name, index): diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py index 29d6d189..af2d1b53 100644 --- a/fail2ban/server/transmitter.py +++ b/fail2ban/server/transmitter.py @@ -52,7 +52,7 @@ class Transmitter: def proceed(self, command): # Deserialize object - logSys.debug("Command: " + repr(command)) + logSys.debug("Command: %r", command) try: ret = self.__commandHandler(command) ack = 0, ret @@ -263,6 +263,7 @@ class Transmitter: action = self.__server.getAction(name, actionname) if multiple: for cmd in command[3]: + logSys.debug(" %r", cmd) actionkey = cmd[0] if callable(getattr(action, actionkey, None)): actionvalue = json.loads(cmd[1]) if len(cmd)>1 else {} From d4201480552da3e69a6586c271f097ea18b98f41 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 13 May 2016 22:53:57 +0200 Subject: [PATCH 12/61] database: always explicit convert `ip` to `str`, because may be an IPAddr, that will be unsupported type by bind parameter (as long as we've found any default wrapper handler for sqlite3) --- fail2ban/server/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/server/database.py b/fail2ban/server/database.py index 6a3d87c3..a6b3feb1 100644 --- a/fail2ban/server/database.py +++ b/fail2ban/server/database.py @@ -455,7 +455,7 @@ class Fail2BanDb(object): queryArgs.append(MyTime.time() - bantime) if ip is not None: query += " AND ip=?" - queryArgs.append(ip) + queryArgs.append(str(ip)) query += " ORDER BY ip, timeofban desc" return cur.execute(query, queryArgs) From 2b6f8737a72703426149dfd42bac4f74b1e8a6ce Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 13 May 2016 17:17:46 -0400 Subject: [PATCH 13/61] ENH: version Fail2Ban in this branch as 0.10.0 alpha 1 --- fail2ban/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fail2ban/version.py b/fail2ban/version.py index 140ca959..db4daa97 100644 --- a/fail2ban/version.py +++ b/fail2ban/version.py @@ -24,4 +24,4 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko, Steven Hiscocks, Daniel Black" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2005-2016 Yaroslav Halchenko, 2013-2014 Steven Hiscocks, Daniel Black" __license__ = "GPL-v2+" -version = "0.9.4.dev0" +version = "0.10.0a1" From 7e54cee8d61be84bfb8f3eeff70f9f86b90eef9e Mon Sep 17 00:00:00 2001 From: TorontoMedia Date: Fri, 13 May 2016 21:36:27 -0400 Subject: [PATCH 14/61] updated firewallcmd actions --- config/action.d/firewallcmd-allports.conf | 28 +++----- config/action.d/firewallcmd-common.conf | 66 +++++++++++++++++++ config/action.d/firewallcmd-ipset.conf | 6 +- config/action.d/firewallcmd-multiport.conf | 51 +++----------- config/action.d/firewallcmd-new.conf | 29 +++----- config/action.d/firewallcmd-rich-logging.conf | 34 +++------- config/action.d/firewallcmd-rich-rules.conf | 35 +++------- 7 files changed, 119 insertions(+), 130 deletions(-) create mode 100644 config/action.d/firewallcmd-common.conf diff --git a/config/action.d/firewallcmd-allports.conf b/config/action.d/firewallcmd-allports.conf index 571d5ba6..de0e7f91 100644 --- a/config/action.d/firewallcmd-allports.conf +++ b/config/action.d/firewallcmd-allports.conf @@ -6,34 +6,26 @@ [INCLUDES] -before = iptables-common.conf +before = firewallcmd-common.conf [Definition] -actionstart = firewall-cmd --direct --add-chain ipv4 filter f2b- - firewall-cmd --direct --add-rule ipv4 filter f2b- 1000 -j RETURN - firewall-cmd --direct --add-rule ipv4 filter 0 -j f2b- +actionstart = firewall-cmd --direct --add-chain filter f2b- + firewall-cmd --direct --add-rule filter f2b- 1000 -j RETURN + firewall-cmd --direct --add-rule filter 0 -j f2b- -actionstop = firewall-cmd --direct --remove-rule ipv4 filter 0 -j f2b- - firewall-cmd --direct --remove-rules ipv4 filter f2b- - firewall-cmd --direct --remove-chain ipv4 filter f2b- +actionstop = firewall-cmd --direct --remove-rule filter 0 -j f2b- + firewall-cmd --direct --remove-rules filter f2b- + firewall-cmd --direct --remove-chain filter f2b- # Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-recidive$' -actioncheck = firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-$' +actioncheck = firewall-cmd --direct --get-chains filter | sed -e 's, ,\n,g' | grep -q '^f2b-$' -actionban = firewall-cmd --direct --add-rule ipv4 filter f2b- 0 -s -j +actionban = firewall-cmd --direct --add-rule filter f2b- 0 -s -j -actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b- 0 -s -j - -[Init] - -# Default name of the chain -# -name = default - -chain = INPUT_direct +actionunban = firewall-cmd --direct --remove-rule filter f2b- 0 -s -j # DEV NOTES: # diff --git a/config/action.d/firewallcmd-common.conf b/config/action.d/firewallcmd-common.conf new file mode 100644 index 00000000..106ef216 --- /dev/null +++ b/config/action.d/firewallcmd-common.conf @@ -0,0 +1,66 @@ +# Fail2Ban configuration file +# +# Author: Donald Yandt +# + +[Init] + +# Option: name +# Notes Default name of the chain +# Values: STRING +name = default + +# Option: family(ipv4) +# Notes specifies the socket address family type +# Values: STRING +family = ipv4 + +# Option: chain +# Notes specifies the firewalld chain to which the Fail2Ban rules should be +# added +# Values: STRING Default: INPUT_direct +chain = INPUT_direct + +# Option: zone +# Notes use command firewall-cmd --get-active-zones to see a list of all active zones. See firewalld man pages for more information on zones +# Values: STRING Default: public +zone = public + +# Option: service +# Notes use command firewall-cmd --get-services to see a list of services available +# Examples zones: amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps +# freeipa-replication ftp high-availability http https imaps ipp ipp-client ipsec iscsi-target kadmin kerberos +# kpasswd ldap ldaps libvirt libvirt-tls mdns mosh mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s +# postgresql privoxy proxy-dhcp puppetmaster radius rpc-bind rsyncd samba samba-client sane smtp squid ssh synergy +# telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server +# Values: STRING Default: ssh +service = ssh + +# Option: rejecttype (ipv4) +# Note: See iptables/firewalld man pages for ipv4 reject types. +# Values: STRING +rejecttype = icmp-port-unreachable + +# Option: blocktype (ipv4/ipv6) +# Note: See iptables/firewalld man pages for jump targets. Common values are REJECT, +# REJECT --reject-with icmp-port-unreachable, DROP +# Values: STRING +blocktype = REJECT --reject-with + +# Option: rich-blocktype (ipv4/ipv6) +# Note: See firewalld man pages for jump targets. Common values are reject, +# reject type="icmp-port-unreachable", drop +# Values: STRING +rich-blocktype = reject type='' + +[Init?family=inet6] + +# Option: family(ipv6) +# Notes specifies the socket address family type +# Values: STRING +family = ipv6 + +# Option: rejecttype (ipv6) +# Note: See iptables/firewalld man pages for ipv6 reject types. +# Values: STRING +rejecttype = icmp6-port-unreachable diff --git a/config/action.d/firewallcmd-ipset.conf b/config/action.d/firewallcmd-ipset.conf index 38b0f3d3..b05f4f53 100644 --- a/config/action.d/firewallcmd-ipset.conf +++ b/config/action.d/firewallcmd-ipset.conf @@ -14,14 +14,14 @@ [INCLUDES] -before = iptables-common.conf +before = firewallcmd-common.conf [Definition] actionstart = ipset create fail2ban- hash:ip timeout - firewall-cmd --direct --add-rule ipv4 filter 0 -p -m multiport --dports -m set --match-set fail2ban- src -j + firewall-cmd --direct --add-rule filter 0 -p -m multiport --dports -m set --match-set fail2ban- src -j -actionstop = firewall-cmd --direct --remove-rule ipv4 filter 0 -p -m multiport --dports -m set --match-set fail2ban- src -j +actionstop = firewall-cmd --direct --remove-rule filter 0 -p -m multiport --dports -m set --match-set fail2ban- src -j ipset flush fail2ban- ipset destroy fail2ban- diff --git a/config/action.d/firewallcmd-multiport.conf b/config/action.d/firewallcmd-multiport.conf index 438d4cf7..cc40d668 100644 --- a/config/action.d/firewallcmd-multiport.conf +++ b/config/action.d/firewallcmd-multiport.conf @@ -5,59 +5,28 @@ [INCLUDES] -before = iptables-common.conf +before = firewallcmd-common.conf [Definition] -actionstart = firewall-cmd --direct --add-chain ipv4 filter f2b- - firewall-cmd --direct --add-rule ipv4 filter f2b- 1000 -j RETURN - firewall-cmd --direct --add-rule ipv4 filter 0 -m conntrack --ctstate NEW -p -m multiport --dports -j f2b- +actionstart = firewall-cmd --direct --add-chain filter f2b- + firewall-cmd --direct --add-rule filter f2b- 1000 -j RETURN + firewall-cmd --direct --add-rule filter 0 -m conntrack --ctstate NEW -p -m multiport --dports -j f2b- -actionstop = firewall-cmd --direct --remove-rule ipv4 filter 0 -m conntrack --ctstate NEW -p -m multiport --dports -j f2b- - firewall-cmd --direct --remove-rules ipv4 filter f2b- - firewall-cmd --direct --remove-chain ipv4 filter f2b- +actionstop = firewall-cmd --direct --remove-rule filter 0 -m conntrack --ctstate NEW -p -m multiport --dports -j f2b- + firewall-cmd --direct --remove-rules filter f2b- + firewall-cmd --direct --remove-chain filter f2b- # Example actioncheck: firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-apache-modsecurity$' -actioncheck = firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-$' +actioncheck = firewall-cmd --direct --get-chains filter | sed -e 's, ,\n,g' | grep -q '^f2b-$' -actionban = firewall-cmd --direct --add-rule ipv4 filter f2b- 0 -s -j +actionban = firewall-cmd --direct --add-rule filter f2b- 0 -s -j -actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b- 0 -s -j +actionunban = firewall-cmd --direct --remove-rule filter f2b- 0 -s -j [Init] -# Default name of the chain -name = default - -chain = INPUT_direct - # Could also use port numbers separated by a comma. port = 1:65535 - -# Option: protocol -# Values: [ tcp | udp | icmp | all ] - -protocol = tcp - - - -# DEV NOTES: -# -# Author: Donald Yandt -# Uses "FirewallD" instead of the "iptables daemon". -# -# -# Output: -# actionstart: -# $ firewall-cmd --direct --add-chain ipv4 filter f2b-apache-modsecurity -# success -# $ firewall-cmd --direct --add-rule ipv4 filter f2b-apache-modsecurity 1000 -j RETURN -# success -# $ sudo firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -m state --state NEW -p tcp -m multiport --dports 80,443 -j f2b-apache-modsecurity -# success -# actioncheck: -# $ firewall-cmd --direct --get-chains ipv4 filter f2b-apache-modsecurity | sed -e 's, ,\n,g' | grep -q '^f2b-apache-modsecurity$' -# f2b-apache-modsecurity - diff --git a/config/action.d/firewallcmd-new.conf b/config/action.d/firewallcmd-new.conf index ac72a68a..e64601e1 100644 --- a/config/action.d/firewallcmd-new.conf +++ b/config/action.d/firewallcmd-new.conf @@ -4,32 +4,23 @@ [INCLUDES] -before = iptables-common.conf +before = firewallcmd-common.conf [Definition] -actionstart = firewall-cmd --direct --add-chain ipv4 filter f2b- - firewall-cmd --direct --add-rule ipv4 filter f2b- 1000 -j RETURN - firewall-cmd --direct --add-rule ipv4 filter 0 -m state --state NEW -p -m multiport --dports -j f2b- +actionstart = firewall-cmd --direct --add-chain filter f2b- + firewall-cmd --direct --add-rule filter f2b- 1000 -j RETURN + firewall-cmd --direct --add-rule filter 0 -m state --state NEW -p -m multiport --dports -j f2b- -actionstop = firewall-cmd --direct --remove-rule ipv4 filter 0 -m state --state NEW -p -m multiport --dports -j f2b- - firewall-cmd --direct --remove-rules ipv4 filter f2b- - firewall-cmd --direct --remove-chain ipv4 filter f2b- +actionstop = firewall-cmd --direct --remove-rule filter 0 -m state --state NEW -p -m multiport --dports -j f2b- + firewall-cmd --direct --remove-rules filter f2b- + firewall-cmd --direct --remove-chain filter f2b- -actioncheck = firewall-cmd --direct --get-chains ipv4 filter | grep -q 'f2b-$' +actioncheck = firewall-cmd --direct --get-chains filter | grep -q 'f2b-$' -actionban = firewall-cmd --direct --add-rule ipv4 filter f2b- 0 -s -j +actionban = firewall-cmd --direct --add-rule filter f2b- 0 -s -j -actionunban = firewall-cmd --direct --remove-rule ipv4 filter f2b- 0 -s -j - -[Init] - -# Option: chain -# Notes specifies the iptables chain to which the fail2ban rules should be -# added -# Values: [ STRING ] -# -chain = INPUT_direct +actionunban = firewall-cmd --direct --remove-rule filter f2b- 0 -s -j # DEV NOTES: # diff --git a/config/action.d/firewallcmd-rich-logging.conf b/config/action.d/firewallcmd-rich-logging.conf index 1b88c2d9..34cb2413 100644 --- a/config/action.d/firewallcmd-rich-logging.conf +++ b/config/action.d/firewallcmd-rich-logging.conf @@ -15,6 +15,10 @@ # firewall-cmd [--zone=] --list-all # firewall-cmd [--zone=zone] --query-rich-rule='rule' +[INCLUDES] + +before = firewallcmd-common.conf + [Definition] actionstart = @@ -26,40 +30,22 @@ actioncheck = # you can also use zones and/or service names. # # zone example: -# firewall-cmd --zone= --add-rich-rule="rule family='ipv4' source address='' port port='' protocol='' log prefix='f2b-' level='' limit value='/m' " +# firewall-cmd --zone= --add-rich-rule="rule family='' source address='' port port='' protocol='' log prefix='f2b-' level='' limit value='/m' " +# # service name example: -# firewall-cmd --zone= --add-rich-rule="rule family='ipv4' source address='' service name='' log prefix='f2b-' level='' limit value='/m' " +# firewall-cmd --zone= --add-rich-rule="rule family='' source address='' service name='' log prefix='f2b-' level='' limit value='/m' " +# # Because rich rules can only handle single or a range of ports we must split ports and execute the command for each port. Ports can be single and ranges seperated by a comma or space for an example: http, https, 22-60, 18 smtp -actionban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='ipv4' source address='' port port='$p' protocol='' log prefix='f2b-' level='' limit value='/m' "; done +actionban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='' source address='' port port='$p' protocol='' log prefix='f2b-' level='' limit value='/m' "; done -actionunban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='ipv4' source address='' port port='$p' protocol='' log prefix='f2b-' level='' limit value='/m' "; done +actionunban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='' source address='' port port='$p' protocol='' log prefix='f2b-' level='' limit value='/m' "; done [Init] -name = default - # log levels are "emerg", "alert", "crit", "error", "warning", "notice", "info" or "debug" level = info # log rate per minute rate = 1 -zone = public - -# use command firewall-cmd --get-services to see a list of services available -# -# Examples: -# -# amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps -# freeipa-replication ftp high-availability http https imaps ipp ipp-client ipsec iscsi-target kadmin kerberos -# kpasswd ldap ldaps libvirt libvirt-tls mdns mosh mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s -# postgresql privoxy proxy-dhcp puppetmaster radius rpc-bind rsyncd samba samba-client sane smtp squid ssh synergy -# telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server - -service = ssh - -# reject types: 'icmp-net-unreachable', 'icmp-host-unreachable', 'icmp-port-unreachable', 'icmp-proto-unreachable', -# 'icmp-net-prohibited', 'icmp-host-prohibited', 'icmp-admin-prohibited' or 'tcp-reset' - -blocktype = reject type='icmp-port-unreachable' diff --git a/config/action.d/firewallcmd-rich-rules.conf b/config/action.d/firewallcmd-rich-rules.conf index 4e39df54..e64c3823 100644 --- a/config/action.d/firewallcmd-rich-rules.conf +++ b/config/action.d/firewallcmd-rich-rules.conf @@ -13,6 +13,10 @@ # firewall-cmd [--zone=] --list-all # firewall-cmd [--zone=zone] --query-rich-rule='rule' +[INCLUDES] + +before = firewallcmd-common.conf + [Definition] actionstart = @@ -24,34 +28,15 @@ actioncheck = #you can also use zones and/or service names. # # zone example: -# firewall-cmd --zone= --add-rich-rule="rule family='ipv4' source address='' port port='' protocol='' " +# firewall-cmd --zone= --add-rich-rule="rule family='ipv4' source address='' port port='' protocol='' " +# # service name example: -# firewall-cmd --zone= --add-rich-rule="rule family='ipv4' source address='' service name='' " +# firewall-cmd --zone= --add-rich-rule="rule family='ipv4' source address='' service name='' " +# # Because rich rules can only handle single or a range of ports we must split ports and execute the command for each port. Ports can be single and ranges seperated by a comma or space for an example: http, https, 22-60, 18 smtp -actionban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='ipv4' source address='' port port='$p' protocol='' "; done +actionban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --add-rich-rule="rule family='' source address='' port port='$p' protocol='' "; done -actionunban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='ipv4' source address='' port port='$p' protocol='' "; done +actionunban = ports=""; for p in $(echo $ports | tr ", " " "); do firewall-cmd --remove-rich-rule="rule family='' source address='' port port='$p' protocol='' "; done -[Init] -name = default - -zone = public - -# use command firewall-cmd --get-services to see a list of services available -# -# Examples: -# -# amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps -# freeipa-replication ftp high-availability http https imaps ipp ipp-client ipsec iscsi-target kadmin kerberos -# kpasswd ldap ldaps libvirt libvirt-tls mdns mosh mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s -# postgresql privoxy proxy-dhcp puppetmaster radius rpc-bind rsyncd samba samba-client sane smtp squid ssh synergy -# telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server - -service = ssh - -# reject types: 'icmp-net-unreachable', 'icmp-host-unreachable', 'icmp-port-unreachable', 'icmp-proto-unreachable', -# 'icmp-net-prohibited', 'icmp-host-prohibited', 'icmp-admin-prohibited' or 'tcp-reset' - -blocktype = reject type='icmp-port-unreachable' From 810d5996b5b96ed637041492a7f80584973fa47b Mon Sep 17 00:00:00 2001 From: TorontoMedia Date: Fri, 13 May 2016 22:10:25 -0400 Subject: [PATCH 15/61] Update firewallcmd-rich-logging.conf --- config/action.d/firewallcmd-rich-logging.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/action.d/firewallcmd-rich-logging.conf b/config/action.d/firewallcmd-rich-logging.conf index 34cb2413..d2c8fc2f 100644 --- a/config/action.d/firewallcmd-rich-logging.conf +++ b/config/action.d/firewallcmd-rich-logging.conf @@ -30,10 +30,10 @@ actioncheck = # you can also use zones and/or service names. # # zone example: -# firewall-cmd --zone= --add-rich-rule="rule family='' source address='' port port='' protocol='' log prefix='f2b-' level='' limit value='/m' " +# firewall-cmd --zone= --add-rich-rule="rule family='' source address='' port port='' protocol='' log prefix='f2b-' level='' limit value='/m' " # # service name example: -# firewall-cmd --zone= --add-rich-rule="rule family='' source address='' service name='' log prefix='f2b-' level='' limit value='/m' " +# firewall-cmd --zone= --add-rich-rule="rule family='' source address='' service name='' log prefix='f2b-' level='' limit value='/m' " # # Because rich rules can only handle single or a range of ports we must split ports and execute the command for each port. Ports can be single and ranges seperated by a comma or space for an example: http, https, 22-60, 18 smtp From 07de83e04a453ec5bb172f48db0ba863cf8d42a7 Mon Sep 17 00:00:00 2001 From: TorontoMedia Date: Fri, 13 May 2016 22:38:10 -0400 Subject: [PATCH 16/61] Update firewallcmd-common.conf --- config/action.d/firewallcmd-common.conf | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/config/action.d/firewallcmd-common.conf b/config/action.d/firewallcmd-common.conf index 106ef216..4abe5318 100644 --- a/config/action.d/firewallcmd-common.conf +++ b/config/action.d/firewallcmd-common.conf @@ -10,6 +10,16 @@ # Values: STRING name = default +# Option port +# Notes Can also use port numbers separated by a comma and in rich-rules comma and/or space. +# Value STRING Default: 1:65535 +port = 1:65535 + +# Option: protocol +# Notes [ tcp | udp | icmp | all ] +# Values: STRING Default: tcp +protocol = tcp + # Option: family(ipv4) # Notes specifies the socket address family type # Values: STRING @@ -28,7 +38,7 @@ zone = public # Option: service # Notes use command firewall-cmd --get-services to see a list of services available -# Examples zones: amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps +# Examples services: amanda-client amanda-k5-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns freeipa-ldap freeipa-ldaps # freeipa-replication ftp high-availability http https imaps ipp ipp-client ipsec iscsi-target kadmin kerberos # kpasswd ldap ldaps libvirt libvirt-tls mdns mosh mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s # postgresql privoxy proxy-dhcp puppetmaster radius rpc-bind rsyncd samba samba-client sane smtp squid ssh synergy @@ -37,18 +47,18 @@ zone = public service = ssh # Option: rejecttype (ipv4) -# Note: See iptables/firewalld man pages for ipv4 reject types. +# Notes See iptables/firewalld man pages for ipv4 reject types. # Values: STRING rejecttype = icmp-port-unreachable # Option: blocktype (ipv4/ipv6) -# Note: See iptables/firewalld man pages for jump targets. Common values are REJECT, +# Notes See iptables/firewalld man pages for jump targets. Common values are REJECT, # REJECT --reject-with icmp-port-unreachable, DROP # Values: STRING blocktype = REJECT --reject-with # Option: rich-blocktype (ipv4/ipv6) -# Note: See firewalld man pages for jump targets. Common values are reject, +# Notes See firewalld man pages for jump targets. Common values are reject, # reject type="icmp-port-unreachable", drop # Values: STRING rich-blocktype = reject type='' From ffebde68e0fae17b07e620b8d9bc62aa4c967426 Mon Sep 17 00:00:00 2001 From: TorontoMedia Date: Fri, 13 May 2016 22:38:36 -0400 Subject: [PATCH 17/61] Update firewallcmd-multiport.conf --- config/action.d/firewallcmd-multiport.conf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/config/action.d/firewallcmd-multiport.conf b/config/action.d/firewallcmd-multiport.conf index cc40d668..81540e5b 100644 --- a/config/action.d/firewallcmd-multiport.conf +++ b/config/action.d/firewallcmd-multiport.conf @@ -24,9 +24,3 @@ actioncheck = firewall-cmd --direct --get-chains filter | sed -e 's, ,\ actionban = firewall-cmd --direct --add-rule filter f2b- 0 -s -j actionunban = firewall-cmd --direct --remove-rule filter f2b- 0 -s -j - -[Init] - -# Could also use port numbers separated by a comma. -port = 1:65535 - From 0c44ecfc770113b2e0c2ff3937d3e3a91d7a97ca Mon Sep 17 00:00:00 2001 From: sebres Date: Sat, 14 May 2016 15:00:23 +0200 Subject: [PATCH 18/61] action.d/firewallcmd-ipset.conf: different name of the match set's for IPv4/IPv6, using conditional , analog to the iptables-ipset; test cases for 3 firewallcmd extended; --- config/action.d/firewallcmd-ipset.conf | 20 +++-- fail2ban/tests/servertestcase.py | 108 +++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 7 deletions(-) diff --git a/config/action.d/firewallcmd-ipset.conf b/config/action.d/firewallcmd-ipset.conf index b05f4f53..69447627 100644 --- a/config/action.d/firewallcmd-ipset.conf +++ b/config/action.d/firewallcmd-ipset.conf @@ -18,16 +18,16 @@ before = firewallcmd-common.conf [Definition] -actionstart = ipset create fail2ban- hash:ip timeout - firewall-cmd --direct --add-rule filter 0 -p -m multiport --dports -m set --match-set fail2ban- src -j +actionstart = ipset create hash:ip timeout + firewall-cmd --direct --add-rule filter 0 -p -m multiport --dports -m set --match-set src -j -actionstop = firewall-cmd --direct --remove-rule filter 0 -p -m multiport --dports -m set --match-set fail2ban- src -j - ipset flush fail2ban- - ipset destroy fail2ban- +actionstop = firewall-cmd --direct --remove-rule filter 0 -p -m multiport --dports -m set --match-set src -j + ipset flush + ipset destroy -actionban = ipset add fail2ban- timeout -exist +actionban = ipset add timeout -exist -actionunban = ipset del fail2ban- -exist +actionunban = ipset del -exist [Init] @@ -44,6 +44,12 @@ chain = INPUT_direct bantime = 600 +ipmset = f2b- + +[Init?family=inet6] + +ipmset = f2b-6 + # DEV NOTES: # diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index cc14e97f..1809933c 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -1391,6 +1391,114 @@ class ServerConfigReaderTests(LogCaptureTestCase): 'ip6-ban': ("`pfctl -t f2b-j-w-pf -T add 2001:db8::`",), 'ip6-unban': ("`pfctl -t f2b-j-w-pf -T delete 2001:db8::`",), }), + # firewallcmd-multiport -- + ('j-w-fwcmd-mp', 'firewallcmd-multiport[name=%(__name__)s, bantime="600", port="http,https", protocol="tcp", chain="INPUT"]', { + 'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'), + 'start': ( + "`firewall-cmd --direct --add-chain ipv4 filter f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-mp 1000 -j RETURN`", + "`firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --add-chain ipv6 filter f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-mp 1000 -j RETURN`", + "`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`", + ), + 'stop': ( + "`firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --remove-rules ipv4 filter f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --remove-chain ipv4 filter f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --remove-rule ipv6 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --remove-rules ipv6 filter f2b-j-w-fwcmd-mp`", + "`firewall-cmd --direct --remove-chain ipv6 filter f2b-j-w-fwcmd-mp`", + ), + 'ip4-check': ( + r"`firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-mp$'`", + ), + 'ip6-check': ( + r"`firewall-cmd --direct --get-chains ipv6 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-mp$'`", + ), + 'ip4-ban': ( + r"`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-mp 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`", + ), + 'ip4-unban': ( + r"`firewall-cmd --direct --remove-rule ipv4 filter f2b-j-w-fwcmd-mp 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`", + ), + 'ip6-ban': ( + r"`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-mp 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`", + ), + 'ip6-unban': ( + r"`firewall-cmd --direct --remove-rule ipv6 filter f2b-j-w-fwcmd-mp 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`", + ), + }), + # firewallcmd-allports -- + ('j-w-fwcmd-ap', 'firewallcmd-allports[name=%(__name__)s, bantime="600", protocol="tcp", chain="INPUT"]', { + 'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'), + 'start': ( + "`firewall-cmd --direct --add-chain ipv4 filter f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-ap 1000 -j RETURN`", + "`firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -j f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --add-chain ipv6 filter f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-ap 1000 -j RETURN`", + "`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -j f2b-j-w-fwcmd-ap`", + ), + 'stop': ( + "`firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -j f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --remove-rules ipv4 filter f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --remove-chain ipv4 filter f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --remove-rule ipv6 filter INPUT 0 -j f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --remove-rules ipv6 filter f2b-j-w-fwcmd-ap`", + "`firewall-cmd --direct --remove-chain ipv6 filter f2b-j-w-fwcmd-ap`", + ), + 'ip4-check': ( + r"`firewall-cmd --direct --get-chains ipv4 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-ap$'`", + ), + 'ip6-check': ( + r"`firewall-cmd --direct --get-chains ipv6 filter | sed -e 's, ,\n,g' | grep -q '^f2b-j-w-fwcmd-ap$'`", + ), + 'ip4-ban': ( + r"`firewall-cmd --direct --add-rule ipv4 filter f2b-j-w-fwcmd-ap 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`", + ), + 'ip4-unban': ( + r"`firewall-cmd --direct --remove-rule ipv4 filter f2b-j-w-fwcmd-ap 0 -s 192.0.2.1 -j REJECT --reject-with icmp-port-unreachable`", + ), + 'ip6-ban': ( + r"`firewall-cmd --direct --add-rule ipv6 filter f2b-j-w-fwcmd-ap 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`", + ), + 'ip6-unban': ( + r"`firewall-cmd --direct --remove-rule ipv6 filter f2b-j-w-fwcmd-ap 0 -s 2001:db8:: -j REJECT --reject-with icmp6-port-unreachable`", + ), + }), + # firewallcmd-ipset -- + ('j-w-fwcmd-ipset', 'firewallcmd-ipset[name=%(__name__)s, bantime="600", port="http", protocol="tcp", chain="INPUT"]', { + 'ip4': (' f2b-j-w-fwcmd-ipset ',), 'ip6': (' f2b-j-w-fwcmd-ipset6 ',), + 'start': ( + "`ipset create f2b-j-w-fwcmd-ipset hash:ip timeout 600`", + "`firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset src -j REJECT --reject-with icmp-port-unreachable`", + "`ipset create f2b-j-w-fwcmd-ipset6 hash:ip timeout 600`", + "`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset6 src -j REJECT --reject-with icmp6-port-unreachable`", + ), + 'stop': ( + "`firewall-cmd --direct --remove-rule ipv4 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset src -j REJECT --reject-with icmp-port-unreachable`", + "`ipset flush f2b-j-w-fwcmd-ipset`", + "`ipset destroy f2b-j-w-fwcmd-ipset`", + "`firewall-cmd --direct --remove-rule ipv6 filter INPUT 0 -p tcp -m multiport --dports http -m set --match-set f2b-j-w-fwcmd-ipset6 src -j REJECT --reject-with icmp6-port-unreachable`", + "`ipset flush f2b-j-w-fwcmd-ipset6`", + "`ipset destroy f2b-j-w-fwcmd-ipset6`", + ), + 'ip4-check': (), + 'ip6-check': (), + 'ip4-ban': ( + r"`ipset add f2b-j-w-fwcmd-ipset 192.0.2.1 timeout 600 -exist`", + ), + 'ip4-unban': ( + r"`ipset del f2b-j-w-fwcmd-ipset 192.0.2.1 -exist`", + ), + 'ip6-ban': ( + r"`ipset add f2b-j-w-fwcmd-ipset6 2001:db8:: timeout 600 -exist`", + ), + 'ip6-unban': ( + r"`ipset del f2b-j-w-fwcmd-ipset6 2001:db8:: -exist`", + ), + }), ) server = TestServer() transm = server._Server__transm From 975608dfb629c637622f0d638335637c1c688970 Mon Sep 17 00:00:00 2001 From: Alexander Koeppe Date: Sun, 15 May 2016 21:08:32 +0200 Subject: [PATCH 19/61] no hardcoded python interpreter path --- bin/fail2ban-client | 2 +- bin/fail2ban-regex | 2 +- bin/fail2ban-server | 2 +- bin/fail2ban-testcases | 2 +- config/filter.d/ignorecommands/apache-fakegooglebot | 2 +- fail2ban/client/fail2banregex.py | 2 +- fail2ban/tests/files/config/apache-auth/digest.py | 2 +- fail2ban/tests/files/ignorecommand.py | 2 +- setup.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/fail2ban-client b/bin/fail2ban-client index bc0c0be8..4b1407c4 100755 --- a/bin/fail2ban-client +++ b/bin/fail2ban-client @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : diff --git a/bin/fail2ban-regex b/bin/fail2ban-regex index 584c1ea7..09044f0a 100755 --- a/bin/fail2ban-regex +++ b/bin/fail2ban-regex @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : # diff --git a/bin/fail2ban-server b/bin/fail2ban-server index f522f418..5ec645a4 100755 --- a/bin/fail2ban-server +++ b/bin/fail2ban-server @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : diff --git a/bin/fail2ban-testcases b/bin/fail2ban-testcases index 606b0b06..768c584d 100755 --- a/bin/fail2ban-testcases +++ b/bin/fail2ban-testcases @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : """Script to run Fail2Ban tests battery diff --git a/config/filter.d/ignorecommands/apache-fakegooglebot b/config/filter.d/ignorecommands/apache-fakegooglebot index 86a28eaa..fe4b6591 100755 --- a/config/filter.d/ignorecommands/apache-fakegooglebot +++ b/config/filter.d/ignorecommands/apache-fakegooglebot @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Inspired by https://isc.sans.edu/forums/diary/When+Google+isnt+Google/15968/ # # Written in Python to reuse built-in Python batteries and not depend on diff --git a/fail2ban/client/fail2banregex.py b/fail2ban/client/fail2banregex.py index 9ecb7229..fae4f354 100755 --- a/fail2ban/client/fail2banregex.py +++ b/fail2ban/client/fail2banregex.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : # diff --git a/fail2ban/tests/files/config/apache-auth/digest.py b/fail2ban/tests/files/config/apache-auth/digest.py index 875ebffe..f4fcfcb9 100755 --- a/fail2ban/tests/files/config/apache-auth/digest.py +++ b/fail2ban/tests/files/config/apache-auth/digest.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python import requests try: diff --git a/fail2ban/tests/files/ignorecommand.py b/fail2ban/tests/files/ignorecommand.py index dd6b5aab..473980ec 100755 --- a/fail2ban/tests/files/ignorecommand.py +++ b/fail2ban/tests/files/ignorecommand.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python import sys if sys.argv[1] == "10.0.0.1": exit(0) diff --git a/setup.py b/setup.py index e3c499d2..5579c981 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : From de813acf5104dc00c0086b544c6fe82f428d4ca8 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 17 May 2016 11:33:49 +0200 Subject: [PATCH 20/61] extends generic `__prefix_line` with optional brackets for the date ambit (gh-1421), added new parameter `__date_ambit` + test case added; --- ChangeLog | 2 ++ config/filter.d/common.conf | 4 +++- fail2ban/tests/files/logs/zzz-generic-example | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 44ba9d11..d52e1cff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21,6 +21,8 @@ ver. 0.9.5 (2016/XX/XXX) - wanna-be-released * filter.d/common.conf - unexpected extra regex-space in generic `__prefix_line` (gh-1405) - all optional spaces normalized in `common.conf`, test covered now + - generic `__prefix_line` extended with optional brackets for the date ambit (gh-1421), + added new parameter `__date_ambit` - New Features: * New Actions: diff --git a/config/filter.d/common.conf b/config/filter.d/common.conf index 115fa96c..23fd1d5a 100644 --- a/config/filter.d/common.conf +++ b/config/filter.d/common.conf @@ -46,12 +46,14 @@ __md5hex = (?:[\da-f]{2}:){15}[\da-f]{2} # appearing before the host as per testcases/files/logs/bsd/*. __bsd_syslog_verbose = <[^.]+\.[^.]+> +__date_ambit = \[\] + # Common line prefixes (beginnings) which could be used in filters # # [bsdverbose]? [hostname] [vserver tag] daemon_id spaces # # This can be optional (for instance if we match named native log files) -__prefix_line = \s*(?:(?:%(__bsd_syslog_verbose)s)\s*)?(?:(?:%(__hostname)s)\s*)?(?:(?:%(__kernel_prefix)s)\s*)?(?:(?:@vserver_\S+)\s*)?(?:(?:%(__daemon_combs_re)s)\s*)?(?:(?:%(__daemon_extra_re)s)\s*)? +__prefix_line = (?:%(__date_ambit)s)?\s*(?:(?:%(__bsd_syslog_verbose)s)\s*)?(?:(?:%(__hostname)s)\s*)?(?:(?:%(__kernel_prefix)s)\s*)?(?:(?:@vserver_\S+)\s*)?(?:(?:%(__daemon_combs_re)s)\s*)?(?:(?:%(__daemon_extra_re)s)\s*)? # PAM authentication mechanism check for failures, e.g.: pam_unix, pam_sss, # pam_ldap diff --git a/fail2ban/tests/files/logs/zzz-generic-example b/fail2ban/tests/files/logs/zzz-generic-example index ecc5a1c6..e3480b63 100644 --- a/fail2ban/tests/files/logs/zzz-generic-example +++ b/fail2ban/tests/files/logs/zzz-generic-example @@ -26,3 +26,6 @@ Jun 21 16:55:01 machine kernel: [ 970.699396] @vserver_demo test-de # -- the same as above with additional spaces around -- # failJSON: { "time": "2005-06-21T16:55:02", "match": true , "host": "192.0.2.3" } Jun 21 16:55:02 machine kernel: [ 970.699396] @vserver_demo test-demo(pam_unix)[13709] [ID 255 test] F2B: failure from 192.0.2.3 +# -- the same as above with brackets as date ambit -- +# failJSON: { "time": "2005-06-21T16:55:03", "match": true , "host": "192.0.2.3" } +[Jun 21 16:55:03] machine kernel: [ 970.699396] @vserver_demo test-demo(pam_unix)[13709] [ID 255 test] F2B: failure from 192.0.2.3 From cb4f9be8b2748e940303cff77d72ab7fdcf06a53 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 17 May 2016 11:54:08 +0200 Subject: [PATCH 21/61] the date brackets removed from filters using `__prefix_line`, because `__prefix_line` already contains the date ambit; --- config/filter.d/asterisk.conf | 20 ++++++++++---------- config/filter.d/nsd.conf | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/config/filter.d/asterisk.conf b/config/filter.d/asterisk.conf index 3975fb29..01063efa 100644 --- a/config/filter.d/asterisk.conf +++ b/config/filter.d/asterisk.conf @@ -16,17 +16,17 @@ __pid_re = (?:\[\d+\]) iso8601 = \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{4} # All Asterisk log messages begin like this: -log_prefix= (?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[C-[\da-f]*\])? \S+:\d*( in \w+:)? +log_prefix= (?:NOTICE|SECURITY|WARNING)%(__pid_re)s:?(?:\[C-[\da-f]*\])? [^:]+:\d*( in \w+:)? -failregex = ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Registration from '[^']*' failed for '(:\d+)?' - (Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$ - ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Call from '[^']*' \(:\d+\) to extension '[^']*' rejected because extension not found in context - ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host failed to authenticate as '[^']*'$ - ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s No registration for peer '[^']*' \(from \)$ - ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Host failed MD5 authentication for '[^']*' \([^)]+\)$ - ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s Failed to authenticate (user|device) [^@]+@\S*$ - ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s hacking attempt detected ''$ - ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)//\d+"(,Challenge="[\w/]+")?(,ReceivedChallenge="\w+")?(,Response="\w+",ExpectedResponse="\w*")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$ - ^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )Ext\. s: "Rejecting unknown SIP connection from "$ +failregex = ^%(__prefix_line)s%(log_prefix)s Registration from '[^']*' failed for '(:\d+)?' - (Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$ + ^%(__prefix_line)s%(log_prefix)s Call from '[^']*' \(:\d+\) to extension '[^']*' rejected because extension not found in context + ^%(__prefix_line)s%(log_prefix)s Host failed to authenticate as '[^']*'$ + ^%(__prefix_line)s%(log_prefix)s No registration for peer '[^']*' \(from \)$ + ^%(__prefix_line)s%(log_prefix)s Host failed MD5 authentication for '[^']*' \([^)]+\)$ + ^%(__prefix_line)s%(log_prefix)s Failed to authenticate (user|device) [^@]+@\S*$ + ^%(__prefix_line)s%(log_prefix)s hacking attempt detected ''$ + ^%(__prefix_line)s%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)//\d+"(,Challenge="[\w/]+")?(,ReceivedChallenge="\w+")?(,Response="\w+",ExpectedResponse="\w*")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$ + ^%(__prefix_line)s%(log_prefix)s "Rejecting unknown SIP connection from "$ ignoreregex = diff --git a/config/filter.d/nsd.conf b/config/filter.d/nsd.conf index 70b41ca4..8f32f7be 100644 --- a/config/filter.d/nsd.conf +++ b/config/filter.d/nsd.conf @@ -22,7 +22,7 @@ _daemon = nsd # (?:::f{4,6}:)?(?P[\w\-.^_]+) # Values: TEXT -failregex = ^\[\]%(__prefix_line)sinfo: ratelimit block .* query TYPE255$ - ^\[\]%(__prefix_line)sinfo: .* refused, no acl matches\.$ +failregex = ^%(__prefix_line)sinfo: ratelimit block .* query TYPE255$ + ^%(__prefix_line)sinfo: .* refused, no acl matches\.$ ignoreregex = From 5e06e8be624b1d46169c60797b6959f0c9d7ee49 Mon Sep 17 00:00:00 2001 From: Alexander Koeppe Date: Tue, 17 May 2016 16:42:23 +0200 Subject: [PATCH 22/61] removed shebang and changed mode for fail2banregex.py --- fail2ban/client/fail2banregex.py | 1 - 1 file changed, 1 deletion(-) mode change 100755 => 100644 fail2ban/client/fail2banregex.py diff --git a/fail2ban/client/fail2banregex.py b/fail2ban/client/fail2banregex.py old mode 100755 new mode 100644 index fae4f354..e2222792 --- a/fail2ban/client/fail2banregex.py +++ b/fail2ban/client/fail2banregex.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- # vi: set ft=python sts=4 ts=4 sw=4 noet : # From c6f63c7263741403d24faaf4eb59f716fea205c5 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 17 May 2016 18:06:46 +0200 Subject: [PATCH 23/61] jailreader: support multiple option groups, syntax `action = act[p1=...][p2=...]` + test case for it (see gh-1425, gh-1429) --- fail2ban/client/jailreader.py | 7 ++++++- fail2ban/tests/clientreadertestcase.py | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py index fda5d40c..98086cf4 100644 --- a/fail2ban/client/jailreader.py +++ b/fail2ban/client/jailreader.py @@ -42,9 +42,14 @@ logSys = getLogger(__name__) class JailReader(ConfigReader): + # regex, to extract list of options: optionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$") + # regex, to iterate over single option in option list, syntax: + # `action = act[p1="...", p2='...', p3=...]`, where the p3=... not contains `,` or ']' + # since v0.10 separator extended with `]\s*[` for support of multiple option groups, syntax + # `action = act[p1=...][p2=...]` optionExtractRE = re.compile( - r'([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,]*))(?:,|$)') + r'([\w\-_\.]+)=(?:"([^"]*)"|\'([^\']*)\'|([^,\]]*))(?:,|\]\s*\[|$)') def __init__(self, name, force_enable=False, **kwargs): ConfigReader.__init__(self, **kwargs) diff --git a/fail2ban/tests/clientreadertestcase.py b/fail2ban/tests/clientreadertestcase.py index 5e66e25e..521fda2e 100644 --- a/fail2ban/tests/clientreadertestcase.py +++ b/fail2ban/tests/clientreadertestcase.py @@ -255,6 +255,13 @@ class JailReaderTest(LogCaptureTestCase): result = JailReader.extractOptions(option) self.assertEqual(expected, result) + # And multiple groups (`][` instead of `,`) + result = JailReader.extractOptions(option.replace(',', '][')) + expected2 = (expected[0], + dict((k, v.replace(',', '][')) for k, v in expected[1].iteritems()) + ) + self.assertEqual(expected2, result) + def testVersionAgent(self): jail = JailReader('blocklisttest', force_enable=True, basedir=CONFIG_DIR) # emulate jail.read(), because such jail not exists: From 3e1328c83b2d6fe9b444df1b13a74738a3c78123 Mon Sep 17 00:00:00 2001 From: Alexander Koeppe Date: Sun, 15 May 2016 22:30:07 +0200 Subject: [PATCH 24/61] split PF config files between all- and multi port --- config/action.d/pf-allports.conf | 73 +++++++++++++++++++ .../action.d/{pf.conf => pf-multiport.conf} | 6 -- 2 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 config/action.d/pf-allports.conf rename config/action.d/{pf.conf => pf-multiport.conf} (95%) diff --git a/config/action.d/pf-allports.conf b/config/action.d/pf-allports.conf new file mode 100644 index 00000000..e77bea43 --- /dev/null +++ b/config/action.d/pf-allports.conf @@ -0,0 +1,73 @@ +# Fail2Ban configuration file +# +# OpenBSD pf ban/unban +# +# Author: Nick Hilliard +# Modified by: Alexander Koeppe making PF work seamless and with IPv4 and IPv6 +# +# + +[Definition] + +# Option: actionstart +# Notes.: command executed once at the start of Fail2Ban. +# Values: CMD +# +# we don't enable PF automatically; to enable run pfctl -e +# or add `pf_enable="YES"` to /etc/rc.conf (tested on FreeBSD) +actionstart = echo "table <-> persist counters" | pfctl -f- + echo "block proto from <-> to any" | pfctl -f- + + +# Option: actionstop +# Notes.: command executed once at the end of Fail2Ban +# Values: CMD +# +# we only disable PF rules we've installed prior +actionstop = pfctl -sr 2>/dev/null | grep -v - | pfctl -f- + pfctl -t - -T flush + pfctl -t - -T kill + + +# Option: actioncheck +# Notes.: command executed once before each actionban command +# Values: CMD +# +actioncheck = pfctl -sr | grep -q - + + +# Option: actionban +# Notes.: command executed when banning an IP. Take care that the +# command is executed with Fail2Ban user rights. +# Tags: IP address +# number of failures +#