mirror of https://github.com/fail2ban/fail2ban
Merge pull request #1728 from sebres/_0.10/fix-gh-1719
commit
bb9541b7a9
|
@ -37,24 +37,24 @@ cmnfailre = ^[aA]uthentication (?:failure|error|failed) for <F-USER>.*</F-USER>
|
||||||
^User <F-USER>.+</F-USER> from <HOST> not allowed because listed in DenyUsers\s*%(__suff)s$
|
^User <F-USER>.+</F-USER> from <HOST> not allowed because listed in DenyUsers\s*%(__suff)s$
|
||||||
^User <F-USER>.+</F-USER> from <HOST> not allowed because not in any group\s*%(__suff)s$
|
^User <F-USER>.+</F-USER> from <HOST> not allowed because not in any group\s*%(__suff)s$
|
||||||
^refused connect from \S+ \(<HOST>\)\s*%(__suff)s$
|
^refused connect from \S+ \(<HOST>\)\s*%(__suff)s$
|
||||||
^Received disconnect from <HOST>%(__on_port_opt)s:\s*3: .*: Auth fail%(__suff)s$
|
^Received <F-MLFFORGET>disconnect</F-MLFFORGET> from <HOST>%(__on_port_opt)s:\s*3: .*: Auth fail%(__suff)s$
|
||||||
^User <F-USER>.+</F-USER> from <HOST> not allowed because a group is listed in DenyGroups\s*%(__suff)s$
|
^User <F-USER>.+</F-USER> from <HOST> not allowed because a group is listed in DenyGroups\s*%(__suff)s$
|
||||||
^User <F-USER>.+</F-USER> from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*%(__suff)s$
|
^User <F-USER>.+</F-USER> from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*%(__suff)s$
|
||||||
^pam_unix\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=<F-USER>\S*</F-USER>\s*rhost=<HOST>\s.*%(__suff)s$
|
^pam_unix\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=<F-USER>\S*</F-USER>\s*rhost=<HOST>\s.*%(__suff)s$
|
||||||
^(error: )?maximum authentication attempts exceeded for <F-USER>.*</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?%(__suff)s$
|
^(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
|
^User <F-USER>.+</F-USER> not allowed because account is locked%(__suff)s
|
||||||
^Disconnecting: Too many authentication failures(?: for <F-USER>.+?</F-USER>)?%(__suff)s
|
^<F-MLFFORGET>Disconnecting</F-MLFFORGET>: Too many authentication failures(?: for <F-USER>.+?</F-USER>)?%(__suff)s
|
||||||
^<F-NOFAIL>Received disconnect</F-NOFAIL> from <HOST>: 11:
|
^<F-NOFAIL>Received <F-MLFFORGET>disconnect</F-MLFFORGET></F-NOFAIL> from <HOST>: 11:
|
||||||
^<F-NOFAIL>Connection closed</F-NOFAIL> by <HOST>%(__suff)s$
|
^<F-NOFAIL>Connection <F-MLFFORGET>closed</F-MLFFORGET></F-NOFAIL> by <HOST>%(__suff)s$
|
||||||
|
|
||||||
mdre-normal =
|
mdre-normal =
|
||||||
|
|
||||||
mdre-ddos = ^Did not receive identification string from <HOST>%(__suff)s$
|
mdre-ddos = ^Did not receive identification string from <HOST>%(__suff)s$
|
||||||
^Connection reset by <HOST>%(__on_port_opt)s%(__suff)s
|
^Connection <F-MLFFORGET>reset</F-MLFFORGET> by <HOST>%(__on_port_opt)s%(__suff)s
|
||||||
^<F-NOFAIL>SSH: Server;Ltype:</F-NOFAIL> (?:Authname|Version|Kex);Remote: <HOST>-\d+;[A-Z]\w+:
|
^<F-NOFAIL>SSH: Server;Ltype:</F-NOFAIL> (?:Authname|Version|Kex);Remote: <HOST>-\d+;[A-Z]\w+:
|
||||||
^Read from socket failed: Connection reset by peer%(__suff)s
|
^Read from socket failed: Connection <F-MLFFORGET>reset</F-MLFFORGET> by peer%(__suff)s
|
||||||
|
|
||||||
mdre-extra = ^Received disconnect from <HOST>%(__on_port_opt)s:\s*14: No supported authentication methods available%(__suff)s$
|
mdre-extra = ^Received <F-MLFFORGET>disconnect</F-MLFFORGET> from <HOST>%(__on_port_opt)s:\s*14: No supported authentication methods available%(__suff)s$
|
||||||
^Unable to negotiate with <HOST>%(__on_port_opt)s: no matching (?:cipher|key exchange method) found.
|
^Unable to negotiate with <HOST>%(__on_port_opt)s: no matching (?:cipher|key exchange method) found.
|
||||||
^Unable to negotiate a (?:cipher|key exchange method)%(__suff)s$
|
^Unable to negotiate a (?:cipher|key exchange method)%(__suff)s$
|
||||||
|
|
||||||
|
|
|
@ -220,8 +220,8 @@ class JailReader(ConfigReader):
|
||||||
if self.__filter:
|
if self.__filter:
|
||||||
stream.extend(self.__filter.convert())
|
stream.extend(self.__filter.convert())
|
||||||
for opt, value in self.__opts.iteritems():
|
for opt, value in self.__opts.iteritems():
|
||||||
if opt == "logpath" and \
|
if opt == "logpath":
|
||||||
not self.__opts.get('backend', None).startswith("systemd"):
|
if self.__opts.get('backend', None).startswith("systemd"): continue
|
||||||
found_files = 0
|
found_files = 0
|
||||||
for path in value.split("\n"):
|
for path in value.split("\n"):
|
||||||
path = path.rsplit(" ", 1)
|
path = path.rsplit(" ", 1)
|
||||||
|
|
|
@ -550,24 +550,29 @@ class Filter(JailThread):
|
||||||
|
|
||||||
def _mergeFailure(self, mlfid, fail, failRegex):
|
def _mergeFailure(self, mlfid, fail, failRegex):
|
||||||
mlfidFail = self.mlfidCache.get(mlfid) if self.__mlfidCache else None
|
mlfidFail = self.mlfidCache.get(mlfid) if self.__mlfidCache else None
|
||||||
|
# if multi-line failure id (connection id) known:
|
||||||
if mlfidFail:
|
if mlfidFail:
|
||||||
mlfidGroups = mlfidFail[1]
|
mlfidGroups = mlfidFail[1]
|
||||||
# if current line not failure, but previous was failure:
|
# update - if not forget (disconnect/reset):
|
||||||
if fail.get('nofail') and not mlfidGroups.get('nofail'):
|
if not fail.get('mlfforget'):
|
||||||
del fail['nofail'] # remove nofail flag - was already market as failure
|
mlfidGroups.update(fail)
|
||||||
self.mlfidCache.unset(mlfid) # remove cache entry
|
else:
|
||||||
# if current line is failure, but previous was not:
|
self.mlfidCache.unset(mlfid) # remove cached entry
|
||||||
elif not fail.get('nofail') and mlfidGroups.get('nofail'):
|
# merge with previous info:
|
||||||
del mlfidGroups['nofail'] # remove nofail flag
|
|
||||||
self.mlfidCache.unset(mlfid) # remove cache entry
|
|
||||||
fail2 = mlfidGroups.copy()
|
fail2 = mlfidGroups.copy()
|
||||||
fail2.update(fail)
|
fail2.update(fail)
|
||||||
|
if not fail.get('nofail'): # be sure we've correct current state
|
||||||
|
try:
|
||||||
|
del fail2['nofail']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
fail2["matches"] = fail.get("matches", []) + failRegex.getMatchedTupleLines()
|
fail2["matches"] = fail.get("matches", []) + failRegex.getMatchedTupleLines()
|
||||||
fail = fail2
|
fail = fail2
|
||||||
elif fail.get('nofail'):
|
elif not fail.get('mlfforget'):
|
||||||
fail["matches"] = failRegex.getMatchedTupleLines()
|
|
||||||
mlfidFail = [self.__lastDate, fail]
|
mlfidFail = [self.__lastDate, fail]
|
||||||
self.mlfidCache.set(mlfid, mlfidFail)
|
self.mlfidCache.set(mlfid, mlfidFail)
|
||||||
|
if fail.get('nofail'):
|
||||||
|
fail["matches"] = failRegex.getMatchedTupleLines()
|
||||||
return fail
|
return fail
|
||||||
|
|
||||||
|
|
||||||
|
@ -683,6 +688,11 @@ class Filter(JailThread):
|
||||||
mlfid = fail.get('mlfid')
|
mlfid = fail.get('mlfid')
|
||||||
if mlfid is not None:
|
if mlfid is not None:
|
||||||
fail = self._mergeFailure(mlfid, fail, failRegex)
|
fail = self._mergeFailure(mlfid, fail, failRegex)
|
||||||
|
# bypass if no-failure case:
|
||||||
|
if fail.get('nofail'):
|
||||||
|
logSys.log(7, "Nofail by mlfid %r in regex %s: %s",
|
||||||
|
mlfid, failRegexIndex, fail.get('mlfforget', "waiting for failure"))
|
||||||
|
if not self.checkAllRegex: return failList
|
||||||
else:
|
else:
|
||||||
# matched lines:
|
# matched lines:
|
||||||
fail["matches"] = fail.get("matches", []) + failRegex.getMatchedTupleLines()
|
fail["matches"] = fail.get("matches", []) + failRegex.getMatchedTupleLines()
|
||||||
|
@ -702,18 +712,16 @@ class Filter(JailThread):
|
||||||
host = fail.get('dns')
|
host = fail.get('dns')
|
||||||
if host is None:
|
if host is None:
|
||||||
# first try to check we have mlfid case (cache connection id):
|
# first try to check we have mlfid case (cache connection id):
|
||||||
if fid is None:
|
if fid is None and mlfid is None:
|
||||||
if mlfid:
|
|
||||||
fail = self._mergeFailure(mlfid, fail, failRegex)
|
|
||||||
else:
|
|
||||||
# if no failure-id also (obscure case, wrong regex), throw error inside getFailID:
|
# if no failure-id also (obscure case, wrong regex), throw error inside getFailID:
|
||||||
fid = failRegex.getFailID()
|
fid = failRegex.getFailID()
|
||||||
host = fid
|
host = fid
|
||||||
cidr = IPAddr.CIDR_RAW
|
cidr = IPAddr.CIDR_RAW
|
||||||
# if mlfid case (not failure):
|
# if mlfid case (not failure):
|
||||||
if host is None:
|
if host is None:
|
||||||
if not self.checkAllRegex: # or fail.get('nofail'):
|
logSys.log(7, "No failure-id by mlfid %r in regex %s: %s",
|
||||||
return failList
|
mlfid, failRegexIndex, fail.get('mlfforget', "waiting for identifier"))
|
||||||
|
if not self.checkAllRegex: return failList
|
||||||
ips = [None]
|
ips = [None]
|
||||||
# if raw - add single ip or failure-id,
|
# if raw - add single ip or failure-id,
|
||||||
# otherwise expand host to multiple ips using dns (or ignore it if not valid):
|
# otherwise expand host to multiple ips using dns (or ignore it if not valid):
|
||||||
|
|
|
@ -113,6 +113,11 @@ May 27 00:16:33 host sshd[2364]: Received disconnect from 198.51.100.76: 11: Bye
|
||||||
# failJSON: { "time": "2004-09-29T16:28:02", "match": true , "host": "127.0.0.1" }
|
# failJSON: { "time": "2004-09-29T16:28:02", "match": true , "host": "127.0.0.1" }
|
||||||
Sep 29 16:28:02 spaceman sshd[16699]: Failed password for dan from 127.0.0.1 port 45416 ssh1
|
Sep 29 16:28:02 spaceman sshd[16699]: Failed password for dan from 127.0.0.1 port 45416 ssh1
|
||||||
|
|
||||||
|
# failJSON: { "match": false, "desc": "no failure, just cache mlfid (conn-id)" }
|
||||||
|
Sep 29 16:28:05 localhost sshd[16700]: Connection from 192.0.2.5
|
||||||
|
# failJSON: { "match": false, "desc": "no failure, just covering mlfid (conn-id) forget" }
|
||||||
|
Sep 29 16:28:05 localhost sshd[16700]: Connection closed by 192.0.2.5 [preauth]
|
||||||
|
|
||||||
# failJSON: { "time": "2004-09-29T17:15:02", "match": true , "host": "127.0.0.1" }
|
# failJSON: { "time": "2004-09-29T17:15:02", "match": true , "host": "127.0.0.1" }
|
||||||
Sep 29 17:15:02 spaceman sshd[12946]: Failed hostbased for dan from 127.0.0.1 port 45785 ssh2: RSA 8c:e3:aa:0f:64:51:02:f7:14:79:89:3f:65:84:7c:30, client user "dan", client host "localhost.localdomain"
|
Sep 29 17:15:02 spaceman sshd[12946]: Failed hostbased for dan from 127.0.0.1 port 45785 ssh2: RSA 8c:e3:aa:0f:64:51:02:f7:14:79:89:3f:65:84:7c:30, client user "dan", client host "localhost.localdomain"
|
||||||
|
|
||||||
|
@ -233,4 +238,6 @@ Nov 26 13:03:30 srv sshd[45]: fatal: Unable to negotiate with 192.0.2.2 port 554
|
||||||
# failJSON: { "match": false }
|
# failJSON: { "match": false }
|
||||||
Nov 26 15:03:30 host sshd[22440]: Connection from 192.0.2.3 port 39678 on 192.168.1.9 port 22
|
Nov 26 15:03:30 host sshd[22440]: Connection from 192.0.2.3 port 39678 on 192.168.1.9 port 22
|
||||||
# failJSON: { "time": "2004-11-26T15:03:31", "match": true , "host": "192.0.2.3", "desc": "Multiline - no matching key exchange method" }
|
# failJSON: { "time": "2004-11-26T15:03:31", "match": true , "host": "192.0.2.3", "desc": "Multiline - no matching key exchange method" }
|
||||||
Nov 26 15:03:31 host sshd[22440]: fatal: Unable to negotiate a key exchange method [preauth]
|
Nov 26 15:03:31 host sshd[22440]: fatal: Unable to negotiate a key exchange method [preauth]
|
||||||
|
# failJSON: { "time": "2004-11-26T15:03:32", "match": true , "host": "192.0.2.3", "filter": "sshd", "desc": "Second attempt within the same connect" }
|
||||||
|
Nov 26 15:03:32 host sshd[22440]: fatal: Unable to negotiate a key exchange method [preauth]
|
||||||
|
|
|
@ -182,6 +182,9 @@ def testSampleRegexsFactory(name, basedir):
|
||||||
try:
|
try:
|
||||||
ret = self.filter.processLine(line)
|
ret = self.filter.processLine(line)
|
||||||
if not ret:
|
if not ret:
|
||||||
|
# Bypass if filter constraint specified:
|
||||||
|
if faildata.get('filter') and name != faildata.get('filter'):
|
||||||
|
continue
|
||||||
# Check line is flagged as none match
|
# Check line is flagged as none match
|
||||||
self.assertFalse(faildata.get('match', True),
|
self.assertFalse(faildata.get('match', True),
|
||||||
"Line not matched when should have")
|
"Line not matched when should have")
|
||||||
|
@ -200,13 +203,13 @@ def testSampleRegexsFactory(name, basedir):
|
||||||
self.assertEqual(len(ret), 1,
|
self.assertEqual(len(ret), 1,
|
||||||
"Multiple regexs matched %r" % (map(lambda x: x[0], ret)))
|
"Multiple regexs matched %r" % (map(lambda x: x[0], ret)))
|
||||||
|
|
||||||
# Fallback for backwards compatibility (previously no fid, was host only):
|
|
||||||
if faildata.get("host", None) is not None and fail.get("host", None) is None:
|
|
||||||
fail["host"] = fid
|
|
||||||
# Verify match captures (at least fid/host) and timestamp as expected
|
# Verify match captures (at least fid/host) and timestamp as expected
|
||||||
for k, v in faildata.iteritems():
|
for k, v in faildata.iteritems():
|
||||||
if k not in ("time", "match", "desc"):
|
if k not in ("time", "match", "desc", "filter"):
|
||||||
fv = fail.get(k, None)
|
fv = fail.get(k, None)
|
||||||
|
# Fallback for backwards compatibility (previously no fid, was host only):
|
||||||
|
if k == "host" and fv is None:
|
||||||
|
fv = fid
|
||||||
self.assertEqual(fv, v)
|
self.assertEqual(fv, v)
|
||||||
|
|
||||||
t = faildata.get("time", None)
|
t = faildata.get("time", None)
|
||||||
|
|
Loading…
Reference in New Issue