diff options
author | Ilya Mashchenko <ilya@netdata.cloud> | 2021-12-23 17:05:39 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-23 17:05:39 +0300 |
commit | 438b475cd16e1dc609c448aa38d3253c4de53f5a (patch) | |
tree | 79537812a5da1fb0e8c5de60c30ed7d009bc2dde | |
parent | f2c604eedb05e3486f2feaf715fe00e191c0cf2f (diff) |
feat(python.d/fail2ban): add "Failed attempts" chart, cleanup (#11825)
Co-authored-by: Vladimir Kobal <vlad@prokk.net>
-rw-r--r-- | collectors/python.d.plugin/fail2ban/README.md | 59 | ||||
-rw-r--r-- | collectors/python.d.plugin/fail2ban/fail2ban.chart.py | 57 | ||||
-rw-r--r-- | web/gui/dashboard_info.js | 25 |
3 files changed, 110 insertions, 31 deletions
diff --git a/collectors/python.d.plugin/fail2ban/README.md b/collectors/python.d.plugin/fail2ban/README.md index c1ad994a59..90a59dce07 100644 --- a/collectors/python.d.plugin/fail2ban/README.md +++ b/collectors/python.d.plugin/fail2ban/README.md @@ -10,14 +10,55 @@ Monitors the fail2ban log file to show all bans for all active jails. ## Requirements -- fail2ban.log file MUST BE readable by Netdata (A good idea is to add **create 0640 root netdata** to fail2ban conf at logrotate.d) +The `fail2ban.log` file must be readable by the user `netdata`: -It produces one chart with multiple lines (one line per jail) +- change the file ownership and access permissions. +- update `/etc/logrotate.d/fail2ban` to persists the changes after rotating the log file. + +<details> + <summary>Click to expand the instruction.</summary> + +To change the file ownership and access permissions, execute the following: + +```shell +sudo chown root:netdata /var/log/fail2ban.log +sudo chmod 640 /var/log/fail2ban.log +``` + +To persist the changes after rotating the log file, add `create 640 root netdata` to the `/etc/logrotate.d/fail2ban`: + +```shell +/var/log/fail2ban.log { + + weekly + rotate 4 + compress + + delaycompress + missingok + postrotate + fail2ban-client flushlogs 1>/dev/null + endscript + + # If fail2ban runs as non-root it still needs to have write access + # to logfiles. + # create 640 fail2ban adm + create 640 root netdata +} +``` + +</details> + +## Charts + +- Failed attempts in attempts/s +- Bans in bans/s +- Banned IP addresses (since the last restart of netdata) in ips ## Configuration -Edit the `python.d/fail2ban.conf` configuration file using `edit-config` from the Netdata [config -directory](/docs/configure/nodes.md), which is typically at `/etc/netdata`. +Edit the `python.d/fail2ban.conf` configuration file using `edit-config` from the +Netdata [config directory](/docs/configure/nodes.md), which is typically at `/etc/netdata`. ```bash cd /etc/netdata # Replace this path with your Netdata config directory, if different @@ -28,13 +69,13 @@ Sample: ```yaml local: - log_path: '/var/log/fail2ban.log' - conf_path: '/etc/fail2ban/jail.local' - exclude: 'dropbear apache' + log_path: '/var/log/fail2ban.log' + conf_path: '/etc/fail2ban/jail.local' + exclude: 'dropbear apache' ``` -If no configuration is given, module will attempt to read log file at `/var/log/fail2ban.log` and conf file at `/etc/fail2ban/jail.local`. -If conf file is not found default jail is `ssh`. +If no configuration is given, module will attempt to read log file at `/var/log/fail2ban.log` and conf file +at `/etc/fail2ban/jail.local`. If conf file is not found default jail is `ssh`. --- diff --git a/collectors/python.d.plugin/fail2ban/fail2ban.chart.py b/collectors/python.d.plugin/fail2ban/fail2ban.chart.py index 99dbf79dd0..76f6d92b4e 100644 --- a/collectors/python.d.plugin/fail2ban/fail2ban.chart.py +++ b/collectors/python.d.plugin/fail2ban/fail2ban.chart.py @@ -11,8 +11,9 @@ from glob import glob from bases.FrameworkServices.LogService import LogService ORDER = [ + 'jails_failed_attempts', 'jails_bans', - 'jails_in_jail', + 'jails_banned_ips', ] @@ -23,40 +24,49 @@ def charts(jails): ch = { ORDER[0]: { - 'options': [None, 'Jails Ban Rate', 'bans/s', 'bans', 'jail.bans', 'line'], + 'options': [None, 'Failed attempts', 'attempts/s', 'failed attempts', 'fail2ban.failed_attempts', 'line'], 'lines': [] }, ORDER[1]: { - 'options': [None, 'Banned IPs (since the last restart of netdata)', 'IPs', 'in jail', - 'jail.in_jail', 'line'], + 'options': [None, 'Bans', 'bans/s', 'bans', 'fail2ban.bans', 'line'], + 'lines': [] + }, + ORDER[2]: { + 'options': [None, 'Banned IP addresses (since the last restart of netdata)', 'ips', 'banned ips', + 'fail2ban.banned_ips', 'line'], 'lines': [] }, } for jail in jails: - dim = [ - jail, - jail, - 'incremental', - ] + dim = ['{0}_failed_attempts'.format(jail), jail, 'incremental'] ch[ORDER[0]]['lines'].append(dim) - dim = [ - '{0}_in_jail'.format(jail), - jail, - 'absolute', - ] + dim = [jail, jail, 'incremental'] ch[ORDER[1]]['lines'].append(dim) + dim = ['{0}_in_jail'.format(jail), jail, 'absolute'] + ch[ORDER[2]]['lines'].append(dim) + return ch RE_JAILS = re.compile(r'\[([a-zA-Z0-9_-]+)\][^\[\]]+?enabled\s+= +(true|yes|false|no)') +ACTION_BAN = 'Ban' +ACTION_UNBAN = 'Unban' +ACTION_RESTORE_BAN = 'Restore Ban' +ACTION_FOUND = 'Found' + # Example: -# 2018-09-12 11:45:53,715 fail2ban.actions[25029]: WARNING [ssh] Unban 195.201.88.33 -# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Ban 217.59.246.27 -# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Restore Ban 217.59.246.27 -RE_DATA = re.compile(r'\[(?P<jail>[A-Za-z-_0-9]+)\] (?P<action>Unban|Ban|Restore Ban) (?P<ip>[a-f0-9.:]+)') +# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Found 203.0.113.1 +# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Ban 203.0.113.1 +# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Restore Ban 203.0.113.1 +# 2018-09-12 11:45:53,715 fail2ban.actions[25029]: WARNING [ssh] Unban 203.0.113.1 +RE_DATA = re.compile( + r'\[(?P<jail>[A-Za-z-_0-9]+)\] (?P<action>{0}|{1}|{2}|{3}) (?P<ip>[a-f0-9.:]+)'.format( + ACTION_BAN, ACTION_UNBAN, ACTION_RESTORE_BAN, ACTION_FOUND + ) +) DEFAULT_JAILS = [ 'ssh', @@ -94,6 +104,7 @@ class Service(LogService): self.monitoring_jails = self.jails_auto_detection() for jail in self.monitoring_jails: + self.data['{0}_failed_attempts'.format(jail)] = 0 self.data[jail] = 0 self.data['{0}_in_jail'.format(jail)] = 0 @@ -124,12 +135,14 @@ class Service(LogService): jail, action, ip = match['jail'], match['action'], match['ip'] - if action == 'Ban' or action == 'Restore Ban': + if action == ACTION_FOUND: + self.data['{0}_failed_attempts'.format(jail)] += 1 + elif action in (ACTION_BAN, ACTION_RESTORE_BAN): self.data[jail] += 1 if ip not in self.banned_ips[jail]: self.banned_ips[jail].add(ip) self.data['{0}_in_jail'.format(jail)] += 1 - else: + elif action == ACTION_UNBAN: if ip in self.banned_ips[jail]: self.banned_ips[jail].remove(ip) self.data['{0}_in_jail'.format(jail)] -= 1 @@ -196,9 +209,9 @@ class Service(LogService): if name in exclude: continue - if status in ('true','yes') and name not in active_jails: + if status in ('true', 'yes') and name not in active_jails: active_jails.append(name) - elif status in ('false','no') and name in active_jails: + elif status in ('false', 'no') and name in active_jails: active_jails.remove(name) return active_jails or DEFAULT_JAILS diff --git a/web/gui/dashboard_info.js b/web/gui/dashboard_info.js index a87b69bd74..128301a8ac 100644 --- a/web/gui/dashboard_info.js +++ b/web/gui/dashboard_info.js @@ -703,6 +703,12 @@ netdataDashboard.menu = { icon: '<i class="fas fa-brain"></i>', info: 'Charts relating to anomaly detection, increased <code>anomalous</code> dimensions or a higher than usual <code>anomaly_rate</code> could be signs of some abnormal behaviour. Read our <a href="https://learn.netdata.cloud/guides/monitor/anomaly-detection" target="_blank">anomaly detection guide</a> for more details.' }, + + 'fail2ban': { + title: 'Fail2ban', + icon: '<i class="fas fa-shield-alt"></i>', + info: 'Netdata keeps track of the current jail status by reading the Fail2ban log file.' + }, }; @@ -6373,4 +6379,23 @@ netdataDashboard.context = { info: 'Diagnostic metrics relating to training time of anomaly detection. ' }, + // ------------------------------------------------------------------------ + // Supervisor + + 'fail2ban.failed_attempts': { + info: '<p>The number of failed attempts.</p>'+ + '<p>This chart reflects the number of \'Found\' lines. '+ + 'Found means a line in the service’s log file matches the failregex in its filter.</p>' + }, + + 'fail2ban.bans': { + info: '<p>The number of bans.</p>'+ + '<p>This chart reflects the number of \'Ban\' and \'Restore Ban\' lines. '+ + 'Ban action happens when the number of failed attempts (maxretry) occurred in the last configured interval (findtime).</p>' + }, + + 'fail2ban.banned_ips': { + info: '<p>The number of banned IP addresses.</p>' + }, + }; |