mirror of https://github.com/fail2ban/fail2ban
action info extended with new members for jail info (usable as tags in command actions):
`jail.found`, `jail.found_total` - current and total found failures `jail.banned`, `jail.banned_total` - current and total bans closes #10pull/2337/merge
parent
08393f9d82
commit
725354c793
|
@ -84,7 +84,7 @@ class Actions(JailThread, Mapping):
|
|||
self._jail = jail
|
||||
self._actions = OrderedDict()
|
||||
## The ban manager.
|
||||
self.__banManager = BanManager()
|
||||
self.banManager = BanManager()
|
||||
self.banEpoch = 0
|
||||
self.__lastConsistencyCheckTM = 0
|
||||
## Precedence of ban (over unban), so max number of tickets banned (to call an unban check):
|
||||
|
@ -203,7 +203,7 @@ class Actions(JailThread, Mapping):
|
|||
|
||||
def setBanTime(self, value):
|
||||
value = MyTime.str2seconds(value)
|
||||
self.__banManager.setBanTime(value)
|
||||
self.banManager.setBanTime(value)
|
||||
logSys.info(" banTime: %s" % value)
|
||||
|
||||
##
|
||||
|
@ -212,10 +212,10 @@ class Actions(JailThread, Mapping):
|
|||
# @return the time
|
||||
|
||||
def getBanTime(self):
|
||||
return self.__banManager.getBanTime()
|
||||
return self.banManager.getBanTime()
|
||||
|
||||
def getBanned(self, ids):
|
||||
lst = self.__banManager.getBanList()
|
||||
lst = self.banManager.getBanList()
|
||||
if not ids:
|
||||
return lst
|
||||
if len(ids) == 1:
|
||||
|
@ -230,7 +230,7 @@ class Actions(JailThread, Mapping):
|
|||
list
|
||||
The list of banned IP addresses.
|
||||
"""
|
||||
return self.__banManager.getBanList(ordered=True, withTime=withTime)
|
||||
return self.banManager.getBanList(ordered=True, withTime=withTime)
|
||||
|
||||
def addBannedIP(self, ip):
|
||||
"""Ban an IP or list of IPs."""
|
||||
|
@ -282,7 +282,7 @@ class Actions(JailThread, Mapping):
|
|||
if db and self._jail.database is not None:
|
||||
self._jail.database.delBan(self._jail, ip)
|
||||
# Find the ticket with the IP.
|
||||
ticket = self.__banManager.getTicketByID(ip)
|
||||
ticket = self.banManager.getTicketByID(ip)
|
||||
if ticket is not None:
|
||||
# Unban the IP.
|
||||
self.__unBan(ticket)
|
||||
|
@ -291,7 +291,7 @@ class Actions(JailThread, Mapping):
|
|||
if not isinstance(ip, IPAddr):
|
||||
ipa = IPAddr(ip)
|
||||
if not ipa.isSingle: # subnet (mask/cidr) or raw (may be dns/hostname):
|
||||
ips = filter(ipa.contains, self.__banManager.getBanList())
|
||||
ips = filter(ipa.contains, self.banManager.getBanList())
|
||||
if ips:
|
||||
return self.removeBannedIP(ips, db, ifexists)
|
||||
# not found:
|
||||
|
@ -350,7 +350,7 @@ class Actions(JailThread, Mapping):
|
|||
continue
|
||||
# wait for ban (stop if gets inactive, pending ban or unban):
|
||||
bancnt = 0
|
||||
wt = min(self.sleeptime, self.__banManager._nextUnbanTime - MyTime.time())
|
||||
wt = min(self.sleeptime, self.banManager._nextUnbanTime - MyTime.time())
|
||||
logSys.log(5, "Actions: wait for pending tickets %s (default %s)", wt, self.sleeptime)
|
||||
if Utils.wait_for(lambda: not self.active or self._jail.hasFailTickets, wt):
|
||||
bancnt = self.__checkBan()
|
||||
|
@ -397,7 +397,12 @@ class Actions(JailThread, Mapping):
|
|||
"ipfailures": lambda self: self._mi4ip(True).getAttempt(),
|
||||
"ipjailfailures": lambda self: self._mi4ip().getAttempt(),
|
||||
# raw ticket info:
|
||||
"raw-ticket": lambda self: repr(self.__ticket)
|
||||
"raw-ticket": lambda self: repr(self.__ticket),
|
||||
# jail info:
|
||||
"jail.banned": lambda self: self.__jail.actions.banManager.size(),
|
||||
"jail.banned_total": lambda self: self.__jail.actions.banManager.getBanTotal(),
|
||||
"jail.found": lambda self: self.__jail.filter.failManager.size(),
|
||||
"jail.found_total": lambda self: self.__jail.filter.failManager.getFailTotal()
|
||||
}
|
||||
|
||||
__slots__ = CallingMap.__slots__ + ('__ticket', '__jail', '__mi4ip')
|
||||
|
@ -494,11 +499,11 @@ class Actions(JailThread, Mapping):
|
|||
for ticket in tickets:
|
||||
|
||||
bTicket = BanTicket.wrap(ticket)
|
||||
btime = ticket.getBanTime(self.__banManager.getBanTime())
|
||||
btime = ticket.getBanTime(self.banManager.getBanTime())
|
||||
ip = bTicket.getIP()
|
||||
aInfo = self._getActionInfo(bTicket)
|
||||
reason = {}
|
||||
if self.__banManager.addBanTicket(bTicket, reason=reason):
|
||||
if self.banManager.addBanTicket(bTicket, reason=reason):
|
||||
cnt += 1
|
||||
# report ticket to observer, to check time should be increased and hereafter observer writes ban to database (asynchronous)
|
||||
if Observers.Main is not None and not bTicket.restored:
|
||||
|
@ -557,7 +562,7 @@ class Actions(JailThread, Mapping):
|
|||
# and increase ticket time if "bantime.increment" set)
|
||||
if cnt:
|
||||
logSys.debug("Banned %s / %s, %s ticket(s) in %r", cnt,
|
||||
self.__banManager.getBanTotal(), self.__banManager.size(), self._jail.name)
|
||||
self.banManager.getBanTotal(), self.banManager.size(), self._jail.name)
|
||||
return cnt
|
||||
|
||||
def __reBan(self, ticket, actions=None, log=True):
|
||||
|
@ -597,7 +602,7 @@ class Actions(JailThread, Mapping):
|
|||
def _prolongBan(self, ticket):
|
||||
# prevent to prolong ticket that was removed in-between,
|
||||
# if it in ban list - ban time already prolonged (and it stays there):
|
||||
if not self.__banManager._inBanList(ticket): return
|
||||
if not self.banManager._inBanList(ticket): return
|
||||
# do actions :
|
||||
aInfo = None
|
||||
for name, action in self._actions.iteritems():
|
||||
|
@ -622,13 +627,13 @@ class Actions(JailThread, Mapping):
|
|||
|
||||
Unban IP addresses which are outdated.
|
||||
"""
|
||||
lst = self.__banManager.unBanList(MyTime.time(), maxCount)
|
||||
lst = self.banManager.unBanList(MyTime.time(), maxCount)
|
||||
for ticket in lst:
|
||||
self.__unBan(ticket)
|
||||
cnt = len(lst)
|
||||
if cnt:
|
||||
logSys.debug("Unbanned %s, %s ticket(s) in %r",
|
||||
cnt, self.__banManager.size(), self._jail.name)
|
||||
cnt, self.banManager.size(), self._jail.name)
|
||||
return cnt
|
||||
|
||||
def __flushBan(self, db=False, actions=None, stop=False):
|
||||
|
@ -642,10 +647,10 @@ class Actions(JailThread, Mapping):
|
|||
log = True
|
||||
if actions is None:
|
||||
logSys.debug(" Flush ban list")
|
||||
lst = self.__banManager.flushBanList()
|
||||
lst = self.banManager.flushBanList()
|
||||
else:
|
||||
log = False # don't log "[jail] Unban ..." if removing actions only.
|
||||
lst = iter(self.__banManager)
|
||||
lst = iter(self.banManager)
|
||||
cnt = 0
|
||||
# first we'll execute flush for actions supporting this operation:
|
||||
unbactions = {}
|
||||
|
@ -682,7 +687,7 @@ class Actions(JailThread, Mapping):
|
|||
self.__unBan(ticket, actions=actions, log=log)
|
||||
cnt += 1
|
||||
logSys.debug(" Unbanned %s, %s ticket(s) in %r",
|
||||
cnt, self.__banManager.size(), self._jail.name)
|
||||
cnt, self.banManager.size(), self._jail.name)
|
||||
return cnt
|
||||
|
||||
def __unBan(self, ticket, actions=None, log=True):
|
||||
|
@ -725,18 +730,18 @@ class Actions(JailThread, Mapping):
|
|||
logSys.warning("Unsupported extended jail status flavor %r. Supported: %s" % (flavor, supported_flavors))
|
||||
# Always print this information (basic)
|
||||
if flavor != "short":
|
||||
banned = self.__banManager.getBanList()
|
||||
banned = self.banManager.getBanList()
|
||||
cnt = len(banned)
|
||||
else:
|
||||
cnt = self.__banManager.size()
|
||||
cnt = self.banManager.size()
|
||||
ret = [("Currently banned", cnt),
|
||||
("Total banned", self.__banManager.getBanTotal())]
|
||||
("Total banned", self.banManager.getBanTotal())]
|
||||
if flavor != "short":
|
||||
ret += [("Banned IP list", banned)]
|
||||
if flavor == "cymru":
|
||||
cymru_info = self.__banManager.getBanListExtendedCymruInfo()
|
||||
cymru_info = self.banManager.getBanListExtendedCymruInfo()
|
||||
ret += \
|
||||
[("Banned ASN list", self.__banManager.geBanListExtendedASN(cymru_info)),
|
||||
("Banned Country list", self.__banManager.geBanListExtendedCountry(cymru_info)),
|
||||
("Banned RIR list", self.__banManager.geBanListExtendedRIR(cymru_info))]
|
||||
[("Banned ASN list", self.banManager.geBanListExtendedASN(cymru_info)),
|
||||
("Banned Country list", self.banManager.geBanListExtendedCountry(cymru_info)),
|
||||
("Banned RIR list", self.banManager.geBanListExtendedRIR(cymru_info))]
|
||||
return ret
|
||||
|
|
|
@ -29,7 +29,7 @@ import tempfile
|
|||
import sqlite3
|
||||
import shutil
|
||||
|
||||
from ..server.filter import FileContainer
|
||||
from ..server.filter import FileContainer, Filter
|
||||
from ..server.mytime import MyTime
|
||||
from ..server.ticket import FailTicket
|
||||
from ..server.actions import Actions, Utils
|
||||
|
@ -544,17 +544,21 @@ class DatabaseTest(LogCaptureTestCase):
|
|||
self.testAddJail() # Jail required
|
||||
self.jail.database = self.db
|
||||
self.db.addJail(self.jail)
|
||||
actions = Actions(self.jail)
|
||||
actions = self.jail.actions
|
||||
actions.add(
|
||||
"action_checkainfo",
|
||||
os.path.join(TEST_FILES_DIR, "action.d/action_checkainfo.py"),
|
||||
{})
|
||||
actions.banManager.setBanTotal(20)
|
||||
self.jail._Jail__filter = flt = Filter(self.jail)
|
||||
flt.failManager.setFailTotal(50)
|
||||
ticket = FailTicket("1.2.3.4")
|
||||
ticket.setAttempt(5)
|
||||
ticket.setMatches(['test', 'test'])
|
||||
self.jail.putFailTicket(ticket)
|
||||
actions._Actions__checkBan()
|
||||
self.assertLogged("ban ainfo %s, %s, %s, %s" % (True, True, True, True))
|
||||
self.assertLogged("jail info %d, %d, %d, %d" % (1, 21, 0, 50))
|
||||
|
||||
def testDelAndAddJail(self):
|
||||
self.testAddJail() # Add jail
|
||||
|
|
|
@ -1410,8 +1410,9 @@ class Fail2banServerTest(Fail2banClientServerBase):
|
|||
'jails': (
|
||||
# default:
|
||||
'''test_action = dummy[actionstart_on_demand=1, init="start: %(__name__)s", target="%(tmp)s/test.txt",
|
||||
actionban='<known/actionban>;
|
||||
echo "<matches>"; printf "=====\\n%%b\\n=====\\n\\n" "<matches>" >> <target>']''',
|
||||
actionban='<known/actionban>; echo "found: <jail.found> / <jail.found_total>, banned: <jail.banned> / <jail.banned_total>"
|
||||
echo "<matches>"; printf "=====\\n%%b\\n=====\\n\\n" "<matches>" >> <target>',
|
||||
actionstop='<known/actionstop>; echo "stats <name> - found: <jail.found_total>, banned: <jail.banned_total>"']''',
|
||||
# jail sendmail-auth:
|
||||
'[sendmail-auth]',
|
||||
'backend = polling',
|
||||
|
@ -1456,7 +1457,8 @@ class Fail2banServerTest(Fail2banClientServerBase):
|
|||
_write_file(lgfn, "w+", *smaut_msg)
|
||||
# wait and check it caused banned (and dump in the test-file):
|
||||
self.assertLogged(
|
||||
"[sendmail-auth] Ban 192.0.2.1", "1 ticket(s) in 'sendmail-auth'", all=True, wait=MID_WAITTIME)
|
||||
"[sendmail-auth] Ban 192.0.2.1", "stdout: 'found: 0 / 3, banned: 1 / 1'",
|
||||
"1 ticket(s) in 'sendmail-auth'", all=True, wait=MID_WAITTIME)
|
||||
_out_file(tofn)
|
||||
td = _read_file(tofn)
|
||||
# check matches (maxmatches = 2, so only 2 & 3 available):
|
||||
|
@ -1470,7 +1472,8 @@ class Fail2banServerTest(Fail2banClientServerBase):
|
|||
_write_file(lgfn, "w+", *smrej_msg)
|
||||
# wait and check it caused banned (and dump in the test-file):
|
||||
self.assertLogged(
|
||||
"[sendmail-reject] Ban 192.0.2.2", "1 ticket(s) in 'sendmail-reject'", all=True, wait=MID_WAITTIME)
|
||||
"[sendmail-reject] Ban 192.0.2.2", "stdout: 'found: 0 / 3, banned: 1 / 1'",
|
||||
"1 ticket(s) in 'sendmail-reject'", all=True, wait=MID_WAITTIME)
|
||||
_out_file(tofn)
|
||||
td = _read_file(tofn)
|
||||
# check matches (no maxmatches, so all matched messages are available):
|
||||
|
@ -1484,6 +1487,8 @@ class Fail2banServerTest(Fail2banClientServerBase):
|
|||
# wait a bit:
|
||||
self.assertLogged(
|
||||
"Reload finished.",
|
||||
"stdout: 'stats sendmail-auth - found: 3, banned: 1'",
|
||||
"stdout: 'stats sendmail-reject - found: 3, banned: 1'",
|
||||
"[sendmail-auth] Restore Ban 192.0.2.1", "1 ticket(s) in 'sendmail-auth'", all=True, wait=MID_WAITTIME)
|
||||
# check matches again - (dbmaxmatches = 1), so it should be only last match after restart:
|
||||
td = _read_file(tofn)
|
||||
|
|
|
@ -8,6 +8,9 @@ class TestAction(ActionBase):
|
|||
self._logSys.info("ban ainfo %s, %s, %s, %s",
|
||||
aInfo["ipmatches"] != '', aInfo["ipjailmatches"] != '', aInfo["ipfailures"] > 0, aInfo["ipjailfailures"] > 0
|
||||
)
|
||||
self._logSys.info("jail info %d, %d, %d, %d",
|
||||
aInfo["jail.banned"], aInfo["jail.banned_total"], aInfo["jail.found"], aInfo["jail.found_total"]
|
||||
)
|
||||
|
||||
def unban(self, aInfo):
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue