From a7775d4bfde813d51a03d7dab9c0b70f748d9670 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 9 Nov 2022 17:42:26 +0100 Subject: [PATCH] New upstream version 1.0.2 --- ChangeLog | 19 +++++++++ config/filter.d/dovecot.conf | 8 ++-- config/filter.d/named-refused.conf | 7 +++- fail2ban/server/filtersystemd.py | 53 +++++++++++++++++++------ fail2ban/tests/fail2banregextestcase.py | 26 ++++++------ fail2ban/tests/files/logs/dovecot | 22 ++++++++++ fail2ban/tests/files/logs/named-refused | 5 +++ fail2ban/version.py | 2 +- man/fail2ban-client.1 | 4 +- man/fail2ban-python.1 | 2 +- man/fail2ban-regex.1 | 2 +- man/fail2ban-server.1 | 4 +- man/fail2ban-testcases.1 | 2 +- 13 files changed, 117 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index d7848d19..8c3be67d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,25 @@ Fail2Ban: Changelog =================== +ver. 1.0.2 (2022/11/09) - finally-war-game-test-tape-not-a-nuclear-alarm +----------- + +### Fixes +* backend `systemd`: code review and several fixes: + - wait only if it is necessary, e. g. in operational mode and if no more entries retrieved (end of journal); + - ensure we give enough time after possible rotation, vacuuming or adding/removing journal files, + and move cursor back and forth to avoid entering dead space +* `filter.d/named-refused.conf`: + - support BIND named log categories, gh-3388 + - allow `info:` as possible error prefix too ("query (cache) denied" may occur as info) +* `filter.d/dovecot.conf`: + - fixes regression introduced in gh-3210: resolve extremely long search by repeated apply of non-greedy RE-part + with following branches (it may be extremely slow up to infinite search depending on message), gh-3370 + - fixes regression and matches new format in aggressive mode too (amend to gh-3210) + +### New Features and Enhancements + + ver. 1.0.1 (2022/09/27) - energy-equals-mass-times-the-speed-of-light-squared ----------- diff --git a/config/filter.d/dovecot.conf b/config/filter.d/dovecot.conf index 0415ecb4..dc3ebbcd 100644 --- a/config/filter.d/dovecot.conf +++ b/config/filter.d/dovecot.conf @@ -7,19 +7,21 @@ before = common.conf [Definition] +_daemon = (?:dovecot(?:-auth)?|auth) + _auth_worker = (?:dovecot: )?auth(?:-worker)? _auth_worker_info = (?:conn \w+:auth(?:-worker)? \([^\)]+\): auth(?:-worker)?<\d+>: )? -_daemon = (?:dovecot(?:-auth)?|auth) +_bypass_reject_reason = (?:: (?:\w+\([^\):]*\) \w+|[^\(]+))* prefregex = ^%(__prefix_line)s(?:%(_auth_worker)s(?:\([^\)]+\))?: )?(?:%(__pam_auth)s(?:\(dovecot:auth\))?: |(?:pop3|imap|managesieve|submission)-login: )?(?:Info: )?%(_auth_worker_info)s.+$ failregex = ^authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=(?:\s+user=\S*)?\s*$ - ^(?:Aborted login|Disconnected|Remote closed connection|Client has quit the connection)(?:: (?:[^\(]+|\w+\([^\)]*\))+)? \((?:auth failed, \d+ attempts(?: in \d+ secs)?|tried to use (?:disabled|disallowed) \S+ auth|proxy dest auth failed)\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=(?:[^>]*(?:, session=<\S+>)?)\s*$ + ^(?:Aborted login|Disconnected|Remote closed connection|Client has quit the connection)%(_bypass_reject_reason)s \((?:auth failed, \d+ attempts(?: in \d+ secs)?|tried to use (?:disabled|disallowed) \S+ auth|proxy dest auth failed)\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=(?:[^>]*(?:, session=<\S+>)?)\s*$ ^pam\(\S+,(?:,\S*)?\): pam_authenticate\(\) failed: (?:User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \([Pp]assword mismatch\?\)|Permission denied)\s*$ ^[a-z\-]{3,15}\(\S*,(?:,\S*)?\): (?:[Uu]nknown user|[Ii]nvalid credentials|[Pp]assword mismatch) > -mdre-aggressive = ^(?:Aborted login|Disconnected|Remote closed connection|Client has quit the connection)(?::(?: [^ \(]+)+)? \((?:no auth attempts|disconnected before auth was ready,|client didn't finish \S+ auth,)(?: (?:in|waited) \d+ secs)?\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=(?:[^>]*(?:, session=<\S+>)?)\s*$ +mdre-aggressive = ^(?:Aborted login|Disconnected|Remote closed connection|Client has quit the connection)%(_bypass_reject_reason)s \((?:no auth attempts|disconnected before auth was ready,|client didn't finish \S+ auth,)(?: (?:in|waited) \d+ secs)?\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=(?:[^>]*(?:, session=<\S+>)?)\s*$ mdre-normal = diff --git a/config/filter.d/named-refused.conf b/config/filter.d/named-refused.conf index 6dbbbf81..798f66e6 100644 --- a/config/filter.d/named-refused.conf +++ b/config/filter.d/named-refused.conf @@ -30,11 +30,14 @@ __pid_re=(?:\[\d+\]) __daemon_re=\(?%(_daemon)s(?:\(\S+\))?\)?:? __daemon_combs_re=(?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:) +_category = (?!error|info)[\w-]+ +_category_re = (?:%(_category)s: )? + # hostname daemon_id spaces # this can be optional (for instance if we match named native log files) -__line_prefix=(?:\s*\S+ %(__daemon_combs_re)s\s+)? +__line_prefix=\s*(?:\S+ %(__daemon_combs_re)s\s+)?%(_category_re)s -prefregex = ^%(__line_prefix)s(?: error:)?\s*client(?: @\S*)? #\S+(?: \([\S.]+\))?: .+\s(?:denied|\(NOTAUTH\))\s*$ +prefregex = ^%(__line_prefix)s(?:(?:error|info):\s*)?client(?: @\S*)? #\S+(?: \([\S.]+\))?: .+\s(?:denied|\(NOTAUTH\))\s*$ failregex = ^(?:view (?:internal|external): )?query(?: \(cache\))? ^zone transfer diff --git a/fail2ban/server/filtersystemd.py b/fail2ban/server/filtersystemd.py index bf6885ef..a83b7a13 100644 --- a/fail2ban/server/filtersystemd.py +++ b/fail2ban/server/filtersystemd.py @@ -312,20 +312,37 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover except OSError: pass # Reading failure, so safe to ignore + wcode = journal.NOP line = None while self.active: # wait for records (or for timeout in sleeptime seconds): try: - ## todo: find better method as wait_for to break (e.g. notify) journal.wait(self.sleeptime), - ## don't use `journal.close()` for it, because in some python/systemd implementation it may - ## cause abnormal program termination - #self.__journal.wait(self.sleeptime) != journal.NOP - ## - ## wait for entries without sleep in intervals, because "sleeping" in journal.wait: - if not logentry: - Utils.wait_for(lambda: not self.active or \ - self.__journal.wait(Utils.DEFAULT_SLEEP_INTERVAL) != journal.NOP, + ## wait for entries using journal.wait: + if wcode == journal.NOP and self.inOperation: + ## todo: find better method as wait_for to break (e.g. notify) journal.wait(self.sleeptime), + ## don't use `journal.close()` for it, because in some python/systemd implementation it may + ## cause abnormal program termination (e. g. segfault) + ## + ## wait for entries without sleep in intervals, because "sleeping" in journal.wait, + ## journal.NOP is 0, so we can wait for non zero (APPEND or INVALIDATE): + wcode = Utils.wait_for(lambda: not self.active and journal.APPEND or \ + self.__journal.wait(Utils.DEFAULT_SLEEP_INTERVAL), self.sleeptime, 0.00001) + ## if invalidate (due to rotation, vacuuming or journal files added/removed etc): + if self.active and wcode == journal.INVALIDATE: + if self.ticks: + logSys.log(logging.DEBUG, "[%s] Invalidate signaled, take a little break (rotation ends)", self.jailName) + time.sleep(self.sleeptime * 0.25) + Utils.wait_for(lambda: not self.active or \ + self.__journal.wait(Utils.DEFAULT_SLEEP_INTERVAL) != journal.INVALIDATE, + self.sleeptime * 3, 0.00001) + if self.ticks: + # move back and forth to ensure do not end up in dead space by rotation or vacuuming, + # if position beyond end of journal (gh-3396) + try: + if self.__journal.get_previous(): self.__journal.get_next() + except OSError: + pass if self.idle: # because journal.wait will returns immediatelly if we have records in journal, # just wait a little bit here for not idle, to prevent hi-load: @@ -360,11 +377,13 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover self.processLineAndAdd(line, tm) self.__modified += 1 if self.__modified >= 100: # todo: should be configurable + wcode = journal.APPEND; # don't need wait - there are still unprocessed entries break else: # "in operation" mode since we don't have messages anymore (reached end of journal): if not self.inOperation: self.inOperationMode() + wcode = journal.NOP; # enter wait - no more entries to process break self.__modified = 0 if self.ticks % 10 == 0: @@ -384,6 +403,7 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover except Exception as e: # pragma: no cover if not self.active: # if not active - error by stop... break + wcode = journal.NOP logSys.error("Caught unhandled exception in main cycle: %r", e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) # incr common error counter: @@ -392,15 +412,20 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover logSys.debug("[%s] filter terminated", self.jailName) # close journal: + self.closeJournal() + + logSys.debug("[%s] filter exited (systemd)", self.jailName) + return True + + def closeJournal(self): try: - if self.__journal: - self.__journal.close() + jnl, self.__journal = self.__journal, None + if jnl: + jnl.close() except Exception as e: # pragma: no cover logSys.error("Close journal failed: %r", e, exc_info=logSys.getEffectiveLevel()<=logging.DEBUG) - logSys.debug("[%s] filter exited (systemd)", self.jailName) - return True def status(self, flavor="basic"): ret = super(FilterSystemd, self).status(flavor=flavor) @@ -422,6 +447,8 @@ class FilterSystemd(JournalFilter): # pragma: systemd no cover def onStop(self): """Stop monitoring of journal. Invoked after run method. """ + # close journal: + self.closeJournal() # ensure positions of pending logs are up-to-date: if self._pendDBUpdates and self.jail.database: self._updateDBPending() diff --git a/fail2ban/tests/fail2banregextestcase.py b/fail2ban/tests/fail2banregextestcase.py index f29f3333..213ea89b 100644 --- a/fail2ban/tests/fail2banregextestcase.py +++ b/fail2ban/tests/fail2banregextestcase.py @@ -36,7 +36,7 @@ from .utils import CONFIG_DIR fail2banregex.logSys = logSys def _test_output(*args): - logSys.notice(args[0]) + logSys.notice('output: %s', args[0]) fail2banregex.output = _test_output @@ -360,57 +360,57 @@ class Fail2banRegexTest(LogCaptureTestCase): def testFrmtOutput(self): # id/ip only: self.assertTrue(_test_exec('-o', 'id', STR_00, RE_00_ID)) - self.assertLogged('kevin') + self.assertLogged('output: %s' % 'kevin') self.pruneLog() # multiple id combined to a tuple (id, tuple_id): self.assertTrue(_test_exec('-o', 'id', '-d', '{^LN-BEG}EPOCH', '1591983743.667 192.0.2.1 192.0.2.2', r'^\s* \S+')) - self.assertLogged(str(('192.0.2.1', '192.0.2.2'))) + self.assertLogged('output: %s' % str(('192.0.2.1', '192.0.2.2'))) self.pruneLog() # multiple id combined to a tuple, id first - (id, tuple_id_1, tuple_id_2): self.assertTrue(_test_exec('-o', 'id', '-d', '{^LN-BEG}EPOCH', '1591983743.667 left 192.0.2.3 right', r'^\s*\S+ \S+')) - self.assertLogged(str(('192.0.2.3', 'left', 'right'))) + self.assertLogged('output: %s' % str(('192.0.2.3', 'left', 'right'))) self.pruneLog() # id had higher precedence as ip-address: self.assertTrue(_test_exec('-o', 'id', '-d', '{^LN-BEG}EPOCH', '1591983743.667 left [192.0.2.4]:12345 right', r'^\s*\S+ : \S+')) - self.assertLogged(str(('[192.0.2.4]:12345', 'left', 'right'))) + self.assertLogged('output: %s' % str(('[192.0.2.4]:12345', 'left', 'right'))) self.pruneLog() # ip is not id anymore (if IP-address deviates from ID): self.assertTrue(_test_exec('-o', 'ip', '-d', '{^LN-BEG}EPOCH', '1591983743.667 left [192.0.2.4]:12345 right', r'^\s*\S+ : \S+')) - self.assertNotLogged(str(('[192.0.2.4]:12345', 'left', 'right'))) - self.assertLogged('192.0.2.4') + self.assertNotLogged('output: %s' % str(('[192.0.2.4]:12345', 'left', 'right'))) + self.assertLogged('output: %s' % '192.0.2.4') self.pruneLog() self.assertTrue(_test_exec('-o', 'ID: | IP:', '-d', '{^LN-BEG}EPOCH', '1591983743.667 left [192.0.2.4]:12345 right', r'^\s*\S+ : \S+')) - self.assertLogged('ID:'+str(('[192.0.2.4]:12345', 'left', 'right'))+' | IP:192.0.2.4') + self.assertLogged('output: %s' % 'ID:'+str(('[192.0.2.4]:12345', 'left', 'right'))+' | IP:192.0.2.4') self.pruneLog() # row with id : self.assertTrue(_test_exec('-o', 'row', STR_00, RE_00_ID)) - self.assertLogged("['kevin'", "'ip4': '192.0.2.0'", "'fid': 'kevin'", all=True) + self.assertLogged('output: %s' % "['kevin'", "'ip4': '192.0.2.0'", "'fid': 'kevin'", all=True) self.pruneLog() # row with ip : self.assertTrue(_test_exec('-o', 'row', STR_00, RE_00_USER)) - self.assertLogged("['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() # log msg : self.assertTrue(_test_exec('-o', 'msg', STR_00, RE_00_USER)) - self.assertLogged(STR_00) + self.assertLogged('output: %s' % STR_00) self.pruneLog() # item of match (user): self.assertTrue(_test_exec('-o', 'user', STR_00, RE_00_USER)) - self.assertLogged('kevin') + self.assertLogged('output: %s' % 'kevin') self.pruneLog() # complex substitution using tags (ip, user, family): self.assertTrue(_test_exec('-o', ', , ', STR_00, RE_00_USER)) - self.assertLogged('192.0.2.0, kevin, inet4') + self.assertLogged('output: %s' % '192.0.2.0, kevin, inet4') self.pruneLog() def testStalledIPByNoFailFrmtOutput(self): diff --git a/fail2ban/tests/files/logs/dovecot b/fail2ban/tests/files/logs/dovecot index 75934c37..0e332961 100644 --- a/fail2ban/tests/files/logs/dovecot +++ b/fail2ban/tests/files/logs/dovecot @@ -115,6 +115,17 @@ Aug 28 06:38:51 s166-62-100-187 dovecot: imap-login: Disconnected (auth failed, # failJSON: { "time": "2004-08-28T06:38:52", "match": true , "host": "192.0.2.4", "desc": "open parenthesis in optional part between Disconnected and (auth failed ...), gh-3210" } Aug 28 06:38:52 s166-62-100-187 dovecot: imap-login: Disconnected: Connection closed: read(size=1003) failed: Connection reset by peer (auth failed, 1 attempts in 0 secs): user=, rip=192.0.2.4, lip=127.0.0.19, session= +# failJSON: { "time": "2004-08-29T01:49:33", "match": false , "desc": "avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: imap-login: Disconnected: Connection closed: read(size=1026) failed: Connection reset by peer (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: read(size=1026) failed: Connection reset by peer +# failJSON: { "time": "2004-08-29T01:49:33", "match": false , "desc": "avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: imap-login: Disconnected: Connection closed: SSL_accept() failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: SSL_accept() failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number +# failJSON: { "time": "2004-08-29T01:49:33", "match": false , "desc": "avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: managesieve-login: Disconnected: Too many invalid commands. (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1 +# failJSON: { "time": "2004-08-29T01:49:33", "match": false , "desc": "avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: managesieve-login: Disconnected: Connection closed: read(size=1007) failed: Connection reset by peer (no auth attempts in 1 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1 +# failJSON: { "time": "2004-08-29T01:49:33", "match": false , "desc": "avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[472]: imap-login: Disconnected: Connection closed: SSL_accept() failed: error:14209102:SSL routines:tls_early_post_process_client_hello:unsupported protocol (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: SSL_accept() failed: error:14209102:SSL routines:tls_early_post_process_client_hello:unsupported protocol + # failJSON: { "time": "2004-08-29T03:17:18", "match": true , "host": "192.0.2.133" } Aug 29 03:17:18 server dovecot: submission-login: Client has quit the connection (auth failed, 1 attempts in 2 secs): user=, method=LOGIN, rip=192.0.2.133, lip=0.0.0.0 # failJSON: { "time": "2004-08-29T03:53:52", "match": true , "host": "192.0.2.169" } @@ -128,6 +139,17 @@ Aug 29 15:33:53 server dovecot: managesieve-login: Disconnected: Too many invali # filterOptions: [{"mode": "aggressive"}] +# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: imap-login: Disconnected: Connection closed: read(size=1026) failed: Connection reset by peer (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: read(size=1026) failed: Connection reset by peer +# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: imap-login: Disconnected: Connection closed: SSL_accept() failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: SSL_accept() failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number +# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: managesieve-login: Disconnected: Too many invalid commands. (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1 +# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[459]: managesieve-login: Disconnected: Connection closed: read(size=1007) failed: Connection reset by peer (no auth attempts in 1 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1 +# failJSON: { "time": "2004-08-29T01:49:33", "match": true , "host": "192.0.2.5", "desc": "matches in aggressive mode, avoid slow RE, gh-3370" } +Aug 29 01:49:33 server dovecot[472]: imap-login: Disconnected: Connection closed: SSL_accept() failed: error:14209102:SSL routines:tls_early_post_process_client_hello:unsupported protocol (no auth attempts in 0 secs): user=<>, rip=192.0.2.5, lip=127.0.0.1, TLS handshaking: SSL_accept() failed: error:14209102:SSL routines:tls_early_post_process_client_hello:unsupported protocol + # failJSON: { "time": "2004-08-29T16:06:58", "match": true , "host": "192.0.2.5" } Aug 29 16:06:58 s166-62-100-187 dovecot: imap-login: Disconnected (disconnected before auth was ready, waited 0 secs): user=<>, rip=192.0.2.5, lip=192.168.1.2, TLS handshaking: SSL_accept() syscall failed: Connection reset by peer # failJSON: { "time": "2004-08-31T16:15:10", "match": true , "host": "192.0.2.6" } diff --git a/fail2ban/tests/files/logs/named-refused b/fail2ban/tests/files/logs/named-refused index c06a4146..5ef42074 100644 --- a/fail2ban/tests/files/logs/named-refused +++ b/fail2ban/tests/files/logs/named-refused @@ -27,6 +27,11 @@ Aug 27 16:58:31 vhost1-ua named[29206]: client 176.9.92.38#42592 (simmarket.com. # failJSON: { "time": "2004-08-27T16:59:00", "match": true , "host": "192.0.2.1", "desc": "new log format, 9.11.0 (#2406)" } Aug 27 16:59:00 host named[28098]: client @0x7f6450002ef0 192.0.2.1#23332 (example.com): bad zone transfer request: 'test.com/IN': non-authoritative zone (NOTAUTH) +# failJSON: { "match": true , "host": "192.0.2.8", "desc": "log message with category (security), gh-3388" } +Oct 23 02:06:39 security: info: client @0x7f4e446fd6e8 192.0.2.8#53 (example.io): query (cache) 'example.io/A/IN' denied +# failJSON: { "match": true , "host": "192.0.2.237", "desc": "log message with category, gh-3388" } +Oct 23 03:35:40 update-security: error: client @0x7f4e45c07a48 192.0.2.237#55956 (example.ca): zone transfer 'example.ca/AXFR/IN' denied + # filterOptions: {"logtype": "journal"} # failJSON: { "match": true , "host": "192.0.2.1", "desc": "systemd-journal entry" } diff --git a/fail2ban/version.py b/fail2ban/version.py index d08b3b10..25ac2284 100644 --- a/fail2ban/version.py +++ b/fail2ban/version.py @@ -24,7 +24,7 @@ __author__ = "Cyril Jaquier, Yaroslav Halchenko, Steven Hiscocks, Daniel Black" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2005-2016 Yaroslav Halchenko, 2013-2014 Steven Hiscocks, Daniel Black" __license__ = "GPL-v2+" -version = "1.0.1" +version = "1.0.2" def normVersion(): """ Returns fail2ban version in normalized machine-readable format""" diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index 549d3124..e4d2f44c 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH FAIL2BAN-CLIENT "1" "September 2022" "Fail2Ban v1.0.1" "User Commands" +.TH FAIL2BAN-CLIENT "1" "November 2022" "Fail2Ban v1.0.2" "User Commands" .SH NAME fail2ban-client \- configure and control the server .SH SYNOPSIS .B fail2ban-client [\fI\,OPTIONS\/\fR] \fI\,\/\fR .SH DESCRIPTION -Fail2Ban v1.0.1 reads log file that contains password failure report +Fail2Ban v1.0.2 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP diff --git a/man/fail2ban-python.1 b/man/fail2ban-python.1 index eddbe7ab..225c8295 100644 --- a/man/fail2ban-python.1 +++ b/man/fail2ban-python.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH FAIL2BAN-PYTHON "1" "September 2022" "fail2ban-python 1.0.1" "User Commands" +.TH FAIL2BAN-PYTHON "1" "November 2022" "fail2ban-python 1.0.2" "User Commands" .SH NAME fail2ban-python \- a helper for Fail2Ban to assure that the same Python is used .SH DESCRIPTION diff --git a/man/fail2ban-regex.1 b/man/fail2ban-regex.1 index d008d421..5e64ef5b 100644 --- a/man/fail2ban-regex.1 +++ b/man/fail2ban-regex.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH FAIL2BAN-REGEX "1" "September 2022" "fail2ban-regex 1.0.1" "User Commands" +.TH FAIL2BAN-REGEX "1" "November 2022" "fail2ban-regex 1.0.2" "User Commands" .SH NAME fail2ban-regex \- test Fail2ban "failregex" option .SH SYNOPSIS diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1 index a0204e1b..ad1d84de 100644 --- a/man/fail2ban-server.1 +++ b/man/fail2ban-server.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH FAIL2BAN-SERVER "1" "September 2022" "Fail2Ban v1.0.1" "User Commands" +.TH FAIL2BAN-SERVER "1" "November 2022" "Fail2Ban v1.0.2" "User Commands" .SH NAME fail2ban-server \- start the server .SH SYNOPSIS .B fail2ban-server [\fI\,OPTIONS\/\fR] .SH DESCRIPTION -Fail2Ban v1.0.1 reads log file that contains password failure report +Fail2Ban v1.0.2 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP diff --git a/man/fail2ban-testcases.1 b/man/fail2ban-testcases.1 index addc5de4..7221c0cd 100644 --- a/man/fail2ban-testcases.1 +++ b/man/fail2ban-testcases.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH FAIL2BAN-TESTCASES "1" "September 2022" "fail2ban-testcases 1.0.1" "User Commands" +.TH FAIL2BAN-TESTCASES "1" "November 2022" "fail2ban-testcases 1.0.2" "User Commands" .SH NAME fail2ban-testcases \- run Fail2Ban unit-tests .SH SYNOPSIS