Recognize restored (from database) tickets after restart (tell action restored state of the ticket);

Prevent executing of several actions (e.g. mail, send-mail etc) on restart (bans were already notified).
Test cases extended (smtp and by restart in ServerReloadTest).
Closes gh-1141
Closes gh-921
pull/1669/head
sebres 2017-01-13 19:06:17 +01:00
parent 4a65e069e1
commit ee3c787cc6
21 changed files with 114 additions and 29 deletions

View File

@ -58,7 +58,8 @@ actioncheck =
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = oifs=${IFS};
actionban = %(_bypass_if_restored)s
oifs=${IFS};
IFS=.; SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(dig +short -t txt -q $4.$3.$2.$1.abuse-contacts.abusix.org);
IFS=,; ADDRESSES=$(echo $ADDRESSES)
IFS=${oifs}

View File

@ -26,6 +26,10 @@
# configure how often the buffer is flushed).
#
[INCLUDES]
before = helpers-common.conf
[Definition]
# Option: actionstart
@ -64,7 +68,8 @@ actioncheck =
# few seconds out, are incorrect. See
# http://sourceforge.net/tracker/index.php?func=detail&aid=2017795&group_id=121032&atid=689047
#
actionban = TZONE=`date +%%z | sed 's/\([+-]..\)\(..\)/\1:\2/'`
actionban = %(_bypass_if_restored)s
TZONE=`date +%%z | sed 's/\([+-]..\)\(..\)/\1:\2/'`
DATETIME="`perl -e '@t=localtime(<time>);printf "%%4d-%%02d-%%02d %%02d:%%02d:%%02d",1900+$t[5],$t[4]+1,$t[3],$t[2],$t[1],$t[0]'` $TZONE"
PROTOCOL=`awk '{IGNORECASE=1;if($1=="<protocol>"){print $2;exit}}' /etc/protocols`
if [ -z "$PROTOCOL" ]; then PROTOCOL=<protocol>; fi
@ -91,7 +96,8 @@ actionban = TZONE=`date +%%z | sed 's/\([+-]..\)\(..\)/\1:\2/'`
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = if [ -f <tmpfile>.first ]; then
actionunban = %(_bypass_if_restored)s
if [ -f <tmpfile>.first ]; then
NOW=`date +%%s`
LOGAGE=$(($NOW - `cat <tmpfile>.first`))
if [ $LOGAGE -gt <maxbufferage> ]; then

View File

@ -7,6 +7,9 @@
_grep_logs = logpath="<logpath>"; grep <grepopts> -E %(_grep_logs_args)s $logpath | <greplimit>
_grep_logs_args = '(^|[^0-9])<ip>([^0-9]|$)'
# Used for actions, that should not by executed if ticket was restored:
_bypass_if_restored = if [ '<restored>' = '1' ]; then exit 0; fi;
[Init]
greplimit = tail -n <grepmax>
grepmax = 1000

View File

@ -4,6 +4,11 @@
#
#
[INCLUDES]
before = helpers-common.conf
[Definition]
# Option: actionstart
@ -45,7 +50,8 @@ actioncheck =
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
actionban = %(_bypass_if_restored)s
printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
LINE=$( wc -l <tmpfile> | awk '{ print $1 }' )
if [ $LINE -ge <lines> ]; then
printf %%b "Hi,\n

View File

