mirror of https://github.com/fail2ban/fail2ban
actions support IPv6 now:
- introduced "conditional" sections, see for example `[Init?family=inet6]`; - iptables-common and other iptables config(s) made IPv6 capable; - several small code optimizations; * all test cases passed (py3.x compatible);pull/1414/head
parent
75028585c0
commit
504e5ba6f2
|
@ -6,6 +6,9 @@
|
||||||
# used in all iptables based actions by default.
|
# used in all iptables based actions by default.
|
||||||
#
|
#
|
||||||
# The user can override the defaults in iptables-common.local
|
# The user can override the defaults in iptables-common.local
|
||||||
|
#
|
||||||
|
# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
|
||||||
|
# made config file IPv6 capable (see new section Init?family=inet6)
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
|
@ -13,6 +16,7 @@ after = iptables-blocktype.local
|
||||||
iptables-common.local
|
iptables-common.local
|
||||||
# iptables-blocktype.local is obsolete
|
# iptables-blocktype.local is obsolete
|
||||||
|
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
# Option: chain
|
# Option: chain
|
||||||
|
@ -62,3 +66,19 @@ lockingopt = -w
|
||||||
# Notes.: Actual command to be executed, including common to all calls options
|
# Notes.: Actual command to be executed, including common to all calls options
|
||||||
# Values: STRING
|
# Values: STRING
|
||||||
iptables = iptables <lockingopt>
|
iptables = iptables <lockingopt>
|
||||||
|
|
||||||
|
|
||||||
|
[Init?family=inet6]
|
||||||
|
|
||||||
|
# Option: blocktype (ipv6)
|
||||||
|
# Note: This is what the action does with rules. This can be any jump target
|
||||||
|
# as per the iptables man page (section 8). Common values are DROP
|
||||||
|
# REJECT, REJECT --reject-with icmp6-port-unreachable
|
||||||
|
# Values: STRING
|
||||||
|
blocktype = REJECT --reject-with icmp6-port-unreachable
|
||||||
|
|
||||||
|
# Option: iptables (ipv6)
|
||||||
|
# Notes.: Actual command to be executed, including common to all calls options
|
||||||
|
# Values: STRING
|
||||||
|
iptables = ip6tables <lockingopt>
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
#
|
#
|
||||||
# If you are running on an older kernel you make need to patch in external
|
# If you are running on an older kernel you make need to patch in external
|
||||||
# modules which probably won't be protocol version 6.
|
# modules which probably won't be protocol version 6.
|
||||||
|
#
|
||||||
|
# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
|
||||||
|
# made config file IPv6 capable (see new section Init?family=inet6)
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
|
@ -23,16 +26,16 @@ before = iptables-common.conf
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
actionstart = ipset create <ipmset> hash:ip timeout <bantime><familyopt>
|
||||||
<iptables> -I <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
<iptables> -I <chain> -m set --match-set <ipmset> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = <iptables> -D <chain> -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = <iptables> -D <chain> -m set --match-set <ipmset> src -j <blocktype>
|
||||||
ipset flush f2b-<name>
|
ipset flush <ipmset>
|
||||||
ipset destroy f2b-<name>
|
ipset destroy <ipmset>
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -40,7 +43,7 @@ actionstop = <iptables> -D <chain> -m set --match-set f2b-<name> src -j <blockty
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = ipset add f2b-<name> <ip> timeout <bantime> -exist
|
actionban = ipset add <ipmset> <ip> timeout <bantime> -exist
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -48,7 +51,7 @@ actionban = ipset add f2b-<name> <ip> timeout <bantime> -exist
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = ipset del f2b-<name> <ip> -exist
|
actionunban = ipset del <ipmset> <ip> -exist
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
@ -57,3 +60,12 @@ actionunban = ipset del f2b-<name> <ip> -exist
|
||||||
# Values: [ NUM ] Default: 600
|
# Values: [ NUM ] Default: 600
|
||||||
#
|
#
|
||||||
bantime = 600
|
bantime = 600
|
||||||
|
|
||||||
|
ipmset = f2b-<name>
|
||||||
|
familyopt =
|
||||||
|
|
||||||
|
|
||||||
|
[Init?family=inet6]
|
||||||
|
|
||||||
|
ipmset = f2b-<name>6
|
||||||
|
familyopt = <sp>family inet6
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
#
|
#
|
||||||
# If you are running on an older kernel you make need to patch in external
|
# If you are running on an older kernel you make need to patch in external
|
||||||
# modules.
|
# modules.
|
||||||
|
#
|
||||||
|
# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
|
||||||
|
# made config file IPv6 capable (see new section Init?family=inet6)
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
|
@ -23,16 +26,16 @@ before = iptables-common.conf
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstart = ipset create f2b-<name> hash:ip timeout <bantime>
|
actionstart = ipset create <ipmset> hash:ip timeout <bantime><familyopt>
|
||||||
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set f2b-<name> src -j <blocktype>
|
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>
|
||||||
ipset flush f2b-<name>
|
ipset flush <ipmset>
|
||||||
ipset destroy f2b-<name>
|
ipset destroy <ipmset>
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -40,7 +43,7 @@ actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -m
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = ipset add f2b-<name> <ip> timeout <bantime> -exist
|
actionban = ipset add <ipmset> <ip> timeout <bantime> -exist
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -48,7 +51,7 @@ actionban = ipset add f2b-<name> <ip> timeout <bantime> -exist
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = ipset del f2b-<name> <ip> -exist
|
actionunban = ipset del <ipmset> <ip> -exist
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
@ -57,3 +60,12 @@ actionunban = ipset del f2b-<name> <ip> -exist
|
||||||
# Values: [ NUM ] Default: 600
|
# Values: [ NUM ] Default: 600
|
||||||
#
|
#
|
||||||
bantime = 600
|
bantime = 600
|
||||||
|
|
||||||
|
ipmset = f2b-<name>
|
||||||
|
familyopt =
|
||||||
|
|
||||||
|
|
||||||
|
[Init?family=inet6]
|
||||||
|
|
||||||
|
ipmset = f2b-<name>6
|
||||||
|
familyopt = <sp>family inet6
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
#
|
#
|
||||||
# Author: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
|
# Author: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
|
||||||
#
|
#
|
||||||
#
|
# Modified: Alexander Koeppe <format_c@online.de>, Serg G. Brester <serg.brester@sebres.de>
|
||||||
|
# made config file IPv6 capable
|
||||||
|
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
|
|
||||||
|
@ -22,30 +23,30 @@ before = iptables-common.conf
|
||||||
# iptables-persistent package).
|
# iptables-persistent package).
|
||||||
#
|
#
|
||||||
# Explanation of the rule below:
|
# Explanation of the rule below:
|
||||||
# Check if any packets coming from an IP on the f2b-<name>
|
# Check if any packets coming from an IP on the <iptname>
|
||||||
# list have been seen in the last 3600 seconds. If yes, update the
|
# list have been seen in the last 3600 seconds. If yes, update the
|
||||||
# timestamp for this IP and drop the packet. If not, let the packet
|
# timestamp for this IP and drop the packet. If not, let the packet
|
||||||
# through.
|
# through.
|
||||||
#
|
#
|
||||||
# Fail2ban inserts blacklisted hosts into the f2b-<name> list
|
# Fail2ban inserts blacklisted hosts into the <iptname> list
|
||||||
# and removes them from the list after some time, according to its
|
# and removes them from the list after some time, according to its
|
||||||
# own rules. The 3600 second timeout is independent and acts as a
|
# own rules. The 3600 second timeout is independent and acts as a
|
||||||
# safeguard in case the fail2ban process dies unexpectedly. The
|
# safeguard in case the fail2ban process dies unexpectedly. The
|
||||||
# shorter of the two timeouts actually matters.
|
# shorter of the two timeouts actually matters.
|
||||||
actionstart = if [ `id -u` -eq 0 ];then <iptables> -I <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
actionstart = if [ `id -u` -eq 0 ];then <iptables> -I <chain> -m recent --update --seconds 3600 --name <iptname> -j <blocktype>;fi
|
||||||
|
|
||||||
# Option: actionstop
|
# Option: actionstop
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionstop = echo / > /proc/net/xt_recent/f2b-<name>
|
actionstop = echo / > /proc/net/xt_recent/<iptname>
|
||||||
if [ `id -u` -eq 0 ];then <iptables> -D <chain> -m recent --update --seconds 3600 --name f2b-<name> -j <blocktype>;fi
|
if [ `id -u` -eq 0 ];then <iptables> -D <chain> -m recent --update --seconds 3600 --name <iptname> -j <blocktype>;fi
|
||||||
|
|
||||||
# Option: actioncheck
|
# Option: actioncheck
|
||||||
# Notes.: command executed once before each actionban command
|
# Notes.: command executed once before each actionban command
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actioncheck = test -e /proc/net/xt_recent/f2b-<name>
|
actioncheck = test -e /proc/net/xt_recent/<iptname>
|
||||||
|
|
||||||
# Option: actionban
|
# Option: actionban
|
||||||
# Notes.: command executed when banning an IP. Take care that the
|
# Notes.: command executed when banning an IP. Take care that the
|
||||||
|
@ -53,7 +54,7 @@ actioncheck = test -e /proc/net/xt_recent/f2b-<name>
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionban = echo +<ip> > /proc/net/xt_recent/f2b-<name>
|
actionban = echo +<ip> > /proc/net/xt_recent/<iptname>
|
||||||
|
|
||||||
# Option: actionunban
|
# Option: actionunban
|
||||||
# Notes.: command executed when unbanning an IP. Take care that the
|
# Notes.: command executed when unbanning an IP. Take care that the
|
||||||
|
@ -61,7 +62,12 @@ actionban = echo +<ip> > /proc/net/xt_recent/f2b-<name>
|
||||||
# Tags: See jail.conf(5) man page
|
# Tags: See jail.conf(5) man page
|
||||||
# Values: CMD
|
# Values: CMD
|
||||||
#
|
#
|
||||||
actionunban = echo -<ip> > /proc/net/xt_recent/f2b-<name>
|
actionunban = echo -<ip> > /proc/net/xt_recent/<iptname>
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
|
||||||
|
iptname = f2b-<name>
|
||||||
|
|
||||||
|
[Init?family=inet6]
|
||||||
|
|
||||||
|
iptname = f2b-<name>6
|
||||||
|
|
|
@ -25,6 +25,7 @@ __copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko'
|
||||||
__license__ = 'GPL'
|
__license__ = 'GPL'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from ..helpers import getLogger
|
from ..helpers import getLogger
|
||||||
|
|
||||||
|
@ -99,6 +100,8 @@ after = 1.conf
|
||||||
|
|
||||||
SECTION_NAME = "INCLUDES"
|
SECTION_NAME = "INCLUDES"
|
||||||
|
|
||||||
|
CONDITIONAL_RE = re.compile(r"^(\w+)(\?.+)$")
|
||||||
|
|
||||||
if sys.version_info >= (3,2):
|
if sys.version_info >= (3,2):
|
||||||
# overload constructor only for fancy new Python3's
|
# overload constructor only for fancy new Python3's
|
||||||
def __init__(self, share_config=None, *args, **kwargs):
|
def __init__(self, share_config=None, *args, **kwargs):
|
||||||
|
@ -225,7 +228,19 @@ after = 1.conf
|
||||||
# merge defaults and all sections to self:
|
# merge defaults and all sections to self:
|
||||||
alld.update(cfg.get_defaults())
|
alld.update(cfg.get_defaults())
|
||||||
for n, s in cfg.get_sections().iteritems():
|
for n, s in cfg.get_sections().iteritems():
|
||||||
if isinstance(s, dict):
|
curalls = alls
|
||||||
|
# conditional sections
|
||||||
|
cond = SafeConfigParserWithIncludes.CONDITIONAL_RE.match(n)
|
||||||
|
if cond:
|
||||||
|
n, cond = cond.groups()
|
||||||
|
s = s.copy()
|
||||||
|
try:
|
||||||
|
del(s['__name__'])
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
for k in s.keys():
|
||||||
|
v = s.pop(k)
|
||||||
|
s[k + cond] = v
|
||||||
s2 = alls.get(n)
|
s2 = alls.get(n)
|
||||||
if isinstance(s2, dict):
|
if isinstance(s2, dict):
|
||||||
# save previous known values, for possible using in local interpolations later:
|
# save previous known values, for possible using in local interpolations later:
|
||||||
|
@ -238,8 +253,6 @@ after = 1.conf
|
||||||
s2.update(s)
|
s2.update(s)
|
||||||
else:
|
else:
|
||||||
alls[n] = s.copy()
|
alls[n] = s.copy()
|
||||||
else:
|
|
||||||
alls[n] = s
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -254,9 +267,12 @@ after = 1.conf
|
||||||
|
|
||||||
def merge_section(self, section, options, pref='known/'):
|
def merge_section(self, section, options, pref='known/'):
|
||||||
alls = self.get_sections()
|
alls = self.get_sections()
|
||||||
|
if pref == '':
|
||||||
|
alls[section].update(options)
|
||||||
|
return
|
||||||
sk = {}
|
sk = {}
|
||||||
for k, v in options.iteritems():
|
for k, v in options.iteritems():
|
||||||
if pref == '' or (not k.startswith(pref) and k != '__name__'):
|
if not k.startswith(pref) and k != '__name__':
|
||||||
sk[pref+k] = v
|
sk[pref+k] = v
|
||||||
alls[section].update(sk)
|
alls[section].update(sk)
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import time
|
||||||
from abc import ABCMeta
|
from abc import ABCMeta
|
||||||
from collections import MutableMapping
|
from collections import MutableMapping
|
||||||
|
|
||||||
|
from .ipdns import asip
|
||||||
from .utils import Utils
|
from .utils import Utils
|
||||||
from ..helpers import getLogger
|
from ..helpers import getLogger
|
||||||
|
|
||||||
|
@ -41,6 +42,11 @@ logSys = getLogger(__name__)
|
||||||
# Create a lock for running system commands
|
# Create a lock for running system commands
|
||||||
_cmd_lock = threading.Lock()
|
_cmd_lock = threading.Lock()
|
||||||
|
|
||||||
|
# Todo: make it configurable resp. automatically set, ex.: `[ -f /proc/net/if_inet6 ] && echo 'yes' || echo 'no'`:
|
||||||
|
allowed_ipv6 = True
|
||||||
|
|
||||||
|
# compiled RE for tag name (replacement name)
|
||||||
|
TAG_CRE = re.compile(r'<([^ <>]+)>')
|
||||||
|
|
||||||
class CallingMap(MutableMapping):
|
class CallingMap(MutableMapping):
|
||||||
"""A Mapping type which returns the result of callable values.
|
"""A Mapping type which returns the result of callable values.
|
||||||
|
@ -256,14 +262,18 @@ class CommandAction(ActionBase):
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
if (self._properties and
|
# check valid tags in properties (raises ValueError if self recursion, etc.):
|
||||||
not self.substituteRecursiveTags(self._properties)):
|
if self._properties:
|
||||||
self._logSys.error(
|
self.substituteRecursiveTags(self._properties)
|
||||||
"properties contain self referencing definitions "
|
# common (resp. ipv4):
|
||||||
"and cannot be resolved")
|
startCmd = self.replaceTag('<actionstart>', self._properties, conditional='family=inet4')
|
||||||
raise RuntimeError("Error starting action")
|
res = self.executeCmd(startCmd, self.timeout)
|
||||||
startCmd = self.replaceTag(self.actionstart, self._properties)
|
# start ipv6 actions if available:
|
||||||
if not self.executeCmd(startCmd, self.timeout):
|
if allowed_ipv6:
|
||||||
|
startCmd6 = self.replaceTag('<actionstart>', self._properties, conditional='family=inet6')
|
||||||
|
if startCmd6 != startCmd:
|
||||||
|
res &= self.executeCmd(startCmd6, self.timeout)
|
||||||
|
if not res:
|
||||||
raise RuntimeError("Error starting action")
|
raise RuntimeError("Error starting action")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -289,7 +299,7 @@ class CommandAction(ActionBase):
|
||||||
Dictionary which includes information in relation to
|
Dictionary which includes information in relation to
|
||||||
the ban.
|
the ban.
|
||||||
"""
|
"""
|
||||||
if not self._processCmd(self.actionban, aInfo):
|
if not self._processCmd('<actionban>', aInfo):
|
||||||
raise RuntimeError("Error banning %(ip)s" % aInfo)
|
raise RuntimeError("Error banning %(ip)s" % aInfo)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -315,7 +325,7 @@ class CommandAction(ActionBase):
|
||||||
Dictionary which includes information in relation to
|
Dictionary which includes information in relation to
|
||||||
the ban.
|
the ban.
|
||||||
"""
|
"""
|
||||||
if not self._processCmd(self.actionunban, aInfo):
|
if not self._processCmd('<actionunban>', aInfo):
|
||||||
raise RuntimeError("Error unbanning %(ip)s" % aInfo)
|
raise RuntimeError("Error unbanning %(ip)s" % aInfo)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -350,12 +360,19 @@ 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.
|
||||||
"""
|
"""
|
||||||
stopCmd = self.replaceTag(self.actionstop, self._properties)
|
# common (resp. ipv4):
|
||||||
if not self.executeCmd(stopCmd, self.timeout):
|
stopCmd = self.replaceTag('<actionstop>', self._properties, conditional='family=inet4')
|
||||||
|
res = self.executeCmd(stopCmd, self.timeout)
|
||||||
|
# ipv6 actions if available:
|
||||||
|
if allowed_ipv6:
|
||||||
|
stopCmd6 = self.replaceTag('<actionstop>', self._properties, conditional='family=inet6')
|
||||||
|
if stopCmd6 != stopCmd:
|
||||||
|
res &= self.executeCmd(stopCmd6, self.timeout)
|
||||||
|
if not res:
|
||||||
raise RuntimeError("Error stopping action")
|
raise RuntimeError("Error stopping action")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def substituteRecursiveTags(cls, tags):
|
def substituteRecursiveTags(cls, tags, conditional=''):
|
||||||
"""Sort out tag definitions within other tags.
|
"""Sort out tag definitions within other tags.
|
||||||
Since v.0.9.2 supports embedded interpolation (see test cases for examples).
|
Since v.0.9.2 supports embedded interpolation (see test cases for examples).
|
||||||
|
|
||||||
|
@ -374,7 +391,7 @@ class CommandAction(ActionBase):
|
||||||
Dictionary of tags(keys) and their values, with tags
|
Dictionary of tags(keys) and their values, with tags
|
||||||
within the values recursively replaced.
|
within the values recursively replaced.
|
||||||
"""
|
"""
|
||||||
t = re.compile(r'<([^ <>]+)>')
|
t = TAG_CRE
|
||||||
# repeat substitution while embedded-recursive (repFlag is True)
|
# repeat substitution while embedded-recursive (repFlag is True)
|
||||||
while True:
|
while True:
|
||||||
repFlag = False
|
repFlag = False
|
||||||
|
@ -394,14 +411,21 @@ class CommandAction(ActionBase):
|
||||||
if found_tag == tag or found_tag in done:
|
if found_tag == tag or found_tag in done:
|
||||||
# recursive definitions are bad
|
# recursive definitions are bad
|
||||||
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
#logSys.log(5, 'recursion fail tag: %s value: %s' % (tag, value) )
|
||||||
return False
|
raise ValueError(
|
||||||
if found_tag in cls._escapedTags or not found_tag in tags:
|
"properties contain self referencing definitions "
|
||||||
|
"and cannot be resolved, fail tag: %s value: %s" % (tag, value))
|
||||||
|
repl = None
|
||||||
|
if found_tag not in cls._escapedTags:
|
||||||
|
repl = tags.get(found_tag + '?' + conditional)
|
||||||
|
if repl is None:
|
||||||
|
repl = tags.get(found_tag)
|
||||||
|
if repl is None:
|
||||||
# Escaped or missing tags - just continue on searching after end of match
|
# Escaped or missing tags - just continue on searching after end of match
|
||||||
# Missing tags are ok - cInfo can contain aInfo elements like <HOST> and valid shell
|
# Missing tags are ok - cInfo can contain aInfo elements like <HOST> and valid shell
|
||||||
# constructs like <STDIN>.
|
# constructs like <STDIN>.
|
||||||
m = t.search(value, m.end())
|
m = t.search(value, m.end())
|
||||||
continue
|
continue
|
||||||
value = value.replace('<%s>' % found_tag , tags[found_tag])
|
value = value.replace('<%s>' % found_tag, repl)
|
||||||
#logSys.log(5, 'value now: %s' % value)
|
#logSys.log(5, 'value now: %s' % value)
|
||||||
done.append(found_tag)
|
done.append(found_tag)
|
||||||
m = t.search(value, m.start())
|
m = t.search(value, m.start())
|
||||||
|
@ -443,7 +467,7 @@ class CommandAction(ActionBase):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def replaceTag(cls, query, aInfo):
|
def replaceTag(cls, query, aInfo, conditional=''):
|
||||||
"""Replaces tags in `query` with property values.
|
"""Replaces tags in `query` with property values.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -459,7 +483,7 @@ class CommandAction(ActionBase):
|
||||||
`query` string with tags replaced.
|
`query` string with tags replaced.
|
||||||
"""
|
"""
|
||||||
string = query
|
string = query
|
||||||
aInfo = cls.substituteRecursiveTags(aInfo)
|
aInfo = cls.substituteRecursiveTags(aInfo, conditional)
|
||||||
for tag in aInfo:
|
for tag in aInfo:
|
||||||
if "<%s>" % tag in query:
|
if "<%s>" % tag in query:
|
||||||
value = str(aInfo[tag]) # assure string
|
value = str(aInfo[tag]) # assure string
|
||||||
|
@ -469,10 +493,10 @@ class CommandAction(ActionBase):
|
||||||
value = cls.escapeTag(value)
|
value = cls.escapeTag(value)
|
||||||
string = string.replace('<' + tag + '>', value)
|
string = string.replace('<' + tag + '>', value)
|
||||||
# New line
|
# New line
|
||||||
string = string.replace("<br>", '\n')
|
string = reduce(lambda s, kv: s.replace(*kv), (("<br>", '\n'), ("<sp>", " ")), string)
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def _processCmd(self, cmd, aInfo = None):
|
def _processCmd(self, cmd, aInfo=None, conditional=''):
|
||||||
"""Executes a command with preliminary checks and substitutions.
|
"""Executes a command with preliminary checks and substitutions.
|
||||||
|
|
||||||
Before executing any commands, executes the "check" command first
|
Before executing any commands, executes the "check" command first
|
||||||
|
@ -496,7 +520,17 @@ class CommandAction(ActionBase):
|
||||||
self._logSys.debug("Nothing to do")
|
self._logSys.debug("Nothing to do")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
checkCmd = self.replaceTag(self.actioncheck, self._properties)
|
if conditional == '':
|
||||||
|
conditional = 'family=inet4'
|
||||||
|
if allowed_ipv6:
|
||||||
|
try:
|
||||||
|
ip = aInfo["ip"]
|
||||||
|
if ip and asip(ip).isIPv6:
|
||||||
|
conditional = 'family=inet6'
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
checkCmd = self.replaceTag('<actioncheck>', self._properties, conditional=conditional)
|
||||||
if not self.executeCmd(checkCmd, self.timeout):
|
if not self.executeCmd(checkCmd, self.timeout):
|
||||||
self._logSys.error(
|
self._logSys.error(
|
||||||
"Invariant check failed. Trying to restore a sane environment")
|
"Invariant check failed. Trying to restore a sane environment")
|
||||||
|
@ -506,15 +540,15 @@ class CommandAction(ActionBase):
|
||||||
self._logSys.critical("Unable to restore environment")
|
self._logSys.critical("Unable to restore environment")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Replace static fields
|
||||||
|
realCmd = self.replaceTag(cmd, self._properties, conditional=conditional)
|
||||||
|
|
||||||
# Replace tags
|
# Replace tags
|
||||||
if not aInfo is None:
|
if aInfo is not None:
|
||||||
realCmd = self.replaceTag(cmd, aInfo)
|
realCmd = self.replaceTag(realCmd, aInfo, conditional=conditional)
|
||||||
else:
|
else:
|
||||||
realCmd = cmd
|
realCmd = cmd
|
||||||
|
|
||||||
# Replace static fields
|
|
||||||
realCmd = self.replaceTag(realCmd, self._properties)
|
|
||||||
|
|
||||||
return self.executeCmd(realCmd, self.timeout)
|
return self.executeCmd(realCmd, self.timeout)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -54,12 +54,17 @@ class CommandActionTest(LogCaptureTestCase):
|
||||||
'xyz': "890 <ABC>",
|
'xyz': "890 <ABC>",
|
||||||
}
|
}
|
||||||
# Recursion is bad
|
# Recursion is bad
|
||||||
self.assertFalse(CommandAction.substituteRecursiveTags({'A': '<A>'}))
|
self.assertRaises(ValueError,
|
||||||
self.assertFalse(CommandAction.substituteRecursiveTags({'A': '<B>', 'B': '<A>'}))
|
lambda: CommandAction.substituteRecursiveTags({'A': '<A>'}))
|
||||||
self.assertFalse(CommandAction.substituteRecursiveTags({'A': '<B>', 'B': '<C>', 'C': '<A>'}))
|
self.assertRaises(ValueError,
|
||||||
|
lambda: CommandAction.substituteRecursiveTags({'A': '<B>', 'B': '<A>'}))
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
lambda: CommandAction.substituteRecursiveTags({'A': '<B>', 'B': '<C>', 'C': '<A>'}))
|
||||||
# Unresolveable substition
|
# Unresolveable substition
|
||||||
self.assertFalse(CommandAction.substituteRecursiveTags({'A': 'to=<B> fromip=<IP>', 'C': '<B>', 'B': '<C>', 'D': ''}))
|
self.assertRaises(ValueError,
|
||||||
self.assertFalse(CommandAction.substituteRecursiveTags({'failregex': 'to=<honeypot> fromip=<IP>', 'sweet': '<honeypot>', 'honeypot': '<sweet>', 'ignoreregex': ''}))
|
lambda: CommandAction.substituteRecursiveTags({'A': 'to=<B> fromip=<IP>', 'C': '<B>', 'B': '<C>', 'D': ''}))
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
lambda: CommandAction.substituteRecursiveTags({'failregex': 'to=<honeypot> fromip=<IP>', 'sweet': '<honeypot>', 'honeypot': '<sweet>', 'ignoreregex': ''}))
|
||||||
# missing tags are ok
|
# missing tags are ok
|
||||||
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C>'}), {'A': '<C>'})
|
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C>'}), {'A': '<C>'})
|
||||||
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C> <D> <X>','X':'fun'}), {'A': '<C> <D> fun', 'X':'fun'})
|
self.assertEqual(CommandAction.substituteRecursiveTags({'A': '<C> <D> <X>','X':'fun'}), {'A': '<C> <D> fun', 'X':'fun'})
|
||||||
|
@ -127,6 +132,7 @@ class CommandActionTest(LogCaptureTestCase):
|
||||||
CallingMap(matches=lambda: str(10))),
|
CallingMap(matches=lambda: str(10))),
|
||||||
"09 10 11")
|
"09 10 11")
|
||||||
|
|
||||||
|
def testReplaceNoTag(self):
|
||||||
# As tag not present, therefore callable should not be called
|
# As tag not present, therefore callable should not be called
|
||||||
# Will raise ValueError if it is
|
# Will raise ValueError if it is
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
@ -1416,7 +1416,6 @@ class DNSUtilsNetworkTests(unittest.TestCase):
|
||||||
self.assertNotEqual(ip4[0], None)
|
self.assertNotEqual(ip4[0], None)
|
||||||
self.assertTrue(ip4[0] is not None)
|
self.assertTrue(ip4[0] is not None)
|
||||||
self.assertFalse(ip4[0] is None)
|
self.assertFalse(ip4[0] is None)
|
||||||
self.assertLess(None, ip4[0])
|
|
||||||
self.assertLess(ip4[0], ip4[1])
|
self.assertLess(ip4[0], ip4[1])
|
||||||
self.assertLess(ip4[1], ip4[2])
|
self.assertLess(ip4[1], ip4[2])
|
||||||
self.assertEqual(sorted(reversed(ip4)), ip4)
|
self.assertEqual(sorted(reversed(ip4)), ip4)
|
||||||
|
@ -1424,7 +1423,6 @@ class DNSUtilsNetworkTests(unittest.TestCase):
|
||||||
self.assertNotEqual(ip6[0], None)
|
self.assertNotEqual(ip6[0], None)
|
||||||
self.assertTrue(ip6[0] is not None)
|
self.assertTrue(ip6[0] is not None)
|
||||||
self.assertFalse(ip6[0] is None)
|
self.assertFalse(ip6[0] is None)
|
||||||
self.assertLess(None, ip6[0])
|
|
||||||
self.assertLess(ip6[0], ip6[1])
|
self.assertLess(ip6[0], ip6[1])
|
||||||
self.assertLess(ip6[1], ip6[2])
|
self.assertLess(ip6[1], ip6[2])
|
||||||
self.assertEqual(sorted(reversed(ip6)), ip6)
|
self.assertEqual(sorted(reversed(ip6)), ip6)
|
||||||
|
|
Loading…
Reference in New Issue