From 9120747a0773123eb41ef55d5e131f1789e1bad5 Mon Sep 17 00:00:00 2001
From: Thuban <thuban@yeuxdelibad.net>
Date: Sat, 09 Sep 2017 07:27:07 +0000
Subject: [PATCH] Merge branch 'master' into 'master'

---
 vilain.py       |    6 +-
 Makefile        |    8 ++
 vilainreport.py |  123 +++++++++++++++++++++++++++++++++++++++++
 vilain          |    2 
 vilainreport    |   11 +++
 5 files changed, 145 insertions(+), 5 deletions(-)

diff --git a/Makefile b/Makefile
index 26060f0..92f620b 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 # See LICENSE file for copyright and license details.
 #
 # vilain version
-VERSION = 0.6
+VERSION = 0.7
 
 # Customize below to fit your system
 # paths
@@ -13,10 +13,14 @@
 	@echo installing executable file to ${DESTDIR}${PREFIX}/bin
 	@mkdir -p ${DESTDIR}${PREFIX}/bin
 	@cp -f vilain ${DESTDIR}${PREFIX}/bin
+	@cp -f vilainreport ${DESTDIR}${PREFIX}/bin
 	@echo installing script file to ${DESTDIR}${PREFIX}/sbin
 	@cp -f vilain.py ${DESTDIR}${PREFIX}/sbin
+	@cp -f vilainreport.py ${DESTDIR}${PREFIX}/sbin
 	@chmod 755 ${DESTDIR}${PREFIX}/bin/vilain
+	@chmod 755 ${DESTDIR}${PREFIX}/bin/vilainreport
 	@chmod 644 ${DESTDIR}${PREFIX}/sbin/vilain.py
+	@chmod 644 ${DESTDIR}${PREFIX}/sbin/vilainreport.py
 	@echo installing init script in /etc/rc.d
 	@cp -f vilain.rc /etc/rc.d/vilain
 	@chmod 755 /etc/rc.d/vilain
@@ -29,7 +33,9 @@
 uninstall:
 	@echo removing executable file from ${DESTDIR}${PREFIX}/bin
 	@rm -f ${DESTDIR}${PREFIX}/bin/vilain
+	@rm -f ${DESTDIR}${PREFIX}/bin/vilainreport
 	@rm -f ${DESTDIR}${PREFIX}/sbin/vilain.py
+	@rm -f ${DESTDIR}${PREFIX}/sbin/vilainreport.py
 	@echo removing manual page to ${DESTDIR}${MANPREFIX}/
 	@rm -f ${DESTDIR}${MANPREFIX}/vilain.1
 
diff --git a/vilain b/vilain
index d385066..9c2841c 100755
--- a/vilain
+++ b/vilain
@@ -1,5 +1,5 @@
 #!/bin/sh
-# script to launch vilain with the latest python3 version avaiable
+# script to launch vilain with the latest python3 version available
 
 PYTHONVERSION=$(ls -l /usr/local/bin/python3.* |grep -Eo "3\.[0-9]" |tail -n1)
 PYTHON="/usr/local/bin/python$PYTHONVERSION"
diff --git a/vilain.py b/vilain.py
index eb3a608..082b15f 100755
--- a/vilain.py
+++ b/vilain.py
@@ -30,7 +30,7 @@
 import time
 
 CONFIGFILE = "/etc/vilain.conf"
-VERSION = "0.6"
+VERSION = "0.7"
 vilain_table = "vilain_bruteforce"
 LOGFILE = "/var/log/daemon"
 
@@ -46,7 +46,7 @@
     log_handler = logging.handlers.WatchedFileHandler(LOGFILE)
     formatter = logging.Formatter(
             '%(asctime)s %(module)s:%(funcName)s:%(message)s',
-            '%b %d %H:%M:%S')
+            '%Y-%m-%d %H:%M:%S')
     log_handler.setFormatter(formatter)
     logger.addHandler(log_handler)
     logger.setLevel(logging.INFO)
@@ -182,7 +182,7 @@
             logger.info("{} detected, reason {}, count: {}, maxtries: {}".format(ip, reason, n_ip, maxtries))
             if n_ip >= maxtries:
                 ret = subprocess.call(["pfctl", "-t", self.vilain_table, "-T", "add", ip])
-                logger.info("Blacklisting {}, return code:{}".format(ip, ret))
+                logger.info("Blacklisting {}, reason {}, return code:{}".format(ip, reason, ret))
             #for debugging, this line allow us to see if the script run until here
             logger.debug('ban_ips end:{}'.format(self.ip_seen_at))
 
