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
Yaroslav Halchenko 12 years ago
commit 9a14cf8b7b

@ -18,5 +18,5 @@ before_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 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

@ -17,6 +17,18 @@ Will carry all fixes in 0.8.x series and new features and enhancements
- New features:
Steven Hiscocks
* 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:
ver. 0.8.8 (2012/12/06) - stable

@ -253,6 +253,10 @@ Releasing
# 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
(cd man ; ./generate-man )

@ -43,7 +43,7 @@ socket = /var/run/fail2ban/fail2ban.sock
# Option: pidfile
# Notes.: Set the PID file. This is used to store the process ID of the
# fail2ban server.
# Values: FILE Default: /var/run/fail2ban/fail2ban.sock
# Values: FILE Default: /var/run/fail2ban/fail2ban.pid
#
pidfile = /var/run/fail2ban/fail2ban.pid

@ -297,10 +297,8 @@ class Action:
if not Action.executeCmd(checkCmd):
logSys.error("Invariant check failed. Trying to restore a sane" +
" environment")
stopCmd = Action.replaceTag(self.__actionStop, self.__cInfo)
Action.executeCmd(stopCmd)
startCmd = Action.replaceTag(self.__actionStart, self.__cInfo)
Action.executeCmd(startCmd)
self.execActionStop()
self.execActionStart()
if not Action.executeCmd(checkCmd):
logSys.fatal("Unable to restore environment")
return False

@ -28,7 +28,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
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
@ -118,6 +118,7 @@ class AsyncServer(asyncore.dispatcher):
except TypeError:
logSys.warning("Type error")
return
AsyncServer.__markCloseOnExec(conn)
# Creates an instance of the handler class to handle the
# request/response on the incoming connection.
RequestHandler(conn, self.__transmitter)
@ -145,6 +146,7 @@ class AsyncServer(asyncore.dispatcher):
self.bind(sock)
except Exception:
raise AsyncServerException("Unable to bind socket %s" % self.__sock)
AsyncServer.__markCloseOnExec(self.socket)
self.listen(1)
# Sets the init flag.
self.__init = True
@ -170,6 +172,18 @@ class AsyncServer(asyncore.dispatcher):
os.remove(self.__sock)
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.

@ -610,7 +610,8 @@ class FileContainer:
self.__handler = open(self.__filename, 'rb')
# Set the file descriptor to be FD_CLOEXEC
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()
# Computes the MD5 of the first line.
myHash = md5sum(firstLine).digest()

@ -27,7 +27,7 @@ from failmanager import FailManagerEmpty
from filter import FileFilter
from mytime import MyTime
import time, logging, gamin
import time, logging, gamin, fcntl
# Gets the instance of the logger.
logSys = logging.getLogger(__name__)
@ -52,6 +52,9 @@ class FilterGamin(FileFilter):
self.__modified = False
# Gamin monitor
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")

@ -66,7 +66,7 @@ class Server:
# First set the mask to only allow access to owner
os.umask(0077)
if self.__daemon:
if self.__daemon: # pragma: no cover
logSys.info("Starting in daemon mode")
ret = self.__createDaemon()
if ret:
@ -389,7 +389,7 @@ class Server:
try:
handler.flush()
handler.close()
except (ValueError, KeyError):
except (ValueError, KeyError): # pragma: no cover
if (2,6) <= sys.version_info < (3,) or \
(3,2) <= sys.version_info:
raise
@ -415,7 +415,7 @@ class Server:
finally:
self.__loggingLock.release()
def __createDaemon(self):
def __createDaemon(self): # pragma: no cover
""" Detach a process from the controlling terminal and run it in the
background as a daemon.

@ -123,7 +123,7 @@ class Transmitter:
elif command[2] == "off":
self.__server.setIdleJail(name, False)
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)
# Filter
elif command[1] == "addignoreip":

@ -62,6 +62,23 @@ class ExecuteAction(unittest.TestCase):
def _is_logged(self, s):
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):
self.__action.setActionStart("touch /tmp/fail2ban.test")
self.__action.setActionStop("rm -f /tmp/fail2ban.test")

@ -520,7 +520,9 @@ class TransmitterLogging(TransmitterBase):
def testLogLevel(self):
self.setGetTest("loglevel", "4", 4)
self.setGetTest("loglevel", "3", 3)
self.setGetTest("loglevel", "2", 2)
self.setGetTest("loglevel", "1", 1)
self.setGetTest("loglevel", "-1", -1)
self.setGetTest("loglevel", "0", 0)
self.setGetTestNOK("loglevel", "Bird")

Loading…
Cancel
Save