Add ability to set log encoding for jail

pull/128/merge^2
Steven Hiscocks 2013-02-27 18:09:55 +00:00
parent d23d365be2
commit 66367876bb
8 changed files with 74 additions and 8 deletions

View File

@ -110,6 +110,9 @@ class Beautifier:
for path in response[:-1]: for path in response[:-1]:
msg = msg + "|- " + path + "\n" msg = msg + "|- " + path + "\n"
msg = msg + "`- " + response[len(response)-1] msg = msg + "`- " + response[len(response)-1]
elif inC[2] == "logencoding":
msg = "Current log encoding is set to:\n"
msg = msg + response
elif inC[2] in ("ignoreip", "addignoreip", "delignoreip"): elif inC[2] in ("ignoreip", "addignoreip", "delignoreip"):
if len(response) == 0: if len(response) == 0:
msg = "No IP address/network is ignored" msg = "No IP address/network is ignored"

View File

@ -61,6 +61,7 @@ class JailReader(ConfigReader):
def getOptions(self): def getOptions(self):
opts = [["bool", "enabled", "false"], opts = [["bool", "enabled", "false"],
["string", "logpath", "/var/log/messages"], ["string", "logpath", "/var/log/messages"],
["string", "logencoding", "auto"],
["string", "backend", "auto"], ["string", "backend", "auto"],
["int", "maxretry", 3], ["int", "maxretry", 3],
["int", "findtime", 600], ["int", "findtime", 600],
@ -110,6 +111,8 @@ class JailReader(ConfigReader):
logSys.error("No file found for " + path) logSys.error("No file found for " + path)
for p in pathList: for p in pathList:
stream.append(["set", self.__name, "addlogpath", p]) stream.append(["set", self.__name, "addlogpath", p])
elif opt == "logencoding":
stream.append(["set", self.__name, "logencoding", self.__opts[opt]])
elif opt == "backend": elif opt == "backend":
backend = self.__opts[opt] backend = self.__opts[opt]
elif opt == "maxretry": elif opt == "maxretry":

View File

@ -56,6 +56,7 @@ protocol = [
["set <JAIL> delignoreip <IP>", "removes <IP> from the ignore list of <JAIL>"], ["set <JAIL> delignoreip <IP>", "removes <IP> from the ignore list of <JAIL>"],
["set <JAIL> addlogpath <FILE>", "adds <FILE> to the monitoring list of <JAIL>"], ["set <JAIL> addlogpath <FILE>", "adds <FILE> to the monitoring list of <JAIL>"],
["set <JAIL> dellogpath <FILE>", "removes <FILE> from the monitoring list of <JAIL>"], ["set <JAIL> dellogpath <FILE>", "removes <FILE> from the monitoring list of <JAIL>"],
["set <JAIL> logencoding <ENCODING>", "sets the <ENCODING> of the log files for <JAIL>"],
["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"], ["set <JAIL> addfailregex <REGEX>", "adds the regular expression <REGEX> which must match failures for <JAIL>"],
["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"], ["set <JAIL> delfailregex <INDEX>", "removes the regular expression at <INDEX> for failregex"],
["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"], ["set <JAIL> addignoreregex <REGEX>", "adds the regular expression <REGEX> which should match pattern to exclude for <JAIL>"],
@ -77,6 +78,7 @@ protocol = [
["set <JAIL> actionunban <ACT> <CMD>", "sets the unban command <CMD> of the action <ACT> for <JAIL>"], ["set <JAIL> actionunban <ACT> <CMD>", "sets the unban command <CMD> of the action <ACT> for <JAIL>"],
['', "JAIL INFORMATION", ""], ['', "JAIL INFORMATION", ""],
["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"], ["get <JAIL> logpath", "gets the list of the monitored files for <JAIL>"],
["get <JAIL> logencoding <ENCODING>", "gets the <ENCODING> of the log files for <JAIL>"],
["get <JAIL> ignoreip", "gets the list of ignored IP addresses for <JAIL>"], ["get <JAIL> ignoreip", "gets the list of ignored IP addresses for <JAIL>"],
["get <JAIL> failregex", "gets the list of regular expressions which matches the failures for <JAIL>"], ["get <JAIL> failregex", "gets the list of regular expressions which matches the failures for <JAIL>"],
["get <JAIL> ignoreregex", "gets the list of regular expressions which matches patterns to ignore for <JAIL>"], ["get <JAIL> ignoreregex", "gets the list of regular expressions which matches patterns to ignore for <JAIL>"],

View File

@ -55,6 +55,13 @@ backend = auto
# but it will be logged as info. # but it will be logged as info.
usedns = warn usedns = warn
# "logencoding" specifies the encoding of the log files handled by the jail
# This is used to decode the lines from the log file.
# Typical examples: "ascii", "utf-8"
#
# auto: will use the system locale setting
logencoding = auto
# This jail corresponds to the standard configuration in Fail2ban 0.6. # This jail corresponds to the standard configuration in Fail2ban 0.6.
# The mail-whois action send a notification e-mail with a whois request # The mail-whois action send a notification e-mail with a whois request

View File

@ -35,7 +35,7 @@ from datedetector import DateDetector
from mytime import MyTime from mytime import MyTime
from failregex import FailRegex, Regex, RegexException from failregex import FailRegex, Regex, RegexException
import logging, re, os, fcntl, time, sys import logging, re, os, fcntl, time, sys, locale, codecs
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = logging.getLogger("fail2ban.filter") logSys = logging.getLogger("fail2ban.filter")
@ -392,6 +392,7 @@ class FileFilter(Filter):
Filter.__init__(self, jail, **kwargs) Filter.__init__(self, jail, **kwargs)
## The log file path. ## The log file path.
self.__logPath = [] self.__logPath = []
self.setLogEncoding("auto")
## ##
# Add a log file path # Add a log file path
@ -402,7 +403,7 @@ class FileFilter(Filter):
if self.containsLogPath(path): if self.containsLogPath(path):
logSys.error(path + " already exists") logSys.error(path + " already exists")
else: else:
container = FileContainer(path, tail) container = FileContainer(path, self.getLogEncoding(), tail)
self.__logPath.append(container) self.__logPath.append(container)
logSys.info("Added logfile = %s" % path) logSys.info("Added logfile = %s" % path)
self._addLogPath(path) # backend specific self._addLogPath(path) # backend specific
@ -451,6 +452,28 @@ class FileFilter(Filter):
return True return True
return False return False
##
# Set the log file encoding
#
# @param encoding the encoding used with log files
def setLogEncoding(self, encoding):
if encoding.lower() == "auto":
encoding = locale.getpreferredencoding()
codecs.lookup(encoding) # Raise LookupError if invalid codec
for log in self.getLogPath():
log.setEncoding(encoding)
self.__encoding = encoding
logSys.info("Set jail log file encoding to %s" % encoding)
##
# Get the log file encoding
#
# @return log encoding value
def getLogEncoding(self):
return self.__encoding
def getFileContainer(self, path): def getFileContainer(self, path):
for log in self.__logPath: for log in self.__logPath:
if log.getFileName() == path: if log.getFileName() == path:
@ -510,8 +533,9 @@ except ImportError:
class FileContainer: class FileContainer:
def __init__(self, filename, tail = False): def __init__(self, filename, encoding, tail = False):
self.__filename = filename self.__filename = filename
self.setEncoding(encoding)
self.__tail = tail self.__tail = tail
self.__handler = None self.__handler = None
# Try to open the file. Raises an exception if an error occured. # Try to open the file. Raises an exception if an error occured.
@ -534,6 +558,13 @@ class FileContainer:
def getFileName(self): def getFileName(self):
return self.__filename return self.__filename
def setEncoding(self, encoding):
codecs.lookup(encoding) # Raises LookupError if invalid
self.__encoding = encoding
def getEncoding(self):
return self.__encoding
def open(self): def open(self):
self.__handler = open(self.__filename, 'rb') self.__handler = open(self.__filename, 'rb')
# Set the file descriptor to be FD_CLOEXEC # Set the file descriptor to be FD_CLOEXEC
@ -557,11 +588,12 @@ class FileContainer:
return "" return ""
line = self.__handler.readline() line = self.__handler.readline()
try: try:
line = line.decode('utf-8', 'strict') line = line.decode(self.getEncoding(), 'strict')
except UnicodeDecodeError: except UnicodeDecodeError:
logSys.warn("Error decoding line to utf-8: %s" % `line`) logSys.warn("Error decoding line from '%s' with '%s': %s" %
if sys.version_info >= (3,): # In python3, must be unicode (self.getFileName(), self.getEncoding(), `line`))
line = line.decode('utf-8', 'ignore') if sys.version_info >= (3,): # In python3, must be decoded
line = line.decode(self.getEncoding(), 'ignore')
return line return line
def close(self): def close(self):

View File

@ -181,6 +181,12 @@ class Server:
return [m.getFileName() return [m.getFileName()
for m in self.__jails.getFilter(name).getLogPath()] for m in self.__jails.getFilter(name).getLogPath()]
def setLogEncoding(self, name, encoding):
return self.__jails.getFilter(name).setLogEncoding(encoding)
def getLogEncoding(self, name):
return self.__jails.getFilter(name).getLogEncoding()
def setFindTime(self, name, value): def setFindTime(self, name, value):
self.__jails.getFilter(name).setFindTime(value) self.__jails.getFilter(name).setFindTime(value)

View File

@ -139,6 +139,10 @@ class Transmitter:
value = command[2] value = command[2]
self.__server.delLogPath(name, value) self.__server.delLogPath(name, value)
return self.__server.getLogPath(name) return self.__server.getLogPath(name)
elif command[1] == "logencoding":
value = command[2]
self.__server.setLogEncoding(name, value)
return self.__server.getLogEncoding(name)
elif command[1] == "addfailregex": elif command[1] == "addfailregex":
value = command[2] value = command[2]
self.__server.addFailRegex(name, value) self.__server.addFailRegex(name, value)
@ -234,6 +238,8 @@ class Transmitter:
# Filter # Filter
elif command[1] == "logpath": elif command[1] == "logpath":
return self.__server.getLogPath(name) return self.__server.getLogPath(name)
elif command[1] == "logencoding":
return self.__server.getLogEncoding(name)
elif command[1] == "ignoreip": elif command[1] == "ignoreip":
return self.__server.getIgnoreIP(name) return self.__server.getIgnoreIP(name)
elif command[1] == "failregex": elif command[1] == "failregex":

View File

@ -27,7 +27,7 @@ __date__ = "$Date$"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier" __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL" __license__ = "GPL"
import unittest, socket, time, tempfile, os import unittest, socket, time, tempfile, os, locale
from server.server import Server from server.server import Server
class StartStop(unittest.TestCase): class StartStop(unittest.TestCase):
@ -258,6 +258,13 @@ class Transmitter(unittest.TestCase):
self.setGetTest("maxretry", "-2", -2, jail=self.jailName) self.setGetTest("maxretry", "-2", -2, jail=self.jailName)
self.setGetTestNOK("maxretry", "Duck", jail=self.jailName) self.setGetTestNOK("maxretry", "Duck", jail=self.jailName)
def testJailLogEncoding(self):
self.setGetTest("logencoding", "UTF-8", jail=self.jailName)
self.setGetTest("logencoding", "ascii", jail=self.jailName)
self.setGetTest("logencoding", "auto", locale.getpreferredencoding(),
jail=self.jailName)
self.setGetTestNOK("logencoding", "Monkey", jail=self.jailName)
def testJailLogPath(self): def testJailLogPath(self):
self.jailAddDelTest( self.jailAddDelTest(
"logpath", "logpath",