mirror of https://github.com/fail2ban/fail2ban
improve processing of pending failures (lines without ID/IP) - fail2ban-regex would show those in matched lines now (as well as increase count of matched RE);
avoid overwrite of data with empty tags by ticket constructed from multi-line failures; amend to d1b7e2b5fb2b389d04845369d7d29db65425dcf2: better output (as well as ignoring of pending lines) using `--out msg`; filter.d/sshd.conf: don't forget mlf-cache on "disconnecting: too many authentication failures" - message does not have IP (must be followed by "closed [preauth]" to obtain host-IP).pull/2638/head
parent
ac8e8db814
commit
1492ab2247
|
@ -55,7 +55,7 @@ cmnfailre = ^[aA]uthentication (?:failure|error|failed) for <F-USER>.*</F-USER>
|
|||
^(error: )?maximum authentication attempts exceeded for <F-USER>.*</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?%(__suff)s$
|
||||
^User <F-USER>.+</F-USER> not allowed because account is locked%(__suff)s
|
||||
^<F-MLFFORGET>Disconnecting</F-MLFFORGET>(?: from)?(?: (?:invalid|authenticating)) user <F-USER>\S+</F-USER> <HOST>%(__on_port_opt)s:\s*Change of username or service not allowed:\s*.*\[preauth\]\s*$
|
||||
^<F-MLFFORGET>Disconnecting</F-MLFFORGET>: Too many authentication failures(?: for <F-USER>.+?</F-USER>)?%(__suff)s$
|
||||
^Disconnecting: Too many authentication failures(?: for <F-USER>.+?</F-USER>)?%(__suff)s$
|
||||
^<F-NOFAIL>Received <F-MLFFORGET>disconnect</F-MLFFORGET></F-NOFAIL> from <HOST>%(__on_port_opt)s:\s*11:
|
||||
<mdre-<mode>-other>
|
||||
^<F-MLFFORGET><F-MLFGAINED>Accepted \w+</F-MLFGAINED></F-MLFFORGET> for <F-USER>\S+</F-USER> from <HOST>(?:\s|$)
|
||||
|
|
|
@ -272,6 +272,8 @@ class Fail2banRegex(object):
|
|||
self._filter.returnRawHost = opts.raw
|
||||
self._filter.checkFindTime = False
|
||||
self._filter.checkAllRegex = opts.checkAllRegex and not opts.out
|
||||
# ignore pending (without ID/IP), added to matches if it hits later (if ID/IP can be retreved)
|
||||
self._filter.ignorePending = opts.out;
|
||||
self._backend = 'auto'
|
||||
|
||||
def output(self, line):
|
||||
|
@ -452,7 +454,6 @@ class Fail2banRegex(object):
|
|||
try:
|
||||
found = self._filter.processLine(line, date)
|
||||
lines = []
|
||||
line = self._filter.processedLine()
|
||||
ret = []
|
||||
for match in found:
|
||||
# Append True/False flag depending if line was matched by
|
||||
|
@ -488,7 +489,7 @@ class Fail2banRegex(object):
|
|||
self._line_stats.matched += 1
|
||||
self._line_stats.missed -= 1
|
||||
if lines: # pre-lines parsed in multiline mode (buffering)
|
||||
lines.append(line)
|
||||
lines.append(self._filter.processedLine())
|
||||
line = "\n".join(lines)
|
||||
return line, ret, is_ignored
|
||||
|
||||
|
@ -523,7 +524,8 @@ class Fail2banRegex(object):
|
|||
output(ret[1])
|
||||
elif self._opts.out == 'msg':
|
||||
for ret in ret:
|
||||
output('\n'.join(map(lambda v:''.join(v for v in v), ret[3].get('matches'))))
|
||||
for ret in ret[3].get('matches'):
|
||||
output(''.join(v for v in ret))
|
||||
elif self._opts.out == 'row':
|
||||
for ret in ret:
|
||||
output('[%r,\t%r,\t%r],' % (ret[1],ret[2],dict((k,v) for k, v in ret[3].iteritems() if k != 'matches')))
|
||||
|
|
|
@ -105,6 +105,8 @@ class Filter(JailThread):
|
|||
self.returnRawHost = False
|
||||
## check each regex (used for test purposes):
|
||||
self.checkAllRegex = False
|
||||
## avoid finding of pending failures (without ID/IP, used in fail2ban-regex):
|
||||
self.ignorePending = True
|
||||
## if true ignores obsolete failures (failure time < now - findTime):
|
||||
self.checkFindTime = True
|
||||
## Ticks counter
|
||||
|
@ -651,7 +653,7 @@ class Filter(JailThread):
|
|||
fail['users'] = users = set()
|
||||
users.add(user)
|
||||
return users
|
||||
return None
|
||||
return users
|
||||
|
||||
# # ATM incremental (non-empty only) merge deactivated ...
|
||||
# @staticmethod
|
||||
|
@ -680,25 +682,22 @@ class Filter(JailThread):
|
|||
if not fail.get('nofail'):
|
||||
fail['nofail'] = fail["mlfgained"]
|
||||
elif fail.get('nofail'): nfflgs |= 1
|
||||
if fail.get('mlfforget'): nfflgs |= 2
|
||||
if fail.pop('mlfforget', None): nfflgs |= 2
|
||||
# if multi-line failure id (connection id) known:
|
||||
if mlfidFail:
|
||||
mlfidGroups = mlfidFail[1]
|
||||
# update users set (hold all users of connect):
|
||||
users = self._updateUsers(mlfidGroups, fail.get('user'))
|
||||
# be sure we've correct current state ('nofail' and 'mlfgained' only from last failure)
|
||||
try:
|
||||
del mlfidGroups['nofail']
|
||||
del mlfidGroups['mlfgained']
|
||||
except KeyError:
|
||||
pass
|
||||
mlfidGroups.pop('nofail', None)
|
||||
mlfidGroups.pop('mlfgained', None)
|
||||
# # ATM incremental (non-empty only) merge deactivated (for future version only),
|
||||
# # it can be simulated using alternate value tags, like <F-ALT_VAL>...</F-ALT_VAL>,
|
||||
# # so previous value 'val' will be overwritten only if 'alt_val' is not empty...
|
||||
# _updateFailure(mlfidGroups, fail)
|
||||
#
|
||||
# overwrite multi-line failure with all values, available in fail:
|
||||
mlfidGroups.update(fail)
|
||||
mlfidGroups.update(((k,v) for k,v in fail.iteritems() if v is not None))
|
||||
# new merged failure data:
|
||||
fail = mlfidGroups
|
||||
# if forget (disconnect/reset) - remove cached entry:
|
||||
|
@ -709,20 +708,14 @@ class Filter(JailThread):
|
|||
mlfidFail = [self.__lastDate, fail]
|
||||
self.mlfidCache.set(mlfid, mlfidFail)
|
||||
# check users in order to avoid reset failure by multiple logon-attempts:
|
||||
if users and len(users) > 1:
|
||||
if fail.pop('mlfpending', 0) or users and len(users) > 1:
|
||||
# we've new user, reset 'nofail' because of multiple users attempts:
|
||||
try:
|
||||
del fail['nofail']
|
||||
fail.pop('nofail', None)
|
||||
nfflgs &= ~1 # reset nofail
|
||||
except KeyError:
|
||||
pass
|
||||
# merge matches:
|
||||
if not (nfflgs & 1): # current nofail state (corresponding users)
|
||||
try:
|
||||
m = fail.pop("nofail-matches")
|
||||
m = fail.pop("nofail-matches", [])
|
||||
m += fail.get("matches", [])
|
||||
except KeyError:
|
||||
m = fail.get("matches", [])
|
||||
if not (nfflgs & 8): # no gain signaled
|
||||
m += failRegex.getMatchedTupleLines()
|
||||
fail["matches"] = m
|
||||
|
@ -888,7 +881,8 @@ class Filter(JailThread):
|
|||
if host is None:
|
||||
if ll <= 7: logSys.log(7, "No failure-id by mlfid %r in regex %s: %s",
|
||||
mlfid, failRegexIndex, fail.get('mlfforget', "waiting for identifier"))
|
||||
if not self.checkAllRegex: return failList
|
||||
fail['mlfpending'] = 1; # mark failure is pending
|
||||
if not self.checkAllRegex and self.ignorePending: return failList
|
||||
ips = [None]
|
||||
# if raw - add single ip or failure-id,
|
||||
# otherwise expand host to multiple ips using dns (or ignore it if not valid):
|
||||
|
|
|
@ -134,7 +134,7 @@ Sep 29 17:15:02 spaceman sshd[12946]: Failed password for user from 127.0.0.1 po
|
|||
# failJSON: { "time": "2004-09-29T17:15:02", "match": true , "host": "127.0.0.1", "desc": "Injecting while exhausting initially present {0,100} match length limits set for ruser etc" }
|
||||
Sep 29 17:15:02 spaceman sshd[12946]: Failed password for user from 127.0.0.1 port 20000 ssh1: ruser XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX from 1.2.3.4
|
||||
# failJSON: { "time": "2004-09-29T17:15:03", "match": true , "host": "aaaa:bbbb:cccc:1234::1:1", "desc": "Injecting while exhausting initially present {0,100} match length limits set for ruser etc" }
|
||||
Sep 29 17:15:03 spaceman sshd[12946]: Failed password for user from aaaa:bbbb:cccc:1234::1:1 port 20000 ssh1: ruser XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX from 1.2.3.4
|
||||
Sep 29 17:15:03 spaceman sshd[12947]: Failed password for user from aaaa:bbbb:cccc:1234::1:1 port 20000 ssh1: ruser XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX from 1.2.3.4
|
||||
|
||||
# failJSON: { "time": "2004-11-11T08:04:51", "match": true , "host": "127.0.0.1", "desc": "Injecting on username ssh 'from 10.10.1.1'@localhost" }
|
||||
Nov 11 08:04:51 redbamboo sshd[2737]: Failed password for invalid user from 10.10.1.1 from 127.0.0.1 port 58946 ssh2
|
||||
|
|
|
@ -135,7 +135,7 @@ srv sshd[12946]: Failed password for user from 127.0.0.1 port 20000 ssh1: ruser
|
|||
# failJSON: { "match": true , "host": "127.0.0.1", "desc": "Injecting while exhausting initially present {0,100} match length limits set for ruser etc" }
|
||||
srv sshd[12946]: Failed password for user from 127.0.0.1 port 20000 ssh1: ruser XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX from 1.2.3.4
|
||||
# failJSON: { "match": true , "host": "aaaa:bbbb:cccc:1234::1:1", "desc": "Injecting while exhausting initially present {0,100} match length limits set for ruser etc" }
|
||||
srv sshd[12946]: Failed password for user from aaaa:bbbb:cccc:1234::1:1 port 20000 ssh1: ruser XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX from 1.2.3.4
|
||||
srv sshd[12947]: Failed password for user from aaaa:bbbb:cccc:1234::1:1 port 20000 ssh1: ruser XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX from 1.2.3.4
|
||||
|
||||
# failJSON: { "match": true , "host": "127.0.0.1", "desc": "Injecting on username ssh 'from 10.10.1.1'@localhost" }
|
||||
srv sshd[2737]: Failed password for invalid user from 10.10.1.1 from 127.0.0.1 port 58946 ssh2
|
||||
|
|
Loading…
Reference in New Issue