@cryptotaxi247 / netdata-1 / commits / 4730aee11

fail2ban_plugin: "Currently In Jail" chart added

Ilya committed Mar 24, 2017 at 12:11 UTC 4730aee1167c1a6e518690ebab6625fd2ad59fd0
1 file changed +51 -24
python.d/fail2ban.chart.py
+51 -24
@@ -3,7 +3,7 @@
3 # Author: l2isbad
4
5 from base import LogService
6 -from re import compile
6 +from re import compile as r_compile
7
8 try:
9 from itertools import filterfalse
@@ -12,11 +12,13 @@ except ImportError:
12 from os import access as is_accessible, R_OK
13 from os.path import isdir
14 from glob import glob
15 +import bisect
16
17 priority = 60000
18 retries = 60
18 -REGEX = compile(r'\[([A-Za-z-_]+)][^\[\]]*?(?<!# )enabled = true')
19 -ORDER = ['jails_group']
19 +REGEX_JAILS = r_compile(r'\[([A-Za-z-_]+)][^\[\]]*?(?<!# )enabled = true')
20 +REGEX_DATA = r_compile(r'\[(?P<jail>[a-z]+)\] (?P<ban>[A-Z])[a-z]+ (?P<ipaddr>\d{1,3}(?:\.\d{1,3}){3})')
21 +ORDER = ['jails_bans', 'jails_in_jail']
22
23
24 class Service(LogService):
@@ -26,35 +28,39 @@ class Service(LogService):
28 self.log_path = self.configuration.get('log_path', '/var/log/fail2ban.log')
29 self.conf_path = self.configuration.get('conf_path', '/etc/fail2ban/jail.local')
30 self.conf_dir = self.configuration.get('conf_dir', '')
31 + self.bans = dict()
32 try:
33 self.exclude = self.configuration['exclude'].split()
34 except (KeyError, AttributeError):
32 - self.exclude = []
35 + self.exclude = list()
36
37 def _get_data(self):
38 """
39 Parse new log lines
40 :return: dict
41 """
39 - try:
40 - raw = self._get_raw_data()
41 - if raw is None:
42 - return None
43 - elif not raw:
44 - return self.data
45 - except (ValueError, AttributeError):
42 + raw = self._get_raw_data()
43 + if raw is None:
44 return None
45 + elif not raw:
46 + return self.data
47
48 # Fail2ban logs looks like
49 # 2016-12-25 12:36:04,711 fail2ban.actions[2455]: WARNING [ssh] Ban 178.156.32.231
50 - data = dict(
51 - zip(
52 - self.jails_list,
53 - [len(list(filterfalse(lambda line: (jail + '] Ban') not in line, raw))) for jail in self.jails_list]
54 - ))
55 -
56 - for jail in data:
57 - self.data[jail] += data[jail]
50 + for row in raw:
51 + match = REGEX_DATA.search(row)
52 + if match:
53 + match_dict = match.groupdict()
54 + jail, ban, ipaddr = match_dict['jail'], match_dict['ban'], match_dict['ipaddr']
55 + if jail in self.jails_list:
56 + if ban == 'B':
57 + self.data[jail] += 1
58 + if address_not_in_jail(self.bans[jail], ipaddr, self.data[jail + '_in_jail']):
59 + self.data[jail + '_in_jail'] += 1
60 + else:
61 + if ipaddr in self.bans[jail]:
62 + self.bans[jail].remove(ipaddr)
63 + self.data[jail + '_in_jail'] -= 1
64
65 return self.data
66
@@ -81,17 +87,25 @@ class Service(LogService):
87
88 # If for some reason parse failed we still can START with default jails_list.
89 self.jails_list = list(set(jails_list) - set(self.exclude)) or ['ssh']
90 +
91 self.data = dict([(jail, 0) for jail in self.jails_list])
92 + self.data.update(dict([(jail + '_in_jail', 0) for jail in self.jails_list]))
93 + self.bans = dict([(jail, list()) for jail in self.jails_list])
94 +
95 self.create_dimensions()
96 self.info('Plugin successfully started. Jails: %s' % self.jails_list)
97 return True
98
99 def create_dimensions(self):
100 self.definitions = {
91 - 'jails_group': {'options': [None, "Jails ban statistics", "bans/s", 'jails', 'jail.ban', 'line'],
92 - 'lines': []}}
101 + 'jails_bans': {'options': [None, "Jails Ban Statistics", "bans/s", 'bans', 'jail.bans', 'line'],
102 + 'lines': []},
103 + 'jails_in_jail': {'options': [None, "Currently In Jail", "ip addresses", 'in jail', 'jail.in_jail', 'line'],
104 + 'lines': []},
105 + }
106 for jail in self.jails_list:
94 - self.definitions['jails_group']['lines'].append([jail, jail, 'incremental'])
107 + self.definitions['jails_bans']['lines'].append([jail, jail, 'incremental'])
108 + self.definitions['jails_in_jail']['lines'].append([jail + '_in_jail', jail, 'absolute'])
109
110
111 def parse_conf_dir(conf_dir):
@@ -114,7 +128,7 @@ def parse_conf_dir(conf_dir):
128 raw_data = f.read()
129
130 data = ' '.join(raw_data.split())
117 - jails_list.extend(REGEX.findall(data))
131 + jails_list.extend(REGEX_JAILS.findall(data))
132 jails_list = list(set(jails_list))
133
134 return jails_list, 'can\'t locate any jails in %s. Default jail is [\'ssh\']' % conf_dir
@@ -128,5 +142,18 @@ def parse_conf_path(conf_path):
142 raw_data = jails_conf.read()
143
144 data = raw_data.split()
131 - jails_list = REGEX.findall(' '.join(data))
145 + jails_list = REGEX_JAILS.findall(' '.join(data))
146 return jails_list, 'can\'t locate any jails in %s. Default jail is [\'ssh\']' % conf_path
147 +
148 +
149 +def address_not_in_jail(pool, address, pool_size):
150 + index = bisect.bisect_left(pool, address)
151 + if index < pool_size:
152 + if pool[index] == address:
153 + return False
154 + else:
155 + bisect.insort_left(pool, address)
156 + return True
157 + else:
158 + bisect.insort_left(pool, address)
159 + return True