mirror of https://github.com/fail2ban/fail2ban
Merge pull request #1742 from sebres/0.10-actionstart-on-demand
0.10 - Execution of `actionstart` on demand (fixes gh-1741)pull/1743/head
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:
|
||||||
if startCmd:
|
startCmd = self._getOperation(tag, 'inet4')
|
||||||
res &= self.executeCmd(startCmd, self.timeout)
|
if startCmd:
|
||||||
|
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()
|
||||||
self.assertLogged(*tests['start'], all=True)
|
if tests.get('start'):
|
||||||
|
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…
Reference in New Issue