Makefile | ●●●●● patch | view | raw | blame | history | |
vilain | ●●●●● patch | view | raw | blame | history | |
vilain.py | ●●●●● patch | view | raw | blame | history | |
vilainreport | ●●●●● patch | view | raw | blame | history | |
vilainreport.py | ●●●●● patch | view | raw | blame | history |
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 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" 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)) vilainreport
New file @@ -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 vilainreport.py
New file @@ -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))