mirror of https://github.com/fail2ban/fail2ban
extend `ignorecommand` to use actions-similar replacement (ticket-based now, so capable to interpolate all possible tags)
parent
11c1bf0149
commit
9b6d17d07e
|
@ -50,6 +50,8 @@ ver. 0.10.4-dev-1 (20??/??/??) - development edition
|
||||||
* systemd: fixed type error on option `journalflags`: an integer is required (gh-2125);
|
* systemd: fixed type error on option `journalflags`: an integer is required (gh-2125);
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
* `ignorecommand` extended to use actions-similar replacement (capable to interpolate
|
||||||
|
all possible tags like `<ip-host>`, `<family>`, `<fid>`, `F-USER` etc.)
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
* `filter.d/dovecot.conf`: extended with tags F-USER (and alternatives) to collect user-logins (gh-2168)
|
* `filter.d/dovecot.conf`: extended with tags F-USER (and alternatives) to collect user-logins (gh-2168)
|
||||||
|
|
|
@ -30,6 +30,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from .actions import Actions
|
||||||
from .failmanager import FailManagerEmpty, FailManager
|
from .failmanager import FailManagerEmpty, FailManager
|
||||||
from .ipdns import DNSUtils, IPAddr
|
from .ipdns import DNSUtils, IPAddr
|
||||||
from .ticket import FailTicket
|
from .ticket import FailTicket
|
||||||
|
@ -418,11 +419,12 @@ class Filter(JailThread):
|
||||||
def addBannedIP(self, ip):
|
def addBannedIP(self, ip):
|
||||||
if not isinstance(ip, IPAddr):
|
if not isinstance(ip, IPAddr):
|
||||||
ip = IPAddr(ip)
|
ip = IPAddr(ip)
|
||||||
if self.inIgnoreIPList(ip, log_ignore=False):
|
|
||||||
logSys.warning('Requested to manually ban an ignored IP %s. User knows best. Proceeding to ban it.', ip)
|
|
||||||
|
|
||||||
unixTime = MyTime.time()
|
unixTime = MyTime.time()
|
||||||
self.failManager.addFailure(FailTicket(ip, unixTime), self.failManager.getMaxRetry())
|
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.
|
# Perform the banning of the IP now.
|
||||||
try: # pragma: no branch - exception is the only way out
|
try: # pragma: no branch - exception is the only way out
|
||||||
|
@ -487,13 +489,19 @@ class Filter(JailThread):
|
||||||
#
|
#
|
||||||
# Check if the given IP address matches an IP address/DNS or a CIDR
|
# Check if the given IP address matches an IP address/DNS or a CIDR
|
||||||
# mask in the ignore list.
|
# mask in the ignore list.
|
||||||
# @param ip IP address object
|
# @param ip IP address object or ticket
|
||||||
# @return True if IP address is in ignore list
|
# @return True if IP address is in ignore list
|
||||||
|
|
||||||
def inIgnoreIPList(self, ip, log_ignore=True):
|
def inIgnoreIPList(self, ip, log_ignore=True):
|
||||||
if not isinstance(ip, IPAddr):
|
ticket = None
|
||||||
|
if isinstance(ip, FailTicket):
|
||||||
|
ticket = ip
|
||||||
|
ip = ticket.getIP()
|
||||||
|
elif not isinstance(ip, IPAddr):
|
||||||
ip = IPAddr(ip)
|
ip = IPAddr(ip)
|
||||||
|
return self._inIgnoreIPList(ip, ticket, log_ignore)
|
||||||
|
|
||||||
|
def _inIgnoreIPList(self, ip, ticket, log_ignore=True):
|
||||||
# check own IPs should be ignored and 'ip' is self IP:
|
# check own IPs should be ignored and 'ip' is self IP:
|
||||||
if self.__ignoreSelf and ip in DNSUtils.getSelfIPs():
|
if self.__ignoreSelf and ip in DNSUtils.getSelfIPs():
|
||||||
self.logIgnoreIp(ip, log_ignore, ignore_source="ignoreself rule")
|
self.logIgnoreIp(ip, log_ignore, ignore_source="ignoreself rule")
|
||||||
|
@ -506,7 +514,11 @@ class Filter(JailThread):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.__ignoreCommand:
|
if self.__ignoreCommand:
|
||||||
command = CommandAction.replaceTag(self.__ignoreCommand, { 'ip': ip } )
|
if ticket:
|
||||||
|
aInfo = Actions.ActionInfo(ticket, self.jail)
|
||||||
|
command = CommandAction.replaceDynamicTags(self.__ignoreCommand, aInfo)
|
||||||
|
else:
|
||||||
|
command = CommandAction.replaceTag(self.__ignoreCommand, { 'ip': ip })
|
||||||
logSys.debug('ignore command: %s', command)
|
logSys.debug('ignore command: %s', command)
|
||||||
ret, ret_ignore = CommandAction.executeCmd(command, success_codes=(0, 1))
|
ret, ret_ignore = CommandAction.executeCmd(command, success_codes=(0, 1))
|
||||||
ret_ignore = ret and ret_ignore == 0
|
ret_ignore = ret and ret_ignore == 0
|
||||||
|
@ -549,12 +561,12 @@ class Filter(JailThread):
|
||||||
fail = element[3]
|
fail = element[3]
|
||||||
logSys.debug("Processing line with time:%s and ip:%s",
|
logSys.debug("Processing line with time:%s and ip:%s",
|
||||||
unixTime, ip)
|
unixTime, ip)
|
||||||
if self.inIgnoreIPList(ip):
|
tick = FailTicket(ip, unixTime, data=fail)
|
||||||
|
if self._inIgnoreIPList(ip, tick):
|
||||||
continue
|
continue
|
||||||
logSys.info(
|
logSys.info(
|
||||||
"[%s] Found %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
|
"[%s] Found %s - %s", self.jailName, ip, datetime.datetime.fromtimestamp(unixTime).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
)
|
)
|
||||||
tick = FailTicket(ip, unixTime, data=fail)
|
|
||||||
self.failManager.addFailure(tick)
|
self.failManager.addFailure(tick)
|
||||||
# reset (halve) error counter (successfully processed line):
|
# reset (halve) error counter (successfully processed line):
|
||||||
if self._errors:
|
if self._errors:
|
||||||
|
|
|
@ -38,7 +38,7 @@ except ImportError:
|
||||||
|
|
||||||
from ..server.jail import Jail
|
from ..server.jail import Jail
|
||||||
from ..server.filterpoll import FilterPoll
|
from ..server.filterpoll import FilterPoll
|
||||||
from ..server.filter import Filter, FileFilter, FileContainer
|
from ..server.filter import FailTicket, Filter, FileFilter, FileContainer
|
||||||
from ..server.failmanager import FailManagerEmpty
|
from ..server.failmanager import FailManagerEmpty
|
||||||
from ..server.ipdns import DNSUtils, IPAddr
|
from ..server.ipdns import DNSUtils, IPAddr
|
||||||
from ..server.mytime import MyTime
|
from ..server.mytime import MyTime
|
||||||
|
@ -409,6 +409,24 @@ class IgnoreIP(LogCaptureTestCase):
|
||||||
self.assertFalse(self.filter.inIgnoreIPList(""))
|
self.assertFalse(self.filter.inIgnoreIPList(""))
|
||||||
self.assertLogged("usage: ignorecommand IP", "returned 10", all=True)
|
self.assertLogged("usage: ignorecommand IP", "returned 10", all=True)
|
||||||
|
|
||||||
|
def testIgnoreCommandForTicket(self):
|
||||||
|
# by host of IP (2001:db8::1 and 2001:db8::ffff map to "test-host" and "test-other" in the test-suite):
|
||||||
|
self.filter.setIgnoreCommand('if [ "<ip-host>" = "test-host" ]; then exit 0; fi; exit 1')
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertTrue(self.filter.inIgnoreIPList(FailTicket("2001:db8::1")))
|
||||||
|
self.assertLogged("returned successfully 0")
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("2001:db8::ffff")))
|
||||||
|
self.assertLogged("returned successfully 1")
|
||||||
|
# by user-name (ignore tester):
|
||||||
|
self.filter.setIgnoreCommand('if [ "<F-USER>" = "tester" ]; then exit 0; fi; exit 1')
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertTrue(self.filter.inIgnoreIPList(FailTicket("tester", data={'user': 'tester'})))
|
||||||
|
self.assertLogged("returned successfully 0")
|
||||||
|
self.pruneLog()
|
||||||
|
self.assertFalse(self.filter.inIgnoreIPList(FailTicket("root", data={'user': 'root'})))
|
||||||
|
self.assertLogged("returned successfully 1", all=True)
|
||||||
|
|
||||||
def testIgnoreCauseOK(self):
|
def testIgnoreCauseOK(self):
|
||||||
ip = "93.184.216.34"
|
ip = "93.184.216.34"
|
||||||
for ignore_source in ["dns", "ip", "command"]:
|
for ignore_source in ["dns", "ip", "command"]:
|
||||||
|
|
|
@ -316,6 +316,7 @@ def initTests(opts):
|
||||||
c.set('203.0.113.%s' % i, None)
|
c.set('203.0.113.%s' % i, None)
|
||||||
c.set('2001:db8::%s' %i, 'test-host')
|
c.set('2001:db8::%s' %i, 'test-host')
|
||||||
# some legal ips used in our test cases (prevent slow dns-resolving and failures if will be changed later):
|
# some legal ips used in our test cases (prevent slow dns-resolving and failures if will be changed later):
|
||||||
|
c.set('2001:db8::ffff', 'test-other')
|
||||||
c.set('87.142.124.10', 'test-host')
|
c.set('87.142.124.10', 'test-host')
|
||||||
if unittest.F2B.no_network: # pragma: no cover
|
if unittest.F2B.no_network: # pragma: no cover
|
||||||
# precache all wrong dns to ip's used in test cases:
|
# precache all wrong dns to ip's used in test cases:
|
||||||
|
|
Loading…
Reference in New Issue