@ -52,7 +52,9 @@ _ban_mail_content = ( printf %%b "Hi,\n
printf %%b "\n
Regards,\n
Fail2Ban" )
actionban = %(_ban_mail_content)s | <mailcmd> "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
actionban = %(_bypass_if_restored)s
%(_ban_mail_content)s | <mailcmd> "[Fail2Ban] <name>: banned <ip> from `uname -n`" <dest>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the

View File

@ -7,6 +7,7 @@
[INCLUDES]
before = mail-whois-common.conf
helpers-common.conf
[Definition]
@ -40,7 +41,8 @@ actioncheck =
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Hi,\n
actionban = %(_bypass_if_restored)s
printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n\n
Here is more information about <ip> :\n

View File

@ -4,6 +4,10 @@
#
#
[INCLUDES]
before = helpers-common.conf
[Definition]
# Option: actionstart
@ -36,7 +40,8 @@ actioncheck =
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Hi,\n
actionban = %(_bypass_if_restored)s
printf %%b "Hi,\n
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts against <name>.\n
Regards,\n

View File

@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
helpers-common.conf
[Definition]
@ -58,7 +59,8 @@ actioncheck =
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
actionban = %(_bypass_if_restored)s
printf %%b "`date`: <ip> (<failures> failures)\n" >> <tmpfile>
LINE=$( wc -l <tmpfile> | awk '{ print $1 }' )
if [ $LINE -ge <lines> ]; then
printf %%b "Subject: [Fail2Ban] <name>: summary from `uname -n`

View File

@ -20,7 +20,8 @@ before = sendmail-common.conf
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = ( printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
actionban = %(_bypass_if_restored)s
( printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
From: <sendername> <<sender>>
To: <dest>\n

View File

@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
helpers-common.conf
[Definition]
@ -16,7 +17,8 @@ before = sendmail-common.conf
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
actionban = %(_bypass_if_restored)s
printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
From: <sendername> <<sender>>
To: <dest>\n

View File

@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
helpers-common.conf
[Definition]
@ -16,7 +17,8 @@ before = sendmail-common.conf
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
actionban = %(_bypass_if_restored)s
printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
From: <sendername> <<sender>>
To: <dest>\n

View File

@ -17,7 +17,8 @@ before = sendmail-common.conf
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = ( printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
actionban = %(_bypass_if_restored)s
( printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
From: <sendername> <<sender>>
To: <dest>\n

View File

@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
helpers-common.conf
[Definition]
@ -16,7 +17,8 @@ before = sendmail-common.conf
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
actionban = %(_bypass_if_restored)s
printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
From: <sendername> <<sender>>
To: <dest>\n

View File

@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
helpers-common.conf
[Definition]
@ -16,7 +17,8 @@ before = sendmail-common.conf
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
actionban = %(_bypass_if_restored)s
printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
From: <sendername> <<sender>>
To: <dest>\n

View File

@ -7,6 +7,7 @@
[INCLUDES]
before = sendmail-common.conf
helpers-common.conf
[Definition]
@ -16,7 +17,8 @@ before = sendmail-common.conf
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
actionban = %(_bypass_if_restored)s
printf %%b "Subject: [Fail2Ban] <name>: banned <ip> from `uname -n`
Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"`
From: <sendername> <<sender>>
To: <dest>\n

View File

@ -211,6 +211,8 @@ class SMTPAction(ActionBase):
Dictionary which includes information in relation to
the ban.
"""
if aInfo.get('restored'):
return
aInfo.update(self.message_values)
message = "".join([
messages['ban']['head'],

View File

@ -30,6 +30,10 @@
#
#
[INCLUDES]
before = helpers-common.conf
[Definition]
actionstart =
@ -38,7 +42,8 @@ actionstop =
actioncheck =
actionban = oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(dig +short -t txt -q $4.$3.$2.$1.abuse-contacts.abusix.org); IFS=${oifs}
actionban = %(_bypass_if_restored)s
oifs=${IFS}; IFS=.;SEP_IP=( <ip> ); set -- ${SEP_IP}; ADDRESSES=$(dig +short -t txt -q $4.$3.$2.$1.abuse-contacts.abusix.org); IFS=${oifs}
IP=<ip>
FROM=<sender>
SERVICE=<service>

View File

@ -219,9 +219,9 @@ class CommandAction(ActionBase):
self.timeout = 60
## Command executed in order to initialize the system.
self.actionstart = ''
## Command executed when an IP address gets banned.
## Command executed when ticket gets banned.
self.actionban = ''
## Command executed when an IP address gets removed.
## Command executed when ticket gets removed.
self.actionunban = ''
## Command executed in order to check requirements.
self.actioncheck = ''

View File

@ -348,6 +348,8 @@ class Actions(JailThread, Mapping):
aInfo["failures"] = bTicket.getAttempt()
aInfo["time"] = bTicket.getTime()
aInfo["matches"] = "\n".join(bTicket.getMatches())
# to bypass actions, that should not be executed for restored tickets
aInfo["restored"] = 1 if ticket.restored else 0
if self._jail.database is not None:
mi4ip = lambda overalljails=False, self=self, \
mi={'ip':ip, 'ticket':bTicket}: self.__getBansMerged(mi, overalljails)
@ -449,6 +451,8 @@ class Actions(JailThread, Mapping):
aInfo["failures"] = ticket.getAttempt()
aInfo["time"] = ticket.getTime()
aInfo["matches"] = "".join(ticket.getMatches())
# to bypass actions, that should not be executed for restored tickets
aInfo["restored"] = 1 if ticket.restored else 0
if actions is None:
logSys.notice("[%s] Unban %s", self._jail.name, aInfo["ip"])
for name, action in unbactions.iteritems():

View File

@ -94,7 +94,7 @@ class SMTPActionTest(unittest.TestCase):
"Subject: [Fail2Ban] %s: stopped" %
self.jail.name in self.smtpd.data)
def testBan(self):
def _testBan(self, restored=False):
aInfo = {
'ip': "127.0.0.2",
'failures': 3,
@ -102,8 +102,13 @@ class SMTPActionTest(unittest.TestCase):
'ipjailmatches': "Test fail 1\nTest Fail2\n",
'ipmatches': "Test fail 1\nTest Fail2\nTest Fail3\n",
}
if restored:
aInfo['restored'] = 1
self.action.ban(aInfo)
if restored: # no mail, should raises attribute error:
self.assertRaises(AttributeError, lambda: self.smtpd.mailfrom)
return
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
self.assertEqual(self.smtpd.rcpttos, ["root"])
subject = "Subject: [Fail2Ban] %s: banned %s" % (
@ -124,6 +129,12 @@ class SMTPActionTest(unittest.TestCase):
self.action.ban(aInfo)
self.assertIn(aInfo['ipmatches'], self.smtpd.data)
def testBan(self):
self._testBan()
def testNOPByRestored(self):
self._testBan(restored=True)
def testOptions(self):
self.action.start()
self.assertEqual(self.smtpd.mailfrom, "fail2ban")

View File

@ -756,9 +756,10 @@ class Fail2banServerTest(Fail2banClientServerBase):
return
_write_file(fn, "w",
"[Definition]",
"restore = ",
"actionstart = echo '[<name>] %s: ** start'" % actname, start,
"actionreload = echo '[<name>] %s: .. reload'" % actname, reload,
"actionban = echo '[<name>] %s: ++ ban <ip>'" % actname, ban,
"actionban = echo '[<name>] %s: ++ ban <ip> %%(restore)s'" % actname, ban,
"actionunban = echo '[<name>] %s: -- unban <ip>'" % actname, unban,
"actionstop = echo '[<name>] %s: __ stop'" % actname, stop,
)
@ -777,17 +778,22 @@ class Fail2banServerTest(Fail2banClientServerBase):
"",
"[test-jail1]", "backend = " + backend, "filter =",
"action = ",
" test-action1[name='%(__name__)s']" if 1 in actions else "",
" test-action2[name='%(__name__)s']" if 2 in actions else "",
" test-action1[name='%(__name__)s']" \
if 1 in actions else "",
" test-action2[name='%(__name__)s', restore='restored: <restored>']" \
if 2 in actions else "",
"logpath = " + test1log,
" " + test2log if 2 in enabled else "",
" " + test3log if 2 in enabled else "",
"failregex = ^\s*failure (401|403) from <HOST>",
" ^\s*error (401|403) from <HOST>" if 2 in enabled else "",
" ^\s*error (401|403) from <HOST>" \
if 2 in enabled else "",
"enabled = true" if 1 in enabled else "",
"",
"[test-jail2]", "backend = " + backend, "filter =",
"action =",
"action = ",
" test-action2[name='%(__name__)s', restore='restored: <restored>']" \
if 2 in actions else "",
"logpath = " + test2log,
"enabled = true" if 2 in enabled else "",
)
@ -821,6 +827,10 @@ class Fail2banServerTest(Fail2banClientServerBase):
self.assertLogged(
"stdout: '[test-jail1] test-action1: ** start'",
"stdout: '[test-jail1] test-action2: ** start'", all=True)
# test restored is 0:
self.assertLogged(
"stdout: '[test-jail1] test-action2: ++ ban 192.0.2.1 restored: 0'",
all=True, wait=MID_WAITTIME)
# broken jail was logged (in client and server log):
self.assertLogged(
@ -882,10 +892,10 @@ class Fail2banServerTest(Fail2banClientServerBase):
self.assertNotLogged(
"stdout: '[test-jail1] test-action1: -- unban 192.0.2.1'")
# don't need both actions anymore:
# don't need action1 anymore:
_write_action_cfg(actname="test-action1", allow=False)
_write_action_cfg(actname="test-action2", allow=False)
_write_jail_cfg(actions=[])
# leave action2 just to test restored interpolation:
_write_jail_cfg(actions=[2])
# write new failures:
self.pruneLog("[test-phase 2b]")
@ -913,7 +923,8 @@ class Fail2banServerTest(Fail2banClientServerBase):
"[test-jail2] Found 192.0.2.2",
"[test-jail2] Ban 192.0.2.2",
"[test-jail2] Found 192.0.2.3",
"[test-jail2] Ban 192.0.2.3", all=True)
"[test-jail2] Ban 192.0.2.3",
all=True)
# rotate logs:
_write_file(test1log, "w+")
@ -936,6 +947,19 @@ class Fail2banServerTest(Fail2banClientServerBase):
"[test-jail2] Restore Ban 192.0.2.4",
"[test-jail2] Restore Ban 192.0.2.8", all=True
)
# test restored is 1:
self.assertLogged(
"stdout: '[test-jail2] test-action2: ++ ban 192.0.2.4 restored: 1'",
"stdout: '[test-jail2] test-action2: ++ ban 192.0.2.8 restored: 1'",
all=True, wait=MID_WAITTIME)
self.assertNotLogged(
"stdout: '[test-jail2] test-action2: ++ ban 192.0.2.4 restored: 0'",
"stdout: '[test-jail2] test-action2: ++ ban 192.0.2.8 restored: 0'",
all=True)
# don't need actions anymore:
_write_action_cfg(actname="test-action2", allow=False)
_write_jail_cfg(actions=[])
# restart jail with unban all:
self.pruneLog("[test-phase 2d]")