diff options
Diffstat (limited to 'python.d/fail2ban.chart.py')
-rw-r--r-- | python.d/fail2ban.chart.py | 213 |
1 files changed, 0 insertions, 213 deletions
diff --git a/python.d/fail2ban.chart.py b/python.d/fail2ban.chart.py deleted file mode 100644 index 895833f8..00000000 --- a/python.d/fail2ban.chart.py +++ /dev/null @@ -1,213 +0,0 @@ -# -*- coding: utf-8 -*- -# Description: fail2ban log netdata python.d module -# Author: l2isbad - -import bisect - -from glob import glob -from re import compile as r_compile -from os import access as is_accessible, R_OK -from os.path import isdir, getsize - - -from bases.FrameworkServices.LogService import LogService - -priority = 60000 -retries = 60 -REGEX_JAILS = r_compile(r'\[([a-zA-Z0-9_-]+)\][^\[\]]+?enabled\s+= (true|false)') -REGEX_DATA = r_compile(r'\[(?P<jail>[A-Za-z-_0-9]+)\] (?P<action>U|B)[a-z]+ (?P<ipaddr>\d{1,3}(?:\.\d{1,3}){3})') -ORDER = ['jails_bans', 'jails_in_jail'] - - -class Service(LogService): - """ - fail2ban log class - Reads logs line by line - Jail auto detection included - It produces following charts: - * Bans per second for every jail - * Banned IPs for every jail (since the last restart of netdata) - """ - def __init__(self, configuration=None, name=None): - LogService.__init__(self, configuration=configuration, name=name) - self.order = ORDER - self.definitions = dict() - self.log_path = self.configuration.get('log_path', '/var/log/fail2ban.log') - self.conf_path = self.configuration.get('conf_path', '/etc/fail2ban/jail.local') - self.conf_dir = self.configuration.get('conf_dir', '/etc/fail2ban/jail.d/') - self.exclude = self.configuration.get('exclude') - - def _get_data(self): - """ - Parse new log lines - :return: dict - """ - raw = self._get_raw_data() - if raw is None: - return None - elif not raw: - return self.to_netdata - - # Fail2ban logs looks like - # 2016-12-25 12:36:04,711 fail2ban.actions[2455]: WARNING [ssh] Ban 178.156.32.231 - for row in raw: - match = REGEX_DATA.search(row) - if match: - match_dict = match.groupdict() - jail, action, ipaddr = match_dict['jail'], match_dict['action'], match_dict['ipaddr'] - if jail in self.jails_list: - if action == 'B': - self.to_netdata[jail] += 1 - if address_not_in_jail(self.banned_ips[jail], ipaddr, self.to_netdata[jail + '_in_jail']): - self.to_netdata[jail + '_in_jail'] += 1 - else: - if ipaddr in self.banned_ips[jail]: - self.banned_ips[jail].remove(ipaddr) - self.to_netdata[jail + '_in_jail'] -= 1 - - return self.to_netdata - - def check(self): - """ - :return: bool - - Check if the "log_path" is not empty and readable - """ - - if not (is_accessible(self.log_path, R_OK) and getsize(self.log_path) != 0): - self.error('%s is not readable or empty' % self.log_path) - return False - self.jails_list, self.to_netdata, self.banned_ips = self.jails_auto_detection_() - self.definitions = create_definitions_(self.jails_list) - self.info('Jails: %s' % self.jails_list) - return True - - def jails_auto_detection_(self): - """ - return: <tuple> - - * jails_list - list of enabled jails (['ssh', 'apache', ...]) - * to_netdata - dict ({'ssh': 0, 'ssh_in_jail': 0, ...}) - * banned_ips - here will be stored all the banned ips ({'ssh': ['1.2.3.4', '5.6.7.8', ...], ...}) - """ - raw_jails_list = list() - jails_list = list() - - for raw_jail in parse_configuration_files_(self.conf_path, self.conf_dir, self.error): - raw_jails_list.extend(raw_jail) - - for jail, status in raw_jails_list: - if status == 'true' and jail not in jails_list: - jails_list.append(jail) - elif status == 'false' and jail in jails_list: - jails_list.remove(jail) - # If for some reason parse failed we still can START with default jails_list. - jails_list = list(set(jails_list) - set(self.exclude.split() - if isinstance(self.exclude, str) else list())) or ['ssh'] - - to_netdata = dict([(jail, 0) for jail in jails_list]) - to_netdata.update(dict([(jail + '_in_jail', 0) for jail in jails_list])) - banned_ips = dict([(jail, list()) for jail in jails_list]) - - return jails_list, to_netdata, banned_ips - - -def create_definitions_(jails_list): - """ - Chart definitions creating - """ - - definitions = { - 'jails_bans': {'options': [None, 'Jails Ban Statistics', 'bans/s', 'bans', 'jail.bans', 'line'], - 'lines': []}, - 'jails_in_jail': {'options': [None, 'Banned IPs (since the last restart of netdata)', 'IPs', - 'in jail', 'jail.in_jail', 'line'], - 'lines': []}} - for jail in jails_list: - definitions['jails_bans']['lines'].append([jail, jail, 'incremental']) - definitions['jails_in_jail']['lines'].append([jail + '_in_jail', jail, 'absolute']) - - return definitions - - -def parse_configuration_files_(jails_conf_path, jails_conf_dir, print_error): - """ - :param jails_conf_path: <str> - :param jails_conf_dir: <str> - :param print_error: <function> - :return: <tuple> - - Uses "find_jails_in_files" function to find all jails in the "jails_conf_dir" directory - and in the "jails_conf_path" - - All files must endswith ".local" or ".conf" - Return order is important. - According man jail.conf it should be - * jail.conf - * jail.d/*.conf (in alphabetical order) - * jail.local - * jail.d/*.local (in alphabetical order) - """ - path_conf, path_local, dir_conf, dir_local = list(), list(), list(), list() - - # Parse files in the directory - if not (isinstance(jails_conf_dir, str) and isdir(jails_conf_dir)): - print_error('%s is not a directory' % jails_conf_dir) - else: - dir_conf = list(filter(lambda conf: is_accessible(conf, R_OK), glob(jails_conf_dir + '/*.conf'))) - dir_local = list(filter(lambda local: is_accessible(local, R_OK), glob(jails_conf_dir + '/*.local'))) - if not (dir_conf or dir_local): - print_error('%s is empty or not readable' % jails_conf_dir) - else: - dir_conf, dir_local = (find_jails_in_files(dir_conf, print_error), - find_jails_in_files(dir_local, print_error)) - - # Parse .conf and .local files - if isinstance(jails_conf_path, str) and jails_conf_path.endswith(('.local', '.conf')): - path_conf, path_local = (find_jails_in_files([jails_conf_path.split('.')[0] + '.conf'], print_error), - find_jails_in_files([jails_conf_path.split('.')[0] + '.local'], print_error)) - - return path_conf, dir_conf, path_local, dir_local - - -def find_jails_in_files(list_of_files, print_error): - """ - :param list_of_files: <list> - :param print_error: <function> - :return: <list> - - Open a file and parse it to find all (enabled and disabled) jails - The output is a list of tuples: - [('ssh', 'true'), ('apache', 'false'), ...] - """ - jails_list = list() - for conf in list_of_files: - if is_accessible(conf, R_OK): - with open(conf, 'rt') as f: - raw_data = f.readlines() - data = ' '.join(line for line in raw_data if line.startswith(('[', 'enabled'))) - jails_list.extend(REGEX_JAILS.findall(data)) - else: - print_error('%s is not readable or not exist' % conf) - return jails_list - - -def address_not_in_jail(pool, address, pool_size): - """ - :param pool: <list> - :param address: <str> - :param pool_size: <int> - :return: bool - - Checks if the address is in the pool. - If not address will be added - """ - index = bisect.bisect_left(pool, address) - if index < pool_size: - if pool[index] == address: - return False - bisect.insort_left(pool, address) - return True - else: - bisect.insort_left(pool, address) - return True |