From 7bcfd2ace98ed74bfb8b1e601b5c38ad6572633e Mon Sep 17 00:00:00 2001 From: Cyril Jaquier <cyril.jaquier@fail2ban.org> Date: Sun, 21 Jan 2007 22:21:13 +0000 Subject: [PATCH] - Added file support to fail2ban-regex. Benchmark feature has been removed git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/trunk@523 a942ae1a-1317-0410-a47c-b1dcaea8d605 --- fail2ban-regex | 232 ++++++++++++++++++++++++++++++------------ man/fail2ban-client.1 | 9 +- man/fail2ban-regex.1 | 30 +++++- man/fail2ban-server.1 | 4 +- man/generate-man | 28 ++++- 5 files changed, 225 insertions(+), 78 deletions(-) diff --git a/fail2ban-regex b/fail2ban-regex index 8074a853..261f742b 100755 --- a/fail2ban-regex +++ b/fail2ban-regex @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python -O # This file is part of Fail2Ban. # # Fail2Ban is free software; you can redistribute it and/or modify @@ -25,12 +25,14 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -import locale, getopt, sys, time, logging, gc +import getopt, sys, time, logging, os # Inserts our own modules path first in the list # fix for bug #343821 sys.path.insert(1, "/usr/share/fail2ban") +from ConfigParser import SafeConfigParser +from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError from common.version import version from server.filter import Filter from server.regex import RegexException @@ -38,13 +40,38 @@ from server.regex import RegexException # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.regex") +class RegexStat: + + def __init__(self, failregex): + self.__stats = 0 + self.__failregex = failregex + self.__ipList = list() + + def inc(self): + self.__stats += 1 + + def getStats(self): + return self.__stats + + def getFailRegex(self): + return self.__failregex + + def appendIP(self, value): + self.__ipList.extend(value) + + def getIPList(self): + return self.__ipList + class Fail2banRegex: + test = None + def __init__(self): self.__filter = Filter(None) + self.__failregex = list() # Setup logging logging.getLogger("fail2ban").handlers = [] - self.__hdlr = logging.StreamHandler(sys.stdout) + self.__hdlr = logging.StreamHandler(Fail2banRegex.test) # set a format which is simpler for console use formatter = logging.Formatter("%(message)s") # tell the handler to use this format @@ -52,7 +79,8 @@ class Fail2banRegex: logging.getLogger("fail2ban").addHandler(self.__hdlr) logging.getLogger("fail2ban").setLevel(logging.ERROR) - def dispVersion(self): + @staticmethod + def dispVersion(): print "Fail2Ban v" + version print print "Copyright (c) 2004-2006 Cyril Jaquier" @@ -62,14 +90,26 @@ class Fail2banRegex: print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>." print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>." - def dispUsage(self): - print "Usage: "+sys.argv[0]+" <logline> <failregex>" + @staticmethod + def dispUsage(): + print "Usage: "+sys.argv[0]+" [OPTIONS] <LOG> <REGEX>" print print "Fail2Ban v" + version + " reads log file that contains password failure report" print "and bans the corresponding IP addresses using firewall rules." print - print "This tools can test and benchmark your regular expressions for the \"failregex\"" - print "option." + print "This tools can test regular expressions for \"fail2ban\"." + print + print "Options:" + print " -h, --help display this help message" + print " -V, --version print the version" + print + print "Log:" + print " string a string representing a log line" + print " filename path to a log file (/var/log/auth.log)" + print + print "Regex:" + print " string a string representing a 'failregex'" + print " filename path to a filter file (filter.d/sshd.conf)" print print "Report bugs to <lostcontrol@users.sourceforge.net>" @@ -78,87 +118,149 @@ class Fail2banRegex: """ for opt in optList: if opt[0] in ["-h", "--help"]: - self.dispUsage() - sys.exit(0) - elif opt[0] in ["-V", "--version"]: - self.dispVersion() - sys.exit(0) + self.dispUsage() + sys.exit(0) + elif opt[0] in ["-V", "--version"]: + self.dispVersion() + sys.exit(0) + + @staticmethod + def logIsFile(value): + return os.path.isfile(value) + + def readRegex(self, value): + if os.path.isfile(value): + reader = SafeConfigParser() + try: + reader.read(value) + self.__failregex = [RegexStat(m) + for m in reader.get("Definition", "failregex").split('\n')] + except NoSectionError: + print "No [Definition] section in " + value + return False + except NoOptionError: + print "No failregex option in " + value + return False + except MissingSectionHeaderError: + print "No section headers in " + value + return False + else: + self.__failregex = [RegexStat(value)] + return True - def testRegex(self, line, regex): - print - try: + def testRegex(self, line): + for regex in self.__failregex: logging.getLogger("fail2ban").setLevel(logging.DEBUG) - self.__filter.addFailRegex(regex) - ret = self.__filter.findFailure(line) - print - logging.getLogger("fail2ban").setLevel(logging.CRITICAL) - except RegexException, e: - print e - return False - except IndexError: - print "Sorry, but no <host> found in regex" - return False - if len(ret) == 0: + try: + self.__filter.addFailRegex(regex.getFailRegex()) + try: + ret = self.__filter.findFailure(line) + if not len(ret) == 0: + regex.inc() + regex.appendIP(ret) + except RegexException, e: + print e + return False + except IndexError: + print "Sorry, but no <host> found in regex" + return False + finally: + self.__filter.delFailRegex(0) + logging.getLogger("fail2ban").setLevel(logging.CRITICAL) + + def printStats(self): + print + print "Results" + print "=======" + print + + # Print title + cnt = 1 + print "Failregex:" + for failregex in self.__failregex: + print "[" + str(cnt) + "] " + failregex.getFailRegex() + cnt += 1 + + print + + # Print stats + cnt = 1 + total = 0 + print "Number of matches:" + for failregex in self.__failregex: + match = failregex.getStats() + total += match + print "[" + str(cnt) + "] " + str(match) + " match(es)" + cnt += 1 + + print + + if total == 0: print "Sorry, no match" + print + print "Look at the above section 'Running tests' which could contain important" + print "information." return False else: - print "Success, the following data were found:" - timeTuple = time.localtime(ret[0][1]) - print "Date: " + time.strftime("%a %b %d %H:%M:%S %Y", timeTuple) - ipList = "" - for i in ret: - ipList = ipList + " " + i[0] - print "IP :" + ipList + # Print stats + cnt = 1 + print "Addresses found:" + for failregex in self.__failregex: + print "[" + str(cnt) + "]" + for ip in failregex.getIPList(): + timeTuple = time.localtime(ip[1]) + timeString = time.strftime("%a %b %d %H:%M:%S %Y", timeTuple) + print " " + ip[0] + " (" + timeString + ")" + cnt += 1 + print + print "Date template hits:" for template in self.__filter.dateDetector.getTemplates(): print `template.getHits()` + " hit: " + template.getName() + print - print "Benchmark. Executing 1000..." - gc.disable() - total = 0 - maxValue = 0 - maxPos = 0 - minValue = 99999999 - minPos = 0 - for i in range(1000): - start = time.time() - ret = self.__filter.findFailure(line) - end = time.time() - diff = (end - start) * 1000 - total = total + diff - minValue = min(minValue, diff) - if minValue == diff: - minPos = i - maxValue = max(maxValue, diff) - if maxValue == diff: - maxPos = i - gc.enable() - print "Performance" - print "Avg: " + `total / 1000` + " ms" - print "Max: " + `maxValue` + " ms (Run " + `maxPos` + ")" - print "Min: " + `minValue` + " ms (Run " + `minPos` + ")" + + print "Success, the total number of match is " + str(total) + print + print "However, look at the above section 'Running tests' which could contain important" + print "information." return True + if __name__ == "__main__": - regex = Fail2banRegex() + fail2banRegex = Fail2banRegex() # Reads the command line options. try: cmdOpts = 'hV' cmdLongOpts = ['help', 'version'] optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts) except getopt.GetoptError: - regex.dispUsage() + fail2banRegex.dispUsage() sys.exit(-1) # Process command line - regex.getCmdLineOptions(optList) + fail2banRegex.getCmdLineOptions(optList) # We need exactly 3 parameters - if len(sys.argv) <> 3: - regex.dispUsage() + if not len(sys.argv) == 3: + fail2banRegex.dispUsage() sys.exit(-1) else: - ret = regex.testRegex(sys.argv[1], sys.argv[2]) - if ret: + if fail2banRegex.readRegex(sys.argv[2]) == False: + sys.exit(-1) + + print + print "Running tests" + print "=============" + print + + if fail2banRegex.logIsFile(sys.argv[1]): + hdlr = open(sys.argv[1]) + for line in hdlr: + fail2banRegex.testRegex(line) + else: + fail2banRegex.testRegex(sys.argv[1]) + + if fail2banRegex.printStats(): sys.exit(0) else: sys.exit(-1) diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index 1ee203c7..b99c5ada 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -1,12 +1,11 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. -.TH FAIL2BAN-CLIENT "1" "January 2007" "fail2ban-client v0.7.6" "User Commands" +.TH FAIL2BAN-CLIENT "1" "January 2007" "fail2ban-client v0.7.6-SVN" "User Commands" .SH NAME fail2ban-client \- configure and control the server -.SH SYNOPSIS -.B fail2ban-client -[\fIOPTIONS\fR]... \fI<COMMAND>\fR .SH DESCRIPTION -Fail2Ban v0.7.6 reads log file that contains password failure report +[?1034hUsage: ../fail2ban\-client [OPTIONS]... <COMMAND> +.PP +Fail2Ban v0.7.6\-SVN reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP diff --git a/man/fail2ban-regex.1 b/man/fail2ban-regex.1 index 16e3fa5a..499c1c96 100644 --- a/man/fail2ban-regex.1 +++ b/man/fail2ban-regex.1 @@ -1,16 +1,36 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. -.TH FAIL2BAN-REGEX "1" "January 2007" "fail2ban-regex v0.7.6" "User Commands" +.TH FAIL2BAN-REGEX "1" "January 2007" "fail2ban-regex v0.7.6-SVN" "User Commands" .SH NAME fail2ban-regex \- test Fail2ban "failregex" option .SH SYNOPSIS .B fail2ban-regex -\fI<logline> <failregex>\fR +[\fIOPTIONS\fR] \fI<LOG> <REGEX>\fR .SH DESCRIPTION -Fail2Ban v0.7.6 reads log file that contains password failure report +Fail2Ban v0.7.6\-SVN reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .PP -This tools can test and benchmark your regular expressions for the "failregex" -option. +This tools can test regular expressions for "fail2ban". +.SH OPTIONS +.TP +\fB\-h\fR, \fB\-\-help\fR +display this help message +.TP +\fB\-V\fR, \fB\-\-version\fR +print the version +.SH LOG +.TP +\fBstring\fR +a string representing a log line +.TP +\fBfilename\fR +path to a log file (/var/log/auth.log) +.SH REGEX +.TP +\fBstring\fR +a string representing a 'failregex' +.TP +\fBfilename\fR +path to a filter file (filter.d/sshd.conf) .SH AUTHOR Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>. Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>. diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1 index 85780a23..211cbabf 100644 --- a/man/fail2ban-server.1 +++ b/man/fail2ban-server.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36. -.TH FAIL2BAN-SERVER "1" "January 2007" "fail2ban-server v0.7.6" "User Commands" +.TH FAIL2BAN-SERVER "1" "January 2007" "fail2ban-server v0.7.6-SVN" "User Commands" .SH NAME fail2ban-server \- start the server .SH SYNOPSIS .B fail2ban-server [\fIOPTIONS\fR] .SH DESCRIPTION -Fail2Ban v0.7.6 reads log file that contains password failure report +Fail2Ban v0.7.6\-SVN reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .PP Only use this command for debugging purpose. Start the server with diff --git a/man/generate-man b/man/generate-man index bf4f651e..86ad5c12 100755 --- a/man/generate-man +++ b/man/generate-man @@ -40,4 +40,30 @@ echo "[done]" echo -n "Generating fail2ban-regex " help2man --section=1 --no-info --include=fail2ban-regex.h2m --output fail2ban-regex.1 ../fail2ban-regex echo "[done]" - +echo -n "Patching fail2ban-regex " +# Changes the title. +sed -i -e 's/.SS "Log:"/.SH LOG/' fail2ban-regex.1 +sed -i -e 's/.SS "Regex:"/.SH REGEX/' fail2ban-regex.1 +# Sets bold font for commands. +IFS=" +" +NEXT=0 +FOUND=0 +LINES=$( cat fail2ban-regex.1 ) +echo -n "" > fail2ban-regex.1 +for LINE in $LINES; do + if [ "$LINE" = ".SH LOG" ]; then + FOUND=1 + fi + if [ $NEXT -eq 1 ] && [ $FOUND -eq 1 ]; then + echo "\fB$LINE\fR" >> fail2ban-regex.1 + else + echo "$LINE" >> fail2ban-regex.1 + fi + if [ "$LINE" = ".TP" ]; then + NEXT=1 + else + NEXT=0 + fi +done +echo "[done]"