Thuban
2017-04-18 a11218715d122662cddcf35c424290be146f2a43
vilain.py
@@ -31,15 +31,15 @@
CONFIGFILE = "/etc/vilain.conf"
VERSION = "0.5"
vilain_table = "vilain_bruteforce"
logfile = "/var/log/daemon"
LOGFILE = "/var/log/daemon"
if os.geteuid() != 0:
    print("Only root can use this tool")
    sys.exit()
    sys.exit(1)
# Configure logging
logger = logging.getLogger(__name__)
logging.basicConfig(filename=logfile,
logging.basicConfig(filename=LOGFILE,
                    format='%(asctime)s %(module)s:%(funcName)s:%(message)s',
                    datefmt='%H:%M:%S')
logger.setLevel(logging.INFO)
@@ -59,7 +59,7 @@
    c = readconfig()
    d = c.defaults()
    watch_while = int(d['watch_while'])
    vilain_table = d['vilain_table']
    VILAIN_TABLE = d['vilain_table']
    default_maxtries = int(d['maxtries'])
    sleeptime = float(d['sleeptime'])
    ignore_ips = []
@@ -72,16 +72,15 @@
    c = readconfig()
    for s in c.sections():
        if c.has_option(s,'logfile'):
            logfile = c.get(s,'logfile')
            LOGFILE = c.get(s,'logfile')
            regex = c.get(s,'regex')
            #we take the default value of maxtries
            maxtries = c.defaults()['maxtries']
            if c.has_option(s,'maxtries'):
                #if we have a maxtries defined in the section, we overwrite the default
                maxtries = int(c.get(s,'maxtries'))
            d = {'name' : s, 'logfile':logfile, 'regex':regex, 'maxtries': maxtries}
            d = {'name' : s, 'logfile':LOGFILE, 'regex':regex, 'maxtries': maxtries}
            yield d
class Vilain():
    def __init__(self):
@@ -93,7 +92,7 @@
        self.bad_ip_queue = asyncio.Queue(loop=self.loop)
        for entry in load_sections():
            logger.info("Start vilain for {}".format(entry['name']))
            logger.info("Start vilain for {}".format(entry))
            asyncio.ensure_future(self.check_logs(entry['logfile'], entry['maxtries'], entry['regex'], entry['name']))
        asyncio.ensure_future(self.ban_ips())
@@ -114,12 +113,12 @@
    def start(self):
        try:
            logger.info('Run forever loop')
            self.loop.run_forever()
        except KeyboardInterrupt:
            self.loop.close()
        finally:
            self.loop.close()
    async def check_logs(self, logfile, maxtries, regex, reason):
        """
@@ -131,16 +130,21 @@
            # Watch the file for changes
            stat = os.stat(logfile)
            size = stat.st_size
            inode = stat.st_ino
            mtime = stat.st_mtime
            RE = re.compile(regex)
            while True:
                await asyncio.sleep(self.sleeptime)
                stat = os.stat(logfile)
                if mtime < stat.st_mtime:
                if size > stat.st_size and inode != stat.st_ino:
                    logger.info("The file {} has rotated. We start from position 0".format(logfile))
                    size = 0
                    inode = stat.st_ino
                if mtime < stat.st_mtime and inode == stat.st_ino:
                    logger.debug("{} has been modified".format(logfile))
                    mtime = stat.st_mtime
                    with open(logfile, "rb") as f:
                        f.seek(size)
                        f.seek(size,0)
                        for bline in f.readlines():
                            line = bline.decode().strip()
                            ret = RE.match(line)
@@ -153,7 +157,6 @@
                                    logger.debug('queue size: {}'.format(self.bad_ip_queue.qsize()))
                                else:
                                    logger.info('line match {}. But IP in ingore list'.format(bad_ip))
                    size = stat.st_size
    async def ban_ips(self):
@@ -161,9 +164,8 @@
        record time when this IP has been seen in ip_seen_at = { ip:{'time':<time>,'count':<counter} }
        and ban with pf
        """
        logger.info('ban_ips sarted with sleeptime={}'.format(self.sleeptime))
        logger.info('ban_ips sarted')
        while True:
            # await asyncio.sleep(self.sleeptime)
            ip_item = await self.bad_ip_queue.get()
            logger.debug('ban_ips awake')
            ip = ip_item['ip']
@@ -176,7 +178,6 @@
            if n_ip >= maxtries:
                ret = subprocess.call(["pfctl", "-t", self.vilain_table, "-T", "add", ip])
                logger.info("Blacklisting {}, return code:{}".format(ip, ret))
                self.ip_seen_at.pop(ip)
            #for debugging, this line allow us to see if the script run until here
            logger.debug('ban_ips end:{}'.format(self.ip_seen_at))
@@ -186,7 +187,7 @@
        """
        logger.info('clean_ips sarted with sleeptime={}'.format(self.sleeptime))
        while True:
            await asyncio.sleep(self.sleeptime)
            await asyncio.sleep(self.watch_while)
            to_remove = []
            for recorded_ip, data in self.ip_seen_at.items():
                if time.time() - data['time'] >= self.watch_while: