mirror of https://github.com/fail2ban/fail2ban
Merge branch '0.10' into 0.11 (conflicts resolved, tests fixed)
commit
e44cd671b2
|
@ -82,6 +82,14 @@ ver. 0.10.5-dev-1 (20??/??/??) - development edition
|
|||
### Enhancements
|
||||
* jail-reader extended (amend to gh-1622): actions support multi-line options now (interpolations
|
||||
containing new-line);
|
||||
* fail2ban-client: extended to ban/unban multiple tickets (see gh-2351, gh-2349);
|
||||
Syntax:
|
||||
- `fail2ban-client set <jain> banip <ip1> ... <ipN>`
|
||||
- `fail2ban-client set <jain> unbanip [--report-absent] <ip1> ... <ipN>`
|
||||
* fail2ban-client: extended with new feature which allows to inform fail2ban about single or multiple
|
||||
attempts (failure) for IP (resp. failure-ID), see gh-2351;
|
||||
Syntax:
|
||||
- `fail2ban-client set <jail> attempt <ip> [<failure-message1> ... <failure-messageN>]`
|
||||
|
||||
|
||||
ver. 0.10.4 (2018/10/04) - ten-four-on-due-date-ten-four
|
||||
|
|
1
MANIFEST
1
MANIFEST
|
@ -220,6 +220,7 @@ fail2ban/tests/actiontestcase.py
|
|||
fail2ban/tests/banmanagertestcase.py
|
||||
fail2ban/tests/clientbeautifiertestcase.py
|
||||
fail2ban/tests/clientreadertestcase.py
|
||||
fail2ban/tests/config/action.d/action.conf
|
||||
fail2ban/tests/config/action.d/brokenaction.conf
|
||||
fail2ban/tests/config/fail2ban.conf
|
||||
fail2ban/tests/config/filter.d/simple.conf
|
||||
|
|
|
@ -186,8 +186,7 @@ class Fail2banClient(Fail2banCmdLine, Thread):
|
|||
logSys.error("Fail2ban seems to be in unexpected state (not running but the socket exists)")
|
||||
return None
|
||||
|
||||
stream.append(['server-status'])
|
||||
return stream
|
||||
return [["server-stream", stream], ['server-status']]
|
||||
|
||||
##
|
||||
def __startServer(self, background=True):
|
||||
|
|
|
@ -253,8 +253,7 @@ class JailReader(ConfigReader):
|
|||
elif opt == "backend":
|
||||
backend = value
|
||||
elif opt == "ignoreip":
|
||||
for ip in splitwords(value):
|
||||
stream.append(["set", self.__name, "addignoreip", ip])
|
||||
stream.append(["set", self.__name, "addignoreip"] + splitwords(value))
|
||||
elif opt in ("failregex", "ignoreregex"):
|
||||
multi = []
|
||||
for regex in value.split('\n'):
|
||||
|
|
|
@ -99,8 +99,9 @@ protocol = [
|
|||
["set <JAIL> bantime <TIME>", "sets the number of seconds <TIME> a host will be banned for <JAIL>"],
|
||||
["set <JAIL> datepattern <PATTERN>", "sets the <PATTERN> used to match date/times for <JAIL>"],
|
||||
["set <JAIL> usedns <VALUE>", "sets the usedns mode for <JAIL>"],
|
||||
["set <JAIL> banip <IP>", "manually Ban <IP> for <JAIL>"],
|
||||
["set <JAIL> unbanip <IP>", "manually Unban <IP> in <JAIL>"],
|
||||
["set <JAIL> attempt <IP> [<failure1> ... <failureN>]", "manually notify about <IP> failure"],
|
||||
["set <JAIL> banip <IP> ... <IP>", "manually Ban <IP> for <JAIL>"],
|
||||
["set <JAIL> unbanip [--report-absent] <IP> ... <IP>", "manually Unban <IP> in <JAIL>"],
|
||||
["set <JAIL> maxretry <RETRY>", "sets the number of failures <RETRY> before banning the host for <JAIL>"],
|
||||
["set <JAIL> maxlines <LINES>", "sets the number of <LINES> to buffer for regex search for <JAIL>"],
|
||||
["set <JAIL> addaction <ACT>[ <PYTHONFILE> <JSONKWARGS>]", "adds a new action named <ACT> for <JAIL>. Optionally for a Python based action, a <PYTHONFILE> and <JSONKWARGS> can be specified, else will be a Command Action"],
|
||||
|
|
|
@ -35,6 +35,7 @@ except ImportError:
|
|||
OrderedDict = dict
|
||||
|
||||
from .banmanager import BanManager, BanTicket
|
||||
from .ipdns import IPAddr
|
||||
from .jailthread import JailThread
|
||||
from .action import ActionBase, CommandAction, CallingMap
|
||||
from .mytime import MyTime
|
||||
|
@ -214,6 +215,19 @@ class Actions(JailThread, Mapping):
|
|||
"""
|
||||
return self.__banManager.getBanList(ordered=True, withTime=withTime)
|
||||
|
||||
def addBannedIP(self, ip):
|
||||
"""Ban an IP or list of IPs."""
|
||||
unixTime = MyTime.time()
|
||||
|
||||
if isinstance(ip, list):
|
||||
# Multiple IPs:
|
||||
tickets = (BanTicket(ip if isinstance(ip, IPAddr) else IPAddr(ip), unixTime) for ip in ip)
|
||||
else:
|
||||
# Single IP:
|
||||
tickets = (BanTicket(ip if isinstance(ip, IPAddr) else IPAddr(ip), unixTime),)
|
||||
|
||||
return self.__checkBan(tickets)
|
||||
|
||||
def removeBannedIP(self, ip=None, db=True, ifexists=False):
|
||||
"""Removes banned IP calling actions' unban method
|
||||
|
||||
|
@ -222,8 +236,8 @@ class Actions(JailThread, Mapping):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
ip : str or IPAddr or None
|
||||
The IP address to unban or all IPs if None
|
||||
ip : list, str, IPAddr or None
|
||||
The IP address (or multiple IPs as list) to unban or all IPs if None
|
||||
|
||||
Raises
|
||||
------
|
||||
|
@ -233,6 +247,19 @@ class Actions(JailThread, Mapping):
|
|||
# Unban all?
|
||||
if ip is None:
|
||||
return self.__flushBan(db)
|
||||
# Multiple IPs:
|
||||
if isinstance(ip, list):
|
||||
missed = []
|
||||
cnt = 0
|
||||
for i in ip:
|
||||
try:
|
||||
cnt += self.removeBannedIP(i, db, ifexists)
|
||||
except ValueError:
|
||||
if not ifexists:
|
||||
missed.append(i)
|
||||
if missed:
|
||||
raise ValueError("not banned: %r" % missed)
|
||||
return cnt
|
||||
# Single IP:
|
||||
# Always delete ip from database (also if currently not banned)
|
||||
if db and self._jail.database is not None:
|
||||
|
@ -243,9 +270,11 @@ class Actions(JailThread, Mapping):
|
|||
# Unban the IP.
|
||||
self.__unBan(ticket)
|
||||
else:
|
||||
msg = "%s is not banned" % ip
|
||||
logSys.log(logging.MSG, msg)
|
||||
if ifexists:
|
||||
return 0
|
||||
raise ValueError("%s is not banned" % ip)
|
||||
raise ValueError(msg)
|
||||
return 1
|
||||
|
||||
|
||||
|
@ -391,11 +420,20 @@ class Actions(JailThread, Mapping):
|
|||
aInfo = Actions.ActionInfo(ticket, self._jail)
|
||||
return aInfo
|
||||
|
||||
def __getFailTickets(self, count=100):
|
||||
"""Generator to get maximal count failure tickets from fail-manager."""
|
||||
cnt = 0
|
||||
while cnt < count:
|
||||
ticket = self._jail.getFailTicket()
|
||||
if not ticket:
|
||||
break
|
||||
yield ticket
|
||||
cnt += 1
|
||||
|
||||
def __checkBan(self):
|
||||
def __checkBan(self, tickets=None):
|
||||
"""Check for IP address to ban.
|
||||
|
||||
Look in the jail queue for FailTicket. If a ticket is available,
|
||||
If tickets are not specified look in the jail queue for FailTicket. If a ticket is available,
|
||||
it executes the "ban" command and adds a ticket to the BanManager.
|
||||
|
||||
Returns
|
||||
|
@ -404,10 +442,9 @@ class Actions(JailThread, Mapping):
|
|||
True if an IP address get banned.
|
||||
"""
|
||||
cnt = 0
|
||||
while cnt < 100:
|
||||
ticket = self._jail.getFailTicket()
|
||||
if not ticket:
|
||||
break
|
||||
if not tickets:
|
||||
tickets = self.__getFailTickets()
|
||||
for ticket in tickets:
|
||||
|
||||
bTicket = BanTicket.wrap(ticket)
|
||||
btime = ticket.getBanTime(self.__banManager.getBanTime())
|
||||
|
|
|
@ -162,7 +162,7 @@ class FailManager:
|
|||
|
||||
def toBan(self, fid=None):
|
||||
with self.__lock:
|
||||
for fid in ([fid] if fid != None and fid in self.__failList else self.__failList):
|
||||
for fid in ([fid] if fid is not None and fid in self.__failList else self.__failList):
|
||||
data = self.__failList[fid]
|
||||
if data.getRetry() >= self.__maxRetry:
|
||||
del self.__failList[fid]
|
||||
|
|
|
@ -428,23 +428,9 @@ class Filter(JailThread):
|
|||
)
|
||||
else:
|
||||
self.__ignoreCache = None
|
||||
##
|
||||
# Ban an IP - http://blogs.buanzo.com.ar/2009/04/fail2ban-patch-ban-ip-address-manually.html
|
||||
# Arturo 'Buanzo' Busleiman <buanzo@buanzo.com.ar>
|
||||
#
|
||||
# to enable banip fail2ban-client BAN command
|
||||
|
||||
def addBannedIP(self, ip):
|
||||
if not isinstance(ip, IPAddr):
|
||||
ip = IPAddr(ip)
|
||||
|
||||
unixTime = MyTime.time()
|
||||
ticket = FailTicket(ip, unixTime)
|
||||
if self._inIgnoreIPList(ip, ticket, log_ignore=False):
|
||||
logSys.warning('Requested to manually ban an ignored IP %s. User knows best. Proceeding to ban it.', ip)
|
||||
self.failManager.addFailure(ticket, self.failManager.getMaxRetry())
|
||||
|
||||
# Perform the banning of the IP now.
|
||||
def performBan(self, ip=None):
|
||||
"""Performs a ban for IPs (or given ip) that are reached maxretry of the jail."""
|
||||
try: # pragma: no branch - exception is the only way out
|
||||
while True:
|
||||
ticket = self.failManager.toBan(ip)
|
||||
|
@ -452,7 +438,24 @@ class Filter(JailThread):
|
|||
except FailManagerEmpty:
|
||||
self.failManager.cleanup(MyTime.time())
|
||||
|
||||
return ip
|
||||
def addAttempt(self, ip, *matches):
|
||||
"""Generate a failed attempt for ip"""
|
||||
if not isinstance(ip, IPAddr):
|
||||
ip = IPAddr(ip)
|
||||
matches = list(matches) # tuple to list
|
||||
|
||||
# Generate the failure attempt for the IP:
|
||||
unixTime = MyTime.time()
|
||||
ticket = FailTicket(ip, unixTime, matches=matches)
|
||||
logSys.info(
|
||||
"[%s] Attempt %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
self.failManager.addFailure(ticket, len(matches) or 1)
|
||||
|
||||
# Perform the ban if this attempt is resulted to:
|
||||
self.performBan(ip)
|
||||
|
||||
return 1
|
||||
|
||||
##
|
||||
# Ignore own IP/DNS.
|
||||
|
|
|
@ -79,12 +79,7 @@ class FilterGamin(FileFilter):
|
|||
this is a common logic and must be shared/provided by FileFilter
|
||||
"""
|
||||
self.getFailures(path)
|
||||
try:
|
||||
while True:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
except FailManagerEmpty:
|
||||
self.failManager.cleanup(MyTime.time())
|
||||
self.performBan()
|
||||
self.__modified = False
|
||||
|
||||
##
|
||||
|
|
|
@ -117,12 +117,7 @@ class FilterPoll(FileFilter):
|
|||
|
||||
self.ticks += 1
|
||||
if self.__modified:
|
||||
try:
|
||||
while True:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
except FailManagerEmpty:
|
||||
self.failManager.cleanup(MyTime.time())
|
||||
self.performBan()
|
||||
self.__modified = False
|
||||
except Exception as e: # pragma: no cover
|
||||
if not self.active: # if not active - error by stop...
|
||||
|
|
|
@ -140,12 +140,7 @@ class FilterPyinotify(FileFilter):
|
|||
"""
|
||||
if not self.idle:
|
||||
self.getFailures(path)
|
||||
try:
|
||||
while True:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
except FailManagerEmpty:
|
||||
self.failManager.cleanup(MyTime.time())
|
||||
self.performBan()
|
||||
self.__modified = False
|
||||
|
||||
def _addPending(self, path, reason, isDir=False):
|
||||
|
|
|
@ -300,12 +300,8 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover
|
|||
else:
|
||||
break
|
||||
if self.__modified:
|
||||
try:
|
||||
while True:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
except FailManagerEmpty:
|
||||
self.failManager.cleanup(MyTime.time())
|
||||
self.performBan()
|
||||
self.__modified = 0
|
||||
except Exception as e: # pragma: no cover
|
||||
if not self.active: # if not active - error by stop...
|
||||
break
|
||||
|
|
|
@ -489,22 +489,24 @@ class Server:
|
|||
def setBanTime(self, name, value):
|
||||
self.__jails[name].actions.setBanTime(value)
|
||||
|
||||
def addAttemptIP(self, name, *args):
|
||||
return self.__jails[name].filter.addAttempt(*args)
|
||||
|
||||
def setBanIP(self, name, value):
|
||||
return self.__jails[name].filter.addBannedIP(value)
|
||||
|
||||
def setUnbanIP(self, name=None, value=None):
|
||||
return self.__jails[name].actions.addBannedIP(value)
|
||||
|
||||
def setUnbanIP(self, name=None, value=None, ifexists=True):
|
||||
if name is not None:
|
||||
# in all jails:
|
||||
# single jail:
|
||||
jails = [self.__jails[name]]
|
||||
else:
|
||||
# single jail:
|
||||
# in all jails:
|
||||
jails = self.__jails.values()
|
||||
# unban given or all (if value is None):
|
||||
cnt = 0
|
||||
ifexists |= (name is None)
|
||||
for jail in jails:
|
||||
cnt += jail.actions.removeBannedIP(value, ifexists=(name is None))
|
||||
if value and not cnt:
|
||||
logSys.info("%s is not banned", value)
|
||||
cnt += jail.actions.removeBannedIP(value, ifexists=ifexists)
|
||||
return cnt
|
||||
|
||||
def getBanTime(self, name):
|
||||
|
|
|
@ -43,6 +43,7 @@ class Transmitter:
|
|||
|
||||
def __init__(self, server):
|
||||
self.__server = server
|
||||
self.__quiet = 0
|
||||
|
||||
##
|
||||
# Proceeds a command.
|
||||
|
@ -69,9 +70,10 @@ class Transmitter:
|
|||
#
|
||||
|
||||
def __commandHandler(self, command):
|
||||
if command[0] == "ping":
|
||||
name = command[0]
|
||||
if name == "ping":
|
||||
return "pong"
|
||||
elif command[0] == "add":
|
||||
elif name == "add":
|
||||
name = command[1]
|
||||
if name == "--all":
|
||||
raise Exception("Reserved name %r" % (name,))
|
||||
|
@ -81,11 +83,15 @@ class Transmitter:
|
|||
backend = "auto"
|
||||
self.__server.addJail(name, backend)
|
||||
return name
|
||||
elif command[0] == "start":
|
||||
elif name == "multi-set":
|
||||
return self.__commandSet(command[1:], True)
|
||||
elif name == "set":
|
||||
return self.__commandSet(command[1:])
|
||||
elif name == "start":
|
||||
name = command[1]
|
||||
self.__server.startJail(name)
|
||||
return None
|
||||
elif command[0] == "stop":
|
||||
elif name == "stop":
|
||||
if len(command) == 1:
|
||||
self.__server.quit()
|
||||
elif command[1] == "--all":
|
||||
|
@ -94,47 +100,50 @@ class Transmitter:
|
|||
name = command[1]
|
||||
self.__server.stopJail(name)
|
||||
return None
|
||||
elif command[0] == "reload":
|
||||
elif name == "reload":
|
||||
opts = command[1:3]
|
||||
self.__quiet = 1
|
||||
try:
|
||||
self.__server.reloadJails(*opts, begin=True)
|
||||
for cmd in command[3]:
|
||||
self.__commandHandler(cmd)
|
||||
finally:
|
||||
self.__quiet = 0
|
||||
self.__server.reloadJails(*opts, begin=False)
|
||||
return 'OK'
|
||||
elif len(command) >= 2 and command[0] == "unban":
|
||||
elif name == "unban" and len(command) >= 2:
|
||||
# unban in all jails:
|
||||
value = command[1:]
|
||||
# if all ips:
|
||||
if len(value) == 1 and value[0] == "--all":
|
||||
return self.__server.setUnbanIP()
|
||||
cnt = 0
|
||||
for value in value:
|
||||
cnt += self.__server.setUnbanIP(None, value)
|
||||
return cnt
|
||||
elif command[0] == "echo":
|
||||
return self.__server.setUnbanIP(None, value)
|
||||
elif name == "echo":
|
||||
return command[1:]
|
||||
elif command[0] == "server-status":
|
||||
elif name == "server-status":
|
||||
logSys.debug("Status: ready")
|
||||
return "Server ready"
|
||||
elif command[0] == "sleep":
|
||||
elif name == "server-stream":
|
||||
self.__quiet = 1
|
||||
try:
|
||||
for cmd in command[1]:
|
||||
self.__commandHandler(cmd)
|
||||
finally:
|
||||
self.__quiet = 0
|
||||
return None
|
||||
elif name == "sleep":
|
||||
value = command[1]
|
||||
time.sleep(float(value))
|
||||
return None
|
||||
elif command[0] == "flushlogs":
|
||||
elif name == "flushlogs":
|
||||
return self.__server.flushLogs()
|
||||
elif command[0] == "multi-set":
|
||||
return self.__commandSet(command[1:], True)
|
||||
elif command[0] == "set":
|
||||
return self.__commandSet(command[1:])
|
||||
elif command[0] == "get":
|
||||
elif name == "get":
|
||||
return self.__commandGet(command[1:])
|
||||
elif command[0] == "status":
|
||||
elif name == "status":
|
||||
return self.status(command[1:])
|
||||
elif command[0] == "version":
|
||||
elif name == "version":
|
||||
return version.version
|
||||
elif command[0] == "config-error":
|
||||
elif name == "config-error":
|
||||
logSys.error(command[1])
|
||||
return None
|
||||
raise Exception("Invalid command")
|
||||
|
@ -145,16 +154,19 @@ class Transmitter:
|
|||
if name == "loglevel":
|
||||
value = command[1]
|
||||
self.__server.setLogLevel(value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getLogLevel()
|
||||
elif name == "logtarget":
|
||||
value = command[1]
|
||||
if self.__server.setLogTarget(value):
|
||||
if self.__quiet: return
|
||||
return self.__server.getLogTarget()
|
||||
else:
|
||||
raise Exception("Failed to change log target")
|
||||
elif name == "syslogsocket":
|
||||
value = command[1]
|
||||
if self.__server.setSyslogSocket(value):
|
||||
if self.__quiet: return
|
||||
return self.__server.getSyslogSocket()
|
||||
else:
|
||||
raise Exception("Failed to change syslog socket")
|
||||
|
@ -165,6 +177,7 @@ class Transmitter:
|
|||
if db is None:
|
||||
return None
|
||||
else:
|
||||
if self.__quiet: return
|
||||
return db.filename
|
||||
elif name == "dbpurgeage":
|
||||
db = self.__server.getDatabase()
|
||||
|
@ -173,6 +186,7 @@ class Transmitter:
|
|||
return None
|
||||
else:
|
||||
db.purgeage = command[1]
|
||||
if self.__quiet: return
|
||||
return db.purgeage
|
||||
# Jail
|
||||
elif command[1] == "idle":
|
||||
|
@ -182,27 +196,33 @@ class Transmitter:
|
|||
self.__server.setIdleJail(name, False)
|
||||
else:
|
||||
raise Exception("Invalid idle option, must be 'on' or 'off'")
|
||||
if self.__quiet: return
|
||||
return self.__server.getIdleJail(name)
|
||||
# Filter
|
||||
elif command[1] == "ignoreself":
|
||||
value = command[2]
|
||||
self.__server.setIgnoreSelf(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getIgnoreSelf(name)
|
||||
elif command[1] == "addignoreip":
|
||||
value = command[2]
|
||||
self.__server.addIgnoreIP(name, value)
|
||||
for value in command[2:]:
|
||||
self.__server.addIgnoreIP(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getIgnoreIP(name)
|
||||
elif command[1] == "delignoreip":
|
||||
value = command[2]
|
||||
self.__server.delIgnoreIP(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getIgnoreIP(name)
|
||||
elif command[1] == "ignorecommand":
|
||||
value = command[2]
|
||||
self.__server.setIgnoreCommand(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getIgnoreCommand(name)
|
||||
elif command[1] == "ignorecache":
|
||||
value = command[2]
|
||||
self.__server.setIgnoreCache(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getIgnoreCache(name)
|
||||
elif command[1] == "addlogpath":
|
||||
value = command[2]
|
||||
|
@ -215,93 +235,120 @@ class Transmitter:
|
|||
elif len(command) > 4:
|
||||
raise ValueError("Only one file can be added at a time")
|
||||
self.__server.addLogPath(name, value, tail)
|
||||
if self.__quiet: return
|
||||
return self.__server.getLogPath(name)
|
||||
elif command[1] == "dellogpath":
|
||||
value = command[2]
|
||||
self.__server.delLogPath(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getLogPath(name)
|
||||
elif command[1] == "logencoding":
|
||||
value = command[2]
|
||||
self.__server.setLogEncoding(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getLogEncoding(name)
|
||||
elif command[1] == "addjournalmatch": # pragma: systemd no cover
|
||||
value = command[2:]
|
||||
self.__server.addJournalMatch(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getJournalMatch(name)
|
||||
elif command[1] == "deljournalmatch": # pragma: systemd no cover
|
||||
value = command[2:]
|
||||
self.__server.delJournalMatch(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getJournalMatch(name)
|
||||
elif command[1] == "prefregex":
|
||||
value = command[2]
|
||||
self.__server.setPrefRegex(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getPrefRegex(name)
|
||||
elif command[1] == "addfailregex":
|
||||
value = command[2]
|
||||
self.__server.addFailRegex(name, value, multiple=multiple)
|
||||
if multiple:
|
||||
return True
|
||||
if self.__quiet: return
|
||||
return self.__server.getFailRegex(name)
|
||||
elif command[1] == "delfailregex":
|
||||
value = int(command[2])
|
||||
self.__server.delFailRegex(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getFailRegex(name)
|
||||
elif command[1] == "addignoreregex":
|
||||
value = command[2]
|
||||
self.__server.addIgnoreRegex(name, value, multiple=multiple)
|
||||
if multiple:
|
||||
return True
|
||||
if self.__quiet: return
|
||||
return self.__server.getIgnoreRegex(name)
|
||||
elif command[1] == "delignoreregex":
|
||||
value = int(command[2])
|
||||
self.__server.delIgnoreRegex(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getIgnoreRegex(name)
|
||||
elif command[1] == "usedns":
|
||||
value = command[2]
|
||||
self.__server.setUseDns(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getUseDns(name)
|
||||
elif command[1] == "findtime":
|
||||
value = command[2]
|
||||
self.__server.setFindTime(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getFindTime(name)
|
||||
elif command[1] == "datepattern":
|
||||
value = command[2]
|
||||
self.__server.setDatePattern(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getDatePattern(name)
|
||||
elif command[1] == "logtimezone":
|
||||
value = command[2]
|
||||
self.__server.setLogTimeZone(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getLogTimeZone(name)
|
||||
elif command[1] == "maxretry":
|
||||
value = command[2]
|
||||
self.__server.setMaxRetry(name, int(value))
|
||||
if self.__quiet: return
|
||||
return self.__server.getMaxRetry(name)
|
||||
elif command[1] == "maxlines":
|
||||
value = command[2]
|
||||
self.__server.setMaxLines(name, int(value))
|
||||
if self.__quiet: return
|
||||
return self.__server.getMaxLines(name)
|
||||
# command
|
||||
elif command[1] == "bantime":
|
||||
value = command[2]
|
||||
self.__server.setBanTime(name, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getBanTime(name)
|
||||
elif command[1] == "attempt":
|
||||
value = command[2:]
|
||||
if self.__quiet: return
|
||||
return self.__server.addAttemptIP(name, *value)
|
||||
elif command[1].startswith("bantime."):
|
||||
value = command[2]
|
||||
opt = command[1][len("bantime."):]
|
||||
self.__server.setBanTimeExtra(name, opt, value)
|
||||
if self.__quiet: return
|
||||
return self.__server.getBanTimeExtra(name, opt)
|
||||
elif command[1] == "banip":
|
||||
value = command[2]
|
||||
value = command[2:]
|
||||
return self.__server.setBanIP(name,value)
|
||||
elif command[1] == "unbanip":
|
||||
value = command[2]
|
||||
self.__server.setUnbanIP(name, value)
|
||||
return value
|
||||
ifexists = True
|
||||
if command[2] != "--report-absent":
|
||||
value = command[2:]
|
||||
else:
|
||||
ifexists = False
|
||||
value = command[3:]
|
||||
return self.__server.setUnbanIP(name, value, ifexists=ifexists)
|
||||
elif command[1] == "addaction":
|
||||
args = [command[2]]
|
||||
if len(command) > 3:
|
||||
args.extend([command[3], json.loads(command[4])])
|
||||
self.__server.addAction(name, *args)
|
||||
if self.__quiet: return
|
||||
return args[0]
|
||||
elif command[1] == "delaction":
|
||||
value = command[2]
|
||||
|
@ -325,10 +372,12 @@ class Transmitter:
|
|||
actionkey = command[3]
|
||||
if callable(getattr(action, actionkey, None)):
|
||||
actionvalue = json.loads(command[4]) if len(command)>4 else {}
|
||||
if self.__quiet: return
|
||||
return getattr(action, actionkey)(**actionvalue)
|
||||
else:
|
||||
actionvalue = command[4]
|
||||
setattr(action, actionkey, actionvalue)
|
||||
if self.__quiet: return
|
||||
return getattr(action, actionkey)
|
||||
raise Exception("Invalid command %r (no set action or not yet implemented)" % (command[1],))
|
||||
|
||||
|
|
|
@ -78,6 +78,16 @@ class ExecuteActions(LogCaptureTestCase):
|
|||
self.assertEqual(self.__actions.getBanTime(),127)
|
||||
self.assertRaises(ValueError, self.__actions.removeBannedIP, '127.0.0.1')
|
||||
|
||||
def testAddBannedIP(self):
|
||||
self.assertEqual(self.__actions.addBannedIP('192.0.2.1'), 1)
|
||||
self.assertLogged('Ban 192.0.2.1')
|
||||
self.pruneLog()
|
||||
self.assertEqual(self.__actions.addBannedIP(['192.0.2.1', '192.0.2.2', '192.0.2.3']), 2)
|
||||
self.assertLogged('192.0.2.1 already banned')
|
||||
self.assertNotLogged('Ban 192.0.2.1')
|
||||
self.assertLogged('Ban 192.0.2.2')
|
||||
self.assertLogged('Ban 192.0.2.3')
|
||||
|
||||
def testActionsOutput(self):
|
||||
self.defaultActions()
|
||||
self.__actions.start()
|
||||
|
|
|
@ -878,9 +878,9 @@ class JailsReaderTest(LogCaptureTestCase):
|
|||
% (option, commands))
|
||||
|
||||
# Set up of logging should come first
|
||||
self.assertEqual(find_set('syslogsocket'), 0)
|
||||
self.assertEqual(find_set('loglevel'), 1)
|
||||
self.assertEqual(find_set('logtarget'), 2)
|
||||
self.assertTrue(
|
||||
find_set('syslogsocket') < find_set('loglevel') < find_set('logtarget')
|
||||
)
|
||||
# then dbfile should be before dbpurgeage
|
||||
self.assertTrue(find_set('dbpurgeage') > find_set('dbfile'))
|
||||
|
||||
|
@ -888,7 +888,7 @@ class JailsReaderTest(LogCaptureTestCase):
|
|||
# server
|
||||
self.assertSortedEqual(commands,
|
||||
[['set', 'dbfile',
|
||||
'/var/lib/fail2ban/fail2ban.sqlite3'],
|
||||
'/var/lib/fail2ban/fail2ban.sqlite3'],
|
||||
['set', 'dbpurgeage', '1d'],
|
||||
['set', 'loglevel', "INFO"],
|
||||
['set', 'logtarget', '/var/log/fail2ban.log'],
|
||||
|
|
|
@ -1165,7 +1165,7 @@ class Fail2banServerTest(Fail2banClientServerBase):
|
|||
"--async", "unban", "192.0.2.5", "192.0.2.6")
|
||||
self.assertLogged(
|
||||
"192.0.2.5 is not banned",
|
||||
"[test-jail1] Unban 192.0.2.6", all=True
|
||||
"[test-jail1] Unban 192.0.2.6", all=True, wait=MID_WAITTIME
|
||||
)
|
||||
|
||||
# reload all (one jail) with unban all:
|
||||
|
@ -1238,6 +1238,14 @@ class Fail2banServerTest(Fail2banClientServerBase):
|
|||
"Jail 'test-jail1' stopped",
|
||||
"Jail 'test-jail1' started", all=True, wait=MID_WAITTIME)
|
||||
|
||||
# Coverage for pickle of IPAddr (as string):
|
||||
self.pruneLog("[test-phase end-3]")
|
||||
self.execCmd(SUCCESS, startparams,
|
||||
"--async", "set", "test-jail1", "addignoreip", "192.0.2.1/32", "2001:DB8::1/96")
|
||||
self.execCmd(SUCCESS, startparams,
|
||||
"--async", "get", "test-jail1", "ignoreip")
|
||||
self.assertLogged("192.0.2.1/32", "2001:DB8::1/96", all=True)
|
||||
|
||||
# test action.d/nginx-block-map.conf --
|
||||
@unittest.F2B.skip_if_cfg_missing(action="nginx-block-map")
|
||||
@with_foreground_server_thread(startextra={
|
||||
|
|
|
@ -394,11 +394,13 @@ class IgnoreIP(LogCaptureTestCase):
|
|||
self.assertLogged('Ignore 192.168.1.32')
|
||||
tearDownMyTime()
|
||||
|
||||
def testIgnoreAddBannedIP(self):
|
||||
self.filter.addIgnoreIP('192.168.1.0/25')
|
||||
self.filter.addBannedIP('192.168.1.32')
|
||||
self.assertNotLogged('Ignore 192.168.1.32')
|
||||
self.assertLogged('Requested to manually ban an ignored IP 192.168.1.32. User knows best. Proceeding to ban it.')
|
||||
def testAddAttempt(self):
|
||||
self.filter.setMaxRetry(3)
|
||||
for i in xrange(1, 1+3):
|
||||
self.filter.addAttempt('192.0.2.1')
|
||||
self.assertLogged('Attempt 192.0.2.1', '192.0.2.1:%d' % i, all=True, wait=True)
|
||||
self.jail.actions._Actions__checkBan()
|
||||
self.assertLogged('Ban 192.0.2.1', wait=True)
|
||||
|
||||
def testIgnoreCommand(self):
|
||||
self.filter.ignoreCommand = sys.executable + ' ' + os.path.join(TEST_FILES_DIR, "ignorecommand.py <ip>")
|
||||
|
|
|
@ -330,22 +330,49 @@ class Transmitter(TransmitterBase):
|
|||
self.server.startJail(self.jailName) # Jail must be started
|
||||
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", self.jailName, "banip", "127.0.0.1"]),
|
||||
(0, "127.0.0.1"))
|
||||
self.assertLogged("Ban 127.0.0.1", wait=True) # Give chance to ban
|
||||
self.transm.proceed(["set", self.jailName, "banip", "192.0.2.1", "192.0.2.1", "192.0.2.2"]),
|
||||
(0, 2))
|
||||
self.assertLogged("Ban 192.0.2.1", "Ban 192.0.2.2", all=True, wait=True) # Give chance to ban
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", self.jailName, "banip", "Badger"]),
|
||||
(0, "Badger")) #NOTE: Is IP address validated? Is DNS Lookup done?
|
||||
(0, 1)) #NOTE: Is IP address validated? Is DNS Lookup done?
|
||||
self.assertLogged("Ban Badger", wait=True) # Give chance to ban
|
||||
# Unban IP
|
||||
# Unban IP (first/last are not banned, so checking unban of both other succeeds):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "unbanip", "127.0.0.1"]),
|
||||
(0, "127.0.0.1"))
|
||||
# Unban IP which isn't banned
|
||||
["set", self.jailName, "unbanip", "192.0.2.255", "192.0.2.1", "192.0.2.2", "192.0.2.254"]),
|
||||
(0, 2))
|
||||
self.assertLogged("Unban 192.0.2.1", "Unban 192.0.2.2", all=True, wait=True)
|
||||
self.assertLogged("192.0.2.255 is not banned", "192.0.2.254 is not banned", all=True, wait=True)
|
||||
self.pruneLog()
|
||||
# Unban IP which isn't banned (error):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "unbanip", "192.168.1.1"])[0],1)
|
||||
["set", self.jailName, "unbanip", "--report-absent", "192.0.2.255"])[0],1)
|
||||
# ... (no error, IPs logged only):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(
|
||||
["set", self.jailName, "unbanip", "192.0.2.255", "192.0.2.254"]),(0, 0))
|
||||
self.assertLogged("192.0.2.255 is not banned", "192.0.2.254 is not banned", all=True, wait=True)
|
||||
|
||||
def testJailAttemptIP(self):
|
||||
self.server.startJail(self.jailName) # Jail must be started
|
||||
|
||||
def attempt(ip, matches):
|
||||
return self.transm.proceed(["set", self.jailName, "attempt", ip] + matches)
|
||||
|
||||
self.setGetTest("maxretry", "5", 5, jail=self.jailName)
|
||||
# produce 2 single attempts per IP:
|
||||
for i in (1, 2):
|
||||
for ip in ("192.0.2.1", "192.0.2.2"):
|
||||
self.assertEqual(attempt(ip, ["test failure %d" % i]), (0, 1))
|
||||
self.assertLogged("192.0.2.1:2", "192.0.2.2:2", all=True, wait=True)
|
||||
# this 3 attempts at once should cause a ban:
|
||||
self.assertEqual(attempt(ip, ["test failure %d" % i for i in (3,4,5)]), (0, 1))
|
||||
self.assertLogged("192.0.2.2:5", wait=True)
|
||||
# resulted to ban for "192.0.2.2" but not for "192.0.2.1":
|
||||
self.assertLogged("Ban 192.0.2.2", wait=True)
|
||||
self.assertNotLogged("Ban 192.0.2.1")
|
||||
|
||||
def testJailBanList(self):
|
||||
jail = "TestJailBanList"
|
||||
|
@ -359,13 +386,13 @@ class Transmitter(TransmitterBase):
|
|||
if banip is not None:
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", jail, "banip", banip]),
|
||||
(0, banip))
|
||||
(0, 1))
|
||||
self.assertLogged("Ban %s" % banip, wait=True) # Give chance to ban
|
||||
# Unban IP address
|
||||
if unbanip is not None:
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["set", jail, "unbanip", unbanip]),
|
||||
(0, unbanip))
|
||||
(0, 1))
|
||||
self.assertLogged("Unban %s" % unbanip, wait=True) # Give chance to unban
|
||||
# Compare the list of banned IP addresses with outList
|
||||
self.assertSortedEqual(
|
||||
|
|
|
@ -272,10 +272,13 @@ date/times for <JAIL>
|
|||
\fBset <JAIL> usedns <VALUE>\fR
|
||||
sets the usedns mode for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> banip <IP>\fR
|
||||
\fBset <JAIL> attempt <IP> [<failure1> ... <failureN>]\fR
|
||||
manually notify about <IP> failure
|
||||
.TP
|
||||
\fBset <JAIL> banip <IP> ... <IP>\fR
|
||||
manually Ban <IP> for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> unbanip <IP>\fR
|
||||
\fBset <JAIL> unbanip [\-\-report\-absent] <IP> ... <IP>\fR
|
||||
manually Unban <IP> in <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> maxretry <RETRY>\fR
|
||||
|
|
Loading…
Reference in New Issue