@cryptotaxi247 / netdata-1 / commits / 438b475cd

feat(python.d/fail2ban): add "Failed attempts" chart, cleanup (#11825)

Co-authored-by: Vladimir Kobal <vlad@prokk.net>

Ilya Mashchenko committed Dec 23, 2021 at 17:05 UTC 438b475cd16e1dc609c448aa38d3253c4de53f5a
3 files changed +110 -31
collectors/python.d.plugin/fail2ban/README.md
+50 -9
@@ -10,14 +10,55 @@ Monitors the fail2ban log file to show all bans for all active jails.
10
11 ## Requirements
12
13 -- fail2ban.log file MUST BE readable by Netdata (A good idea is to add **create 0640 root netdata** to fail2ban conf at logrotate.d)
13 +The `fail2ban.log` file must be readable by the user `netdata`:
14
15 -It produces one chart with multiple lines (one line per jail)
15 +- change the file ownership and access permissions.
16 +- update `/etc/logrotate.d/fail2ban` to persists the changes after rotating the log file.
17 +
18 +<details>
19 + <summary>Click to expand the instruction.</summary>
20 +
21 +To change the file ownership and access permissions, execute the following:
22 +
23 +```shell
24 +sudo chown root:netdata /var/log/fail2ban.log
25 +sudo chmod 640 /var/log/fail2ban.log
26 +```
27 +
28 +To persist the changes after rotating the log file, add `create 640 root netdata` to the `/etc/logrotate.d/fail2ban`:
29 +
30 +```shell
31 +/var/log/fail2ban.log {
32 +
33 + weekly
34 + rotate 4
35 + compress
36 +
37 + delaycompress
38 + missingok
39 + postrotate
40 + fail2ban-client flushlogs 1>/dev/null
41 + endscript
42 +
43 + # If fail2ban runs as non-root it still needs to have write access
44 + # to logfiles.
45 + # create 640 fail2ban adm
46 + create 640 root netdata
47 +}
48 +```
49 +
50 +</details>
51 +
52 +## Charts
53 +
54 +- Failed attempts in attempts/s
55 +- Bans in bans/s
56 +- Banned IP addresses (since the last restart of netdata) in ips
57
58 ## Configuration
59
19 -Edit the `python.d/fail2ban.conf` configuration file using `edit-config` from the Netdata [config
20 -directory](/docs/configure/nodes.md), which is typically at `/etc/netdata`.
60 +Edit the `python.d/fail2ban.conf` configuration file using `edit-config` from the
61 +Netdata [config directory](/docs/configure/nodes.md), which is typically at `/etc/netdata`.
62
63 ```bash
64 cd /etc/netdata # Replace this path with your Netdata config directory, if different
@@ -28,13 +69,13 @@ Sample:
69
70 ```yaml
71 local:
31 - log_path: '/var/log/fail2ban.log'
32 - conf_path: '/etc/fail2ban/jail.local'
33 - exclude: 'dropbear apache'
72 + log_path: '/var/log/fail2ban.log'
73 + conf_path: '/etc/fail2ban/jail.local'
74 + exclude: 'dropbear apache'
75 ```
76
36 -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`.
37 -If conf file is not found default jail is `ssh`.
77 +If no configuration is given, module will attempt to read log file at `/var/log/fail2ban.log` and conf file
78 +at `/etc/fail2ban/jail.local`. If conf file is not found default jail is `ssh`.
79
80 ---
81
collectors/python.d.plugin/fail2ban/fail2ban.chart.py
+35 -22
@@ -11,8 +11,9 @@ from glob import glob
11 from bases.FrameworkServices.LogService import LogService
12
13 ORDER = [
14 + 'jails_failed_attempts',
15 'jails_bans',
15 - 'jails_in_jail',
16 + 'jails_banned_ips',
17 ]
18
19
@@ -23,40 +24,49 @@ def charts(jails):
24
25 ch = {
26 ORDER[0]: {
26 - 'options': [None, 'Jails Ban Rate', 'bans/s', 'bans', 'jail.bans', 'line'],
27 + 'options': [None, 'Failed attempts', 'attempts/s', 'failed attempts', 'fail2ban.failed_attempts', 'line'],
28 'lines': []
29 },
30 ORDER[1]: {
30 - 'options': [None, 'Banned IPs (since the last restart of netdata)', 'IPs', 'in jail',
31 - 'jail.in_jail', 'line'],
31 + 'options': [None, 'Bans', 'bans/s', 'bans', 'fail2ban.bans', 'line'],
32 + 'lines': []
33 + },
34 + ORDER[2]: {
35 + 'options': [None, 'Banned IP addresses (since the last restart of netdata)', 'ips', 'banned ips',
36 + 'fail2ban.banned_ips', 'line'],
37 'lines': []
38 },
39 }
40 for jail in jails:
36 - dim = [
37 - jail,
38 - jail,
39 - 'incremental',
40 - ]
41 + dim = ['{0}_failed_attempts'.format(jail), jail, 'incremental']
42 ch[ORDER[0]]['lines'].append(dim)
43
43 - dim = [
44 - '{0}_in_jail'.format(jail),
45 - jail,
46 - 'absolute',
47 - ]
44 + dim = [jail, jail, 'incremental']
45 ch[ORDER[1]]['lines'].append(dim)
46
47 + dim = ['{0}_in_jail'.format(jail), jail, 'absolute']
48 + ch[ORDER[2]]['lines'].append(dim)
49 +
50 return ch
51
52
53 RE_JAILS = re.compile(r'\[([a-zA-Z0-9_-]+)\][^\[\]]+?enabled\s+= +(true|yes|false|no)')
54
55 +ACTION_BAN = 'Ban'
56 +ACTION_UNBAN = 'Unban'
57 +ACTION_RESTORE_BAN = 'Restore Ban'
58 +ACTION_FOUND = 'Found'
59 +
60 # Example:
56 -# 2018-09-12 11:45:53,715 fail2ban.actions[25029]: WARNING [ssh] Unban 195.201.88.33
57 -# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Ban 217.59.246.27
58 -# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Restore Ban 217.59.246.27
59 -RE_DATA = re.compile(r'\[(?P<jail>[A-Za-z-_0-9]+)\] (?P<action>Unban|Ban|Restore Ban) (?P<ip>[a-f0-9.:]+)')
61 +# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Found 203.0.113.1
62 +# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Ban 203.0.113.1
63 +# 2018-09-12 11:45:58,727 fail2ban.actions[25029]: WARNING [ssh] Restore Ban 203.0.113.1
64 +# 2018-09-12 11:45:53,715 fail2ban.actions[25029]: WARNING [ssh] Unban 203.0.113.1
65 +RE_DATA = re.compile(
66 + r'\[(?P<jail>[A-Za-z-_0-9]+)\] (?P<action>{0}|{1}|{2}|{3}) (?P<ip>[a-f0-9.:]+)'.format(
67 + ACTION_BAN, ACTION_UNBAN, ACTION_RESTORE_BAN, ACTION_FOUND
68 + )
69 +)
70
71 DEFAULT_JAILS = [
72 'ssh',
@@ -94,6 +104,7 @@ class Service(LogService):
104
105 self.monitoring_jails = self.jails_auto_detection()
106 for jail in self.monitoring_jails:
107 + self.data['{0}_failed_attempts'.format(jail)] = 0
108 self.data[jail] = 0
109 self.data['{0}_in_jail'.format(jail)] = 0
110
@@ -124,12 +135,14 @@ class Service(LogService):
135
136 jail, action, ip = match['jail'], match['action'], match['ip']
137
127 - if action == 'Ban' or action == 'Restore Ban':
138 + if action == ACTION_FOUND:
139 + self.data['{0}_failed_attempts'.format(jail)] += 1
140 + elif action in (ACTION_BAN, ACTION_RESTORE_BAN):
141 self.data[jail] += 1
142 if ip not in self.banned_ips[jail]:
143 self.banned_ips[jail].add(ip)
144 self.data['{0}_in_jail'.format(jail)] += 1
132 - else:
145 + elif action == ACTION_UNBAN:
146 if ip in self.banned_ips[jail]:
147 self.banned_ips[jail].remove(ip)
148 self.data['{0}_in_jail'.format(jail)] -= 1
@@ -196,9 +209,9 @@ class Service(LogService):
209 if name in exclude:
210 continue
211
199 - if status in ('true','yes') and name not in active_jails:
212 + if status in ('true', 'yes') and name not in active_jails:
213 active_jails.append(name)
201 - elif status in ('false','no') and name in active_jails:
214 + elif status in ('false', 'no') and name in active_jails:
215 active_jails.remove(name)
216
217 return active_jails or DEFAULT_JAILS
web/gui/dashboard_info.js
+25
@@ -703,6 +703,12 @@ netdataDashboard.menu = {
703 icon: '<i class="fas fa-brain"></i>',
704 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.'
705 },
706 +
707 + 'fail2ban': {
708 + title: 'Fail2ban',
709 + icon: '<i class="fas fa-shield-alt"></i>',
710 + info: 'Netdata keeps track of the current jail status by reading the Fail2ban log file.'
711 + },
712 };
713
714
@@ -6373,4 +6379,23 @@ netdataDashboard.context = {
6379 info: 'Diagnostic metrics relating to training time of anomaly detection. '
6380 },
6381
6382 + // ------------------------------------------------------------------------
6383 + // Supervisor
6384 +
6385 + 'fail2ban.failed_attempts': {
6386 + info: '<p>The number of failed attempts.</p>'+
6387 + '<p>This chart reflects the number of \'Found\' lines. '+
6388 + 'Found means a line in the service’s log file matches the failregex in its filter.</p>'
6389 + },
6390 +
6391 + 'fail2ban.bans': {
6392 + info: '<p>The number of bans.</p>'+
6393 + '<p>This chart reflects the number of \'Ban\' and \'Restore Ban\' lines. '+
6394 + 'Ban action happens when the number of failed attempts (maxretry) occurred in the last configured interval (findtime).</p>'
6395 + },
6396 +
6397 + 'fail2ban.banned_ips': {
6398 + info: '<p>The number of banned IP addresses.</p>'
6399 + },
6400 +
6401 };