Merge pull request #1742 from sebres/0.10-actionstart-on-demand

0.10 - Execution of `actionstart` on demand (fixes gh-1741)
pull/1743/head
Serg G. Brester 8 years ago committed by GitHub
commit 4dcdcc3002

@ -18,6 +18,9 @@
actionstart = echo "table <<tablename>-<name>> persist counters" | pfctl -f- actionstart = echo "table <<tablename>-<name>> persist counters" | pfctl -f-
echo "block proto <protocol> from <<tablename>-<name>> to <actiontype>" | pfctl -f- echo "block proto <protocol> from <<tablename>-<name>> to <actiontype>" | pfctl -f-
# Option: start_on_demand - to start action on demand
# Example: `action=pf[actionstart_on_demand=true]`
actionstart_on_demand = false
# Option: actionstop # Option: actionstop
# Notes.: command executed once at the end of Fail2Ban # Notes.: command executed once at the end of Fail2Ban
@ -71,8 +74,6 @@ tablename = f2b
# #
protocol = tcp protocol = tcp
# Option: actiontype # Option: actiontype
# Notes.: defines additions to the blocking rule # Notes.: defines additions to the blocking rule
# Values: leave empty to block all attempts from the host # Values: leave empty to block all attempts from the host

@ -38,6 +38,7 @@ class ActionReader(DefinitionInitConfigReader):
_configOpts = { _configOpts = {
"actionstart": ["string", None], "actionstart": ["string", None],
"actionstart_on_demand": ["string", None],
"actionstop": ["string", None], "actionstop": ["string", None],
"actionreload": ["string", None], "actionreload": ["string", None],
"actioncheck": ["string", None], "actioncheck": ["string", None],
@ -73,8 +74,10 @@ class ActionReader(DefinitionInitConfigReader):
opts = self.getCombined( opts = self.getCombined(
ignore=CommandAction._escapedTags | set(('timeout', 'bantime'))) ignore=CommandAction._escapedTags | set(('timeout', 'bantime')))
# type-convert only after combined (otherwise boolean converting prevents substitution): # type-convert only after combined (otherwise boolean converting prevents substitution):
if opts.get('norestored'): for o in ('norestored', 'actionstart_on_demand'):
opts['norestored'] = self._convert_to_boolean(opts['norestored']) if opts.get(o):
opts[o] = self._convert_to_boolean(opts[o])
# stream-convert: # stream-convert:
head = ["set", self._jailName] head = ["set", self._jailName]
stream = list() stream = list()

@ -50,6 +50,8 @@ allowed_ipv6 = True
# capture groups from filter for map to ticket data: # capture groups from filter for map to ticket data:
FCUSTAG_CRE = re.compile(r'<F-([A-Z0-9_\-]+)>'); # currently uppercase only FCUSTAG_CRE = re.compile(r'<F-([A-Z0-9_\-]+)>'); # currently uppercase only
CONDITIONAL_FAM_RE = re.compile(r"^(\w+)\?(family)=")
# New line, space # New line, space
ADD_REPL_TAGS = { ADD_REPL_TAGS = {
"br": "\n", "br": "\n",
@ -201,17 +203,17 @@ class ActionBase(object):
self._name = name self._name = name
self._logSys = getLogger("fail2ban.%s" % self.__class__.__name__) self._logSys = getLogger("fail2ban.%s" % self.__class__.__name__)
def start(self): def start(self): # pragma: no cover - abstract
"""Executed when the jail/action is started. """Executed when the jail/action is started.
""" """
pass pass
def stop(self): def stop(self): # pragma: no cover - abstract
"""Executed when the jail/action is stopped. """Executed when the jail/action is stopped.
""" """
pass pass
def ban(self, aInfo): def ban(self, aInfo): # pragma: no cover - abstract
"""Executed when a ban occurs. """Executed when a ban occurs.
Parameters Parameters
@ -222,7 +224,7 @@ class ActionBase(object):
""" """
pass pass
def unban(self, aInfo): def unban(self, aInfo): # pragma: no cover - abstract
"""Executed when a ban expires. """Executed when a ban expires.
Parameters Parameters
@ -290,6 +292,7 @@ class CommandAction(ActionBase):
super(CommandAction, self).__init__(jail, name) super(CommandAction, self).__init__(jail, name)
self.__init = 1 self.__init = 1
self.__properties = None self.__properties = None
self.__started = {}
self.__substCache = {} self.__substCache = {}
self.clearAllParams() self.clearAllParams()
self._logSys.debug("Created %s" % self.__class__) self._logSys.debug("Created %s" % self.__class__)
@ -342,7 +345,11 @@ class CommandAction(ActionBase):
def _substCache(self): def _substCache(self):
return self.__substCache return self.__substCache
def _executeOperation(self, tag, operation): def _getOperation(self, tag, family):
return self.replaceTag(tag, self._properties,
conditional=('family=' + family), cache=self.__substCache)
def _executeOperation(self, tag, operation, family=[]):
"""Executes the operation commands (like "actionstart", "actionstop", etc). """Executes the operation commands (like "actionstart", "actionstop", etc).
Replace the tags in the action command with actions properties Replace the tags in the action command with actions properties
@ -352,14 +359,14 @@ class CommandAction(ActionBase):
res = True res = True
try: try:
# common (resp. ipv4): # common (resp. ipv4):
startCmd = self.replaceTag(tag, self._properties, startCmd = None
conditional='family=inet4', cache=self.__substCache) if not family or 'inet4' in family:
startCmd = self._getOperation(tag, 'inet4')
if startCmd: if startCmd:
res &= self.executeCmd(startCmd, self.timeout) res &= self.executeCmd(startCmd, self.timeout)
# start ipv6 actions if available: # start ipv6 actions if available:
if allowed_ipv6: if allowed_ipv6 and (not family or 'inet6' in family):
startCmd6 = self.replaceTag(tag, self._properties, startCmd6 = self._getOperation(tag, 'inet6')
conditional='family=inet6', cache=self.__substCache)
if startCmd6 and startCmd6 != startCmd: if startCmd6 and startCmd6 != startCmd:
res &= self.executeCmd(startCmd6, self.timeout) res &= self.executeCmd(startCmd6, self.timeout)
if not res: if not res:
@ -367,13 +374,34 @@ class CommandAction(ActionBase):
except ValueError as e: except ValueError as e:
raise RuntimeError("Error %s action %s/%s: %r" % (operation, self._jail, self._name, e)) raise RuntimeError("Error %s action %s/%s: %r" % (operation, self._jail, self._name, e))
def start(self): COND_FAMILIES = {'inet4':1, 'inet6':1}
@property
def _startOnDemand(self):
"""Checks the action depends on family (conditional)"""
v = self._properties.get('actionstart_on_demand')
if v is None:
v = False
for n in self._properties:
if CONDITIONAL_FAM_RE.match(n):
v = True
break
self._properties['actionstart_on_demand'] = v
return v
def start(self, family=[]):
"""Executes the "actionstart" command. """Executes the "actionstart" command.
Replace the tags in the action command with actions properties Replace the tags in the action command with actions properties
and executes the resulting command. and executes the resulting command.
""" """
return self._executeOperation('<actionstart>', 'starting') if not family:
# check the action depends on family (conditional):
if self._startOnDemand:
return True
elif self.__started.get(family): # pragma: no cover - normally unreachable
return True
return self._executeOperation('<actionstart>', 'starting', family=family)
def ban(self, aInfo): def ban(self, aInfo):
"""Executes the "actionban" command. """Executes the "actionban" command.
@ -387,6 +415,20 @@ class CommandAction(ActionBase):
Dictionary which includes information in relation to Dictionary which includes information in relation to
the ban. the ban.
""" """
# if we should start the action on demand (conditional by family):
if self._startOnDemand:
family = aInfo.get('family')
if not self.__started.get(family):
self.start(family)
self.__started[family] = 1
# mark also another families as "started" (-1), if they are equal
# (on demand, but the same for ipv4 and ipv6):
cmd = self._getOperation('<actionstart>', family)
for f in CommandAction.COND_FAMILIES:
if f != family and not self.__started.get(f):
if cmd == self._getOperation('<actionstart>', f):
self.__started[f] = -1
# ban:
if not self._processCmd('<actionban>', aInfo): if not self._processCmd('<actionban>', aInfo):
raise RuntimeError("Error banning %(ip)s" % aInfo) raise RuntimeError("Error banning %(ip)s" % aInfo)
@ -411,7 +453,16 @@ class CommandAction(ActionBase):
Replaces the tags in the action command with actions properties Replaces the tags in the action command with actions properties
and executes the resulting command. and executes the resulting command.
""" """
return self._executeOperation('<actionstop>', 'stopping') family = []
# cumulate started families, if started on demand (conditional):
if self._startOnDemand:
for f in CommandAction.COND_FAMILIES:
if self.__started.get(f) == 1: # only real started:
family.append(f)
self.__started[f] = 0
# if no started (on demand) actions:
if not family: return True
return self._executeOperation('<actionstop>', 'stopping', family=family)
def reload(self, **kwargs): def reload(self, **kwargs):
"""Executes the "actionreload" command. """Executes the "actionreload" command.

@ -1074,16 +1074,16 @@ class ServerConfigReaderTests(LogCaptureTestCase):
action.start() action.start()
# test ban ip4 : # test ban ip4 :
logSys.debug('# === ban-ipv4 ==='); self.pruneLog() logSys.debug('# === ban-ipv4 ==='); self.pruneLog()
action.ban({'ip': IPAddr('192.0.2.1')}) action.ban({'ip': IPAddr('192.0.2.1'), 'family': 'inet4'})
# test unban ip4 : # test unban ip4 :
logSys.debug('# === unban ipv4 ==='); self.pruneLog() logSys.debug('# === unban ipv4 ==='); self.pruneLog()
action.unban({'ip': IPAddr('192.0.2.1')}) action.unban({'ip': IPAddr('192.0.2.1'), 'family': 'inet4'})
# test ban ip6 : # test ban ip6 :
logSys.debug('# === ban ipv6 ==='); self.pruneLog() logSys.debug('# === ban ipv6 ==='); self.pruneLog()
action.ban({'ip': IPAddr('2001:DB8::')}) action.ban({'ip': IPAddr('2001:DB8::'), 'family': 'inet6'})
# test unban ip6 : # test unban ip6 :
logSys.debug('# === unban ipv6 ==='); self.pruneLog() logSys.debug('# === unban ipv6 ==='); self.pruneLog()
action.unban({'ip': IPAddr('2001:DB8::')}) action.unban({'ip': IPAddr('2001:DB8::'), 'family': 'inet6'})
# test stop : # test stop :
logSys.debug('# === stop ==='); self.pruneLog() logSys.debug('# === stop ==='); self.pruneLog()
action.stop() action.stop()
@ -1185,10 +1185,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# iptables-multiport -- # iptables-multiport --
('j-w-iptables-mp', 'iptables-multiport[name=%(__name__)s, bantime="10m", port="http,https", protocol="tcp", chain="INPUT"]', { ('j-w-iptables-mp', 'iptables-multiport[name=%(__name__)s, bantime="10m", port="http,https", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'), 'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': ( 'ip4-start': (
"`iptables -w -N f2b-j-w-iptables-mp`", "`iptables -w -N f2b-j-w-iptables-mp`",
"`iptables -w -A f2b-j-w-iptables-mp -j RETURN`", "`iptables -w -A f2b-j-w-iptables-mp -j RETURN`",
"`iptables -w -I INPUT -p tcp -m multiport --dports http,https -j f2b-j-w-iptables-mp`", "`iptables -w -I INPUT -p tcp -m multiport --dports http,https -j f2b-j-w-iptables-mp`",
),
'ip6-start': (
"`ip6tables -w -N f2b-j-w-iptables-mp`", "`ip6tables -w -N f2b-j-w-iptables-mp`",
"`ip6tables -w -A f2b-j-w-iptables-mp -j RETURN`", "`ip6tables -w -A f2b-j-w-iptables-mp -j RETURN`",
"`ip6tables -w -I INPUT -p tcp -m multiport --dports http,https -j f2b-j-w-iptables-mp`", "`ip6tables -w -I INPUT -p tcp -m multiport --dports http,https -j f2b-j-w-iptables-mp`",
@ -1223,10 +1225,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# iptables-allports -- # iptables-allports --
('j-w-iptables-ap', 'iptables-allports[name=%(__name__)s, bantime="10m", protocol="tcp", chain="INPUT"]', { ('j-w-iptables-ap', 'iptables-allports[name=%(__name__)s, bantime="10m", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'), 'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': ( 'ip4-start': (
"`iptables -w -N f2b-j-w-iptables-ap`", "`iptables -w -N f2b-j-w-iptables-ap`",
"`iptables -w -A f2b-j-w-iptables-ap -j RETURN`", "`iptables -w -A f2b-j-w-iptables-ap -j RETURN`",
"`iptables -w -I INPUT -p tcp -j f2b-j-w-iptables-ap`", "`iptables -w -I INPUT -p tcp -j f2b-j-w-iptables-ap`",
),
'ip6-start': (
"`ip6tables -w -N f2b-j-w-iptables-ap`", "`ip6tables -w -N f2b-j-w-iptables-ap`",
"`ip6tables -w -A f2b-j-w-iptables-ap -j RETURN`", "`ip6tables -w -A f2b-j-w-iptables-ap -j RETURN`",
"`ip6tables -w -I INPUT -p tcp -j f2b-j-w-iptables-ap`", "`ip6tables -w -I INPUT -p tcp -j f2b-j-w-iptables-ap`",
@ -1261,9 +1265,11 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# iptables-ipset-proto6 -- # iptables-ipset-proto6 --
('j-w-iptables-ipset', 'iptables-ipset-proto6[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', { ('j-w-iptables-ipset', 'iptables-ipset-proto6[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': (' f2b-j-w-iptables-ipset ',), 'ip6': (' f2b-j-w-iptables-ipset6 ',), 'ip4': (' f2b-j-w-iptables-ipset ',), 'ip6': (' f2b-j-w-iptables-ipset6 ',),
'start': ( 'ip4-start': (
"`ipset create f2b-j-w-iptables-ipset hash:ip timeout 600`", "`ipset create f2b-j-w-iptables-ipset hash:ip timeout 600`",
"`iptables -w -I INPUT -p tcp -m multiport --dports http -m set --match-set f2b-j-w-iptables-ipset src -j REJECT --reject-with icmp-port-unreachable`", "`iptables -w -I INPUT -p tcp -m multiport --dports http -m set --match-set f2b-j-w-iptables-ipset src -j REJECT --reject-with icmp-port-unreachable`",
),
'ip6-start': (
"`ipset create f2b-j-w-iptables-ipset6 hash:ip timeout 600 family inet6`", "`ipset create f2b-j-w-iptables-ipset6 hash:ip timeout 600 family inet6`",
"`ip6tables -w -I INPUT -p tcp -m multiport --dports http -m set --match-set f2b-j-w-iptables-ipset6 src -j REJECT --reject-with icmp6-port-unreachable`", "`ip6tables -w -I INPUT -p tcp -m multiport --dports http -m set --match-set f2b-j-w-iptables-ipset6 src -j REJECT --reject-with icmp6-port-unreachable`",
), ),
@ -1293,9 +1299,11 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# iptables-ipset-proto6-allports -- # iptables-ipset-proto6-allports --
('j-w-iptables-ipset-ap', 'iptables-ipset-proto6-allports[name=%(__name__)s, bantime="10m", chain="INPUT"]', { ('j-w-iptables-ipset-ap', 'iptables-ipset-proto6-allports[name=%(__name__)s, bantime="10m", chain="INPUT"]', {
'ip4': (' f2b-j-w-iptables-ipset-ap ',), 'ip6': (' f2b-j-w-iptables-ipset-ap6 ',), 'ip4': (' f2b-j-w-iptables-ipset-ap ',), 'ip6': (' f2b-j-w-iptables-ipset-ap6 ',),
'start': ( 'ip4-start': (
"`ipset create f2b-j-w-iptables-ipset-ap hash:ip timeout 600`", "`ipset create f2b-j-w-iptables-ipset-ap hash:ip timeout 600`",
"`iptables -w -I INPUT -m set --match-set f2b-j-w-iptables-ipset-ap src -j REJECT --reject-with icmp-port-unreachable`", "`iptables -w -I INPUT -m set --match-set f2b-j-w-iptables-ipset-ap src -j REJECT --reject-with icmp-port-unreachable`",
),
'ip6-start': (
"`ipset create f2b-j-w-iptables-ipset-ap6 hash:ip timeout 600 family inet6`", "`ipset create f2b-j-w-iptables-ipset-ap6 hash:ip timeout 600 family inet6`",
"`ip6tables -w -I INPUT -m set --match-set f2b-j-w-iptables-ipset-ap6 src -j REJECT --reject-with icmp6-port-unreachable`", "`ip6tables -w -I INPUT -m set --match-set f2b-j-w-iptables-ipset-ap6 src -j REJECT --reject-with icmp6-port-unreachable`",
), ),
@ -1325,10 +1333,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# iptables -- # iptables --
('j-w-iptables', 'iptables[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', { ('j-w-iptables', 'iptables[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'), 'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': ( 'ip4-start': (
"`iptables -w -N f2b-j-w-iptables`", "`iptables -w -N f2b-j-w-iptables`",
"`iptables -w -A f2b-j-w-iptables -j RETURN`", "`iptables -w -A f2b-j-w-iptables -j RETURN`",
"`iptables -w -I INPUT -p tcp --dport http -j f2b-j-w-iptables`", "`iptables -w -I INPUT -p tcp --dport http -j f2b-j-w-iptables`",
),
'ip6-start': (
"`ip6tables -w -N f2b-j-w-iptables`", "`ip6tables -w -N f2b-j-w-iptables`",
"`ip6tables -w -A f2b-j-w-iptables -j RETURN`", "`ip6tables -w -A f2b-j-w-iptables -j RETURN`",
"`ip6tables -w -I INPUT -p tcp --dport http -j f2b-j-w-iptables`", "`ip6tables -w -I INPUT -p tcp --dport http -j f2b-j-w-iptables`",
@ -1363,10 +1373,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# iptables-new -- # iptables-new --
('j-w-iptables-new', 'iptables-new[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', { ('j-w-iptables-new', 'iptables-new[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'), 'ip4': ('`iptables ', 'icmp-port-unreachable'), 'ip6': ('`ip6tables ', 'icmp6-port-unreachable'),
'start': ( 'ip4-start': (
"`iptables -w -N f2b-j-w-iptables-new`", "`iptables -w -N f2b-j-w-iptables-new`",
"`iptables -w -A f2b-j-w-iptables-new -j RETURN`", "`iptables -w -A f2b-j-w-iptables-new -j RETURN`",
"`iptables -w -I INPUT -m state --state NEW -p tcp --dport http -j f2b-j-w-iptables-new`", "`iptables -w -I INPUT -m state --state NEW -p tcp --dport http -j f2b-j-w-iptables-new`",
),
'ip6-start': (
"`ip6tables -w -N f2b-j-w-iptables-new`", "`ip6tables -w -N f2b-j-w-iptables-new`",
"`ip6tables -w -A f2b-j-w-iptables-new -j RETURN`", "`ip6tables -w -A f2b-j-w-iptables-new -j RETURN`",
"`ip6tables -w -I INPUT -m state --state NEW -p tcp --dport http -j f2b-j-w-iptables-new`", "`ip6tables -w -I INPUT -m state --state NEW -p tcp --dport http -j f2b-j-w-iptables-new`",
@ -1401,8 +1413,10 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# iptables-xt_recent-echo -- # iptables-xt_recent-echo --
('j-w-iptables-xtre', 'iptables-xt_recent-echo[name=%(__name__)s, bantime="10m", chain="INPUT"]', { ('j-w-iptables-xtre', 'iptables-xt_recent-echo[name=%(__name__)s, bantime="10m", chain="INPUT"]', {
'ip4': ('`iptables ', '/f2b-j-w-iptables-xtre`'), 'ip6': ('`ip6tables ', '/f2b-j-w-iptables-xtre6`'), 'ip4': ('`iptables ', '/f2b-j-w-iptables-xtre`'), 'ip6': ('`ip6tables ', '/f2b-j-w-iptables-xtre6`'),
'start': ( 'ip4-start': (
"`if [ `id -u` -eq 0 ];then iptables -w -I INPUT -m recent --update --seconds 3600 --name f2b-j-w-iptables-xtre -j REJECT --reject-with icmp-port-unreachable;fi`", "`if [ `id -u` -eq 0 ];then iptables -w -I INPUT -m recent --update --seconds 3600 --name f2b-j-w-iptables-xtre -j REJECT --reject-with icmp-port-unreachable;fi`",
),
'ip6-start': (
"`if [ `id -u` -eq 0 ];then ip6tables -w -I INPUT -m recent --update --seconds 3600 --name f2b-j-w-iptables-xtre6 -j REJECT --reject-with icmp6-port-unreachable;fi`", "`if [ `id -u` -eq 0 ];then ip6tables -w -I INPUT -m recent --update --seconds 3600 --name f2b-j-w-iptables-xtre6 -j REJECT --reject-with icmp6-port-unreachable;fi`",
), ),
'stop': ( 'stop': (
@ -1431,7 +1445,7 @@ class ServerConfigReaderTests(LogCaptureTestCase):
), ),
}), }),
# pf default -- multiport on default port (tag <port> set in jail.conf, but not in this test case) # pf default -- multiport on default port (tag <port> set in jail.conf, but not in this test case)
('j-w-pf', 'pf[name=%(__name__)s]', { ('j-w-pf', 'pf[name=%(__name__)s, actionstart_on_demand=false]', {
'ip4': (), 'ip6': (), 'ip4': (), 'ip6': (),
'start': ( 'start': (
'`echo "table <f2b-j-w-pf> persist counters" | pfctl -f-`', '`echo "table <f2b-j-w-pf> persist counters" | pfctl -f-`',
@ -1468,13 +1482,14 @@ class ServerConfigReaderTests(LogCaptureTestCase):
'ip6-ban': ("`pfctl -t f2b-j-w-pf-mp -T add 2001:db8::`",), 'ip6-ban': ("`pfctl -t f2b-j-w-pf-mp -T add 2001:db8::`",),
'ip6-unban': ("`pfctl -t f2b-j-w-pf-mp -T delete 2001:db8::`",), 'ip6-unban': ("`pfctl -t f2b-j-w-pf-mp -T delete 2001:db8::`",),
}), }),
# pf allports -- # pf allports -- test additionally "actionstart_on_demand" was set to true
('j-w-pf-ap', 'pf[actiontype=<allports>][name=%(__name__)s]', { ('j-w-pf-ap', 'pf[actiontype=<allports>, actionstart_on_demand=true][name=%(__name__)s]', {
'ip4': (), 'ip6': (), 'ip4': (), 'ip6': (),
'start': ( 'ip4-start': (
'`echo "table <f2b-j-w-pf-ap> persist counters" | pfctl -f-`', '`echo "table <f2b-j-w-pf-ap> persist counters" | pfctl -f-`',
'`echo "block proto tcp from <f2b-j-w-pf-ap> to any" | pfctl -f-`', '`echo "block proto tcp from <f2b-j-w-pf-ap> to any" | pfctl -f-`',
), ),
'ip6-start': (), # the same as ipv4
'stop': ( 'stop': (
'`pfctl -sr 2>/dev/null | grep -v f2b-j-w-pf-ap | pfctl -f-`', '`pfctl -sr 2>/dev/null | grep -v f2b-j-w-pf-ap | pfctl -f-`',
'`pfctl -t f2b-j-w-pf-ap -T flush`', '`pfctl -t f2b-j-w-pf-ap -T flush`',
@ -1490,10 +1505,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# firewallcmd-multiport -- # firewallcmd-multiport --
('j-w-fwcmd-mp', 'firewallcmd-multiport[name=%(__name__)s, bantime="10m", port="http,https", protocol="tcp", chain="INPUT"]', { ('j-w-fwcmd-mp', 'firewallcmd-multiport[name=%(__name__)s, bantime="10m", port="http,https", protocol="tcp", chain="INPUT"]', {
'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'), 'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'),
'start': ( 'ip4-start': (
"`firewall-cmd --direct --add-chain ipv4 filter f2b-j-w-fwcmd-mp`", "`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 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-rule ipv4 filter INPUT 0 -m conntrack --ctstate NEW -p tcp -m multiport --dports http,https -j f2b-j-w-fwcmd-mp`",
),
'ip6-start': (
"`firewall-cmd --direct --add-chain ipv6 filter 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 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`", "`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`",
@ -1528,10 +1545,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# firewallcmd-allports -- # firewallcmd-allports --
('j-w-fwcmd-ap', 'firewallcmd-allports[name=%(__name__)s, bantime="10m", protocol="tcp", chain="INPUT"]', { ('j-w-fwcmd-ap', 'firewallcmd-allports[name=%(__name__)s, bantime="10m", protocol="tcp", chain="INPUT"]', {
'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'), 'ip4': (' ipv4 ', 'icmp-port-unreachable'), 'ip6': (' ipv6 ', 'icmp6-port-unreachable'),
'start': ( 'ip4-start': (
"`firewall-cmd --direct --add-chain ipv4 filter f2b-j-w-fwcmd-ap`", "`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 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-rule ipv4 filter INPUT 0 -j f2b-j-w-fwcmd-ap`",
),
'ip6-start': (
"`firewall-cmd --direct --add-chain ipv6 filter 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 f2b-j-w-fwcmd-ap 1000 -j RETURN`",
"`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -j f2b-j-w-fwcmd-ap`", "`firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -j f2b-j-w-fwcmd-ap`",
@ -1566,9 +1585,11 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# firewallcmd-ipset -- # firewallcmd-ipset --
('j-w-fwcmd-ipset', 'firewallcmd-ipset[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', { ('j-w-fwcmd-ipset', 'firewallcmd-ipset[name=%(__name__)s, bantime="10m", port="http", protocol="tcp", chain="INPUT"]', {
'ip4': (' f2b-j-w-fwcmd-ipset ',), 'ip6': (' f2b-j-w-fwcmd-ipset6 ',), 'ip4': (' f2b-j-w-fwcmd-ipset ',), 'ip6': (' f2b-j-w-fwcmd-ipset6 ',),
'start': ( 'ip4-start': (
"`ipset create f2b-j-w-fwcmd-ipset hash:ip timeout 600`", "`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`", "`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`",
),
'ip6-start': (
"`ipset create f2b-j-w-fwcmd-ipset6 hash:ip timeout 600`", "`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`", "`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`",
), ),
@ -1614,6 +1635,10 @@ class ServerConfigReaderTests(LogCaptureTestCase):
jails = server._Server__jails jails = server._Server__jails
tickets = {
'ip4': BanTicket('192.0.2.1'),
'ip6': BanTicket('2001:DB8::'),
}
for jail, act, tests in testJailsActions: for jail, act, tests in testJailsActions:
# print(jail, jails[jail]) # print(jail, jails[jail])
for a in jails[jail].actions: for a in jails[jail].actions:
@ -1627,25 +1652,36 @@ class ServerConfigReaderTests(LogCaptureTestCase):
# test start : # test start :
self.pruneLog('# === start ===') self.pruneLog('# === start ===')
action.start() action.start()
if tests.get('start'):
self.assertLogged(*tests['start'], all=True) self.assertLogged(*tests['start'], all=True)
else:
self.assertNotLogged(*tests['ip4-start']+tests['ip6-start'], all=True)
ainfo = {
'ip4': _actions.Actions.ActionInfo(tickets['ip4'], jails[jail]),
'ip6': _actions.Actions.ActionInfo(tickets['ip6'], jails[jail]),
}
# test ban ip4 : # test ban ip4 :
self.pruneLog('# === ban-ipv4 ===') self.pruneLog('# === ban-ipv4 ===')
action.ban({'ip': IPAddr('192.0.2.1')}) action.ban(ainfo['ip4'])
if tests.get('ip4-start'): self.assertLogged(*tests['ip4-start'], all=True)
if tests.get('ip6-start'): self.assertNotLogged(*tests['ip6-start'], all=True)
self.assertLogged(*tests['ip4-check']+tests['ip4-ban'], all=True) self.assertLogged(*tests['ip4-check']+tests['ip4-ban'], all=True)
self.assertNotLogged(*tests['ip6'], all=True) self.assertNotLogged(*tests['ip6'], all=True)
# test unban ip4 : # test unban ip4 :
self.pruneLog('# === unban ipv4 ===') self.pruneLog('# === unban ipv4 ===')
action.unban({'ip': IPAddr('192.0.2.1')}) action.unban(ainfo['ip4'])
self.assertLogged(*tests['ip4-check']+tests['ip4-unban'], all=True) self.assertLogged(*tests['ip4-check']+tests['ip4-unban'], all=True)
self.assertNotLogged(*tests['ip6'], all=True) self.assertNotLogged(*tests['ip6'], all=True)
# test ban ip6 : # test ban ip6 :
self.pruneLog('# === ban ipv6 ===') self.pruneLog('# === ban ipv6 ===')
action.ban({'ip': IPAddr('2001:DB8::')}) action.ban(ainfo['ip6'])
if tests.get('ip6-start'): self.assertLogged(*tests['ip6-start'], all=True)
if tests.get('ip4-start'): self.assertNotLogged(*tests['ip4-start'], all=True)
self.assertLogged(*tests['ip6-check']+tests['ip6-ban'], all=True) self.assertLogged(*tests['ip6-check']+tests['ip6-ban'], all=True)
self.assertNotLogged(*tests['ip4'], all=True) self.assertNotLogged(*tests['ip4'], all=True)
# test unban ip6 : # test unban ip6 :
self.pruneLog('# === unban ipv6 ===') self.pruneLog('# === unban ipv6 ===')
action.unban({'ip': IPAddr('2001:DB8::')}) action.unban(ainfo['ip6'])
self.assertLogged(*tests['ip6-check']+tests['ip6-unban'], all=True) self.assertLogged(*tests['ip6-check']+tests['ip6-unban'], all=True)
self.assertNotLogged(*tests['ip4'], all=True) self.assertNotLogged(*tests['ip4'], all=True)
# test stop : # test stop :

Loading…
Cancel
Save