Merge pull request #4001 from sebres/f2b-regex--inverted-out

fail2ban-regex: new feature `-i` or `--invert` to output not-matched lines by `-o` or `--out`
pull/3993/merge
Sergey G. Brester 2025-06-03 22:23:19 +02:00 committed by GitHub
commit cfa3356e0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 39 additions and 5 deletions

View File

@ -84,6 +84,8 @@ ver. 1.1.1-dev-1 (20??/??/??) - development nightly edition
* `filter.d/proxmox.conf` - add support to Proxmox Web GUI (gh-2966) * `filter.d/proxmox.conf` - add support to Proxmox Web GUI (gh-2966)
* `filter.d/openvpn.conf` - new filter and jail for openvpn recognizing failed TLS handshakes (gh-2702) * `filter.d/openvpn.conf` - new filter and jail for openvpn recognizing failed TLS handshakes (gh-2702)
* `filter.d/vaultwarden.conf` - new filter and jail for Vaultwarden (gh-3979) * `filter.d/vaultwarden.conf` - new filter and jail for Vaultwarden (gh-3979)
* `fail2ban-regex` extended with new option `-i` or `--invert` to output not-matched lines by `-o` or `--out` (gh-4001)
ver. 1.1.0 (2024/04/25) - object-found--norad-59479-cospar-2024-069a--altitude-36267km ver. 1.1.0 (2024/04/25) - object-found--norad-59479-cospar-2024-069a--altitude-36267km
----------- -----------

View File