diff --git a/vilainreport b/vilainreport
new file mode 100644
index 0000000..7d6e7a8
--- /dev/null
+++ b/vilainreport
@@ -0,0 +1,11 @@
+#!/bin/sh
+# script to launch vilainreport with the latest python3 version available
+
+PYTHONVERSION=$(ls -l /usr/local/bin/python3.* |grep -Eo "3\.[0-9]" |tail -n1)
+PYTHON="/usr/local/bin/python$PYTHONVERSION"
+if [ -x $PYTHON ]; then
+	$PYTHON /usr/local/sbin/vilainreport.py
+else
+	echo "Error : no python3 executable found"
+fi
+exit
diff --git a/vilainreport.py b/vilainreport.py
new file mode 100644
index 0000000..5a259dd
--- /dev/null
+++ b/vilainreport.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+# -*- coding:Utf-8 -*-
+
+import re
+import sys
+
+pattern = '(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+).*Blacklisting (\d+\.\d+\.\d+\.\d+), reason (.*), return'
+regex = re.compile(pattern)
+
+
+class CounterDict:
+
+    def __init__(self):
+        self._counters = dict()
+
+    def inc(self, k):
+        v = self._counters.get(k, 0) + 1
+        self._counters[k] = v
+
+    def get(self, k):
+        return self._counters.get(k, 0)
+
+    def keys(self):
+        return self._counters.keys()
+
+    def reset(self):
+        self._counters = dict()
+
+    def topitems(self):
+        return sorted(self._counters.items(), key=lambda x: x[1], reverse=True)
+
+
+class Value:
+
+    def __init__(self):
+        self._value = ""
+
+    def __str__(self):
+        return self._value
+
+    def __eq__(self, other):
+        return str(self._value) == str(other)
+
+    def set(self, value):
+        self._value = value
+
+
+last_day = Value()
+
+# daily counters: key is reason
+dcounters = CounterDict()
+
+# global counters: key is reason
+gcounters = CounterDict()
+
+# hourly counters: key is hour
+hcounters = CounterDict()
+
+# top counters: key is IP
+tcounters = CounterDict()
+
+
+def plural(noun, count):
+    if count > 1:
+        return noun + "s"
+    else:
+        return noun
+
+
+def process(m):
+    current_day = m.group(1) + "-" + m.group(2) + "-" + m.group(3)
+    current_hour = m.group(4)
+    full_time = m.group(4) + ":" + m.group(5) + ":" + m.group(6)
+    ip = m.group(7)
+    reason = m.group(8)
+
+    # new day
+    #print("({})-({}) => {}".format(last_day, current_day, last_day == current_day))
+    if last_day != current_day:
+        # display day counters
+        sys.stdout.write("\n")
+        for reason in dcounters.keys():
+            count = dcounters.get(reason)
+            sys.stdout.write("Probe '{}': {} {}\n".format(reason, count, plural("attack", count)))
+        last_day.set(current_day)
+        dcounters.reset()
+        sys.stdout.write("\n### Date {}\n".format(current_day))
+
+    # output current line
+    sys.stdout.write("{} blacklist IP {} ({})\n".format(full_time, ip, reason))
+
+    # increment counters
+    dcounters.inc(reason)
+    gcounters.inc(reason)
+    hcounters.inc(current_hour)
+    tcounters.inc(ip)
+
+
+# parse stdin
+for line in sys.stdin:
+    match = regex.match(line)
+    if match:
+        process(match)
+
+# output counters
+sys.stdout.write("\n")
+for reason in dcounters.keys():
+    sys.stdout.write("Probe '{}' : {} attacks\n".format(reason, dcounters.get(reason)))
+
+sys.stdout.write("\n### Attacks per probe\n")
+for k in gcounters.keys():
+    count = gcounters.get(k)
+    sys.stdout.write("Probe '{}': {} {} \n".format(k, count, plural("attack", count)))
+
+sys.stdout.write("\n### Hourly repartition\n")
+for k in sorted(hcounters.keys()):
+    sys.stdout.write("Hour {} - {:02d}: {}\n".format(k, int(k) + 1, hcounters.get(k)))
+
+sys.stdout.write("\n### Top attackers\n")
+for k, v in tcounters.topitems():
+    if v < 2:
+        break
+    sys.stdout.write("IP {:16}: {}\n".format(k, v))

--
Gitblit v1.9.3