From 5a4bc8d9ade7e757c362396078bf619549f62ba0 Mon Sep 17 00:00:00 2001 From: Julien Pecqueur Date: Fri, 25 Aug 2017 15:28:37 +0200 Subject: [PATCH] basic stuff --- README.md | 12 +++++++ fail2ban-getlog | 30 ++++++++++++++++++ fail2ban-report | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ log.csv | 40 +++++++++++++++++++++++ report.pdf | Bin 0 -> 18351 bytes 5 files changed, 164 insertions(+) create mode 100755 fail2ban-getlog create mode 100755 fail2ban-report create mode 100644 log.csv create mode 100644 report.pdf diff --git a/README.md b/README.md index c5c9ac8..80a8eab 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,14 @@ # fail2ban-report Fail2ban report + +Example : + +Extract fail2ban logs in log.csv : + +./fail2ban-getlog log.csv + +Generate report.pdf from log.csv data using GeoLite2-Country.mmdb database : + +./fail2ban-report log.csv GeoLite2-Country.mmdb report.pdf + +You can download GeoLite2-Country database here : diff --git a/fail2ban-getlog b/fail2ban-getlog new file mode 100755 index 0000000..fc8aed4 --- /dev/null +++ b/fail2ban-getlog @@ -0,0 +1,30 @@ +#!/usr/bin/python3 +# coding: utf-8 +# Julien Pecqueur + +from sys import argv + +if len(argv) != 2: + print("Usage: fail2ban-getlog ") + print("Extract fail2ban logs to CSV file.") +else: + F_LOG = "/var/log/fail2ban.log" + f_old = open(argv[1], "r") + old = [] + for l in f_old: + old.append(l[0:-1]) + f_old.close() + print(len(old), "existing line(s).") + f_in = open(F_LOG, "r") + f_out = open(argv[1], "a+") + c = 0 + for l in f_in: + l = l[0:-1].split(" ") + if "Ban" in l: + l = l[-1]+";"+l[0]+";"+l[1][0:-4] + if not l in old: + f_out.write(l+"\n") + c += 1 + f_in.close() + f_out.close() + print("Added", c, "new line(s).") diff --git a/fail2ban-report b/fail2ban-report new file mode 100755 index 0000000..80efb40 --- /dev/null +++ b/fail2ban-report @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +# coding: utf-8 +# Julien Pecqueur + +import pandas as pd +import matplotlib +matplotlib.use('Agg') +import geoip2.database +from matplotlib.backends.backend_pdf import PdfPages +from sys import argv + + +def load_file(file): + data = [] + f = open(file) + for l in f: + ip, date, time = l.split(";") + data.append([ip, date]) + return(data) + +def get_nb_ban_by_date(data): + d = {} + for ip, date in data: + if date in d.keys(): + d[date] += 1 + else: + d[date] = 1 + return(d) + +def get_nb_ban_by_ip(data): + d = {} + for ip, date in data: + if ip in d.keys(): + d[ip] += 1 + else: + d[ip] = 1 + return(d) + +def get_nb_ban_by_country(data, f_country): + reader = geoip2.database.Reader(f_country) + d = {} + for ip, date in data: + response = reader.country(ip) + country = response.country.name + if country in d.keys(): + d[country] += 1 + else: + d[country] = 1 + reader.close() + return(d) + +def generate_report(f_log, f_country, f_pdf): + + data = load_file(f_log) + + pp = PdfPages(f_pdf) + + nb_ban_by_date = get_nb_ban_by_date(data) + df = pd.DataFrame.from_dict(nb_ban_by_date, orient='index').rename(columns={0:"Bans"}).sort_index() + plot = df.plot(title="Bans by date", figsize=(10.0,4.0)) + fig = plot.get_figure() + fig.savefig(pp, format='pdf') + + nb_ban_by_country = get_nb_ban_by_country(data, f_country) + df = pd.DataFrame.from_dict(nb_ban_by_country, orient='index').rename(columns={0:"Bans"}).sort_values(by='Bans', ascending=True) #.query('Bans > 1') + plot = df.plot(title="Bans by country", kind='pie', subplots=True, legend=False, figsize=(10.0,10.0)) + fig = plot[0].get_figure() + fig.savefig(pp, format='pdf') + + nb_ban_by_ip = get_nb_ban_by_ip(data) + df = pd.DataFrame.from_dict(nb_ban_by_ip, orient='index').rename(columns={0:"Bans"}).sort_values(by='Bans', ascending=True) #.query('Bans > 1') + plot = df.plot(title="Bans by IP", kind='pie', legend=False, subplots=True, figsize=(10.0,10.0)) + fig = plot[0].get_figure() + fig.savefig(pp, format='pdf') + + pp.close() + +if len(argv) != 4: + print("Usage: fail2ban-report ") + print("Compute fail2ban CSV file and generate PDF report.") +else: + generate_report(argv[1], argv[2], argv[3]) diff --git a/log.csv b/log.csv new file mode 100644 index 0000000..037f7e2 --- /dev/null +++ b/log.csv @@ -0,0 +1,40 @@ +61.41.4.26;2017-08-22;06:33:24 +61.41.4.26;2017-08-22;06:45:13 +61.41.4.26;2017-08-22;06:57:36 +91.197.232.109;2017-08-22;07:03:14 +61.41.4.26;2017-08-22;07:09:54 +61.41.4.26;2017-08-22;07:22:57 +61.41.4.26;2017-08-22;07:34:47 +61.41.4.26;2017-08-22;07:46:39 +61.41.4.26;2017-08-22;07:58:55 +61.41.4.26;2017-08-22;08:10:52 +61.41.4.26;2017-08-22;08:22:44 +61.41.4.26;2017-08-22;08:34:33 +61.41.4.26;2017-08-22;08:46:44 +61.41.4.26;2017-08-22;08:58:43 +61.41.4.26;2017-08-22;09:10:45 +61.41.4.26;2017-08-22;09:22:26 +61.41.4.26;2017-08-22;09:34:24 +61.41.4.26;2017-08-22;09:46:37 +61.41.4.26;2017-08-22;09:59:33 +61.41.4.26;2017-08-22;10:11:24 +61.41.4.26;2017-08-22;10:23:16 +103.89.88.102;2017-08-22;15:00:57 +103.89.88.102;2017-08-23;00:45:33 +103.89.88.102;2017-08-23;00:56:12 +91.197.232.109;2017-08-23;04:07:52 +91.197.232.107;2017-08-23;11:03:01 +91.197.232.109;2017-08-23;11:41:29 +103.89.88.102;2017-08-23;16:01:43 +195.154.63.133;2017-08-23;20:26:55 +91.197.232.109;2017-08-23;23:50:12 +103.89.88.102;2017-08-24;02:44:35 +195.154.63.133;2017-08-24;03:32:24 +62.210.24.228;2017-08-24;10:57:14 +103.89.88.102;2017-08-24;12:38:36 +103.89.88.102;2017-08-24;21:20:16 +91.197.232.109;2017-08-24;23:56:20 +103.207.38.211;2017-08-25;01:19:37 +193.201.224.214;2017-08-25;02:26:16 +103.89.88.102;2017-08-25;03:27:00 +91.197.232.109;2017-08-25;03:43:36 diff --git a/report.pdf b/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ca6ced73ac104d059fdd195689835dae1430b102 GIT binary patch literal 18351 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