@ -172,6 +172,8 @@ def get_opt_parser():
help="Disable check for all regex's"), help="Disable check for all regex's"),
Option("-o", "--out", action="store", dest="out", default=None, Option("-o", "--out", action="store", dest="out", default=None,
help="Set token to print failure information only (row, id, ip, msg, host, ip4, ip6, dns, matches, ...)"), help="Set token to print failure information only (row, id, ip, msg, host, ip4, ip6, dns, matches, ...)"),
Option("-i", "--invert", action="store_true", dest="invert",
help="Invert the sense of matching, to output non-matching lines."),
Option("--print-no-missed", action='store_true', Option("--print-no-missed", action='store_true',
help="Do not print any missed lines"), help="Do not print any missed lines"),
Option("--print-no-ignored", action='store_true', Option("--print-no-ignored", action='store_true',
@ -529,7 +531,7 @@ class Fail2banRegex(object):
except RegexException as e: # pragma: no cover except RegexException as e: # pragma: no cover
output( 'ERROR: %s' % e ) output( 'ERROR: %s' % e )
return None, 0, None return None, 0, None
if self._filter.getMaxLines() > 1: if self._filter.getMaxLines() > 1 and not self._opts.out:
for bufLine in orgLineBuffer[int(fullBuffer):]: for bufLine in orgLineBuffer[int(fullBuffer):]:
if bufLine not in self._filter._Filter__lineBuffer: if bufLine not in self._filter._Filter__lineBuffer:
try: try:
@ -619,8 +621,10 @@ class Fail2banRegex(object):
def process(self, test_lines): def process(self, test_lines):
t0 = time.time() t0 = time.time()
out = None
if self._opts.out: # get out function if self._opts.out: # get out function
out = self._prepaireOutput() out = self._prepaireOutput()
outinv = self._opts.invert
for line in test_lines: for line in test_lines:
if isinstance(line, tuple): if isinstance(line, tuple):
line_datetimestripped, ret, is_ignored = self.testRegex(line[0], line[1]) line_datetimestripped, ret, is_ignored = self.testRegex(line[0], line[1])
@ -632,8 +636,13 @@ class Fail2banRegex(object):
continue continue
line_datetimestripped, ret, is_ignored = self.testRegex(line) line_datetimestripped, ret, is_ignored = self.testRegex(line)
if self._opts.out: # (formatted) output: if out: # (formatted) output:
if len(ret) > 0 and not is_ignored: out(ret) if len(ret) > 0 and not is_ignored:
if not outinv: out(ret)
elif outinv: # inverted output (currently only time and message as matches):
if not len(ret): # [failRegexIndex, fid, date, fail]
ret = [[-1, "", self._filter._Filter__lastDate, {"fid":"", "matches":[line]}]]
out(ret)
continue continue
if is_ignored: if is_ignored:

View File

@ -432,8 +432,16 @@ class Fail2banRegexTest(LogCaptureTestCase):
self.assertLogged('output: %s' % "['192.0.2.0'", "'ip4': '192.0.2.0'", "'user': 'kevin'", all=True) self.assertLogged('output: %s' % "['192.0.2.0'", "'ip4': '192.0.2.0'", "'user': 'kevin'", all=True)
self.pruneLog() self.pruneLog()
# log msg : # log msg :
self.assertTrue(_test_exec('-o', 'msg', STR_00, RE_00_USER)) nmline = "Dec 31 12:00:00 [sshd] error: PAM: No failure for user from 192.0.2.123"
lines = STR_00+"\n"+nmline
self.assertTrue(_test_exec('-o', 'msg', lines, RE_00_USER))
self.assertLogged('output: %s' % STR_00) self.assertLogged('output: %s' % STR_00)
self.assertNotLogged('output: %s' % nmline)
self.pruneLog()
# log msg (inverted) :
self.assertTrue(_test_exec('-o', 'msg', '-i', lines, RE_00_USER))
self.assertLogged('output: %s' % nmline)
self.assertNotLogged('output: %s' % STR_00)
self.pruneLog() self.pruneLog()
# item of match (user): # item of match (user):
self.assertTrue(_test_exec('-o', 'user', STR_00, RE_00_USER)) self.assertTrue(_test_exec('-o', 'user', STR_00, RE_00_USER))
@ -443,6 +451,17 @@ class Fail2banRegexTest(LogCaptureTestCase):
self.assertTrue(_test_exec('-o', '<ip>, <F-USER>, <family>', STR_00, RE_00_USER)) self.assertTrue(_test_exec('-o', '<ip>, <F-USER>, <family>', STR_00, RE_00_USER))
self.assertLogged('output: %s' % '192.0.2.0, kevin, inet4') self.assertLogged('output: %s' % '192.0.2.0, kevin, inet4')
self.pruneLog() self.pruneLog()
# log msg :
lines = nmline+"\n"+STR_00; # just reverse lines (to cover possible order dependencies)
self.assertTrue(_test_exec('-o', '<time> : <msg>', lines, RE_00_USER))
self.assertLogged('output: %s : %s' % (1104490799.0, STR_00))
self.assertNotLogged('output: %s' % nmline)
self.pruneLog()
# log msg (inverted) :
self.assertTrue(_test_exec('-o', '<time> : <msg>', '-i', lines, RE_00_USER))
self.assertLogged('output: %s : %s' % (1104490800.0, nmline))
self.assertNotLogged('output: %s' % STR_00)
self.pruneLog()
def testStalledIPByNoFailFrmtOutput(self): def testStalledIPByNoFailFrmtOutput(self):
opts = ( opts = (

View File

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH FAIL2BAN-REGEX "1" "April 2024" "fail2ban-regex 1.1.1.dev1" "User Commands" .TH FAIL2BAN-REGEX "1" "June 2025" "fail2ban-regex 1.1.1.dev1" "User Commands"
.SH NAME .SH NAME
fail2ban-regex \- test Fail2ban "failregex" option fail2ban-regex \- test Fail2ban "failregex" option
.SH SYNOPSIS .SH SYNOPSIS
@ -96,6 +96,10 @@ Disable check for all regex's
Set token to print failure information only (row, id, Set token to print failure information only (row, id,
ip, msg, host, ip4, ip6, dns, matches, ...) ip, msg, host, ip4, ip6, dns, matches, ...)
.TP .TP
\fB\-i\fR, \fB\-\-invert\fR
Invert the sense of matching, to output non\-matching
lines.
.TP
\fB\-\-print\-no\-missed\fR \fB\-\-print\-no\-missed\fR
Do not print any missed lines Do not print any missed lines
.TP .TP