instructions
This commit is contained in:
25
fail2ban-master/fail2ban/tests/__init__.py
Normal file
25
fail2ban-master/fail2ban/tests/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
22
fail2ban-master/fail2ban/tests/action_d/__init__.py
Normal file
22
fail2ban-master/fail2ban/tests/action_d/__init__.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
__author__ = "Steven Hiscocks"
|
||||
__copyright__ = "Copyright (c) 2014 Steven Hiscocks"
|
||||
__license__ = "GPL"
|
||||
316
fail2ban-master/fail2ban/tests/action_d/test_smtp.py
Normal file
316
fail2ban-master/fail2ban/tests/action_d/test_smtp.py
Normal file
@@ -0,0 +1,316 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import os
|
||||
import threading
|
||||
import unittest
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
import importlib
|
||||
|
||||
from ..dummyjail import DummyJail
|
||||
from ..utils import CONFIG_DIR, asyncserver, Utils, uni_decode
|
||||
|
||||
|
||||
class _SMTPActionTestCase():
|
||||
|
||||
def _reset_smtpd(self):
|
||||
for a in ('mailfrom', 'org_data', 'data'):
|
||||
if hasattr(self.smtpd, a): delattr(self.smtpd, a)
|
||||
self.ready = False
|
||||
|
||||
def _exec_and_wait(self, doaction, timeout=3, short=False):
|
||||
if short: timeout /= 25
|
||||
self.smtpd.ready = False
|
||||
doaction()
|
||||
Utils.wait_for(lambda: self.smtpd.ready, timeout)
|
||||
|
||||
def testStart(self):
|
||||
self._exec_and_wait(self.action.start)
|
||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||
self.action.ssl = False # ensure it works without TLS as a sanity check
|
||||
self.assertTrue(
|
||||
"Subject: [Fail2Ban] %s: started" % self.jail.name
|
||||
in self.smtpd.data)
|
||||
|
||||
def testStop(self):
|
||||
self._exec_and_wait(self.action.stop)
|
||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||
self.assertTrue(
|
||||
"Subject: [Fail2Ban] %s: stopped" %
|
||||
self.jail.name in self.smtpd.data)
|
||||
|
||||
def _testBan(self, restored=False):
|
||||
aInfo = {
|
||||
'ip': "127.0.0.2",
|
||||
'failures': 3,
|
||||
'matches': "Test fail 1\n",
|
||||
'ipjailmatches': "Test fail 1\nTest Fail2\n",
|
||||
'ipmatches': "Test fail 1\nTest Fail2\nTest Fail3\n",
|
||||
}
|
||||
if restored:
|
||||
aInfo['restored'] = 1
|
||||
|
||||
self._exec_and_wait(lambda: self.action.ban(aInfo), short=restored)
|
||||
if restored: # no mail, should raises attribute error:
|
||||
self.assertRaises(AttributeError, lambda: self.smtpd.mailfrom)
|
||||
return
|
||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||
subject = "Subject: [Fail2Ban] %s: banned %s" % (
|
||||
self.jail.name, aInfo['ip'])
|
||||
self.assertIn(subject, self.smtpd.data)
|
||||
self.assertIn(
|
||||
"%i attempts" % aInfo['failures'], self.smtpd.data)
|
||||
|
||||
self.action.matches = "matches"
|
||||
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
||||
self.assertIn(aInfo['matches'], self.smtpd.data)
|
||||
|
||||
self.action.matches = "ipjailmatches"
|
||||
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
||||
self.assertIn(aInfo['ipjailmatches'], self.smtpd.data)
|
||||
|
||||
self.action.matches = "ipmatches"
|
||||
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
||||
self.assertIn(aInfo['ipmatches'], self.smtpd.data)
|
||||
|
||||
def testBan(self):
|
||||
self._testBan()
|
||||
|
||||
def testNOPByRestored(self):
|
||||
self._testBan(restored=True)
|
||||
|
||||
def testOptions(self):
|
||||
self._exec_and_wait(self.action.start)
|
||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||
|
||||
self.action.fromname = "Test"
|
||||
self.action.fromaddr = "test@example.com"
|
||||
self.action.toaddr = "test@example.com, test2@example.com"
|
||||
self._exec_and_wait(self.action.start)
|
||||
self.assertEqual(self.smtpd.mailfrom, "test@example.com")
|
||||
self.assertTrue("From: %s <%s>" %
|
||||
(self.action.fromname, self.action.fromaddr) in self.smtpd.data)
|
||||
self.assertEqual(set(self.smtpd.rcpttos), set(["test@example.com", "test2@example.com"]))
|
||||
|
||||
try:
|
||||
import smtpd
|
||||
|
||||
class TestSMTPServer(smtpd.SMTPServer):
|
||||
|
||||
def __init__(self, *args):
|
||||
smtpd.SMTPServer.__init__(self, *args)
|
||||
self.ready = False
|
||||
|
||||
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
|
||||
self.peer = peer
|
||||
self.mailfrom = mailfrom
|
||||
self.rcpttos = rcpttos
|
||||
self.org_data = data
|
||||
# replace new line (with tab or space) for possible mime translations (word wrap),
|
||||
self.data = re.sub(r"\n[\t ]", " ", uni_decode(data))
|
||||
self.ready = True
|
||||
|
||||
|
||||
class SMTPActionTest(unittest.TestCase, _SMTPActionTestCase):
|
||||
|
||||
def setUpClass():
|
||||
"""Call before tests."""
|
||||
unittest.F2B.SkipIfCfgMissing(action='smtp.py')
|
||||
|
||||
cls = SMTPActionTest
|
||||
cls.smtpd = TestSMTPServer(("localhost", 0), None)
|
||||
cls.port = cls.smtpd.socket.getsockname()[1]
|
||||
|
||||
## because of bug in loop (see loop in asyncserver.py) use it's loop instead of asyncore.loop:
|
||||
cls._active = True
|
||||
cls._loop_thread = threading.Thread(
|
||||
target=asyncserver.loop, kwargs={'active': lambda: cls._active})
|
||||
cls._loop_thread.daemon = True
|
||||
cls._loop_thread.start()
|
||||
|
||||
def tearDownClass():
|
||||
"""Call after tests."""
|
||||
cls = SMTPActionTest
|
||||
cls.smtpd.close()
|
||||
cls._active = False
|
||||
cls._loop_thread.join()
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
unittest.F2B.SkipIfCfgMissing(action='smtp.py')
|
||||
super(SMTPActionTest, self).setUp()
|
||||
self.jail = DummyJail()
|
||||
pythonModule = os.path.join(CONFIG_DIR, "action.d", "smtp.py")
|
||||
pythonModuleName = os.path.basename(pythonModule.rstrip(".py"))
|
||||
customActionModule = importlib.machinery.SourceFileLoader(
|
||||
pythonModuleName, pythonModule).load_module()
|
||||
|
||||
self.action = customActionModule.Action(
|
||||
self.jail, "test", host="localhost:%i" % self.port)
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
self._reset_smtpd()
|
||||
super(SMTPActionTest, self).tearDown()
|
||||
|
||||
except ImportError as e:
|
||||
if tuple(sys.version_info) <= (3, 11):
|
||||
print("I: Skipping smtp tests: %s" % e)
|
||||
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
from aiosmtpd.controller import Controller
|
||||
import socket
|
||||
import ssl
|
||||
import tempfile
|
||||
|
||||
class TestSMTPHandler:
|
||||
def __init__(self, *args):
|
||||
self.ready = False
|
||||
|
||||
async def handle_DATA(self, server, session, envelope):
|
||||
self.peer = session.peer
|
||||
self.mailfrom = envelope.mail_from
|
||||
self.rcpttos = envelope.rcpt_tos
|
||||
self.org_data = envelope.content.decode()
|
||||
# normalize CRLF -> LF:
|
||||
self.data = re.sub(r"\r\n", "\n", uni_decode(self.org_data))
|
||||
self.ready = True
|
||||
return '250 OK'
|
||||
|
||||
async def handle_exception(self, error):
|
||||
print(error)
|
||||
return '542 Internal server error'
|
||||
|
||||
|
||||
class AIOSMTPActionTest(unittest.TestCase, _SMTPActionTestCase):
|
||||
|
||||
@classmethod
|
||||
def create_temp_self_signed_cert(cls):
|
||||
"""
|
||||
Create a self signed SSL certificate in temporary files for host
|
||||
'localhost'
|
||||
|
||||
Returns a tuple containing the certificate file name and the key
|
||||
file name.
|
||||
|
||||
The cert (ECC:256, 100years) created with:
|
||||
openssl req -x509 -out /tmp/f2b-localhost.crt -keyout /tmp/f2b-localhost.key -days 36500 -newkey ec:<(openssl ecparam -name prime256v1) -nodes -sha256 \
|
||||
-subj '/CN=localhost' -extensions EXT -config <( \
|
||||
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth" \
|
||||
)
|
||||
cat /tmp/f2b-localhost.*
|
||||
rm /tmp/f2b-localhost.*
|
||||
|
||||
"""
|
||||
if hasattr(cls, 'crtfiles'): return cls.crtfiles
|
||||
cls.crtfiles = crtfiles = (tempfile.mktemp(".crt", "f2b_cert_"), tempfile.mktemp(".key", "f2b_cert_"))
|
||||
with open(crtfiles[0], 'w') as f:
|
||||
f.write(
|
||||
'-----BEGIN CERTIFICATE-----\n'
|
||||
'MIIBhDCCASugAwIBAgIUCuW168kD3G7XrpFwGHwE6vGfoJkwCgYIKoZIzj0EAwIw\n'
|
||||
'FDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTIzMTIzMDE3NDUzNFoYDzIxMjMxMjA2\n'
|
||||
'MTc0NTM0WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjO\n'
|
||||
'PQMBBwNCAARDa8BO/UE4axzvnOQ/pCc/ZTp351X1TqIfjEFaMoZOItz1/MW3ZCuS\n'
|
||||
'2vuby3rMn0WZ59RWVotBqA6lcMVcgDq3o1kwVzAUBgNVHREEDTALgglsb2NhbGhv\n'
|
||||
'c3QwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBS8\n'
|
||||
'kH1Ucuq+wlex5DxxHDe1kKGdcjAKBggqhkjOPQQDAgNHADBEAiBmv05+BvXWMzLg\n'
|
||||
'TtF4McoQNrU/0TTKhV8o+mgd+47tMAIgaaSNRnfjGIfJMbXg7Bh53qOIu5+lnm1b\n'
|
||||
'ySygMgFmePs=\n'
|
||||
'-----END CERTIFICATE-----\n'
|
||||
)
|
||||
with open(crtfiles[1], 'w') as f:
|
||||
f.write(
|
||||
'-----BEGIN PRIVATE KEY-----\n'
|
||||
'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoBGcojKPZMYut7aP\n'
|
||||
'JGe2GW+2lVV0zJpgCsZ7816a9uqhRANCAARDa8BO/UE4axzvnOQ/pCc/ZTp351X1\n'
|
||||
'TqIfjEFaMoZOItz1/MW3ZCuS2vuby3rMn0WZ59RWVotBqA6lcMVcgDq3\n'
|
||||
'-----END PRIVATE KEY-----\n'
|
||||
)
|
||||
# return file names
|
||||
return crtfiles
|
||||
|
||||
@classmethod
|
||||
def _del_cert(cls):
|
||||
if hasattr(cls, 'crtfiles') and cls.crtfiles:
|
||||
for f in cls.crtfiles:
|
||||
try:
|
||||
os.unlink(f)
|
||||
except FileNotFoundError: pass
|
||||
cls.crtfiles = None
|
||||
|
||||
@staticmethod
|
||||
def _free_port():
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(('localhost', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
def setUpClass():
|
||||
"""Call before tests."""
|
||||
unittest.F2B.SkipIfCfgMissing(action='smtp.py')
|
||||
|
||||
cert_file, cert_key = AIOSMTPActionTest.create_temp_self_signed_cert()
|
||||
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ssl_context.load_cert_chain(cert_file, cert_key)
|
||||
|
||||
cls = AIOSMTPActionTest
|
||||
cls.port = cls._free_port()
|
||||
cls.smtpd = TestSMTPHandler()
|
||||
cls.controller = Controller(cls.smtpd, hostname='localhost', server_hostname='localhost', port=cls.port,
|
||||
server_kwargs={'tls_context': ssl_context, 'require_starttls': False})
|
||||
# Run the event loop in a separate thread.
|
||||
cls.controller.start()
|
||||
|
||||
def tearDownClass():
|
||||
"""Call after tests."""
|
||||
cls = AIOSMTPActionTest
|
||||
cls.controller.stop()
|
||||
cls._del_cert()
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
unittest.F2B.SkipIfCfgMissing(action='smtp.py')
|
||||
super(AIOSMTPActionTest, self).setUp()
|
||||
self.jail = DummyJail()
|
||||
pythonModule = os.path.join(CONFIG_DIR, "action.d", "smtp.py")
|
||||
pythonModuleName = os.path.basename(pythonModule.rstrip(".py"))
|
||||
ldr = importlib.machinery.SourceFileLoader(pythonModuleName, pythonModule)
|
||||
mod = types.ModuleType(ldr.name)
|
||||
ldr.exec_module(mod)
|
||||
|
||||
self.action = mod.Action(
|
||||
self.jail, "test", host="localhost:%i" % self.port)
|
||||
|
||||
self.action.ssl = True
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
self._reset_smtpd()
|
||||
super(AIOSMTPActionTest, self).tearDown()
|
||||
|
||||
except ImportError as e:
|
||||
if tuple(sys.version_info) >= (3, 10):
|
||||
print("I: Skipping SSL smtp tests: %s" % e)
|
||||
512
fail2ban-master/fail2ban/tests/actionstestcase.py
Normal file
512
fail2ban-master/fail2ban/tests/actionstestcase.py
Normal file
@@ -0,0 +1,512 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Daniel Black
|
||||
#
|
||||
|
||||
__author__ = "Daniel Black"
|
||||
__copyright__ = "Copyright (c) 2013 Daniel Black"
|
||||
__license__ = "GPL"
|
||||
|
||||
import time
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from ..server.ticket import FailTicket
|
||||
from ..server.utils import Utils
|
||||
from .dummyjail import DummyJail
|
||||
from .utils import LogCaptureTestCase, with_alt_time, with_tmpdir, MyTime
|
||||
|
||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||
|
||||
|
||||
class ExecuteActions(LogCaptureTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
super(ExecuteActions, self).setUp()
|
||||
self.__jail = DummyJail()
|
||||
self.__actions = self.__jail.actions
|
||||
|
||||
def tearDown(self):
|
||||
super(ExecuteActions, self).tearDown()
|
||||
|
||||
def defaultAction(self, o={}):
|
||||
self.__actions.add('ip')
|
||||
act = self.__actions['ip']
|
||||
act.actionstart = 'echo ip start'+o.get('start', '')
|
||||
act.actionban = 'echo ip ban <ip>'+o.get('ban', '')
|
||||
act.actionunban = 'echo ip unban <ip>'+o.get('unban', '')
|
||||
act.actioncheck = 'echo ip check'+o.get('check', '')
|
||||
act.actionflush = 'echo ip flush'+o.get('flush', '')
|
||||
act.actionstop = 'echo ip stop'+o.get('stop', '')
|
||||
return act
|
||||
|
||||
def testActionsAddDuplicateName(self):
|
||||
self.__actions.add('test')
|
||||
self.assertRaises(ValueError, self.__actions.add, 'test')
|
||||
|
||||
def testActionsManipulation(self):
|
||||
self.__actions.add('test')
|
||||
self.assertTrue(self.__actions['test'])
|
||||
self.assertIn('test', self.__actions)
|
||||
self.assertNotIn('nonexistent action', self.__actions)
|
||||
self.__actions.add('test1')
|
||||
del self.__actions['test']
|
||||
del self.__actions['test1']
|
||||
self.assertNotIn('test', self.__actions)
|
||||
self.assertEqual(len(self.__actions), 0)
|
||||
|
||||
self.__actions.setBanTime(127)
|
||||
self.assertEqual(self.__actions.getBanTime(),127)
|
||||
self.assertRaises(ValueError, self.__actions.removeBannedIP, '127.0.0.1')
|
||||
|
||||
def testAddBannedIP(self):
|
||||
self.assertEqual(self.__actions.addBannedIP('192.0.2.1'), 1)
|
||||
self.assertLogged('Ban 192.0.2.1')
|
||||
self.pruneLog()
|
||||
self.assertEqual(self.__actions.addBannedIP(['192.0.2.1', '192.0.2.2', '192.0.2.3']), 2)
|
||||
self.assertLogged('192.0.2.1 already banned')
|
||||
self.assertNotLogged('Ban 192.0.2.1')
|
||||
self.assertLogged('Ban 192.0.2.2')
|
||||
self.assertLogged('Ban 192.0.2.3')
|
||||
|
||||
def testActionsOutput(self):
|
||||
self.defaultAction()
|
||||
self.__actions.start()
|
||||
self.assertLogged("stdout: %r" % 'ip start', wait=True)
|
||||
self.__actions.stop()
|
||||
self.__actions.join()
|
||||
self.assertLogged("stdout: %r" % 'ip flush', "stdout: %r" % 'ip stop')
|
||||
self.assertEqual(self.__actions.status(),[("Currently banned", 0 ),
|
||||
("Total banned", 0 ), ("Banned IP list", [] )])
|
||||
self.assertEqual(self.__actions.status('short'),[("Currently banned", 0 ),
|
||||
("Total banned", 0 )])
|
||||
|
||||
def testAddActionPython(self):
|
||||
self.__actions.add(
|
||||
"Action", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
||||
{'opt1': 'value'})
|
||||
|
||||
self.assertLogged("TestAction initialised")
|
||||
|
||||
self.__actions.start()
|
||||
self.assertTrue( Utils.wait_for(lambda: self._is_logged("TestAction action start"), 3) )
|
||||
|
||||
self.__actions.stop()
|
||||
self.__actions.join()
|
||||
self.assertLogged("TestAction action stop")
|
||||
|
||||
self.assertRaises(IOError,
|
||||
self.__actions.add, "Action3", "/does/not/exist.py", {})
|
||||
|
||||
# With optional argument
|
||||
self.__actions.add(
|
||||
"Action4", os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
||||
{'opt1': 'value', 'opt2': 'value2'})
|
||||
# With too many arguments
|
||||
self.assertRaises(
|
||||
TypeError, self.__actions.add, "Action5",
|
||||
os.path.join(TEST_FILES_DIR, "action.d/action.py"),
|
||||
{'opt1': 'value', 'opt2': 'value2', 'opt3': 'value3'})
|
||||
# Missing required argument
|
||||
self.assertRaises(
|
||||
TypeError, self.__actions.add, "Action5",
|
||||
os.path.join(TEST_FILES_DIR, "action.d/action.py"), {})
|
||||
|
||||
def testAddPythonActionNOK(self):
|
||||
self.assertRaises(RuntimeError, self.__actions.add,
|
||||
"Action", os.path.join(TEST_FILES_DIR,
|
||||
"action.d/action_noAction.py"),
|
||||
{})
|
||||
self.assertRaises(RuntimeError, self.__actions.add,
|
||||
"Action", os.path.join(TEST_FILES_DIR,
|
||||
"action.d/action_nomethod.py"),
|
||||
{})
|
||||
self.__actions.add(
|
||||
"Action", os.path.join(TEST_FILES_DIR,
|
||||
"action.d/action_errors.py"),
|
||||
{})
|
||||
self.__actions.start()
|
||||
self.assertTrue( Utils.wait_for(lambda: self._is_logged("Failed to start"), 3) )
|
||||
self.__actions.stop()
|
||||
self.__actions.join()
|
||||
self.assertLogged("Failed to stop")
|
||||
|
||||
def testBanActionsAInfo(self):
|
||||
# Action which deletes IP address from aInfo
|
||||
self.__actions.add(
|
||||
"action1",
|
||||
os.path.join(TEST_FILES_DIR, "action.d/action_modifyainfo.py"),
|
||||
{})
|
||||
self.__actions.add(
|
||||
"action2",
|
||||
os.path.join(TEST_FILES_DIR, "action.d/action_modifyainfo.py"),
|
||||
{})
|
||||
self.__jail.putFailTicket(FailTicket("1.2.3.4"))
|
||||
self.__actions._Actions__checkBan()
|
||||
# Will fail if modification of aInfo from first action propagates
|
||||
# to second action, as both delete same key
|
||||
self.assertNotLogged("Failed to execute ban")
|
||||
self.assertLogged("action1 ban deleted aInfo IP")
|
||||
self.assertLogged("action2 ban deleted aInfo IP")
|
||||
|
||||
self.__actions._Actions__flushBan()
|
||||
# Will fail if modification of aInfo from first action propagates
|
||||
# to second action, as both delete same key
|
||||
self.assertNotLogged("Failed to execute unban")
|
||||
self.assertLogged("action1 unban deleted aInfo IP")
|
||||
self.assertLogged("action2 unban deleted aInfo IP")
|
||||
|
||||
@with_alt_time
|
||||
def testUnbanOnBusyBanBombing(self):
|
||||
# check unban happens in-between of "ban bombing" despite lower precedence,
|
||||
# if it is not work, we'll not see "Unbanned 30" (rather "Unbanned 50")
|
||||
# because then all the unbans occur earliest at flushing (after stop)
|
||||
|
||||
# each 3rd ban we should see an unban check (and up to 5 tickets gets unbanned):
|
||||
self.__actions.banPrecedence = 3
|
||||
self.__actions.unbanMaxCount = 5
|
||||
self.__actions.setBanTime(100)
|
||||
|
||||
self.__actions.start()
|
||||
|
||||
MyTime.setTime(0); # avoid "expired bantime" (in 0.11)
|
||||
i = 0
|
||||
while i < 20:
|
||||
ip = "192.0.2.%d" % i
|
||||
self.__jail.putFailTicket(FailTicket(ip, 0))
|
||||
i += 1
|
||||
|
||||
# wait for last ban (all 20 tickets gets banned):
|
||||
self.assertLogged(' / 20,', wait=True)
|
||||
|
||||
MyTime.setTime(200); # unban time for 20 tickets reached
|
||||
|
||||
while i < 50:
|
||||
ip = "192.0.2.%d" % i
|
||||
self.__jail.putFailTicket(FailTicket(ip, 200))
|
||||
i += 1
|
||||
|
||||
# wait for last ban (all 50 tickets gets banned):
|
||||
self.assertLogged(' / 50,', wait=True)
|
||||
self.__actions.stop()
|
||||
self.__actions.join()
|
||||
|
||||
self.assertLogged('Unbanned 30, 0 ticket(s)')
|
||||
self.assertNotLogged('Unbanned 50, 0 ticket(s)')
|
||||
|
||||
def testActionsConsistencyCheck(self):
|
||||
act = self.defaultAction({'check':' <family>', 'flush':' <family>'})
|
||||
# flush for inet6 is intentionally "broken" here - test no unhandled except and invariant check:
|
||||
act['actionflush?family=inet6'] = act.actionflush + '; exit 1'
|
||||
act.actionstart_on_demand = True
|
||||
# force errors via check in ban/unban:
|
||||
act.actionban = "<actioncheck> ; " + act.actionban
|
||||
act.actionunban = "<actioncheck> ; " + act.actionunban
|
||||
self.__actions.start()
|
||||
self.assertNotLogged("stdout: %r" % 'ip start')
|
||||
|
||||
self.assertEqual(self.__actions.addBannedIP('192.0.2.1'), 1)
|
||||
self.assertEqual(self.__actions.addBannedIP('2001:db8::1'), 1)
|
||||
self.assertLogged('Ban 192.0.2.1', 'Ban 2001:db8::1',
|
||||
"stdout: %r" % 'ip start',
|
||||
"stdout: %r" % 'ip ban 192.0.2.1',
|
||||
"stdout: %r" % 'ip ban 2001:db8::1',
|
||||
all=True, wait=True)
|
||||
|
||||
# check should fail (so cause stop/start):
|
||||
self.pruneLog('[test-phase 1a] simulate inconsistent irreparable env by unban')
|
||||
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
||||
self.__actions.removeBannedIP('2001:db8::1')
|
||||
self.assertLogged('Invariant check failed. Unban is impossible.',
|
||||
wait=True)
|
||||
self.pruneLog('[test-phase 1b] simulate inconsistent irreparable env by flush')
|
||||
self.__actions._Actions__flushBan()
|
||||
self.assertLogged(
|
||||
"stdout: %r" % 'ip flush inet4',
|
||||
"stdout: %r" % 'ip flush inet6',
|
||||
'Failed to flush bans',
|
||||
'No flush occurred, do consistency check',
|
||||
'Invariant check failed. Trying to restore a sane environment',
|
||||
"stdout: %r" % 'ip stop', # same for both families
|
||||
'Failed to flush bans',
|
||||
all=True, wait=True)
|
||||
|
||||
# check succeeds:
|
||||
self.pruneLog('[test-phase 2] consistent env')
|
||||
act['actioncheck?family=inet6'] = act.actioncheck
|
||||
self.assertEqual(self.__actions.addBannedIP('2001:db8::1'), 1)
|
||||
self.assertLogged('Ban 2001:db8::1',
|
||||
"stdout: %r" % 'ip start', # same for both families
|
||||
"stdout: %r" % 'ip ban 2001:db8::1',
|
||||
all=True, wait=True)
|
||||
self.assertNotLogged("stdout: %r" % 'ip check inet4',
|
||||
all=True)
|
||||
|
||||
self.pruneLog('[test-phase 3] failed flush in consistent env')
|
||||
self.__actions._Actions__flushBan()
|
||||
self.assertLogged('Failed to flush bans',
|
||||
'No flush occurred, do consistency check',
|
||||
"stdout: %r" % 'ip flush inet6',
|
||||
"stdout: %r" % 'ip check inet6',
|
||||
all=True, wait=True)
|
||||
self.assertNotLogged(
|
||||
"stdout: %r" % 'ip flush inet4',
|
||||
"stdout: %r" % 'ip stop',
|
||||
"stdout: %r" % 'ip start',
|
||||
'Unable to restore environment',
|
||||
all=True)
|
||||
|
||||
# stop, flush succeeds:
|
||||
self.pruneLog('[test-phase end] flush successful')
|
||||
act['actionflush?family=inet6'] = act.actionflush
|
||||
self.__actions.stop()
|
||||
self.__actions.join()
|
||||
self.assertLogged(
|
||||
"stdout: %r" % 'ip flush inet6',
|
||||
"stdout: %r" % 'ip stop', # same for both families
|
||||
'action ip terminated',
|
||||
all=True, wait=True)
|
||||
# no flush for inet4 (already successfully flushed):
|
||||
self.assertNotLogged("ERROR",
|
||||
"stdout: %r" % 'ip flush inet4',
|
||||
'Unban tickets each individually',
|
||||
all=True)
|
||||
|
||||
def testActionsConsistencyCheckDiffFam(self):
|
||||
# same as testActionsConsistencyCheck, but different start/stop commands for both families and repair on unban
|
||||
act = self.defaultAction({'start':' <family>', 'check':' <family>', 'flush':' <family>', 'stop':' <family>'})
|
||||
# flush for inet6 is intentionally "broken" here - test no unhandled except and invariant check:
|
||||
act['actionflush?family=inet6'] = act.actionflush + '; exit 1'
|
||||
act.actionstart_on_demand = True
|
||||
act.actionrepair_on_unban = True
|
||||
# force errors via check in ban/unban:
|
||||
act.actionban = "<actioncheck> ; " + act.actionban
|
||||
act.actionunban = "<actioncheck> ; " + act.actionunban
|
||||
self.__actions.start()
|
||||
self.assertNotLogged("stdout: %r" % 'ip start')
|
||||
|
||||
self.assertEqual(self.__actions.addBannedIP('192.0.2.1'), 1)
|
||||
self.assertEqual(self.__actions.addBannedIP('2001:db8::1'), 1)
|
||||
self.assertLogged('Ban 192.0.2.1', 'Ban 2001:db8::1',
|
||||
"stdout: %r" % 'ip start inet4',
|
||||
"stdout: %r" % 'ip ban 192.0.2.1',
|
||||
"stdout: %r" % 'ip start inet6',
|
||||
"stdout: %r" % 'ip ban 2001:db8::1',
|
||||
all=True, wait=True)
|
||||
|
||||
# check should fail (so cause stop/start):
|
||||
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
||||
self.pruneLog('[test-phase 1a] simulate inconsistent irreparable env by unban')
|
||||
self.__actions.removeBannedIP('2001:db8::1')
|
||||
self.assertLogged('Invariant check failed. Trying to restore a sane environment',
|
||||
"stdout: %r" % 'ip stop inet6',
|
||||
all=True, wait=True)
|
||||
self.assertNotLogged(
|
||||
"stdout: %r" % 'ip start inet6', # start on demand (not on repair)
|
||||
"stdout: %r" % 'ip stop inet4', # family inet4 is not affected
|
||||
"stdout: %r" % 'ip start inet4',
|
||||
all=True)
|
||||
|
||||
self.pruneLog('[test-phase 1b] simulate inconsistent irreparable env by ban')
|
||||
self.assertEqual(self.__actions.addBannedIP('2001:db8::1'), 1)
|
||||
self.assertLogged('Invariant check failed. Trying to restore a sane environment',
|
||||
"stdout: %r" % 'ip stop inet6',
|
||||
"stdout: %r" % 'ip start inet6',
|
||||
"stdout: %r" % 'ip check inet6',
|
||||
'Unable to restore environment',
|
||||
'Failed to execute ban',
|
||||
all=True, wait=True)
|
||||
self.assertNotLogged(
|
||||
"stdout: %r" % 'ip stop inet4', # family inet4 is not affected
|
||||
"stdout: %r" % 'ip start inet4',
|
||||
all=True)
|
||||
|
||||
act['actioncheck?family=inet6'] = act.actioncheck
|
||||
self.assertEqual(self.__actions.addBannedIP('2001:db8::2'), 1)
|
||||
act['actioncheck?family=inet6'] = act.actioncheck + '; exit 1'
|
||||
self.pruneLog('[test-phase 1c] simulate inconsistent irreparable env by flush')
|
||||
self.__actions._Actions__flushBan()
|
||||
self.assertLogged(
|
||||
"stdout: %r" % 'ip flush inet4',
|
||||
"stdout: %r" % 'ip flush inet6',
|
||||
'Failed to flush bans',
|
||||
'No flush occurred, do consistency check',
|
||||
'Invariant check failed. Trying to restore a sane environment',
|
||||
"stdout: %r" % 'ip stop inet6',
|
||||
'Failed to flush bans in jail',
|
||||
all=True, wait=True)
|
||||
# start/stop should be called for inet6 only:
|
||||
self.assertNotLogged(
|
||||
"stdout: %r" % 'ip stop inet4',
|
||||
all=True)
|
||||
|
||||
# check succeeds:
|
||||
self.pruneLog('[test-phase 2] consistent env')
|
||||
act['actioncheck?family=inet6'] = act.actioncheck
|
||||
self.assertEqual(self.__actions.addBannedIP('2001:db8::1'), 1)
|
||||
self.assertLogged('Ban 2001:db8::1',
|
||||
"stdout: %r" % 'ip start inet6',
|
||||
"stdout: %r" % 'ip ban 2001:db8::1',
|
||||
all=True, wait=True)
|
||||
self.assertNotLogged(
|
||||
"stdout: %r" % 'ip check inet4',
|
||||
"stdout: %r" % 'ip start inet4',
|
||||
all=True)
|
||||
|
||||
self.pruneLog('[test-phase 3] failed flush in consistent env')
|
||||
act['actioncheck?family=inet6'] = act.actioncheck
|
||||
self.__actions._Actions__flushBan()
|
||||
self.assertLogged('Failed to flush bans',
|
||||
'No flush occurred, do consistency check',
|
||||
"stdout: %r" % 'ip flush inet6',
|
||||
"stdout: %r" % 'ip check inet6',
|
||||
all=True, wait=True)
|
||||
self.assertNotLogged(
|
||||
"stdout: %r" % 'ip flush inet4',
|
||||
"stdout: %r" % 'ip stop inet4',
|
||||
"stdout: %r" % 'ip start inet4',
|
||||
"stdout: %r" % 'ip stop inet6',
|
||||
"stdout: %r" % 'ip start inet6',
|
||||
all=True)
|
||||
|
||||
# stop, flush succeeds:
|
||||
self.pruneLog('[test-phase end] flush successful')
|
||||
act['actionflush?family=inet6'] = act.actionflush
|
||||
self.__actions.stop()
|
||||
self.__actions.join()
|
||||
self.assertLogged(
|
||||
"stdout: %r" % 'ip flush inet6',
|
||||
"stdout: %r" % 'ip stop inet4',
|
||||
"stdout: %r" % 'ip stop inet6',
|
||||
'action ip terminated',
|
||||
all=True, wait=True)
|
||||
# no flush for inet4 (already successfully flushed):
|
||||
self.assertNotLogged("ERROR",
|
||||
"stdout: %r" % 'ip flush inet4',
|
||||
'Unban tickets each individually',
|
||||
all=True)
|
||||
|
||||
@with_alt_time
|
||||
@with_tmpdir
|
||||
def testActionsRebanBrokenAfterRepair(self, tmp):
|
||||
act = self.defaultAction({
|
||||
'start':' <family>; touch "<FN>"',
|
||||
'check':' <family>; test -f "<FN>"',
|
||||
'flush':' <family>; echo -n "" > "<FN>"',
|
||||
'stop': ' <family>; rm -f "<FN>"',
|
||||
'ban': ' <family>; echo "<ip> <family>" >> "<FN>"',
|
||||
})
|
||||
act['FN'] = tmp+'/<family>'
|
||||
act.actionstart_on_demand = True
|
||||
act.actionrepair = 'echo ip repair <family>; touch "<FN>"'
|
||||
act.actionreban = 'echo ip reban <ip> <family>; echo "<ip> <family> -- rebanned" >> "<FN>"'
|
||||
self.pruneLog('[test-phase 0] initial ban')
|
||||
self.assertEqual(self.__actions.addBannedIP(['192.0.2.1', '2001:db8::1']), 2)
|
||||
self.assertLogged('Ban 192.0.2.1', 'Ban 2001:db8::1',
|
||||
"stdout: %r" % 'ip start inet4',
|
||||
"stdout: %r" % 'ip ban 192.0.2.1 inet4',
|
||||
"stdout: %r" % 'ip start inet6',
|
||||
"stdout: %r" % 'ip ban 2001:db8::1 inet6',
|
||||
all=True)
|
||||
|
||||
self.pruneLog('[test-phase 1] check ban')
|
||||
self.dumpFile(tmp+'/inet4')
|
||||
self.assertLogged('192.0.2.1 inet4')
|
||||
self.assertNotLogged('2001:db8::1 inet6')
|
||||
self.pruneLog()
|
||||
self.dumpFile(tmp+'/inet6')
|
||||
self.assertLogged('2001:db8::1 inet6')
|
||||
self.assertNotLogged('192.0.2.1 inet4')
|
||||
|
||||
# simulate 3 seconds past:
|
||||
MyTime.setTime(MyTime.time() + 4)
|
||||
# already banned produces events:
|
||||
self.pruneLog('[test-phase 2] check already banned')
|
||||
self.assertEqual(self.__actions.addBannedIP(['192.0.2.1', '2001:db8::1', '2001:db8::2']), 1)
|
||||
self.assertLogged(
|
||||
'192.0.2.1 already banned', '2001:db8::1 already banned', 'Ban 2001:db8::2',
|
||||
"stdout: %r" % 'ip check inet4', # both checks occurred
|
||||
"stdout: %r" % 'ip check inet6',
|
||||
all=True)
|
||||
self.dumpFile(tmp+'/inet4')
|
||||
self.dumpFile(tmp+'/inet6')
|
||||
# no reban should occur:
|
||||
self.assertNotLogged('Reban 192.0.2.1', 'Reban 2001:db8::1',
|
||||
"stdout: %r" % 'ip ban 192.0.2.1 inet4',
|
||||
"stdout: %r" % 'ip reban 192.0.2.1 inet4',
|
||||
"stdout: %r" % 'ip ban 2001:db8::1 inet6',
|
||||
"stdout: %r" % 'ip reban 2001:db8::1 inet6',
|
||||
'192.0.2.1 inet4 -- repaired',
|
||||
'2001:db8::1 inet6 -- repaired',
|
||||
all=True)
|
||||
|
||||
# simulate 3 seconds past:
|
||||
MyTime.setTime(MyTime.time() + 4)
|
||||
# break env (remove both files, so check would fail):
|
||||
os.remove(tmp+'/inet4')
|
||||
os.remove(tmp+'/inet6')
|
||||
# test again already banned (it shall cause reban now):
|
||||
self.pruneLog('[test-phase 3a] check reban after sane env repaired')
|
||||
self.assertEqual(self.__actions.addBannedIP(['192.0.2.1', '2001:db8::1']), 2)
|
||||
self.assertLogged(
|
||||
"Invariant check failed. Trying to restore a sane environment",
|
||||
"stdout: %r" % 'ip repair inet4', # both repairs occurred
|
||||
"stdout: %r" % 'ip repair inet6',
|
||||
"Reban 192.0.2.1, action 'ip'", "Reban 2001:db8::1, action 'ip'", # both rebans also
|
||||
"stdout: %r" % 'ip reban 192.0.2.1 inet4',
|
||||
"stdout: %r" % 'ip reban 2001:db8::1 inet6',
|
||||
all=True)
|
||||
|
||||
# now last IP (2001:db8::2) - no repair, but still old epoch of ticket, so it gets rebanned:
|
||||
self.pruneLog('[test-phase 3a] check reban by epoch mismatch (without repair)')
|
||||
self.assertEqual(self.__actions.addBannedIP('2001:db8::2'), 1)
|
||||
self.assertLogged(
|
||||
"Reban 2001:db8::2, action 'ip'",
|
||||
"stdout: %r" % 'ip reban 2001:db8::2 inet6',
|
||||
all=True)
|
||||
self.assertNotLogged(
|
||||
"Invariant check failed. Trying to restore a sane environment",
|
||||
"stdout: %r" % 'ip repair inet4', # both repairs occurred
|
||||
"stdout: %r" % 'ip repair inet6',
|
||||
"Reban 192.0.2.1, action 'ip'", "Reban 2001:db8::1, action 'ip'", # both rebans also
|
||||
"stdout: %r" % 'ip reban 192.0.2.1 inet4',
|
||||
"stdout: %r" % 'ip reban 2001:db8::1 inet6',
|
||||
all=True)
|
||||
|
||||
# and bans present in files:
|
||||
self.pruneLog('[test-phase 4] check reban')
|
||||
self.dumpFile(tmp+'/inet4')
|
||||
self.assertLogged('192.0.2.1 inet4 -- rebanned')
|
||||
self.assertNotLogged('2001:db8::1 inet6 -- rebanned')
|
||||
self.pruneLog()
|
||||
self.dumpFile(tmp+'/inet6')
|
||||
self.assertLogged(
|
||||
'2001:db8::1 inet6 -- rebanned',
|
||||
'2001:db8::2 inet6 -- rebanned', all=True)
|
||||
self.assertNotLogged('192.0.2.1 inet4 -- rebanned')
|
||||
|
||||
# coverage - intended error in reban (no unhandled exception, message logged):
|
||||
act.actionreban = ''
|
||||
act.actionban = 'exit 1'
|
||||
self.assertEqual(self.__actions._Actions__reBan(FailTicket("192.0.2.1", 0)), 0)
|
||||
self.assertLogged(
|
||||
'Failed to execute reban',
|
||||
'Error banning 192.0.2.1', all=True)
|
||||
679
fail2ban-master/fail2ban/tests/actiontestcase.py
Normal file
679
fail2ban-master/fail2ban/tests/actiontestcase.py
Normal file
@@ -0,0 +1,679 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from ..server.action import CommandAction, CallingMap, substituteRecursiveTags
|
||||
from ..server.actions import OrderedDict, Actions
|
||||
from ..server.utils import Utils
|
||||
|
||||
from .dummyjail import DummyJail
|
||||
from .utils import pid_exists, with_tmpdir, LogCaptureTestCase
|
||||
|
||||
|
||||
class CommandActionTest(LogCaptureTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
LogCaptureTestCase.setUp(self)
|
||||
self.__action = CommandAction(None, "Test")
|
||||
# prevent execute stop if start fails (or event not started at all):
|
||||
self.__action_started = False
|
||||
orgstart = self.__action.start
|
||||
def _action_start():
|
||||
self.__action_started = True
|
||||
return orgstart()
|
||||
self.__action.start = _action_start
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
if self.__action_started:
|
||||
self.__action.stop()
|
||||
LogCaptureTestCase.tearDown(self)
|
||||
|
||||
def testSubstituteRecursiveTags(self):
|
||||
aInfo = {
|
||||
'HOST': "192.0.2.0",
|
||||
'ABC': "123 <HOST>",
|
||||
'xyz': "890 <ABC>",
|
||||
}
|
||||
# Recursion is bad
|
||||
self.assertRaises(ValueError,
|
||||
lambda: substituteRecursiveTags({'A': '<A>'}))
|
||||
self.assertRaises(ValueError,
|
||||
lambda: substituteRecursiveTags({'A': '<B>', 'B': '<A>'}))
|
||||
self.assertRaises(ValueError,
|
||||
lambda: substituteRecursiveTags({'A': '<B>', 'B': '<C>', 'C': '<A>'}))
|
||||
# Unresolveable substitution
|
||||
self.assertRaises(ValueError,
|
||||
lambda: substituteRecursiveTags({'A': 'to=<B> fromip=<IP>', 'C': '<B>', 'B': '<C>', 'D': ''}))
|
||||
self.assertRaises(ValueError,
|
||||
lambda: substituteRecursiveTags({'failregex': 'to=<honeypot> fromip=<IP>', 'sweet': '<honeypot>', 'honeypot': '<sweet>', 'ignoreregex': ''}))
|
||||
# No cyclic recursion, just multiple replacement of tag <T>, should be successful:
|
||||
self.assertEqual(substituteRecursiveTags( OrderedDict(
|
||||
(('X', 'x=x<T>'), ('T', '1'), ('Z', '<X> <T> <Y>'), ('Y', 'y=y<T>')))
|
||||
), {'X': 'x=x1', 'T': '1', 'Y': 'y=y1', 'Z': 'x=x1 1 y=y1'}
|
||||
)
|
||||
# No cyclic recursion, just multiple replacement of tag <T> in composite tags, should be successful:
|
||||
self.assertEqual(substituteRecursiveTags( OrderedDict(
|
||||
(('X', 'x=x<T> <Z> <<R1>> <<R2>>'), ('R1', 'Z'), ('R2', 'Y'), ('T', '1'), ('Z', '<T> <Y>'), ('Y', 'y=y<T>')))
|
||||
), {'X': 'x=x1 1 y=y1 1 y=y1 y=y1', 'R1': 'Z', 'R2': 'Y', 'T': '1', 'Z': '1 y=y1', 'Y': 'y=y1'}
|
||||
)
|
||||
# No cyclic recursion, just multiple replacement of same tags, should be successful:
|
||||
self.assertEqual(substituteRecursiveTags( OrderedDict((
|
||||
('actionstart', 'ipset create <ipmset> hash:ip timeout <bantime> family <ipsetfamily>\n<iptables> -I <chain> <actiontype>'),
|
||||
('ipmset', 'f2b-<name>'),
|
||||
('name', 'any'),
|
||||
('bantime', '600'),
|
||||
('ipsetfamily', 'inet'),
|
||||
('iptables', 'iptables <lockingopt>'),
|
||||
('lockingopt', '-w'),
|
||||
('chain', 'INPUT'),
|
||||
('actiontype', '<multiport>'),
|
||||
('multiport', '-p <protocol> -m multiport --dports <port> -m set --match-set <ipmset> src -j <blocktype>'),
|
||||
('protocol', 'tcp'),
|
||||
('port', 'ssh'),
|
||||
('blocktype', 'REJECT',),
|
||||
))
|
||||
), OrderedDict((
|
||||
('actionstart', 'ipset create f2b-any hash:ip timeout 600 family inet\niptables -w -I INPUT -p tcp -m multiport --dports ssh -m set --match-set f2b-any src -j REJECT'),
|
||||
('ipmset', 'f2b-any'),
|
||||
('name', 'any'),
|
||||
('bantime', '600'),
|
||||
('ipsetfamily', 'inet'),
|
||||
('iptables', 'iptables -w'),
|
||||
('lockingopt', '-w'),
|
||||
('chain', 'INPUT'),
|
||||
('actiontype', '-p tcp -m multiport --dports ssh -m set --match-set f2b-any src -j REJECT'),
|
||||
('multiport', '-p tcp -m multiport --dports ssh -m set --match-set f2b-any src -j REJECT'),
|
||||
('protocol', 'tcp'),
|
||||
('port', 'ssh'),
|
||||
('blocktype', 'REJECT')
|
||||
))
|
||||
)
|
||||
# Cyclic recursion by composite tag creation, tags "create" another tag, that closes cycle:
|
||||
self.assertRaises(ValueError, lambda: substituteRecursiveTags( OrderedDict((
|
||||
('A', '<<B><C>>'),
|
||||
('B', 'D'), ('C', 'E'),
|
||||
('DE', 'cycle <A>'),
|
||||
)) ))
|
||||
self.assertRaises(ValueError, lambda: substituteRecursiveTags( OrderedDict((
|
||||
('DE', 'cycle <A>'),
|
||||
('A', '<<B><C>>'),
|
||||
('B', 'D'), ('C', 'E'),
|
||||
)) ))
|
||||
|
||||
# missing tags are ok
|
||||
self.assertEqual(substituteRecursiveTags({'A': '<C>'}), {'A': '<C>'})
|
||||
self.assertEqual(substituteRecursiveTags({'A': '<C> <D> <X>','X':'fun'}), {'A': '<C> <D> fun', 'X':'fun'})
|
||||
self.assertEqual(substituteRecursiveTags({'A': '<C> <B>', 'B': 'cool'}), {'A': '<C> cool', 'B': 'cool'})
|
||||
# Escaped tags should be ignored
|
||||
self.assertEqual(substituteRecursiveTags({'A': '<matches> <B>', 'B': 'cool'}), {'A': '<matches> cool', 'B': 'cool'})
|
||||
# Multiple stuff on same line is ok
|
||||
self.assertEqual(substituteRecursiveTags({'failregex': 'to=<honeypot> fromip=<IP> evilperson=<honeypot>', 'honeypot': 'pokie', 'ignoreregex': ''}),
|
||||
{ 'failregex': "to=pokie fromip=<IP> evilperson=pokie",
|
||||
'honeypot': 'pokie',
|
||||
'ignoreregex': '',
|
||||
})
|
||||
# rest is just cool
|
||||
self.assertEqual(substituteRecursiveTags(aInfo),
|
||||
{ 'HOST': "192.0.2.0",
|
||||
'ABC': '123 192.0.2.0',
|
||||
'xyz': '890 123 192.0.2.0',
|
||||
})
|
||||
# obscure embedded case
|
||||
self.assertEqual(substituteRecursiveTags({'A': '<<PREF>HOST>', 'PREF': 'IPV4'}),
|
||||
{'A': '<IPV4HOST>', 'PREF': 'IPV4'})
|
||||
self.assertEqual(substituteRecursiveTags({'A': '<<PREF>HOST>', 'PREF': 'IPV4', 'IPV4HOST': '1.2.3.4'}),
|
||||
{'A': '1.2.3.4', 'PREF': 'IPV4', 'IPV4HOST': '1.2.3.4'})
|
||||
# more embedded within a string and two interpolations
|
||||
self.assertEqual(substituteRecursiveTags({'A': 'A <IP<PREF>HOST> B IP<PREF> C', 'PREF': 'V4', 'IPV4HOST': '1.2.3.4'}),
|
||||
{'A': 'A 1.2.3.4 B IPV4 C', 'PREF': 'V4', 'IPV4HOST': '1.2.3.4'})
|
||||
|
||||
def testSubstRec_DontTouchUnusedCallable(self):
|
||||
cm = CallingMap({
|
||||
'A':0,
|
||||
'B':lambda self: '<A><A>',
|
||||
'C':'',
|
||||
'D':''
|
||||
})
|
||||
#
|
||||
# should raise no exceptions:
|
||||
substituteRecursiveTags(cm)
|
||||
# add exception tag:
|
||||
cm['C'] = lambda self,i=0: 5 // int(self['A']) # raise error by access
|
||||
# test direct get of callable (should raise an error):
|
||||
self.assertRaises(ZeroDivisionError, lambda: cm['C'])
|
||||
# should raise no exceptions (tag "C" still unused):
|
||||
substituteRecursiveTags(cm)
|
||||
# add reference to "broken" tag:
|
||||
cm['D'] = 'test=<C>'
|
||||
# should raise an exception (BOOM by replacement of tag "D" recursive):
|
||||
self.assertRaises(ZeroDivisionError, lambda: substituteRecursiveTags(cm))
|
||||
#
|
||||
# should raise no exceptions:
|
||||
self.assertEqual(self.__action.replaceTag('test=<A>', cm), "test=0")
|
||||
# **Important**: recursive replacement of dynamic data from calling map should be prohibited,
|
||||
# otherwise may be vulnerable on foreign user-input:
|
||||
self.assertEqual(self.__action.replaceTag('test=<A>--<B>--<A>', cm), "test=0--<A><A>--0")
|
||||
# should raise an exception (BOOM by replacement of tag "C"):
|
||||
self.assertRaises(ZeroDivisionError, lambda: self.__action.replaceTag('test=<C>', cm))
|
||||
# should raise no exceptions (replaces tag "D" only):
|
||||
self.assertEqual(self.__action.replaceTag('<D>', cm), "test=<C>")
|
||||
|
||||
def testReplaceTag(self):
|
||||
aInfo = {
|
||||
'HOST': "192.0.2.0",
|
||||
'ABC': "123",
|
||||
'xyz': "890",
|
||||
}
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("Text<br>text", aInfo),
|
||||
"Text\ntext")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("Text <HOST> text", aInfo),
|
||||
"Text 192.0.2.0 text")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("Text <xyz> text <ABC> ABC", aInfo),
|
||||
"Text 890 text 123 ABC")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<matches>",
|
||||
{'matches': "some >char< should \\< be[ escap}ed&\n"}),
|
||||
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\\n")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<ipmatches>",
|
||||
{'ipmatches': "some >char< should \\< be[ escap}ed&\n"}),
|
||||
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\\n")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<ipjailmatches>",
|
||||
{'ipjailmatches': "some >char< should \\< be[ escap}ed&\r\n"}),
|
||||
"some \\>char\\< should \\\\\\< be\\[ escap\\}ed\\&\\r\\n")
|
||||
|
||||
# Recursive
|
||||
aInfo["ABC"] = "<xyz>"
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("Text <xyz> text <ABC> ABC", aInfo),
|
||||
"Text 890 text 890 ABC")
|
||||
|
||||
# Callable
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("09 <matches> 11",
|
||||
CallingMap(matches=lambda self: str(10))),
|
||||
"09 10 11")
|
||||
|
||||
def testReplaceNoTag(self):
|
||||
# As tag not present, therefore callable should not be called
|
||||
# Will raise ValueError if it is
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("abc",
|
||||
CallingMap(matches=lambda self: int("a"))), "abc")
|
||||
|
||||
def testReplaceTagSelfRecursion(self):
|
||||
setattr(self.__action, 'a', "<a")
|
||||
setattr(self.__action, 'b', "c>")
|
||||
setattr(self.__action, 'b?family=inet6', "b>")
|
||||
setattr(self.__action, 'ac', "<a><b>")
|
||||
setattr(self.__action, 'ab', "<ac>")
|
||||
setattr(self.__action, 'x?family=inet6', "")
|
||||
# produce self-referencing properties except:
|
||||
self.assertRaisesRegex(ValueError, r"properties contain self referencing definitions",
|
||||
lambda: self.__action.replaceTag("<a><b>",
|
||||
self.__action._properties, conditional="family=inet4")
|
||||
)
|
||||
# remote self-referencing in props:
|
||||
delattr(self.__action, 'ac')
|
||||
# produce self-referencing query except:
|
||||
self.assertRaisesRegex(ValueError, r"possible self referencing definitions in query",
|
||||
lambda: self.__action.replaceTag("<x"*30+">"*30,
|
||||
self.__action._properties, conditional="family=inet6")
|
||||
)
|
||||
|
||||
def testReplaceTagConditionalCached(self):
|
||||
setattr(self.__action, 'abc', "123")
|
||||
setattr(self.__action, 'abc?family=inet4', "345")
|
||||
setattr(self.__action, 'abc?family=inet6', "567")
|
||||
setattr(self.__action, 'xyz', "890-<abc>")
|
||||
setattr(self.__action, 'banaction', "Text <xyz> text <abc>")
|
||||
# test replacement in sub tags and direct, conditional, cached:
|
||||
cache = self.__action._substCache
|
||||
for i in range(2):
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<banaction> '<abc>'", self.__action._properties,
|
||||
conditional="", cache=cache),
|
||||
"Text 890-123 text 123 '123'")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<banaction> '<abc>'", self.__action._properties,
|
||||
conditional="family=inet4", cache=cache),
|
||||
"Text 890-345 text 345 '345'")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<banaction> '<abc>'", self.__action._properties,
|
||||
conditional="family=inet6", cache=cache),
|
||||
"Text 890-567 text 567 '567'")
|
||||
self.assertTrue(len(cache) >= 3)
|
||||
# set one parameter - internal properties and cache should be reset:
|
||||
setattr(self.__action, 'xyz', "000-<abc>")
|
||||
self.assertEqual(len(cache), 0)
|
||||
# test againg, should have 000 instead of 890:
|
||||
for i in range(2):
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<banaction> '<abc>'", self.__action._properties,
|
||||
conditional="", cache=cache),
|
||||
"Text 000-123 text 123 '123'")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<banaction> '<abc>'", self.__action._properties,
|
||||
conditional="family=inet4", cache=cache),
|
||||
"Text 000-345 text 345 '345'")
|
||||
self.assertEqual(
|
||||
self.__action.replaceTag("<banaction> '<abc>'", self.__action._properties,
|
||||
conditional="family=inet6", cache=cache),
|
||||
"Text 000-567 text 567 '567'")
|
||||
self.assertTrue(len(cache) >= 3)
|
||||
|
||||
@with_tmpdir
|
||||
def testExecuteActionBan(self, tmp):
|
||||
tmp += "/fail2ban.test"
|
||||
self.__action.actionstart = "touch '%s'" % tmp
|
||||
self.__action.actionrepair = self.__action.actionstart
|
||||
self.assertEqual(self.__action.actionstart, "touch '%s'" % tmp)
|
||||
self.__action.actionstop = "rm -f '%s'" % tmp
|
||||
self.assertEqual(self.__action.actionstop, "rm -f '%s'" % tmp)
|
||||
self.__action.actionban = "<actioncheck> && echo -n"
|
||||
self.assertEqual(self.__action.actionban, "<actioncheck> && echo -n")
|
||||
self.__action.actioncheck = "[ -e '%s' ]" % tmp
|
||||
self.assertEqual(self.__action.actioncheck, "[ -e '%s' ]" % tmp)
|
||||
self.__action.actionunban = "true"
|
||||
self.assertEqual(self.__action.actionunban, 'true')
|
||||
self.pruneLog()
|
||||
|
||||
self.assertNotLogged('returned')
|
||||
# no action was actually executed yet
|
||||
|
||||
# start on demand is false, so it should cause failure on first attempt of ban:
|
||||
self.__action.ban({'ip': None})
|
||||
self.assertLogged('Invariant check failed')
|
||||
self.assertLogged('returned successfully')
|
||||
self.__action.stop()
|
||||
self.assertLogged(self.__action.actionstop)
|
||||
|
||||
def testExecuteActionEmptyUnban(self):
|
||||
# unban will be executed for actions with banned items only:
|
||||
self.__action.actionban = ""
|
||||
self.__action.actionunban = ""
|
||||
self.__action.actionflush = "echo -n 'flush'"
|
||||
self.__action.actionstop = "echo -n 'stop'"
|
||||
self.__action.start();
|
||||
self.__action.ban({});
|
||||
self.pruneLog()
|
||||
self.__action.unban({})
|
||||
self.assertLogged('Nothing to do', wait=True)
|
||||
# same as above but with interim flush, so no unban anymore:
|
||||
self.__action.ban({});
|
||||
self.pruneLog('[phase 2]')
|
||||
self.__action.flush()
|
||||
self.__action.unban({})
|
||||
self.__action.stop()
|
||||
self.assertLogged('stop', wait=True)
|
||||
self.assertNotLogged('Nothing to do')
|
||||
|
||||
@with_tmpdir
|
||||
def testExecuteActionStartCtags(self, tmp):
|
||||
tmp += '/fail2ban.test'
|
||||
self.__action.HOST = "192.0.2.0"
|
||||
self.__action.actionstart = "touch '%s.<HOST>'" % tmp
|
||||
self.__action.actionstop = "rm -f '%s.<HOST>'" % tmp
|
||||
self.__action.actioncheck = "[ -e '%s.192.0.2.0' ]" % tmp
|
||||
self.__action.start()
|
||||
self.__action.consistencyCheck()
|
||||
|
||||
@with_tmpdir
|
||||
def testExecuteActionCheckRestoreEnvironment(self, tmp):
|
||||
tmp += '/fail2ban.test'
|
||||
self.__action.actionstart = ""
|
||||
self.__action.actionstop = "rm -f '%s'" % tmp
|
||||
self.__action.actionban = "rm '%s'" % tmp
|
||||
self.__action.actioncheck = "[ -e '%s' ]" % tmp
|
||||
self.assertRaises(RuntimeError, self.__action.ban, {'ip': None})
|
||||
self.assertLogged('Invariant check failed', 'Unable to restore environment', all=True)
|
||||
# 2nd time, try to restore with producing error in stop, but succeeded start hereafter:
|
||||
self.pruneLog('[phase 2]')
|
||||
self.__action.actionstart = "touch '%s'" % tmp
|
||||
self.__action.actionstop = "rm '%s'" % tmp
|
||||
self.__action.actionban = """<actioncheck> && printf "%%%%b\n" <ip> >> '%s'""" % tmp
|
||||
self.__action.actioncheck = "[ -e '%s' ]" % tmp
|
||||
self.__action.ban({'ip': None})
|
||||
self.assertLogged('Invariant check failed')
|
||||
self.assertNotLogged('Unable to restore environment')
|
||||
|
||||
@with_tmpdir
|
||||
def testExecuteActionCheckOnBanFailure(self, tmp):
|
||||
tmp += '/fail2ban.test'
|
||||
self.__action.actionstart = "touch '%s'; echo 'started ...'" % tmp
|
||||
self.__action.actionstop = "rm -f '%s'" % tmp
|
||||
self.__action.actionban = "[ -e '%s' ] && echo 'banned '<ip>" % tmp
|
||||
self.__action.actioncheck = "[ -e '%s' ] && echo 'check ok' || { echo 'check failed'; exit 1; }" % tmp
|
||||
self.__action.actionrepair = "echo 'repair ...'; touch '%s'" % tmp
|
||||
self.__action.actionstart_on_demand = False
|
||||
self.__action.start()
|
||||
# phase 1: with repair;
|
||||
# phase 2: without repair (start/stop), not on demand;
|
||||
# phase 3: without repair (start/stop), start on demand.
|
||||
for i in (1, 2, 3):
|
||||
self.pruneLog('[phase %s]' % i)
|
||||
# 1st time with success ban:
|
||||
self.__action.ban({'ip': '192.0.2.1'})
|
||||
self.assertLogged(
|
||||
"stdout: %r" % 'banned 192.0.2.1', all=True)
|
||||
self.assertNotLogged("Invariant check failed. Trying",
|
||||
"stdout: %r" % 'check failed',
|
||||
"stdout: %r" % ('repair ...' if self.__action.actionrepair else 'started ...'),
|
||||
"stdout: %r" % 'check ok', all=True)
|
||||
# force error in ban:
|
||||
os.remove(tmp)
|
||||
self.pruneLog()
|
||||
# 2nd time with fail recognition, success repair, check and ban:
|
||||
self.__action.ban({'ip': '192.0.2.2'})
|
||||
self.assertLogged("Invariant check failed. Trying",
|
||||
"stdout: %r" % 'check failed',
|
||||
"stdout: %r" % ('repair ...' if self.__action.actionrepair else 'started ...'),
|
||||
"stdout: %r" % 'check ok',
|
||||
"stdout: %r" % 'banned 192.0.2.2', all=True)
|
||||
# repeat without repair (stop/start), herafter enable on demand:
|
||||
if self.__action.actionrepair:
|
||||
self.__action.actionrepair = ""
|
||||
elif not self.__action.actionstart_on_demand:
|
||||
self.__action.actionstart_on_demand = True
|
||||
|
||||
@with_tmpdir
|
||||
def testExecuteActionCheckRepairEnvironment(self, tmp):
|
||||
tmp += '/fail2ban.test'
|
||||
self.__action.actionstart = ""
|
||||
self.__action.actionstop = ""
|
||||
self.__action.actionban = "rm '%s'" % tmp
|
||||
self.__action.actioncheck = "[ -e '%s' ]" % tmp
|
||||
self.__action.actionrepair = "echo 'repair ...'; touch '%s'" % tmp
|
||||
# 1st time with success repair:
|
||||
self.__action.ban({'ip': None})
|
||||
self.assertLogged("Invariant check failed. Trying", "echo 'repair ...'", all=True)
|
||||
self.pruneLog()
|
||||
# 2nd time failed (not really repaired):
|
||||
self.__action.actionrepair = "echo 'repair ...'"
|
||||
self.assertRaises(RuntimeError, self.__action.ban, {'ip': None})
|
||||
self.assertLogged(
|
||||
"Invariant check failed. Trying",
|
||||
"echo 'repair ...'",
|
||||
"Unable to restore environment", all=True)
|
||||
|
||||
def testExecuteActionChangeCtags(self):
|
||||
self.assertRaises(AttributeError, getattr, self.__action, "ROST")
|
||||
self.__action.ROST = "192.0.2.0"
|
||||
self.assertEqual(self.__action.ROST,"192.0.2.0")
|
||||
|
||||
def testExecuteActionUnbanAinfo(self):
|
||||
aInfo = CallingMap({
|
||||
'ABC': "123",
|
||||
'ip': '192.0.2.1',
|
||||
'F-*': lambda self: {
|
||||
'fid': 111,
|
||||
'fport': 222,
|
||||
'user': "tester"
|
||||
}
|
||||
})
|
||||
self.__action.actionban = "echo '<ABC>, failure <F-ID> of <F-USER> -<F-TEST>- from <ip>:<F-PORT>'"
|
||||
self.__action.actionunban = "echo '<ABC>, user <F-USER> unbanned'"
|
||||
self.__action.ban(aInfo)
|
||||
self.__action.unban(aInfo)
|
||||
self.assertLogged(
|
||||
" -- stdout: '123, failure 111 of tester -- from 192.0.2.1:222'",
|
||||
" -- stdout: '123, user tester unbanned'",
|
||||
all=True
|
||||
)
|
||||
|
||||
def testExecuteActionStartEmpty(self):
|
||||
self.__action.actionstart = ""
|
||||
self.__action.start()
|
||||
self.assertTrue(self.__action.executeCmd(""))
|
||||
self.assertLogged('Nothing to do')
|
||||
self.pruneLog()
|
||||
self.assertTrue(self.__action._processCmd(""))
|
||||
self.assertLogged('Nothing to do')
|
||||
self.pruneLog()
|
||||
|
||||
def testExecuteWithVars(self):
|
||||
self.assertTrue(self.__action.executeCmd(
|
||||
r'''printf %b "foreign input:\n'''
|
||||
r''' -- $f2bV_A --\n'''
|
||||
r''' -- $f2bV_B --\n'''
|
||||
r''' -- $(echo -n $f2bV_C) --''' # echo just replaces \n to test it as single line
|
||||
r'''"''',
|
||||
varsDict={
|
||||
'f2bV_A': 'I\'m a hacker; && $(echo $f2bV_B)',
|
||||
'f2bV_B': 'I"m very bad hacker',
|
||||
'f2bV_C': '`Very | very\n$(bad & worst hacker)`'
|
||||
}))
|
||||
self.assertLogged(r"""foreign input:""",
|
||||
' -- I\'m a hacker; && $(echo $f2bV_B) --',
|
||||
' -- I"m very bad hacker --',
|
||||
' -- `Very | very $(bad & worst hacker)` --', all=True)
|
||||
|
||||
def testExecuteReplaceEscapeWithVars(self):
|
||||
self.__action.actionban = 'echo "** ban <ip>, reason: <reason> ...\\n<matches>"'
|
||||
self.__action.actionunban = 'echo "** unban <ip>"'
|
||||
self.__action.actionstop = 'echo "** stop monitoring"'
|
||||
matches = [
|
||||
'<actionunban>',
|
||||
'" Hooray! #',
|
||||
'`I\'m cool script kiddy',
|
||||
'`I`m very cool > /here-is-the-path/to/bin/.x-attempt.sh',
|
||||
'<actionstop>',
|
||||
]
|
||||
aInfo = {
|
||||
'ip': '192.0.2.1',
|
||||
'reason': 'hacking attempt ( he thought he knows how f2b internally works ;)',
|
||||
'matches': '\n'.join(matches)
|
||||
}
|
||||
self.pruneLog()
|
||||
self.__action.ban(aInfo)
|
||||
self.assertLogged(
|
||||
'** ban %s' % aInfo['ip'], aInfo['reason'], *matches, all=True)
|
||||
self.assertNotLogged(
|
||||
'** unban %s' % aInfo['ip'], '** stop monitoring', all=True)
|
||||
self.pruneLog()
|
||||
self.__action.unban(aInfo)
|
||||
self.__action.stop()
|
||||
self.assertLogged(
|
||||
'** unban %s' % aInfo['ip'], '** stop monitoring', all=True)
|
||||
|
||||
def testExecuteIncorrectCmd(self):
|
||||
CommandAction.executeCmd('/bin/ls >/dev/null\nbogusXXX now 2>/dev/null')
|
||||
self.assertLogged('HINT on 127: "Command not found"')
|
||||
|
||||
def testExecuteTimeout(self):
|
||||
stime = time.time()
|
||||
timeout = 1 if not unittest.F2B.fast else 0.01
|
||||
# Should take a 30 seconds (so timeout will occur)
|
||||
self.assertFalse(CommandAction.executeCmd('sleep 30', timeout=timeout))
|
||||
# give a test still 1 second, because system could be too busy
|
||||
self.assertTrue(time.time() >= stime + timeout and time.time() <= stime + timeout + 1)
|
||||
self.assertLogged('sleep 30', ' -- timed out after', all=True)
|
||||
self.assertLogged(' -- killed with SIGTERM',
|
||||
' -- killed with SIGKILL')
|
||||
|
||||
def testExecuteTimeoutWithNastyChildren(self):
|
||||
# temporary file for a nasty kid shell script
|
||||
tmpFilename = tempfile.mktemp(".sh", "fail2ban_")
|
||||
# Create a nasty script which would hang there for a while
|
||||
with open(tmpFilename, 'w') as f:
|
||||
f.write("""#!/bin/bash
|
||||
trap : HUP EXIT TERM
|
||||
|
||||
echo "$$" > %s.pid
|
||||
echo "my pid $$ . sleeping lo-o-o-ong"
|
||||
sleep 30
|
||||
""" % tmpFilename)
|
||||
stime = 0
|
||||
|
||||
# timeout as long as pid-file was not created, but max 5 seconds
|
||||
def getnasty_tout():
|
||||
return (
|
||||
getnastypid() is not None
|
||||
or time.time() - stime > 5
|
||||
)
|
||||
|
||||
def getnastypid():
|
||||
cpid = None
|
||||
if os.path.isfile(tmpFilename + '.pid'):
|
||||
with open(tmpFilename + '.pid') as f:
|
||||
try:
|
||||
cpid = int(f.read())
|
||||
except ValueError:
|
||||
pass
|
||||
return cpid
|
||||
|
||||
# First test if can kill the bastard
|
||||
stime = time.time()
|
||||
self.assertFalse(CommandAction.executeCmd(
|
||||
'bash %s' % tmpFilename, timeout=getnasty_tout))
|
||||
# Wait up to 3 seconds, the child got killed
|
||||
cpid = getnastypid()
|
||||
# Verify that the process itself got killed
|
||||
self.assertTrue(Utils.wait_for(lambda: not pid_exists(cpid), 3)) # process should have been killed
|
||||
self.assertLogged('my pid ', 'Resource temporarily unavailable')
|
||||
self.assertLogged('timed out')
|
||||
self.assertLogged('killed with SIGTERM',
|
||||
'killed with SIGKILL')
|
||||
os.unlink(tmpFilename + '.pid')
|
||||
|
||||
# A bit evolved case even though, previous test already tests killing children processes
|
||||
stime = time.time()
|
||||
self.assertFalse(CommandAction.executeCmd(
|
||||
'out=`bash %s`; echo ALRIGHT' % tmpFilename, timeout=getnasty_tout))
|
||||
# Wait up to 3 seconds, the child got killed
|
||||
cpid = getnastypid()
|
||||
# Verify that the process itself got killed
|
||||
self.assertTrue(Utils.wait_for(lambda: not pid_exists(cpid), 3))
|
||||
self.assertLogged('my pid ', 'Resource temporarily unavailable')
|
||||
self.assertLogged(' -- timed out')
|
||||
self.assertLogged(' -- killed with SIGTERM',
|
||||
' -- killed with SIGKILL')
|
||||
os.unlink(tmpFilename)
|
||||
os.unlink(tmpFilename + '.pid')
|
||||
|
||||
|
||||
def testCaptureStdOutErr(self):
|
||||
CommandAction.executeCmd('echo "How now brown cow"')
|
||||
self.assertLogged("stdout: 'How now brown cow'\n")
|
||||
CommandAction.executeCmd(
|
||||
'echo "The rain in Spain stays mainly in the plain" 1>&2')
|
||||
self.assertLogged(
|
||||
"stderr: 'The rain in Spain stays mainly in the plain'\n")
|
||||
|
||||
def testCallingMap(self):
|
||||
mymap = CallingMap(callme=lambda self: str(10), error=lambda self: int('a'),
|
||||
dontcallme= "string", number=17)
|
||||
|
||||
# Should work fine
|
||||
self.assertEqual(
|
||||
"%(callme)s okay %(dontcallme)s %(number)i" % mymap,
|
||||
"10 okay string 17")
|
||||
# Error will now trip, demonstrating delayed call
|
||||
self.assertRaises(ValueError, lambda x: "%(error)i" % x, mymap)
|
||||
|
||||
def testCallingMapModify(self):
|
||||
m = CallingMap({
|
||||
'a': lambda self: 2 + 3,
|
||||
'b': lambda self: self['a'] + 6,
|
||||
'c': 'test',
|
||||
})
|
||||
# test reset (without modifications):
|
||||
m.reset()
|
||||
# do modifications:
|
||||
m['a'] = 4
|
||||
del m['c']
|
||||
# test set and delete:
|
||||
self.assertEqual(len(m), 2)
|
||||
self.assertNotIn('c', m)
|
||||
self.assertEqual((m['a'], m['b']), (4, 10))
|
||||
# reset to original and test again:
|
||||
m.reset()
|
||||
s = repr(m)
|
||||
self.assertEqual(len(m), 3)
|
||||
self.assertIn('c', m)
|
||||
self.assertEqual((m['a'], m['b'], m['c']), (5, 11, 'test'))
|
||||
# immutability of copy:
|
||||
m['d'] = 'dddd'
|
||||
m2 = m.copy()
|
||||
m2['c'] = lambda self: self['a'] + 7
|
||||
m2['a'] = 1
|
||||
del m2['b']
|
||||
del m2['d']
|
||||
self.assertTrue('b' in m)
|
||||
self.assertTrue('d' in m)
|
||||
self.assertFalse('b' in m2)
|
||||
self.assertFalse('d' in m2)
|
||||
self.assertEqual((m['a'], m['b'], m['c'], m['d']), (5, 11, 'test', 'dddd'))
|
||||
self.assertEqual((m2['a'], m2['c']), (1, 8))
|
||||
|
||||
def testCallingMapRep(self):
|
||||
m = CallingMap({
|
||||
'a': lambda self: 2 + 3,
|
||||
'b': lambda self: self['a'] + 6,
|
||||
'c': ''
|
||||
})
|
||||
s = repr(m); # only stored values (no calculated)
|
||||
self.assertNotIn("'a': ", s)
|
||||
self.assertNotIn("'b': ", s)
|
||||
self.assertIn("'c': ''", s)
|
||||
|
||||
s = m._asrepr(True) # all values (including calculated)
|
||||
self.assertIn("'a': 5", s)
|
||||
self.assertIn("'b': 11", s)
|
||||
self.assertIn("'c': ''", s)
|
||||
|
||||
m['c'] = lambda self: self['xxx'] + 7; # unresolvable
|
||||
s = m._asrepr(True)
|
||||
self.assertIn("'a': 5", s)
|
||||
self.assertIn("'b': 11", s)
|
||||
self.assertIn("'c': ", s) # presents as callable
|
||||
self.assertNotIn("'c': ''", s) # but not empty
|
||||
|
||||
def testActionsIdleMode(self):
|
||||
a = Actions(DummyJail())
|
||||
a.sleeptime = 0.0001; # don't need to wait long
|
||||
# enter idle mode right now (start idle):
|
||||
a.idle = True;
|
||||
# start:
|
||||
a.start()
|
||||
# wait for enter/leave of idle mode:
|
||||
self.assertLogged("Actions: enter idle mode", wait=10)
|
||||
# leave idle mode:
|
||||
a.idle = False
|
||||
self.assertLogged("Actions: leave idle mode", wait=10)
|
||||
# stop it:
|
||||
a.active = False
|
||||
a.join()
|
||||
250
fail2ban-master/fail2ban/tests/banmanagertestcase.py
Normal file
250
fail2ban-master/fail2ban/tests/banmanagertestcase.py
Normal file
@@ -0,0 +1,250 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest
|
||||
|
||||
from .utils import setUpMyTime, tearDownMyTime
|
||||
|
||||
from ..server.banmanager import BanManager
|
||||
from ..server.ipdns import DNSUtils
|
||||
from ..server.ticket import BanTicket
|
||||
|
||||
class AddFailure(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
super(AddFailure, self).setUp()
|
||||
setUpMyTime()
|
||||
self.__ticket = BanTicket('193.168.0.128', 1167605999.0)
|
||||
self.__banManager = BanManager()
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
super(AddFailure, self).tearDown()
|
||||
tearDownMyTime()
|
||||
|
||||
def testAdd(self):
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
self.assertEqual(self.__banManager.size(), 1)
|
||||
self.assertEqual(self.__banManager.getBanTotal(), 1)
|
||||
self.__banManager.setBanTotal(0)
|
||||
self.assertEqual(self.__banManager.getBanTotal(), 0)
|
||||
|
||||
def testAddDuplicate(self):
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
self.assertFalse(self.__banManager.addBanTicket(self.__ticket))
|
||||
self.assertEqual(self.__banManager.size(), 1)
|
||||
|
||||
def testAddDuplicateWithTime(self):
|
||||
defBanTime = self.__banManager.getBanTime()
|
||||
prevEndOfBanTime = 0
|
||||
# add again a duplicate :
|
||||
# 0) with same start time and the same (default) ban time
|
||||
# 1) with newer start time and the same (default) ban time
|
||||
# 2) with same start time and longer ban time
|
||||
# 3) with permanent ban time (-1)
|
||||
for tnew, btnew in (
|
||||
(1167605999.0, None),
|
||||
(1167605999.0 + 100, None),
|
||||
(1167605999.0, 24*60*60),
|
||||
(1167605999.0, -1),
|
||||
):
|
||||
ticket1 = BanTicket('193.168.0.128', 1167605999.0)
|
||||
ticket2 = BanTicket('193.168.0.128', tnew)
|
||||
if btnew is not None:
|
||||
ticket2.setBanTime(btnew)
|
||||
self.assertTrue(self.__banManager.addBanTicket(ticket1))
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket2))
|
||||
self.assertEqual(self.__banManager.size(), 1)
|
||||
# pop ticket and check it was prolonged :
|
||||
banticket = self.__banManager.getTicketByID(ticket2.getID())
|
||||
self.assertEqual(banticket.getEndOfBanTime(defBanTime), ticket2.getEndOfBanTime(defBanTime))
|
||||
self.assertTrue(banticket.getEndOfBanTime(defBanTime) > prevEndOfBanTime)
|
||||
prevEndOfBanTime = ticket1.getEndOfBanTime(defBanTime)
|
||||
# but the start time should not be changed (+ 100 is ignored):
|
||||
self.assertEqual(banticket.getTime(), 1167605999.0)
|
||||
# if prolong to permanent, it should also have permanent ban time:
|
||||
if btnew == -1:
|
||||
self.assertEqual(banticket.getBanTime(defBanTime), -1)
|
||||
|
||||
def testInListOK(self):
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
ticket = BanTicket('193.168.0.128', 1167605999.0)
|
||||
self.assertTrue(self.__banManager._inBanList(ticket))
|
||||
|
||||
def testInListNOK(self):
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
ticket = BanTicket('111.111.1.111', 1167605999.0)
|
||||
self.assertFalse(self.__banManager._inBanList(ticket))
|
||||
|
||||
def testBanTimeIncr(self):
|
||||
ticket = BanTicket(self.__ticket.getID(), self.__ticket.getTime())
|
||||
## increase twice and at end permanent, check time/count increase:
|
||||
c = 0
|
||||
for i in (1000, 2000, -1):
|
||||
self.__banManager.addBanTicket(self.__ticket); c += 1
|
||||
ticket.setBanTime(i)
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket)); # no incr of c (already banned)
|
||||
self.assertEqual(str(self.__banManager.getTicketByID(ticket.getID())),
|
||||
"BanTicket: ip=%s time=%s bantime=%s bancount=%s #attempts=0 matches=[]" % (ticket.getID(), ticket.getTime(), i, c))
|
||||
## after permanent, it should remain permanent ban time (-1):
|
||||
self.__banManager.addBanTicket(self.__ticket); c += 1
|
||||
ticket.setBanTime(-1)
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket)); # no incr of c (already banned)
|
||||
ticket.setBanTime(1000)
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket)); # no incr of c (already banned)
|
||||
self.assertEqual(str(self.__banManager.getTicketByID(ticket.getID())),
|
||||
"BanTicket: ip=%s time=%s bantime=%s bancount=%s #attempts=0 matches=[]" % (ticket.getID(), ticket.getTime(), -1, c))
|
||||
|
||||
def testUnban(self):
|
||||
btime = self.__banManager.getBanTime()
|
||||
stime = self.__ticket.getTime()
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
self.assertTrue(self.__banManager._inBanList(self.__ticket))
|
||||
self.assertEqual(self.__banManager.unBanList(stime), [])
|
||||
self.assertEqual(self.__banManager.unBanList(stime + btime + 1), [self.__ticket])
|
||||
self.assertEqual(self.__banManager.size(), 0)
|
||||
## again, but now we will prolong ban-time and then try to unban again (1st too early):
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
# prolong ban:
|
||||
ticket = BanTicket(self.__ticket.getID(), stime + 600)
|
||||
self.assertFalse(self.__banManager.addBanTicket(ticket))
|
||||
# try unban too early:
|
||||
self.assertEqual(len(self.__banManager.unBanList(stime + btime + 1)), 0)
|
||||
# try unban using correct time:
|
||||
self.assertEqual(len(self.__banManager.unBanList(stime + btime + 600 + 1)), 1)
|
||||
## again, but now we test removing tickets particular (to test < 2/3-rule):
|
||||
for i in range(5):
|
||||
ticket = BanTicket('193.168.0.%s' % i, stime)
|
||||
ticket.setBanTime(ticket.getBanTime(btime) + i*10)
|
||||
self.assertTrue(self.__banManager.addBanTicket(ticket))
|
||||
self.assertEqual(len(self.__banManager.unBanList(stime + btime + 1*10 + 1)), 2)
|
||||
self.assertEqual(len(self.__banManager.unBanList(stime + btime + 5*10 + 1)), 3)
|
||||
self.assertEqual(self.__banManager.size(), 0)
|
||||
|
||||
def testUnbanPermanent(self):
|
||||
btime = self.__banManager.getBanTime()
|
||||
self.__banManager.setBanTime(-1)
|
||||
try:
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
self.assertTrue(self.__banManager._inBanList(self.__ticket))
|
||||
self.assertEqual(self.__banManager.unBanList(self.__ticket.getTime() + btime + 1), [])
|
||||
self.assertEqual(self.__banManager.size(), 1)
|
||||
finally:
|
||||
self.__banManager.setBanTime(btime)
|
||||
|
||||
def testBanList(self):
|
||||
tickets = [
|
||||
BanTicket('192.0.2.1', 1167605999.0),
|
||||
BanTicket('192.0.2.2', 1167605999.0),
|
||||
]
|
||||
tickets[1].setBanTime(-1)
|
||||
for t in tickets:
|
||||
self.__banManager.addBanTicket(t)
|
||||
self.assertSortedEqual(self.__banManager.getBanList(ordered=True, withTime=True),
|
||||
[
|
||||
'192.0.2.1 \t2006-12-31 23:59:59 + 600 = 2007-01-01 00:09:59',
|
||||
'192.0.2.2 \t2006-12-31 23:59:59 + -1 = 9999-12-31 23:59:59'
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class StatusExtendedCymruInfo(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
super(StatusExtendedCymruInfo, self).setUp()
|
||||
unittest.F2B.SkipIfNoNetwork()
|
||||
setUpMyTime()
|
||||
self.__ban_ip = next(iter(DNSUtils.dnsToIp("resolver1.opendns.com")))
|
||||
self.__asn = "36692"
|
||||
self.__country = "US"
|
||||
self.__rir = "arin"
|
||||
ticket = BanTicket(self.__ban_ip, 1167605999.0)
|
||||
self.__banManager = BanManager()
|
||||
self.assertTrue(self.__banManager.addBanTicket(ticket))
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
super(StatusExtendedCymruInfo, self).tearDown()
|
||||
tearDownMyTime()
|
||||
|
||||
available = True, None
|
||||
|
||||
def _getBanListExtendedCymruInfo(self):
|
||||
tc = StatusExtendedCymruInfo
|
||||
if tc.available[0]:
|
||||
cymru_info = self.__banManager.getBanListExtendedCymruInfo(
|
||||
timeout=(2 if unittest.F2B.fast else 20))
|
||||
else: # pragma: no cover - availability (once after error case only)
|
||||
cymru_info = tc.available[1]
|
||||
if cymru_info.get("error"): # pragma: no cover - availability
|
||||
tc.available = False, cymru_info
|
||||
raise unittest.SkipTest('Skip test because service is not available: %s' % cymru_info["error"])
|
||||
return cymru_info
|
||||
|
||||
|
||||
def testCymruInfo(self):
|
||||
cymru_info = self._getBanListExtendedCymruInfo()
|
||||
self.assertDictEqual(cymru_info,
|
||||
{"asn": [self.__asn],
|
||||
"country": [self.__country],
|
||||
"rir": [self.__rir]})
|
||||
|
||||
def testCymruInfoASN(self):
|
||||
self.assertEqual(
|
||||
self.__banManager.geBanListExtendedASN(self._getBanListExtendedCymruInfo()),
|
||||
[self.__asn])
|
||||
|
||||
def testCymruInfoCountry(self):
|
||||
self.assertEqual(
|
||||
self.__banManager.geBanListExtendedCountry(self._getBanListExtendedCymruInfo()),
|
||||
[self.__country])
|
||||
|
||||
def testCymruInfoRIR(self):
|
||||
self.assertEqual(
|
||||
self.__banManager.geBanListExtendedRIR(self._getBanListExtendedCymruInfo()),
|
||||
[self.__rir])
|
||||
|
||||
def testCymruInfoNxdomain(self):
|
||||
self.__banManager = BanManager()
|
||||
|
||||
# non-existing IP
|
||||
ticket = BanTicket("0.0.0.0", 1167605999.0)
|
||||
self.assertTrue(self.__banManager.addBanTicket(ticket))
|
||||
cymru_info = self._getBanListExtendedCymruInfo()
|
||||
self.assertDictEqual(cymru_info,
|
||||
{"asn": ["nxdomain"],
|
||||
"country": ["nxdomain"],
|
||||
"rir": ["nxdomain"]})
|
||||
|
||||
# Since it outputs for all active tickets we would get previous results
|
||||
# and new ones
|
||||
ticket = BanTicket("8.0.0.0", 1167606000.0)
|
||||
self.assertTrue(self.__banManager.addBanTicket(ticket))
|
||||
cymru_info = self._getBanListExtendedCymruInfo()
|
||||
self.assertSortedEqual(cymru_info,
|
||||
{"asn": ["nxdomain", "3356",],
|
||||
"country": ["nxdomain", "US"],
|
||||
"rir": ["nxdomain", "arin"]}, level=-1, key=str)
|
||||
358
fail2ban-master/fail2ban/tests/clientbeautifiertestcase.py
Normal file
358
fail2ban-master/fail2ban/tests/clientbeautifiertestcase.py
Normal file
@@ -0,0 +1,358 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
__author__ = "Alexander Koeppe"
|
||||
__copyright__ = "Copyright (c) 2016 Cyril Jaquier, 2011-2013 Yaroslav Halchenko"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest
|
||||
|
||||
from ..client.beautifier import Beautifier
|
||||
from ..version import version
|
||||
from ..server.ipdns import IPAddr, FileIPAddrSet
|
||||
from ..exceptions import UnknownJailException, DuplicateJailException
|
||||
|
||||
class BeautifierTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
""" Call before every test case """
|
||||
super(BeautifierTest, self).setUp()
|
||||
self.b = Beautifier()
|
||||
self.b.encUtf = 0; ## we prefer ascii in test suite (see #3750)
|
||||
|
||||
def tearDown(self):
|
||||
""" Call after every test case """
|
||||
super(BeautifierTest, self).tearDown()
|
||||
|
||||
def testGetInputCmd(self):
|
||||
cmd = ["test"]
|
||||
self.b.setInputCmd(cmd)
|
||||
self.assertEqual(self.b.getInputCmd(), cmd)
|
||||
|
||||
def testPing(self):
|
||||
self.b.setInputCmd(["ping"])
|
||||
self.assertEqual(self.b.beautify("pong"), "Server replied: pong")
|
||||
|
||||
def testVersion(self):
|
||||
self.b.setInputCmd(["version"])
|
||||
self.assertEqual(self.b.beautify(version), version)
|
||||
|
||||
def testAddJail(self):
|
||||
self.b.setInputCmd(["add"])
|
||||
self.assertEqual(self.b.beautify("ssh"), "Added jail ssh")
|
||||
|
||||
def testStartJail(self):
|
||||
self.b.setInputCmd(["start"])
|
||||
self.assertEqual(self.b.beautify(None), "Jail started")
|
||||
|
||||
def testStopJail(self):
|
||||
self.b.setInputCmd(["stop", "ssh"])
|
||||
self.assertEqual(self.b.beautify(None), "Jail stopped")
|
||||
|
||||
def testShutdown(self):
|
||||
self.b.setInputCmd(["stop"])
|
||||
self.assertEqual(self.b.beautify(None), "Shutdown successful")
|
||||
|
||||
def testStatus(self):
|
||||
self.b.setInputCmd(["status"])
|
||||
response = (("Number of jails", 2), ("Jail list", ", ".join(["ssh", "exim4"])))
|
||||
output = "Status\n|- Number of jails:\t2\n`- Jail list:\tssh, exim4"
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
self.b.setInputCmd(["status", "ssh"])
|
||||
response = (
|
||||
("Filter", [
|
||||
("Currently failed", 0),
|
||||
("Total failed", 0),
|
||||
("File list", "/var/log/auth.log")
|
||||
]
|
||||
),
|
||||
("Actions", [
|
||||
("Currently banned", 3),
|
||||
("Total banned", 3),
|
||||
("Banned IP list", [
|
||||
IPAddr("192.168.0.1"),
|
||||
IPAddr("::ffff:10.2.2.1"),
|
||||
IPAddr("2001:db8::1")
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
output = "Status for the jail: ssh\n"
|
||||
output += "|- Filter\n"
|
||||
output += "| |- Currently failed: 0\n"
|
||||
output += "| |- Total failed: 0\n"
|
||||
output += "| `- File list: /var/log/auth.log\n"
|
||||
output += "`- Actions\n"
|
||||
output += " |- Currently banned: 3\n"
|
||||
output += " |- Total banned: 3\n"
|
||||
output += " `- Banned IP list: 192.168.0.1 10.2.2.1 2001:db8::1"
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
self.b.setInputCmd(["status", "--all"])
|
||||
response = (("Number of jails", 2), ("Jail list", ", ".join(["ssh", "exim4"])), {
|
||||
"ssh": (
|
||||
("Filter", [
|
||||
("Currently failed", 0),
|
||||
("Total failed", 0),
|
||||
("File list", "/var/log/auth.log")
|
||||
]
|
||||
),
|
||||
("Actions", [
|
||||
("Currently banned", 3),
|
||||
("Total banned", 3),
|
||||
("Banned IP list", [
|
||||
IPAddr("192.168.0.1"),
|
||||
IPAddr("::ffff:10.2.2.1"),
|
||||
IPAddr("2001:db8::1")
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
"exim4": (
|
||||
("Filter", [
|
||||
("Currently failed", 3),
|
||||
("Total failed", 6),
|
||||
("File list", "/var/log/exim4/mainlog")
|
||||
]
|
||||
),
|
||||
("Actions", [
|
||||
("Currently banned", 0),
|
||||
("Total banned", 0),
|
||||
("Banned IP list", []
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
})
|
||||
output = (
|
||||
"Status\n"
|
||||
+ "|- Number of jails:\t2\n"
|
||||
+ "|- Jail list:\tssh, exim4\n"
|
||||
+ "`- Status for the jails:\n"
|
||||
+ " |- Jail: ssh\n"
|
||||
+ " | |- Filter\n"
|
||||
+ " | | |- Currently failed: 0\n"
|
||||
+ " | | |- Total failed: 0\n"
|
||||
+ " | | `- File list: /var/log/auth.log\n"
|
||||
+ " | `- Actions\n"
|
||||
+ " | |- Currently banned: 3\n"
|
||||
+ " | |- Total banned: 3\n"
|
||||
+ " | `- Banned IP list: 192.168.0.1 10.2.2.1 2001:db8::1\n"
|
||||
+ " `- Jail: exim4\n"
|
||||
+ " |- Filter\n"
|
||||
+ " | |- Currently failed: 3\n"
|
||||
+ " | |- Total failed: 6\n"
|
||||
+ " | `- File list: /var/log/exim4/mainlog\n"
|
||||
+ " `- Actions\n"
|
||||
+ " |- Currently banned: 0\n"
|
||||
+ " |- Total banned: 0\n"
|
||||
+ " `- Banned IP list: "
|
||||
)
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
def testStatusStats(self):
|
||||
self.b.setInputCmd(["stats"])
|
||||
## no jails:
|
||||
self.assertEqual(self.b.beautify({}), "No jails found.")
|
||||
## 3 jails:
|
||||
response = {
|
||||
"ssh": ["systemd", (3, 6), (12, 24)],
|
||||
"exim4": ["pyinotify", (6, 12), (20, 20)],
|
||||
"jail-with-long-name": ["polling", (0, 0), (0, 0)]
|
||||
}
|
||||
output = (""
|
||||
+ " | | Filter | Actions \n"
|
||||
+ " Jail | Backend |-----------x-----------\n"
|
||||
+ " | | cur | tot | cur | tot\n"
|
||||
+ "---------------------x-----------x-----------x-----------\n"
|
||||
+ " ssh | systemd | 3 | 6 | 12 | 24\n"
|
||||
+ " exim4 | pyinotify | 6 | 12 | 20 | 20\n"
|
||||
+ " jail-with-long-name | polling | 0 | 0 | 0 | 0\n"
|
||||
+ "---------------------------------------------------------"
|
||||
)
|
||||
response = self.b.beautify(response)
|
||||
self.assertEqual(response, output)
|
||||
|
||||
|
||||
def testFlushLogs(self):
|
||||
self.b.setInputCmd(["flushlogs"])
|
||||
self.assertEqual(self.b.beautify("rolled over"), "logs: rolled over")
|
||||
|
||||
def testSyslogSocket(self):
|
||||
self.b.setInputCmd(["get", "syslogsocket"])
|
||||
output = "Current syslog socket is:\n`- auto"
|
||||
self.assertEqual(self.b.beautify("auto"), output)
|
||||
|
||||
def testLogTarget(self):
|
||||
self.b.setInputCmd(["get", "logtarget"])
|
||||
output = "Current logging target is:\n`- /var/log/fail2ban.log"
|
||||
self.assertEqual(self.b.beautify("/var/log/fail2ban.log"), output)
|
||||
|
||||
def testLogLevel(self):
|
||||
self.b.setInputCmd(["get", "loglevel"])
|
||||
output = "Current logging level is 'INFO'"
|
||||
self.assertEqual(self.b.beautify("INFO"), output)
|
||||
|
||||
def testDbFile(self):
|
||||
self.b.setInputCmd(["get", "dbfile"])
|
||||
response = "/var/lib/fail2ban/fail2ban.sqlite3"
|
||||
output = "Current database file is:\n`- " + response
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
self.assertEqual(self.b.beautify(None), "Database currently disabled")
|
||||
|
||||
def testDbPurgeAge(self):
|
||||
self.b.setInputCmd(["get", "dbpurgeage"])
|
||||
output = "Current database purge age is:\n`- 86400seconds"
|
||||
self.assertEqual(self.b.beautify(86400), output)
|
||||
self.assertEqual(self.b.beautify(None), "Database currently disabled")
|
||||
|
||||
def testLogPath(self):
|
||||
self.b.setInputCmd(["get", "sshd", "logpath"])
|
||||
response = []
|
||||
output = "No file is currently monitored"
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
response = ["/var/log/auth.log"]
|
||||
output = "Current monitored log file(s):\n`- /var/log/auth.log"
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
self.b.setInputCmd(["set", "sshd", "addlogpath", "/var/log/messages"])
|
||||
response = ["/var/log/messages", "/var/log/auth.log"]
|
||||
outputadd = "Current monitored log file(s):\n"
|
||||
outputadd += "|- /var/log/messages\n`- /var/log/auth.log"
|
||||
self.assertEqual(self.b.beautify(response), outputadd)
|
||||
|
||||
self.b.setInputCmd(["set", "sshd", "dellogpath", "/var/log/messages"])
|
||||
response = ["/var/log/auth.log"]
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
def testLogEncoding(self):
|
||||
self.b.setInputCmd(["get", "sshd", "logencoding"])
|
||||
output = "Current log encoding is set to:\nUTF-8"
|
||||
self.assertEqual(self.b.beautify("UTF-8"), output)
|
||||
|
||||
def testJournalMatch(self):
|
||||
self.b.setInputCmd(["get", "sshd", "journalmatch"])
|
||||
self.assertEqual(self.b.beautify([]), "No journal match filter set")
|
||||
|
||||
self.b.setInputCmd(["set", "sshd", "addjournalmatch"])
|
||||
response = [["_SYSTEMD_UNIT", "sshd.service"]]
|
||||
output = "Current match filter:\n"
|
||||
output += "_SYSTEMD_UNIT sshd.service"
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
response.append(["_COMM", "sshd"])
|
||||
output += " + _COMM sshd"
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
self.b.setInputCmd(["set", "sshd", "deljournalmatch"])
|
||||
response.remove(response[1])
|
||||
self.assertEqual(self.b.beautify(response), output.split(" + ")[0])
|
||||
|
||||
def testDatePattern(self):
|
||||
self.b.setInputCmd(["get", "sshd", "datepattern"])
|
||||
output = "Current date pattern set to: "
|
||||
response = (None, "Default Detectors")
|
||||
self.assertEqual(self.b.beautify(None),
|
||||
output + "Not set/required")
|
||||
self.assertEqual(self.b.beautify(response),
|
||||
output + "Default Detectors")
|
||||
self.assertEqual(self.b.beautify(("test", "test")),
|
||||
output + "test (test)")
|
||||
|
||||
def testIgnoreIP(self):
|
||||
self.b.setInputCmd(["get", "sshd", "ignoreip"])
|
||||
output = "No IP address/network is ignored"
|
||||
self.assertEqual(self.b.beautify([]), output)
|
||||
|
||||
self.b.setInputCmd(["set", "sshd", "addignoreip"])
|
||||
response = [
|
||||
IPAddr("127.0.0.0", 8),
|
||||
IPAddr("::1"),
|
||||
IPAddr("2001:db8::", 32),
|
||||
IPAddr("::ffff:10.0.2.1")
|
||||
]
|
||||
output = "These IP addresses/networks are ignored:\n"
|
||||
output += "|- 127.0.0.0/8\n"
|
||||
output += "|- ::1\n"
|
||||
output += "|- 2001:db8::/32\n"
|
||||
output += "`- 10.0.2.1"
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
def testIgnoreIPFile(self):
|
||||
self.b.setInputCmd(["set", "sshd", "addignoreip"])
|
||||
response = [FileIPAddrSet("/test/file-ipaddr-set")]
|
||||
output = ("These IP addresses/networks are ignored:\n"
|
||||
"`- file://test/file-ipaddr-set")
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
def testFailRegex(self):
|
||||
self.b.setInputCmd(["get", "sshd", "failregex"])
|
||||
output = "No regular expression is defined"
|
||||
self.assertEqual(self.b.beautify([]), output)
|
||||
|
||||
output = "The following regular expression are defined:\n"
|
||||
output += "|- [0]: ^$\n`- [1]: .*"
|
||||
self.assertEqual(self.b.beautify(["^$", ".*"]), output)
|
||||
|
||||
def testActions(self):
|
||||
self.b.setInputCmd(["get", "sshd", "actions"])
|
||||
output = "No actions for jail sshd"
|
||||
self.assertEqual(self.b.beautify([]), output)
|
||||
|
||||
output = "The jail sshd has the following actions:\n"
|
||||
output += "iptables-multiport"
|
||||
self.assertEqual(self.b.beautify(["iptables-multiport"]), output)
|
||||
|
||||
def testActionProperties(self):
|
||||
self.b.setInputCmd(["get", "sshd", "actionproperties", "iptables"])
|
||||
output = "No properties for jail sshd action iptables"
|
||||
self.assertEqual(self.b.beautify([]), output)
|
||||
|
||||
output = "The jail sshd action iptables has the following properties:"
|
||||
output += "\nactionban, actionunban"
|
||||
response = ("actionban", "actionunban")
|
||||
self.assertEqual(self.b.beautify(response), output)
|
||||
|
||||
def testActionMethods(self):
|
||||
self.b.setInputCmd(["get", "sshd", "actionmethods", "iptables"])
|
||||
output = "No methods for jail sshd action iptables"
|
||||
self.assertEqual(self.b.beautify([]), output)
|
||||
|
||||
output = "The jail sshd action iptables has the following methods:\n"
|
||||
output += "ban, unban"
|
||||
self.assertEqual(self.b.beautify(["ban", "unban"]), output)
|
||||
|
||||
# def testException(self):
|
||||
# self.b.setInputCmd(["get", "sshd", "logpath"])
|
||||
# self.assertRaises(self.b.beautify(1), TypeError)
|
||||
|
||||
def testBeautifyError(self):
|
||||
response = UnknownJailException("sshd")
|
||||
output = "Sorry but the jail 'sshd' does not exist"
|
||||
self.assertEqual(self.b.beautifyError(response), output)
|
||||
|
||||
response = DuplicateJailException("sshd")
|
||||
output = "The jail 'sshd' already exists"
|
||||
self.assertEqual(self.b.beautifyError(response), output)
|
||||
|
||||
output = "Sorry but the command is invalid"
|
||||
self.assertEqual(self.b.beautifyError(IndexError()), output)
|
||||
1074
fail2ban-master/fail2ban/tests/clientreadertestcase.py
Normal file
1074
fail2ban-master/fail2ban/tests/clientreadertestcase.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
|
||||
[Definition]
|
||||
|
||||
actionban = echo "name: <actname>, ban: <ip>, logs: %(logpath)s"
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
[Definition]
|
||||
|
||||
actionban = hit with big stick <ip>
|
||||
5
fail2ban-master/fail2ban/tests/config/fail2ban.conf
Normal file
5
fail2ban-master/fail2ban/tests/config/fail2ban.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
[Definition]
|
||||
|
||||
# 3 = INFO
|
||||
loglevel = 3
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes (logtype is set in default section)
|
||||
before = ../../../../config/filter.d/common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = test
|
||||
|
||||
failregex = ^<lt_<logtype>/__prefix_line> failure from <HOST>$
|
||||
ignoreregex =
|
||||
|
||||
# following sections define prefix line considering logtype:
|
||||
|
||||
# backend-related (retrieved from backend, overwrite default):
|
||||
[lt_file]
|
||||
__prefix_line = FILE
|
||||
|
||||
[lt_journal]
|
||||
__prefix_line = JRNL
|
||||
|
||||
# specified in definition section of filter (see filter checklogtype_test.conf):
|
||||
[lt_test]
|
||||
__prefix_line = TEST
|
||||
|
||||
# specified in init parameter of jail (see ../jail.conf, jail checklogtype_init):
|
||||
[lt_init]
|
||||
__prefix_line = INIT
|
||||
@@ -0,0 +1,12 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes (logtype is set in default section)
|
||||
before = checklogtype.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
# overwrite logtype in definition (no backend anymore):
|
||||
logtype = test
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = <IP>
|
||||
13
fail2ban-master/fail2ban/tests/config/filter.d/test.conf
Normal file
13
fail2ban-master/fail2ban/tests/config/filter.d/test.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
#[INCLUDES]
|
||||
#before = common.conf
|
||||
|
||||
[DEFAULT]
|
||||
_daemon = default
|
||||
|
||||
[Definition]
|
||||
where = conf
|
||||
failregex = failure <_daemon> <one> (filter.d/test.%(where)s) <HOST>
|
||||
|
||||
[Init]
|
||||
# test parameter, should be overridden in jail by "filter=test[one=1,...]"
|
||||
one = *1*
|
||||
16
fail2ban-master/fail2ban/tests/config/filter.d/test.local
Normal file
16
fail2ban-master/fail2ban/tests/config/filter.d/test.local
Normal file
@@ -0,0 +1,16 @@
|
||||
#[INCLUDES]
|
||||
#before = common.conf
|
||||
|
||||
[Definition]
|
||||
# overwrite default daemon, additionally it should be accessible in jail with "%(known/_daemon)s":
|
||||
_daemon = test
|
||||
# interpolate previous regex (from test.conf) + new 2nd + dynamical substitution) of "two" an "where":
|
||||
failregex = %(known/failregex)s
|
||||
failure %(_daemon)s <two> (filter.d/test.<where>) <HOST>
|
||||
# parameter "two" should be specified in jail by "filter=test[..., two=2]"
|
||||
|
||||
[Init]
|
||||
# this parameter can be used in jail with "%(known/three)s":
|
||||
three = 3
|
||||
# this parameter "where" does not overwrite "where" in definition of test.conf (dynamical values only):
|
||||
where = local
|
||||
@@ -0,0 +1,27 @@
|
||||
# Fail2Ban generic example resp. test filter
|
||||
#
|
||||
# Author: Serg G. Brester (sebres)
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes. If any customizations available -- read them from
|
||||
# common.local. common.conf is a symlink to the original common.conf and
|
||||
# should be copied (dereferenced) during installation
|
||||
before = ../../../../config/filter.d/common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = test-demo
|
||||
|
||||
failregex = ^%(__prefix_line)sF2B: failure from <HOST>$
|
||||
^%(__prefix_line)sF2B: error from <HOST>$
|
||||
|
||||
# just to test multiple ignoreregex:
|
||||
ignoreregex = ^%(__prefix_line)sF2B: error from 192.0.2.251$
|
||||
^%(__prefix_line)sF2B: error from 192.0.2.252$
|
||||
|
||||
# specify only exact date patterns, +1 with %%Y to test usage of last known date by wrong dates like 0000-00-00...
|
||||
datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
|
||||
{^LN-BEG}(?:%%a )?%%b %%d %%H:%%M:%%S(?:\.%%f)?(?: %%ExY)?
|
||||
{^LN-BEG}%%Y(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
|
||||
@@ -0,0 +1,102 @@
|
||||
# Fail2Ban obsolete multiline example resp. test filter (previously sshd.conf)
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes. If any customizations available -- read them from
|
||||
# common.local
|
||||
before = ../../../../config/filter.d/common.conf
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
_daemon = sshd(?:-session)?
|
||||
|
||||
# optional prefix (logged from several ssh versions) like "error: ", "error: PAM: " or "fatal: "
|
||||
__pref = (?:(?:error|fatal): (?:PAM: )?)?
|
||||
# optional suffix (logged from several ssh versions) like " [preauth]"
|
||||
__suff = (?: (?:port \d+|on \S+|\[preauth\])){0,3}\s*
|
||||
__on_port_opt = (?: (?:port \d+|on \S+)){0,2}
|
||||
# close by authenticating user:
|
||||
__authng_user = (?: authenticating user <F-USER>\S+|.+?</F-USER>)?
|
||||
|
||||
# single line prefix:
|
||||
__prefix_line_sl = %(__prefix_line)s%(__pref)s
|
||||
# multi line prefixes (for first and second lines):
|
||||
__prefix_line_ml1 = (?P<__prefix>%(__prefix_line)s)%(__pref)s
|
||||
__prefix_line_ml2 = %(__suff)s$<SKIPLINES>^(?P=__prefix)%(__pref)s
|
||||
|
||||
# for all possible (also future) forms of "no matching (cipher|mac|MAC|compression method|key exchange method|host key type) found",
|
||||
# see ssherr.c for all possible SSH_ERR_..._ALG_MATCH errors.
|
||||
__alg_match = (?:(?:\w+ (?!found\b)){0,2}\w+)
|
||||
|
||||
# PAM authentication mechanism, can be overridden, e. g. `filter = sshd[__pam_auth='pam_ldap']`:
|
||||
__pam_auth = pam_[a-z]+
|
||||
|
||||
[Definition]
|
||||
|
||||
cmnfailre = ^%(__prefix_line_sl)s[aA]uthentication (?:failure|error|failed) for .* from <HOST>( via \S+)?\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser not known to the underlying authentication module for .* from <HOST>\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sFailed \S+ for invalid user <F-USER>(?P<cond_user>\S+)|(?:(?! from ).)*?</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
|
||||
^%(__prefix_line_sl)sFailed (?:<F-NOFAIL>publickey</F-NOFAIL>|\S+) for (?P<cond_inv>invalid user )?<F-USER>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)</F-USER> from <HOST>%(__on_port_opt)s(?: ssh\d*)?(?(cond_user): |(?:(?:(?! from ).)*)$)
|
||||
^%(__prefix_line_sl)sROOT LOGIN REFUSED FROM <HOST>
|
||||
^%(__prefix_line_sl)s[iI](?:llegal|nvalid) user .*? from <HOST>%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because not in any group\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)srefused connect from \S+ \(<HOST>\)
|
||||
^%(__prefix_line_sl)sReceived disconnect from <HOST>%(__on_port_opt)s:\s*3: .*: Auth fail%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*%(__suff)s$
|
||||
^%(__prefix_line_sl)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*%(__suff)s$
|
||||
^%(__prefix_line_ml1)s%(__pam_auth)s\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=\S*\s*rhost=<HOST>\s.*%(__suff)s$%(__prefix_line_ml2)sConnection closed
|
||||
^%(__prefix_line_sl)s(error: )?maximum authentication attempts exceeded for .* from <HOST>%(__on_port_opt)s(?: ssh\d*)? \[preauth\]$
|
||||
^%(__prefix_line_ml1)sUser .+ not allowed because account is locked%(__prefix_line_ml2)sReceived disconnect from <HOST>%(__on_port_opt)s:\s*11: .+%(__suff)s$
|
||||
^%(__prefix_line_ml1)sDisconnecting: Too many authentication failures(?: for .+?)?%(__suff)s%(__prefix_line_ml2)sConnection closed by%(__authng_user)s <HOST>%(__suff)s$
|
||||
^%(__prefix_line_ml1)sConnection from <HOST>%(__on_port_opt)s%(__prefix_line_ml2)sDisconnecting: Too many authentication failures(?: for .+?)?%(__suff)s$
|
||||
|
||||
mdre-normal =
|
||||
|
||||
mdre-ddos = ^%(__prefix_line_sl)sDid not receive identification string from <HOST>
|
||||
^%(__prefix_line_sl)sBad protocol version identification '.*' from <HOST>
|
||||
^%(__prefix_line_sl)sConnection (?:closed|reset) by%(__authng_user)s <HOST>%(__on_port_opt)s\s+\[preauth\]\s*$
|
||||
^%(__prefix_line_ml1)sSSH: Server;Ltype: (?:Authname|Version|Kex);Remote: <HOST>-\d+;[A-Z]\w+:.*%(__prefix_line_ml2)sRead from socket failed: Connection reset by peer%(__suff)s$
|
||||
|
||||
mdre-extra = ^%(__prefix_line_sl)sReceived disconnect from <HOST>%(__on_port_opt)s:\s*14: No(?: supported)? authentication methods available
|
||||
^%(__prefix_line_sl)sUnable to negotiate with <HOST>%(__on_port_opt)s: no matching <__alg_match> found.
|
||||
^%(__prefix_line_ml1)sConnection from <HOST>%(__on_port_opt)s%(__prefix_line_ml2)sUnable to negotiate a <__alg_match>
|
||||
^%(__prefix_line_ml1)sConnection from <HOST>%(__on_port_opt)s%(__prefix_line_ml2)sno matching <__alg_match> found:
|
||||
^%(__prefix_line_sl)sDisconnected(?: from)?(?: (?:invalid|authenticating)) user <F-USER>\S+</F-USER> <HOST>%(__on_port_opt)s \[preauth\]\s*$
|
||||
|
||||
mdre-aggressive = %(mdre-ddos)s
|
||||
%(mdre-extra)s
|
||||
|
||||
failregex = %(cmnfailre)s
|
||||
<mdre-<mode>>
|
||||
|
||||
# Parameter "mode": normal (default), ddos, extra or aggressive (combines all)
|
||||
# Usage example (for jail.local):
|
||||
# [sshd]
|
||||
# mode = extra
|
||||
# # or another jail (rewrite filter parameters of jail):
|
||||
# [sshd-aggressive]
|
||||
# filter = sshd[mode=aggressive]
|
||||
#
|
||||
mode = normal
|
||||
|
||||
ignoreregex =
|
||||
|
||||
# "maxlines" is number of log lines to buffer for multi-line regex searches
|
||||
maxlines = 10
|
||||
|
||||
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
|
||||
|
||||
datepattern = {^LN-BEG}
|
||||
|
||||
# DEV Notes:
|
||||
#
|
||||
# "Failed \S+ for .*? from <HOST>..." failregex uses non-greedy catch-all because
|
||||
# it is coming before use of <HOST> which is not hard-anchored at the end as well,
|
||||
# and later catch-all's could contain user-provided input, which need to be greedily
|
||||
# matched away first.
|
||||
#
|
||||
# Author: Cyril Jaquier, Yaroslav Halchenko, Petr Voralek, Daniel Black
|
||||
|
||||
107
fail2ban-master/fail2ban/tests/config/jail.conf
Normal file
107
fail2ban-master/fail2ban/tests/config/jail.conf
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
[DEFAULT]
|
||||
filter = simple
|
||||
logpath = /non/exist
|
||||
|
||||
[emptyaction]
|
||||
enabled = true
|
||||
filter =
|
||||
action =
|
||||
|
||||
[special]
|
||||
failregex = <IP>
|
||||
ignoreregex =
|
||||
ignoreip =
|
||||
|
||||
[test-known-interp]
|
||||
enabled = true
|
||||
filter = test[one=1,two=2]
|
||||
failregex = %(known/failregex)s
|
||||
failure %(known/_daemon)s %(known/three)s (jail.local) <HOST>
|
||||
|
||||
[missinglogfiles]
|
||||
enabled = true
|
||||
journalmatch = _COMM=test ;# allow to switch to systemd (by backend = `auto` and no logs found)
|
||||
logpath = /weapons/of/mass/destruction
|
||||
|
||||
[missinglogfiles_skip]
|
||||
enabled = true
|
||||
skip_if_nologs = true
|
||||
logpath = /weapons/of/mass/destruction
|
||||
|
||||
[brokenactiondef]
|
||||
enabled = true
|
||||
action = joho[foo
|
||||
|
||||
[brokenfilterdef]
|
||||
enabled = true
|
||||
filter = flt[test
|
||||
|
||||
[brokenaction]
|
||||
enabled = true
|
||||
action = brokenaction
|
||||
|
||||
[missingaction]
|
||||
enabled = true
|
||||
action = noactionfileforthisaction
|
||||
|
||||
[missingbitsjail]
|
||||
enabled = true
|
||||
filter = catchallthebadies
|
||||
action = thefunkychickendance
|
||||
|
||||
[parse_to_end_of_jail.conf]
|
||||
enabled = true
|
||||
action =
|
||||
|
||||
[tz_correct]
|
||||
enabled = true
|
||||
logtimezone = UTC+0200
|
||||
|
||||
[multi-log]
|
||||
enabled = false
|
||||
filter =
|
||||
logpath = a.log
|
||||
b.log
|
||||
c.log
|
||||
log2nd = %(logpath)s
|
||||
d.log
|
||||
action = action[actname='ban']
|
||||
action[actname='log', logpath="%(log2nd)s"]
|
||||
action[actname='test']
|
||||
|
||||
[sshd-override-flt-opts]
|
||||
filter = zzz-sshd-obsolete-multiline[logtype=short]
|
||||
backend = systemd
|
||||
prefregex = ^Test
|
||||
failregex = ^Test unused <ADDR>$
|
||||
ignoreregex = ^Test ignore <ADDR>$
|
||||
journalmatch = _COMM=test
|
||||
maxlines = 2
|
||||
usedns = no
|
||||
enabled = false
|
||||
|
||||
[checklogtype_jrnl]
|
||||
filter = checklogtype
|
||||
backend = systemd
|
||||
action = action
|
||||
enabled = false
|
||||
|
||||
[checklogtype_file]
|
||||
filter = checklogtype
|
||||
backend = polling
|
||||
logpath = README.md
|
||||
action = action
|
||||
enabled = false
|
||||
|
||||
[checklogtype_test]
|
||||
filter = checklogtype_test
|
||||
backend = systemd
|
||||
action = action
|
||||
enabled = false
|
||||
|
||||
[checklogtype_init]
|
||||
filter = checklogtype_test[logtype=init]
|
||||
backend = systemd
|
||||
action = action
|
||||
enabled = false
|
||||
632
fail2ban-master/fail2ban/tests/databasetestcase.py
Normal file
632
fail2ban-master/fail2ban/tests/databasetestcase.py
Normal file
@@ -0,0 +1,632 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Fail2Ban developers
|
||||
|
||||
__copyright__ = "Copyright (c) 2013 Steven Hiscocks"
|
||||
__license__ = "GPL"
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import tempfile
|
||||
import sqlite3
|
||||
import shutil
|
||||
|
||||
from ..server.filter import FileContainer, Filter
|
||||
from ..server.mytime import MyTime
|
||||
from ..server.ticket import FailTicket
|
||||
from ..server.actions import Actions, Utils
|
||||
from .dummyjail import DummyJail
|
||||
try:
|
||||
from ..server import database
|
||||
Fail2BanDb = database.Fail2BanDb
|
||||
except ImportError: # pragma: no cover
|
||||
Fail2BanDb = None
|
||||
from .utils import LogCaptureTestCase, logSys as DefLogSys, uni_decode
|
||||
|
||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||
|
||||
|
||||
# because of tests performance use memory instead of file:
|
||||
def getFail2BanDb(filename):
|
||||
if unittest.F2B.memory_db: # pragma: no cover
|
||||
return Fail2BanDb(':memory:')
|
||||
return Fail2BanDb(filename)
|
||||
|
||||
|
||||
class DatabaseTest(LogCaptureTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
super(DatabaseTest, self).setUp()
|
||||
if Fail2BanDb is None: # pragma: no cover
|
||||
raise unittest.SkipTest(
|
||||
"Unable to import fail2ban database module as sqlite is not "
|
||||
"available.")
|
||||
self.dbFilename = None
|
||||
if not unittest.F2B.memory_db:
|
||||
_, self.dbFilename = tempfile.mkstemp(".db", "fail2ban_")
|
||||
self._db = ':auto-create-in-memory:'
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
if isinstance(self._db, str) and self._db == ':auto-create-in-memory:':
|
||||
self._db = getFail2BanDb(self.dbFilename)
|
||||
return self._db
|
||||
@db.setter
|
||||
def db(self, value):
|
||||
if isinstance(self._db, Fail2BanDb): # pragma: no cover
|
||||
self._db.close()
|
||||
self._db = value
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
super(DatabaseTest, self).tearDown()
|
||||
if Fail2BanDb is None: # pragma: no cover
|
||||
return
|
||||
# Cleanup
|
||||
if self.dbFilename is not None:
|
||||
os.remove(self.dbFilename)
|
||||
|
||||
def testGetFilename(self):
|
||||
if self.db.filename == ':memory:': # pragma: no cover
|
||||
raise unittest.SkipTest("in :memory: database")
|
||||
self.assertEqual(self.dbFilename, self.db.filename)
|
||||
|
||||
def testPurgeAge(self):
|
||||
self.assertEqual(self.db.purgeage, 86400)
|
||||
self.db.purgeage = '1y6mon15d5h30m'
|
||||
self.assertEqual(self.db.purgeage, 48652200)
|
||||
self.db.purgeage = '2y 12mon 30d 10h 60m'
|
||||
self.assertEqual(self.db.purgeage, 48652200*2)
|
||||
|
||||
def testCreateInvalidPath(self):
|
||||
self.assertRaises(
|
||||
sqlite3.OperationalError,
|
||||
Fail2BanDb,
|
||||
"/this/path/should/not/exist")
|
||||
|
||||
def testCreateAndReconnect(self):
|
||||
if self.db.filename == ':memory:': # pragma: no cover
|
||||
raise unittest.SkipTest("in :memory: database")
|
||||
self.testAddJail()
|
||||
# Reconnect...
|
||||
self.db = Fail2BanDb(self.dbFilename)
|
||||
# and check jail of same name still present
|
||||
self.assertTrue(
|
||||
self.jail.name in self.db.getJailNames(),
|
||||
"Jail not retained in Db after disconnect reconnect.")
|
||||
|
||||
@staticmethod
|
||||
def _mockupFailedDB(): # pragma: no cover -- only sqlite >= 3.42
|
||||
"""[Mock-Up] broken connect to cover reparable restore."""
|
||||
_org_connect = sqlite3.connect;
|
||||
class _mckp_Cursor(sqlite3.Cursor):
|
||||
def execute(*args, **kwargs):
|
||||
# intended BOOM (simulate broken database):
|
||||
raise sqlite3.Error("[mock-up] broken database");
|
||||
class _mckp_Connection(sqlite3.Connection):
|
||||
def cursor(*args, **kwargs):
|
||||
return _mckp_Cursor(*args, **kwargs)
|
||||
def _mckp_connect(*args, **kwargs):
|
||||
DefLogSys.debug("[mock-up] broken connect to cover reparable restore")
|
||||
# restore original connect immediately:
|
||||
sqlite3.connect = _org_connect
|
||||
# return mockup connect (caused BOOM during first cursor execute):
|
||||
return _mckp_Connection(*args, **kwargs);
|
||||
sqlite3.connect = _mckp_connect;
|
||||
|
||||
def testRepairDb(self):
|
||||
ret = Utils.executeCmd("sqlite3 --version", output=True)
|
||||
if not ret or not ret[0]: # pragma: no cover
|
||||
raise unittest.SkipTest("no sqlite3 command")
|
||||
# version:
|
||||
ret = uni_decode(ret[1]).split(' ')
|
||||
ret = tuple(map(int, (str(ret[0]).split('.'))))if ret else (3,0,0);
|
||||
self.db = None
|
||||
if self.dbFilename is None: # pragma: no cover
|
||||
_, self.dbFilename = tempfile.mkstemp(".db", "fail2ban_")
|
||||
# test truncated database with different sizes:
|
||||
# - 14000 bytes - seems to be reparable,
|
||||
# - 4000 bytes - is totally broken.
|
||||
for truncSize in (14000, 4000):
|
||||
if truncSize >= 14000 and ret > (3,42): # pragma: no cover -- only sqlite >= 3.42
|
||||
truncSize = 14400
|
||||
self._mockupFailedDB(); # mock-up it to ensure it fails by open
|
||||
self.pruneLog("[test-repair], next phase - file-size: %d" % truncSize)
|
||||
shutil.copyfile(
|
||||
os.path.join(TEST_FILES_DIR, 'database_v1.db'), self.dbFilename)
|
||||
# produce corrupt database:
|
||||
f = os.open(self.dbFilename, os.O_RDWR)
|
||||
os.ftruncate(f, truncSize)
|
||||
os.close(f)
|
||||
# test repair:
|
||||
try:
|
||||
self.db = Fail2BanDb(self.dbFilename)
|
||||
if truncSize >= 14000: # restored:
|
||||
self.assertLogged("Repair seems to be successful",
|
||||
"Check integrity", "Database updated", all=True)
|
||||
self.assertEqual(self.db.getLogPaths(), set(['/tmp/Fail2BanDb_pUlZJh.log']))
|
||||
self.assertEqual(len(self.db.getJailNames()), 1)
|
||||
else: # recreated:
|
||||
self.assertLogged("Repair seems to be failed",
|
||||
"Check integrity", "New database created.", all=True)
|
||||
self.assertEqual(len(self.db.getLogPaths()), 0)
|
||||
self.assertEqual(len(self.db.getJailNames()), 0)
|
||||
finally:
|
||||
if self.db and self.db._dbFilename != ":memory:":
|
||||
os.remove(self.db._dbBackupFilename)
|
||||
self.db = None
|
||||
|
||||
def testUpdateDb(self):
|
||||
self.db = None
|
||||
try:
|
||||
if self.dbFilename is None: # pragma: no cover
|
||||
_, self.dbFilename = tempfile.mkstemp(".db", "fail2ban_")
|
||||
shutil.copyfile(
|
||||
os.path.join(TEST_FILES_DIR, 'database_v1.db'), self.dbFilename)
|
||||
self.db = Fail2BanDb(self.dbFilename)
|
||||
self.assertEqual(self.db.getJailNames(), set(['DummyJail #29162448 with 0 tickets']))
|
||||
self.assertEqual(self.db.getLogPaths(), set(['/tmp/Fail2BanDb_pUlZJh.log']))
|
||||
ticket = FailTicket("127.0.0.1", 1388009242.26, ["abc\n"])
|
||||
self.assertEqual(self.db.getBans()[0], ticket)
|
||||
|
||||
self.assertEqual(self.db.updateDb(Fail2BanDb.__version__), Fail2BanDb.__version__)
|
||||
self.assertRaises(NotImplementedError, self.db.updateDb, Fail2BanDb.__version__ + 1)
|
||||
# check current bans (should find exactly 1 ticket after upgrade):
|
||||
tickets = self.db.getCurrentBans(fromtime=1388009242, correctBanTime=123456)
|
||||
self.assertEqual(len(tickets), 1)
|
||||
self.assertEqual(tickets[0].getBanTime(), 123456); # ban-time was unknown (normally updated from jail)
|
||||
finally:
|
||||
if self.db and self.db._dbFilename != ":memory:":
|
||||
os.remove(self.db._dbBackupFilename)
|
||||
|
||||
def testUpdateDb2(self):
|
||||
self.db = None
|
||||
if self.dbFilename is None: # pragma: no cover
|
||||
_, self.dbFilename = tempfile.mkstemp(".db", "fail2ban_")
|
||||
shutil.copyfile(
|
||||
os.path.join(TEST_FILES_DIR, 'database_v2.db'), self.dbFilename)
|
||||
self.db = Fail2BanDb(self.dbFilename)
|
||||
self.assertEqual(self.db.getJailNames(), set(['pam-generic']))
|
||||
self.assertEqual(self.db.getLogPaths(), set(['/var/log/auth.log']))
|
||||
bans = self.db.getBans()
|
||||
self.assertEqual(len(bans), 2)
|
||||
# compare first ticket completely:
|
||||
ticket = FailTicket("1.2.3.7", 1417595494, [
|
||||
'Dec 3 09:31:08 f2btest test:auth[27658]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7',
|
||||
'Dec 3 09:31:32 f2btest test:auth[27671]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7',
|
||||
'Dec 3 09:31:34 f2btest test:auth[27673]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7'
|
||||
])
|
||||
ticket.setAttempt(3)
|
||||
self.assertEqual(bans[0], ticket)
|
||||
# second ban found also:
|
||||
self.assertEqual(bans[1].getID(), "1.2.3.8")
|
||||
# updated ?
|
||||
self.assertEqual(self.db.updateDb(Fail2BanDb.__version__), Fail2BanDb.__version__)
|
||||
# check current bans (should find 2 tickets after upgrade):
|
||||
self.jail = DummyJail(name='pam-generic')
|
||||
tickets = self.db.getCurrentBans(jail=self.jail, fromtime=1417595494)
|
||||
self.assertEqual(len(tickets), 2)
|
||||
self.assertEqual(tickets[0].getBanTime(), 600)
|
||||
# further update should fail:
|
||||
self.assertRaises(NotImplementedError, self.db.updateDb, Fail2BanDb.__version__ + 1)
|
||||
# clean:
|
||||
os.remove(self.db._dbBackupFilename)
|
||||
|
||||
def testAddJail(self):
|
||||
self.jail = DummyJail()
|
||||
self.db.addJail(self.jail)
|
||||
self.assertTrue(
|
||||
self.jail.name in self.db.getJailNames(True),
|
||||
"Jail not added to database")
|
||||
|
||||
def _testAddLog(self):
|
||||
self.testAddJail() # Jail required
|
||||
|
||||
_, filename = tempfile.mkstemp(".log", "Fail2BanDb_")
|
||||
self.fileContainer = FileContainer(filename, "utf-8")
|
||||
|
||||
pos = self.db.addLog(self.jail, self.fileContainer)
|
||||
self.assertTrue(pos is None); # unknown previously
|
||||
|
||||
self.assertIn(filename, self.db.getLogPaths(self.jail))
|
||||
os.remove(filename)
|
||||
|
||||
def testUpdateLog(self):
|
||||
self._testAddLog() # Add log file
|
||||
|
||||
# Write some text
|
||||
filename = self.fileContainer.getFileName()
|
||||
file_ = open(filename, "w")
|
||||
file_.write("Some text to write which will change md5sum\n")
|
||||
file_.close()
|
||||
self.fileContainer.open()
|
||||
self.fileContainer.readline()
|
||||
self.fileContainer.close()
|
||||
|
||||
# Capture position which should be after line just written
|
||||
lastPos = self.fileContainer.getPos()
|
||||
self.assertTrue(lastPos > 0)
|
||||
self.db.updateLog(self.jail, self.fileContainer)
|
||||
|
||||
# New FileContainer for file
|
||||
self.fileContainer = FileContainer(filename, "utf-8")
|
||||
self.assertEqual(self.fileContainer.getPos(), 0)
|
||||
|
||||
# Database should return previous position in file
|
||||
self.assertEqual(
|
||||
self.db.addLog(self.jail, self.fileContainer), lastPos)
|
||||
|
||||
# Change md5sum
|
||||
file_ = open(filename, "w") # Truncate
|
||||
file_.write("Some different text to change md5sum\n")
|
||||
file_.close()
|
||||
|
||||
self.fileContainer = FileContainer(filename, "utf-8")
|
||||
self.assertEqual(self.fileContainer.getPos(), 0)
|
||||
|
||||
# Database should be aware of md5sum change, such doesn't return
|
||||
# last position in file
|
||||
self.assertEqual(
|
||||
self.db.addLog(self.jail, self.fileContainer), None)
|
||||
os.remove(filename)
|
||||
|
||||
def testUpdateJournal(self):
|
||||
self.testAddJail() # Jail required
|
||||
# not yet updated:
|
||||
self.assertEqual(self.db.getJournalPos(self.jail, 'systemd-journal'), None)
|
||||
# update 3 times (insert and 2 updates) and check it was set (and overwritten):
|
||||
for t in (1500000000, 1500000001, 1500000002):
|
||||
self.db.updateJournal(self.jail, 'systemd-journal', t, 'TEST'+str(t))
|
||||
self.assertEqual(self.db.getJournalPos(self.jail, 'systemd-journal'), t)
|
||||
|
||||
def testAddBan(self):
|
||||
self.testAddJail()
|
||||
ticket = FailTicket("127.0.0.1", 0, ["abc\n"])
|
||||
self.db.addBan(self.jail, ticket)
|
||||
|
||||
tickets = self.db.getBans(jail=self.jail)
|
||||
self.assertEqual(len(tickets), 1)
|
||||
self.assertTrue(
|
||||
isinstance(tickets[0], FailTicket))
|
||||
|
||||
def testAddBanInvalidEncoded(self):
|
||||
self.testAddJail()
|
||||
# invalid + valid, invalid + valid unicode, invalid + valid dual converted (like in filter:readline by fallback) ...
|
||||
tickets = [
|
||||
FailTicket("127.0.0.1", 0, ['user "test"', 'user "\xd1\xe2\xe5\xf2\xe0"', 'user "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"']),
|
||||
FailTicket("127.0.0.2", 0, ['user "test"', 'user "\xd1\xe2\xe5\xf2\xe0"', 'user "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"']),
|
||||
FailTicket("127.0.0.3", 0, ['user "test"', b'user "\xd1\xe2\xe5\xf2\xe0"', b'user "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"']),
|
||||
FailTicket("127.0.0.4", 0, ['user "test"', 'user "\xd1\xe2\xe5\xf2\xe0"', 'user "\xe4\xf6\xfc\xdf"']),
|
||||
FailTicket("127.0.0.5", 0, ['user "test"', 'unterminated \xcf']),
|
||||
FailTicket("127.0.0.6", 0, ['user "test"', 'unterminated \xcf']),
|
||||
FailTicket("127.0.0.7", 0, ['user "test"', b'unterminated \xcf'])
|
||||
]
|
||||
for ticket in tickets:
|
||||
self.db.addBan(self.jail, ticket)
|
||||
|
||||
self.assertNotLogged("json dumps failed")
|
||||
|
||||
readtickets = self.db.getBans(jail=self.jail)
|
||||
|
||||
self.assertNotLogged("json loads failed")
|
||||
|
||||
## all tickets available
|
||||
self.assertEqual(len(readtickets), 7)
|
||||
|
||||
## too different to cover all possible constellations for python 2 and 3,
|
||||
## can replace/ignore some non-ascii chars by json dump/load (unicode/str),
|
||||
## so check ip and matches count only:
|
||||
for i, ticket in enumerate(tickets):
|
||||
DefLogSys.debug('readtickets[%d]: %r', i, readtickets[i].getData())
|
||||
DefLogSys.debug(' == tickets[%d]: %r', i, ticket.getData())
|
||||
self.assertEqual(readtickets[i].getID(), ticket.getID())
|
||||
self.assertEqual(len(readtickets[i].getMatches()), len(ticket.getMatches()))
|
||||
|
||||
self.pruneLog('[test-phase 2] simulate errors')
|
||||
## simulate errors in dumps/loads:
|
||||
priorEnc = database.PREFER_ENC
|
||||
try:
|
||||
database.PREFER_ENC = 'f2b-test::non-existing-encoding'
|
||||
|
||||
for ticket in tickets:
|
||||
self.db.addBan(self.jail, ticket)
|
||||
|
||||
self.assertLogged("json dumps failed")
|
||||
|
||||
readtickets = self.db.getBans(jail=self.jail)
|
||||
|
||||
self.assertLogged("json loads failed")
|
||||
|
||||
## despite errors all tickets written and loaded (check adapter-handlers are error-safe):
|
||||
self.assertEqual(len(readtickets), 14)
|
||||
finally:
|
||||
database.PREFER_ENC = priorEnc
|
||||
|
||||
## check the database is still operable (not locked) after all the errors:
|
||||
self.pruneLog('[test-phase 3] still operable?')
|
||||
self.db.addBan(self.jail, FailTicket("127.0.0.8"))
|
||||
readtickets = self.db.getBans(jail=self.jail)
|
||||
self.assertEqual(len(readtickets), 15)
|
||||
self.assertNotLogged("json loads failed", "json dumps failed")
|
||||
|
||||
def _testAdd3Bans(self):
|
||||
self.testAddJail()
|
||||
for i in (1, 2, 3):
|
||||
ticket = FailTicket(("192.0.2.%d" % i), 0, ["test\n"])
|
||||
self.db.addBan(self.jail, ticket)
|
||||
tickets = self.db.getBans(jail=self.jail)
|
||||
self.assertEqual(len(tickets), 3)
|
||||
return tickets
|
||||
|
||||
def testDelBan(self):
|
||||
tickets = self._testAdd3Bans()
|
||||
# delete single IP:
|
||||
self.db.delBan(self.jail, tickets[0].getID())
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 2)
|
||||
# delete two IPs:
|
||||
self.db.delBan(self.jail, tickets[1].getID(), tickets[2].getID())
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 0)
|
||||
|
||||
def testFlushBans(self):
|
||||
self._testAdd3Bans()
|
||||
# flush all bans:
|
||||
self.db.delBan(self.jail)
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 0)
|
||||
|
||||
def testGetBansWithTime(self):
|
||||
self.testAddJail()
|
||||
self.db.addBan(
|
||||
self.jail, FailTicket("127.0.0.1", MyTime.time() - 60, ["abc\n"]))
|
||||
self.db.addBan(
|
||||
self.jail, FailTicket("127.0.0.1", MyTime.time() - 40, ["abc\n"]))
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=50)), 1)
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=20)), 0)
|
||||
# Negative values are for persistent bans, and such all bans should
|
||||
# be returned
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail,bantime=-1)), 2)
|
||||
|
||||
def testGetBansMerged_MaxMatches(self):
|
||||
self.testAddJail()
|
||||
maxMatches = 2
|
||||
failures = [
|
||||
{"matches": ["abc\n"], "user": set(['test'])},
|
||||
{"matches": ["123\n"], "user": set(['test'])},
|
||||
{"matches": ["ABC\n"], "user": set(['test', 'root'])},
|
||||
{"matches": ["1234\n"], "user": set(['test', 'root'])},
|
||||
]
|
||||
matches2find = [f["matches"][0] for f in failures]
|
||||
# add failures sequential:
|
||||
i = 80
|
||||
for f in failures:
|
||||
i -= 10
|
||||
ticket = FailTicket("127.0.0.1", MyTime.time() - i, data=f)
|
||||
ticket.setAttempt(1)
|
||||
self.db.addBan(self.jail, ticket)
|
||||
# should retrieve 2 matches only, but count of all attempts:
|
||||
self.db.maxMatches = maxMatches;
|
||||
ticket = self.db.getBansMerged("127.0.0.1")
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getAttempt(), len(failures))
|
||||
self.assertEqual(len(ticket.getMatches()), maxMatches)
|
||||
self.assertEqual(ticket.getMatches(), matches2find[-maxMatches:])
|
||||
# add more failures at once:
|
||||
ticket = FailTicket("127.0.0.1", MyTime.time() - 10, matches2find,
|
||||
data={"user": set(['test', 'root'])})
|
||||
ticket.setAttempt(len(failures))
|
||||
self.db.addBan(self.jail, ticket)
|
||||
# should retrieve 2 matches only, but count of all attempts:
|
||||
ticket = self.db.getBansMerged("127.0.0.1")
|
||||
self.assertEqual(ticket.getAttempt(), 2 * len(failures))
|
||||
self.assertEqual(len(ticket.getMatches()), maxMatches)
|
||||
self.assertEqual(ticket.getMatches(), matches2find[-maxMatches:])
|
||||
# also using getCurrentBans:
|
||||
ticket = self.db.getCurrentBans(self.jail, "127.0.0.1", fromtime=MyTime.time()-100)
|
||||
self.assertTrue(ticket is not None)
|
||||
self.assertEqual(ticket.getAttempt(), len(failures))
|
||||
self.assertEqual(len(ticket.getMatches()), maxMatches)
|
||||
self.assertEqual(ticket.getMatches(), matches2find[-maxMatches:])
|
||||
# maxmatches of jail < dbmaxmatches (so read 1 match and 0 matches):
|
||||
ticket = self.db.getCurrentBans(self.jail, "127.0.0.1", fromtime=MyTime.time()-100,
|
||||
maxmatches=1)
|
||||
self.assertEqual(len(ticket.getMatches()), 1)
|
||||
self.assertEqual(ticket.getMatches(), failures[3]['matches'])
|
||||
ticket = self.db.getCurrentBans(self.jail, "127.0.0.1", fromtime=MyTime.time()-100,
|
||||
maxmatches=0)
|
||||
self.assertEqual(len(ticket.getMatches()), 0)
|
||||
# dbmaxmatches = 0, should retrieve 0 matches by last ban:
|
||||
ticket.setMatches(["1","2","3"])
|
||||
self.db.maxMatches = 0;
|
||||
self.db.addBan(self.jail, ticket)
|
||||
ticket = self.db.getCurrentBans(self.jail, "127.0.0.1", fromtime=MyTime.time()-100)
|
||||
self.assertTrue(ticket is not None)
|
||||
self.assertEqual(ticket.getAttempt(), len(failures))
|
||||
self.assertEqual(len(ticket.getMatches()), 0)
|
||||
|
||||
def testGetBansMerged(self):
|
||||
self.testAddJail()
|
||||
|
||||
jail2 = DummyJail(name='DummyJail-2')
|
||||
self.db.addJail(jail2)
|
||||
|
||||
ticket = FailTicket("127.0.0.1", MyTime.time() - 40, ["abc\n"])
|
||||
ticket.setAttempt(10)
|
||||
self.db.addBan(self.jail, ticket)
|
||||
ticket = FailTicket("127.0.0.1", MyTime.time() - 30, ["123\n"])
|
||||
ticket.setAttempt(20)
|
||||
self.db.addBan(self.jail, ticket)
|
||||
ticket = FailTicket("127.0.0.2", MyTime.time() - 20, ["ABC\n"])
|
||||
ticket.setAttempt(30)
|
||||
self.db.addBan(self.jail, ticket)
|
||||
ticket = FailTicket("127.0.0.1", MyTime.time() - 10, ["ABC\n"])
|
||||
ticket.setAttempt(40)
|
||||
self.db.addBan(jail2, ticket)
|
||||
|
||||
# All for IP 127.0.0.1
|
||||
ticket = self.db.getBansMerged("127.0.0.1")
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getAttempt(), 70)
|
||||
self.assertEqual(ticket.getMatches(), ["abc\n", "123\n", "ABC\n"])
|
||||
|
||||
# All for IP 127.0.0.1 for single jail
|
||||
ticket = self.db.getBansMerged("127.0.0.1", jail=self.jail)
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
self.assertEqual(ticket.getAttempt(), 30)
|
||||
self.assertEqual(ticket.getMatches(), ["abc\n", "123\n"])
|
||||
|
||||
# Should cache result if no extra bans added
|
||||
self.assertEqual(
|
||||
id(ticket),
|
||||
id(self.db.getBansMerged("127.0.0.1", jail=self.jail)))
|
||||
|
||||
newTicket = FailTicket("127.0.0.2", MyTime.time() - 20, ["ABC\n"])
|
||||
ticket.setAttempt(40)
|
||||
# Add ticket, but not for same IP, so cache still valid
|
||||
self.db.addBan(self.jail, newTicket)
|
||||
self.assertEqual(
|
||||
id(ticket),
|
||||
id(self.db.getBansMerged("127.0.0.1", jail=self.jail)))
|
||||
|
||||
newTicket = FailTicket("127.0.0.1", MyTime.time() - 10, ["ABC\n"])
|
||||
ticket.setAttempt(40)
|
||||
self.db.addBan(self.jail, newTicket)
|
||||
# Added ticket, so cache should have been cleared
|
||||
self.assertNotEqual(
|
||||
id(ticket),
|
||||
id(self.db.getBansMerged("127.0.0.1", jail=self.jail)))
|
||||
|
||||
tickets = self.db.getBansMerged()
|
||||
self.assertEqual(len(tickets), 2)
|
||||
self.assertSortedEqual(
|
||||
list(set(ticket.getID() for ticket in tickets)),
|
||||
[ticket.getID() for ticket in tickets])
|
||||
|
||||
tickets = self.db.getBansMerged(jail=jail2)
|
||||
self.assertEqual(len(tickets), 1)
|
||||
|
||||
tickets = self.db.getBansMerged(bantime=25)
|
||||
self.assertEqual(len(tickets), 2)
|
||||
tickets = self.db.getBansMerged(bantime=15)
|
||||
self.assertEqual(len(tickets), 1)
|
||||
tickets = self.db.getBansMerged(bantime=5)
|
||||
self.assertEqual(len(tickets), 0)
|
||||
# Negative values are for persistent bans, and such all bans should
|
||||
# be returned
|
||||
tickets = self.db.getBansMerged(bantime=-1)
|
||||
self.assertEqual(len(tickets), 2)
|
||||
# getCurrentBans:
|
||||
tickets = self.db.getCurrentBans(jail=self.jail)
|
||||
self.assertEqual(len(tickets), 2)
|
||||
ticket = self.db.getCurrentBans(jail=None, ip="127.0.0.1");
|
||||
self.assertEqual(ticket.getID(), "127.0.0.1")
|
||||
|
||||
# positive case (1 ticket not yet expired):
|
||||
tickets = self.db.getCurrentBans(jail=self.jail, forbantime=15,
|
||||
fromtime=MyTime.time())
|
||||
self.assertEqual(len(tickets), 1)
|
||||
# negative case (all are expired in 1year):
|
||||
tickets = self.db.getCurrentBans(jail=self.jail, forbantime=15,
|
||||
fromtime=MyTime.time() + MyTime.str2seconds("1year"))
|
||||
self.assertEqual(len(tickets), 0)
|
||||
# persistent bantime (-1), so never expired (but no persistent tickets):
|
||||
tickets = self.db.getCurrentBans(jail=self.jail, forbantime=-1,
|
||||
fromtime=MyTime.time() + MyTime.str2seconds("1year"))
|
||||
self.assertEqual(len(tickets), 0)
|
||||
# add persistent one:
|
||||
ticket.setBanTime(-1)
|
||||
self.db.addBan(self.jail, ticket)
|
||||
# persistent bantime (-1), so never expired (but jail has other max bantime now):
|
||||
tickets = self.db.getCurrentBans(jail=self.jail, forbantime=-1,
|
||||
fromtime=MyTime.time() + MyTime.str2seconds("1year"))
|
||||
# no tickets should be found (max ban time = 600):
|
||||
self.assertEqual(len(tickets), 0)
|
||||
self.assertLogged("ignore ticket (with new max ban-time %r)" % self.jail.getMaxBanTime())
|
||||
# change jail to persistent ban and try again (1 persistent ticket):
|
||||
self.jail.actions.setBanTime(-1)
|
||||
tickets = self.db.getCurrentBans(jail=self.jail, forbantime=-1,
|
||||
fromtime=MyTime.time() + MyTime.str2seconds("1year"))
|
||||
self.assertEqual(len(tickets), 1)
|
||||
self.assertEqual(tickets[0].getBanTime(), -1); # current jail ban time.
|
||||
|
||||
def testActionWithDB(self):
|
||||
# test action together with database functionality
|
||||
self.testAddJail() # Jail required
|
||||
self.jail.database = self.db
|
||||
self.db.addJail(self.jail)
|
||||
actions = self.jail.actions
|
||||
actions.add(
|
||||
"action_checkainfo",
|
||||
os.path.join(TEST_FILES_DIR, "action.d/action_checkainfo.py"),
|
||||
{})
|
||||
actions.banManager.setBanTotal(20)
|
||||
self.jail._Jail__filter = flt = Filter(self.jail)
|
||||
flt.failManager.setFailTotal(50)
|
||||
ticket = FailTicket("1.2.3.4")
|
||||
ticket.setAttempt(5)
|
||||
ticket.setMatches(['test', 'test'])
|
||||
self.jail.putFailTicket(ticket)
|
||||
actions._Actions__checkBan()
|
||||
self.assertLogged("ban ainfo %s, %s, %s, %s" % (True, True, True, True))
|
||||
self.assertLogged("jail info %d, %d, %d, %d" % (1, 21, 0, 50))
|
||||
|
||||
def testDelAndAddJail(self):
|
||||
self.testAddJail() # Add jail
|
||||
# Delete jail (just disabled it):
|
||||
self.db.delJail(self.jail)
|
||||
jails = self.db.getJailNames()
|
||||
self.assertIn(len(jails) == 1 and self.jail.name, jails)
|
||||
jails = self.db.getJailNames(enabled=False)
|
||||
self.assertIn(len(jails) == 1 and self.jail.name, jails)
|
||||
jails = self.db.getJailNames(enabled=True)
|
||||
self.assertTrue(len(jails) == 0)
|
||||
# Add it again - should just enable it:
|
||||
self.db.addJail(self.jail)
|
||||
jails = self.db.getJailNames()
|
||||
self.assertIn(len(jails) == 1 and self.jail.name, jails)
|
||||
jails = self.db.getJailNames(enabled=True)
|
||||
self.assertIn(len(jails) == 1 and self.jail.name, jails)
|
||||
jails = self.db.getJailNames(enabled=False)
|
||||
self.assertTrue(len(jails) == 0)
|
||||
|
||||
def testPurge(self):
|
||||
self.testAddJail() # Add jail
|
||||
|
||||
self.db.purge() # Jail enabled by default so shouldn't be purged
|
||||
self.assertEqual(len(self.db.getJailNames()), 1)
|
||||
|
||||
self.db.delJail(self.jail)
|
||||
self.db.purge() # Should remove jail
|
||||
self.assertEqual(len(self.db.getJailNames()), 0)
|
||||
|
||||
self.testAddBan()
|
||||
self.db.delJail(self.jail)
|
||||
self.db.purge() # Purge should remove all bans
|
||||
self.assertEqual(len(self.db.getJailNames()), 0)
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 0)
|
||||
|
||||
# Should leave jail
|
||||
self.testAddJail()
|
||||
self.db.addBan(
|
||||
self.jail, FailTicket("127.0.0.1", MyTime.time(), ["abc\n"]))
|
||||
self.db.delJail(self.jail)
|
||||
self.db.purge() # Should leave jail as ban present
|
||||
self.assertEqual(len(self.db.getJailNames()), 1)
|
||||
self.assertEqual(len(self.db.getBans(jail=self.jail)), 1)
|
||||
593
fail2ban-master/fail2ban/tests/datedetectortestcase.py
Normal file
593
fail2ban-master/fail2ban/tests/datedetectortestcase.py
Normal file
@@ -0,0 +1,593 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from ..server.datedetector import DateDetector
|
||||
from ..server import datedetector
|
||||
from ..server.datetemplate import DatePatternRegex, DateTemplate
|
||||
from .utils import setUpMyTime, tearDownMyTime, LogCaptureTestCase
|
||||
from ..helpers import getLogger
|
||||
|
||||
logSys = getLogger("fail2ban")
|
||||
|
||||
|
||||
class DateDetectorTest(LogCaptureTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
LogCaptureTestCase.setUp(self)
|
||||
setUpMyTime()
|
||||
self.__datedetector = None
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
LogCaptureTestCase.tearDown(self)
|
||||
tearDownMyTime()
|
||||
|
||||
@property
|
||||
def datedetector(self):
|
||||
if self.__datedetector is None:
|
||||
self.__datedetector = DateDetector()
|
||||
self.__datedetector.addDefaultTemplate()
|
||||
return self.__datedetector
|
||||
|
||||
def testGetEpochTime(self):
|
||||
self.__datedetector = DateDetector()
|
||||
self.__datedetector.appendTemplate('EPOCH')
|
||||
# correct epoch time, using all variants:
|
||||
for dateUnix in (1138049999, 32535244799):
|
||||
for date in ("%s", "[%s]", "[%s.555]", "audit(%s.555:101)"):
|
||||
date = date % dateUnix
|
||||
log = date + " [sshd] error: PAM: Authentication failure"
|
||||
datelog = self.datedetector.getTime(log)
|
||||
self.assertTrue(datelog, "Parse epoch time for %s failed" % (date,))
|
||||
( datelog, matchlog ) = datelog
|
||||
self.assertEqual(int(datelog), dateUnix)
|
||||
self.assertIn(matchlog.group(1), (str(dateUnix), str(dateUnix)+'.555'))
|
||||
# wrong, no epoch time (< 10 digits, more as 11 digits, begin/end of word) :
|
||||
for dateUnix in ('123456789', '9999999999999999', '1138049999A', 'A1138049999'):
|
||||
for date in ("%s", "[%s]", "[%s.555]", "audit(%s.555:101)"):
|
||||
date = date % dateUnix
|
||||
log = date + " [sshd] error: PAM: Authentication failure"
|
||||
datelog = self.datedetector.getTime(log)
|
||||
self.assertFalse(datelog)
|
||||
|
||||
def testGetEpochMsTime(self):
|
||||
self.__datedetector = DateDetector()
|
||||
self.__datedetector.appendTemplate('LEPOCH')
|
||||
# correct short/long epoch time, using all variants:
|
||||
for fact in (1, 1000, 1000000):
|
||||
for dateUnix in (1138049999, 32535244799):
|
||||
for date in ("%s", "[%s]", "[%s]", "audit(%s:101)"):
|
||||
dateLong = dateUnix * fact
|
||||
date = date % dateLong
|
||||
log = date + " [sshd] error: PAM: Authentication failure"
|
||||
datelog = self.datedetector.getTime(log)
|
||||
self.assertTrue(datelog, "Parse epoch time for %s failed" % (date,))
|
||||
( datelog, matchlog ) = datelog
|
||||
self.assertEqual(int(datelog), dateUnix)
|
||||
self.assertEqual(matchlog.group(1), str(dateLong))
|
||||
# wrong, no epoch time (< 10 digits, more as 17 digits, begin/end of word) :
|
||||
for dateUnix in ('123456789', '999999999999999999', '1138049999A', 'A1138049999'):
|
||||
for date in ("%s", "[%s]", "[%s.555]", "audit(%s.555:101)"):
|
||||
date = date % dateUnix
|
||||
log = date + " [sshd] error: PAM: Authentication failure"
|
||||
datelog = self.datedetector.getTime(log)
|
||||
self.assertFalse(datelog)
|
||||
|
||||
def testGetEpochPattern(self):
|
||||
self.__datedetector = DateDetector()
|
||||
self.__datedetector.appendTemplate(r'(?<=\|\s){LEPOCH}(?=\s\|)')
|
||||
# correct short/long epoch time, using all variants:
|
||||
for fact in (1, 1000, 1000000):
|
||||
for dateUnix in (1138049999, 32535244799):
|
||||
dateLong = dateUnix * fact
|
||||
log = "auth-error | %s | invalid password" % dateLong
|
||||
datelog = self.datedetector.getTime(log)
|
||||
self.assertTrue(datelog, "Parse epoch time failed: %r" % (log,))
|
||||
( datelog, matchlog ) = datelog
|
||||
self.assertEqual(int(datelog), dateUnix)
|
||||
self.assertEqual(matchlog.group(1), str(dateLong))
|
||||
# wrong epoch time format (does not match pattern):
|
||||
for log in ("test%s123", "test-right | %stest", "test%s | test-left"):
|
||||
log = log % dateLong
|
||||
datelog = self.datedetector.getTime(log)
|
||||
self.assertFalse(datelog)
|
||||
|
||||
def testGetEpochPatternCut(self):
|
||||
self.__datedetector = DateDetector()
|
||||
self.__datedetector.appendTemplate(r'^type=\S+ msg=audit\(({EPOCH})')
|
||||
# correct epoch time and cut out epoch string only (captured group only, not the whole match):
|
||||
line = "type=USER_AUTH msg=audit(1106513999.000:987)"
|
||||
datelog = self.datedetector.getTime(line)
|
||||
timeMatch = datelog[1]
|
||||
self.assertEqual([int(datelog[0]), line[timeMatch.start(1):timeMatch.end(1)]], [1106513999, '1106513999.000'])
|
||||
|
||||
def testGetTime(self):
|
||||
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
|
||||
dateUnix = 1106513999.0
|
||||
# yoh: testing only up to 6 elements, since the day of the week
|
||||
# is not correctly determined atm, since year is not present
|
||||
# in the log entry. Since this doesn't effect the operation
|
||||
# of fail2ban -- we just ignore incorrect day of the week
|
||||
( datelog, matchlog ) = self.datedetector.getTime(log)
|
||||
self.assertEqual(datelog, dateUnix)
|
||||
self.assertEqual(matchlog.group(1), 'Jan 23 21:59:59')
|
||||
|
||||
def testDefaultTimeZone(self):
|
||||
# use special date-pattern (with %Exz), because %z currently does not supported
|
||||
# zone abbreviations except Z|UTC|GMT.
|
||||
dd = DateDetector()
|
||||
dd.appendTemplate('^%ExY-%Exm-%Exd %H:%M:%S(?: ?%Exz)?')
|
||||
dt = datetime.datetime
|
||||
logdt = "2017-01-23 15:00:00"
|
||||
dtUTC = dt(2017, 1, 23, 15, 0)
|
||||
for tz, log, desired in (
|
||||
# no TZ in input-string:
|
||||
('UTC+0300', logdt, dt(2017, 1, 23, 12, 0)), # so in UTC, it was noon
|
||||
('UTC', logdt, dtUTC), # UTC
|
||||
('UTC-0430', logdt, dt(2017, 1, 23, 19, 30)),
|
||||
('GMT+12', logdt, dt(2017, 1, 23, 3, 0)),
|
||||
(None, logdt, dt(2017, 1, 23, 14, 0)), # default CET in our test-framework
|
||||
# CET:
|
||||
('CET', logdt, dt(2017, 1, 23, 14, 0)),
|
||||
('+0100', logdt, dt(2017, 1, 23, 14, 0)),
|
||||
('CEST-01', logdt, dt(2017, 1, 23, 14, 0)),
|
||||
# CEST:
|
||||
('CEST', logdt, dt(2017, 1, 23, 13, 0)),
|
||||
('+0200', logdt, dt(2017, 1, 23, 13, 0)),
|
||||
('CET+01', logdt, dt(2017, 1, 23, 13, 0)),
|
||||
('CET+0100', logdt, dt(2017, 1, 23, 13, 0)),
|
||||
# check offset in minutes:
|
||||
('CET+0130', logdt, dt(2017, 1, 23, 12, 30)),
|
||||
# TZ in input-string have precedence:
|
||||
('UTC+0300', logdt+' GMT', dtUTC), # GMT wins
|
||||
('UTC', logdt+' GMT', dtUTC), # GMT wins
|
||||
('UTC-0430', logdt+' GMT', dtUTC), # GMT wins
|
||||
(None, logdt+' GMT', dtUTC), # GMT wins
|
||||
('UTC', logdt+' -1045', dt(2017, 1, 24, 1, 45)), # -1045 wins
|
||||
(None, logdt+' -10:45', dt(2017, 1, 24, 1, 45)), # -1045 wins
|
||||
('UTC', logdt+' +0945', dt(2017, 1, 23, 5, 15)), # +0945 wins
|
||||
(None, logdt+' +09:45', dt(2017, 1, 23, 5, 15)), # +0945 wins
|
||||
('UTC+0300', logdt+' Z', dtUTC), # Z wins (UTC)
|
||||
('GMT+12', logdt+' CET', dt(2017, 1, 23, 14, 0)), # CET wins
|
||||
('GMT+12', logdt+' CEST', dt(2017, 1, 23, 13, 0)), # CEST wins
|
||||
('GMT+12', logdt+' CET+0130', dt(2017, 1, 23, 12, 30)), # CET+0130 wins
|
||||
):
|
||||
logSys.debug('== test %r with TZ %r', log, tz)
|
||||
dd.default_tz=tz; datelog, _ = dd.getTime(log)
|
||||
val = dt.utcfromtimestamp(datelog)
|
||||
self.assertEqual(val, desired,
|
||||
"wrong offset %r != %r by %r with default TZ %r (%r)" % (val, desired, log, tz, dd.default_tz))
|
||||
|
||||
self.assertRaises(ValueError, setattr, dd, 'default_tz', 'WRONG-TZ')
|
||||
dd.default_tz = None
|
||||
|
||||
def testVariousTimes(self):
|
||||
"""Test detection of various common date/time formats f2b should understand
|
||||
"""
|
||||
dateUnix = 1106513999.0
|
||||
|
||||
# anchored - matching expression (pattern) is anchored
|
||||
# bound - pattern can be tested using word boundary (e.g. False if contains in front some optional part)
|
||||
# sdate - date string used in test log-line
|
||||
# rdate - if specified, the result match, which differs from sdate
|
||||
for anchored, bound, sdate, rdate in (
|
||||
(False, True, "Jan 23 21:59:59", None),
|
||||
(False, False, "Sun Jan 23 21:59:59 2005", None),
|
||||
(False, False, "Sun Jan 23 21:59:59", None),
|
||||
(False, False, "Sun Jan 23 2005 21:59:59", None),
|
||||
(False, True, "2005/01/23 21:59:59", None),
|
||||
(False, True, "2005.01.23 21:59:59", None),
|
||||
(False, True, "23/01/2005 21:59:59", None),
|
||||
(False, True, "23/01/05 21:59:59", None),
|
||||
(False, True, "23/Jan/2005:21:59:59", None),
|
||||
(False, True, "23/Jan/2005:21:59:59 +0100", None),
|
||||
(False, True, "01/23/2005:21:59:59", None),
|
||||
(False, True, "2005-01-23 21:59:59", None),
|
||||
(False, True, "2005-01-23 21:59:59,000", None), # proftpd
|
||||
(False, True, "23-Jan-2005 21:59:59", None),
|
||||
(False, True, "23-Jan-2005 21:59:59.02", None),
|
||||
(False, True, "23-Jan-2005 21:59:59 +0100", None),
|
||||
(False, True, "23-01-2005 21:59:59", None),
|
||||
(True, True, "1106513999", None), # Portsetry
|
||||
(False, True, "01-23-2005 21:59:59.252", None), # reported on f2b, causes Feb29 fix to break
|
||||
(False, False, "@4000000041f4104f00000000", None), # TAI64N
|
||||
(False, True, "2005-01-23T20:59:59.252Z", None), #ISO 8601 (UTC)
|
||||
(False, True, "2005-01-23T15:59:59-05:00", None), #ISO 8601 with TZ
|
||||
(False, True, "2005-01-23 21:59:59", None), #ISO 8601 no TZ, assume local
|
||||
(False, True, "20050123T215959", None), #Short ISO with T
|
||||
(False, True, "20050123 215959", None), #Short ISO with space
|
||||
(True, True, "<01/23/05@21:59:59>", None),
|
||||
(False, True, "050123 21:59:59", None), # MySQL
|
||||
(True, True, "Jan-23-05 21:59:59", None), # ASSP like
|
||||
(False, True, "Jan 23, 2005 9:59:59 PM", None), # Apache Tomcat
|
||||
(True, True, "1106513999", None), # Regular epoch
|
||||
(True, True, "1106513999.000", None), # Regular epoch with millisec
|
||||
(True, True, "[1106513999.000]", "1106513999.000"), # epoch squared (brackets are not in match)
|
||||
(False, True, "audit(1106513999.000:987)", "1106513999.000"), # SELinux
|
||||
(True, True, "no date line", None), # no date in string
|
||||
):
|
||||
if rdate is None and sdate != "no date line": rdate = sdate
|
||||
logSys.debug('== test %r', (anchored, bound, sdate, rdate))
|
||||
for should_match, prefix in (
|
||||
(rdate is not None, ""),
|
||||
(not anchored, "bogus-prefix "),
|
||||
(False, "word-boundary")
|
||||
):
|
||||
log = prefix + sdate + "[sshd] error: PAM: Authentication failure"
|
||||
# if not allowed boundary test:
|
||||
if not bound and prefix == "word-boundary": continue
|
||||
logSys.debug(' -- test %-5s for %r', should_match, log)
|
||||
# with getTime:
|
||||
logtime = self.datedetector.getTime(log)
|
||||
if should_match:
|
||||
self.assertNotEqual(logtime, None,
|
||||
"getTime retrieved nothing: failure for %s by prefix %r, anchored: %r, log: %s" % ( sdate, prefix, anchored, log))
|
||||
( logUnix, logMatch ) = logtime
|
||||
self.assertEqual(logUnix, dateUnix,
|
||||
"getTime comparison failure for %s: by prefix %r \"%s\" is not \"%s\"" % (sdate, prefix, logUnix, dateUnix))
|
||||
self.assertEqual(logMatch.group(1), rdate)
|
||||
else:
|
||||
self.assertEqual(logtime, None,
|
||||
"getTime should have not matched for %r by prefix %r Got: %s" % (sdate, prefix, logtime))
|
||||
# with getTime(matchTime) - this combination used in filter:
|
||||
(timeMatch, template) = matchTime = self.datedetector.matchTime(log)
|
||||
logtime = self.datedetector.getTime(log, matchTime)
|
||||
logSys.debug(' -- found - %r', template.name if timeMatch else False)
|
||||
if should_match:
|
||||
self.assertNotEqual(logtime, None,
|
||||
"getTime retrieved nothing: failure for %s by prefix %r, anchored: %r, log: %s" % ( sdate, prefix, anchored, log))
|
||||
( logUnix, logMatch ) = logtime
|
||||
self.assertEqual(logUnix, dateUnix,
|
||||
"getTime comparison failure for %s by prefix %r: \"%s\" is not \"%s\"" % (sdate, prefix, logUnix, dateUnix))
|
||||
self.assertEqual(logMatch.group(1), rdate)
|
||||
else:
|
||||
self.assertEqual(logtime, None,
|
||||
"getTime should have not matched for %r by prefix %r Got: %s" % (sdate, prefix, logtime))
|
||||
logSys.debug(' -- OK')
|
||||
|
||||
def testAllUniqueTemplateNames(self):
|
||||
self.assertRaises(ValueError, self.datedetector.appendTemplate,
|
||||
self.datedetector.templates[0])
|
||||
|
||||
def testFullYearMatch_gh130(self):
|
||||
# see https://github.com/fail2ban/fail2ban/pull/130
|
||||
# yoh: unfortunately this test is not really effective to reproduce the
|
||||
# situation but left in place to assure consistent behavior
|
||||
mu = time.mktime(datetime.datetime(2012, 10, 11, 2, 37, 17).timetuple())
|
||||
logdate = self.datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')
|
||||
self.assertNotEqual(logdate, None)
|
||||
( logTime, logMatch ) = logdate
|
||||
self.assertEqual(logTime, mu)
|
||||
self.assertEqual(logMatch.group(1), '2012/10/11 02:37:17')
|
||||
# confuse it with year being at the end
|
||||
for i in range(10):
|
||||
( logTime, logMatch ) = self.datedetector.getTime('11/10/2012 02:37:17 [error] 18434#0')
|
||||
self.assertEqual(logTime, mu)
|
||||
self.assertEqual(logMatch.group(1), '11/10/2012 02:37:17')
|
||||
# and now back to the original
|
||||
( logTime, logMatch ) = self.datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')
|
||||
self.assertEqual(logTime, mu)
|
||||
self.assertEqual(logMatch.group(1), '2012/10/11 02:37:17')
|
||||
|
||||
def testDateTemplate(self):
|
||||
t = DateTemplate()
|
||||
t.setRegex('^a{3,5}b?c*$')
|
||||
self.assertEqual(t.regex, '^(a{3,5}b?c*)$')
|
||||
self.assertRaises(Exception, t.getDate, '')
|
||||
self.assertEqual(t.matchDate('aaaac').group(1), 'aaaac')
|
||||
|
||||
## no word boundaries left and right:
|
||||
t = DatePatternRegex()
|
||||
t.pattern = '(?iu)**time:%ExY%Exm%ExdT%ExH%ExM%ExS**'
|
||||
# ** was removed from end-regex:
|
||||
self.assertFalse('**' in t.regex)
|
||||
# match date:
|
||||
dt = 'TIME:20050102T010203'
|
||||
self.assertEqual(t.matchDate('X' + dt + 'X').group(1), dt)
|
||||
self.assertEqual(t.matchDate(dt).group(1), dt)
|
||||
# wrong year (for exact %ExY):
|
||||
dt = 'TIME:50050102T010203'
|
||||
self.assertFalse(t.matchDate(dt))
|
||||
|
||||
## start boundary left and word boundary right (automatically if not **):
|
||||
t = DatePatternRegex()
|
||||
t.pattern = '{^LN-BEG}time:%ExY%Exm%ExdT%ExH%ExM%ExS'
|
||||
self.assertTrue('^' in t.regex)
|
||||
# try match date:
|
||||
dt = 'time:20050102T010203'
|
||||
self.assertFalse(t.matchDate('X' + dt))
|
||||
self.assertFalse(t.matchDate(dt + 'X'))
|
||||
self.assertEqual(t.matchDate('##' + dt + '...').group(1), dt)
|
||||
self.assertEqual(t.matchDate(dt).group(1), dt)
|
||||
# case sensitive:
|
||||
dt = 'TIME:20050102T010203'
|
||||
self.assertFalse(t.matchDate(dt))
|
||||
|
||||
## auto-switching "ignore case" and "unicode"
|
||||
t = DatePatternRegex()
|
||||
t.pattern = '^%Y %b %d'
|
||||
self.assertTrue('(?iu)' in t.regex)
|
||||
dt = '2005 jun 03'; self.assertEqual(t.matchDate(dt).group(1), dt)
|
||||
dt = '2005 Jun 03'; self.assertEqual(t.matchDate(dt).group(1), dt)
|
||||
dt = '2005 JUN 03'; self.assertEqual(t.matchDate(dt).group(1), dt)
|
||||
|
||||
def testNotAnchoredCollision(self):
|
||||
# try for patterns with and without word boundaries:
|
||||
for dp in (r'%H:%M:%S', r'{UNB}%H:%M:%S'):
|
||||
dd = DateDetector()
|
||||
dd.appendTemplate(dp)
|
||||
# boundary of timestamp changes right and left (and time is left and right in line):
|
||||
for fmt in ('%s test', '%8s test', 'test %s', 'test %8s'):
|
||||
for dt in (
|
||||
'00:01:02',
|
||||
'00:01:2',
|
||||
'00:1:2',
|
||||
'0:1:2',
|
||||
'00:1:2',
|
||||
'00:01:2',
|
||||
'00:01:02',
|
||||
'0:1:2',
|
||||
'00:01:02',
|
||||
):
|
||||
t = dd.getTime(fmt % dt)
|
||||
self.assertEqual((t[0], t[1].group()), (1123970462.0, dt))
|
||||
|
||||
def testAmbiguousInOrderedTemplates(self):
|
||||
dd = self.datedetector
|
||||
for (debit, line, cnt) in (
|
||||
# shortest distance to datetime should win:
|
||||
("030324 0:03:59", "some free text 030324 0:03:59 -- 2003-03-07 17:05:01 ...", 1),
|
||||
# some free text with datetime:
|
||||
("2003-03-07 17:05:01", "some free text 2003-03-07 17:05:01 test ...", 15),
|
||||
# distance collision detection (date from foreign input should not be found):
|
||||
("030324 0:04:00", "server mysqld[1000]: 030324 0:04:00 [Warning] Access denied ..."
|
||||
" foreign-input just some free text 2003-03-07 17:05:01 test", 10),
|
||||
# distance collision detection (first date should be found):
|
||||
("Sep 16 21:30:26", "server mysqld[1020]: Sep 16 21:30:26 server mysqld: 030916 21:30:26 [Warning] Access denied", 15),
|
||||
# just to test sorting:
|
||||
("2005-10-07 06:09:42", "server mysqld[5906]: 2005-10-07 06:09:42 5907 [Warning] Access denied", 20),
|
||||
("2005-10-08T15:26:18.237955", "server mysqld[5906]: 2005-10-08T15:26:18.237955 6 [Note] Access denied", 20),
|
||||
# date format changed again:
|
||||
("051009 10:05:30", "server mysqld[1000]: 051009 10:05:30 [Warning] Access denied ...", 50),
|
||||
):
|
||||
logSys.debug('== test: %r', (debit, line, cnt))
|
||||
for i in range(cnt):
|
||||
logSys.debug('Line: %s', line)
|
||||
match, template = dd.matchTime(line)
|
||||
self.assertTrue(match)
|
||||
self.assertEqual(match.group(1), debit)
|
||||
|
||||
def testLowLevelLogging(self):
|
||||
# test coverage for the deep (heavy) debug messages:
|
||||
try:
|
||||
self.__old_eff_level = datedetector.logLevel
|
||||
if datedetector.logLevel < logSys.getEffectiveLevel()+1:
|
||||
datedetector.logLevel = logSys.getEffectiveLevel()+1
|
||||
dd = self.datedetector
|
||||
i = 0
|
||||
for (line, cnt) in (
|
||||
("server mysqld[5906]: 2005-10-07 06:09:%02i 5907 [Warning] Access denied", 2),
|
||||
("server mysqld[5906]: 051007 06:10:%02i 5907 [Warning] Access denied", 5),
|
||||
("server mysqld[5906]: 2005-10-07 06:09:%02i 5907 [Warning] Access denied", 10),
|
||||
):
|
||||
for i in range(i, i+cnt+1):
|
||||
logSys.debug('== test: %r', (line % i, cnt))
|
||||
match, template = dd.matchTime(line % i)
|
||||
self.assertTrue(match)
|
||||
finally:
|
||||
datedetector.logLevel = self.__old_eff_level
|
||||
|
||||
def testWrongTemplate(self):
|
||||
t = DatePatternRegex('(%ExY%Exm%Exd')
|
||||
# lazy compiling used, so try match:
|
||||
self.assertRaises(Exception, t.matchDate, '(20050101')
|
||||
self.assertLogged("Compile %r failed" % t.name)
|
||||
# abstract:
|
||||
t = DateTemplate()
|
||||
self.assertRaises(Exception, t.getDate, 'no date line')
|
||||
|
||||
|
||||
iso8601 = DatePatternRegex(r"%Y-%m-%d[T ]%H:%M:%S(?:\.%f)?%z")
|
||||
|
||||
class CustomDateFormatsTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
unittest.TestCase.setUp(self)
|
||||
setUpMyTime()
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
unittest.TestCase.tearDown(self)
|
||||
tearDownMyTime()
|
||||
|
||||
def testIso8601(self):
|
||||
date = datetime.datetime.utcfromtimestamp(
|
||||
iso8601.getDate("2007-01-25T12:00:00Z")[0])
|
||||
self.assertEqual(
|
||||
date,
|
||||
datetime.datetime(2007, 1, 25, 12, 0))
|
||||
self.assertRaises(TypeError, iso8601.getDate, None)
|
||||
self.assertRaises(TypeError, iso8601.getDate, date)
|
||||
|
||||
self.assertEqual(iso8601.getDate(""), None)
|
||||
self.assertEqual(iso8601.getDate("Z"), None)
|
||||
|
||||
self.assertEqual(iso8601.getDate("2007-01-01T120:00:00Z"), None)
|
||||
self.assertEqual(iso8601.getDate("2007-13-01T12:00:00Z"), None)
|
||||
date = datetime.datetime.utcfromtimestamp(
|
||||
iso8601.getDate("2007-01-25T12:00:00+0400")[0])
|
||||
self.assertEqual(
|
||||
date,
|
||||
datetime.datetime(2007, 1, 25, 8, 0))
|
||||
date = datetime.datetime.utcfromtimestamp(
|
||||
iso8601.getDate("2007-01-25T12:00:00+04:00")[0])
|
||||
self.assertEqual(
|
||||
date,
|
||||
datetime.datetime(2007, 1, 25, 8, 0))
|
||||
date = datetime.datetime.utcfromtimestamp(
|
||||
iso8601.getDate("2007-01-25T12:00:00-0400")[0])
|
||||
self.assertEqual(
|
||||
date,
|
||||
datetime.datetime(2007, 1, 25, 16, 0))
|
||||
date = datetime.datetime.utcfromtimestamp(
|
||||
iso8601.getDate("2007-01-25T12:00:00-04")[0])
|
||||
self.assertEqual(
|
||||
date,
|
||||
datetime.datetime(2007, 1, 25, 16, 0))
|
||||
|
||||
def testAmbiguousDatePattern(self):
|
||||
defDD = DateDetector()
|
||||
defDD.addDefaultTemplate()
|
||||
for (matched, dp, line) in (
|
||||
# positive case:
|
||||
('Jan 23 21:59:59', None, 'Test failure Jan 23 21:59:59 for 192.0.2.1'),
|
||||
# ambiguous "unbound" patterns (missed):
|
||||
(False, None, 'Test failure TestJan 23 21:59:59.011 2015 for 192.0.2.1'),
|
||||
(False, None, 'Test failure Jan 23 21:59:59123456789 for 192.0.2.1'),
|
||||
# ambiguous "no optional year" patterns (matched):
|
||||
('Aug 8 11:25:50', None, 'Aug 8 11:25:50 20030f2329b8 Authentication failed from 192.0.2.1'),
|
||||
('Aug 8 11:25:50', None, '[Aug 8 11:25:50] 20030f2329b8 Authentication failed from 192.0.2.1'),
|
||||
('Aug 8 11:25:50 2014', None, 'Aug 8 11:25:50 2014 20030f2329b8 Authentication failed from 192.0.2.1'),
|
||||
# direct specified patterns:
|
||||
('20:00:00 01.02.2003', r'%H:%M:%S %d.%m.%Y$', '192.0.2.1 at 20:00:00 01.02.2003'),
|
||||
('[20:00:00 01.02.2003]', r'\[%H:%M:%S %d.%m.%Y\]', '192.0.2.1[20:00:00 01.02.2003]'),
|
||||
('[20:00:00 01.02.2003]', r'\[%H:%M:%S %d.%m.%Y\]', '[20:00:00 01.02.2003]192.0.2.1'),
|
||||
('[20:00:00 01.02.2003]', r'\[%H:%M:%S %d.%m.%Y\]$', '192.0.2.1[20:00:00 01.02.2003]'),
|
||||
('[20:00:00 01.02.2003]', r'^\[%H:%M:%S %d.%m.%Y\]', '[20:00:00 01.02.2003]192.0.2.1'),
|
||||
('[17/Jun/2011 17:00:45]', r'^\[%d/%b/%Y %H:%M:%S\]', '[17/Jun/2011 17:00:45] Attempt, IP address 192.0.2.1'),
|
||||
('[17/Jun/2011 17:00:45]', r'\[%d/%b/%Y %H:%M:%S\]', 'Attempt [17/Jun/2011 17:00:45] IP address 192.0.2.1'),
|
||||
('[17/Jun/2011 17:00:45]', r'\[%d/%b/%Y %H:%M:%S\]', 'Attempt IP address 192.0.2.1, date: [17/Jun/2011 17:00:45]'),
|
||||
# direct specified patterns (begin/end, missed):
|
||||
(False, r'%H:%M:%S %d.%m.%Y', '192.0.2.1x20:00:00 01.02.2003'),
|
||||
(False, r'%H:%M:%S %d.%m.%Y', '20:00:00 01.02.2003x192.0.2.1'),
|
||||
# direct specified unbound patterns (no begin/end boundary):
|
||||
('20:00:00 01.02.2003', r'**%H:%M:%S %d.%m.%Y**', '192.0.2.1x20:00:00 01.02.2003'),
|
||||
('20:00:00 01.02.2003', r'**%H:%M:%S %d.%m.%Y**', '20:00:00 01.02.2003x192.0.2.1'),
|
||||
# pattern enclosed with stars (in comparison to example above):
|
||||
('*20:00:00 01.02.2003*', r'\**%H:%M:%S %d.%m.%Y\**', 'test*20:00:00 01.02.2003*test'),
|
||||
# direct specified patterns (begin/end, matched):
|
||||
('20:00:00 01.02.2003', r'%H:%M:%S %d.%m.%Y', '192.0.2.1 20:00:00 01.02.2003'),
|
||||
('20:00:00 01.02.2003', r'%H:%M:%S %d.%m.%Y', '20:00:00 01.02.2003 192.0.2.1'),
|
||||
# wrong year in 1st date, so failed by convert using not precise year (filter used last known date),
|
||||
# in the 2nd and 3th tests (with precise year) it should find correct the 2nd date:
|
||||
(None, r'%Y-%Exm-%Exd %ExH:%ExM:%ExS', "0000-12-30 00:00:00 - 2003-12-30 00:00:00"),
|
||||
('2003-12-30 00:00:00', r'%ExY-%Exm-%Exd %ExH:%ExM:%ExS', "0000-12-30 00:00:00 - 2003-12-30 00:00:00"),
|
||||
('2003-12-30 00:00:00', None, "0000-12-30 00:00:00 - 2003-12-30 00:00:00"),
|
||||
# wrong date recognized short month/day (unbounded date pattern without separator between parts),
|
||||
# in the 2nd and 3th tests (with precise month and day) it should find correct the 2nd date:
|
||||
('200333 010203', r'%Y%m%d %H%M%S', "text:200333 010203 | date:20031230 010203"),
|
||||
('20031230 010203', r'%ExY%Exm%Exd %ExH%ExM%ExS', "text:200333 010203 | date:20031230 010203"),
|
||||
('20031230 010203', None, "text:200333 010203 | date:20031230 010203"),
|
||||
# Explicit bound in start of the line using {^LN-BEG} key,
|
||||
# (negative) in the 1st case without line begin boundary - wrong date may be found,
|
||||
# (positive) in the 2nd case with line begin boundary - unexpected date / log line (not found)
|
||||
# (positive) and in 3th case with line begin boundary - find the correct date
|
||||
("20030101 000000", "%ExY%Exm%Exd %ExH%ExM%ExS", "00001230 010203 - 20030101 000000"),
|
||||
(None, "{^LN-BEG}%ExY%Exm%Exd %ExH%ExM%ExS", "00001230 010203 - 20030101 000000"),
|
||||
("20031230 010203", "{^LN-BEG}%ExY%Exm%Exd %ExH%ExM%ExS", "20031230 010203 - 20030101 000000"),
|
||||
# Explicit bound in start of the line using {^LN-BEG} key,
|
||||
# up to 2 non-alphanumeric chars front, ** - no word boundary on the right
|
||||
("20031230010203", "{^LN-BEG}%ExY%Exm%Exd%ExH%ExM%ExS**", "2003123001020320030101000000"),
|
||||
("20031230010203", "{^LN-BEG}%ExY%Exm%Exd%ExH%ExM%ExS**", "#2003123001020320030101000000"),
|
||||
("20031230010203", "{^LN-BEG}%ExY%Exm%Exd%ExH%ExM%ExS**", "##2003123001020320030101000000"),
|
||||
("20031230010203", "{^LN-BEG}%ExY%Exm%Exd%ExH%ExM%ExS", "[20031230010203]20030101000000"),
|
||||
# UTC/GMT time zone offset (with %z and %Z):
|
||||
(1072746123.0 - 3600, "{^LN-BEG}%ExY-%Exm-%Exd %ExH:%ExM:%ExS(?: %z)?", "[2003-12-30 01:02:03] server ..."),
|
||||
(1072746123.0 - 3600, "{^LN-BEG}%ExY-%Exm-%Exd %ExH:%ExM:%ExS(?: %Z)?", "[2003-12-30 01:02:03] server ..."),
|
||||
(1072746123.0, "{^LN-BEG}%ExY-%Exm-%Exd %ExH:%ExM:%ExS(?: %z)?", "[2003-12-30 01:02:03 UTC] server ..."),
|
||||
(1072746123.0, "{^LN-BEG}%ExY-%Exm-%Exd %ExH:%ExM:%ExS(?: %Z)?", "[2003-12-30 01:02:03 UTC] server ..."),
|
||||
(1072746123.0, "{^LN-BEG}%ExY-%Exm-%Exd %ExH:%ExM:%ExS(?: %z)?", "[2003-12-30 01:02:03 Z] server ..."),
|
||||
(1072746123.0, "{^LN-BEG}%ExY-%Exm-%Exd %ExH:%ExM:%ExS(?: %z)?", "[2003-12-30 01:02:03 +0000] server ..."),
|
||||
(1072746123.0, "{^LN-BEG}%ExY-%Exm-%Exd %ExH:%ExM:%ExS(?: %Z)?", "[2003-12-30 01:02:03 Z] server ..."),
|
||||
):
|
||||
logSys.debug('== test: %r', (matched, dp, line))
|
||||
if dp is None:
|
||||
dd = defDD
|
||||
else:
|
||||
dd = DateDetector()
|
||||
dd.appendTemplate(dp)
|
||||
date = dd.getTime(line)
|
||||
if matched:
|
||||
self.assertTrue(date)
|
||||
if isinstance(matched, str):
|
||||
self.assertEqual(matched, date[1].group(1))
|
||||
else:
|
||||
self.assertEqual(matched, date[0])
|
||||
else:
|
||||
self.assertEqual(date, None)
|
||||
|
||||
def testVariousFormatSpecs(self):
|
||||
for (matched, dp, line) in (
|
||||
# cover %B (full-month-name) and %I (as 12 == 0):
|
||||
(1106438399.0, "^%B %Exd %I:%ExM:%ExS**", 'January 23 12:59:59'),
|
||||
# cover %U (week of year starts on sunday) and %A (weekday):
|
||||
(985208399.0, "^%y %U %A %ExH:%ExM:%ExS**", '01 11 Wednesday 21:59:59'),
|
||||
# cover %W (week of year starts on monday) and %A (weekday):
|
||||
(984603599.0, "^%y %W %A %ExH:%ExM:%ExS**", '01 11 Wednesday 21:59:59'),
|
||||
# cover %W (week of year starts on monday) and %w (weekday, 0 - sunday):
|
||||
(984949199.0, "^%y %W %w %ExH:%ExM:%ExS**", '01 11 0 21:59:59'),
|
||||
# cover %W (week of year starts on monday) and %w (weekday, 6 - saturday):
|
||||
(984862799.0, "^%y %W %w %ExH:%ExM:%ExS**", '01 11 6 21:59:59'),
|
||||
# cover time only, current date, in test cases now == 14 Aug 2005 12:00 -> back to yesterday (13 Aug):
|
||||
(1123963199.0, "^%ExH:%ExM:%ExS**", '21:59:59'),
|
||||
# cover time only, current date, in test cases now == 14 Aug 2005 12:00 -> today (14 Aug):
|
||||
(1123970401.0, "^%ExH:%ExM:%ExS**", '00:00:01'),
|
||||
# cover date with current year, in test cases now == Aug 2005 -> back to last year (Sep 2004):
|
||||
(1094068799.0, "^%m/%d %ExH:%ExM:%ExS**", '09/01 21:59:59'),
|
||||
# no time (only date) in pattern, assume local 00:00:00 for H:M:S :
|
||||
(1093989600.0, "^%Y-%m-%d**", '2004-09-01'),
|
||||
(1093996800.0, "^%Y-%m-%d%z**", '2004-09-01Z'),
|
||||
):
|
||||
logSys.debug('== test: %r', (matched, dp, line))
|
||||
dd = DateDetector()
|
||||
dd.appendTemplate(dp)
|
||||
date = dd.getTime(line)
|
||||
if matched:
|
||||
self.assertTrue(date)
|
||||
if isinstance(matched, str): # pragma: no cover
|
||||
self.assertEqual(matched, date[1].group(1))
|
||||
else:
|
||||
self.assertEqual(matched, date[0])
|
||||
else: # pragma: no cover
|
||||
self.assertEqual(date, None)
|
||||
|
||||
# def testDefaultTemplate(self):
|
||||
# self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
|
||||
# self.__datedetector.setDefaultPattern("%b %d %H:%M:%S")
|
||||
#
|
||||
# log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
|
||||
# date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
|
||||
# dateUnix = 1106513999.0
|
||||
#
|
||||
# self.assertEqual(self.__datedetector.getTime(log), date)
|
||||
# self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
|
||||
|
||||
85
fail2ban-master/fail2ban/tests/dummyjail.py
Normal file
85
fail2ban-master/fail2ban/tests/dummyjail.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Fail2Ban developers
|
||||
|
||||
__copyright__ = "Copyright (c) 2012 Yaroslav Halchenko"
|
||||
__license__ = "GPL"
|
||||
|
||||
from threading import Lock
|
||||
|
||||
from ..server.jail import Jail
|
||||
from ..server.actions import Actions
|
||||
|
||||
|
||||
class DummyActions(Actions):
|
||||
def checkBan(self):
|
||||
return self._Actions__checkBan()
|
||||
|
||||
|
||||
class DummyJail(Jail):
|
||||
"""A simple 'jail' to suck in all the tickets generated by Filter's
|
||||
"""
|
||||
def __init__(self, name='DummyJail', backend=None):
|
||||
self.lock = Lock()
|
||||
self.queue = []
|
||||
super(DummyJail, self).__init__(name=name, backend=backend)
|
||||
self.__actions = DummyActions(self)
|
||||
|
||||
def __len__(self):
|
||||
with self.lock:
|
||||
return len(self.queue)
|
||||
|
||||
def isEmpty(self):
|
||||
with self.lock:
|
||||
return not self.queue
|
||||
|
||||
def isFilled(self):
|
||||
with self.lock:
|
||||
return bool(self.queue)
|
||||
|
||||
@property
|
||||
def hasFailTickets(self):
|
||||
return bool(self.queue)
|
||||
|
||||
def putFailTicket(self, ticket):
|
||||
with self.lock:
|
||||
self.queue.append(ticket)
|
||||
|
||||
def getFailTicket(self):
|
||||
with self.lock:
|
||||
try:
|
||||
return self.queue.pop()
|
||||
except IndexError:
|
||||
return False
|
||||
|
||||
@property
|
||||
def idle(self):
|
||||
return False;
|
||||
|
||||
@idle.setter
|
||||
def idle(self, value):
|
||||
pass
|
||||
|
||||
@property
|
||||
def actions(self):
|
||||
return self.__actions;
|
||||
|
||||
def isAlive(self):
|
||||
return True;
|
||||
1724
fail2ban-master/fail2ban/tests/fail2banclienttestcase.py
Normal file
1724
fail2ban-master/fail2ban/tests/fail2banclienttestcase.py
Normal file
File diff suppressed because it is too large
Load Diff
717
fail2ban-master/fail2ban/tests/fail2banregextestcase.py
Normal file
717
fail2ban-master/fail2ban/tests/fail2banregextestcase.py
Normal file
@@ -0,0 +1,717 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Fail2Ban developers
|
||||
|
||||
__author__ = "Serg Brester"
|
||||
__copyright__ = "Copyright (c) 2015 Serg G. Brester (sebres), 2008- Fail2Ban Contributors"
|
||||
__license__ = "GPL"
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from ..client import fail2banregex
|
||||
from ..client.fail2banregex import Fail2banRegex, get_opt_parser, exec_command_line, output, str2LogLevel
|
||||
from .utils import setUpMyTime, tearDownMyTime, LogCaptureTestCase, logSys
|
||||
from .utils import CONFIG_DIR
|
||||
|
||||
|
||||
fail2banregex.logSys = logSys
|
||||
def _test_output(*args):
|
||||
logSys.notice('output: %s', args[0])
|
||||
|
||||
fail2banregex.output = _test_output
|
||||
|
||||
TEST_CONFIG_DIR = os.path.join(os.path.dirname(__file__), "config")
|
||||
TEST_FILES_DIR = os.path.join(os.path.dirname(__file__), "files")
|
||||
|
||||
DEV_NULL = None
|
||||
|
||||
def _Fail2banRegex(*args):
|
||||
parser = get_opt_parser()
|
||||
(opts, args) = parser.parse_args(list(args))
|
||||
# put down log-level if expected, because of too many debug-messages:
|
||||
if opts.log_level in ("notice", "warning"):
|
||||
logSys.setLevel(str2LogLevel(opts.log_level))
|
||||
return (opts, args, Fail2banRegex(opts))
|
||||
|
||||
def _test_exec(*args):
|
||||
(opts, args, fail2banRegex) = _Fail2banRegex(*args)
|
||||
return fail2banRegex.start(args)
|
||||
|
||||
class ExitException(Exception):
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
self.msg = 'Exit with code: %s' % code
|
||||
|
||||
def _test_exec_command_line(*args):
|
||||
def _exit(code=0):
|
||||
raise ExitException(code)
|
||||
global DEV_NULL
|
||||
_org = {'exit': sys.exit, 'stdout': sys.stdout, 'stderr': sys.stderr}
|
||||
_exit_code = 0
|
||||
sys.exit = _exit
|
||||
if not DEV_NULL: DEV_NULL = open(os.devnull, "w")
|
||||
sys.stderr = sys.stdout = DEV_NULL
|
||||
try:
|
||||
exec_command_line(list(args))
|
||||
except ExitException as e:
|
||||
_exit_code = e.code
|
||||
finally:
|
||||
sys.exit = _org['exit']
|
||||
sys.stdout = _org['stdout']
|
||||
sys.stderr = _org['stderr']
|
||||
return _exit_code
|
||||
|
||||
def _reset():
|
||||
# reset global warn-counter:
|
||||
from ..server.filter import _decode_line_warn
|
||||
_decode_line_warn.clear()
|
||||
|
||||
STR_00 = "Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 192.0.2.0"
|
||||
STR_00_NODT = "[sshd] error: PAM: Authentication failure for kevin from 192.0.2.0"
|
||||
|
||||
RE_00 = r"(?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) <HOST>"
|
||||
RE_00_ID = r"Authentication failure for <F-ID>.*?</F-ID> from <ADDR>$"
|
||||
RE_00_USER = r"Authentication failure for <F-USER>.*?</F-USER> from <ADDR>$"
|
||||
|
||||
FILENAME_01 = os.path.join(TEST_FILES_DIR, "testcase01.log")
|
||||
FILENAME_02 = os.path.join(TEST_FILES_DIR, "testcase02.log")
|
||||
FILENAME_WRONGCHAR = os.path.join(TEST_FILES_DIR, "testcase-wrong-char.log")
|
||||
|
||||
# STR_ML_SSHD -- multiline log-excerpt with two sessions:
|
||||
# 192.0.2.1 (sshd[32307]) makes 2 failed attempts using public keys (without "Disconnecting: Too many authentication"),
|
||||
# and delayed success on accepted (STR_ML_SSHD_OK) or no success by close on preauth phase (STR_ML_SSHD_FAIL)
|
||||
# 192.0.2.2 (sshd[32310]) makes 2 failed attempts using public keys (with "Disconnecting: Too many authentication"),
|
||||
# and closed on preauth phase
|
||||
STR_ML_SSHD = """Nov 28 09:16:03 srv sshd[32307]: Failed publickey for git from 192.0.2.1 port 57904 ssh2: ECDSA 0e:ff:xx:xx:xx:xx:xx:xx:xx:xx:xx:...
|
||||
Nov 28 09:16:03 srv sshd[32307]: Failed publickey for git from 192.0.2.1 port 57904 ssh2: RSA 04:bc:xx:xx:xx:xx:xx:xx:xx:xx:xx:...
|
||||
Nov 28 09:16:03 srv sshd[32307]: Postponed publickey for git from 192.0.2.1 port 57904 ssh2 [preauth]
|
||||
Nov 28 09:16:05 srv sshd[32310]: Failed publickey for git from 192.0.2.2 port 57910 ssh2: ECDSA 1e:fe:xx:xx:xx:xx:xx:xx:xx:xx:xx:...
|
||||
Nov 28 09:16:05 srv sshd[32310]: Failed publickey for git from 192.0.2.2 port 57910 ssh2: RSA 14:ba:xx:xx:xx:xx:xx:xx:xx:xx:xx:...
|
||||
Nov 28 09:16:05 srv sshd[32310]: Disconnecting: Too many authentication failures for git [preauth]
|
||||
Nov 28 09:16:05 srv sshd[32310]: Connection closed by 192.0.2.2 [preauth]"""
|
||||
STR_ML_SSHD_OK = "Nov 28 09:16:06 srv sshd[32307]: Accepted publickey for git from 192.0.2.1 port 57904 ssh2: DSA 36:48:xx:xx:xx:xx:xx:xx:xx:xx:xx:..."
|
||||
STR_ML_SSHD_FAIL = "Nov 28 09:16:06 srv sshd[32307]: Connection closed by 192.0.2.1 [preauth]"
|
||||
|
||||
|
||||
FILENAME_SSHD = os.path.join(TEST_FILES_DIR, "logs", "sshd")
|
||||
FILTER_SSHD = os.path.join(CONFIG_DIR, 'filter.d', 'sshd.conf')
|
||||
FILENAME_ZZZ_SSHD = os.path.join(TEST_FILES_DIR, 'zzz-sshd-obsolete-multiline.log')
|
||||
FILTER_ZZZ_SSHD = os.path.join(TEST_CONFIG_DIR, 'filter.d', 'zzz-sshd-obsolete-multiline.conf')
|
||||
|
||||
FILENAME_ZZZ_GEN = os.path.join(TEST_FILES_DIR, "logs", "zzz-generic-example")
|
||||
FILTER_ZZZ_GEN = os.path.join(TEST_CONFIG_DIR, 'filter.d', 'zzz-generic-example.conf')
|
||||
|
||||
|
||||
class Fail2banRegexTest(LogCaptureTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
LogCaptureTestCase.setUp(self)
|
||||
setUpMyTime()
|
||||
_reset()
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
LogCaptureTestCase.tearDown(self)
|
||||
tearDownMyTime()
|
||||
|
||||
def testWrongRE(self):
|
||||
self.assertFalse(_test_exec(
|
||||
"test", r".** from <HOST>$"
|
||||
))
|
||||
self.assertLogged("Unable to compile regular expression")
|
||||
self.assertLogged("multiple repeat", "at position 2", all=False); # details of failed compilation
|
||||
self.pruneLog()
|
||||
self.assertFalse(_test_exec(
|
||||
"test", r"^(?:(?P<type>A)|B)? (?(typo)...) from <ADDR>"
|
||||
))
|
||||
self.assertLogged("Unable to compile regular expression")
|
||||
self.assertLogged("unknown group name", "at position 23", all=False); # details of failed compilation
|
||||
|
||||
def testWrongIgnoreRE(self):
|
||||
self.assertFalse(_test_exec(
|
||||
"--datepattern", "{^LN-BEG}EPOCH",
|
||||
"test", r".*? from <HOST>$", r".**"
|
||||
))
|
||||
self.assertLogged("Unable to compile regular expression")
|
||||
self.assertLogged("multiple repeat", "at position 2", all=False); # details of failed compilation
|
||||
|
||||
def testWrongFilterOptions(self):
|
||||
self.assertFalse(_test_exec(
|
||||
"test", "flt[a='x,y,z',b=z,y,x]"
|
||||
))
|
||||
self.assertLogged("Wrong filter name or options", "wrong syntax at 14: y,x", all=True)
|
||||
|
||||
def testDirectFound(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
"--print-all-matched", "--print-no-missed",
|
||||
STR_00,
|
||||
r"Authentication failure for .*? from <HOST>$"
|
||||
))
|
||||
self.assertLogged('Lines: 1 lines, 0 ignored, 1 matched, 0 missed')
|
||||
|
||||
def testDirectNotFound(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--print-all-missed",
|
||||
STR_00,
|
||||
r"XYZ from <HOST>$"
|
||||
))
|
||||
self.assertLogged('Lines: 1 lines, 0 ignored, 0 matched, 1 missed')
|
||||
|
||||
def testDirectIgnored(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--print-all-ignored",
|
||||
STR_00,
|
||||
r"Authentication failure for .*? from <HOST>$",
|
||||
r"kevin from 192.0.2.0$"
|
||||
))
|
||||
self.assertLogged('Lines: 1 lines, 1 ignored, 0 matched, 0 missed')
|
||||
|
||||
def testDirectRE_1(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
"--print-all-matched",
|
||||
FILENAME_01, RE_00
|
||||
))
|
||||
self.assertLogged('Lines: 19 lines, 0 ignored, 16 matched, 3 missed')
|
||||
|
||||
self.assertLogged('Error decoding line');
|
||||
self.assertLogged('Continuing to process line ignoring invalid characters')
|
||||
|
||||
self.assertLogged('Dez 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128')
|
||||
self.assertLogged('Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10')
|
||||
|
||||
def testDirectRE_1raw(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
"--print-all-matched", "--raw",
|
||||
FILENAME_01, RE_00
|
||||
))
|
||||
self.assertLogged('Lines: 19 lines, 0 ignored, 19 matched, 0 missed')
|
||||
|
||||
def testDirectRE_1raw_noDns(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
"--print-all-matched", "--raw", "--usedns=no",
|
||||
FILENAME_01, RE_00
|
||||
))
|
||||
self.assertLogged('Lines: 19 lines, 0 ignored, 16 matched, 3 missed')
|
||||
# usage of <F-ID>\S+</F-ID> causes raw handling automatically:
|
||||
self.pruneLog()
|
||||
self.assertTrue(_test_exec(
|
||||
"-d", "^Epoch",
|
||||
"1490349000 test failed.dns.ch", r"^\s*test <F-ID>\S+</F-ID>"
|
||||
))
|
||||
self.assertLogged('Lines: 1 lines, 0 ignored, 1 matched, 0 missed', all=True)
|
||||
self.assertNotLogged('Unable to find a corresponding IP address')
|
||||
# no confusion to IP/CIDR
|
||||
self.pruneLog()
|
||||
self.assertTrue(_test_exec(
|
||||
"-d", "^Epoch", "-o", "id",
|
||||
"1490349000 test this/is/some/path/32", r"^\s*test <F-ID>\S+</F-ID>"
|
||||
))
|
||||
self.assertLogged('this/is/some/path/32', all=True)
|
||||
|
||||
def testDirectRE_2(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
"--print-all-matched",
|
||||
FILENAME_02, RE_00
|
||||
))
|
||||
self.assertLogged('Lines: 13 lines, 0 ignored, 5 matched, 8 missed')
|
||||
|
||||
def testVerbose(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
"--timezone", "UTC+0200",
|
||||
"--verbose", "--verbose-date", "--print-no-missed",
|
||||
FILENAME_02, RE_00
|
||||
))
|
||||
self.assertLogged('Lines: 13 lines, 0 ignored, 5 matched, 8 missed')
|
||||
|
||||
self.assertLogged('141.3.81.106 Sun Aug 14 11:53:59 2005')
|
||||
self.assertLogged('141.3.81.106 Sun Aug 14 11:54:59 2005')
|
||||
|
||||
def testVerboseFullSshd(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"-v", "--verbose-date", "--print-all-matched", "--print-all-ignored",
|
||||
"-c", CONFIG_DIR,
|
||||
FILENAME_SSHD, "sshd.conf"
|
||||
))
|
||||
# test failure line and not-failure lines both presents:
|
||||
self.assertLogged("[29116]: User root not allowed because account is locked",
|
||||
"[29116]: Received disconnect from 1.2.3.4", all=True)
|
||||
self.pruneLog()
|
||||
# show real options:
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"-vv", "-c", CONFIG_DIR,
|
||||
"Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 192.0.2.1",
|
||||
"filter.d/sshd[logtype=short]"
|
||||
))
|
||||
# tet logtype is specified and set in real options:
|
||||
self.assertLogged("Real filter options :", "'logtype': 'short'", all=True)
|
||||
self.assertNotLogged("'logtype': 'file'", "'logtype': 'journal'", all=True)
|
||||
|
||||
def testFastSshd(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"--print-all-matched",
|
||||
"-c", CONFIG_DIR,
|
||||
FILENAME_ZZZ_SSHD, "sshd.conf[mode=normal]"
|
||||
))
|
||||
# test failure line and all not-failure lines presents:
|
||||
self.assertLogged(
|
||||
"[29116]: Connection from 192.0.2.4",
|
||||
"[29116]: User root not allowed because account is locked",
|
||||
"[29116]: Received disconnect from 192.0.2.4", all=True)
|
||||
|
||||
def testLoadFromJail(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"-c", CONFIG_DIR, '-vv',
|
||||
FILENAME_ZZZ_SSHD, "sshd[logtype=short]"
|
||||
))
|
||||
# test it was jail not filter:
|
||||
self.assertLogged(
|
||||
"Use %11s jail : %s" % ('','sshd'))
|
||||
|
||||
def testMultilineSshd(self):
|
||||
# by the way test of missing lines by multiline in `for bufLine in orgLineBuffer[int(fullBuffer):]`
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"--print-all-matched", "--print-all-missed",
|
||||
"-c", os.path.dirname(FILTER_ZZZ_SSHD),
|
||||
FILENAME_ZZZ_SSHD, os.path.basename(FILTER_ZZZ_SSHD)
|
||||
))
|
||||
# test "failure" line presents (2nd part only, because multiline fewer precise):
|
||||
self.assertLogged(
|
||||
"[29116]: Received disconnect from 192.0.2.4", all=True)
|
||||
|
||||
def testFullGeneric(self):
|
||||
# by the way test of ignoreregex (specified in filter file)...
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
FILENAME_ZZZ_GEN, FILTER_ZZZ_GEN+"[mode=test]"
|
||||
))
|
||||
self.assertLogged("Ignoreregex: 2 total",
|
||||
"Lines: 23 lines, 2 ignored, 16 matched, 5 missed", all=True)
|
||||
# cover filter ignoreregex gets overwritten by command argument:
|
||||
self.pruneLog("[test-phase 2]")
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"[Jun 21 16:56:03] machine test-demo(pam_unix)[13709] F2B: error from 192.0.2.251\n"
|
||||
"[Jun 21 16:56:04] machine test-demo(pam_unix)[13709] F2B: error from 192.0.2.252\n"
|
||||
"[Jun 21 16:56:05] machine test-demo(pam_unix)[13709] F2B: error from 192.0.2.255\n",
|
||||
FILTER_ZZZ_GEN+"[mode=test]",
|
||||
"F2B: error from 192.0.2.255$"
|
||||
))
|
||||
self.assertLogged("Use ignoreregex line", "Ignoreregex: 1 total",
|
||||
"Lines: 3 lines, 1 ignored, 2 matched, 0 missed", all=True)
|
||||
|
||||
def testDirectMultilineBuf(self):
|
||||
# test it with some pre-lines also to cover correct buffer scrolling (all multi-lines printed):
|
||||
for preLines in (0, 20):
|
||||
self.pruneLog("[test-phase %s]" % preLines)
|
||||
self.assertTrue(_test_exec(
|
||||
"--usedns", "no", "-d", "^Epoch", "--print-all-matched", "--maxlines", "5",
|
||||
("1490349000 TEST-NL\n"*preLines) +
|
||||
"1490349000 FAIL\n1490349000 TEST1\n1490349001 TEST2\n1490349001 HOST 192.0.2.34",
|
||||
r"^\s*FAIL\s*$<SKIPLINES>^\s*HOST <HOST>\s*$"
|
||||
))
|
||||
self.assertLogged('Lines: %s lines, 0 ignored, 2 matched, %s missed' % (preLines+4, preLines+2))
|
||||
# both matched lines were printed:
|
||||
self.assertLogged("| 1490349000 FAIL", "| 1490349001 HOST 192.0.2.34", all=True)
|
||||
|
||||
|
||||
def testDirectMultilineBufDebuggex(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"--usedns", "no", "-d", "^Epoch", "--debuggex", "--print-all-matched", "--maxlines", "5",
|
||||
"1490349000 FAIL\n1490349000 TEST1\n1490349001 TEST2\n1490349001 HOST 192.0.2.34",
|
||||
r"^\s*FAIL\s*$<SKIPLINES>^\s*HOST <HOST>\s*$"
|
||||
))
|
||||
self.assertLogged('Lines: 4 lines, 0 ignored, 2 matched, 2 missed')
|
||||
# the sequence in args-dict is currently undefined (so can be 1st argument)
|
||||
self.assertLogged("&flags=m", "?flags=m")
|
||||
|
||||
def testSinglelineWithNLinContent(self):
|
||||
#
|
||||
self.assertTrue(_test_exec(
|
||||
"--usedns", "no", "-d", "^Epoch", "--print-all-matched",
|
||||
"-L", "2", "1490349000 FAIL: failure\nhost: 192.0.2.35",
|
||||
r"^\s*FAIL:\s*.*\nhost:\s+<HOST>$"
|
||||
))
|
||||
self.assertLogged('Lines: 2 lines, 0 ignored, 2 matched, 0 missed')
|
||||
|
||||
def testRegexEpochPatterns(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"-r", "-d", r"^\[{LEPOCH}\]\s+", "--maxlines", "5",
|
||||
"[1516469849] 192.0.2.1 FAIL: failure\n"
|
||||
"[1516469849551] 192.0.2.2 FAIL: failure\n"
|
||||
"[1516469849551000] 192.0.2.3 FAIL: failure\n"
|
||||
"[1516469849551.000] 192.0.2.4 FAIL: failure",
|
||||
r"^<HOST> FAIL\b"
|
||||
))
|
||||
self.assertLogged('Lines: 4 lines, 0 ignored, 4 matched, 0 missed')
|
||||
|
||||
def testRegexSubnet(self):
|
||||
self.assertTrue(_test_exec(
|
||||
"-vv", "-d", r"^\[{LEPOCH}\]\s+", "--maxlines", "5",
|
||||
"[1516469849] 192.0.2.1 FAIL: failure\n"
|
||||
"[1516469849] 192.0.2.1/24 FAIL: failure\n"
|
||||
"[1516469849] 2001:DB8:FF:FF::1 FAIL: failure\n"
|
||||
"[1516469849] 2001:DB8:FF:FF::1/60 FAIL: failure\n",
|
||||
r"^<SUBNET> FAIL\b"
|
||||
))
|
||||
self.assertLogged('Lines: 4 lines, 0 ignored, 4 matched, 0 missed')
|
||||
self.assertLogged('192.0.2.0/24', '2001:db8:ff:f0::/60', all=True)
|
||||
|
||||
def testFrmtOutput(self):
|
||||
# id/ip only:
|
||||
self.assertTrue(_test_exec('-o', 'id', STR_00, RE_00_ID))
|
||||
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*<F-ID/> <F-TUPLE_ID>\S+</F-TUPLE_ID>'))
|
||||
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*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID/> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
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*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID><ADDR>:<F-PORT/></F-ID> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
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*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID><ADDR>:<F-PORT/></F-ID> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
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:<fid> | IP:<ip>', '-d', '{^LN-BEG}EPOCH',
|
||||
'1591983743.667 left [192.0.2.4]:12345 right',
|
||||
r'^\s*<F-TUPLE_ID_1>\S+</F-TUPLE_ID_1> <F-ID><ADDR>:<F-PORT/></F-ID> <F-TUPLE_ID_2>\S+</F-TUPLE_ID_2>'))
|
||||
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('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('output: %s' % "['192.0.2.0'", "'ip4': '192.0.2.0'", "'user': 'kevin'", all=True)
|
||||
self.pruneLog()
|
||||
# log msg :
|
||||
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.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()
|
||||
# item of match (user):
|
||||
self.assertTrue(_test_exec('-o', 'user', STR_00, RE_00_USER))
|
||||
self.assertLogged('output: %s' % 'kevin')
|
||||
self.pruneLog()
|
||||
# complex substitution using tags (ip, user, family):
|
||||
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.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):
|
||||
opts = (
|
||||
'-c', CONFIG_DIR,
|
||||
"-d", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
)
|
||||
log = (
|
||||
'May 27 00:16:33 host sshd[2364]: User root not allowed because account is locked\n'
|
||||
'May 27 00:16:33 host sshd[2364]: Received disconnect from 192.0.2.76 port 58846:11: Bye Bye [preauth]'
|
||||
)
|
||||
_test = lambda *args: _test_exec(*(opts + args))
|
||||
# with MLFID from prefregex and IP after failure obtained from F-NOFAIL RE:
|
||||
self.assertTrue(_test('-o', 'IP:<ip>', log, 'sshd.conf'))
|
||||
self.assertLogged('IP:192.0.2.76')
|
||||
self.pruneLog()
|
||||
# test diverse ID/IP constellations:
|
||||
def _test_variants(flt="sshd.conf", prefix=""):
|
||||
# with different ID/IP from failregex (ID/User from first, IP from second message):
|
||||
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
||||
flt+'[failregex="'
|
||||
'^'+prefix+r'<F-ID>User <F-USER>\S+</F-USER></F-ID> not allowed'+'\n'
|
||||
'^'+prefix+r'Received disconnect from <ADDR>'
|
||||
'"]'))
|
||||
self.assertLogged('ID:"User root" | IP:192.0.2.76 | U:root')
|
||||
self.pruneLog()
|
||||
# with different ID/IP from failregex (User from first, ID and IP from second message):
|
||||
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
||||
flt+'[failregex="'
|
||||
'^'+prefix+r'User <F-USER>\S+</F-USER> not allowed'+'\n'
|
||||
'^'+prefix+r'Received disconnect from <F-ID><ADDR> port \d+</F-ID>'
|
||||
'"]'))
|
||||
self.assertLogged('ID:"192.0.2.76 port 58846" | IP:192.0.2.76 | U:root')
|
||||
self.pruneLog()
|
||||
# first with sshd and prefregex:
|
||||
_test_variants()
|
||||
# the same without prefregex and MLFID directly in failregex (no merge with prefregex groups):
|
||||
_test_variants('common.conf', prefix=r"\s*\S+ sshd\[<F-MLFID>\d+</F-MLFID>\]:\s+")
|
||||
|
||||
def testNoDateTime(self):
|
||||
# datepattern doesn't match:
|
||||
self.assertTrue(_test_exec('-d', '{^LN-BEG}EPOCH', '-o', 'Found-ID:<F-ID>', STR_00_NODT, RE_00_ID))
|
||||
self.assertLogged(
|
||||
"Found a match but no valid date/time found",
|
||||
"Match without a timestamp:",
|
||||
"Found-ID:kevin", all=True)
|
||||
self.pruneLog()
|
||||
# explicitly no datepattern:
|
||||
self.assertTrue(_test_exec('-d', '{NONE}', '-o', 'Found-ID:<F-ID>', STR_00_NODT, RE_00_ID))
|
||||
self.assertLogged(
|
||||
"Found-ID:kevin", all=True)
|
||||
self.assertNotLogged(
|
||||
"Found a match but no valid date/time found",
|
||||
"Match without a timestamp:", all=True)
|
||||
|
||||
def testIncompleteDateTime(self):
|
||||
# datepattern in followed lines doesn't match previously known pattern + line is too short
|
||||
# (logging break-off, no flush, etc):
|
||||
self.assertTrue(_test_exec(
|
||||
'-o', 'Found-ADDR:<ip>',
|
||||
'192.0.2.1 - - [02/May/2021:18:40:55 +0100] "GET / HTTP/1.1" 302 328 "-" "Mozilla/5.0" "-"\n'
|
||||
'192.0.2.2 - - [02/May/2021:18:40:55 +0100\n'
|
||||
'192.0.2.3 - - [02/May/2021:18:40:55',
|
||||
'^<ADDR>'))
|
||||
self.assertLogged(
|
||||
"Found-ADDR:192.0.2.1", "Found-ADDR:192.0.2.2", "Found-ADDR:192.0.2.3", all=True)
|
||||
|
||||
def testFrmtOutputWrapML(self):
|
||||
unittest.F2B.SkipIfCfgMissing(stock=True)
|
||||
# complex substitution using tags and message (ip, user, msg):
|
||||
self.assertTrue(_test_exec('-o', '<ip>, <F-USER>, <msg>',
|
||||
'-c', CONFIG_DIR, '--usedns', 'no',
|
||||
STR_ML_SSHD + "\n" + STR_ML_SSHD_OK, 'sshd.conf[logtype=short, publickey=invalid]'))
|
||||
# be sure we don't have IP in one line and have it in another:
|
||||
lines = STR_ML_SSHD.split("\n")
|
||||
self.assertTrue('192.0.2.2' not in lines[-2] and '192.0.2.2' in lines[-1])
|
||||
# but both are in output "merged" with IP and user:
|
||||
self.assertLogged(
|
||||
'192.0.2.2, git, '+lines[-2],
|
||||
'192.0.2.2, git, '+lines[-1],
|
||||
all=True)
|
||||
# nothing should be found for 192.0.2.1 (mode is not aggressive):
|
||||
self.assertNotLogged('192.0.2.1, git, ')
|
||||
|
||||
# test with publickey (nofail) - would not produce output for 192.0.2.1 because accepted:
|
||||
self.pruneLog("[test-phase 1] mode=aggressive & publickey=nofail + OK (accepted)")
|
||||
self.assertTrue(_test_exec('-o', '<ip>, <F-USER>, <msg>',
|
||||
'-c', CONFIG_DIR, '--usedns', 'no',
|
||||
STR_ML_SSHD + "\n" + STR_ML_SSHD_OK, 'sshd.conf[logtype=short, mode=aggressive]'))
|
||||
self.assertLogged(
|
||||
'192.0.2.2, git, '+lines[-4],
|
||||
'192.0.2.2, git, '+lines[-3],
|
||||
'192.0.2.2, git, '+lines[-2],
|
||||
'192.0.2.2, git, '+lines[-1],
|
||||
all=True)
|
||||
# nothing should be found for 192.0.2.1 (access gained so failures ignored):
|
||||
self.assertNotLogged('192.0.2.1, git, ')
|
||||
|
||||
# now same test but "accepted" replaced with "closed" on preauth phase:
|
||||
self.pruneLog("[test-phase 2] mode=aggressive & publickey=nofail + FAIL (closed on preauth)")
|
||||
self.assertTrue(_test_exec('-o', '<ip>, <F-USER>, <msg>',
|
||||
'-c', CONFIG_DIR, '--usedns', 'no',
|
||||
STR_ML_SSHD + "\n" + STR_ML_SSHD_FAIL, 'sshd.conf[logtype=short, mode=aggressive]'))
|
||||
# 192.0.2.1 should be found for every failure (2x failed key + 1x closed):
|
||||
lines = STR_ML_SSHD.split("\n")[0:2] + STR_ML_SSHD_FAIL.split("\n")[-1:]
|
||||
self.assertLogged(
|
||||
'192.0.2.1, git, '+lines[-3],
|
||||
'192.0.2.1, git, '+lines[-2],
|
||||
'192.0.2.1, git, '+lines[-1],
|
||||
all=True)
|
||||
|
||||
def testOutputNoPendingFailuresAfterGained(self):
|
||||
unittest.F2B.SkipIfCfgMissing(stock=True)
|
||||
# connect finished without authorization must generate a failure, because
|
||||
# connect started will produce pending failure which gets reset by gained
|
||||
# connect authorized.
|
||||
self.assertTrue(_test_exec('-o', 'failure from == <ip> ==',
|
||||
'-c', CONFIG_DIR, '-d', '{NONE}',
|
||||
'svc[1] connect started 192.0.2.3\n'
|
||||
'svc[1] connect finished 192.0.2.3\n'
|
||||
'svc[2] connect started 192.0.2.4\n'
|
||||
'svc[2] connect authorized 192.0.2.4\n'
|
||||
'svc[2] connect finished 192.0.2.4\n',
|
||||
r'common.conf[prefregex="^svc\[<F-MLFID>\d+</F-MLFID>\] connect <F-CONTENT>.+</F-CONTENT>$"'
|
||||
', failregex="'
|
||||
'^started\n'
|
||||
'^<F-NOFAIL><F-MLFFORGET>finished</F-MLFFORGET></F-NOFAIL> <ADDR>\n'
|
||||
'^<F-MLFGAINED>authorized</F-MLFGAINED> <ADDR>'
|
||||
'", maxlines=1]'
|
||||
))
|
||||
self.assertLogged('failure from == 192.0.2.3 ==')
|
||||
self.assertNotLogged('failure from == 192.0.2.4 ==')
|
||||
|
||||
def testWrongFilterFile(self):
|
||||
# use test log as filter file to cover error cases...
|
||||
self.assertFalse(_test_exec(
|
||||
FILENAME_ZZZ_GEN, FILENAME_ZZZ_GEN
|
||||
))
|
||||
|
||||
def testWrongChar(self):
|
||||
unittest.F2B.SkipIfCfgMissing(stock=True)
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
FILENAME_WRONGCHAR, FILTER_SSHD
|
||||
))
|
||||
self.assertLogged('Lines: 4 lines, 0 ignored, 2 matched, 2 missed')
|
||||
|
||||
self.assertLogged('Error decoding line')
|
||||
self.assertLogged('Continuing to process line ignoring invalid characters:')
|
||||
|
||||
self.assertLogged('Nov 8 00:16:12 main sshd[32548]: input_userauth_request: invalid user llinco')
|
||||
self.assertLogged('Nov 8 00:16:12 main sshd[32547]: pam_succeed_if(sshd:auth): error retrieving information about user llinco')
|
||||
|
||||
def testWrongCharDebuggex(self):
|
||||
unittest.F2B.SkipIfCfgMissing(stock=True)
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"--datepattern", r"^(?:%a )?%b %d %H:%M:%S(?:\.%f)?(?: %ExY)?",
|
||||
"--debuggex", "--print-all-matched",
|
||||
FILENAME_WRONGCHAR, FILTER_SSHD,
|
||||
r"llinco[^\\]"
|
||||
))
|
||||
self.assertLogged('Error decoding line')
|
||||
self.assertLogged('Lines: 4 lines, 1 ignored, 2 matched, 1 missed')
|
||||
|
||||
self.assertLogged('https://')
|
||||
|
||||
def testNLCharAsPartOfUniChar(self):
|
||||
fname = tempfile.mktemp(prefix='tmp_fail2ban', suffix='uni')
|
||||
# test two multi-byte encodings (both contains `\x0A` in either \x02\x0A or \x0A\x02):
|
||||
for enc in ('utf-16be', 'utf-16le'):
|
||||
self.pruneLog("[test-phase encoding=%s]" % enc)
|
||||
try:
|
||||
fout = open(fname, 'wb')
|
||||
# test on unicode string containing \x0A as part of uni-char,
|
||||
# it must produce exactly 2 lines (both are failures):
|
||||
for l in (
|
||||
'1490349000 \u20AC Failed auth: invalid user Test\u020A from 192.0.2.1\n',
|
||||
'1490349000 \u20AC Failed auth: invalid user TestI from 192.0.2.2\n'
|
||||
):
|
||||
fout.write(l.encode(enc))
|
||||
fout.close()
|
||||
|
||||
self.assertTrue(_test_exec(
|
||||
"-l", "notice", # put down log-level, because of too many debug-messages
|
||||
"--encoding", enc,
|
||||
"--datepattern", r"^EPOCH",
|
||||
fname, r"Failed .* from <HOST>",
|
||||
))
|
||||
|
||||
self.assertLogged(" encoding : %s" % enc,
|
||||
"Lines: 2 lines, 0 ignored, 2 matched, 0 missed", all=True)
|
||||
self.assertNotLogged("Missed line(s)")
|
||||
finally:
|
||||
fout.close()
|
||||
os.unlink(fname)
|
||||
|
||||
def testExecCmdLine_Usage(self):
|
||||
self.assertNotEqual(_test_exec_command_line(), 0)
|
||||
self.pruneLog()
|
||||
self.assertEqual(_test_exec_command_line('-V'), 0)
|
||||
self.assertLogged(fail2banregex.normVersion())
|
||||
self.pruneLog()
|
||||
self.assertEqual(_test_exec_command_line('--version'), 0)
|
||||
|
||||
def testExecCmdLine_Direct(self):
|
||||
self.assertEqual(_test_exec_command_line(
|
||||
'-l', 'info',
|
||||
STR_00, r"Authentication failure for .*? from <HOST>$"
|
||||
), 0)
|
||||
self.assertLogged('Lines: 1 lines, 0 ignored, 1 matched, 0 missed')
|
||||
|
||||
def testExecCmdLine_MissFailID(self):
|
||||
self.assertNotEqual(_test_exec_command_line(
|
||||
'-l', 'info',
|
||||
STR_00, r"Authentication failure"
|
||||
), 0)
|
||||
self.assertLogged('No failure-id group in ')
|
||||
|
||||
def testExecCmdLine_ErrorParam(self):
|
||||
# single line error:
|
||||
self.assertNotEqual(_test_exec_command_line(
|
||||
'-l', 'notice', '-d', '%:%.%-', 'LOG', 'RE'
|
||||
), 0)
|
||||
self.assertLogged('ERROR: Failed to set datepattern')
|
||||
# verbose (traceback/callstack):
|
||||
self.pruneLog()
|
||||
self.assertNotEqual(_test_exec_command_line(
|
||||
'-v', '-d', '%:%.%-', 'LOG', 'RE'
|
||||
), 0)
|
||||
self.assertLogged('Failed to set datepattern')
|
||||
|
||||
def testLogtypeSystemdJournal(self): # pragma: no cover
|
||||
if not fail2banregex.FilterSystemd:
|
||||
raise unittest.SkipTest('Skip test because no systemd backend available')
|
||||
self.assertTrue(_test_exec(
|
||||
"systemd-journal", FILTER_ZZZ_GEN
|
||||
+'[journalmatch="SYSLOG_IDENTIFIER=\x01\x02dummy\x02\x01",'
|
||||
+' failregex="^\x00\x01\x02dummy regex, never match <F-ID>xxx</F-ID>"]'
|
||||
))
|
||||
self.assertLogged("'logtype': 'journal'")
|
||||
self.assertNotLogged("'logtype': 'file'")
|
||||
self.assertLogged('Lines: 0 lines, 0 ignored, 0 matched, 0 missed')
|
||||
self.pruneLog()
|
||||
# logtype specified explicitly (should win in filter):
|
||||
self.assertTrue(_test_exec(
|
||||
"systemd-journal", FILTER_ZZZ_GEN
|
||||
+'[logtype=file,'
|
||||
+' journalmatch="SYSLOG_IDENTIFIER=\x01\x02dummy\x02\x01",'
|
||||
+' failregex="^\x00\x01\x02dummy regex, never match <F-ID>xxx</F-ID>"]'
|
||||
))
|
||||
self.assertLogged("'logtype': 'file'")
|
||||
self.assertNotLogged("'logtype': 'journal'")
|
||||
262
fail2ban-master/fail2ban/tests/failmanagertestcase.py
Normal file
262
fail2ban-master/fail2ban/tests/failmanagertestcase.py
Normal file
@@ -0,0 +1,262 @@
|
||||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-
|
||||
# vi: set ft=python sts=4 ts=4 sw=4 noet :
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Fail2Ban; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest
|
||||
|
||||
from ..server import failmanager
|
||||
from ..server.failmanager import FailManager, FailManagerEmpty
|
||||
from ..server.ipdns import IPAddr
|
||||
from ..server.ticket import FailTicket
|
||||
|
||||
|
||||
class AddFailure(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
super(AddFailure, self).setUp()
|
||||
self.__items = None
|
||||
self.__failManager = FailManager()
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
super(AddFailure, self).tearDown()
|
||||
|
||||
def _addDefItems(self):
|
||||
self.__items = [['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['87.142.124.10', 1167605999.0],
|
||||
['87.142.124.10', 1167605999.0],
|
||||
['87.142.124.10', 1167605999.0],
|
||||
['100.100.10.10', 1000000000.0],
|
||||
['100.100.10.10', 1000000500.0],
|
||||
['100.100.10.10', 1000001000.0],
|
||||
['100.100.10.10', 1000001500.0],
|
||||
['100.100.10.10', 1000002000.0]]
|
||||
for i in self.__items:
|
||||
self.__failManager.addFailure(FailTicket(i[0], i[1]))
|
||||
|
||||
def testFailManagerAdd(self):
|
||||
self._addDefItems()
|
||||
self.assertEqual(self.__failManager.size(), 3)
|
||||
self.assertEqual(self.__failManager.getFailTotal(), 13)
|
||||
self.__failManager.setFailTotal(0)
|
||||
self.assertEqual(self.__failManager.getFailTotal(), 0)
|
||||
self.__failManager.setFailTotal(13)
|
||||
|
||||
def testFailManagerAdd_MaxMatches(self):
|
||||
maxMatches = 2
|
||||
self.__failManager.maxMatches = maxMatches
|
||||
failures = ["abc\n", "123\n", "ABC\n", "1234\n"]
|
||||
# add failures sequential:
|
||||
i = 80
|
||||
for f in failures:
|
||||
i -= 10
|
||||
ticket = FailTicket("127.0.0.1", 1000002000 - i, [f])
|
||||
ticket.setAttempt(1)
|
||||
self.__failManager.addFailure(ticket)
|
||||
#
|
||||
manFailList = self.__failManager._FailManager__failList
|
||||
self.assertEqual(len(manFailList), 1)
|
||||
ticket = manFailList["127.0.0.1"]
|
||||
# should retrieve 2 matches only, but count of all attempts (4):
|
||||
self.assertEqual(ticket.getAttempt(), len(failures))
|
||||
self.assertEqual(len(ticket.getMatches()), maxMatches)
|
||||
self.assertEqual(ticket.getMatches(), failures[len(failures) - maxMatches:])
|
||||
# add more failures at once:
|
||||
ticket = FailTicket("127.0.0.1", 1000002000 - 10, failures)
|
||||
ticket.setAttempt(len(failures))
|
||||
self.__failManager.addFailure(ticket)
|
||||
#
|
||||
manFailList = self.__failManager._FailManager__failList
|
||||
self.assertEqual(len(manFailList), 1)
|
||||
ticket = manFailList["127.0.0.1"]
|
||||
# should retrieve 2 matches only, but count of all attempts (8):
|
||||
self.assertEqual(ticket.getAttempt(), 2 * len(failures))
|
||||
self.assertEqual(len(ticket.getMatches()), maxMatches)
|
||||
self.assertEqual(ticket.getMatches(), failures[len(failures) - maxMatches:])
|
||||
# add self ticket again:
|
||||
self.__failManager.addFailure(ticket)
|
||||
#
|
||||
manFailList = self.__failManager._FailManager__failList
|
||||
self.assertEqual(len(manFailList), 1)
|
||||
ticket = manFailList["127.0.0.1"]
|
||||
# same matches, but +1 attempt (9)
|
||||
self.assertEqual(ticket.getAttempt(), 2 * len(failures) + 1)
|
||||
self.assertEqual(len(ticket.getMatches()), maxMatches)
|
||||
self.assertEqual(ticket.getMatches(), failures[len(failures) - maxMatches:])
|
||||
# no matches by maxMatches == 0 :
|
||||
self.__failManager.maxMatches = 0
|
||||
self.__failManager.addFailure(ticket)
|
||||
manFailList = self.__failManager._FailManager__failList
|
||||
ticket = manFailList["127.0.0.1"]
|
||||
self.assertEqual(len(ticket.getMatches()), 0)
|
||||
# test set matches None to None:
|
||||
ticket.setMatches(None)
|
||||
|
||||
def testFailManagerMaxTime(self):
|
||||
self._addDefItems()
|
||||
self.assertEqual(self.__failManager.getMaxTime(), 600)
|
||||
self.__failManager.setMaxTime(13)
|
||||
self.assertEqual(self.__failManager.getMaxTime(), 13)
|
||||
self.__failManager.setMaxTime(600)
|
||||
|
||||
def testDel(self):
|
||||
self._addDefItems()
|
||||
self.__failManager.delFailure('193.168.0.128')
|
||||
self.__failManager.delFailure('111.111.1.111')
|
||||
|
||||
self.assertEqual(self.__failManager.size(), 2)
|
||||
|
||||
def testCleanupOK(self):
|
||||
self._addDefItems()
|
||||
timestamp = 1167606999.0
|
||||
self.__failManager.cleanup(timestamp)
|
||||
self.assertEqual(self.__failManager.size(), 0)
|
||||
|
||||
def testCleanupNOK(self):
|
||||
self._addDefItems()
|
||||
timestamp = 1167605990.0
|
||||
self.__failManager.cleanup(timestamp)
|
||||
self.assertEqual(self.__failManager.size(), 2)
|
||||
|
||||
def testbanOK(self):
|
||||
self._addDefItems()
|
||||
self.__failManager.setMaxRetry(5)
|
||||
#ticket = FailTicket('193.168.0.128', None)
|
||||
ticket = self.__failManager.toBan()
|
||||
self.assertEqual(ticket.getID(), "193.168.0.128")
|
||||
self.assertTrue(isinstance(ticket.getID(), (str, IPAddr)))
|
||||
|
||||
# finish with rudimentary tests of the ticket
|
||||
# verify consistent str
|
||||
ticket_str = str(ticket)
|
||||
ticket_repr = repr(ticket)
|
||||
self.assertEqual(
|
||||
ticket_str,
|
||||
'FailTicket: ip=193.168.0.128 time=1167605999.0 bantime=None bancount=0 #attempts=5 matches=[]')
|
||||
self.assertEqual(
|
||||
ticket_repr,
|
||||
'FailTicket: ip=193.168.0.128 time=1167605999.0 bantime=None bancount=0 #attempts=5 matches=[]')
|
||||
self.assertFalse(not ticket)
|
||||
# and some get/set-ers otherwise not tested
|
||||
ticket.setTime(1000002000.0)
|
||||
self.assertEqual(ticket.getTime(), 1000002000.0)
|
||||
# and str() adjusted correspondingly
|
||||
self.assertEqual(
|
||||
str(ticket),
|
||||
'FailTicket: ip=193.168.0.128 time=1000002000.0 bantime=None bancount=0 #attempts=5 matches=[]')
|
||||
|
||||
def testbanNOK(self):
|
||||
self._addDefItems()
|
||||
self.__failManager.setMaxRetry(10)
|
||||
self.assertRaises(FailManagerEmpty, self.__failManager.toBan)
|
||||
|
||||
def testWindow(self):
|
||||
self._addDefItems()
|
||||
ticket = self.__failManager.toBan()
|
||||
self.assertNotEqual(ticket.getID(), "100.100.10.10")
|
||||
ticket = self.__failManager.toBan()
|
||||
self.assertNotEqual(ticket.getID(), "100.100.10.10")
|
||||
self.assertRaises(FailManagerEmpty, self.__failManager.toBan)
|
||||
|
||||
def testBgService(self):
|
||||
bgSvc = self.__failManager._FailManager__bgSvc
|
||||
failManager2nd = FailManager()
|
||||
# test singleton (same object):
|
||||
bgSvc2 = failManager2nd._FailManager__bgSvc
|
||||
self.assertTrue(id(bgSvc) == id(bgSvc2))
|
||||
bgSvc2 = None
|
||||
# test service :
|
||||
self.assertTrue(bgSvc.service(True, True))
|
||||
self.assertFalse(bgSvc.service())
|
||||
# bypass threshold and time:
|
||||
for i in range(1, bgSvc._BgService__threshold):
|
||||
self.assertFalse(bgSvc.service())
|
||||
# bypass time check:
|
||||
bgSvc._BgService__serviceTime = -0x7fffffff
|
||||
self.assertTrue(bgSvc.service())
|
||||
# bypass threshold and time:
|
||||
bgSvc._BgService__serviceTime = -0x7fffffff
|
||||
for i in range(1, bgSvc._BgService__threshold):
|
||||
self.assertFalse(bgSvc.service())
|
||||
self.assertTrue(bgSvc.service(False, True))
|
||||
self.assertFalse(bgSvc.service(False, True))
|
||||
|
||||
|
||||
class FailmanagerComplex(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
super(FailmanagerComplex, self).setUp()
|
||||
self.__failManager = FailManager()
|
||||
# down logging level for all this tests, because of extremely large failure count (several GB on heavydebug)
|
||||
self.__saved_ll = failmanager.logLevel
|
||||
failmanager.logLevel = 3
|
||||
|
||||
def tearDown(self):
|
||||
super(FailmanagerComplex, self).tearDown()
|
||||
# restore level
|
||||
failmanager.logLevel = self.__saved_ll
|
||||
|
||||
@staticmethod
|
||||
def _ip_range(maxips):
|
||||
class _ip(list):
|
||||
def __str__(self):
|
||||
return '.'.join(map(str, self))
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
def __key__(self):
|
||||
return str(self)
|
||||
def __hash__(self):
|
||||
#return (int)(struct.unpack('I', struct.pack("BBBB",*self))[0])
|
||||
return (int)(self[0] << 24 | self[1] << 16 | self[2] << 8 | self[3])
|
||||
i = 0
|
||||
c = [127,0,0,0]
|
||||
while i < maxips:
|
||||
for n in range(3,0,-1):
|
||||
if c[n] < 255:
|
||||
c[n] += 1
|
||||
break
|
||||
c[n] = 0
|
||||
yield (i, _ip(c))
|
||||
i += 1
|
||||
|
||||
def testCheckIPGenerator(self):
|
||||
for i, ip in self._ip_range(65536 if not unittest.F2B.fast else 1000):
|
||||
if i == 254:
|
||||
self.assertEqual(str(ip), '127.0.0.255')
|
||||
elif i == 255:
|
||||
self.assertEqual(str(ip), '127.0.1.0')
|
||||
elif i == 1000:
|
||||
self.assertEqual(str(ip), '127.0.3.233')
|
||||
elif i == 65534:
|
||||
self.assertEqual(str(ip), '127.0.255.255')
|
||||
elif i == 65535:
|
||||
self.assertEqual(str(ip), '127.1.0.0')
|
||||
|
||||
29
fail2ban-master/fail2ban/tests/files/action.d/action.py
Normal file
29
fail2ban-master/fail2ban/tests/files/action.d/action.py
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
from fail2ban.server.action import ActionBase
|
||||
|
||||
|
||||
class TestAction(ActionBase):
|
||||
|
||||
def __init__(self, jail, name, opt1, opt2=None):
|
||||
super(TestAction, self).__init__(jail, name)
|
||||
self._logSys.debug("%s initialised" % self.__class__.__name__)
|
||||
self.opt1 = opt1
|
||||
self.opt2 = opt2
|
||||
self._opt3 = "Hello"
|
||||
|
||||
def start(self):
|
||||
self._logSys.debug("%s action start" % self.__class__.__name__)
|
||||
|
||||
def stop(self):
|
||||
self._logSys.debug("%s action stop" % self.__class__.__name__)
|
||||
|
||||
def ban(self, aInfo):
|
||||
self._logSys.debug("%s action ban" % self.__class__.__name__)
|
||||
|
||||
def unban(self, aInfo):
|
||||
self._logSys.debug("%s action unban" % self.__class__.__name__)
|
||||
|
||||
def testmethod(self, text):
|
||||
return "%s %s %s" % (self._opt3, text, self.opt1)
|
||||
|
||||
Action = TestAction
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
from fail2ban.server.action import ActionBase
|
||||
|
||||
|
||||
class TestAction(ActionBase):
|
||||
|
||||
def ban(self, aInfo):
|
||||
self._logSys.info("ban ainfo %s, %s, %s, %s",
|
||||
aInfo["ipmatches"] != '', aInfo["ipjailmatches"] != '', aInfo["ipfailures"] > 0, aInfo["ipjailfailures"] > 0
|
||||
)
|
||||
self._logSys.info("jail info %d, %d, %d, %d",
|
||||
aInfo["jail.banned"], aInfo["jail.banned_total"], aInfo["jail.found"], aInfo["jail.found_total"]
|
||||
)
|
||||
|
||||
def unban(self, aInfo):
|
||||
pass
|
||||
|
||||
Action = TestAction
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
from fail2ban.server.action import ActionBase
|
||||
|
||||
|
||||
class TestAction(ActionBase):
|
||||
|
||||
def __init__(self, jail, name):
|
||||
super(TestAction, self).__init__(jail, name)
|
||||
|
||||
def start(self):
|
||||
raise Exception()
|
||||
|
||||
def stop(self):
|
||||
raise Exception()
|
||||
|
||||
def ban(self):
|
||||
raise Exception()
|
||||
|
||||
def unban(self):
|
||||
raise Exception()
|
||||
|
||||
Action = TestAction
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
from fail2ban.server.action import ActionBase
|
||||
|
||||
|
||||
class TestAction(ActionBase):
|
||||
|
||||
def ban(self, aInfo):
|
||||
del aInfo['ip']
|
||||
self._logSys.info("%s ban deleted aInfo IP", self._name)
|
||||
|
||||
def unban(self, aInfo):
|
||||
del aInfo['ip']
|
||||
self._logSys.info("%s unban deleted aInfo IP", self._name)
|
||||
|
||||
def flush(self):
|
||||
# intended error to cover no unhandled exception occurs in flush
|
||||
# as well as unbans are done individually after errored flush.
|
||||
raise ValueError("intended error")
|
||||
|
||||
Action = TestAction
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
from fail2ban.server.action import ActionBase
|
||||
|
||||
|
||||
class TestAction(ActionBase):
|
||||
pass
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
class TestAction():
|
||||
|
||||
def __init__(self, jail, name):
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
Action = TestAction
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
Apache Auth.
|
||||
|
||||
This directory contains the configuration file of Apache's Web Server to
|
||||
simulate authentication files.
|
||||
|
||||
These assumed that /var/www/html is the web root and AllowOverrides is "All".
|
||||
|
||||
The subdirectories here are copied to the /var/www/html directory.
|
||||
|
||||
Commands executed are in testcases/files/log/apache-auth with their
|
||||
corresponding failure mechanism.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
AuthType basic
|
||||
AuthName "private area"
|
||||
AuthBasicProvider file
|
||||
AuthUserFile /var/www/html/basic/authz_owner/.htpasswd
|
||||
Require file-owner
|
||||
@@ -0,0 +1 @@
|
||||
username:$apr1$1f5oQUl4$21lLXSN7xQOPtNsj5s4Nk/
|
||||
@@ -0,0 +1,5 @@
|
||||
AuthType basic
|
||||
AuthName "private area"
|
||||
AuthBasicProvider file
|
||||
AuthUserFile /var/www/html/basic/file/.htpasswd
|
||||
Require valid-user
|
||||
@@ -0,0 +1 @@
|
||||
username:$apr1$uUMsOjCQ$.BzXClI/B/vZKddgIAJCR.
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env fail2ban-python
|
||||
import requests
|
||||
|
||||
try:
|
||||
import hashlib
|
||||
md5sum = hashlib.md5
|
||||
except ImportError: # pragma: no cover
|
||||
# hashlib was introduced in Python 2.5. For compatibility with those
|
||||
# elderly Pythons, import from md5
|
||||
import md5
|
||||
md5sum = md5.new
|
||||
|
||||
|
||||
def auth(v):
|
||||
|
||||
ha1 = md5sum(username + ':' + realm + ':' + password).hexdigest()
|
||||
ha2 = md5sum("GET:" + url).hexdigest()
|
||||
|
||||
#response = md5sum(ha1 + ':' + v['nonce'][1:-1] + ':' + v['nc'] + ':' + v['cnonce'][1:-1]
|
||||
# + ':' + v['qop'][1:-1] + ':' + ha2).hexdigest()
|
||||
|
||||
nonce = v['nonce'][1:-1]
|
||||
nc=v.get('nc') or ''
|
||||
cnonce = v.get('cnonce') or ''
|
||||
#opaque = v.get('opaque') or ''
|
||||
qop = v['qop'][1:-1]
|
||||
algorithm = v['algorithm']
|
||||
response = md5sum(ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2).hexdigest()
|
||||
|
||||
p = requests.Request('GET', host + url).prepare()
|
||||
#p.headers['Authentication-Info'] = response
|
||||
p.headers['Authorization'] = """
|
||||
Digest username="%s",
|
||||
algorithm="%s",
|
||||
realm="%s",
|
||||
uri="%s",
|
||||
nonce="%s",
|
||||
cnonce="",
|
||||
nc="",
|
||||
qop=%s,
|
||||
response="%s"
|
||||
""" % ( username, algorithm, realm, url, nonce, qop, response )
|
||||
# opaque="%s",
|
||||
print((p.method, p.url, p.headers))
|
||||
s = requests.Session()
|
||||
return s.send(p)
|
||||
|
||||
|
||||
def preauth():
|
||||
r = requests.get(host + url)
|
||||
print(r)
|
||||
r.headers['www-authenticate'].split(', ')
|
||||
return dict([ a.split('=',1) for a in r.headers['www-authenticate'].split(', ') ])
|
||||
|
||||
|
||||
url='/digest/'
|
||||
host = 'http://localhost:801'
|
||||
|
||||
v = preauth()
|
||||
|
||||
username="username"
|
||||
password = "password"
|
||||
print(v)
|
||||
|
||||
realm = 'so far away'
|
||||
r = auth(v)
|
||||
|
||||
realm = v['Digest realm'][1:-1]
|
||||
|
||||
# [Sun Jul 28 21:27:56.549667 2013] [auth_digest:error] [pid 24835:tid 139895297222400] [client 127.0.0.1:57052] AH01788: realm mismatch - got `so far away' but expected `digest private area'
|
||||
|
||||
|
||||
algorithm = v['algorithm']
|
||||
v['algorithm'] = 'super funky chicken'
|
||||
r = auth(v)
|
||||
|
||||
# [Sun Jul 28 21:41:20 2013] [error] [client 127.0.0.1] Digest: unknown algorithm `super funky chicken' received: /digest/
|
||||
|
||||
print((r.status_code,r.headers, r.text))
|
||||
v['algorithm'] = algorithm
|
||||
|
||||
|
||||
r = auth(v)
|
||||
print((r.status_code,r.headers, r.text))
|
||||
|
||||
nonce = v['nonce']
|
||||
v['nonce']=v['nonce'][5:-5]
|
||||
|
||||
r = auth(v)
|
||||
print((r.status_code,r.headers, r.text))
|
||||
|
||||
# [Sun Jul 28 21:05:31.178340 2013] [auth_digest:error] [pid 24224:tid 139895539455744] [client 127.0.0.1:56906] AH01793: invalid qop `auth' received: /digest/qop_none/
|
||||
|
||||
|
||||
v['nonce']=nonce[0:11] + 'ZZZ' + nonce[14:]
|
||||
|
||||
r = auth(v)
|
||||
print((r.status_code,r.headers, r.text))
|
||||
|
||||
#[Sun Jul 28 21:18:11.769228 2013] [auth_digest:error] [pid 24752:tid 139895505884928] [client 127.0.0.1:56964] AH01776: invalid nonce b9YAiJDiBAZZZ1b1abe02d20063ea3b16b544ea1b0d981c1bafe received - hash is not d42d824dee7aaf50c3ba0a7c6290bd453e3dd35b
|
||||
|
||||
|
||||
url='/digest_time/'
|
||||
v=preauth()
|
||||
|
||||
import time
|
||||
time.sleep(1)
|
||||
|
||||
r = auth(v)
|
||||
print((r.status_code,r.headers, r.text))
|
||||
|
||||
# Obtained by putting the following code in modules/aaa/mod_auth_digest.c
|
||||
# in the function initialize_secret
|
||||
# {
|
||||
# const char *hex = "0123456789abcdef";
|
||||
# char secbuff[SECRET_LEN * 4];
|
||||
# char *hash = secbuff;
|
||||
# int idx;
|
||||
|
||||
# for (idx=0; idx<sizeof(secret); idx++) {
|
||||
# *hash++ = hex[secret[idx] >> 4];
|
||||
# *hash++ = hex[secret[idx] & 0xF];
|
||||
# }
|
||||
# *hash = '\0';
|
||||
# /* remove comment makings in below for apache-2.4+ */
|
||||
# ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, /* APLOGNO(11759) */ "secret: %s", secbuff);
|
||||
# }
|
||||
|
||||
|
||||
import sha
|
||||
import binascii
|
||||
import base64
|
||||
import struct
|
||||
|
||||
apachesecret = binascii.unhexlify('497d8894adafa5ec7c8c981ddf9c8457da7a90ac')
|
||||
s = sha.sha(apachesecret)
|
||||
|
||||
v=preauth()
|
||||
|
||||
print((v['nonce']))
|
||||
realm = v['Digest realm'][1:-1]
|
||||
|
||||
(t,) = struct.unpack('l',base64.b64decode(v['nonce'][1:13]))
|
||||
|
||||
# whee, time travel
|
||||
t = t + 5540
|
||||
|
||||
timepac = base64.b64encode(struct.pack('l',t))
|
||||
|
||||
s.update(realm)
|
||||
s.update(timepac)
|
||||
|
||||
v['nonce'] = v['nonce'][0] + timepac + s.hexdigest() + v['nonce'][-1]
|
||||
|
||||
print(v)
|
||||
|
||||
r = auth(v)
|
||||
#[Mon Jul 29 02:12:55.539813 2013] [auth_digest:error] [pid 9647:tid 139895522670336] [client 127.0.0.1:58474] AH01777: invalid nonce 59QJppTiBAA=b08983fd166ade9840407df1b0f75b9e6e07d88d received - user attempted time travel
|
||||
print((r.status_code,r.headers, r.text))
|
||||
|
||||
url='/digest_onetime/'
|
||||
v=preauth()
|
||||
|
||||
# Need opaque header handling in auth
|
||||
r = auth(v)
|
||||
print((r.status_code,r.headers, r.text))
|
||||
r = auth(v)
|
||||
print((r.status_code,r.headers, r.text))
|
||||
@@ -0,0 +1,6 @@
|
||||
AuthType Digest
|
||||
AuthName "digest private area"
|
||||
AuthDigestDomain /digest/
|
||||
AuthBasicProvider file
|
||||
AuthUserFile /var/www/html/digest/.htpasswd
|
||||
Require valid-user
|
||||
@@ -0,0 +1 @@
|
||||
username:digest private area:fad48d3a7c63f61b5b3567a4105bbb04
|
||||
@@ -0,0 +1,9 @@
|
||||
AuthType Digest
|
||||
AuthName "digest anon"
|
||||
AuthDigestDomain /digest_anon/
|
||||
AuthBasicProvider file anon
|
||||
AuthUserFile /var/www/html/digest_anon/.htpasswd
|
||||
Anonymous_NoUserID off
|
||||
Anonymous anonymous
|
||||
Anonymous_LogEmail on
|
||||
Require valid-user
|
||||
@@ -0,0 +1,3 @@
|
||||
username:digest anon:25e4077a9344ceb1a88f2a62c9fb60d8
|
||||
05bbb04
|
||||
anonymous:digest anon:faa4e5870970cf935bb9674776e6b26a
|
||||
@@ -0,0 +1,7 @@
|
||||
AuthType Digest
|
||||
AuthName "digest private area"
|
||||
AuthDigestDomain /digest_time/
|
||||
AuthBasicProvider file
|
||||
AuthUserFile /var/www/html/digest_time/.htpasswd
|
||||
AuthDigestNonceLifetime 1
|
||||
Require valid-user
|
||||
@@ -0,0 +1 @@
|
||||
username:digest private area:fad48d3a7c63f61b5b3567a4105bbb04
|
||||
@@ -0,0 +1,6 @@
|
||||
AuthType Digest
|
||||
AuthName "digest private area"
|
||||
AuthDigestDomain /digest_wrongrelm/
|
||||
AuthBasicProvider file
|
||||
AuthUserFile /var/www/html/digest_wrongrelm/.htpasswd
|
||||
Require valid-user
|
||||
@@ -0,0 +1,2 @@
|
||||
username:wrongrelm:99cd340e1283c6d0ab34734bd47bdc30
|
||||
4105bbb04
|
||||
@@ -0,0 +1 @@
|
||||
Deny from all
|
||||
BIN
fail2ban-master/fail2ban/tests/files/database_v1.db
Normal file
BIN
fail2ban-master/fail2ban/tests/files/database_v1.db
Normal file
Binary file not shown.
BIN
fail2ban-master/fail2ban/tests/files/database_v2.db
Normal file
BIN
fail2ban-master/fail2ban/tests/files/database_v2.db
Normal file
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
[DEFAULT]
|
||||
|
||||
honeypot = fail2ban@localhost
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = to=<honeypot> fromip=<IP>
|
||||
|
||||
[Init]
|
||||
|
||||
honeypot = sweet@example.com
|
||||
@@ -0,0 +1,41 @@
|
||||
# Generic configuration items (to be used as interpolations) in other
|
||||
# filters or actions configurations
|
||||
#
|
||||
# Author: Yaroslav Halchenko
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
# Daemon definition is to be specialized (if needed) in .conf file
|
||||
_daemon = \S*
|
||||
|
||||
#
|
||||
# Shortcuts for easier comprehension of the failregex
|
||||
#
|
||||
# PID.
|
||||
# EXAMPLES: [123]
|
||||
__pid_re = (?:\[\d+\])
|
||||
|
||||
# Daemon name (with optional source_file:line or whatever)
|
||||
# EXAMPLES: pam_rhosts_auth, [sshd], pop(pam_unix)
|
||||
__daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:?
|
||||
|
||||
# Combinations of daemon name and PID
|
||||
# EXAMPLES: sshd[31607], pop(pam_unix)[4920]
|
||||
__daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:)
|
||||
|
||||
# Some messages have a kernel prefix with a timestamp
|
||||
# EXAMPLES: kernel: [769570.846956]
|
||||
__kernel_prefix = kernel: \[\d+\.\d+\]
|
||||
|
||||
__hostname = \S+
|
||||
|
||||
#
|
||||
# Common line prefixes (beginnings) which could be used in filters
|
||||
#
|
||||
# [hostname] [vserver tag] daemon_id spaces
|
||||
# this can be optional (for instance if we match named native log files)
|
||||
__prefix_line = \s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s*
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes. If any customizations available -- read them from
|
||||
# common.local
|
||||
before = testcase-common.conf
|
||||
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = sshd
|
||||
|
||||
# Option: failregex
|
||||
# Notes.: regex to match the password failures messages in the logfile. The
|
||||
# host must be matched by a group named "host". The tag "<HOST>" can
|
||||
# be used for standard IP/hostname matching and is only an alias for
|
||||
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
|
||||
# Values: TEXT
|
||||
#
|
||||
failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
|
||||
^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
|
||||
^%(__prefix_line)s(?:error: PAM: )?User not known to the\nunderlying authentication.+$<SKIPLINES>^.+ module for .* from <HOST>\s*$
|
||||
|
||||
# Option: ignoreregex
|
||||
# Notes.: regex to ignore. If this regex matches, the line is ignored.
|
||||
# Values: TEXT
|
||||
#
|
||||
ignoreregex = ^.+ john from host 192.168.1.1\s*$
|
||||
|
||||
# "maxlines" is number of log lines to buffer for multi-line regex searches
|
||||
maxlines = 1
|
||||
|
||||
# "datepattern" allows setting of a custom data pattern as alternative
|
||||
# to the default date detectors. See manpage strptime(3) for date formats.
|
||||
# NOTE: that ALL '%' must be prefixed with '%' due to string substitution
|
||||
# e.g. %%Y-%%m-%%d %%H:%%M
|
||||
datepattern = %%Y %%m %%d %%H:%%M:%%S
|
||||
|
||||
# Option: journalmatch
|
||||
# Notes.: systemd journalctl style match filter for journal based backends
|
||||
# Values: TEXT
|
||||
#
|
||||
journalmatch = _COMM=sshd + _SYSTEMD_UNIT=sshd.service _UID=0
|
||||
"FIELD= with spaces " + AFIELD=" with + char and spaces"
|
||||
@@ -0,0 +1,12 @@
|
||||
[INCLUDES]
|
||||
|
||||
# Read common prefixes. If any customizations available -- read them from
|
||||
# common.local
|
||||
before = testcase-common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon = sshd
|
||||
__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )?
|
||||
|
||||
failregex = %(__prefix_line)s test
|
||||
@@ -0,0 +1,4 @@
|
||||
[Definition]
|
||||
|
||||
# no options here, coverage for testFilterReaderSubstKnown:
|
||||
# avoid to overwrite known/option with unmodified (not available) value of option from .local config file
|
||||
8
fail2ban-master/fail2ban/tests/files/ignorecommand.py
Normal file
8
fail2ban-master/fail2ban/tests/files/ignorecommand.py
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env fail2ban-python
|
||||
import sys
|
||||
if len(sys.argv) != 2 or sys.argv[1] == "":
|
||||
sys.stderr.write('usage: ignorecommand IP')
|
||||
exit(10)
|
||||
if sys.argv[1] == "10.0.0.1":
|
||||
exit(0)
|
||||
exit(1)
|
||||
6
fail2ban-master/fail2ban/tests/files/logs/3proxy
Normal file
6
fail2ban-master/fail2ban/tests/files/logs/3proxy
Normal file
@@ -0,0 +1,6 @@
|
||||
# failJSON: { "time": "2013-06-11T02:09:40", "match": true , "host": "1.2.3.4" }
|
||||
11-06-2013 02:09:40 +0300 PROXY.3128 00004 - 1.2.3.4:28783 0.0.0.0:0 0 0 0 GET http://www.yandex.ua/?ncrnd=2169807731 HTTP/1.1
|
||||
# failJSON: { "time": "2013-06-11T02:09:43", "match": true , "host": "1.2.3.4" }
|
||||
11-06-2013 02:09:43 +0300 PROXY.3128 00005 ewr 1.2.3.4:28788 0.0.0.0:0 0 0 0 GET http://www.yandex.ua/?ncrnd=2169807731 HTTP/1.1
|
||||
# failJSON: { "time": "2013-06-13T01:39:34", "match": true , "host": "1.2.3.4" }
|
||||
13-06-2013 01:39:34 +0300 PROXY.3128 00508 - 1.2.3.4:28938 0.0.0.0:0 0 0 0
|
||||
158
fail2ban-master/fail2ban/tests/files/logs/apache-auth
Normal file
158
fail2ban-master/fail2ban/tests/files/logs/apache-auth
Normal file
@@ -0,0 +1,158 @@
|
||||
# Should not match -- DoS vector https://vndh.net/note:fail2ban-089-denial-service
|
||||
# failJSON: { "match": false }
|
||||
[Sat Jun 01 02:17:42 2013] [error] [client 192.168.33.1] File does not exist: /srv/http/site/[client 192.168.0.1] user root not found
|
||||
|
||||
# should match
|
||||
# failJSON: { "time": "2013-07-11T01:21:41", "match": true , "host": "194.228.20.113" }
|
||||
[Thu Jul 11 01:21:41 2013] [error] [client 194.228.20.113] user not found: /
|
||||
|
||||
# failJSON: { "time": "2013-07-11T01:21:43", "match": true , "host": "194.228.20.113" }
|
||||
[Thu Jul 11 01:21:43 2013] [error] [client 194.228.20.113] user dsfasdf not found: /
|
||||
# failJSON: { "time": "2013-07-11T01:21:44", "match": true , "host": "2001:db8::80da:af6b:8b2c" }
|
||||
[Thu Jul 11 01:21:44 2013] [error] [client 2001:db8::80da:af6b:8b2c] user test-ipv6 not found: /
|
||||
|
||||
# The failures below use the configuration described in fail2ban/tests/files/config/apache-auth
|
||||
#
|
||||
|
||||
# wget http://localhost/noentry/cant_get_me.html -O /dev/null
|
||||
# failJSON: { "time": "2013-07-17T23:20:45", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Jul 17 23:20:45 2013] [error] [client 127.0.0.1] client denied by server configuration: /var/www/html/noentry/cant_get_me.html
|
||||
|
||||
# failJSON: { "time": "2013-07-20T21:34:49", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:34:49.453232 2013] [access_compat:error] [pid 17512:tid 140123104306944] [client 127.0.0.1:51380] AH01797: client denied by server configuration: /var/www/html/noentry/cant_get_me.html
|
||||
|
||||
# failJSON: { "time": "2014-09-14T21:44:43", "match": true , "host": "192.3.9.178" }
|
||||
[Sun Sep 14 21:44:43.008606 2014] [authz_core:error] [pid 10691] [client 192.3.9.178:44271] AH01630: client denied by server configuration: /var/www/html/noentry/cant_get_me.html
|
||||
|
||||
# wget --http-user='' --http-password='' http://localhost/basic/file/cant_get_me.html -O /dev/null
|
||||
# failJSON: { "time": "2013-07-17T23:14:37", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Jul 17 23:14:37 2013] [error] [client 127.0.0.1] user not found: /basic/anon/cant_get_me.html
|
||||
|
||||
# failJSON: { "time": "2013-07-20T21:37:32", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:37:32.266605 2013] [auth_basic:error] [pid 17512:tid 140123079128832] [client 127.0.0.1:51386] AH01618: user not found: /basic/file/cant_get_me.html
|
||||
|
||||
# wget --http-user=username --http-password=wrongpass http://localhost/basic/file -O /dev/null
|
||||
# failJSON: { "time": "2013-07-17T22:18:52", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Jul 17 22:18:52 2013] [error] [client 127.0.0.1] user username: authentication failure for "/basic/file": Password Mismatch
|
||||
|
||||
# failJSON: { "time": "2013-07-20T21:39:11", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:39:11.978080 2013] [auth_basic:error] [pid 17512:tid 140123053950720] [client 127.0.0.1:51390] AH01617: user username: authentication failure for "/basic/file": Password Mismatch
|
||||
|
||||
# wget --http-user=wrongusername --http-password=wrongpass http://localhost/basic/file -O /dev/null
|
||||
# failJSON: { "time": "2013-07-17T22:32:48", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Jul 17 22:32:48 2013] [error] [client 127.0.0.1] user wrongusername not found: /basic/file
|
||||
|
||||
# failJSON: { "time": "2013-07-20T21:40:33", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:40:33.803528 2013] [auth_basic:error] [pid 17540:tid 140123095914240] [client 127.0.0.1:51395] AH01618: user wrongusername not found: /basic/file
|
||||
|
||||
# wget --header='Authorization: Digest username="Mufasa",realm="testrealm@host.com",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",uri="/dir/index.html",qop=auth,nc=00000001,cnonce="0a4f113b",response="6629fae49393a05397450978507c4ef1",opaque="5ccc069c403ebaf9f0171e9517f40e41"' http://localhost/basic/file -O /dev/null
|
||||
# failJSON: { "time": "2013-07-17T22:39:55", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Jul 17 22:39:55 2013] [error] [client 127.0.0.1] client used wrong authentication scheme: /basic/file
|
||||
|
||||
# failJSON: { "time": "2013-07-20T21:41:52", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:41:52.523931 2013] [auth_basic:error] [pid 17512:tid 140122964092672] [client 127.0.0.1:51396] AH01614: client used wrong authentication scheme: /basic/file
|
||||
|
||||
# wget --http-user=username --http-password=password http://localhost/basic/authz_owner/cant_get_me.html -O /dev/null
|
||||
# failJSON: { "time": "2013-07-17T22:54:32", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Jul 17 22:54:32 2013] [error] [client 127.0.0.1] Authorization of user username to access /basic/authz_owner/cant_get_me.html failed, reason: file owner dan does not match.
|
||||
|
||||
# failJSON: { "time": "2013-07-20T22:11:43", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 22:11:43.147674 2013] [authz_owner:error] [pid 17540:tid 140122922129152] [client 127.0.0.1:51548] AH01637: Authorization of user username to access /basic/authz_owner/cant_get_me.html failed, reason: file owner dan does not match
|
||||
# failJSON: { "time": "2013-07-20T22:11:44", "match": true , "host": "2001:db8::80da:af6b:8b2c" }
|
||||
[Sat Jul 20 22:11:44.147674 2013] [authz_owner:error] [pid 17540:tid 140122922129152] [client [2001:db8::80da:af6b:8b2c]:51548] AH01637: Authorization of user test-ipv6 to access /basic/authz_owner/cant_get_me.html failed, reason: file owner dan does not match
|
||||
|
||||
# wget --http-user=username --http-password=password http://localhost/basic/authz_owner/cant_get_me.html -O /dev/null
|
||||
# failJSON: { "time": "2013-07-20T21:42:44", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:42:44.304159 2013] [authz_core:error] [pid 17484:tid 140123095914240] [client 127.0.0.1:51397] AH01631: user username: authorization failure for "/basic/authz_owner/cant_get_me.html":
|
||||
|
||||
# wget --http-user='username' --http-password='wrongpassword' http://localhost/digest/cant_get_me.html -O /dev/null
|
||||
# failJSON: { "time": "2013-07-17T23:50:37", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Jul 17 23:50:37 2013] [error] [client 127.0.0.1] Digest: user username: password mismatch: /digest/cant_get_me.html
|
||||
|
||||
# failJSON: { "time": "2013-07-20T21:44:06", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:44:06.867985 2013] [auth_digest:error] [pid 17540:tid 140123070736128] [client 127.0.0.1:51406] AH01792: user username: password mismatch: /digest/cant_get_me.html
|
||||
|
||||
# wget --http-user='username' --http-password='password' http://localhost/digest_wrongrelm/cant_get_me.html -O /dev/null
|
||||
# failJSON: { "time": "2013-07-18T00:08:39", "match": true , "host": "127.0.0.1" }
|
||||
[Thu Jul 18 00:08:39 2013] [error] [client 127.0.0.1] Digest: user `username' in realm `digest private area' not found: /digest_wrongrelm/cant_get_me.html
|
||||
|
||||
# failJSON: { "time": "2013-07-20T21:45:28", "match": true , "host": "127.0.0.1" }
|
||||
[Sat Jul 20 21:45:28.890523 2013] [auth_digest:error] [pid 17540:tid 140122972485376] [client 127.0.0.1:51408] AH01790: user `username' in realm `digest private area' not found: /digest_wrongrelm/cant_get_me.html
|
||||
|
||||
# ./tests/files/config/apache-auth/digest.py
|
||||
# failJSON: { "time": "2013-07-28T21:05:31", "match": true , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:05:31.178340 2013] [auth_digest:error] [pid 24224:tid 139895539455744] [client 127.0.0.1:56906] AH01793: invalid qop `auth' received: /digest/qop_none/
|
||||
|
||||
# ./tests/files/config/apache-auth/digest.py
|
||||
# failJSON: { "time": "2013-07-28T21:12:44", "match": true , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:12:44 2013] [error] [client 127.0.0.1] Digest: invalid nonce JDiBAA=db9372522295196b7ac31db99e10cd1106c received - length is not 52
|
||||
|
||||
|
||||
# ./tests/files/config/apache-auth/digest.py
|
||||
# yoh: ATM it should not match because matching failregex is still under "investigation"
|
||||
# failJSON: { "time": "2013-07-28T21:16:37", "match": false , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:16:37 2013] [error] [client 127.0.0.1] Digest: invalid nonce l19lgpDiBAZZZf1ec3d9613f3b3ef43660e3628d78455fd8b937 received - hash is not 6fda8bbcbcf85ff1ebfe7d1c43faba583bc53a02
|
||||
|
||||
# ./tests/files/config/apache-auth/digest.py
|
||||
# failJSON: { "time": "2013-07-28T21:18:11", "match": false , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:18:11.769228 2013] [auth_digest:error] [pid 24752:tid 139895505884928] [client 127.0.0.1:56964] AH01776: invalid nonce b9YAiJDiBAZZZ1b1abe02d20063ea3b16b544ea1b0d981c1bafe received - hash is not d42d824dee7aaf50c3ba0a7c6290bd453e3dd35b
|
||||
|
||||
# ./tests/files/config/apache-auth/digest.py
|
||||
# failJSON: { "time": "2013-07-28T21:30:02", "match": true , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:30:02 2013] [error] [client 127.0.0.1] Digest: realm mismatch - got `so far away' but expected `digest private area'
|
||||
|
||||
# failJSON: { "time": "2013-07-28T21:27:56", "match": true , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:27:56.549667 2013] [auth_digest:error] [pid 24835:tid 139895297222400] [client 127.0.0.1:57052] AH01788: realm mismatch - got `so far away' but expected `digest private area'
|
||||
|
||||
|
||||
# ./tests/files/config/apache-auth/digest.py
|
||||
# failJSON: { "time": "2013-07-28T21:41:20", "match": true , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:41:20 2013] [error] [client 127.0.0.1] Digest: unknown algorithm `super funky chicken' received: /digest/
|
||||
|
||||
# failJSON: { "time": "2013-07-28T21:42:03", "match": true , "host": "127.0.0.1" }
|
||||
[Sun Jul 28 21:42:03.930190 2013] [auth_digest:error] [pid 24835:tid 139895505884928] [client 127.0.0.1:57115] AH01789: unknown algorithm `super funky chicken' received: /digest/
|
||||
|
||||
# ./tests/files/config/apache-auth/digest.py
|
||||
# failJSON: { "time": "2013-07-29T02:15:26", "match": true , "host": "127.0.0.1" }
|
||||
[Mon Jul 29 02:15:26 2013] [error] [client 127.0.0.1] Digest: invalid nonce LWEDr5TiBAA=ceddd011628c30e3646f7acda4f1a0ab6b7c5ae6 received - user attempted time travel
|
||||
|
||||
# failJSON: { "time": "2013-07-29T02:12:55", "match": true , "host": "127.0.0.1" }
|
||||
[Mon Jul 29 02:12:55.539813 2013] [auth_digest:error] [pid 9647:tid 139895522670336] [client 127.0.0.1:58474] AH01777: invalid nonce 59QJppTiBAA=b08983fd166ade9840407df1b0f75b9e6e07d88d received - user attempted time travel
|
||||
|
||||
# failJSON: { "time": "2013-06-01T02:17:42", "match": true , "host": "192.168.0.2" }
|
||||
[Sat Jun 01 02:17:42 2013] [error] [client 192.168.0.2] user root not found
|
||||
|
||||
# failJSON: { "time": "2013-11-18T22:39:33", "match": true , "host": "91.49.82.139" }
|
||||
[Mon Nov 18 22:39:33 2013] [error] [client 91.49.82.139] user gg not found: /, referer: http://sj.hopto.org/management.html
|
||||
|
||||
# failJSON: { "time": "2018-03-28T01:31:42", "match": true , "host": "91.49.82.139" }
|
||||
[Wed Mar 28 01:31:42.355210 2018] [ssl:error] [pid 6586] [client 91.49.82.139:58028] AH02031: Hostname www.testdom.com provided via SNI, but no hostname provided in HTTP request
|
||||
|
||||
# failJSON: { "time": "2018-03-28T01:31:42", "match": true , "host": "91.49.82.139" }
|
||||
[Wed Mar 28 01:31:42.355210 2018] [ssl:error] [pid 6586] [client 91.49.82.139:58028] AH02032: Hostname www.testdom.com provided via SNI and hostname dummy.com provided via HTTP have no compatible SSL setup
|
||||
|
||||
# failJSON: { "time": "2018-03-28T01:31:42", "match": true , "host": "91.49.82.139" }
|
||||
[Wed Mar 28 01:31:42.355210 2018] [ssl:error] [pid 6586] [client 91.49.82.139:58028] AH02033: No hostname was provided via SNI for a name based virtual host
|
||||
|
||||
# failJSON: { "match": false, "desc": "ignore mod_evasive errors in normal mode (gh-2548)" }
|
||||
[Thu Oct 17 18:43:40.160521 2019] [evasive20:error] [pid 22589] [client 192.0.2.1:56175] client denied by server configuration: /path/index.php, referer: https://hostname/path/
|
||||
|
||||
# failJSON: { "time": "2023-11-06T13:39:56", "match": true , "host": "192.0.2.111", "desc": "remote instead of client, gh-3622" }
|
||||
[Mon Nov 06 13:39:56.637868 2023] [auth_basic:error] [pid 3175001:tid 140494544914112] [remote 192.0.2.111:35924] AH01618: user whatever not found: /admin
|
||||
|
||||
# filterOptions: {"mode": "aggressive"}
|
||||
|
||||
# failJSON: { "time": "2019-10-17T18:43:40", "match": true, "host": "192.0.2.1", "desc": "accept mod_evasive errors in aggressive mode (gh-2548)" }
|
||||
[Thu Oct 17 18:43:40.160521 2019] [evasive20:error] [pid 22589] [client 192.0.2.1:56175] client denied by server configuration: /path/index.php, referer: https://hostname/path/
|
||||
|
||||
# filterOptions: {"logging": "syslog"}
|
||||
|
||||
# failJSON: { "time": "2005-02-15T16:23:00", "match": true , "host": "192.0.2.1", "desc": "using syslog (ErrorLog syslog)" }
|
||||
Feb 15 16:23:00 srv httpd[22034]: [authz_core:error] [pid 22034] [client 192.0.2.1:58585] AH01630: client denied by server configuration: /home/www/
|
||||
# failJSON: { "time": "2005-02-15T16:23:40", "match": true , "host": "192.0.2.2", "desc": "using syslog (ErrorLog syslog)" }
|
||||
Feb 15 16:23:40 srv httpd/backend1[22034]: [authz_core:error] [pid 22036] [client 192.0.2.2:59392] AH01630: client denied by server configuration: /home/backend1/
|
||||
# failJSON: { "time": "2005-02-15T16:54:53", "match": true , "host": "192.0.2.3", "desc": "using syslog (ErrorLog syslog)" }
|
||||
Feb 15 16:54:53 tools apache2[18154]: [:error] [pid 18154:tid 140680873617152] [client 192.0.2.3:48154] AH01630: client denied by server configuration: /var/www
|
||||
|
||||
# failJSON: { "time": "2005-02-16T22:32:48", "match": true , "host": "127.0.0.1" }
|
||||
Feb 16 22:32:48 srv httpd[22034]: [error] [client 127.0.0.1] user wrongusername not found: /basic/file
|
||||
11
fail2ban-master/fail2ban/tests/files/logs/apache-badbots
Normal file
11
fail2ban-master/fail2ban/tests/files/logs/apache-badbots
Normal file
@@ -0,0 +1,11 @@
|
||||
# failJSON: { "time": "2007-03-05T14:39:21", "match": true , "host": "1.2.3.4" }
|
||||
1.2.3.4 - - [05/Mar/2007:14:39:21 +0100] "POST /123.html/trackback/ HTTP/1.0" 301 459 "http://www.mydomain.tld/123.html/trackback" "TrackBack/1.02"
|
||||
|
||||
# failJSON: { "time": "2007-03-05T14:40:21", "match": true , "host": "1.2.3.4" }
|
||||
1.2.3.4 - - [05/Mar/2007:14:40:21 +0100] "GET /123.html/trackback/ HTTP/1.0" 301 459 "http://www.mydomain.tld/123.html/trackback" "TrackBack/1.02"
|
||||
|
||||
# failJSON: { "time": "2007-03-05T14:41:21", "match": true , "host": "1.2.3.4" }
|
||||
1.2.3.4 - - [05/Mar/2007:14:41:21 +0100] "HEAD /123.html/trackback/ HTTP/1.0" 301 459 "http://www.mydomain.tld/123.html/trackback" "TrackBack/1.02"
|
||||
|
||||
# failJSON: { "time": "2024-08-18T22:08:39", "match": true , "host": "192.0.2.222", "desc": "vhost in accesslog, gh-1594" }
|
||||
www.sitename.com 192.0.2.222 - - [18/Aug/2024:21:08:39 +0100] "GET /filename.jpg HTTP/1.1" 403 332 "-" "TrackBack/1.02"
|
||||
43
fail2ban-master/fail2ban/tests/files/logs/apache-botsearch
Normal file
43
fail2ban-master/fail2ban/tests/files/logs/apache-botsearch
Normal file
@@ -0,0 +1,43 @@
|
||||
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
|
||||
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /var/www/wp-login.php
|
||||
|
||||
# failJSON: { "time": "2013-12-23T09:49:10", "match": true , "host": "115.249.248.145" }
|
||||
[Mon Dec 23 09:49:10 2013] [error] [client 115.249.248.145] File does not exist: /var/www/pma
|
||||
# failJSON: { "time": "2013-12-23T09:49:10", "match": true , "host": "115.249.248.145" }
|
||||
[Mon Dec 23 09:49:10 2013] [error] [client 115.249.248.145] File does not exist: /var/www/phpmyadmin
|
||||
# failJSON: { "time": "2013-12-23T09:49:13", "match": true , "host": "115.249.248.145" }
|
||||
[Mon Dec 23 09:49:13 2013] [error] [client 115.249.248.145] File does not exist: /var/www/webmail
|
||||
# failJSON: { "time": "2013-12-23T09:49:13", "match": true , "host": "115.249.248.145" }
|
||||
[Mon Dec 23 09:49:13 2013] [error] [client 115.249.248.145] File does not exist: /var/www/mail
|
||||
|
||||
# failJSON: { "time": "2013-12-31T09:13:47", "match": true , "host": "176.102.37.56" }
|
||||
[Tue Dec 31 09:13:47 2013] [error] [client 176.102.37.56] script '/var/www/wp-login.php' not found or unable to stat
|
||||
|
||||
# failJSON: { "time": "2014-01-03T09:20:23", "match": true , "host": "46.23.77.174" }
|
||||
[Fri Jan 03 09:20:23 2014] [error] [client 46.23.77.174] File does not exist: /var/www/mail
|
||||
# failJSON: { "time": "2014-01-03T09:20:25", "match": true , "host": "46.23.77.174" }
|
||||
[Fri Jan 03 09:20:25 2014] [error] [client 46.23.77.174] File does not exist: /var/www/mail_this_entry
|
||||
# failJSON: { "time": "2014-01-03T09:26:52", "match": true , "host": "46.23.77.174" }
|
||||
[Fri Jan 03 09:26:52 2014] [error] [client 46.23.77.174] File does not exist: /var/www/pmapper-3.2-beta3
|
||||
# failJSON: { "time": "2014-01-03T09:33:53", "match": true , "host": "46.23.77.174" }
|
||||
[Fri Jan 03 09:33:53 2014] [error] [client 46.23.77.174] File does not exist: /var/www/v-webmail
|
||||
# failJSON: { "time": "2014-01-03T09:34:15", "match": true , "host": "46.23.77.174" }
|
||||
[Fri Jan 03 09:34:15 2014] [error] [client 46.23.77.174] File does not exist: /var/www/vwebmail
|
||||
# failJSON: { "time": "2014-01-03T09:35:47", "match": true , "host": "46.23.77.174" }
|
||||
[Fri Jan 03 09:35:47 2014] [error] [client 46.23.77.174] File does not exist: /var/www/webmail
|
||||
# failJSON: { "time": "2013-12-23T21:21:39", "match": true , "host": "183.60.244.49" }
|
||||
[Mon Dec 23 21:21:39 2013] [error] [client 183.60.244.49] File does not exist: /var/www/extmail, referer: http://www.baidu.com
|
||||
# failJSON: { "time": "2013-12-23T21:21:44", "match": true , "host": "183.60.244.49" }
|
||||
[Mon Dec 23 21:21:44 2013] [error] [client 183.60.244.49] File does not exist: /var/www/extmail, referer: http://www.baidu.com
|
||||
# failJSON: { "time": "2013-12-23T21:21:47", "match": true , "host": "183.60.244.49" }
|
||||
[Mon Dec 23 21:21:47 2013] [error] [client 183.60.244.49] File does not exist: /var/www/mails, referer: http://www.baidu.com
|
||||
# failJSON: { "time": "2013-12-23T21:22:00", "match": true , "host": "183.60.244.49" }
|
||||
[Mon Dec 23 21:22:00 2013] [error] [client 183.60.244.49] File does not exist: /var/www/extmail, referer: http://www.baidu.com
|
||||
# failJSON: { "time": "2013-12-23T21:22:16", "match": true , "host": "183.60.244.49" }
|
||||
[Mon Dec 23 21:22:16 2013] [error] [client 183.60.244.49] File does not exist: /var/www/phpmyadmin, referer: http://www.baidu.com
|
||||
|
||||
# failJSON: { "time": "2014-01-03T14:50:39", "match": false , "host": "92.43.20.165" }
|
||||
[Fri Jan 03 14:50:39 2014] [error] [client 92.43.20.165] script '/var/www/forum/mail.php' not found or unable to stat
|
||||
|
||||
# failJSON: { "time": "2014-12-06T09:29:34", "match": false , "host": "122.49.201.178" }
|
||||
[Fri Dec 06 09:29:34 2013] [error] [client 122.49.201.178] client denied by server configuration: /var/www/webmail/.htaccess
|
||||
@@ -0,0 +1,7 @@
|
||||
# Apache 2.2
|
||||
# failJSON: { "time": "2015-01-31T14:29:44", "match": true, "host": "66.249.66.1" }
|
||||
66.249.66.1 - - - [31/Jan/2015:14:29:44 ] fail2ban.org "GET / HTTP/1.1" 200 814 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" + 293 1149 546
|
||||
# failJSON: { "time": "2015-01-31T14:29:44", "match": false, "host": "51.159.55.100" }
|
||||
51.159.55.100 - - - [31/Jan/2015:14:29:44 ] fail2ban.org "GET / HTTP/1.1" 200 814 "-" "NOT A __GOOGLE_BOT__" + 293 1149 546
|
||||
# failJSON: { "time": "2024-08-18T22:08:39", "match": true , "host": "192.0.2.222", "desc": "vhost in accesslog, gh-1594" }
|
||||
www.sitename.com 192.0.2.222 - - [18/Aug/2024:21:08:39 +0100] "GET / HTTP/1.1" 403 332 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
|
||||
11
fail2ban-master/fail2ban/tests/files/logs/apache-modsecurity
Normal file
11
fail2ban-master/fail2ban/tests/files/logs/apache-modsecurity
Normal file
@@ -0,0 +1,11 @@
|
||||
# failJSON: { "time": "2013-12-23T13:12:31", "match": true , "host": "173.255.225.101" }
|
||||
[Mon Dec 23 13:12:31 2013] [error] [client 173.255.225.101] ModSecurity: [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_21_protocol_anomalies.conf"] [line "47"] [id "960015"] [rev "1"] [msg "Request Missing an Accept Header"] [severity "NOTICE"] [ver "OWASP_CRS/2.2.8"] [maturity "9"] [accuracy "9"] [tag "OWASP_CRS/PROTOCOL_VIOLATION/MISSING_HEADER_ACCEPT"] [tag "WASCTC/WASC-21"][tag "OWASP_TOP_10/A7"] [tag "PCI/6.5.10"] Access denied with code 403 (phase 2). Operator EQ matched 0 at REQUEST_HEADERS. [hostname "www.mysite.net"] [uri "/"] [unique_id "Urf@f12qgHIAACrFOlgAAABA"]
|
||||
|
||||
# failJSON: { "time": "2013-12-28T09:18:05", "match": true , "host": "32.65.254.69", "desc": "additional entry (and exact one space)" }
|
||||
[Sat Dec 28 09:18:05 2013] [error] [client 32.65.254.69] ModSecurity: [file "/etc/httpd/modsecurity.d/10_asl_rules.conf"] [line "635"] [id "340069"] [rev "4"] [msg "Atomicorp.com UNSUPPORTED DELAYED Rules: Web vulnerability scanner"] [severity "CRITICAL"] Access denied with code 403 (phase 2). Pattern match "(?:nessus(?:_is_probing_you_|test)|^/w00tw00t\\\\.at\\\\.)" at REQUEST_URI. [hostname "192.81.249.191"] [uri "/w00tw00t.at.blackhats.romanian.anti-sec:)"] [unique_id "4Q6RdsBR@b4AAA65LRUAAAAA"]
|
||||
|
||||
# failJSON: { "time": "2018-09-28T09:18:06", "match": true , "host": "192.0.2.1", "desc": "two client entries in message (gh-2247)" }
|
||||
[Sat Sep 28 09:18:06 2018] [error] [client 192.0.2.1:55555] [client 192.0.2.1] ModSecurity: [file "/etc/httpd/modsecurity.d/10_asl_rules.conf"] [line "635"] [id "340069"] [rev "4"] [msg "Atomicorp.com UNSUPPORTED DELAYED Rules: Web vulnerability scanner"] [severity "CRITICAL"] Access denied with code 403 (phase 2). Pattern match "(?:nessus(?:_is_probing_you_|test)|^/w00tw00t\\\\.at\\\\.)" at REQUEST_URI. [hostname "192.81.249.191"] [uri "/w00tw00t.at.blackhats.romanian.anti-sec:)"] [unique_id "4Q6RdsBR@b4AAA65LRUAAAAA"]
|
||||
|
||||
# failJSON: { "time": "2020-05-09T00:35:52", "match": true , "host": "192.0.2.2", "desc": "new format - apache 2.4 and php-fpm (gh-2717)" }
|
||||
[Sat May 09 00:35:52.389262 2020] [:error] [pid 22406:tid 139985298601728] [client 192.0.2.2:47762] [client 192.0.2.2] ModSecurity: Access denied with code 401 (phase 2). Operator EQ matched 1 at IP:blocked. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_wp_login.conf"] [line "14"] [id "500000"] [msg "Ip address blocked for 15 minutes, more than 5 login attempts in 3 minutes."] [hostname "example.com"] [uri "/wp-login.php"] [unique_id "XrYlGL5IY3I@EoLOgAAAA8"], referer: https://example.com/wp-login.php
|
||||
6
fail2ban-master/fail2ban/tests/files/logs/apache-nohome
Normal file
6
fail2ban-master/fail2ban/tests/files/logs/apache-nohome
Normal file
@@ -0,0 +1,6 @@
|
||||
# Apache 2.2
|
||||
# failJSON: { "time": "2013-06-01T11:23:08", "match": true , "host": "1.2.3.4" }
|
||||
[Sat Jun 01 11:23:08 2013] [error] [client 1.2.3.4] File does not exist: /xxx/~
|
||||
# Apache 2.4
|
||||
# failJSON: { "time": "2013-06-27T11:55:44", "match": true , "host": "192.0.2.12" }
|
||||
[Thu Jun 27 11:55:44.569531 2013] [core:info] [pid 4101:tid 2992634688] [client 192.0.2.12:46652] AH00128: File does not exist: /xxx/~
|
||||
28
fail2ban-master/fail2ban/tests/files/logs/apache-noscript
Normal file
28
fail2ban-master/fail2ban/tests/files/logs/apache-noscript
Normal file
@@ -0,0 +1,28 @@
|
||||
# failJSON: { "time": "2013-06-09T07:57:47", "match": true , "host": "192.0.43.10" }
|
||||
[Sun Jun 09 07:57:47 2013] [error] [client 192.0.43.10] script '/usr/lib/cgi-bin/gitweb.cgiwp-login.php' not found or unable to stat
|
||||
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
|
||||
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] File does not exist: /home/southern/public_html/azenv.php
|
||||
|
||||
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
|
||||
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php
|
||||
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
|
||||
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php5
|
||||
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
|
||||
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php-cgi
|
||||
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
|
||||
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php.cgi
|
||||
# failJSON: { "time": "2008-07-22T06:48:30", "match": true , "host": "198.51.100.86" }
|
||||
[Tue Jul 22 06:48:30 2008] [error] [client 198.51.100.86] script not found or unable to stat: /home/e-smith/files/ibays/Primary/cgi-bin/php4
|
||||
# apache 2.4
|
||||
# failJSON: { "time": "2013-12-23T07:49:01", "match": true , "host": "204.232.202.107" }
|
||||
[Mon Dec 23 07:49:01.981912 2013] [:error] [pid 3790] [client 204.232.202.107:46301] script '/var/www/timthumb.php' not found or unable to stat
|
||||
# failJSON: { "time": "2018-03-11T08:56:20", "match": true , "host": "192.0.2.106", "desc": "php-fpm error" }
|
||||
[Sun Mar 11 08:56:20.913548 2018] [proxy_fcgi:error] [pid 742:tid 140142593419008] [client 192.0.2.106:50900] AH01071: Got error 'Primary script unknown\n'
|
||||
# failJSON: { "time": "2019-07-09T14:27:42", "match": true , "host": "127.0.0.1", "desc": "script unknown, without \n (gh-2466)" }
|
||||
[Tue Jul 09 14:27:42.650548 2019] [proxy_fcgi:error] [pid 22075:tid 140322524440320] [client 127.0.0.1] AH01071: Got error 'Primary script unknown'
|
||||
|
||||
# failJSON: { "time": "2020-08-11T08:56:17", "match": true , "host": "192.0.2.1", "desc": "script not found with AH02811 and cgi-bin path segment in script (gh-2805)" }
|
||||
[Tue Aug 11 08:56:17.580412 2020] [cgi:error] [pid 27550:tid 140110750279424] [client 192.0.2.1:18071] AH02811: script not found or unable to stat: /usr/lib/cgi-bin/kerbynet
|
||||
|
||||
# failJSON: { "time": "2024-12-18T23:58:03", "match": true , "host": "192.0.2.74", "desc": "script not found, changed log-format with stderr from (gh-3900)" }
|
||||
[Wed Dec 18 23:58:03.148113 2024] [cgi:error] [pid 16720:tid 1878] [client 192.0.2.74:35092] AH02811: stderr from /usr/lib/cgi-bin/luci: script not found or unable to stat
|
||||
35
fail2ban-master/fail2ban/tests/files/logs/apache-overflows
Normal file
35
fail2ban-master/fail2ban/tests/files/logs/apache-overflows
Normal file
@@ -0,0 +1,35 @@
|
||||
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=574182
|
||||
# failJSON: { "time": "2010-03-16T15:39:29", "match": true , "host": "58.179.109.179" }
|
||||
[Tue Mar 16 15:39:29 2010] [error] [client 58.179.109.179] Invalid URI in request \xf9h\xa9\xf3\x88\x8cXKj \xbf-l*4\x87n\xe4\xfe\xd4\x1d\x06\x8c\xf8m\\rS\xf6n\xeb\x8
|
||||
# failJSON: { "time": "2010-03-15T15:44:47", "match": true , "host": "121.222.2.133" }
|
||||
[Mon Mar 15 15:44:47 2010] [error] [client 121.222.2.133] Invalid URI in request n\xed*\xbe*\xab\xefd\x80\xb5\xae\xf6\x01\x10M?\xf2\xce\x13\x9c\xd7\xa0N\xa7\xdb%0\xde\xe0\xfc\xd2\xa0\xfe\xe9w\xee\xc4`v\x9b[{\x0c:\xcb\x93\xc6\xa0\x93\x9c`l\\\x8d\xc9
|
||||
# failJSON: { "time": "2010-03-15T16:04:06", "match": true , "host": "192.0.2.1", "desc": "AH00126 failure, gh-2908" }
|
||||
[Sat Mar 15 16:04:06.105212 2010] [core:error] [pid 17408] [client 192.0.2.1:55280] AH00126: Invalid URI in request GET /static/../../../a/../../../../etc/passwd HTTP/1.1
|
||||
|
||||
# http://forum.nconf.org/viewtopic.php?f=14&t=427&p=1488
|
||||
# failJSON: { "time": "2010-07-30T11:23:54", "match": true , "host": "10.85.6.69" }
|
||||
[Fri Jul 30 11:23:54 2010] [error] [client 10.85.6.69] request failed: URI too long (longer than 8190)
|
||||
# failJSON: { "time": "2010-10-27T23:16:37", "match": true , "host": "187.117.240.164" }
|
||||
[Wed Oct 27 23:16:37 2010] [error] [client 187.117.240.164] Invalid URI in request x\xb2\xa1:SMl\xcc{\xfd"\xd1\x91\x84!d\x0e~\xf6:\xfbVu\xdf\xc3\xdb[\xa9\xfe\xd3lpz\x92\xbf\x9f5\xa3\xbbvF\xbc\xee\x1a\xb1\xb0\xf8K\xecE\xbc\xe8r\xacx=\xc7>\xb5\xbd\xa3\xda\xe9\xf09\x95"fd\x1c\x05\x1c\xd5\xf3#:\x91\xe6WE\xdb\xadN;k14;\xdcr\xad\x9e\xa8\xde\x95\xc3\xebw\xa0\xb1N\x8c~\xf1\xcfSY\xd5zX\xd7\x0f\vH\xe4\xb5(\xcf,3\xc98\x19\xefYq@\xd2I\x96\xfb\xc7\xa9\xae._{S\xd1\x9c\xad\x17\xdci\x9b\xca\x93\xafSM\xb8\x99\xd9|\xc2\xd8\xc9\xe7\xe9O\x99\xad\x19\xc3V]\xcc\xddR\xf7$\xaa\xb8\x18\xe0f\xb8\xff
|
||||
|
||||
|
||||
# Could be apache-2.2 or earlier
|
||||
# http://www.aota.net/forums/showthread.php?t=15796
|
||||
# failJSON: { "time": "2003-11-14T16:11:55", "match": true , "host": "1.2.3.4" }
|
||||
[Fri Nov 14 16:11:55 2003] [error] [client 1.2.3.4] request failed: erroneous characters after protocol string: User-Agent: Mozilla/5.0 (Windows; U; Win98; en-US; m18) Gecko/20001108 Netscape6/6.0
|
||||
|
||||
# http://forum.directadmin.com/showthread.php?t=22412
|
||||
# failJSON: { "time": "2007-11-15T03:09:59", "match": true , "host": "89.189.71.87" }
|
||||
[Thu Nov 15 03:09:59 2007] [error] [client 89.189.71.87] Invalid method in request NOOP
|
||||
|
||||
# https://issues.apache.org/bugzilla/show_bug.cgi?id=46123
|
||||
# failJSON: { "time": "2008-10-29T11:55:14", "match": true , "host": "127.0.0.1" }
|
||||
[Wed Oct 29 11:55:14 2008] [error] [client 127.0.0.1] Invalid method in request \x16\x03\x01 - possible attempt to establish SSL connection when the server isn't expecting it
|
||||
|
||||
# failJSON: { "time": "2024-06-26T05:20:26", "match": true , "host": "192.0.2.39", "desc": "AH10244: invalid URI path, gh-3778" }
|
||||
[Wed Jun 26 05:20:26.182799 2024] [core:error] [pid 2928] [client 192.0.2.39:37924] AH10244: invalid URI path (/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh)
|
||||
|
||||
# failJSON: { "time": "2024-12-18T15:23:15", "match": true , "host": "192.0.2.74", "desc": "coverage for another log-format (gh-3900)" }
|
||||
[Wed Dec 18 15:23:15.495667 2024] [core:error] [pid 1672:tid 1839] [client 192.0.2.74:39140] AH10244: invalid URI path (/cgi-bin/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/%%32%65%%32%65/bin/sh)
|
||||
# failJSON: { "time": "2024-12-18T15:23:16", "match": true , "host": "192.0.2.74", "desc": "coverage for another log-format (gh-3900)" }
|
||||
[Wed Dec 18 15:23:16.304454 2024] [core:error] [pid 1673:tid 1845] [client 192.0.2.74:35446] AH10244: invalid URI path (/cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh)
|
||||
2
fail2ban-master/fail2ban/tests/files/logs/apache-pass
Normal file
2
fail2ban-master/fail2ban/tests/files/logs/apache-pass
Normal file
@@ -0,0 +1,2 @@
|
||||
# failJSON: { "time": "2013-06-27T11:55:44", "match": true , "host": "192.0.2.12" }
|
||||
192.0.2.12 - user1 [27/Jun/2013:11:55:44] "GET /knocking/ HTTP/1.1" 200 266 "http://domain.net/hello-world/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:40.0) Gecko/20100101 Firefox/40.0"
|
||||
@@ -0,0 +1,4 @@
|
||||
# failJSON: { "time": "2014-09-25T09:27:18", "match": true , "host": "89.207.132.76" }
|
||||
[Thu Sep 25 09:27:18.813902 2014] [cgi:error] [pid 16860] [client 89.207.132.76:59635] AH01215: /bin/bash: warning: HTTP_TEST: ignoring function definition attempt
|
||||
# failJSON: { "time": "2014-09-25T09:29:56", "match": true , "host": "162.247.73.206" }
|
||||
[Thu Sep 25 09:29:56.141832 2014] [cgi:error] [pid 16864] [client 162.247.73.206:41273] AH01215: /bin/bash: error importing function definition for `HTTP_TEST'
|
||||
44
fail2ban-master/fail2ban/tests/files/logs/assp
Normal file
44
fail2ban-master/fail2ban/tests/files/logs/assp
Normal file
@@ -0,0 +1,44 @@
|
||||
# failJSON: { "time": "2013-04-07T07:08:36", "match": true , "host": "68.171.223.68" }
|
||||
Apr-07-13 07:08:36 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol;
|
||||
# failJSON: { "time": "2013-04-07T07:08:36", "match": true , "host": "68.171.223.68" }
|
||||
Apr-07-13 07:08:36 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol;
|
||||
# failJSON: { "time": "2013-04-07T07:10:37", "match": true , "host": "68.171.223.68" }
|
||||
Apr-07-13 07:10:37 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol;
|
||||
# failJSON: { "time": "2013-04-07T07:12:37", "match": true , "host": "68.171.223.68" }
|
||||
Apr-07-13 07:12:37 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol;
|
||||
# failJSON: { "time": "2013-04-07T07:14:36", "match": true , "host": "68.171.223.68" }
|
||||
Apr-07-13 07:14:36 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol;
|
||||
# failJSON: { "time": "2013-04-27T02:25:09", "match": true , "host": "217.194.197.97" }
|
||||
Apr-27-13 02:25:09 Blocking 217.194.197.97 - too much AUTH errors (8);
|
||||
# failJSON: { "time": "2013-04-27T02:25:09", "match": true , "host": "217.194.197.97" }
|
||||
Apr-27-13 02:25:09 Blocking 217.194.197.97 - too much AUTH errors (9);
|
||||
# failJSON: { "time": "2013-04-27T02:25:09", "match": true , "host": "217.194.197.97" }
|
||||
Apr-27-13 02:25:09 Blocking 217.194.197.97 - too much AUTH errors (10);
|
||||
# failJSON: { "time": "2013-04-27T02:25:10", "match": true , "host": "217.194.197.97" }
|
||||
Apr-27-13 02:25:10 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6;
|
||||
# failJSON: { "time": "2013-04-27T02:25:10", "match": true , "host": "217.194.197.97" }
|
||||
Apr-27-13 02:25:10 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6;
|
||||
# failJSON: { "time": "2013-04-27T02:25:10", "match": true , "host": "217.194.197.97" }
|
||||
Apr-27-13 02:25:10 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6;
|
||||
# failJSON: { "time": "2013-04-27T02:25:11", "match": true , "host": "217.194.197.97" }
|
||||
Apr-27-13 02:25:11 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6;
|
||||
# failJSON: { "time": "2016-07-29T16:49:52", "match": true , "host": "0.0.0.0" }
|
||||
Jul-29-16 16:49:52 m1-25391-06124 [Worker_1] [TLS-out] [RelayAttempt] 0.0.0.0 <user@example.com> to: user@example.org relay attempt blocked for: someone@example.org
|
||||
# failJSON: { "time": "2016-07-30T17:07:25", "match": true , "host": "0.0.0.0" }
|
||||
Jul-30-16 17:07:25 [Worker_1] [TLS-out] 0.0.0.0 [SMTP Error] 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6
|
||||
# failJSON: { "time": "2016-07-30T17:11:05", "match": true , "host": "0.0.0.0" }
|
||||
Jul-30-16 17:11:05 m1-13060-05386 [Worker_1] [TLS-out] 0.0.0.0 [SMTP Error] 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6
|
||||
# failJSON: { "time": "2016-07-31T06:45:59", "match": true , "host": "0.0.0.0" }
|
||||
Jul-31-16 06:45:59 [Worker_1] [TLS-in] [TLS-out] 0.0.0.0 [SMTP Error] 535 5.7.8 Error: authentication failed:
|
||||
# failJSON: { "time": "2016-01-05T08:38:49", "match": true , "host": "0.0.0.0" }
|
||||
Jan-05-16 08:38:49 m1-01129-09140 [Worker_1] [TLS-in] [TLS-out] [RelayAttempt] 0.0.0.0 <user@example.com> relay attempt blocked for (parsing): <user2@example>
|
||||
# failJSON: { "time": "2016-06-12T16:43:37", "match": true , "host": "0.0.0.0" }
|
||||
Jun-12-16 16:43:37 m1-64217-12013 [Worker_1] [TLS-in] [TLS-out] [RelayAttempt] 0.0.0.0 <user@example.com> to: user2@example.com relay attempt blocked for (parsing): <a.notheruser69@example.c>
|
||||
# failJSON: { "time": "2016-01-22T22:25:51", "match": true , "host": "0.0.0.0" }
|
||||
Jan-22-16 22:25:51 [Worker_1] [TLS-out] 0.0.0.0 [SMTP Error] 535 5.7.8 Error: authentication failed: Invalid authentication mechanism
|
||||
# failJSON: { "time": "2016-03-19T13:42:20", "match": true , "host": "0.0.0.0" }
|
||||
Mar-19-16 13:42:20 [Worker_1] [TLS-out] 0.0.0.0 [SMTP Error] 535 5.7.8 Error: authentication failed: Invalid base64 data in continued response
|
||||
# failJSON: { "time": "2016-07-18T16:54:21", "match": true , "host": "0.0.0.0" }
|
||||
Jul-18-16 16:54:21 [Worker_2] [TLS-out] 0.0.0.0 [SMTP Error] 535 5.7.8 Error: authentication failed: Connection lost to authentication server
|
||||
# failJSON: { "time": "2016-07-18T17:14:23", "match": true , "host": "0.0.0.0" }
|
||||
Jul-18-16 17:14:23 m1-76453-02949 [Worker_1] [TLS-out] 0.0.0.0 [SMTP Error] 535 5.7.8 Error: authentication failed: Connection lost to authentication server
|
||||
131
fail2ban-master/fail2ban/tests/files/logs/asterisk
Normal file
131
fail2ban-master/fail2ban/tests/files/logs/asterisk
Normal file
@@ -0,0 +1,131 @@
|
||||
# Sample log files for asterisk
|
||||
# failJSON: { "time": "2012-02-13T17:21:54", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:21:54] NOTICE[1638] chan_sip.c: Registration from '<sip:301@example.com>' failed for '1.2.3.4' - Wrong password
|
||||
# failJSON: { "time": "2012-02-13T17:18:22", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:18:22] NOTICE[1638] chan_sip.c: Registration from '<sip:301@example.com>' failed for '1.2.3.4' - No matching peer found
|
||||
# failJSON: { "time": "2012-02-13T17:21:21", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:21:21] NOTICE[1638] chan_sip.c: Registration from '<sip:301@example.com>' failed for '1.2.3.4' - Username/auth name mismatch
|
||||
# failJSON: { "time": "2012-02-13T17:32:01", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:32:01] NOTICE[1638] chan_sip.c: Registration from '<sip:301@example.com>' failed for '1.2.3.4' - Device does not match ACL
|
||||
# failJSON: { "time": "2012-02-13T17:34:10", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:34:10] NOTICE[1638] chan_sip.c: Registration from '<sip:301@example.com>' failed for '1.2.3.4' - Peer is not supposed to register
|
||||
# failJSON: { "time": "2012-02-13T17:36:23", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:36:23] NOTICE[1638] chan_sip.c: Registration from '<sip:301@example.com>' failed for '1.2.3.4' - ACL error (permit/deny)
|
||||
# failJSON: { "time": "2012-02-13T17:53:59", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:53:59] NOTICE[1638] chan_iax2.c: Host 1.2.3.4 failed to authenticate as 'Fail2ban'
|
||||
# failJSON: { "time": "2012-02-13T17:39:20", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:39:20] NOTICE[1638] chan_iax2.c: No registration for peer 'Fail2ban' (from 1.2.3.4)
|
||||
# failJSON: { "time": "2012-02-13T17:44:26", "match": true , "host": "1.2.3.4" }
|
||||
[2012-02-13 17:44:26] NOTICE[1638] chan_iax2.c: Host 1.2.3.4 failed MD5 authentication for 'Fail2ban' (e7df7cd2ca07f4f1ab415d457a6e1c13 != 53ac4bc41ee4ec77888ed4aa50677247)
|
||||
# failJSON: { "time": "2013-02-05T23:44:42", "match": true , "host": "1.2.3.4" }
|
||||
[2013-02-05 23:44:42] NOTICE[436][C-00000fa9] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '0972598285108' rejected because extension not found in context 'default'.
|
||||
# failJSON: { "time": "2005-01-18T17:39:50", "match": true , "host": "1.2.3.4" }
|
||||
[Jan 18 17:39:50] NOTICE[12049]: res_pjsip_session.c:2337 new_invite: Call from 'anonymous' (TCP:[1.2.3.4]:61470) to extension '9011+442037690237' rejected because extension not found in context 'default'.
|
||||
# failJSON: { "time": "2005-06-12T15:13:54", "match": true , "host": "1.2.3.4" }
|
||||
[Jun 12 15:13:54] NOTICE[3980] res_pjsip_session.c: anonymous: Call (UDP:1.2.3.4:65049) to extension '001447441452805' rejected because extension not found in context 'inbound-foo-bar'.
|
||||
# failJSON: { "time": "2013-03-26T15:47:54", "match": true , "host": "1.2.3.4" }
|
||||
[2013-03-26 15:47:54] NOTICE[1237] chan_sip.c: Registration from '"100"sip:100@1.2.3.4' failed for '1.2.3.4:23930' - No matching peer found
|
||||
# failJSON: { "time": "2013-05-13T07:10:53", "match": true , "host": "1.2.3.4" }
|
||||
[2013-05-13 07:10:53] SECURITY[1204] res_security_log.c: SecurityEvent="InvalidAccountID",EventTV="1368439853-500975",Severity="Error",Service="SIP",EventVersion="1",AccountID="00972599580679",SessionID="0x7f8ecc0421f8",LocalAddress="IPV4/UDP/1.2.3.4/5060",RemoteAddress="IPV4/UDP/1.2.3.4/5070"
|
||||
# failJSON: { "time": "2013-06-10T18:15:03", "match": true , "host": "1.2.3.4" }
|
||||
[2013-06-10 18:15:03] NOTICE[2723] chan_sip.c: Registration from '"100"<sip:100@192.168.0.2:5060>' failed for '1.2.3.4' - Not a local domain
|
||||
# http://sourceforge.net/p/fail2ban/mailman/message/33603322/
|
||||
# failJSON: { "time": "2015-03-16T18:46:34", "match": true , "host": "192.168.2.102" }
|
||||
[2015-03-16 18:46:34] NOTICE[3453] chan_sip.c: hacking attempt detected '192.168.2.102'
|
||||
# failJSON: { "time": "2013-07-06T09:09:25", "match": true , "host": "141.255.164.106" }
|
||||
[2013-07-06 09:09:25] SECURITY[3308] res_security_log.c: SecurityEvent="InvalidPassword",EventTV="1373098165-824497",Severity="Error",Service="SIP",EventVersion="2",AccountID="972592891005",SessionID="0x88aab6c",LocalAddress="IPV4/UDP/92.28.73.180/5060",RemoteAddress="IPV4/UDP/141.255.164.106/5084",Challenge="41d26de5",ReceivedChallenge="41d26de5",ReceivedHash="7a6a3a2e95a05260aee612896e1b4a39"
|
||||
# failJSON: { "time": "2014-01-10T16:39:06", "match": true , "host": "50.30.42.14" }
|
||||
[2014-01-10 16:39:06] SECURITY[1503] res_security_log.c: SecurityEvent="FailedACL",EventTV="1389368346-880526",Severity="Error",Service="SIP",EventVersion="1",AccountID="",SessionID="0x7ff408103b18",LocalAddress="IPV4/UDP/83.11.20.23/5060",RemoteAddress="IPV4/UDP/50.30.42.14/5066",ACLName="domain_must_match"
|
||||
|
||||
# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "192.168.55.152" }
|
||||
[2013-11-11 14:33:38] WARNING[6756][C-0000001d] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152"
|
||||
# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "192.168.55.152" }
|
||||
[2013-11-11 14:33:38] WARNING[8447][C-00000244] Ext. s: "Rejecting unknown SIP connection from 192.168.55.152:52126"
|
||||
# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2001:db8::1" }
|
||||
[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from 2001:db8::1"
|
||||
# failJSON: { "time": "2013-11-11T14:33:38", "match": true , "host": "2001:db8::1" }
|
||||
[2013-11-11 14:33:38] WARNING[12124][C-00000001] Ext. s: "Rejecting unknown SIP connection from [2001:db8::1]:5060"
|
||||
|
||||
# failJSON: { "time": "2004-11-04T18:30:40", "match": true , "host": "192.168.200.100" }
|
||||
Nov 4 18:30:40 localhost asterisk[32229]: NOTICE[32257]: chan_sip.c:23417 in handle_request_register: Registration from '<sip:301@example.com>' failed for '192.168.200.100:36998' - Wrong password
|
||||
# failJSON: { "time": "2016-08-19T11:11:26", "match": true , "host": "192.0.2.1", "desc": "Another log_prefix used (` in` should be optional)" }
|
||||
[2016-08-19 11:11:26] NOTICE[12931]: chan_sip.c:28468 handle_request_register: Registration from 'sip:bob@192.0.2.1' failed for '192.0.2.1:42406' - Wrong password
|
||||
|
||||
# failed authentication attempt on INVITE using PJSIP
|
||||
# failJSON: { "time": "2015-05-24T08:42:16", "match": true, "host": "10.250.251.252" }
|
||||
[2015-05-24 08:42:16] SECURITY[4583] res_security_log.c: SecurityEvent="ChallengeResponseFailed",EventTV="2015-05-24T08:42:16.296+0300",Severity="Error",Service="PJSIP",EventVersion="1",AccountID="<unknown>",SessionID="17a483d-eb8cc0-556164ab@1.2.3.4",LocalAddress="IPV4/UDP/1.2.3.4/5060",RemoteAddress="IPV4/UDP/10.250.251.252/5060",Challenge="1432446136/6d16ccf29ff59d423c6d548af00bf9b4",Response="849dfcf133d8156f77ef11a9194119df",ExpectedResponse=""
|
||||
# failJSON: { "time": "2019-09-20T19:12:43", "match": true, "host": "192.0.2.2", "desc": "TLS before address, gh-2531" }
|
||||
[2019-09-20 19:12:43] SECURITY[1724] res_security_log.c: SecurityEvent="ChallengeResponseFailed",EventTV="2019-09-20T19:12:43.659-0500",Severity="Error",Service="PJSIP",EventVersion="1",AccountID="<unknown>",SessionID="3686a690-f8ccac10-5677c924-51b54926",LocalAddress="IPV4/TLS/1.2.3.4/5062",RemoteAddress="IPV4/TLS/192.0.2.2/30245",Challenge="1569024763/510a7e1ed568b93ce283d1b16bc17a15",Response="8e181448412899ccb20ea585efc8bab0",ExpectedResponse=""
|
||||
|
||||
# SessionID may contain any special characters and spaces
|
||||
# failJSON: { "time": "2015-05-25T07:19:19", "match": true, "host": "10.250.251.252" }
|
||||
[2015-05-25 07:19:19] SECURITY[6988] res_security_log.c: SecurityEvent="InvalidAccountID",EventTV="2015-05-25T07:19:19.015+0300",Severity="Error",Service="PJSIP",EventVersion="1",AccountID="70000180",SessionID="!@#$%^& *(}((')[ -+"++",LocalAddress="IPV4/UDP/1.2.3.4/5060",RemoteAddress="IPV4/UDP/10.250.251.252/5061"
|
||||
|
||||
# SessionID here start with '",LocalAddress' and ends with '5.6.7.8/1111"'
|
||||
# failJSON: { "time": "2015-05-25T07:21:48", "match": true, "host": "10.250.251.252" }
|
||||
[2015-05-25 07:21:48] SECURITY[6988] res_security_log.c: SecurityEvent="InvalidAccountID",EventTV="2015-05-25T07:21:48.275+0300",Severity="Error",Service="PJSIP",EventVersion="1",AccountID="70000180",SessionID="",LocalAddress="IPV4/UDP/127.0.0.1/5060",RemoteAddress="IPV4/UDP/5.6.7.8/1111"",LocalAddress="IPV4/UDP/1.2.3.4/5060",RemoteAddress="IPV4/UDP/10.250.251.252/5061"
|
||||
|
||||
# match UTF-8 in SessionID
|
||||
# failJSON: { "time": "2015-05-25T07:52:36", "match": true, "host": "10.250.251.252" }
|
||||
[2015-05-25 07:52:36] SECURITY[6988] res_security_log.c: SecurityEvent="InvalidAccountID",EventTV="2015-05-25T07:52:36.888+0300",Severity="Error",Service="PJSIP",EventVersion="1",AccountID="70000180",SessionID="Негодяй",LocalAddress="IPV4/UDP/1.2.3.4/5060",RemoteAddress="IPV4/UDP/10.250.251.252/5061"
|
||||
|
||||
# match phone numbers with + symbol (and without number, or other context)
|
||||
# failJSON: { "time": "2016-01-28T10:22:27", "match": true , "host": "1.2.3.4" }
|
||||
[2016-01-28 10:22:27] NOTICE[3477][C-000003bb] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '++441772285411' rejected because extension not found in context 'default'.
|
||||
# failJSON: { "time": "2016-01-28T10:34:31", "match": true , "host": "1.2.3.4" }
|
||||
[2016-01-28 10:34:31] NOTICE[3477][C-000003c3] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '0+441772285407' rejected because extension not found in context 'default'.
|
||||
# failJSON: { "time": "2016-01-28T10:34:33", "match": true , "host": "1.2.3.4" }
|
||||
[2016-01-28 10:34:33] NOTICE[3477][C-000003c3] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '' rejected because extension not found in context 'my-context'.
|
||||
|
||||
# failJSON: { "time": "2016-05-15T22:53:00", "match": true , "host": "192.0.2.4" }
|
||||
[2016-05-15 22:53:00] SECURITY[26428] res_security_log.c: SecurityEvent="FailedACL",EventTV="2016-05-15T22:53:00.203+0300",Severity="Error",Service="AMI",EventVersion="1",AccountID="admin",SessionID="0x7fb580001518",LocalAddress="IPV4/TCP/0.0.0.0/5038",RemoteAddress="IPV4/TCP/192.0.2.4/62389",SessionTV="1970-01-01T03:00:00.000+0300"
|
||||
|
||||
# Failed authentication with pjsip on Asterisk 13+
|
||||
# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" }
|
||||
[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" <sip:1000@10.0.0.1>' failed for '1.2.3.4:48336' (callid: 276666022) - No matching endpoint found
|
||||
# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" }
|
||||
[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" <sip:1000@10.0.0.1>' failed for '1.2.3.4:48336' (callid: 276666022) - Not match Endpoint ACL
|
||||
# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" }
|
||||
[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" <sip:1000@10.0.0.1>' failed for '1.2.3.4:48336' (callid: 276666022) - Not match Endpoint Contact ACL
|
||||
# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" }
|
||||
[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" <sip:1000@10.0.0.1>' failed for '1.2.3.4:48336' (callid: 276666022) - Failed to authenticate
|
||||
# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" }
|
||||
[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" <sip:1000@10.0.0.1>' failed for '1.2.3.4:48336' (callid: 276666022) - Error to authenticate
|
||||
# failJSON: { "time": "2016-06-08T23:40:26", "match": true , "host": "2.3.4.5" }
|
||||
[2016-06-08 23:40:26] NOTICE[32497] res_pjsip/pjsip_distributor.c: Request from '"317" <sip:317@1.2.3.4>' failed for '2.3.4.5:5089' (callid: 206f178f-896564cb-57573f49@1.2.3.4) - No matching endpoint found
|
||||
# failJSON: { "time": "2017-12-14T22:18:00", "match": true , "host": "1.2.3.4" }
|
||||
[2017-12-14 22:18:00] NOTICE[1943] res_pjsip/pjsip_distributor.c: Request 'INVITE' from '<sip:'or''='@4.5.6.7>' failed for '1.2.3.4:43678' (callid: UmOkE9yQPGOsF3Az24YTRe..) - No matching endpoint found
|
||||
|
||||
# failJSON: { "time": "2016-06-09T00:01:02", "match": true , "host": "192.0.2.1" }
|
||||
[2016-06-09 00:01:02] NOTICE [22382] manager.c: 192.0.2.1 failed to authenticate as 'admin'
|
||||
|
||||
# Check AMI logs
|
||||
# failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.4" }
|
||||
[2016-05-06 07:08:09] NOTICE[31554] manager.c: 192.0.2.4 tried to authenticate with nonexistent user 'opennms'
|
||||
# failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.5" }
|
||||
[2016-05-06 07:08:09] NOTICE[6772] manager.c: 192.0.2.5 failed to authenticate as 'Admin'
|
||||
|
||||
# PJSip Errors
|
||||
# failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.6" }
|
||||
[2016-05-06 07:08:09] NOTICE[17103] res_pjsip/pjsip_distributor.c: Request from '"test1" <sip:test1@2.3.4.5>' failed for '192.0.2.6:5678' (callid: deadbeef) - No matching endpoint found
|
||||
# failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.7", "desc": "Test for No matching endpoint found with retry counts (pattern 1)" }
|
||||
[2016-05-06 07:08:09] NOTICE[17103] res_pjsip/pjsip_distributor.c: Request 'INVITE' from '"test2" <sip:test2@3.4.5.6>' failed for '192.0.2.7:5679' (callid: cafebabe) - No matching endpoint found after 5 tries in 2.500 ms
|
||||
|
||||
# # FreePBX Warnings
|
||||
# #_dis_failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.4" }
|
||||
# [2016-05-06 07:08:09] WARNING[6410][C-00000bac] Ext. 50048943556071: Friendly Scanner from 192.0.2.4
|
||||
# #_dis_failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.5" }
|
||||
# [2016-05-06 07:08:09] WARNING[6410][C-00000bac] Ext. s: Friendly Scanner from 192.0.2.5
|
||||
# #_dis_failJSON: { "time": "2016-05-06T07:08:09", "match": true, "host": "192.0.2.6" }
|
||||
# [2016-05-06 07:08:09] WARNING[6410][C-00000bac] Ext. +012345: Friendly Scanner from 192.0.2.6
|
||||
# # Yes, this does have quotes around it.
|
||||
|
||||
# failJSON: { "time": "2005-03-01T15:35:53", "match": true , "host": "192.0.2.2", "desc": "log over remote syslog server" }
|
||||
Mar 1 15:35:53 pbx asterisk[2350]: WARNING[1195][C-00000b43]: Ext. s:6 in @ from-sip-external: "Rejecting unknown SIP connection from 192.0.2.2"
|
||||
|
||||
# filterOptions: [{"logtype": "journal", "test.prefix-line": "server asterisk[123]: "}]
|
||||
|
||||
# failJSON: { "match": true , "host": "192.0.2.1", "desc": "systemd-journal entry" }
|
||||
NOTICE[566]: chan_sip.c:28926 handle_request_register: Registration from '"28" <sip:28@127.0.0.100>' failed for '192.0.2.1:7998' - Wrong password
|
||||
# failJSON: { "match": true , "host": "192.0.2.2", "desc": "systemd-journal entry (with additional timestamp in message)" }
|
||||
[Mar 27 10:06:14] NOTICE[566]: chan_sip.c:28926 handle_request_register: Registration from '"1000" <sip:1000@127.0.0.100>' failed for '192.0.2.2:7998' - Wrong password
|
||||
11
fail2ban-master/fail2ban/tests/files/logs/bitwarden
Normal file
11
fail2ban-master/fail2ban/tests/files/logs/bitwarden
Normal file
@@ -0,0 +1,11 @@
|
||||
# failJSON: { "time": "2019-11-25T18:04:49", "match": true , "host": "192.168.0.16" }
|
||||
2019-11-26 01:04:49.008 +08:00 [WRN] Failed login attempt. 192.168.0.16
|
||||
|
||||
# failJSON: { "time": "2019-11-25T21:39:58", "match": true , "host": "192.168.0.21" }
|
||||
2019-11-25 21:39:58.464 +01:00 [WRN] Failed login attempt, 2FA invalid. 192.168.0.21
|
||||
|
||||
# failJSON: { "time": "2019-11-25T21:39:58", "match": true , "host": "192.168.0.21" }
|
||||
2019-11-25 21:39:58.464 +01:00 [Warning] Failed login attempt, 2FA invalid. 192.168.0.21
|
||||
|
||||
# failJSON: { "time": "2019-09-24T13:16:50", "match": true , "host": "192.168.0.23" }
|
||||
2019-09-24T13:16:50 e5a81dbf7fd1 Bitwarden-Identity[1]: [Bit.Core.IdentityServer.ResourceOwnerPasswordValidator] Failed login attempt. 192.168.0.23
|
||||
@@ -0,0 +1,3 @@
|
||||
Apr 2 17:52:55 pancake sshd[55657]: Invalid user oracle from 192.0.2.100
|
||||
Apr 2 17:53:01 pancake sshd[55657]: error: PAM: authentication error for illegal user oracle from example.com
|
||||
Apr 2 17:53:01 pancake sshd[55657]: Failed keyboard-interactive/pam for invalid user oracle from 192.0.2.100 port 48856 ssh2
|
||||
10
fail2ban-master/fail2ban/tests/files/logs/bsd/syslog-v.txt
Normal file
10
fail2ban-master/fail2ban/tests/files/logs/bsd/syslog-v.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Apr 2 17:51:27 <4.3> pancake sshd[55624]: error: PAM: authentication error for nick from example.com
|
||||
Apr 2 17:51:32 <4.6> pancake sshd[55628]: Invalid user r00t from 192.0.2.100
|
||||
Apr 2 17:51:33 <4.3> pancake sshd[55628]: error: PAM: authentication error for illegal user r00t from example.com
|
||||
Apr 2 17:51:33 <4.6> pancake sshd[55628]: Failed keyboard-interactive/pam for invalid user r00t from 192.0.2.100 port 46050 ssh2
|
||||
Apr 2 17:51:34 <4.3> pancake sshd[55628]: error: PAM: authentication error for illegal user r00t from example.com
|
||||
Apr 2 17:51:34 <4.6> pancake sshd[55628]: Failed keyboard-interactive/pam for invalid user r00t from 192.0.2.100 port 46050 ssh2
|
||||
Apr 2 17:51:36 <4.3> pancake sshd[55628]: error: PAM: authentication error for illegal user r00t from example.com
|
||||
Apr 2 17:51:36 <4.6> pancake sshd[55628]: Failed keyboard-interactive/pam for invalid user r00t from 192.0.2.100 port 46050 ssh2
|
||||
Apr 2 17:52:06 <4.6> pancake sshd[55647]: Invalid user oracle from 192.0.2.100
|
||||
Apr 2 17:52:07 <4.3> pancake sshd[55647]: error: PAM: authentication error for illegal user oracle from example.com
|
||||
@@ -0,0 +1,5 @@
|
||||
Mar 19 23:48:18 <auth.info> pancake sshd[55517]: Invalid user r00t from 183.60.159.20
|
||||
Mar 19 23:48:20 <auth.info> pancake sshd[55519]: Invalid user r00t from 183.60.159.20
|
||||
Mar 19 23:50:03 <auth.info> pancake sshd[55604]: Invalid user http from 183.60.159.20
|
||||
Mar 19 23:50:05 <auth.info> pancake sshd[55606]: Invalid user kylix from 183.60.159.20
|
||||
Mar 19 23:50:08 <auth.info> pancake sshd[55608]: Invalid user nagios from 183.60.159.20
|
||||
4
fail2ban-master/fail2ban/tests/files/logs/centreon
Normal file
4
fail2ban-master/fail2ban/tests/files/logs/centreon
Normal file
@@ -0,0 +1,4 @@
|
||||
# Access of unauthorized host in /var/log/centreon/login.log
|
||||
# failJSON: { "time": "2019-10-21T18:55:15", "match": true , "host": "50.97.225.132" }
|
||||
2019-10-21 18:55:15|-1|0|0|[WEB] [50.97.225.132] Authentication failed for 'admin' : password mismatch
|
||||
|
||||
4
fail2ban-master/fail2ban/tests/files/logs/counter-strike
Normal file
4
fail2ban-master/fail2ban/tests/files/logs/counter-strike
Normal file
@@ -0,0 +1,4 @@
|
||||
# failJSON: { "time": "2014-01-01T01:25:17", "match": true, "host": "31.29.29.89" }
|
||||
L 01/01/2014 - 01:25:17: Bad Rcon: "rcon 1146003691 "284" sv_contact "HLBrute 1.10"" from "31.29.29.89:57370"
|
||||
# failJSON: { "time": "2014-01-01T04:17:01", "match": true, "host": "105.158.241.147" }
|
||||
L 01/01/2014 - 04:17:01: Bad Rcon: "rcon 260639614 "admin" sv_contact "HLBrute 1.10"" from "105.158.241.147:53772"
|
||||
12
fail2ban-master/fail2ban/tests/files/logs/courier-auth
Normal file
12
fail2ban-master/fail2ban/tests/files/logs/courier-auth
Normal file
@@ -0,0 +1,12 @@
|
||||
# failJSON: { "time": "2005-04-23T21:59:01", "match": true , "host": "1.2.3.4" }
|
||||
Apr 23 21:59:01 dns2 imapd: LOGIN FAILED, user=sales@example.com, ip=[::ffff:1.2.3.4]
|
||||
# failJSON: { "time": "2005-04-23T21:59:38", "match": true , "host": "198.51.100.76" }
|
||||
Apr 23 21:59:38 dns2 pop3d: LOGIN FAILED, user=info@example.com, ip=[::ffff:198.51.100.76]
|
||||
# failJSON: { "time": "2004-11-13T08:11:53", "match": true , "host": "198.51.100.33" }
|
||||
Nov 13 08:11:53 server imapd-ssl: LOGIN FAILED, user=user@domain.tld, ip=[::ffff:198.51.100.33]
|
||||
# failJSON: { "time": "2005-04-17T19:17:11", "match": true , "host": "1.2.3.4" }
|
||||
Apr 17 19:17:11 SERVER courierpop3login: LOGIN FAILED, user=USER@EXAMPLE.org, ip=[::ffff:1.2.3.4]
|
||||
# failJSON: { "time": "2005-04-17T19:17:12", "match": true , "host": "192.0.2.4" }
|
||||
Apr 17 19:17:12 server imapd-ssl: LOGIN FAILED, method=PLAIN, ip=[::ffff:192.0.2.4]
|
||||
# failJSON: { "time": "2005-04-27T09:00:00", "match": true , "user": "tester", "host": "192.0.2.5" }
|
||||
Apr 27 09:00:00 servername imapd: LOGIN FAILED, user=tester, ip=[::ffff:192.0.2.5], port=[255]
|
||||
16
fail2ban-master/fail2ban/tests/files/logs/courier-smtp
Normal file
16
fail2ban-master/fail2ban/tests/files/logs/courier-smtp
Normal file
@@ -0,0 +1,16 @@
|
||||
# failJSON: { "time": "2005-04-10T03:47:57", "match": true , "host": "1.2.3.4" }
|
||||
Apr 10 03:47:57 web courieresmtpd: error,relay=::ffff:1.2.3.4,ident=tmf,from=<tmf@example.com>,to=<mailman-subscribe@example.com>: 550 User unknown.
|
||||
# failJSON: { "time": "2005-07-03T23:07:20", "match": true , "host": "1.2.3.4" }
|
||||
Jul 3 23:07:20 szerver courieresmtpd: error,relay=::ffff:1.2.3.4,msg="535 Authentication failed.",cmd: YWRvYmVhZG9iZQ==
|
||||
# failJSON: { "time": "2005-07-04T18:39:39", "match": true , "host": "1.2.3.4" }
|
||||
Jul 4 18:39:39 mail courieresmtpd: error,relay=::ffff:1.2.3.4,from=<picaro@astroboymail.com>,to=<user@update.net>: 550 User <benny> unknown
|
||||
# failJSON: { "time": "2005-07-06T03:42:28", "match": true , "host": "1.2.3.4" }
|
||||
Jul 6 03:42:28 whistler courieresmtpd: error,relay=::ffff:1.2.3.4,from=<>,to=<admin at memcpy>: 550 User unknown.
|
||||
# failJSON: { "time": "2004-11-21T23:16:17", "match": true , "host": "1.2.3.4" }
|
||||
Nov 21 23:16:17 server courieresmtpd: error,relay=::ffff:1.2.3.4,from=<>,to=<>: 550 User unknown.
|
||||
# failJSON: { "time": "2005-08-14T12:51:04", "match": true , "host": "1.2.3.4" }
|
||||
Aug 14 12:51:04 HOSTNAME courieresmtpd: error,relay=::ffff:1.2.3.4,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||
# failJSON: { "time": "2005-08-14T12:51:04", "match": true , "host": "1.2.3.4" }
|
||||
Aug 14 12:51:04 mail.server courieresmtpd[26762]: error,relay=::ffff:1.2.3.4,msg="535 Authentication failed.",cmd: AUTH PLAIN AAAAABBBBCCCCWxlZA== admin
|
||||
# failJSON: { "time": "2005-08-14T12:51:05", "match": true , "host": "192.0.2.3" }
|
||||
Aug 14 12:51:05 mail.server courieresmtpd[425070]: error,relay=::ffff:192.0.2.3,port=43632,msg="535 Authentication failed.",cmd: AUTH LOGIN PlcmSpIp@example.com
|
||||
21
fail2ban-master/fail2ban/tests/files/logs/cyrus-imap
Normal file
21
fail2ban-master/fail2ban/tests/files/logs/cyrus-imap
Normal file
@@ -0,0 +1,21 @@
|
||||
# failJSON: { "time": "2005-01-04T21:51:05", "match": true , "host": "127.0.0.1" }
|
||||
Jan 4 21:51:05 hostname cyrus/imap[5355]: badlogin: localhost.localdomain [127.0.0.1] plaintext cyrus@localdomain SASL(-13): authentication failure: checkpass failed
|
||||
# failJSON: { "time": "2005-01-04T21:51:05", "match": true , "host": "127.0.0.1", "desc": "For secure imaps" }
|
||||
Jan 4 21:51:05 hostname cyrus/imaps[5355]: badlogin: localhost.localdomain [127.0.0.1] plaintext cyrus@localdomain SASL(-13): authentication failure: checkpass failed
|
||||
# failJSON: { "time": "2005-02-20T17:23:32", "match": true , "host": "198.51.100.23" }
|
||||
Feb 20 17:23:32 domain cyrus/pop3[18635]: badlogin: localhost [198.51.100.23] plaintext administrator SASL(-13): authentication failure: checkpass failed
|
||||
# failJSON: { "time": "2005-02-20T17:23:32", "match": true , "host": "1.2.3.4" }
|
||||
Feb 20 17:23:32 cyrus/pop3[4297]: badlogin: example.com [1.2.3.4] plaintext mail0001 SASL(-13): authentication failure: checkpass failed
|
||||
# failJSON: { "time": "2005-06-08T18:11:13", "match": true , "host": "198.51.100.45" }
|
||||
Jun 8 18:11:13 lampserver imap[4480]: badlogin: example.com [198.51.100.45] DIGEST-MD5 [SASL(-13): authentication failure: client response doesn't match what we generated]
|
||||
# failJSON: { "time": "2004-12-21T10:01:57", "match": true , "host": "198.51.100.57" }
|
||||
Dec 21 10:01:57 hostname imapd[18454]: badlogin: example.com [198.51.100.57] CRAM-MD5 [SASL(-13): authentication failure: incorrect digest response]
|
||||
# failJSON: { "time": "2004-12-30T16:03:27", "match": true , "host": "1.2.3.4" }
|
||||
Dec 30 16:03:27 somehost imapd[2517]: badlogin: local-somehost[1.2.3.4] OTP [SASL(-13): authentication failure: External SSF not good enough]
|
||||
# failJSON: { "time": "2005-07-17T22:55:56", "match": true , "host": "1.2.3.4" }
|
||||
Jul 17 22:55:56 derry cyrus/imaps[7568]: badlogin: serafinat.xxxxxx [1.2.3.4] plain [SASL(-13): user not found: user: pressy@derry property: cmusaslsecretPLAIN not found in sasldb]
|
||||
# failJSON: { "time": "2005-07-18T16:46:42", "match": true , "host": "1.2.3.4" }
|
||||
Jul 18 16:46:42 derry cyrus/imaps[27449]: badlogin: serafinat.xxxxxx [1.2.3.4] PLAIN [SASL(-13): user not found: Password verification failed]
|
||||
|
||||
# failJSON: { "time": "2005-03-08T05:25:21", "match": true , "host": "192.0.2.4", "desc": "entry without loginname/hostname before IP" }
|
||||
Mar 8 05:25:21 host imap[22130]: badlogin: [192.0.2.4] plain [SASL(-13): authentication failure: Password verification failed]
|
||||
8
fail2ban-master/fail2ban/tests/files/logs/dante
Normal file
8
fail2ban-master/fail2ban/tests/files/logs/dante
Normal file
@@ -0,0 +1,8 @@
|
||||
# failJSON: { "time": "2005-04-14T15:35:03", "match": true , "host": "1.2.3.4" }
|
||||
Apr 14 15:35:03 vps111111 danted[17969]: info: block(1): tcp/accept ]: 1.2.3.4.50550 0.0.0.0.1080: error after reading 35 bytes in 0 seconds: could not access user "roooooooot"'s records in the system password file: no system error
|
||||
# failJSON: { "time": "2005-04-14T15:44:26", "match": true , "host": "1.2.3.4" }
|
||||
Apr 14 15:44:26 vps111111 danted[1846]: info: block(1): tcp/accept ]: 1.2.3.4.57178 0.0.0.0.1080: error after reading 18 bytes in 0 seconds: system password authentication failed for user "aland"
|
||||
# failJSON: { "time": "2005-04-14T15:44:26", "match": true , "host": "1.2.3.4" }
|
||||
Apr 14 15:44:26 vps111111 danted[1846]: info: block(1): tcp/accept ]: 1.2.3.4.57178 0.0.0.0.1080: error after reading 1 byte in 1 second: system password authentication failed for user "aland"
|
||||
# failJSON: { "time": "2005-04-14T15:44:27", "match": true , "host": "192.0.2.169", "desc": "pam auth failure, gh-3410" }
|
||||
Apr 14 15:44:27 srv danted[3374579]: info: block(1): tcp/accept ]: 192.0.2.169.8490 192.0.2.83.52483: error after reading 31 bytes in 2 seconds: pam_authenticate() for user "socks5_user" failed: Authentication failure
|
||||
14
fail2ban-master/fail2ban/tests/files/logs/directadmin
Normal file
14
fail2ban-master/fail2ban/tests/files/logs/directadmin
Normal file
@@ -0,0 +1,14 @@
|
||||
# failJSON: { "time": "2014-07-02T00:17:45", "match": true , "host": "3.2.1.4" }
|
||||
2014:07:02-00:17:45: '3.2.1.4' 2 failed login attempts. Account 'test'
|
||||
|
||||
# failJSON: { "time": "2014-07-02T13:07:40", "match": true , "host": "40.40.123.231" }
|
||||
2014:07:02-13:07:40: '40.40.123.231' 13 failed login attempts. Account 'admin'
|
||||
|
||||
# failJSON: { "time": "2014-07-02T13:07:50", "match": true , "host": "40.40.123.231" }
|
||||
2014:07:02-13:07:50: '40.40.123.231' 5 failed login attempt. Invalid account 'user%2Ename'
|
||||
|
||||
# failJSON: { "time": "2014-07-02T13:28:39", "match": false , "host": "12.12.123.231" }
|
||||
2014:07:02-13:28:39: '12.12.123.231' successful login to 'nobody' after 1 attempts
|
||||
|
||||
# failJSON: { "time": "2014-07-02T13:29:38", "match": true , "host": "1.2.3.4" }
|
||||
2014:07:02-13:29:38: '1.2.3.4' 2 failed login attempts. Account 'user' via 'admin'
|
||||
13
fail2ban-master/fail2ban/tests/files/logs/domino-smtp
Normal file
13
fail2ban-master/fail2ban/tests/files/logs/domino-smtp
Normal file
@@ -0,0 +1,13 @@
|
||||
# failJSON: { "time": "2005-07-03T23:07:20", "match": true , "host": "1.2.3.4" }
|
||||
03-07-2005 23:07:20 SMTP Server: Authentication failed for user postmaster ; connecting host 1.2.3.4
|
||||
# failJSON: { "time": "2014-06-22T09:56:12", "match": true , "host": "1.2.3.4" }
|
||||
[28325:00010-3735542592] 22-06-2014 09:56:12 smtp: postmaster [1.2.3.4] authentication failure using internet password
|
||||
# failJSON: { "time": "2014-09-08T06:14:27", "match": true , "host": "1.2.3.4" }
|
||||
08-09-2014 06:14:27 smtp: postmaster [1.2.3.4] authentication failure using internet password
|
||||
# failJSON: { "time": "2016-11-07T22:21:20", "match": true , "host": "1.2.3.4" }
|
||||
2016-11-07 22:21:20 smtp: postmaster [1.2.3.4] authentication failure using internet password
|
||||
|
||||
# failJSON: { "time": "2018-09-19T17:25:50", "match": true , "host": "192.0.2.1", "desc":"different log-format" }
|
||||
2018-09-19 17:25:50 SMTP Server [0D14:0027-1334] Authentication failed for user Bad Hacker ; connecting host [192.0.2.1]
|
||||
# failJSON: { "time": "2018-09-19T17:25:52", "match": true , "host": "192.0.2.2", "desc":"gh-2228, rejected for policy reasons" }
|
||||
2018-09-19 17:25:52 SMTP Server [000527:000013-0000001227564800] Connection from [192.0.2.2] rejected for policy reasons. IP address of connecting host not found in reverse DNS lookup.
|
||||
172
fail2ban-master/fail2ban/tests/files/logs/dovecot
Normal file
172
fail2ban-master/fail2ban/tests/files/logs/dovecot
Normal file
@@ -0,0 +1,172 @@
|
||||
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "80.187.101.33" }
|
||||
@400000004c91b044077a9e94 imap-login: Info: Aborted login (auth failed, 1 attempts): user=<martin@waschbuesch.de>, method=CRAM-MD5, rip=80.187.101.33, lip=80.254.129.240, TLS
|
||||
|
||||
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "176.61.140.224" }
|
||||
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=web rhost=176.61.140.224
|
||||
# Above example with injected rhost into ruser -- should not match for 1.2.3.4
|
||||
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "192.0.43.10" }
|
||||
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=rhost=1.2.3.4 rhost=192.0.43.10
|
||||
# failJSON: { "time": "2010-09-16T07:51:00", "match": true , "host": "176.61.140.225" }
|
||||
@400000004c91b044077a9e94 dovecot-auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=root rhost=176.61.140.225 user=root
|
||||
|
||||
# failJSON: { "time": "2004-12-12T11:19:11", "match": true , "host": "190.210.136.21" }
|
||||
Dec 12 11:19:11 dunnart dovecot: pop3-login: Aborted login (tried to use disabled plaintext auth): rip=190.210.136.21, lip=113.212.99.193
|
||||
|
||||
# failJSON: { "time": "2004-12-12T11:19:11", "match": true , "host": "190.210.136.21" }
|
||||
Dec 12 11:19:11 dunnart dovecot: pop3-login: Aborted login (tried to use disallowed plaintext auth): rip=190.210.136.21, lip=113.212.99.193, session=<LgDINsQCkttVIMPg>
|
||||
|
||||
# failJSON: { "time": "2005-06-13T16:30:54", "match": true , "host": "49.176.98.87" }
|
||||
Jun 13 16:30:54 platypus dovecot: imap-login: Disconnected (auth failed, 2 attempts): user=<username.bob>, method=PLAIN, rip=49.176.98.87, lip=113.212.99.194, TLS
|
||||
# failJSON: { "time": "2005-06-14T00:48:21", "match": true , "host": "59.167.242.100" }
|
||||
Jun 14 00:48:21 platypus dovecot: imap-login: Disconnected (auth failed, 1 attempts): method=PLAIN, rip=59.167.242.100, lip=113.212.99.194, TLS: Disconnected
|
||||
# failJSON: { "time": "2005-06-23T00:52:43", "match": true , "host": "193.95.245.163" }
|
||||
Jun 23 00:52:43 vhost1-ua dovecot: pop3-login: Disconnected: Inactivity (auth failed, 1 attempts): user=<info>, method=PLAIN, rip=193.95.245.163, lip=176.214.13.210
|
||||
|
||||
# Dovecot version 2.4
|
||||
# failJSON: { "time": "2005-06-12T19:07:29", "match": true , "host": "192.0.2.241" }
|
||||
Jun 12 19:07:29 hostname dovecot[241]: imap-login: Login aborted: Connection closed (auth failed, 3 attempts in 16 secs) (auth_failed): user=<test>, method=PLAIN, rip=192.0.2.241, lip=203.0.113.104, TLS, session=<9ZHq02g3J8S60fan>
|
||||
# failJSON: { "time": "2005-06-13T16:35:56", "match": true , "host": "192.0.2.241" }
|
||||
Jun 13 16:35:56 mx dovecot[241]: managesieve-login: Login aborted: Logged out (auth failed, 1 attempts in 10 secs) (auth_failed): user=<user@domain>, method=PLAIN, rip=192.0.2.241, lip=203.0.113.104, TLS, session=<Dp8j1Ho3suQYdo+k>
|
||||
|
||||
# failJSON: { "time": "2005-07-02T13:49:31", "match": true , "host": "192.51.100.13" }
|
||||
Jul 02 13:49:31 hostname dovecot[442]: pop3-login: Aborted login (auth failed, 1 attempts in 17 secs): user=<test>, method=PLAIN, rip=192.51.100.13, lip=203.0.113.17, session=<YADINsQCDs5BH8Pg>
|
||||
|
||||
# failJSON: { "time": "2005-07-02T13:49:32", "match": true , "host": "200.76.17.206" }
|
||||
Jul 02 13:49:32 hostname dovecot[442]: dovecot: auth(default): pam(account@MYSERVERNAME.com,200.76.17.206): pam_authenticate() failed: User not known to the underlying authentication module: 2 Time(s)
|
||||
|
||||
# failJSON: { "time": "2013-08-11T03:56:40", "match": true , "host": "1.2.3.4" }
|
||||
2013-08-11 03:56:40 auth-worker(default): Info: pam(username,1.2.3.4): pam_authenticate() failed: Authentication failure (password mismatch?)
|
||||
|
||||
# failJSON: { "time": "2005-01-29T05:32:50", "match": true , "host": "1.2.3.4" }
|
||||
Jan 29 05:32:50 mail dovecot: auth-worker(304): pam(username,1.2.3.4): pam_authenticate() failed: Authentication failure (password mismatch?)
|
||||
|
||||
# failJSON: { "time": "2005-01-29T18:55:55", "match": true , "host": "192.0.2.4", "desc": "Password mismatch (title case, gh-2880)" }
|
||||
Jan 29 18:55:55 mail dovecot: auth-worker(12182): pam(user,192.0.2.4): pam_authenticate() failed: Authentication failure (Password mismatch?)
|
||||
|
||||
# failJSON: { "time": "2005-01-29T05:13:40", "match": true , "host": "1.2.3.4" }
|
||||
Jan 29 05:13:40 mail dovecot: auth-worker(31326): pam(username,1.2.3.4): unknown user
|
||||
|
||||
# failJSON: { "time": "2005-01-29T05:13:50", "match": true , "host": "1.2.3.4" }
|
||||
Jan 29 05:13:50 mail dovecot: auth: passwd-file(username,1.2.3.4): unknown user
|
||||
|
||||
# failJSON: { "time": "2005-01-29T13:54:06", "match": true , "host": "192.0.2.5" }
|
||||
Jan 29 13:54:06 auth-worker(22401): Info: sql(admin@example.de,192.0.2.5,<n4JLdHNVngZGpV2j>): unknown user
|
||||
|
||||
#failJSON: { "time": "2005-06-11T13:57:17", "match": true, "host": "192.168.178.25", "desc": "allow more verbose logging, gh-2573" }
|
||||
Jun 11 13:57:17 main dovecot: auth: ldap(user@server.org,192.168.178.25,<LZmGp6mZaMrAqLIZ>): unknown user (SHA1 of given password: f638ff)
|
||||
|
||||
#failJSON: { "time": "2005-06-11T13:57:17", "match": true, "host": "192.168.144.226" }
|
||||
Jun 11 13:57:17 main dovecot: auth: sql(admin@example.ru,192.168.144.226,<6rXunFtu493AqJDi>): Password mismatch
|
||||
|
||||
#failJSON: { "time": "2005-06-11T13:57:17", "match": true, "host": "192.168.178.25", "desc": "allow more verbose logging, gh-2573" }
|
||||
Jun 11 13:57:17 main dovecot: auth: ldap(user@server.org,192.168.178.25,<LZmGp6mZaMrAqLIZ>): Password mismatch (for LDAP bind) (SHA1 of given password: f638ff)
|
||||
|
||||
# failJSON: { "time": "2005-06-12T11:48:12", "match": true , "host": "192.0.2.6" }
|
||||
Jun 12 11:48:12 auth-worker(80180): Info: conn unix:auth-worker (uid=143): auth-worker<13247>: sql(support,192.0.2.6): unknown user
|
||||
# failJSON: { "time": "2005-06-12T23:06:05", "match": true , "host": "192.0.2.7" }
|
||||
Jun 12 23:06:05 auth-worker(57065): Info: conn unix:auth-worker (uid=143): auth-worker<225622>: sql(user@domain.com,192.0.2.7,<Yx7+W8+Io>): Password mismatch
|
||||
|
||||
# failJSON: { "time": "2005-06-15T11:28:21", "match": true , "host": "192.0.2.7" }
|
||||
Jun 15 11:28:21 hostname dovecot: auth-worker(5787): conn unix:auth-worker (pid=27359,uid=97): auth-worker<55>: pam(webapps,192.0.2.7): unknown user
|
||||
# failJSON: { "time": "2005-06-15T13:57:41", "match": true , "host": "192.0.2.7" }
|
||||
Jun 15 13:57:41 hostname dovecot: auth-worker(3270): conn unix:auth-worker (pid=27359,uid=97): auth-worker<128>: pam(webapps,192.0.2.7): pam_authenticate() failed: Authentication failure (Password mismatch?)
|
||||
|
||||
# failJSON: { "time": "2005-01-29T14:38:51", "match": true , "host": "192.0.2.6", "desc": "PAM Permission denied (gh-1897)" }
|
||||
Jan 29 14:38:51 example.com dovecot[24941]: auth-worker(30165): pam(user@example.com,192.0.2.6,<PNHQq8pZhqIKAQGd>): pam_authenticate() failed: Permission denied
|
||||
|
||||
# failJSON: { "time": "2005-04-19T05:22:20", "match": true , "host": "80.255.3.104" }
|
||||
Apr 19 05:22:20 vm5 auth: pam_unix(dovecot:auth): authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=informix rhost=80.255.3.104
|
||||
|
||||
|
||||
# failJSON: { "time": "2005-01-13T20:51:05", "match": true , "host": "1.2.3.4" }
|
||||
Jan 13 20:51:05 valhalla dovecot: pop3-login: Disconnected: Inactivity (auth failed, 1 attempts in 178 secs): user=<ivo>, method=PLAIN, rip=1.2.3.4, lip=1.1.2.2, session=<6brQWt/vCADDhP/+>
|
||||
# failJSON: { "time": "2005-01-14T15:54:30", "match": true , "host": "1.2.3.4" }
|
||||
Jan 14 15:54:30 valhalla dovecot: pop3-login: Disconnected (auth failed, 1 attempts in 2 secs): user=<ivo>, method=PLAIN, rip=1.2.3.4, lip=1.1.2.2, TLS: Disconnected, session=<q454Xu/vMwBZApgg>
|
||||
|
||||
|
||||
# failJSON: { "time": "2005-01-29T09:33:58", "match": true , "host": "212.9.180.3" }
|
||||
Jan 29 09:33:58 pop3-login: Info: Aborted login (auth failed, 1 attempts in 2 secs): user=<grace>, method=PLAIN, rip=212.9.180.3
|
||||
|
||||
# failJSON: { "time": "2005-01-29T09:34:17", "match": true , "host": "1.2.3.4" }
|
||||
Jan 29 09:34:17 pop3-login: Info: Aborted login (auth failed, 1 attempts in 62 secs): user=<carl.matx@sxxxxxxx.net>, method=PLAIN, rip=1.2.3.4, TLS
|
||||
|
||||
# failJSON: { "time": "2005-01-29T09:38:03", "match": true , "host": "117.218.51.80" }
|
||||
Jan 29 09:38:03 pop3-login: Info: Disconnected: Inactivity (auth failed, 1 attempts in 178 secs): user=<suzanne>, method=PLAIN, rip=117.218.51.80
|
||||
|
||||
# failJSON: { "time": "2005-01-29T09:38:46", "match": false , "host": "176.61.137.100" }
|
||||
Jan 29 09:38:46 pop3-login: Info: Disconnected (no auth attempts in 10 secs): user=<>, rip=176.61.137.100, TLS handshaking: SSL_accept() failed: error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol
|
||||
|
||||
# failJSON: { "time": "2005-06-13T20:48:11", "match": false , "host": "121.44.24.254" }
|
||||
Jun 13 20:48:11 platypus dovecot: pop3-login: Disconnected (no auth attempts): rip=121.44.24.254, lip=113.212.99.194, TLS: Disconnected
|
||||
# failJSON: { "time": "2005-06-13T21:48:06", "match": false , "host": "180.200.180.81" }
|
||||
Jun 13 21:48:06 platypus dovecot: pop3-login: Disconnected: Inactivity (no auth attempts): rip=180.200.180.81, lip=113.212.99.194, TLS
|
||||
# failJSON: { "time": "2005-06-13T20:20:21", "match": false , "host": "180.189.168.166" }
|
||||
Jun 13 20:20:21 platypus dovecot: imap-login: Disconnected (no auth attempts): rip=180.189.168.166, lip=113.212.99.194, TLS handshaking: Disconnected
|
||||
# failJSON: { "time": "2005-07-02T13:49:32", "match": false , "host": "192.51.100.13" }
|
||||
Jul 02 13:49:32 hostname dovecot[442]: pop3-login: Disconnected (no auth attempts in 58 secs): user=<>, rip=192.51.100.13, lip=203.0.113.17, session=<LgDINsQCkttVIMPg>
|
||||
|
||||
# failJSON: { "time": "2005-03-23T06:10:52", "match": true , "host": "52.37.139.121" }
|
||||
Mar 23 06:10:52 auth: Info: ldap(dog,52.37.139.121,): invalid credentials
|
||||
|
||||
# failJSON: { "time": "2005-07-17T09:21:22", "match": true , "host": "192.0.2.4", "desc": "proxy dest auth failed, gh-2184"}
|
||||
Jul 17 09:21:22 mailproxy dovecot: imap-login: Disconnected (proxy dest auth failed): user=<rtomes@acenet.com.au>, method=PLAIN, rip=192.0.2.4, lip=192.168.1.2, session=<NTI4FiZxcQCSud4G>
|
||||
|
||||
# failJSON: { "time": "2005-07-26T11:11:21", "match": true , "host": "192.0.2.1" }
|
||||
Jul 26 11:11:21 hostname dovecot: imap-login: Disconnected: Too many invalid commands (tried to use disallowed plaintext auth): user=<test>, rip=192.0.2.1, lip=192.168.1.1, session=<S5dIdTFCDKUWWMbU>
|
||||
# failJSON: { "time": "2005-07-26T11:12:19", "match": true , "host": "192.0.2.2" }
|
||||
Jul 26 11:12:19 hostname dovecot: imap-login: Disconnected: Too many invalid commands (auth failed, 1 attempts in 17 secs): user=<test>, method=PLAIN, rip=192.0.2.2, lip=192.168.1.1, TLS, session=<g3ZKeDECFqlWWMbU>
|
||||
|
||||
# failJSON: { "time": "2004-08-28T06:38:51", "match": true , "host": "192.0.2.3" }
|
||||
Aug 28 06:38:51 s166-62-100-187 dovecot: imap-login: Disconnected (auth failed, 1 attempts in 9 secs): user=<administrator@example.com>, method=PLAIN, rip=192.0.2.3, lip=192.168.1.2, TLS: Disconnected, TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
|
||||
# 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=<test@example.com>, rip=192.0.2.4, lip=127.0.0.19, session=<Lsz0Oo7WXti3b7xe>
|
||||
|
||||
# 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=<user1>, 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" }
|
||||
Aug 29 03:53:52 server dovecot: submission-login: Remote closed connection (auth failed, 1 attempts in 2 secs): user=<user4>, method=PLAIN, rip=192.0.2.169, lip=0.0.0.0
|
||||
# failJSON: { "time": "2004-08-29T15:33:53", "match": true , "host": "192.0.2.100" }
|
||||
Aug 29 15:33:53 server dovecot: managesieve-login: Disconnected: Too many invalid commands. (auth failed, 1 attempts in 2 secs): user=<myself>, method=PLAIN, rip=192.0.2.100, lip=0.0.0.0, TLS, TLSv1.3 with cipher TLS_CHACHA20_POLY1305_SHA256 (256/256 bits)
|
||||
|
||||
# ---------------------------------------
|
||||
# Test-cases of aggressive mode:
|
||||
# ---------------------------------------
|
||||
|
||||
# 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-22T12:09:41", "match": true , "host": "192.0.2.6", "desc": "matches in aggressive mode, new format with `Login aborted` and `(no_auth_attempts)`" }
|
||||
Aug 22 12:09:41 server dovecot: imap-login: Login aborted: Connection closed: read(size=653) failed: Connection reset by peer (no auth attempts in 1 secs) (no_auth_attempts): user=<>, rip=192.0.2.6, lip=192.0.2.1, TLS: read(size=653) failed: Connection reset by peer, session=<qMLsbvY8PLSkXGoP>
|
||||
|
||||
# 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" }
|
||||
Aug 31 16:15:10 s166-62-100-187 dovecot: imap-login: Disconnected (client didn't finish SASL auth, waited 2 secs): user=<>, method=PLAIN, rip=192.0.2.6, lip=192.168.1.2, TLS: SSL_read() syscall failed: Connection reset by peer, TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)
|
||||
# failJSON: { "time": "2004-08-31T16:21:53", "match": true , "host": "192.0.2.7" }
|
||||
Aug 31 16:21:53 s166-62-100-187 dovecot: imap-login: Disconnected (no auth attempts in 4 secs): user=<>, rip=192.0.2.7, lip=192.168.1.2, TLS handshaking: SSL_accept() syscall failed: Connection reset by peer
|
||||
|
||||
# failJSON: { "time": "2004-08-22T12:09:41", "match": true , "host": "192.0.2.7", "desc": "disconnected during TLS handshake (no application protocol)" }
|
||||
Aug 22 12:09:41 server dovecot: imap-login: Login aborted: Connection closed: SSL_accept() failed: error:0A0000EB:SSL routines::no application protocol (disconnected during TLS handshake) (tls_handshake_not_finished): user=<>, rip=192.0.2.7, lip=192.0.2.1, TLS handshaking: SSL_accept() failed: error:0A0000EB:SSL routines::no application protocol, session=<J5HwbvY8QrSkXGoP>
|
||||
# failJSON: { "time": "2004-08-22T12:09:41", "match": true , "host": "192.0.2.7", "desc": "disconnected during TLS handshake (no shared cipher)" }
|
||||
Aug 22 12:09:41 server dovecot: imap-login: Login aborted: Connection closed: SSL_accept() failed: error:0A0000C1:SSL routines::no shared cipher (disconnected during TLS handshake) (tls_handshake_not_finished): user=<>, rip=192.0.2.7, lip=192.0.2.1, TLS handshaking: SSL_accept() failed: error:0A0000C1:SSL routines::no shared cipher, session=<Q23ybvY8RLSkXGoP>
|
||||
23
fail2ban-master/fail2ban/tests/files/logs/dropbear
Normal file
23
fail2ban-master/fail2ban/tests/files/logs/dropbear
Normal file
@@ -0,0 +1,23 @@
|
||||
# failJSON: { "time": "2005-03-24T15:25:51", "match": true , "host": "198.51.100.87" }
|
||||
Mar 24 15:25:51 buffalo1 dropbear[4092]: bad password attempt for 'root' from 198.51.100.87:5543
|
||||
# failJSON: { "time": "2005-02-11T15:23:17", "match": true , "host": "198.51.100.215" }
|
||||
Feb 11 15:23:17 dropbear[1252]: login attempt for nonexistent user from ::ffff:198.51.100.215:60495
|
||||
# failJSON: { "time": "2005-03-24T15:25:51", "match": true , "host": "198.51.100.87" }
|
||||
Mar 24 15:25:51 buffalo1 dropbear[4092]: bad password attempt for 'root' from 198.51.100.87:5543
|
||||
# failJSON: { "time": "2005-02-11T15:23:17", "match": true , "host": "198.51.100.215" }
|
||||
Feb 11 15:23:17 dropbear[1252]: login attempt for nonexistent user from ::ffff:198.51.100.215:60495
|
||||
|
||||
# failJSON: { "time": "2005-07-27T01:04:12", "match": true , "host": "1.2.3.4" }
|
||||
Jul 27 01:04:12 fail2ban-test dropbear[1335]: Bad password attempt for 'root' from 1.2.3.4:60588
|
||||
# failJSON: { "time": "2005-07-27T01:04:22", "match": true , "host": "1.2.3.4" }
|
||||
Jul 27 01:04:22 fail2ban-test dropbear[1335]: Exit before auth (user 'root', 10 fails): Max auth tries reached - user 'root' from 1.2.3.4:60588
|
||||
# failJSON: { "time": "2005-07-27T01:18:59", "match": true , "host": "1.2.3.4" }
|
||||
Jul 27 01:18:59 fail2ban-test dropbear[1477]: Login attempt for nonexistent user from 1.2.3.4:60794
|
||||
|
||||
# failJSON: { "time": "2005-07-10T23:53:52", "match": true , "host": "1.2.3.4", "desc": "extra pid/timestamp may be logged into journal, gh-3597" }
|
||||
Jul 10 23:53:52 fail2ban-test dropbear[825]: [825] Jul 10 23:53:52 Bad password attempt for 'root' from 1.2.3.4:52289
|
||||
|
||||
# failJSON: { "time": "2005-07-10T23:57:29", "match": true , "host": "192.0.2.3", "desc": "different message format, gh-3791" }
|
||||
Jul 10 23:57:29 fail2ban-test dropbear[825]: [825] Jul 10 23:57:29 Exit before auth from <192.0.2.3:52289>: (user 'root', 10 fails): Max auth tries reached - user 'root'
|
||||
# failJSON: { "time": "2005-07-10T23:59:24", "match": true , "host": "192.0.2.3", "desc": "different message format, gh-3791" }
|
||||
Jul 10 23:59:24 fail2ban-test dropbear[826]: [826] Jul 10 23:59:24 Exit before auth from <192.0.2.3:52325>: Max auth tries reached - user 'is invalid'
|
||||
17
fail2ban-master/fail2ban/tests/files/logs/drupal-auth
Normal file
17
fail2ban-master/fail2ban/tests/files/logs/drupal-auth
Normal file
@@ -0,0 +1,17 @@
|
||||
# failJSON: { "time": "2005-04-26T13:15:25", "match": true , "host": "1.2.3.4" }
|
||||
Apr 26 13:15:25 webserver example.com: https://example.com|1430068525|user|1.2.3.4|https://example.com/?q=user|https://example.com/?q=user|0||Login attempt failed for drupaladmin.
|
||||
# failJSON: { "time": "2005-04-26T13:15:25", "match": true , "host": "1.2.3.4" }
|
||||
Apr 26 13:15:25 webserver example.com: https://example.com/subdir|1430068525|user|1.2.3.4|https://example.com/subdir/user|https://example.com/subdir/user|0||Login attempt failed for drupaladmin.
|
||||
|
||||
# failJSON: { "time": "2005-04-26T13:19:08", "match": false , "host": "1.2.3.4", "user": "drupaladmin" }
|
||||
Apr 26 13:19:08 webserver example.com: https://example.com|1430068748|user|1.2.3.4|https://example.com/user|https://example.com/user|1||Session opened for drupaladmin.
|
||||
|
||||
# failJSON: { "time": "2005-04-26T13:20:00", "match": false, "desc": "attempt to inject on URI (pipe, login failed for), not a failure, gh-2742" }
|
||||
Apr 26 13:20:00 host drupal-site: https://example.com|1613063581|user|192.0.2.5|https://example.com/user/login?test=%7C&test2=%7C...|https://example.com/user/login?test=|&test2=|0||Login attempt failed for tester|2||Session revisited for drupaladmin.
|
||||
|
||||
# failJSON: { "time": "2005-04-26T13:20:01", "match": true , "host": "192.0.2.7", "user": "Jack Sparrow", "desc": "log-format change - for -> from, user name with space, gh-2742" }
|
||||
Apr 26 13:20:01 mweb drupal_site[24864]: https://www.example.com|1613058599|user|192.0.2.7|https://www.example.com/en/user/login|https://www.example.com/en/user/login|0||Login attempt failed from Jack Sparrow.
|
||||
# failJSON: { "time": "2005-04-26T13:20:02", "match": true , "host": "192.0.2.4", "desc": "attempt to inject on URI (pipe), login failed, gh-2742" }
|
||||
Apr 26 13:20:02 host drupal-site: https://example.com|1613063581|user|192.0.2.4|https://example.com/user/login?test=%7C&test2=%7C|https://example.com/user/login?test=|&test2=||0||Login attempt failed from 192.0.2.4.
|
||||
# failJSON: { "time": "2005-04-26T13:20:03", "match": false, "desc": "attempt to inject on URI (pipe, login failed from), not a failure, gh-2742" }
|
||||
Apr 26 13:20:03 host drupal-site: https://example.com|1613063581|user|192.0.2.5|https://example.com/user/login?test=%7C&test2=%7C...|https://example.com/user/login?test=|&test2=|0||Login attempt failed from 1.2.3.4|2||Session revisited for drupaladmin.
|
||||
20
fail2ban-master/fail2ban/tests/files/logs/ejabberd-auth
Normal file
20
fail2ban-master/fail2ban/tests/files/logs/ejabberd-auth
Normal file
@@ -0,0 +1,20 @@
|
||||
# failJSON: { "match": false }
|
||||
=INFO REPORT==== 2013-07-14 17:53:40 ===
|
||||
# failJSON: { "match": false }
|
||||
I(<0.370.0>:ejabberd_listener:281) : (#Port<0.6910>) Accepted connection {{192,0,2,4},12716} -> {{198,51,100,2},5222}
|
||||
|
||||
# failJSON: { "match": false }
|
||||
=INFO REPORT==== 2013-07-14 17:53:40 ===
|
||||
# failJSON: { "time": "2013-07-14T17:53:40", "match": true , "host": "192.0.2.4" }
|
||||
I(<0.1440.0>:ejabberd_c2s:813) : ({socket_state,tls,{tlssock,#Port<0.6910>,#Port<0.6912>},<0.1439.0>}) Failed authentication for user@example.com from IP 192.0.2.4 ({{192,0,2,4},12716})
|
||||
# failJSON: { "time": "2014-01-07T18:09:08", "match": true , "host": "1.2.3.4" }
|
||||
2014-01-07 18:09:08.512 [info] <0.22741.1>@ejabberd_c2s:wait_for_feature_request:662 ({socket_state,p1_tls,{tlssock,#Port<0.24718>,#Port<0.24720>},<0.22740.1>}) Failed authentication for test@example.com from IP 1.2.3.4
|
||||
|
||||
# new format:
|
||||
|
||||
# failJSON: { "time": "2015-03-19T13:57:35", "match": true , "host": "192.0.2.6" }
|
||||
2015-03-19 13:57:35.805 [info] <0.585.0>@ejabberd_c2s:wait_for_sasl_response:965 ({socket_state,p1_tls,{tlssock,#Port<0.6434>,#Port<0.6436>},<0.584.0>}) Failed authentication for robin@example.com from 192.0.2.6
|
||||
|
||||
# 17.06 "new" format:
|
||||
# failJSON: { "time": "2017-07-29T08:24:04", "match": true , "host": "192.0.2.3" }
|
||||
2017-07-29 08:24:04.773 [info] <0.6668.0>@ejabberd_c2s:handle_auth_failure:433 (http_bind|ejabberd_bosh) Failed c2s PLAIN authentication for test@example.ch from ::FFFF:192.0.2.3: Invalid username or password
|
||||
128
fail2ban-master/fail2ban/tests/files/logs/exim
Normal file
128
fail2ban-master/fail2ban/tests/files/logs/exim
Normal file
@@ -0,0 +1,128 @@
|
||||
# From IRC 2013-01-04
|
||||
# failJSON: { "time": "2013-01-04T17:03:46", "match": true , "host": "24.106.174.74" }
|
||||
2013-01-04 17:03:46 login authenticator failed for rrcs-24-106-174-74.se.biz.rr.com ([192.168.2.33]) [24.106.174.74]: 535 Incorrect authentication data (set_id=brian)
|
||||
# From IRC 2013-06-13 XATRIX (Georgiy Mernov)
|
||||
# failJSON: { "time": "2013-06-12T03:57:58", "match": true , "host": "120.196.140.45" }
|
||||
2013-06-12 03:57:58 login authenticator failed for (ylmf-pc) [120.196.140.45]: 535 Incorrect authentication data: 1 Time(s)
|
||||
# failJSON: { "time": "2013-06-12T13:18:11", "match": true , "host": "101.66.165.86" }
|
||||
2013-06-12 13:18:11 login authenticator failed for (USER-KVI9FGS9KP) [101.66.165.86]: 535 Incorrect authentication data
|
||||
|
||||
# 'https://github.com/fail2ban/fail2ban/pull/251#issuecomment-23001227'
|
||||
# failJSON: { "time": "2013-08-20T07:48:02", "match": true , "host": "85.25.92.177" }
|
||||
2013-08-20 07:48:02 login authenticator failed for static-ip-85-25-92-177.inaddr.ip-pool.com (USER) [85.25.92.177]: 535 Incorrect authentication data: 1 Time(s)
|
||||
# failJSON: { "time": "2013-08-20T23:30:05", "match": true , "host": "91.218.72.71" }
|
||||
2013-08-20 23:30:05 plain authenticator failed for ([192.168.2.102]) [91.218.72.71]: 535 Incorrect authentication data: 1 Time(s)
|
||||
# failJSON: { "time": "2013-09-02T09:19:07", "match": true , "host": "118.233.20.68" }
|
||||
2013-09-02 09:19:07 login authenticator failed for (gkzwsoju) [118.233.20.68]: 535 Incorrect authentication data
|
||||
# failJSON: { "time": "2014-01-12T02:07:48", "match": true , "host": "85.214.85.40" }
|
||||
2014-01-12 02:07:48 dovecot_login authenticator failed for h1832461.stratoserver.net (User) [85.214.85.40]: 535 Incorrect authentication data (set_id=scanner)
|
||||
# failJSON: { "time": "2019-10-22T03:39:17", "match": true , "host": "192.0.2.37", "desc": "pid-prefix in form of 'hostname exim[...]:', gh-2553" }
|
||||
2019-10-22 03:39:17 mx1.fqdn.local exim[29786]: dovecot_login authenticator failed for (User) [192.0.2.37]: 535 Incorrect authentication data (set_id=test@domain.com)
|
||||
# failJSON: { "time": "2014-12-02T03:00:23", "match": true , "host": "193.254.202.35" }
|
||||
2014-12-02 03:00:23 auth_plain authenticator failed for (rom182) [193.254.202.35]:41556 I=[10.0.0.1]:25: 535 Incorrect authentication data (set_id=webmaster)
|
||||
# failJSON: { "time": "2017-04-23T22:45:59", "match": true , "host": "192.0.2.2", "desc": "optional part (...)" }
|
||||
2017-04-23 22:45:59 fixed_login authenticator failed for bad.host.example.com [192.0.2.2]:54412 I=[172.89.0.6]:587: 535 Incorrect authentication data (set_id=user@example.com)
|
||||
# failJSON: { "time": "2024-03-21T19:26:06", "match": true , "host": "194.169.175.1" }
|
||||
2024-03-21 19:26:06 dovecot_login authenticator failed for (User) [194.169.175.1]:21298 I=[22.33.44.55]:465 Ci=30416: 535 Incorrect authentication data (set_id=uaf589@example.com)
|
||||
|
||||
## no matches with `mode = normal`:
|
||||
|
||||
# failJSON: { "match": false , "desc": "aggressive mode only" }
|
||||
2017-12-03 08:32:00 no host name found for IP address 192.0.2.8
|
||||
# failJSON: { "match": false , "desc": "aggressive mode only" }
|
||||
2017-12-03 08:51:35 no IP address found for host test.example.com (during SMTP connection from [192.0.2.9])
|
||||
# failJSON: { "match": false , "desc": "aggressive mode only" }
|
||||
2022-04-03 21:53:53 no IP address found for host hos-t.example.tld (during SMTP connection from [63.85.123.6]:49390 I=[31.130.202.17]:25)
|
||||
|
||||
# filterOptions: {"logtype": "journal"}
|
||||
|
||||
# failJSON: { "match": true , "host": "192.0.2.27", "desc": "systemd-journal entry with additional timestamp, gh-3060" }
|
||||
mail.example.com exim[3751842]: 2021-07-17 23:20:49 plain_server authenticator failed for ([192.0.2.17]) [192.0.2.27]: 535 Incorrect authentication data
|
||||
|
||||
# filterOptions: [{"mode": "more"}, {"mode": "aggressive"}]
|
||||
|
||||
# failJSON: { "time": "2013-06-10T10:10:59", "match": true , "host": "193.169.56.211" }
|
||||
2013-06-10 10:10:59 H=ufficioestampa.it (srv.ufficioestampa.it) [193.169.56.211] sender verify fail for <user@example.com>: Unrouteable address
|
||||
# http://forum.lissyara.su/viewtopic.php?f=20&t=29857
|
||||
# 2010-11-24 21:48:41 1PLKOW-00046U-EW F=wvhluo@droolindog.com H=93-143-146-237.adsl.net.t-com.hr (droolindog.com) [93.143.146.237] I=[10.10.10.32]:25 P=esmtp temporarily rejected by local_scan(): Temporary local problem
|
||||
# http://us.generation-nt.com/answer/exim-spamassassin-2010-0-x64-help-204020461.html
|
||||
# 2011-07-07 15:44:16 1QexIu-0006dj-PX F=XXXXXX@XXXXXXXXXXXX H=localhost (saf.bio.caltech.edu) [127.0.0.1] P=esmtp temporarily rejected by local_scan(): Local configuration error - local_scan() library failure/usr/lib/exim/sa-exim.so: cannot open shared object file: No such file or directory
|
||||
# failJSON: { "time": "2013-06-10T18:33:32", "match": true , "host": "202.132.70.178" }
|
||||
2013-06-10 18:33:32 [10099] H=(yakult.com.tw) [202.132.70.178]:3755 I=[1.2.3.4]:25 F=menacedsj04@listserv.eurasia.org rejected RCPT dir@ml3.ru: relay not permitted
|
||||
# failJSON: { "time": "2013-06-02T06:54:20", "match": true , "host": "211.148.195.192" }
|
||||
2013-06-02 06:54:20 [13314] SMTP protocol synchronization error (input sent without waiting for greeting): rejected connection from H=[211.148.195.192]:25936 I=[1.2.3.4]:25 input="GET / HTTP/1.1\r\n\r\n"
|
||||
# failJSON: { "time": "2013-06-02T09:05:48", "match": true , "host": "82.96.160.77" }
|
||||
2013-06-02 09:05:48 [18505] SMTP protocol synchronization error (next input sent too soon: pipelining was not advertised): rejected "RSET" H=ba77.mx83.fr [82.96.160.77]:58302 I=[1.2.3.4]:25 next input="QUIT\r\n"
|
||||
# failJSON: { "time": "2013-06-02T09:22:05", "match": true , "host": "163.14.21.161" }
|
||||
2013-06-02 09:22:05 [19591] SMTP call from pc012-6201.spo.scu.edu.tw [163.14.21.161]:3767 I=[1.2.3.4]:25 dropped: too many nonmail commands (last was "RSET")
|
||||
# failJSON: { "time": "2013-06-02T09:22:06", "match": true , "host": "192.0.2.109" }
|
||||
2013-06-02 09:22:06 SMTP call from [192.0.2.109] dropped: too many syntax or protocol errors (last command was "AUTH LOGIN")
|
||||
# failJSON: { "time": "2013-06-02T09:22:07", "match": true , "host": "192.0.2.109", "desc": "unrecognized commands, gh-3497" }
|
||||
2013-06-02 09:22:07 SMTP call from [192.0.2.109] dropped: too many unrecognized commands (last was "\033%-12345X")
|
||||
# failJSON: { "time": "2013-06-02T09:22:08", "match": true , "host": "192.0.2.109", "desc": "additional suffix at end, gh-3497" }
|
||||
2013-06-02 09:22:08 SMTP call from xxx.example.com [192.0.2.109] dropped: too many syntax or protocol errors (last command was "\300\024?\234?\235?/?5\300\022?", C=EHLO)
|
||||
# failJSON: { "time": "2013-06-02T15:06:18", "match": true , "host": "46.20.35.114" }
|
||||
2013-06-02 15:06:18 H=(VM-WIN2K3-1562) [46.20.35.114] sender verify fail for <usfh@technological-systems.com>: Unknown user
|
||||
# failJSON: { "time": "2013-06-07T02:02:09", "match": true , "host": "91.232.21.92" }
|
||||
2013-06-07 02:02:09 H=treeladders.kiev.ua [91.232.21.92] sender verify fail for <mailer@treeladders.kiev.ua>: all relevant MX records point to non-existent hosts
|
||||
# failJSON: { "time": "2013-06-15T16:34:55", "match": true , "host": "182.18.24.93" }
|
||||
2013-06-15 16:34:55 H=mx.tillions.com [182.18.24.93] F=<buh@caladan.ebay.sun.com> rejected RCPT <ruslan@maslovskiy.com.ua>: Sender verify failed
|
||||
# failJSON: { "time": "2013-06-15T16:36:49", "match": true , "host": "111.67.203.116" }
|
||||
2013-06-15 16:36:49 H=altmx.marsukov.com [111.67.203.116] F=<kadrofutcheti@mail.ru> rejected RCPT <oksana@birzhatm.ua>: Unknown user
|
||||
|
||||
# failJSON: { "time": "2016-03-18T00:34:06", "match": true , "host": "45.32.34.167" }
|
||||
2016-03-18 00:34:06 [7513] SMTP protocol error in "AUTH LOGIN" H=(ylmf-pc) [45.32.34.167]:60723 I=[172.89.0.6]:587 AUTH command used when not advertised
|
||||
# failJSON: { "time": "2016-03-19T18:40:44", "match": true , "host": "92.45.204.170" }
|
||||
2016-03-19 18:40:44 [26221] SMTP protocol error in "AUTH LOGIN aW5mb0BtYW5iYXQub3Jn" H=([127.0.0.1]) [92.45.204.170]:14243 I=[172.89.0.6]:587 AUTH command used when not advertised
|
||||
# failJSON: { "time": "2016-05-17T06:25:27", "match": true , "host": "69.10.61.61", "desc": "from gh-1430" }
|
||||
2016-05-17 06:25:27 SMTP protocol error in "AUTH LOGIN" H=(ylmf-pc) [69.10.61.61] AUTH command used when not advertised
|
||||
# failJSON: { "time": "2016-03-21T06:38:05", "match": true , "host": "49.212.207.15" }
|
||||
2016-03-21 06:38:05 [5718] no MAIL in SMTP connection from www3005.sakura.ne.jp [49.212.207.15]:28890 I=[172.89.0.6]:25 D=21s C=EHLO,STARTTLS
|
||||
# failJSON: { "time": "2016-03-21T06:57:36", "match": true , "host": "122.165.71.116" }
|
||||
2016-03-21 06:57:36 [5908] no MAIL in SMTP connection from [122.165.71.116]:2056 I=[172.89.0.6]:25 D=0s
|
||||
# failJSON: { "time": "2016-03-21T06:57:36", "match": true , "host": "122.165.71.116" }
|
||||
2016-03-21 06:57:36 [5908] no MAIL in SMTP connection from [122.165.71.116] I=[172.89.0.6]:25 D=10s
|
||||
# failJSON: { "time": "2016-03-21T04:07:49", "match": true , "host": "174.137.147.204" }
|
||||
2016-03-21 04:07:49 [25874] 1ahr79-0006jK-G9 SMTP connection from (voyeur.webair.com) [174.137.147.204]:44884 I=[172.89.0.6]:25 closed by DROP in ACL
|
||||
# failJSON: { "time": "2016-03-21T04:33:13", "match": true , "host": "206.214.71.53" }
|
||||
2016-03-21 04:33:13 [26074] 1ahrVl-0006mY-79 SMTP connection from riveruse.com [206.214.71.53]:39865 I=[172.89.0.6]:25 closed by DROP in ACL
|
||||
# failJSON: { "time": "2016-03-21T04:33:14", "match": true , "host": "192.0.2.33", "desc": "short form without optional session-id" }
|
||||
2016-03-21 04:33:14 SMTP connection from (some.domain) [192.0.2.33] closed by DROP in ACL
|
||||
|
||||
# failJSON: { "time": "2016-04-01T11:08:00", "match": true , "host": "192.0.2.29", "desc": "authentication mechanism not supported, gh-3060" }
|
||||
2016-04-01 11:08:00 info exim[8003]: [8003] SMTP protocol error in "AUTH LOGIN" H=(User) [192.0.2.29]:4816 I=[192.0.2.1]:25 Ci=8003 LOGIN authentication mechanism not supported
|
||||
# failJSON: { "time": "2016-04-01T11:08:00", "match": true , "host": "192.0.2.29", "desc": "additional pid logged with syslog-ng, gh-3060" }
|
||||
2016-04-01 11:08:00 info exim[8001]: [8001] no MAIL in SMTP connection from (User) [192.0.2.29]:20042 I=[192.0.2.1]:25 Ci=8001 D=0.349s C=EHLO,AUTH,QUIT
|
||||
|
||||
# failJSON: { "time": "2016-04-01T11:08:39", "match": true , "host": "192.0.2.1" }
|
||||
2016-04-01 11:08:39 [18643] no MAIL in SMTP connection from host.example.com (SERVER) [192.0.2.1]:1418 I=[172.89.0.6]:25 D=34s C=EHLO,AUTH
|
||||
# failJSON: { "time": "2016-04-01T11:08:40", "match": true , "host": "192.0.2.2" }
|
||||
2016-04-01 11:08:40 [18643] no MAIL in SMTP connection from host.example.com (SERVER) [192.0.2.2]:1418 I=[172.89.0.6]:25 D=2m42s C=QUIT
|
||||
# failJSON: { "time": "2016-04-01T11:09:21", "match": true , "host": "192.0.2.1" }
|
||||
2016-04-01 11:09:21 [18648] SMTP protocol error in "AUTH LOGIN" H=host.example.com (SERVER) [192.0.2.1]:4692 I=[172.89.0.6]:25 AUTH command used when not advertised
|
||||
# failJSON: { "time": "2016-03-27T16:48:48", "match": true , "host": "192.0.2.1" }
|
||||
2016-03-27 16:48:48 [21478] 1akDqs-0005aQ-9b SMTP connection from host.example.com (SERVER) [192.0.2.1]:47714 I=[172.89.0.6]:25 closed by DROP in ACL
|
||||
|
||||
# failJSON: { "time": "2017-05-01T07:42:42", "match": true , "host": "192.0.2.3", "desc": "rejected RCPT - Unrouteable address" }
|
||||
2017-05-01 07:42:42 H=some.rev.dns.if.found (the.connector.reports.this.name) [192.0.2.3] F=<some.name@some.domain> rejected RCPT <some.invalid.name@a.domain>: Unrouteable address
|
||||
|
||||
# failJSON: { "time": "2017-11-28T14:14:30", "match": true , "host": "192.0.2.4", "desc": "lower case AUTH command" }
|
||||
2017-11-28 14:14:30 SMTP protocol error in "auth login" H=(roxzgj) [192.0.2.4] AUTH command used when not advertised
|
||||
# failJSON: { "time": "2017-11-28T14:14:31", "match": true , "host": "192.0.2.5", "desc": "mixed case AUTH command" }
|
||||
2017-11-28 14:14:31 SMTP protocol error in "aUtH lOgIn" H=(roxzgj) [192.0.2.5] AUTH command used when not advertised
|
||||
# failJSON: { "time": "2017-11-28T14:14:32", "match": true , "host": "192.0.2.6", "desc": "quoted injecting on AUTH command" }
|
||||
2017-11-28 14:14:32 SMTP protocol error in "aUtH lOgIn" H=(test) [8.8.8.8]" H=(roxzgj) [192.0.2.6] AUTH command used when not advertised
|
||||
|
||||
# failJSON: { "time": "2024-03-21T09:18:51", "match": true , "host": "9.12.1.21" }
|
||||
2024-03-21 09:18:51 H=m05.horp.tld [9.12.1.21]:43030 I=[194.169.175.2]:25 Ci=7326 CV=no SNI=mail.leone.tld F=<user@example.tld> rejected RCPT <locus@leone.tld>: relay not permitted
|
||||
|
||||
# filterOptions: [{"mode": "aggressive"}]
|
||||
|
||||
# failJSON: { "time": "2017-12-03T08:32:00", "match": true , "host": "192.0.2.8", "desc": "no host found for IP" }
|
||||
2017-12-03 08:32:00 no host name found for IP address 192.0.2.8
|
||||
# failJSON: { "time": "2017-12-03T08:51:35", "match": true , "host": "192.0.2.9", "desc": "no IP found for host" }
|
||||
2017-12-03 08:51:35 no IP address found for host test.example.com (during SMTP connection from [192.0.2.9])
|
||||
# failJSON: { "time": "2022-04-03T21:53:53", "match": true , "host": "63.85.123.6", "desc": "no IP found for host long" }
|
||||
2022-04-03 21:53:53 no IP address found for host hos-t.example.tld (during SMTP connection from [63.85.123.6]:49390 I=[31.130.202.17]:25)
|
||||
# failJSON: { "time": "2022-04-03T21:53:54", "match": true , "host": "192.0.2.101", "desc": "dropped by ACL" }
|
||||
2022-04-03 21:53:54 H=[192.0.2.101]:62839 dropped by 'connect' ACL: Country is banned
|
||||
26
fail2ban-master/fail2ban/tests/files/logs/exim-spam
Normal file
26
fail2ban-master/fail2ban/tests/files/logs/exim-spam
Normal file
@@ -0,0 +1,26 @@
|
||||
# http://forum.lissyara.su/viewtopic.php?f=20&t=29857
|
||||
# 2010-11-24 21:48:41 1PLKOW-00046U-EW F=wvhluo@droolindog.com H=93-143-146-237.adsl.net.t-com.hr (droolindog.com) [93.143.146.237] I=[10.10.10.32]:25 P=esmtp temporarily rejected by local_scan(): Temporary local problem
|
||||
# http://us.generation-nt.com/answer/exim-spamassassin-2010-0-x64-help-204020461.html
|
||||
# 2011-07-07 15:44:16 1QexIu-0006dj-PX F=XXXXXX@XXXXXXXXXXXX H=localhost (saf.bio.caltech.edu) [127.0.0.1] P=esmtp temporarily rejected by local_scan(): Local configuration error - local_scan() library failure/usr/lib/exim/sa-exim.so: cannot open shared object file: No such file or directory
|
||||
# http://www.clues.ltd.uk/howto/debian-sa-fprot-HOWTO.html
|
||||
# failJSON: { "time": "2004-01-18T07:15:35", "match": true , "host": "4.47.28.40" }
|
||||
2004-01-18 07:15:35 1Ai79e-0000Dq-8i F=uzwltcmwto24@melissacam.biz H=lsanca1-ar3-4-47-028-040.lsanca1.elnk.dsl.genuity.net [4.47.28.40] P=smtp rejected by local_scan(): Rejected: hits=7.5 required=5.0 trigger=5.0
|
||||
# https://github.com/fail2ban/fail2ban/pull/251#issuecomment-19493875
|
||||
# failJSON: { "time": "2013-06-15T11:19:33", "match": true , "host": "2.181.148.95" }
|
||||
2013-06-15 11:19:33 [2249] H=([2.181.148.95]) [2.181.148.95]:52391 I=[1.2.3.4]:25 F=fantasizesg4@google.com rejected RCPT some@email.com: rejected found in dnsbl zen.spamhaus.org
|
||||
# failJSON: { "time": "2013-06-09T10:21:28", "match": true , "host": "46.254.240.82" }
|
||||
2013-06-09 10:21:28 [14127] 1UlasQ-0003fr-45 F=mcorporation4@aol.com H=(mail38.fssprus.ru) [46.254.240.82]:43671 I=[1.2.3.4]:25 P=esmtp rejected by local_scan(): Rejected
|
||||
# failJSON: { "time": "2013-06-15T11:20:36", "match": true , "host": "83.235.177.148" }
|
||||
2013-06-15 11:20:36 [2516] 1Unmew-0000ea-SE H=egeftech.static.otenet.gr [83.235.177.148]:32706 I=[1.2.3.4]:25 F=auguriesvbd40@google.com rejected after DATA: This message contains a virus (Sanesecurity.Junk.39934.UNOFFICIAL).
|
||||
# failJSON: { "time": "2013-06-16T02:50:43", "match": true , "host": "111.67.203.114" }
|
||||
2013-06-16 02:50:43 H=dbs.marsukov.com [111.67.203.114] F=<trudofspiori@mail.ru> rejected RCPT <info@nanomedtech.ua>: rejected because 111.67.203.114 is in a black list at dnsbl.sorbs.net\nCurrently Sending Spam See: http://www.sorbs.net/lookup.shtml?111.67.203.114
|
||||
# https://github.com/fail2ban/fail2ban/issues/541
|
||||
# failJSON: { "time": "2013-12-30T00:24:50", "match": true , "host": "178.123.108.196" }
|
||||
2013-12-30 00:24:50 1VxPit-000MMd-U4 SA: Action: flagged as Spam but accepted: score=8.2 required=5.0 (scanned in 6/6 secs | Message-Id: 008701cf04ed_24497d70_6cdc7850_@xxx.xx). From <spammer@xxx.xx> (host=ip-4.net-3-2-1.rev.xxx.xx [178.123.108.196]) for trap@example.com
|
||||
# https://github.com/fail2ban/fail2ban/issues/533
|
||||
# failJSON: { "time": "2013-12-29T15:34:12", "match": true , "host": "188.76.45.72" }
|
||||
2013-12-29 15:34:12 1VxHRO-000NiI-Ly SA: Action: silently tossed message: score=31.0 required=5.0 trigger=30.0 (scanned in 6/6 secs | Message-Id: etPan.09bd0c40.c3d5f675.fdf7@server.local). From <Flossiedpd@jazztel.es> (host=72.45.76.188.dynamic.jazztel.es [188.76.45.72]) for me@my.com
|
||||
# https://github.com/fail2ban/fail2ban/issues/533
|
||||
# failJSON: { "time": "2013-12-29T15:39:11", "match": true , "host": "178.123.108.196" }
|
||||
2013-12-29 15:39:11 1VxHWD-000NuW-83 SA: Action: silently tossed message: score=35.8 required=5.0 trigger=30.0 (scanned in 6/6 secs | Message-Id: 1VxHWD-000NuW-83). From <> (host=NULL [178.123.108.196]) for me@my.com
|
||||
|
||||
31
fail2ban-master/fail2ban/tests/files/logs/freeswitch
Normal file
31
fail2ban-master/fail2ban/tests/files/logs/freeswitch
Normal file
@@ -0,0 +1,31 @@
|
||||
# filterOptions: [{}, {"mode": "ddos"}]
|
||||
|
||||
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "81.94.202.251" }
|
||||
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:1533 SIP auth challenge (INVITE) on sofia profile 'internal' for [011448708752617@192.168.2.51] from ip 81.94.202.251
|
||||
|
||||
# filterOptions: [{}, {"mode": "normal"}]
|
||||
|
||||
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "5.11.47.236" }
|
||||
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:1478 SIP auth failure (INVITE) on sofia profile 'internal' for [000972543480510@192.168.2.51] from ip 5.11.47.236
|
||||
# failJSON: { "time": "2013-12-31T17:39:54", "match": false }
|
||||
2013-12-31 17:39:54.767815 [DEBUG] sofia.c:7954 IP 185.24.234.141 Rejected by acl "domains". Falling back to Digest auth.
|
||||
|
||||
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "5.11.47.236" }
|
||||
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:2531 Can't find user [1001@192.168.2.51] from 5.11.47.236
|
||||
# failJSON: { "time": "2013-12-31T17:39:54", "match": true, "host": "185.24.234.141" }
|
||||
2013-12-31 17:39:54.767815 [WARNING] sofia_reg.c:2531 Can't find user [100@192.168.2.51] from 185.24.234.141
|
||||
|
||||
# failJSON: { "time": "2016-09-25T18:57:58", "match": true, "host": "192.0.2.1", "desc": "Systemd dual time with prefix - 1st expr" }
|
||||
2016-09-25T18:57:58.150982 www.srv.tld freeswitch[122921]: 2016-09-25 18:57:58.150982 [WARNING] sofia_reg.c:2889 Can't find user [201@::1] from 192.0.2.1
|
||||
# failJSON: { "time": "2016-09-25T18:57:58", "match": true, "host": "192.0.2.2", "desc": "Systemd dual time with prefix - 2nd expr" }
|
||||
2016-09-25T18:57:58.150982 www.srv.tld freeswitch[122921]: 2016-09-25 18:57:58.150982 [WARNING] sofia_reg.c:1720 SIP auth failure (INVITE) on sofia profile 'sipinterface_1' for [9810972597751739@::1] from ip 192.0.2.2
|
||||
|
||||
# failJSON: { "time": "2005-08-03T07:56:53", "match": true, "host": "192.0.2.3", "desc": "optional year in datepattern and bit different format (gh-2193)" }
|
||||
08-03 07:56:53.026292 [WARN] [SOFIA] [sofia_reg.c:4130] Can't find user [101@148.251.134.154] from 192.0.2.3
|
||||
# failJSON: { "time": "2005-08-03T08:10:21", "match": true, "host": "192.0.2.4", "desc": "optional year in datepattern and bit different format (gh-2193)" }
|
||||
08-03 08:10:21.026299 [WARN] [SOFIA] [sofia_reg.c:2248] SIP auth failure (INVITE) on sofia profile 'external' for [41801148436701961@148.251.134.154] from ip 192.0.2.4
|
||||
|
||||
# failJSON: { "time": "2021-10-29T21:04:58", "match": true, "host": "192.0.2.5", "desc": "percent in prefix (gh-3143)" }
|
||||
2021-10-29 21:04:58.150982 00.00% [WARNING] sofia_reg.c:2889 Can't find user [201@::1] from 192.0.2.5
|
||||
# failJSON: { "time": "2021-10-29T21:05:58", "match": true, "host": "192.0.2.5", "desc": "syslog (and extra date), percent in prefix (gh-3143)" }
|
||||
2021-10-29 21:05:58.894719 www.srv.tld freeswitch[123456]: 2021-10-29 14:13:24.894719 82.43% [WARNING] sofia_reg.c:2889 Can't find user [201@::1] from 192.0.2.5
|
||||
11
fail2ban-master/fail2ban/tests/files/logs/froxlor-auth
Normal file
11
fail2ban-master/fail2ban/tests/files/logs/froxlor-auth
Normal file
@@ -0,0 +1,11 @@
|
||||
# filterOptions: [{"type": "1"}]
|
||||
# failJSON: { "time": "2005-05-21T00:56:27", "match": true , "host": "1.2.3.4" }
|
||||
May 21 00:56:27 jomu Froxlor: [Login Action 1.2.3.4] Unknown user 'user' tried to login.
|
||||
# failJSON: { "time": "2005-05-21T00:57:38", "match": true , "host": "1.2.3.4" }
|
||||
May 21 00:57:38 jomu Froxlor: [Login Action 1.2.3.4] User 'admin' tried to login with wrong password.
|
||||
|
||||
# filterOptions: [{}, {"type": "2"}]
|
||||
# failJSON: { "time": "2025-09-21T17:46:18", "match": true , "host": "1.2.3.4" }
|
||||
2025-09-21T17:46:18.311379+02:00 hostname froxlor[1055219]: froxlor.WARNING: User tried to login with wrong password. {"source":"login","action":"50","user":"1.2.3.4"} []
|
||||
# failJSON: { "time": "2025-09-21T16:30:13", "match": true , "host": "1.2.3.4" }
|
||||
2025-09-21T16:30:13.118232+02:00 hostname froxlor[1054438]: froxlor.WARNING: Unknown user tried to login. {"source":"login","action":"50","user":"1.2.3.4"} []
|
||||
5
fail2ban-master/fail2ban/tests/files/logs/gitlab
Normal file
5
fail2ban-master/fail2ban/tests/files/logs/gitlab
Normal file
@@ -0,0 +1,5 @@
|
||||
# Access of unauthorized host in /var/log/gitlab/gitlab-rails/application.log
|
||||
# failJSON: { "time": "2020-04-09T16:04:00", "match": true , "host": "80.10.11.12" }
|
||||
2020-04-09T14:04:00.667Z: Failed Login: username=admin ip=80.10.11.12
|
||||
# failJSON: { "time": "2020-04-09T16:15:09", "match": true , "host": "80.10.11.12" }
|
||||
2020-04-09T14:15:09.344Z: Failed Login: username=user name ip=80.10.11.12
|
||||
5
fail2ban-master/fail2ban/tests/files/logs/grafana
Normal file
5
fail2ban-master/fail2ban/tests/files/logs/grafana
Normal file
@@ -0,0 +1,5 @@
|
||||
# Access of unauthorized host in /var/log/grafana/grafana.log
|
||||
# failJSON: { "time": "2020-10-19T17:44:33", "match": true , "host": "182.56.23.12" }
|
||||
t=2020-10-19T17:44:33+0200 lvl=eror msg="Invalid username or password" logger=context userId=0 orgId=0 uname= error="Invalid Username or Password" remote_addr=182.56.23.12
|
||||
# failJSON: { "time": "2020-10-19T18:44:33", "match": true , "host": "182.56.23.13" }
|
||||
t=2020-10-19T18:44:33+0200 lvl=eror msg="Invalid username or password" logger=context userId=0 orgId=0 uname= error="User not found" remote_addr=182.56.23.13
|
||||
4
fail2ban-master/fail2ban/tests/files/logs/groupoffice
Normal file
4
fail2ban-master/fail2ban/tests/files/logs/groupoffice
Normal file
@@ -0,0 +1,4 @@
|
||||
# failJSON: { "time": "2014-01-06T10:59:38", "match": true, "host": "127.0.0.1" }
|
||||
[2014-01-06 10:59:38]LOGIN FAILED for user: "asdsad" from IP: 127.0.0.1
|
||||
# failJSON: { "time": "2014-01-06T10:59:49", "match": false, "host": "127.0.0.1" }
|
||||
[2014-01-06 10:59:49]LOGIN SUCCESS for user: "admin" from IP: 127.0.0.1
|
||||
2
fail2ban-master/fail2ban/tests/files/logs/gssftpd
Normal file
2
fail2ban-master/fail2ban/tests/files/logs/gssftpd
Normal file
@@ -0,0 +1,2 @@
|
||||
# failJSON: { "time": "2005-01-22T18:09:46", "match": true , "host": "198.51.100.23" }
|
||||
Jan 22 18:09:46 host ftpd[132]: repeated login failures from 198.51.100.23 (example.com)
|
||||
17
fail2ban-master/fail2ban/tests/files/logs/guacamole
Normal file
17
fail2ban-master/fail2ban/tests/files/logs/guacamole
Normal file
@@ -0,0 +1,17 @@
|
||||
# failJSON: { "match": false }
|
||||
apr 15, 2013 8:34:08 PM org.slf4j.impl.JCLLoggerAdapter warn
|
||||
# failJSON: { "time": "2013-04-15T20:34:08", "match": true , "host": "192.0.2.0" }
|
||||
WARNING: Authentication attempt from 192.0.2.0 for user "null" failed.
|
||||
# failJSON: { "match": false }
|
||||
apr 16, 2013 8:32:13 AM org.slf4j.impl.JCLLoggerAdapter warn
|
||||
# failJSON: { "time": "2013-04-16T08:32:13", "match": true , "host": "192.0.2.0" }
|
||||
WARNING: Authentication attempt from 192.0.2.0 for user "null" failed.
|
||||
# failJSON: { "match": false }
|
||||
apr 16, 2013 8:32:28 AM org.slf4j.impl.JCLLoggerAdapter warn
|
||||
# failJSON: { "time": "2013-04-16T08:32:28", "match": true , "host": "192.0.2.0" }
|
||||
WARNING: Authentication attempt from 192.0.2.0 for user "pippo" failed.
|
||||
|
||||
# filterOptions: {"logging": "webapp"}
|
||||
|
||||
# failJSON: { "time": "2005-08-13T12:57:32", "match": true , "host": "182.23.72.36" }
|
||||
12:57:32.907 [http-nio-8080-exec-10] WARN o.a.g.r.auth.AuthenticationService - Authentication attempt from 182.23.72.36 for user "guacadmin" failed.
|
||||
@@ -0,0 +1,8 @@
|
||||
# failJSON: { "match": false }
|
||||
Nov 14 22:45:27 test haproxy[760]: 192.168.33.1:58444 [14/Nov/2015:22:45:25.439] main app/app1 1939/0/1/0/1940 403 5168 - - ---- 3/3/0/0/0 0/0 "GET / HTTP/1.1"
|
||||
# failJSON: { "time": "2004-11-14T22:45:11", "match": true , "host": "192.168.33.1" }
|
||||
Nov 14 22:45:11 test haproxy[760]: 192.168.33.1:58430 [14/Nov/2015:22:45:11.608] main main/<NOSRV> -1/-1/-1/-1/0 401 248 - - PR-- 0/0/0/0/0 0/0 "GET / HTTP/1.1"
|
||||
# failJSON: { "time": "2004-11-14T22:45:11", "match": true , "host": "2001:db8::1234" }
|
||||
Nov 14 22:45:11 test haproxy[760]: 2001:db8::1234:58430 [14/Nov/2015:22:45:11.608] main main/<NOSRV> -1/-1/-1/-1/0 401 248 - - PR-- 0/0/0/0/0 0/0 "GET / HTTP/1.1"
|
||||
# failJSON: { "time": "2004-11-14T22:45:11", "match": true , "host": "192.168.33.1" }
|
||||
Nov 14 22:45:11 test haproxy[760]: ::ffff:192.168.33.1:58430 [14/Nov/2015:22:45:11.608] main main/<NOSRV> -1/-1/-1/-1/0 401 248 - - PR-- 0/0/0/0/0 0/0 "GET / HTTP/1.1"
|
||||
6
fail2ban-master/fail2ban/tests/files/logs/horde
Normal file
6
fail2ban-master/fail2ban/tests/files/logs/horde
Normal file
@@ -0,0 +1,6 @@
|
||||
# failJSON: { "time": "2004-11-11T18:57:57", "match": true , "host": "203.16.208.190" }
|
||||
Nov 11 18:57:57 HORDE [error] [horde] FAILED LOGIN for graham [203.16.208.190] to Horde [on line 116 of "/home/ace-hosting/public_html/horde/login.php"]
|
||||
|
||||
# failJSON: { "time": "2004-12-15T08:59:59", "match": true , "host": "1.2.3.4" }
|
||||
Dec 15 08:59:59 HORDE [error] [imp] FAILED LOGIN for emai.user@somedomain.com [1.2.3.4] to {mx.somedomain.com:993 [imap/ssl/novalidate-cert]} [pid 68394 on line 139 of /usr/local/www/www.somedomain.com/public_html/horde/imp/lib/Auth/imp.php"]
|
||||
|
||||
44
fail2ban-master/fail2ban/tests/files/logs/kerio
Normal file
44
fail2ban-master/fail2ban/tests/files/logs/kerio
Normal file
@@ -0,0 +1,44 @@
|
||||
# failJSON: { "time": "2011-06-17T17:00:45", "match": true, "host": "200.90.149.178" }
|
||||
[17/Jun/2011 17:00:45] Attempt to deliver to unknown recipient <advertise@aplawrence.com>, from <bekytnabvnvyx@aapug.org>, IP address 200.90.149.178
|
||||
|
||||
# failJSON: { "time": "2014-01-18T06:41:25", "match": true, "host": "202.169.236.195" }
|
||||
[18/Jan/2014 06:41:25] SMTP Spam attack detected from 202.169.236.195, client closed connection before SMTP greeting
|
||||
|
||||
# failJSON: { "time": "2014-01-18T06:42:12", "match": true, "host": "115.147.104.13" }
|
||||
[18/Jan/2014 06:42:12] SMTP Spam attack detected from 115.147.104.13, client sent data before SMTP greeting
|
||||
|
||||
# failJSON: { "time": "2014-01-18T05:47:17", "match": true, "host": "112.140.49.130" }
|
||||
[18/Jan/2014 05:47:17] IP address 112.140.49.130 found in DNS blacklist UCEProtect1, mail from <infootransac@yahoo.com.hk> to <advertise@aplawrence.com>
|
||||
|
||||
# failJSON: { "time": "2014-01-18T06:39:44", "match": true, "host": "91.232.105.66" }
|
||||
[18/Jan/2014 06:39:44] IP address 91.232.105.66 found in DNS blacklist BarracudaCentral, mail from <postmaster@ponetn.us> to <advertise@aplawrence.com>
|
||||
|
||||
# failJSON: { "time": "2013-12-30T05:27:59", "match": true, "host": "64.31.59.75" }
|
||||
[30/Dec/2013 05:27:59] Relay attempt from IP address 64.31.59.75, mail from <smtp2001soho@yahoo.com> to <reply-abuse@bol.com.br> rejected
|
||||
|
||||
# failJSON: { "time": "2013-12-30T19:24:28", "match": true, "host": "74.63.193.116" }
|
||||
[30/Dec/2013 19:24:28] Relay attempt from IP address 74.63.193.116, mail from <smtp2001soho@yahoo.com> to <reply-abuse@bol.com.br> rejected
|
||||
|
||||
# failJSON: { "time": "2013-12-13T00:22:45", "match": true, "host": "23.108.148.156" }
|
||||
[13/Dec/2013 00:22:45] Attempt to deliver to unknown recipient <suzanne@aplawrence.com>, from <info@kaimingjx.com>, IP address 23.108.148.156
|
||||
|
||||
# failJSON: { "time": "2013-12-13T01:11:04", "match": true, "host": "218.85.253.185" }
|
||||
[13/Dec/2013 01:11:04] Attempt to deliver to unknown recipient <marge@aplawrence.com>, from <yu@rrd.com>, IP address 218.85.253.185
|
||||
|
||||
# failJSON: { "time": "2017-05-29T17:29:29", "match": true, "host": "185.140.108.56" }
|
||||
[29/May/2017 17:29:29] IP address 185.140.108.56 found in DNS blacklist SpamCop, mail from <noreply-tjgqNffcgPfpbZtpDzasm@oakspaversusa.com> to <info@verinion.com> rejected
|
||||
|
||||
# failJSON: { "time": "2017-05-17T19:43:42", "match": true, "host": "185.140.108.26" }
|
||||
[17/May/2017 19:43:42] SMTP: User printer@verinion.com doesn't exist. Attempt from IP address 185.140.108.26.
|
||||
|
||||
# failJSON: { "time": "2017-05-17T19:44:25", "match": true, "host": "184.171.168.211" }
|
||||
[17/May/2017 19:44:25] Client with IP address 184.171.168.211 has no reverse DNS entry, connection rejected before SMTP greeting
|
||||
|
||||
# failJSON: { "time": "2017-05-17T19:45:27", "match": true, "host": "170.178.167.136" }
|
||||
[17/May/2017 19:45:27] Administration login into Web Administration from 170.178.167.136 failed: IP address not allowed
|
||||
|
||||
# failJSON: { "time": "2017-05-17T22:14:57", "match": true, "host": "67.211.219.82" }
|
||||
[17/May/2017 22:14:57] Message from IP address 67.211.219.82, sender <promo123@goodresponse.site> rejected: sender domain does not exist
|
||||
|
||||
# failJSON: { "time": "2017-05-18T07:25:15", "match": true, "host": "212.92.127.112" }
|
||||
[18/May/2017 07:25:15] Failed SMTP login from 212.92.127.112 with SASL method CRAM-MD5.
|
||||
18
fail2ban-master/fail2ban/tests/files/logs/lighttpd-auth
Normal file
18
fail2ban-master/fail2ban/tests/files/logs/lighttpd-auth
Normal file
@@ -0,0 +1,18 @@
|
||||
# failJSON: { "time": "2011-12-25T17:09:20", "match": true , "host": "4.4.4.4" }
|
||||
2011-12-25 17:09:20: (http_auth.c.875) password doesn't match for /gitweb/ username: francois, IP: 4.4.4.4
|
||||
# failJSON: { "time": "2012-09-26T10:24:35", "match": true , "host": "4.4.4.4" }
|
||||
2012-09-26 10:24:35: (http_auth.c.1136) digest: auth failed for xxx : wrong password, IP: 4.4.4.4
|
||||
# failJSON: { "time": "2013-08-25T00:24:55", "match": true , "host": "4.4.4.4" }
|
||||
2013-08-25 00:24:55: (http_auth.c.877) get_password failed, IP: 4.4.4.4
|
||||
# failJSON: { "time": "2018-01-16T14:10:32", "match": true , "host": "192.0.2.1", "desc": "http_auth -> mod_auth, gh-2018" }
|
||||
2018-01-16 14:10:32: (mod_auth.c.525) password doesn't match for /test-url username: test, IP: 192.0.2.1
|
||||
# failJSON: { "time": "2021-09-30T16:05:33", "match": true , "host": "192.0.2.2", "user":"test", "desc": "gh-3116" }
|
||||
2021-09-30 16:05:33: mod_auth.c.828) password doesn't match for /secure/ username: test IP: 192.0.2.2
|
||||
# failJSON: { "time": "2021-09-30T17:44:37", "match": true , "host": "192.0.2.3", "user":"tester", "desc": "gh-3116" }
|
||||
2021-09-30 17:44:37: (mod_auth.c.791) digest: auth failed for tester : wrong password, IP: 192.0.2.3
|
||||
# failJSON: { "time": "2021-09-30T17:44:37", "match": true , "host": "192.0.2.4", "desc": "gh-3116" }
|
||||
2021-09-30 17:44:37: (mod_auth.c.791) digest: auth failed: uri mismatch (/uri1 != /uri2), IP: 192.0.2.4
|
||||
|
||||
# systemd-journal
|
||||
# failJSON: { "time": "2025-03-04T02:11:57", "match": true , "host": "192.0.2.211", "desc": "gh-3955" }
|
||||
2025-03-04T02:11:57.602061 ip-172-31-3-150.ap-southeast-2.compute.internal lighttpd[764]: (mod_auth.c.853) password doesn't match for / username: user1 IP: 192.0.2.211
|
||||
30
fail2ban-master/fail2ban/tests/files/logs/mongodb-auth
Normal file
30
fail2ban-master/fail2ban/tests/files/logs/mongodb-auth
Normal file
@@ -0,0 +1,30 @@
|
||||
# failJSON: { "match": false }
|
||||
2016-11-20T00:04:00.110+0100 [conn1] Failed to authenticate root@admin with mechanism MONGODB-CR: AuthenticationFailed UserNotFound Could not find user root@admin
|
||||
# failJSON: { "time": "2016-11-20T00:04:00", "match": true , "host": "192.0.2.35" }
|
||||
2016-11-20T00:04:00.111+0100 [conn1] end connection 192.0.2.35:53276 (0 connections now open)
|
||||
|
||||
# failJSON: { "match": false }
|
||||
2016-11-20T00:24:00.110+0100 [conn5] Failed to authenticate root@admin with mechanism MONGODB-CR: AuthenticationFailed UserNotFound Could not find user root@admin
|
||||
# failJSON: { "time": "2016-11-20T00:24:00", "match": true , "host": "192.0.2.171" }
|
||||
2016-11-20T00:24:00.111+0100 [conn5] end connection 192.0.2.171:53276 (0 connections now open)
|
||||
|
||||
# failJSON: { "match": false }
|
||||
2016-11-20T00:24:00.110+0100 [conn334] Failed to authenticate root@admin with mechanism MONGODB-CR: AuthenticationFailed key mismatch
|
||||
# failJSON: { "time": "2016-11-20T00:24:00", "match": true , "host": "192.0.2.176" }
|
||||
2016-11-20T00:24:00.111+0100 [conn334] end connection 192.0.2.176:53276 (0 connections now open)
|
||||
|
||||
# failJSON: { "match": false }
|
||||
2016-11-20T00:24:00.110+0100 [conn56] Failed to authenticate root@admin with mechanism MONGODB-CR: AuthenticationFailed key mismatch
|
||||
# failJSON: { "time": "2016-11-20T00:24:00", "match": true , "host": "192.0.2.1" }
|
||||
2016-11-20T00:24:00.111+0100 [conn56] end connection 192.0.2.1:53276 (0 connections now open)
|
||||
|
||||
# failJSON: { "match": false }
|
||||
2016-11-20T12:54:02.370+0100 [initandlisten] connection accepted from 127.0.0.1:58774 #2261 (1 connection now open)
|
||||
# failJSON: { "match": false }
|
||||
2016-11-20T12:54:02.370+0100 [conn2261] end connection 127.0.0.1:58774 (0 connections now open)
|
||||
|
||||
# failJSON: { "match": false }
|
||||
2016-11-20T13:07:49.781+0100 [conn2271] authenticate db: admin { authenticate: 1, nonce: "xxx", user: "root", key: "xxx" }
|
||||
# failJSON: { "time": "2016-11-20T13:07:49", "match": false , "host": "192.0.2.178" }
|
||||
2016-11-20T13:07:49.834+0100 [conn2271] end connection 192.0.2.178:60268 (3 connections now open)
|
||||
|
||||
24
fail2ban-master/fail2ban/tests/files/logs/monit
Normal file
24
fail2ban-master/fail2ban/tests/files/logs/monit
Normal file
@@ -0,0 +1,24 @@
|
||||
# Previous version --
|
||||
# failJSON: { "time": "2005-04-17T06:05:29", "match": true , "host": "69.93.127.111" }
|
||||
[PDT Apr 16 21:05:29] error : Warning: Client '69.93.127.111' supplied unknown user 'foo' accessing monit httpd
|
||||
# failJSON: { "time": "2005-04-17T05:59:33", "match": true , "host": "97.113.189.111" }
|
||||
[PDT Apr 16 20:59:33] error : Warning: Client '97.113.189.111' supplied wrong password for user 'admin' accessing monit httpd
|
||||
|
||||
# Current version -- corresponding "https://bitbucket.org/tildeslash/monit/src/6905335aa903d425cae732cab766bd88ea5f2d1d/src/http/processor.c?at=master&fileviewer=file-view-default#processor.c-728"
|
||||
# failJSON: { "time": "2005-03-09T09:18:28", "match": false, "desc": "should be ignored: no login" }
|
||||
Mar 9 09:18:28 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: missing or invalid Authorization header
|
||||
# failJSON: { "time": "2005-03-09T09:18:28", "match": false, "desc": "should be ignored: no login" }
|
||||
Mar 9 09:18:28 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: invalid Authorization header
|
||||
# failJSON: { "time": "2005-03-09T09:18:29", "match": false, "desc": "should be ignored: connect, still no user specified" }
|
||||
Mar 9 09:18:29 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: empty username
|
||||
# failJSON: { "time": "2005-03-09T09:18:31", "match": false, "desc": "should be ignored: connect, still no user specified" }
|
||||
Mar 9 09:18:31 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: unknown user ''
|
||||
# failJSON: { "time": "2005-03-09T09:18:32", "match": true, "host": "1.2.3.4", "desc": "no password try" }
|
||||
Mar 9 09:18:32 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: empty password
|
||||
# failJSON: { "time": "2005-03-09T09:18:33", "match": true, "host": "1.2.3.4", "desc": "unknown user try" }
|
||||
Mar 9 09:18:33 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: unknown user 'test1'
|
||||
# failJSON: { "time": "2005-03-09T09:18:34", "match": true, "host": "1.2.3.4", "desc": "wrong password try" }
|
||||
Mar 9 09:18:34 hostname monit[5731]: HttpRequest: access denied -- client 1.2.3.4: wrong password for user 'test2'
|
||||
|
||||
# failJSON: { "time": "2005-08-06T10:14:52", "match": true, "host": "192.168.1.85", "desc": "IP in brackets, gh-2494" }
|
||||
[CEST Aug 6 10:14:52] error : HttpRequest: access denied -- client [192.168.1.85]: wrong password for user 'root'
|
||||
8
fail2ban-master/fail2ban/tests/files/logs/monitorix
Normal file
8
fail2ban-master/fail2ban/tests/files/logs/monitorix
Normal file
@@ -0,0 +1,8 @@
|
||||
# failJSON: { "time": "2021-04-14T08:11:01", "match": false, "desc": "should be ignored: successful request" }
|
||||
Wed Apr 14 08:11:01 2021 - OK - [127.0.0.1] "GET /monitorix-cgi/monitorix.cgi - Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0"
|
||||
# failJSON: { "time": "2021-04-14T08:54:22", "match": true, "host": "127.0.0.1", "desc": "file does not exist" }
|
||||
Wed Apr 14 08:54:22 2021 - NOTEXIST - [127.0.0.1] File does not exist: /manager/html
|
||||
# failJSON: { "time": "2021-04-14T11:24:31", "match": true, "host": "127.0.0.1", "desc": "access not allowed" }
|
||||
Wed Apr 14 11:24:31 2021 - NOTALLOWED - [127.0.0.1] Access not allowed: /monitorix/
|
||||
# failJSON: { "time": "2021-04-14T11:26:08", "match": true, "host": "127.0.0.1", "desc": "authentication error" }
|
||||
Wed Apr 14 11:26:08 2021 - AUTHERR - [127.0.0.1] Authentication error: /monitorix/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user