mirror of https://github.com/fail2ban/fail2ban
Merge branch 'master' into 0.9
* master: DOC: initiated changelog (but not juice left to actually fill it up ;-)) TST: test all valid loglevels in server testcases TST: Add tag replace and escape test for actions ENH: Minor change to action for consistency of execStart/Stop TST: Coverage for coveralls.io should only be run on success TST: no cover additions to server, primarily daemon creation DOC: thanks @kwirk for spotting the typos in exception message FD_CLOEXEC support Typo in default pidfile in fail2ban.conf Conflicts: .travis.yml -- after_success ChangeLog -- added perspective changelog for 0.8.9 fail2ban/server/asyncserver.py -- imports fail2ban/server/server.py -- no pragma (if I got it right ;-) )pull/181/merge
commit
9a14cf8b7b
|
@ -18,5 +18,5 @@ before_script:
|
||||||
script:
|
script:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PYTHONPATH="$PYTHONPATH:/usr/share/pyshared:/usr/lib/pyshared/python2.7"; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PYTHONPATH="$PYTHONPATH:/usr/share/pyshared:/usr/lib/pyshared/python2.7"; fi
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc bin/fail2ban-testcases; else python bin/fail2ban-testcases; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc bin/fail2ban-testcases; else python bin/fail2ban-testcases; fi
|
||||||
after_script:
|
after_success:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi
|
||||||
|
|
12
ChangeLog
12
ChangeLog
|
@ -17,6 +17,18 @@ Will carry all fixes in 0.8.x series and new features and enhancements
|
||||||
- New features:
|
- New features:
|
||||||
Steven Hiscocks
|
Steven Hiscocks
|
||||||
* Multiline failregex. Close gh-54
|
* Multiline failregex. Close gh-54
|
||||||
|
|
||||||
|
ver. 0.8.9 (2013/04/XXX) - wanna-be-stable
|
||||||
|
----------
|
||||||
|
|
||||||
|
This release incorporates 144 (XXX) non-merge commits from 14
|
||||||
|
contributors (sorted by number of commits): Yaroslav Halchenko, Daniel
|
||||||
|
Black, Steven Hiscocks, ArndRa, hamilton5, pigsyn, Erwan Ben Souiden,
|
||||||
|
Michael Gebetsroither, Orion Poplawski, Artur Penttinen, sebres,
|
||||||
|
Nicolas Collignon, Pascal Borreli, blotus:
|
||||||
|
|
||||||
|
- Fixes:
|
||||||
|
- New features:
|
||||||
- Enhancements:
|
- Enhancements:
|
||||||
|
|
||||||
ver. 0.8.8 (2012/12/06) - stable
|
ver. 0.8.8 (2012/12/06) - stable
|
||||||
|
|
4
DEVELOP
4
DEVELOP
|
@ -253,6 +253,10 @@ Releasing
|
||||||
|
|
||||||
# Add/finalize the corresponding entry in the ChangeLog
|
# Add/finalize the corresponding entry in the ChangeLog
|
||||||
|
|
||||||
|
To generate a list of committers use e.g.
|
||||||
|
|
||||||
|
git shortlog -sn 0.8.8.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g'
|
||||||
|
|
||||||
# Update man pages
|
# Update man pages
|
||||||
|
|
||||||
(cd man ; ./generate-man )
|
(cd man ; ./generate-man )
|
||||||
|
|
|
@ -43,7 +43,7 @@ socket = /var/run/fail2ban/fail2ban.sock
|
||||||
# Option: pidfile
|
# Option: pidfile
|
||||||
# Notes.: Set the PID file. This is used to store the process ID of the
|
# Notes.: Set the PID file. This is used to store the process ID of the
|
||||||
# fail2ban server.
|
# fail2ban server.
|
||||||
# Values: FILE Default: /var/run/fail2ban/fail2ban.sock
|
# Values: FILE Default: /var/run/fail2ban/fail2ban.pid
|
||||||
#
|
#
|
||||||
pidfile = /var/run/fail2ban/fail2ban.pid
|
pidfile = /var/run/fail2ban/fail2ban.pid
|
||||||
|
|
||||||
|
|
|
@ -297,10 +297,8 @@ class Action:
|
||||||
if not Action.executeCmd(checkCmd):
|
if not Action.executeCmd(checkCmd):
|
||||||
logSys.error("Invariant check failed. Trying to restore a sane" +
|
logSys.error("Invariant check failed. Trying to restore a sane" +
|
||||||
" environment")
|
" environment")
|
||||||
stopCmd = Action.replaceTag(self.__actionStop, self.__cInfo)
|
self.execActionStop()
|
||||||
Action.executeCmd(stopCmd)
|
self.execActionStart()
|
||||||
startCmd = Action.replaceTag(self.__actionStart, self.__cInfo)
|
|
||||||
Action.executeCmd(startCmd)
|
|
||||||
if not Action.executeCmd(checkCmd):
|
if not Action.executeCmd(checkCmd):
|
||||||
logSys.fatal("Unable to restore environment")
|
logSys.fatal("Unable to restore environment")
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -28,7 +28,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
import asyncore, asynchat, socket, os, logging, sys, traceback
|
import asyncore, asynchat, socket, os, logging, sys, traceback, fcntl
|
||||||
|
|
||||||
from fail2ban import helpers
|
from fail2ban import helpers
|
||||||
|
|
||||||
|
@ -118,6 +118,7 @@ class AsyncServer(asyncore.dispatcher):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
logSys.warning("Type error")
|
logSys.warning("Type error")
|
||||||
return
|
return
|
||||||
|
AsyncServer.__markCloseOnExec(conn)
|
||||||
# Creates an instance of the handler class to handle the
|
# Creates an instance of the handler class to handle the
|
||||||
# request/response on the incoming connection.
|
# request/response on the incoming connection.
|
||||||
RequestHandler(conn, self.__transmitter)
|
RequestHandler(conn, self.__transmitter)
|
||||||
|
@ -145,6 +146,7 @@ class AsyncServer(asyncore.dispatcher):
|
||||||
self.bind(sock)
|
self.bind(sock)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise AsyncServerException("Unable to bind socket %s" % self.__sock)
|
raise AsyncServerException("Unable to bind socket %s" % self.__sock)
|
||||||
|
AsyncServer.__markCloseOnExec(self.socket)
|
||||||
self.listen(1)
|
self.listen(1)
|
||||||
# Sets the init flag.
|
# Sets the init flag.
|
||||||
self.__init = True
|
self.__init = True
|
||||||
|
@ -170,6 +172,18 @@ class AsyncServer(asyncore.dispatcher):
|
||||||
os.remove(self.__sock)
|
os.remove(self.__sock)
|
||||||
logSys.debug("Socket shutdown")
|
logSys.debug("Socket shutdown")
|
||||||
|
|
||||||
|
##
|
||||||
|
# Marks socket as close-on-exec to avoid leaking file descriptors when
|
||||||
|
# running actions involving command execution.
|
||||||
|
|
||||||
|
# @param sock: socket file.
|
||||||
|
|
||||||
|
#@staticmethod
|
||||||
|
def __markCloseOnExec(sock):
|
||||||
|
fd = sock.fileno()
|
||||||
|
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||||
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
||||||
|
__markCloseOnExec = staticmethod(__markCloseOnExec)
|
||||||
|
|
||||||
##
|
##
|
||||||
# AsyncServerException is used to wrap communication exceptions.
|
# AsyncServerException is used to wrap communication exceptions.
|
||||||
|
|
|
@ -610,7 +610,8 @@ class FileContainer:
|
||||||
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
|
||||||
fd = self.__handler.fileno()
|
fd = self.__handler.fileno()
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, fd | fcntl.FD_CLOEXEC)
|
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||||
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
|
||||||
firstLine = self.__handler.readline()
|
firstLine = self.__handler.readline()
|
||||||
# Computes the MD5 of the first line.
|
# Computes the MD5 of the first line.
|
||||||
myHash = md5sum(firstLine).digest()
|
myHash = md5sum(firstLine).digest()
|
||||||
|
|
|
@ -27,7 +27,7 @@ from failmanager import FailManagerEmpty
|
||||||
from filter import FileFilter
|
from filter import FileFilter
|
||||||
from mytime import MyTime
|
from mytime import MyTime
|
||||||
|
|
||||||
import time, logging, gamin
|
import time, logging, gamin, fcntl
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger(__name__)
|
logSys = logging.getLogger(__name__)
|
||||||
|
@ -52,6 +52,9 @@ class FilterGamin(FileFilter):
|
||||||
self.__modified = False
|
self.__modified = False
|
||||||
# Gamin monitor
|
# Gamin monitor
|
||||||
self.monitor = gamin.WatchMonitor()
|
self.monitor = gamin.WatchMonitor()
|
||||||
|
fd = self.monitor.get_fd()
|
||||||
|
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
||||||
|
fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC)
|
||||||
logSys.debug("Created FilterGamin")
|
logSys.debug("Created FilterGamin")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ class Server:
|
||||||
|
|
||||||
# First set the mask to only allow access to owner
|
# First set the mask to only allow access to owner
|
||||||
os.umask(0077)
|
os.umask(0077)
|
||||||
if self.__daemon:
|
if self.__daemon: # pragma: no cover
|
||||||
logSys.info("Starting in daemon mode")
|
logSys.info("Starting in daemon mode")
|
||||||
ret = self.__createDaemon()
|
ret = self.__createDaemon()
|
||||||
if ret:
|
if ret:
|
||||||
|
@ -389,7 +389,7 @@ class Server:
|
||||||
try:
|
try:
|
||||||
handler.flush()
|
handler.flush()
|
||||||
handler.close()
|
handler.close()
|
||||||
except (ValueError, KeyError):
|
except (ValueError, KeyError): # pragma: no cover
|
||||||
if (2,6) <= sys.version_info < (3,) or \
|
if (2,6) <= sys.version_info < (3,) or \
|
||||||
(3,2) <= sys.version_info:
|
(3,2) <= sys.version_info:
|
||||||
raise
|
raise
|
||||||
|
@ -415,7 +415,7 @@ class Server:
|
||||||
finally:
|
finally:
|
||||||
self.__loggingLock.release()
|
self.__loggingLock.release()
|
||||||
|
|
||||||
def __createDaemon(self):
|
def __createDaemon(self): # pragma: no cover
|
||||||
""" Detach a process from the controlling terminal and run it in the
|
""" Detach a process from the controlling terminal and run it in the
|
||||||
background as a daemon.
|
background as a daemon.
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,7 @@ class Transmitter:
|
||||||
elif command[2] == "off":
|
elif command[2] == "off":
|
||||||
self.__server.setIdleJail(name, False)
|
self.__server.setIdleJail(name, False)
|
||||||
else:
|
else:
|
||||||
raise Exception("Invalid idle option, must be 'yes' or 'no'")
|
raise Exception("Invalid idle option, must be 'on' or 'off'")
|
||||||
return self.__server.getIdleJail(name)
|
return self.__server.getIdleJail(name)
|
||||||
# Filter
|
# Filter
|
||||||
elif command[1] == "addignoreip":
|
elif command[1] == "addignoreip":
|
||||||
|
|
|
@ -62,6 +62,23 @@ class ExecuteAction(unittest.TestCase):
|
||||||
def _is_logged(self, s):
|
def _is_logged(self, s):
|
||||||
return s in self._log.getvalue()
|
return s in self._log.getvalue()
|
||||||
|
|
||||||
|
def testReplaceTag(self):
|
||||||
|
aInfo = {
|
||||||
|
'HOST': "192.0.2.0",
|
||||||
|
'ABC': "123",
|
||||||
|
'xyz': "890",
|
||||||
|
}
|
||||||
|
self.assertEqual(
|
||||||
|
self.__action.replaceTag("Text <HOST> text", aInfo),
|
||||||
|
"Text 192.0.2.0 text")
|
||||||
|
self.assertEqual(
|
||||||
|
self.__action.replaceTag("Text <xyz> text <ABC> ABC", aInfo),
|
||||||
|
"Text 890 text 123 ABC")
|
||||||
|
self.assertEqual(
|
||||||
|
self.__action.replaceTag("<matches>",
|
||||||
|
{'matches': "some >char< should \< be[ escap}ed&"}),
|
||||||
|
r"some \>char\< should \\\< be\[ escap\}ed\&")
|
||||||
|
|
||||||
def testExecuteActionBan(self):
|
def testExecuteActionBan(self):
|
||||||
self.__action.setActionStart("touch /tmp/fail2ban.test")
|
self.__action.setActionStart("touch /tmp/fail2ban.test")
|
||||||
self.__action.setActionStop("rm -f /tmp/fail2ban.test")
|
self.__action.setActionStop("rm -f /tmp/fail2ban.test")
|
||||||
|
|
|
@ -520,7 +520,9 @@ class TransmitterLogging(TransmitterBase):
|
||||||
|
|
||||||
def testLogLevel(self):
|
def testLogLevel(self):
|
||||||
self.setGetTest("loglevel", "4", 4)
|
self.setGetTest("loglevel", "4", 4)
|
||||||
|
self.setGetTest("loglevel", "3", 3)
|
||||||
self.setGetTest("loglevel", "2", 2)
|
self.setGetTest("loglevel", "2", 2)
|
||||||
|
self.setGetTest("loglevel", "1", 1)
|
||||||
self.setGetTest("loglevel", "-1", -1)
|
self.setGetTest("loglevel", "-1", -1)
|
||||||
self.setGetTest("loglevel", "0", 0)
|
self.setGetTest("loglevel", "0", 0)
|
||||||
self.setGetTestNOK("loglevel", "Bird")
|
self.setGetTestNOK("loglevel", "Bird")
|
||||||
|
|
Loading…
Reference in New Issue