extend `ignorecommand` to use actions-similar replacement (ticket-based now, so capable to interpolate all possible tags)

pull/2176/head
sebres 6 years ago
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);
### New Features
* `ignorecommand` extended to use actions-similar replacement (capable to interpolate
all possible tags like `<ip-host>`, `<family>`, `<fid>`, `F-USER` etc.)
### Enhancements
* `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 time
from .actions import Actions
from .failmanager import FailManagerEmpty, FailManager
from .ipdns import DNSUtils, IPAddr
from .ticket import FailTicket
@ -418,11 +419,12 @@ class Filter(JailThread):
def addBannedIP(self, ip):
if not isinstance(ip, IPAddr):
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()
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.
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
# 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
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)
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:
if self.__ignoreSelf and ip in DNSUtils.getSelfIPs():
self.logIgnoreIp(ip, log_ignore, ignore_source="ignoreself rule")
@ -506,7 +514,11 @@ class Filter(JailThread):
return True
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)
ret, ret_ignore = CommandAction.executeCmd(command, success_codes=(0, 1))
ret_ignore = ret and ret_ignore == 0
@ -549,12 +561,12 @@ class Filter(JailThread):
fail = element[3]
logSys.debug("Processing line with time:%s and ip:%s",
unixTime, ip)
if self.inIgnoreIPList(ip):
tick = FailTicket(ip, unixTime, data=fail)
if self._inIgnoreIPList(ip, tick):
continue
logSys.info(
"[%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)
# reset (halve) error counter (successfully processed line):
if self._errors:

@ -38,7 +38,7 @@ except ImportError:
from ..server.jail import Jail
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.ipdns import DNSUtils, IPAddr
from ..server.mytime import MyTime
@ -409,6 +409,24 @@ class IgnoreIP(LogCaptureTestCase):
self.assertFalse(self.filter.inIgnoreIPList(""))
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):
ip = "93.184.216.34"
for ignore_source in ["dns", "ip", "command"]:

@ -316,6 +316,7 @@ def initTests(opts):
c.set('203.0.113.%s' % i, None)
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):
c.set('2001:db8::ffff', 'test-other')
c.set('87.142.124.10', 'test-host')
if unittest.F2B.no_network: # pragma: no cover
# precache all wrong dns to ip's used in test cases:

Loading…
Cancel
Save