mirror of https://github.com/fail2ban/fail2ban
- 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-b1dcaea8d6050.x
parent
a257fdc87d
commit
7bcfd2ace9
206
fail2ban-regex
206
fail2ban-regex
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/python -O
|
||||||
# This file is part of Fail2Ban.
|
# This file is part of Fail2Ban.
|
||||||
#
|
#
|
||||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||||
|
@ -25,12 +25,14 @@ __date__ = "$Date$"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import locale, getopt, sys, time, logging, gc
|
import getopt, sys, time, logging, os
|
||||||
|
|
||||||
# Inserts our own modules path first in the list
|
# Inserts our own modules path first in the list
|
||||||
# fix for bug #343821
|
# fix for bug #343821
|
||||||
sys.path.insert(1, "/usr/share/fail2ban")
|
sys.path.insert(1, "/usr/share/fail2ban")
|
||||||
|
|
||||||
|
from ConfigParser import SafeConfigParser
|
||||||
|
from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError
|
||||||
from common.version import version
|
from common.version import version
|
||||||
from server.filter import Filter
|
from server.filter import Filter
|
||||||
from server.regex import RegexException
|
from server.regex import RegexException
|
||||||
|
@ -38,13 +40,38 @@ from server.regex import RegexException
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger("fail2ban.regex")
|
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:
|
class Fail2banRegex:
|
||||||
|
|
||||||
|
test = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__filter = Filter(None)
|
self.__filter = Filter(None)
|
||||||
|
self.__failregex = list()
|
||||||
# Setup logging
|
# Setup logging
|
||||||
logging.getLogger("fail2ban").handlers = []
|
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
|
# set a format which is simpler for console use
|
||||||
formatter = logging.Formatter("%(message)s")
|
formatter = logging.Formatter("%(message)s")
|
||||||
# tell the handler to use this format
|
# tell the handler to use this format
|
||||||
|
@ -52,7 +79,8 @@ class Fail2banRegex:
|
||||||
logging.getLogger("fail2ban").addHandler(self.__hdlr)
|
logging.getLogger("fail2ban").addHandler(self.__hdlr)
|
||||||
logging.getLogger("fail2ban").setLevel(logging.ERROR)
|
logging.getLogger("fail2ban").setLevel(logging.ERROR)
|
||||||
|
|
||||||
def dispVersion(self):
|
@staticmethod
|
||||||
|
def dispVersion():
|
||||||
print "Fail2Ban v" + version
|
print "Fail2Ban v" + version
|
||||||
print
|
print
|
||||||
print "Copyright (c) 2004-2006 Cyril Jaquier"
|
print "Copyright (c) 2004-2006 Cyril Jaquier"
|
||||||
|
@ -62,14 +90,26 @@ class Fail2banRegex:
|
||||||
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
|
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
|
||||||
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
|
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
|
||||||
|
|
||||||
def dispUsage(self):
|
@staticmethod
|
||||||
print "Usage: "+sys.argv[0]+" <logline> <failregex>"
|
def dispUsage():
|
||||||
|
print "Usage: "+sys.argv[0]+" [OPTIONS] <LOG> <REGEX>"
|
||||||
print
|
print
|
||||||
print "Fail2Ban v" + version + " reads log file that contains password failure report"
|
print "Fail2Ban v" + version + " reads log file that contains password failure report"
|
||||||
print "and bans the corresponding IP addresses using firewall rules."
|
print "and bans the corresponding IP addresses using firewall rules."
|
||||||
print
|
print
|
||||||
print "This tools can test and benchmark your regular expressions for the \"failregex\""
|
print "This tools can test regular expressions for \"fail2ban\"."
|
||||||
print "option."
|
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
|
||||||
print "Report bugs to <lostcontrol@users.sourceforge.net>"
|
print "Report bugs to <lostcontrol@users.sourceforge.net>"
|
||||||
|
|
||||||
|
@ -84,81 +124,143 @@ class Fail2banRegex:
|
||||||
self.dispVersion()
|
self.dispVersion()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def testRegex(self, line, regex):
|
@staticmethod
|
||||||
print
|
def logIsFile(value):
|
||||||
|
return os.path.isfile(value)
|
||||||
|
|
||||||
|
def readRegex(self, value):
|
||||||
|
if os.path.isfile(value):
|
||||||
|
reader = SafeConfigParser()
|
||||||
try:
|
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):
|
||||||
|
for regex in self.__failregex:
|
||||||
logging.getLogger("fail2ban").setLevel(logging.DEBUG)
|
logging.getLogger("fail2ban").setLevel(logging.DEBUG)
|
||||||
self.__filter.addFailRegex(regex)
|
try:
|
||||||
|
self.__filter.addFailRegex(regex.getFailRegex())
|
||||||
|
try:
|
||||||
ret = self.__filter.findFailure(line)
|
ret = self.__filter.findFailure(line)
|
||||||
print
|
if not len(ret) == 0:
|
||||||
logging.getLogger("fail2ban").setLevel(logging.CRITICAL)
|
regex.inc()
|
||||||
|
regex.appendIP(ret)
|
||||||
except RegexException, e:
|
except RegexException, e:
|
||||||
print e
|
print e
|
||||||
return False
|
return False
|
||||||
except IndexError:
|
except IndexError:
|
||||||
print "Sorry, but no <host> found in regex"
|
print "Sorry, but no <host> found in regex"
|
||||||
return False
|
return False
|
||||||
if len(ret) == 0:
|
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 "Sorry, no match"
|
||||||
|
print
|
||||||
|
print "Look at the above section 'Running tests' which could contain important"
|
||||||
|
print "information."
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
print "Success, the following data were found:"
|
# Print stats
|
||||||
timeTuple = time.localtime(ret[0][1])
|
cnt = 1
|
||||||
print "Date: " + time.strftime("%a %b %d %H:%M:%S %Y", timeTuple)
|
print "Addresses found:"
|
||||||
ipList = ""
|
for failregex in self.__failregex:
|
||||||
for i in ret:
|
print "[" + str(cnt) + "]"
|
||||||
ipList = ipList + " " + i[0]
|
for ip in failregex.getIPList():
|
||||||
print "IP :" + ipList
|
timeTuple = time.localtime(ip[1])
|
||||||
|
timeString = time.strftime("%a %b %d %H:%M:%S %Y", timeTuple)
|
||||||
|
print " " + ip[0] + " (" + timeString + ")"
|
||||||
|
cnt += 1
|
||||||
|
|
||||||
print
|
print
|
||||||
|
|
||||||
print "Date template hits:"
|
print "Date template hits:"
|
||||||
for template in self.__filter.dateDetector.getTemplates():
|
for template in self.__filter.dateDetector.getTemplates():
|
||||||
print `template.getHits()` + " hit: " + template.getName()
|
print `template.getHits()` + " hit: " + template.getName()
|
||||||
|
|
||||||
print
|
print
|
||||||
print "Benchmark. Executing 1000..."
|
|
||||||
gc.disable()
|
print "Success, the total number of match is " + str(total)
|
||||||
total = 0
|
print
|
||||||
maxValue = 0
|
print "However, look at the above section 'Running tests' which could contain important"
|
||||||
maxPos = 0
|
print "information."
|
||||||
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` + ")"
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
regex = Fail2banRegex()
|
fail2banRegex = Fail2banRegex()
|
||||||
# Reads the command line options.
|
# Reads the command line options.
|
||||||
try:
|
try:
|
||||||
cmdOpts = 'hV'
|
cmdOpts = 'hV'
|
||||||
cmdLongOpts = ['help', 'version']
|
cmdLongOpts = ['help', 'version']
|
||||||
optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts)
|
optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts)
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
regex.dispUsage()
|
fail2banRegex.dispUsage()
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
# Process command line
|
# Process command line
|
||||||
regex.getCmdLineOptions(optList)
|
fail2banRegex.getCmdLineOptions(optList)
|
||||||
# We need exactly 3 parameters
|
# We need exactly 3 parameters
|
||||||
if len(sys.argv) <> 3:
|
if not len(sys.argv) == 3:
|
||||||
regex.dispUsage()
|
fail2banRegex.dispUsage()
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
else:
|
else:
|
||||||
ret = regex.testRegex(sys.argv[1], sys.argv[2])
|
if fail2banRegex.readRegex(sys.argv[2]) == False:
|
||||||
if ret:
|
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)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
.\" 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
|
.SH NAME
|
||||||
fail2ban-client \- configure and control the server
|
fail2ban-client \- configure and control the server
|
||||||
.SH SYNOPSIS
|
|
||||||
.B fail2ban-client
|
|
||||||
[\fIOPTIONS\fR]... \fI<COMMAND>\fR
|
|
||||||
.SH DESCRIPTION
|
.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.
|
and bans the corresponding IP addresses using firewall rules.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
|
|
|
@ -1,16 +1,36 @@
|
||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 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
|
.SH NAME
|
||||||
fail2ban-regex \- test Fail2ban "failregex" option
|
fail2ban-regex \- test Fail2ban "failregex" option
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B fail2ban-regex
|
.B fail2ban-regex
|
||||||
\fI<logline> <failregex>\fR
|
[\fIOPTIONS\fR] \fI<LOG> <REGEX>\fR
|
||||||
.SH DESCRIPTION
|
.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.
|
and bans the corresponding IP addresses using firewall rules.
|
||||||
.PP
|
.PP
|
||||||
This tools can test and benchmark your regular expressions for the "failregex"
|
This tools can test regular expressions for "fail2ban".
|
||||||
option.
|
.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
|
.SH AUTHOR
|
||||||
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>.
|
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>.
|
||||||
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
.\" 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
|
.SH NAME
|
||||||
fail2ban-server \- start the server
|
fail2ban-server \- start the server
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B fail2ban-server
|
.B fail2ban-server
|
||||||
[\fIOPTIONS\fR]
|
[\fIOPTIONS\fR]
|
||||||
.SH DESCRIPTION
|
.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.
|
and bans the corresponding IP addresses using firewall rules.
|
||||||
.PP
|
.PP
|
||||||
Only use this command for debugging purpose. Start the server with
|
Only use this command for debugging purpose. Start the server with
|
||||||
|
|
|
@ -40,4 +40,30 @@ echo "[done]"
|
||||||
echo -n "Generating fail2ban-regex "
|
echo -n "Generating fail2ban-regex "
|
||||||
help2man --section=1 --no-info --include=fail2ban-regex.h2m --output fail2ban-regex.1 ../fail2ban-regex
|
help2man --section=1 --no-info --include=fail2ban-regex.h2m --output fail2ban-regex.1 ../fail2ban-regex
|
||||||
echo "[done]"
|
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]"
|
||||||
|
|
Loading…
Reference in New Issue