diff --git a/CHANGELOG.md b/CHANGELOG.md index f9cafbe6..92929b83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ Monitoring Plugins: * about-me: Add Moodle detection * dhcp-scope-usage: Ignore PercentageInUse fractions * disk-io: Re-add support for Windows after last rewrite +* fail2ban: More compact output (closes #141) * fs-inodes: Check inode usage on real and different disks. `--mount` parameter is deprecated. * librenms-alerts, librenms-health: Compact output is the new default and shows non-OK only * mysql-thread-cache: DB daemon must have been running for an hour before the cache hit rate is measured. diff --git a/check-plugins/fail2ban/README.rst b/check-plugins/fail2ban/README.rst index 925cbe39..02dde4e5 100644 --- a/check-plugins/fail2ban/README.rst +++ b/check-plugins/fail2ban/README.rst @@ -7,71 +7,6 @@ Overview Checks the amount of banned IP addresses for all jails in Fail2ban. -Permission denied to socket: /var/run/fail2ban/fail2ban.sock, (you must be root) --------------------------------------------------------------------------------- - -The Fail2ban client (used by this check plugin internally) works only with ùser ``root`` by default. The reasons: - -* Fail2ban does not have individual permission or a user privilege model. -* If you would allow the Fail2ban client accessing the Fail2ban sever for non-root, you could stop the server, change runtime config, ban, unban, etc. - - -Preparing Fail2ban by changing permissions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Tested on Debian 11. - -The communication takes place via unix-socket ``/var/run/fail2ban/fail2ban.sock`` which has the following permissions: - -.. code-block:: text - - srwx------ 1 root root ... /var/run/fail2ban/fail2ban.sock - -So you have to grant access to ``fail2ban.sock`` for a user like ``nagios`` or ``icinga``, for example like so: - -.. code-block:: bash - - sudo groupadd fail2ban - sudo usermod --append --groups fail2ban nagios - sudo chown root:fail2ban /var/run/fail2ban/fail2ban.sock - sudo chmod g+w /var/run/fail2ban/fail2ban.sock - -After that, this (and so the check plugin) should work: - -.. code-block:: bash - - sudo -u nagios /usr/bin/fail2ban-client status - sudo -u nagios /usr/lib64/nagios/plugins/fail2ban - -To persist on a system where Fail2ban is managed by Systemd, add the following to the Fail2ban service override file: - -.. code-block:: bash - - sudo systemctl edit fail2ban - -.. code-block:: text - - [Service] - ExecStartPost=/usr/bin/sh -c "while ! [ -S /var/run/fail2ban/fail2ban.sock ]; do sleep 1; done" - ExecStartPost=/usr/bin/chgrp fail2ban /var/run/fail2ban/fail2ban.sock - ExecStartPost=/usr/bin/chmod g+w /var/run/fail2ban/fail2ban.sock - - -Preparing Fail2ban by using sudo -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Tested on RHEL 7+. - -As an alternative you might add a sudoers rule, for example in ``/etc/sudoers.d/fail2ban``: - -.. code-block:: text - - Defaults:icinga !requiretty - icinga ALL = NOPASSWD: /usr/lib64/nagios/plugins/fail2ban - -Click this link to find `a list of sudoers files for all main Linux distributions `_ for Icinga. - - Fact Sheet ---------- @@ -113,18 +48,20 @@ Usage Examples .. code-block:: bash ./fail2ban --warning 2500 --critical 10000 - + Output: .. code-block:: text - IPs banned - apache-dos: 2, portscan: 0, sshd: 5432 [WARNING] + 7406 IPs banned + * 5432 in jail "sshd" [WARNING] + * 1974 in jail "portscan" States ------ -* WARN or CRIT if number of blocked IP addresses is above a given threshold per jail. +* WARN or CRIT if the number of blocked IP addresses in any jail exceeds a specified threshold. Perfdata / Metrics @@ -135,7 +72,71 @@ Perfdata / Metrics :header-rows: 1 Name, Type, Description - , Number, Number of blocked IP addresses. + , Number, Number of blocked IP addresses (per jail). + + +Troubleshooting +--------------- + +Permission denied to socket: /var/run/fail2ban/fail2ban.sock (you must be root) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Fail2ban client (used by this check plugin internally) works only with user ``root`` by default. The reasons: + +* Fail2ban does not have individual permission or a user privilege model. +* If you would allow the Fail2ban client accessing the Fail2ban sever for non-root, you could stop the server, change runtime config, ban, unban, etc. + + +Preparing Fail2ban by changing permissions + Tested on Debian 11. + + The communication takes place via unix-socket ``/var/run/fail2ban/fail2ban.sock`` which has the following permissions: + + .. code-block:: text + + srwx------ 1 root root ... /var/run/fail2ban/fail2ban.sock + + So you have to grant access to ``fail2ban.sock`` for a user like ``nagios`` or ``icinga``, for example like so: + + .. code-block:: bash + + sudo groupadd fail2ban + sudo usermod --append --groups fail2ban nagios + sudo chown root:fail2ban /var/run/fail2ban/fail2ban.sock + sudo chmod g+w /var/run/fail2ban/fail2ban.sock + + After that, this (and so the check plugin) should work: + + .. code-block:: bash + + sudo -u nagios /usr/bin/fail2ban-client status + sudo -u nagios /usr/lib64/nagios/plugins/fail2ban + + To persist on a system where Fail2ban is managed by Systemd, add the following to the Fail2ban service override file: + + .. code-block:: bash + + sudo systemctl edit fail2ban + + .. code-block:: text + + [Service] + ExecStartPost=/usr/bin/sh -c "while ! [ -S /var/run/fail2ban/fail2ban.sock ]; do sleep 1; done" + ExecStartPost=/usr/bin/chgrp fail2ban /var/run/fail2ban/fail2ban.sock + ExecStartPost=/usr/bin/chmod g+w /var/run/fail2ban/fail2ban.sock + + +Preparing Fail2ban by using sudo + Tested on RHEL 7+. + + As an alternative you might add a sudoers rule, for example in ``/etc/sudoers.d/fail2ban``: + + .. code-block:: text + + Defaults:icinga !requiretty + icinga ALL = NOPASSWD: /usr/lib64/nagios/plugins/fail2ban + + Click this link to find `a list of sudoers files for all main Linux distributions `_ for Icinga. Credits, License diff --git a/check-plugins/fail2ban/fail2ban b/check-plugins/fail2ban/fail2ban index 79bfb2e0..6d28c35a 100755 --- a/check-plugins/fail2ban/fail2ban +++ b/check-plugins/fail2ban/fail2ban @@ -23,7 +23,7 @@ from lib.globals import (STATE_CRIT, STATE_OK, # pylint: disable=C0413 STATE_UNKNOWN, STATE_WARN) __author__ = 'Linuxfabrik GmbH, Zurich/Switzerland' -__version__ = '2023112901' +__version__ = '2024120201' DESCRIPTION = 'In fail2ban, checks the amount of banned IP addresses per jail.' @@ -106,7 +106,7 @@ def main(): lib.base.cu('Problem while testing the status of the fail2ban server.') # extract the jail list - jail_list = lib.txt.extract_str(stdout, 'Jail list:\t', '\n') + jail_list = lib.txt.extract_str(stdout, 'Jail list:', '\n').strip() if not jail_list: lib.base.cu('No jails found.') jail_list = jail_list.split(', ') @@ -115,6 +115,7 @@ def main(): msg = '' perfdata = '' state = STATE_OK + count = 0 # analyze data # for each jail_name: @@ -143,21 +144,29 @@ def main(): # important to convert the result to an integer for the comparison later on if f2b_currently_banned: f2b_currently_banned = int(f2b_currently_banned) + count += f2b_currently_banned else: f2b_currently_banned = 0 jail_state = lib.base.get_state(f2b_currently_banned, args.WARN, args.CRIT) state = lib.base.get_worst(state, jail_state) - msg += '{}: {}{}, '.format( - jail, + msg += '* {} in jail "{}"{}\n'.format( f2b_currently_banned, + jail, lib.base.state2str(state, prefix=' '), ) perfdata += lib.base.get_perfdata(jail, f2b_currently_banned, None, args.WARN, args.CRIT, 0, None) # pylint: disable=C0301 # over and out - if msg: - lib.base.oao('IPs banned - ' + msg[:-2], state, perfdata, always_ok=args.ALWAYS_OK) + if not count: + msg = 'Everything is ok.' + else: + msg = '{} {} banned\n{}'.format( + count, + lib.txt.pluralize('IP', count), + msg, + ) + lib.base.oao(msg, state, perfdata, always_ok=args.ALWAYS_OK) if __name__ == '__main__': diff --git a/check-plugins/fail2ban/unit-test/run b/check-plugins/fail2ban/unit-test/run index 984240ab..c79f101e 100755 --- a/check-plugins/fail2ban/unit-test/run +++ b/check-plugins/fail2ban/unit-test/run @@ -26,19 +26,28 @@ class TestCheck(unittest.TestCase): def test_if_check_runs_EXAMPLE01_ping_nok(self): stdout, stderr, retc = lib.base.coe(lib.shell.shell_exec(self.check + ' --test=stdout/EXAMPLE01,,1')) - self.assertRegex(stdout, r'Problem while testing if the fail2ban server is alive.') + self.assertIn('Problem while testing if the fail2ban server is alive.', stdout) self.assertEqual(stderr, '') self.assertEqual(retc, STATE_UNKNOWN) def test_if_check_runs_EXAMPLE01(self): stdout, stderr, retc = lib.base.coe(lib.shell.shell_exec(self.check + ' --test=stdout/EXAMPLE01,,0')) - self.assertRegex(stdout, r'IPs banned - apache-dos: 2, portscan: 0, sshd: 5432 \[WARNING\]') + self.assertIn('5434 IPs banned', stdout) + self.assertIn('* 2 in jail "apache-dos"', stdout) + self.assertIn('* 0 in jail "portscan"', stdout) + self.assertIn('* 5432 in jail "sshd" [WARNING]', stdout) self.assertEqual(stderr, '') self.assertEqual(retc, STATE_WARN) + def test_if_check_runs_EXAMPLE02(self): + stdout, stderr, retc = lib.base.coe(lib.shell.shell_exec(self.check + ' --test=stdout/EXAMPLE02,,0')) + self.assertIn('Everything is ok.', stdout) + self.assertEqual(stderr, '') + self.assertEqual(retc, STATE_OK) + def test_if_check_runs_EXAMPLE99(self): stdout, stderr, retc = lib.base.coe(lib.shell.shell_exec(self.check + ' --test=stdout/EXAMPLE99,,0')) - self.assertRegex(stdout, r'No jails found.') + self.assertIn('No jails found.', stdout) self.assertEqual(stderr, '') self.assertEqual(retc, STATE_UNKNOWN) diff --git a/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-ping b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-ping new file mode 100644 index 00000000..4a9d8811 --- /dev/null +++ b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-ping @@ -0,0 +1 @@ +Server replied: pong \ No newline at end of file diff --git a/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status new file mode 100644 index 00000000..f6215f7f --- /dev/null +++ b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status @@ -0,0 +1,3 @@ +Status +|- Number of jail: 3 +`- Jail list: apache-dos, portscan, sshd diff --git a/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-apache-dos b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-apache-dos new file mode 100644 index 00000000..e6ada1dd --- /dev/null +++ b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-apache-dos @@ -0,0 +1,9 @@ +Status for the jail: apache-dos +|- Filter +| |- Currently failed: 14 +| |- Total failed: 2187665 +| `- File list: /var/log/httpd/domain.ch-access.log /var/log/httpd/domain/log/httpd/domain.log /var/log/httpd/domain-access.log /var/log/httpd/domain.log /var/log/httpd/domain.log /var/log/httpd/domain.log /var/log/httpd/domain-access.log /var/log/httpd/domain-access.log /var/log/httpd/domain.log /var/log/httpd/domain.log /var/log/httpd/domain.log /var/log/httpd/domain-access.log /var/log/httpd/domain-access.log /var/log/httpd/domain.log /var/log/httpd/domain-access.log /var/log/httpd/domain.log /var/log/httpd/domain.log /var/log/httpd/domain.log /var/log/httpd/domain /var/log/httpd/domain /var/log/httpd/domain.log /var/log/httpd/domain-access.log /var/log/httpd/domain-access.log +`- Actions + |- Currently banned: 0 + |- Total banned: 518 + `- Banned IP list: diff --git a/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-portscan b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-portscan new file mode 100644 index 00000000..0f2ed37f --- /dev/null +++ b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-portscan @@ -0,0 +1,9 @@ +Status for the jail: portscan +|- Filter +| |- Currently failed: 0 +| |- Total failed: 0 +| `- Journal matches: _TRANSPORT=kernel +`- Actions + |- Currently banned: 0 + |- Total banned: 0 + `- Banned IP list: \ No newline at end of file diff --git a/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-sshd b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-sshd new file mode 100644 index 00000000..f99c27ba --- /dev/null +++ b/check-plugins/fail2ban/unit-test/stdout/EXAMPLE02-status-sshd @@ -0,0 +1,9 @@ +Status for the jail: sshd +|- Filter +| |- Currently failed: 0 +| |- Total failed: 0 +| `- Journal matches: _TRANSPORT=kernel +`- Actions + |- Currently banned: 0 + |- Total banned: 0 + `- Banned IP list: \ No newline at end of file