diff --git a/ChangeLog b/ChangeLog index 07fc93b6..b5b624eb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -88,10 +88,14 @@ ver. 0.9.5 (2016/XX/XXX) - wanna-be-released added new parameter `__date_ambit` * gentoo-initd fixed --pidfile bug: `--pidfile` is option of start-stop-daemon, not argument of fail2ban (see gh-1434) + * filter.d/asterisk.conf + - fix security log support for PJSIP and Asterisk 13+ (gh-1456) + - improved log support for PJSIP and Asterisk 13+ with different callID (gh-1458) - New Features: * New Actions: - action.d/firewallcmd-rich-rules and action.d/firewallcmd-rich-logging (gh-1367) + - Enhancements: * Extreme speedup of all sqlite database operations (gh-1436), by using of following sqlite options: @@ -100,7 +104,9 @@ ver. 0.9.5 (2016/XX/XXX) - wanna-be-released - (temp_store = MEMORY) temporary tables and indices are kept in memory * journald journalmatch for pure-ftpd (gh-1362) * Add additional regex filter for dovecot ldap authentication failures (gh-1370) - * added additional regex filters for exim (gh-1371) + * filter.d/exim*conf + - added additional regexes (gh-1371) + - made port entry optional ver. 0.9.4 (2016/03/08) - for-you-ladies diff --git a/MANIFEST b/MANIFEST index cd250d3d..05e665b2 100644 --- a/MANIFEST +++ b/MANIFEST @@ -13,9 +13,12 @@ config/action.d/complain.conf config/action.d/dshield.conf config/action.d/dummy.conf config/action.d/firewallcmd-allports.conf +config/action.d/firewallcmd-common.conf config/action.d/firewallcmd-ipset.conf config/action.d/firewallcmd-multiport.conf config/action.d/firewallcmd-new.conf +config/action.d/firewallcmd-rich-logging.conf +config/action.d/firewallcmd-rich-rules.conf config/action.d/hostsdeny.conf config/action.d/ipfilter.conf config/action.d/ipfw.conf @@ -31,6 +34,7 @@ config/action.d/iptables-new.conf config/action.d/iptables-xt_recent-echo.conf config/action.d/mail-buffered.conf config/action.d/mail.conf +config/action.d/mail-whois-common.conf config/action.d/mail-whois.conf config/action.d/mail-whois-lines.conf config/action.d/mynetwatchman.conf @@ -52,6 +56,7 @@ config/action.d/sendmail-whois-ipmatches.conf config/action.d/sendmail-whois-lines.conf config/action.d/sendmail-whois-matches.conf config/action.d/shorewall.conf +config/action.d/shorewall-ipset-proto6.conf config/action.d/smtp.py config/action.d/symbiosis-blacklist-allports.conf config/action.d/ufw.conf @@ -67,6 +72,7 @@ config/filter.d/apache-modsecurity.conf config/filter.d/apache-nohome.conf config/filter.d/apache-noscript.conf config/filter.d/apache-overflows.conf +config/filter.d/apache-pass.conf config/filter.d/apache-shellshock.conf config/filter.d/assp.conf config/filter.d/asterisk.conf @@ -79,17 +85,18 @@ config/filter.d/cyrus-imap.conf config/filter.d/directadmin.conf config/filter.d/dovecot.conf config/filter.d/dropbear.conf +config/filter.d/drupal-auth.conf config/filter.d/ejabberd-auth.conf config/filter.d/exim-common.conf config/filter.d/exim.conf config/filter.d/exim-spam.conf config/filter.d/freeswitch.conf +config/filter.d/froxlor-auth.conf config/filter.d/groupoffice.conf config/filter.d/gssftpd.conf config/filter.d/guacamole.conf config/filter.d/haproxy-http-auth.conf config/filter.d/horde.conf -config/filter.d/ignorecommands config/filter.d/ignorecommands/apache-fakegooglebot config/filter.d/kerio.conf config/filter.d/lighttpd-auth.conf @@ -122,7 +129,6 @@ config/filter.d/selinux-common.conf config/filter.d/selinux-ssh.conf config/filter.d/sendmail-auth.conf config/filter.d/sendmail-reject.conf -config/filter.d/sendmail-spam.conf config/filter.d/sieve.conf config/filter.d/sogo-auth.conf config/filter.d/solid-pop3d.conf @@ -148,7 +154,6 @@ config/paths-osx.conf CONTRIBUTING.md COPYING DEVELOP -doc/run-rootless.txt fail2ban-2to3 fail2ban/client/actionreader.py fail2ban/client/beautifier.py @@ -185,7 +190,6 @@ fail2ban/server/filterpyinotify.py fail2ban/server/filtersystemd.py fail2ban/server/__init__.py fail2ban/server/ipdns.py -fail2ban/server/iso8601.py fail2ban/server/jail.py fail2ban/server/jails.py fail2ban/server/jailthread.py @@ -203,21 +207,19 @@ fail2ban/tests/action_d/test_smtp.py fail2ban/tests/actionstestcase.py fail2ban/tests/actiontestcase.py fail2ban/tests/banmanagertestcase.py -fail2ban/tests/clientreadertestcase.py fail2ban/tests/clientbeautifiertestcase.py +fail2ban/tests/clientreadertestcase.py fail2ban/tests/config/action.d/brokenaction.conf fail2ban/tests/config/fail2ban.conf fail2ban/tests/config/filter.d/simple.conf fail2ban/tests/config/filter.d/test.conf fail2ban/tests/config/filter.d/test.local +fail2ban/tests/config/filter.d/zzz-generic-example.conf fail2ban/tests/config/jail.conf -fail2ban/tests/config/paths-common.conf -fail2ban/tests/config/paths-debian.conf -fail2ban/tests/config/paths-freebsd.conf -fail2ban/tests/config/paths-osx.conf fail2ban/tests/databasetestcase.py fail2ban/tests/datedetectortestcase.py fail2ban/tests/dummyjail.py +fail2ban/tests/fail2banclienttestcase.py fail2ban/tests/fail2banregextestcase.py fail2ban/tests/failmanagertestcase.py fail2ban/tests/files/action.d/action_checkainfo.py @@ -250,13 +252,13 @@ fail2ban/tests/files/ignorecommand.py fail2ban/tests/files/logs/3proxy fail2ban/tests/files/logs/apache-auth fail2ban/tests/files/logs/apache-badbots -fail2ban/tests/files/logs/apache-botscripts fail2ban/tests/files/logs/apache-botsearch fail2ban/tests/files/logs/apache-fakegooglebot fail2ban/tests/files/logs/apache-modsecurity fail2ban/tests/files/logs/apache-nohome fail2ban/tests/files/logs/apache-noscript fail2ban/tests/files/logs/apache-overflows +fail2ban/tests/files/logs/apache-pass fail2ban/tests/files/logs/apache-shellshock fail2ban/tests/files/logs/assp fail2ban/tests/files/logs/asterisk @@ -270,10 +272,12 @@ fail2ban/tests/files/logs/cyrus-imap fail2ban/tests/files/logs/directadmin fail2ban/tests/files/logs/dovecot fail2ban/tests/files/logs/dropbear +fail2ban/tests/files/logs/drupal-auth fail2ban/tests/files/logs/ejabberd-auth fail2ban/tests/files/logs/exim fail2ban/tests/files/logs/exim-spam fail2ban/tests/files/logs/freeswitch +fail2ban/tests/files/logs/froxlor-auth fail2ban/tests/files/logs/groupoffice fail2ban/tests/files/logs/gssftpd fail2ban/tests/files/logs/guacamole @@ -309,7 +313,6 @@ fail2ban/tests/files/logs/screensharingd fail2ban/tests/files/logs/selinux-ssh fail2ban/tests/files/logs/sendmail-auth fail2ban/tests/files/logs/sendmail-reject -fail2ban/tests/files/logs/sendmail-spam fail2ban/tests/files/logs/sieve fail2ban/tests/files/logs/sogo-auth fail2ban/tests/files/logs/solid-pop3d @@ -325,6 +328,7 @@ fail2ban/tests/files/logs/vsftpd fail2ban/tests/files/logs/webmin-auth fail2ban/tests/files/logs/wuftpd fail2ban/tests/files/logs/xinetd-fail +fail2ban/tests/files/logs/zzz-generic-example fail2ban/tests/files/testcase01.log fail2ban/tests/files/testcase02.log fail2ban/tests/files/testcase03.log @@ -356,6 +360,8 @@ files/gentoo-confd files/gentoo-initd files/ipmasq-ZZZzzz_fail2ban.rul files/logwatch/fail2ban +files/logwatch/fail2ban-0.8.log +files/logwatch/fail2ban-0.9.log files/macosx-initd files/monit/fail2ban files/nagios/check_fail2ban @@ -373,6 +379,8 @@ man/fail2ban-regex.1 man/fail2ban-regex.h2m man/fail2ban-server.1 man/fail2ban-server.h2m +man/fail2ban-testcases.1 +man/fail2ban-testcases.h2m man/generate-man man/jail.conf.5 README.md diff --git a/RELEASE b/RELEASE index fa89fc34..e570c9eb 100644 --- a/RELEASE +++ b/RELEASE @@ -190,7 +190,7 @@ Post Release Add the following to the top of the ChangeLog:: - ver. 0.9.6 (2016/XX/XXX) - wanna-be-released + ver. 0.10.0 (2016/XX/XXX) - wanna-be-released ----------- - Fixes: diff --git a/bin/fail2ban-testcases b/bin/fail2ban-testcases index 20d444f0..98b9118f 100755 --- a/bin/fail2ban-testcases +++ b/bin/fail2ban-testcases @@ -30,7 +30,7 @@ import sys import time import unittest -# Check if local fail2ban module exists, and use if it exists by +# Check if local fail2ban module exists, and use if it exists by # modifying the path. This is such that tests can be used in dev # environment. if os.path.exists("fail2ban/__init__.py"): @@ -122,7 +122,7 @@ if verbosity > 1: # pragma: no cover if verbosity > 3: fmt = ' | %(module)15.15s-%(levelno)-2d: %(funcName)-20.20s |' + fmt if verbosity > 2: - fmt = ' +%(relativeCreated)5d %(thread)X %(levelname)-5.5s' + fmt + fmt = ' +%(relativeCreated)5d %(thread)X %(name)-25.25s %(levelname)-5.5s' + fmt else: fmt = ' %(asctime)-15s %(thread)X %(levelname)-5.5s' + fmt # diff --git a/config/filter.d/asterisk.conf b/config/filter.d/asterisk.conf index 01063efa..6af452e2 100644 --- a/config/filter.d/asterisk.conf +++ b/config/filter.d/asterisk.conf @@ -27,6 +27,7 @@ failregex = ^%(__prefix_line)s%(log_prefix)s Registration from '[^']*' failed fo ^%(__prefix_line)s%(log_prefix)s hacking attempt detected ''$ ^%(__prefix_line)s%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)//\d+"(,Challenge="[\w/]+")?(,ReceivedChallenge="\w+")?(,Response="\w+",ExpectedResponse="\w*")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$ ^%(__prefix_line)s%(log_prefix)s "Rejecting unknown SIP connection from "$ + ^%(__prefix_line)s%(log_prefix)s Request (?:'[^']*' )?from '[^']*' failed for '(?::\d+)?'\s\(callid: [^\)]*\) - (?:No matching endpoint found|Not match Endpoint(?: Contact)? ACL|(?:Failed|Error) to authenticate)\s*$ ignoreregex = diff --git a/config/filter.d/exim-common.conf b/config/filter.d/exim-common.conf index 1c0a0a20..0e1b74fa 100644 --- a/config/filter.d/exim-common.conf +++ b/config/filter.d/exim-common.conf @@ -9,8 +9,8 @@ after = exim-common.local [Definition] -host_info = H=([\w.-]+ )?(\(\S+\) )?\[\](:\d+)? (I=\[\S+\]:\d+ )?(U=\S+ )?(P=e?smtp )? -pid = ( \[\d+\])? +host_info = (?:H=([\w.-]+ )?(?:\(\S+\) )?)?\[\](?::\d+)? (?:I=\[\S+\](:\d+)? )?(?:U=\S+ )?(?:P=e?smtp )? +pid = (?: \[\d+\])? # DEV Notes: # From exim source code: ./src/receive.c:add_host_info_for_log diff --git a/config/filter.d/exim.conf b/config/filter.d/exim.conf index 4aadf15c..a1d699c0 100644 --- a/config/filter.d/exim.conf +++ b/config/filter.d/exim.conf @@ -14,13 +14,13 @@ before = exim-common.conf [Definition] failregex = ^%(pid)s %(host_info)ssender verify fail for <\S+>: (?:Unknown user|Unrouteable address|all relevant MX records point to non-existent hosts)\s*$ - ^%(pid)s \w+ authenticator failed for (\S+ )?\(\S+\) \[\](:\d+)?( I=\[\S+\](:\d+)?)?: 535 Incorrect authentication data( \(set_id=.*\)|: \d+ Time\(s\))?\s*$ - ^%(pid)s %(host_info)sF=(<>|[^@]+@\S+) rejected RCPT [^@]+@\S+: (relay not permitted|Sender verify failed|Unknown user)\s*$ - ^%(pid)s SMTP protocol synchronization error \([^)]*\): rejected (connection from|"\S+") %(host_info)s(next )?input=".*"\s*$ - ^%(pid)s SMTP call from \S+ \[\](:\d+)? (I=\[\S+\](:\d+)? )?dropped: too many nonmail commands \(last was "\S+"\)\s*$ - ^%(pid)s SMTP protocol error in "AUTH \S*(| \S*)" H=(|\S* )(|\(\S*\) )\[\]\:\d+ I=\[\S*\]\:\d+ AUTH command used when not advertised\s*$ - ^%(pid)s no MAIL in SMTP connection from (|\S* )(|\(\S*\) )\[\]\:\d+ I=\[\S*\]\:\d+ D=\d+s(| C=\S*)\s*$ - ^%(pid)s \S+ SMTP connection from (|\S* )(|\(\S*\) )\[\]\:\d+ I=\[\S*\]\:\d+ closed by DROP in ACL\s*$ + ^%(pid)s \w+ authenticator failed for (\S+ )?\(\S+\) \[\](?::\d+)?(?: I=\[\S+\](:\d+)?)?: 535 Incorrect authentication data( \(set_id=.*\)|: \d+ Time\(s\))?\s*$ + ^%(pid)s %(host_info)sF=(?:<>|[^@]+@\S+) rejected RCPT [^@]+@\S+: (?:relay not permitted|Sender verify failed|Unknown user)\s*$ + ^%(pid)s SMTP protocol synchronization error \([^)]*\): rejected (?:connection from|"\S+") %(host_info)s(?:next )?input=".*"\s*$ + ^%(pid)s SMTP call from \S+ %(host_info)sdropped: too many nonmail commands \(last was "\S+"\)\s*$ + ^%(pid)s SMTP protocol error in "AUTH \S*(?: \S*)?" %(host_info)sAUTH command used when not advertised\s*$ + ^%(pid)s no MAIL in SMTP connection from (?:\S* )?(?:\(\S*\) )?%(host_info)sD=\d+s(?: C=\S*)?\s*$ + ^%(pid)s \S+ SMTP connection from (?:\S* )?(?:\(\S*\) )?%(host_info)sclosed by DROP in ACL\s*$ ignoreregex = diff --git a/fail2ban/client/fail2banclient.py b/fail2ban/client/fail2banclient.py index a8e0a331..9e6c4bd6 100755 --- a/fail2ban/client/fail2banclient.py +++ b/fail2ban/client/fail2banclient.py @@ -35,7 +35,7 @@ from ..version import version from .csocket import CSocket from .beautifier import Beautifier from .fail2bancmdline import Fail2banCmdLine, ServerExecutionException, ExitException, \ - logSys, PRODUCTION, exit, output + logSys, exit, output PROMPT = "fail2ban> " @@ -227,11 +227,11 @@ class Fail2banClient(Fail2banCmdLine, Thread): # prepare: read config, check configuration is valid, etc.: if phase is not None: phase['start'] = True - logSys.debug('-- client phase %s', phase) + logSys.debug(' client phase %s', phase) stream = self.__prepareStartServer() if phase is not None: phase['ready'] = phase['start'] = (True if stream else False) - logSys.debug('-- client phase %s', phase) + logSys.debug(' client phase %s', phase) if not stream: return False # configure server with config stream: @@ -248,7 +248,7 @@ class Fail2banClient(Fail2banCmdLine, Thread): def __processCommand(self, cmd): if len(cmd) == 1 and cmd[0] == "start": - + ret = self.__startServer(self._conf["background"]) if not ret: return False @@ -322,7 +322,7 @@ class Fail2banClient(Fail2banCmdLine, Thread): # Wait for the server to start (the server has 30 seconds to answer ping) starttime = time.time() logSys.debug("__waitOnServer: %r", (alive, maxtime)) - test = lambda: os.path.exists(self._conf["socket"]) and self.__ping() + test = lambda: os.path.exists(self._conf["socket"]) and self.__ping() with VisualWait(self._conf["verbose"]) as vis: sltime = 0.0125 / 2 while self._alive: @@ -361,19 +361,16 @@ class Fail2banClient(Fail2banCmdLine, Thread): # Interactive mode if self._conf.get("interactive", False): - # no readline in test: - if PRODUCTION: # pragma: no cover - try: - import readline - except ImportError: - raise ServerExecutionException("Readline not available") + try: + import readline + except ImportError: + raise ServerExecutionException("Readline not available") try: ret = True if len(args) > 0: ret = self.__processCommand(args) if ret: - if PRODUCTION: # pragma: no cover - readline.parse_and_bind("tab: complete") + readline.parse_and_bind("tab: complete") self.dispInteractive() while True: cmd = input_command() @@ -411,11 +408,9 @@ class Fail2banClient(Fail2banCmdLine, Thread): signal.signal(s, sh) -## -# Wonderful visual :) -# - class _VisualWait: + """Small progress indication (as "wonderful visual") during waiting process + """ pos = 0 delta = 1 def __init__(self, maxpos=10): @@ -427,12 +422,14 @@ class _VisualWait: sys.stdout.write('\r'+(' '*(35+self.maxpos))+'\r') sys.stdout.flush() def heartbeat(self): + """Show or step for progress indicator + """ if not self.pos: sys.stdout.write("\nINFO [#" + (' '*self.maxpos) + "] Waiting on the server...\r\x1b[8C") self.pos += self.delta if self.delta > 0: s = " #\x1b[1D" if self.pos > 1 else "# \x1b[2D" - else: + else: s = "\x1b[1D# \x1b[2D" sys.stdout.write(s) sys.stdout.flush() @@ -441,6 +438,8 @@ class _VisualWait: elif self.pos < 2: self.delta = 1 class _NotVisualWait: + """Mockup for invisible progress indication (not verbose) + """ def __enter__(self): return self def __exit__(self, *args): @@ -449,6 +448,8 @@ class _NotVisualWait: pass def VisualWait(verbose, *args, **kwargs): + """Wonderful visual progress indication (if verbose) + """ return _VisualWait(*args, **kwargs) if verbose > 1 else _NotVisualWait() diff --git a/fail2ban/client/fail2bancmdline.py b/fail2ban/client/fail2bancmdline.py index 83379a9a..a14a1cf4 100644 --- a/fail2ban/client/fail2bancmdline.py +++ b/fail2ban/client/fail2bancmdline.py @@ -42,6 +42,7 @@ PRODUCTION = True MAX_WAITTIME = 30 + class Fail2banCmdLine(): def __init__(self): @@ -83,9 +84,6 @@ class Fail2banCmdLine(): output("Copyright (c) 2004-2008 Cyril Jaquier, 2008- Fail2Ban Contributors") output("Copyright of modifications held by their respective authors.") output("Licensed under the GNU General Public License v2 (GPL).") - output("") - output("Written by Cyril Jaquier .") - output("Many contributions by Yaroslav O. Halchenko .") def dispUsage(self): """ Prints Fail2Ban command line options and exits @@ -114,7 +112,7 @@ class Fail2banCmdLine(): output(" --timeout timeout to wait for the server (for internal usage only, don't read configuration)") output(" -h, --help display this help message") output(" -V, --version print the version") - + if not caller.endswith('server'): output("") output("Command:") @@ -187,7 +185,7 @@ class Fail2banCmdLine(): if ret is not None: return ret - logSys.debug("-- conf: %r, args: %r", self._conf, self._args) + logSys.debug(" conf: %r, args: %r", self._conf, self._args) if initial and PRODUCTION: # pragma: no cover - can't test verbose = self._conf["verbose"] @@ -259,14 +257,26 @@ class Fail2banCmdLine(): output(c) return True + # + # _exit is made to ease mocking out of the behaviour in tests, + # since method is also exposed in API via globally bound variable @staticmethod - def exit(code=0): # pragma: no cover - can't test - logSys.debug("Exit with code %s", code) - if os._exit: + def _exit(code=0): + if hasattr(os, '_exit') and os._exit: os._exit(code) else: sys.exit(code) + @staticmethod + def exit(code=0): + logSys.debug("Exit with code %s", code) + # because of possible buffered output in python, we should flush it before exit: + sys.stdout.flush() + sys.stderr.flush() + # exit + Fail2banCmdLine._exit(code) + + # global exit handler: exit = Fail2banCmdLine.exit diff --git a/fail2ban/client/fail2banserver.py b/fail2ban/client/fail2banserver.py index a511e017..9f825bf1 100644 --- a/fail2ban/client/fail2banserver.py +++ b/fail2ban/client/fail2banserver.py @@ -46,7 +46,7 @@ class Fail2banServer(Fail2banCmdLine): @staticmethod def startServerDirect(conf, daemon=True): - logSys.debug("-- direct starting of server in %s, deamon: %s", os.getpid(), daemon) + logSys.debug(" direct starting of server in %s, deamon: %s", os.getpid(), daemon) from ..server.server import Server server = None try: @@ -54,7 +54,7 @@ class Fail2banServer(Fail2banCmdLine): # server object will internally fork self if daemon is True server = Server(daemon) server.start(conf["socket"], - conf["pidfile"], conf["force"], + conf["pidfile"], conf["force"], conf=conf) except Exception as e: # pragma: no cover try: @@ -79,7 +79,7 @@ class Fail2banServer(Fail2banCmdLine): frk = not conf["async"] and PRODUCTION if frk: # pragma: no cover pid = os.fork() - logSys.debug("-- async starting of server in %s, fork: %s - %s", os.getpid(), frk, pid) + logSys.debug(" async starting of server in %s, fork: %s - %s", os.getpid(), frk, pid) if pid == 0: args = list() args.append(SERVER) diff --git a/fail2ban/protocol.py b/fail2ban/protocol.py index 648666a1..d671f3c3 100644 --- a/fail2ban/protocol.py +++ b/fail2ban/protocol.py @@ -27,6 +27,9 @@ __license__ = "GPL" import textwrap def output(s): + """Default output handler for printing protocol. + Used to ease mocking in the test cases. + """ print(s) ## diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py index b9616ead..54f00f6a 100644 --- a/fail2ban/server/server.py +++ b/fail2ban/server/server.py @@ -88,6 +88,11 @@ class Server: logSys.debug("Caught signal %d. Flushing logs" % signum) self.flushLogs() + def _rebindSignal(self, s, new): + """Bind new signal handler while storing old one in _prev_signals""" + self.__prev_signals[s] = signal.getsignal(s) + signal.signal(s, new) + def start(self, sock, pidfile, force=False, observer=True, conf={}): # First set the mask to only allow access to owner os.umask(0077) @@ -121,9 +126,10 @@ class Server: # Install signal handlers if _thread_name() == '_MainThread': - for s in (signal.SIGTERM, signal.SIGINT, signal.SIGUSR1): - self.__prev_signals[s] = signal.getsignal(s) - signal.signal(s, self.__sigTERMhandler if s != signal.SIGUSR1 else self.__sigUSR1handler) + for s in (signal.SIGTERM, signal.SIGINT): + self._rebindSignal(s, self.__sigTERMhandler) + self._rebindSignal(signal.SIGUSR1, self.__sigUSR1handler) + # Ensure unhandled exceptions are logged sys.excepthook = excepthook @@ -389,7 +395,7 @@ class Server: return self.__jails[name].getBanTimeExtra(opt) def isStarted(self): - self.__asyncServer.isActive() + return self.__asyncServer is not None and self.__asyncServer.isActive() def isAlive(self, jailnum=None): if jailnum is not None and len(self.__jails) != jailnum: @@ -437,7 +443,7 @@ class Server: getLogger("fail2ban").setLevel(getattr(logging, value)) self.__logLevel = value except AttributeError: - raise ValueError("Invalid log level") + raise ValueError("Invalid log level %r" % value) ## # Get the logging level. @@ -512,14 +518,14 @@ class Server: # Is known to be thrown after logging was shutdown once # with older Pythons -- seems to be safe to ignore there # At least it was still failing on 2.6.2-0ubuntu1 (jaunty) - if (2,6,3) <= sys.version_info < (3,) or \ - (3,2) <= sys.version_info: + if (2, 6, 3) <= sys.version_info < (3,) or \ + (3, 2) <= sys.version_info: raise # tell the handler to use this format hdlr.setFormatter(formatter) logger.addHandler(hdlr) # Does not display this message at startup. - if not self.__logTarget is None: + if self.__logTarget is not None: logSys.info("Start Fail2ban v%s", version.version) logSys.info( "Changed logging target to %s for Fail2ban v%s" @@ -608,9 +614,7 @@ class Server: # We need to set this in the parent process, so it gets inherited by the # child process, and this makes sure that it is effect even if the parent # terminates quickly. - for s in (signal.SIGHUP,): - self.__prev_signals[s] = signal.getsignal(s) - signal.signal(s, signal.SIG_IGN) + self._rebindSignal(signal.SIGHUP, signal.SIG_IGN) try: # Fork a child process so the parent can exit. This will return control diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py index 1a0880c7..ed2d3c46 100644 --- a/fail2ban/tests/fail2banclienttestcase.py +++ b/fail2ban/tests/fail2banclienttestcase.py @@ -31,25 +31,30 @@ import time import signal import unittest +from os.path import join as pjoin, isdir, isfile, exists, dirname from functools import wraps from threading import Thread - from ..client import fail2banclient, fail2banserver, fail2bancmdline -from ..client.fail2banclient import Fail2banClient, exec_command_line as _exec_client, VisualWait +from ..client.fail2bancmdline import Fail2banCmdLine +from ..client.fail2banclient import exec_command_line as _exec_client, VisualWait from ..client.fail2banserver import Fail2banServer, exec_command_line as _exec_server from .. import protocol from ..server import server from ..server.utils import Utils -from .utils import LogCaptureTestCase, logSys, with_tmpdir, shutil, logging +from .utils import LogCaptureTestCase, with_tmpdir, shutil, logging +from ..helpers import getLogger + +# Gets the instance of the logger. +logSys = getLogger(__name__) STOCK_CONF_DIR = "config" -STOCK = os.path.exists(os.path.join(STOCK_CONF_DIR,'fail2ban.conf')) +STOCK = exists(pjoin(STOCK_CONF_DIR, 'fail2ban.conf')) CLIENT = "fail2ban-client" SERVER = "fail2ban-server" -BIN = os.path.dirname(Fail2banServer.getServerPath()) +BIN = dirname(Fail2banServer.getServerPath()) MAX_WAITTIME = 30 if not unittest.F2B.fast else 5 @@ -57,7 +62,7 @@ MAX_WAITTIME = 30 if not unittest.F2B.fast else 5 # Several wrappers and settings for proper testing: # -fail2bancmdline.MAX_WAITTIME = MAX_WAITTIME-1 +fail2bancmdline.MAX_WAITTIME = MAX_WAITTIME - 1 fail2bancmdline.logSys = \ fail2banclient.logSys = \ @@ -72,63 +77,68 @@ fail2banclient.output = \ fail2banserver.output = \ protocol.output = _test_output -def _test_exit(code=0): - logSys.debug("Exit with code %s", code) - if code == 0: - raise ExitException() - else: - raise FailExitException() -fail2bancmdline.exit = \ -fail2banclient.exit = \ -fail2banserver.exit = _test_exit + +# +# Mocking .exit so we could test its correct operation. +# Two custom exceptions will be assessed to be raised in the tests +# + +class ExitException(fail2bancmdline.ExitException): + """Exception upon a normal exit""" + pass + + +class FailExitException(fail2bancmdline.ExitException): + """Exception upon abnormal exit""" + pass + INTERACT = [] + + def _test_input_command(*args): if len(INTERACT): #logSys.debug('--- interact command: %r', INTERACT[0]) return INTERACT.pop(0) else: - return "exit" + return "exit" + fail2banclient.input_command = _test_input_command # prevents change logging params, log capturing, etc: fail2bancmdline.PRODUCTION = \ -fail2banclient.PRODUCTION = \ fail2banserver.PRODUCTION = False -class ExitException(fail2bancmdline.ExitException): - pass -class FailExitException(fail2bancmdline.ExitException): - pass - - -def _out_file(fn): # pragma: no cover +def _out_file(fn): + """Helper which outputs content of the file at HEAVYDEBUG loglevels""" logSys.debug('---- ' + fn + ' ----') for line in fileinput.input(fn): line = line.rstrip('\n') logSys.debug(line) logSys.debug('-'*30) + def _start_params(tmp, use_stock=False, logtarget="/dev/null"): - cfg = tmp+"/config" + cfg = pjoin(tmp, "config") if use_stock and STOCK: # copy config (sub-directories as alias): def ig_dirs(dir, files): - return [f for f in files if os.path.isdir(os.path.join(dir, f))] + """Filters list of 'files' to contain only directories (under dir)""" + return [f for f in files if isdir(pjoin(dir, f))] shutil.copytree(STOCK_CONF_DIR, cfg, ignore=ig_dirs) - os.symlink(STOCK_CONF_DIR+"/action.d", cfg+"/action.d") - os.symlink(STOCK_CONF_DIR+"/filter.d", cfg+"/filter.d") + os.symlink(pjoin(STOCK_CONF_DIR, "action.d"), pjoin(cfg, "action.d")) + os.symlink(pjoin(STOCK_CONF_DIR, "filter.d"), pjoin(cfg, "filter.d")) # replace fail2ban params (database with memory): r = re.compile(r'^dbfile\s*=') - for line in fileinput.input(cfg+"/fail2ban.conf", inplace=True): + for line in fileinput.input(pjoin(cfg, "fail2ban.conf"), inplace=True): line = line.rstrip('\n') if r.match(line): line = "dbfile = :memory:" print(line) # replace jail params (polling as backend to be fast in initialize): r = re.compile(r'^backend\s*=') - for line in fileinput.input(cfg+"/jail.conf", inplace=True): + for line in fileinput.input(pjoin(cfg, "jail.conf"), inplace=True): line = line.rstrip('\n') if r.match(line): line = "backend = polling" @@ -136,77 +146,83 @@ def _start_params(tmp, use_stock=False, logtarget="/dev/null"): else: # just empty config directory without anything (only fail2ban.conf/jail.conf): os.mkdir(cfg) - f = open(cfg+"/fail2ban.conf", "w") + f = open(pjoin(cfg, "fail2ban.conf"), "w") f.write('\n'.join(( "[Definition]", "loglevel = INFO", "logtarget = " + logtarget, "syslogsocket = auto", - "socket = "+tmp+"/f2b.sock", - "pidfile = "+tmp+"/f2b.pid", + "socket = " + pjoin(tmp, "f2b.sock"), + "pidfile = " + pjoin(tmp, "f2b.pid"), "backend = polling", "dbfile = :memory:", "dbpurgeage = 1d", "", ))) f.close() - f = open(cfg+"/jail.conf", "w") + f = open(pjoin(cfg, "jail.conf"), "w") f.write('\n'.join(( "[INCLUDES]", "", "[DEFAULT]", "", "", ))) f.close() - if logSys.level < logging.DEBUG: # if HEAVYDEBUG - _out_file(cfg+"/fail2ban.conf") - _out_file(cfg+"/jail.conf") + if logSys.level < logging.DEBUG: # if HEAVYDEBUG + _out_file(pjoin(cfg, "fail2ban.conf")) + _out_file(pjoin(cfg, "jail.conf")) # parameters (sock/pid and config, increase verbosity, set log, etc.): - return ("-c", cfg, "-s", tmp+"/f2b.sock", "-p", tmp+"/f2b.pid", - "-vv", "--logtarget", logtarget, "--loglevel", "DEBUG", "--syslogsocket", "auto", - "--timeout", str(fail2bancmdline.MAX_WAITTIME), + return ( + "-c", cfg, "-s", pjoin(tmp, "f2b.sock"), "-p", pjoin(tmp, "f2b.pid"), + "-vv", "--logtarget", logtarget, "--loglevel", "DEBUG", "--syslogsocket", "auto", + "--timeout", str(fail2bancmdline.MAX_WAITTIME), ) -def _kill_srv(pidfile): # pragma: no cover - def _pid_exists(pid): - try: - os.kill(pid, 0) - return True - except OSError: - return False - logSys.debug("-- cleanup: %r", (pidfile, os.path.isdir(pidfile))) - if os.path.isdir(pidfile): + +def _kill_srv(pidfile): + logSys.debug("cleanup: %r", (pidfile, isdir(pidfile))) + if isdir(pidfile): piddir = pidfile - pidfile = piddir + "/f2b.pid" - if not os.path.isfile(pidfile): - pidfile = piddir + "/fail2ban.pid" - if not os.path.isfile(pidfile): - logSys.debug("--- cleanup: no pidfile for %r", piddir) + pidfile = pjoin(piddir, "f2b.pid") + if not isfile(pidfile): # pragma: no cover + pidfile = pjoin(piddir, "fail2ban.pid") + + if not isfile(pidfile): + logSys.debug("cleanup: no pidfile for %r", piddir) return True + f = pid = None try: - logSys.debug("--- cleanup pidfile: %r", pidfile) + logSys.debug("cleanup pidfile: %r", pidfile) f = open(pidfile) - pid = f.read().split()[1] + pid = f.read() + pid = re.match(r'\S+', pid).group() pid = int(pid) - logSys.debug("--- cleanup pid: %r", pid) - if pid <= 0: - raise ValueError('pid %s of %s is invalid' % (pid, pidfile)) - if not _pid_exists(pid): - return True - ## try to preper stop (have signal handler): - os.kill(pid, signal.SIGTERM) - ## check still exists after small timeout: - if not Utils.wait_for(lambda: not _pid_exists(pid), 1): - ## try to kill hereafter: - os.kill(pid, signal.SIGKILL) - return not _pid_exists(pid) - except Exception as e: + except Exception as e: # pragma: no cover logSys.debug(e) + return False finally: if f is not None: f.close() + + try: + logSys.debug("cleanup pid: %r", pid) + if pid <= 0 or pid == os.getpid(): # pragma: no cover + raise ValueError('pid %s of %s is invalid' % (pid, pidfile)) + if not Utils.pid_exists(pid): + return True + ## try to properly stop (have signal handler): + os.kill(pid, signal.SIGTERM) + ## check still exists after small timeout: + if not Utils.wait_for(lambda: not Utils.pid_exists(pid), 1): + ## try to kill hereafter: + os.kill(pid, signal.SIGKILL) + logSys.debug("cleanup: kill ready") + return not Utils.pid_exists(pid) + except Exception as e: # pragma: no cover + logSys.exception(e) return True + def with_kill_srv(f): """Helper to decorate tests which receive in the last argument tmpdir to pass to kill_srv @@ -224,187 +240,83 @@ def with_kill_srv(f): class Fail2banClientServerBase(LogCaptureTestCase): + _orig_exit = Fail2banCmdLine._exit + def setUp(self): """Call before every test case.""" LogCaptureTestCase.setUp(self) + Fail2banCmdLine._exit = staticmethod(self._test_exit) def tearDown(self): """Call after every test case.""" + Fail2banCmdLine._exit = self._orig_exit LogCaptureTestCase.tearDown(self) + @staticmethod + def _test_exit(code=0): + if code == 0: + raise ExitException() + else: + raise FailExitException() + def _wait_for_srv(self, tmp, ready=True, startparams=None): try: - sock = tmp+"/f2b.sock" + sock = pjoin(tmp, "f2b.sock") # wait for server (socket): - ret = Utils.wait_for(lambda: os.path.exists(sock), MAX_WAITTIME) + ret = Utils.wait_for(lambda: exists(sock), MAX_WAITTIME) if not ret: - raise Exception('Unexpected: Socket file does not exists.\nStart failed: %r' % (startparams,)) + raise Exception( + 'Unexpected: Socket file does not exists.\nStart failed: %r' + % (startparams,) + ) if ready: # wait for communication with worker ready: ret = Utils.wait_for(lambda: "Server ready" in self.getLog(), MAX_WAITTIME) if not ret: - raise Exception('Unexpected: Server ready was not found.\nStart failed: %r' % (startparams,)) - except: # pragma: no cover - log = tmp+"/f2b.log" - if os.path.isfile(log): + raise Exception( + 'Unexpected: Server ready was not found.\nStart failed: %r' + % (startparams,) + ) + except: # pragma: no cover + log = pjoin(tmp, "f2b.log") + if isfile(log): _out_file(log) else: logSys.debug("No log file %s to examine details of error", log) raise + def execSuccess(self, startparams, *args): + raise NotImplementedError("To be defined in subclass") -class Fail2banClientTest(Fail2banClientServerBase): + def execFailed(self, startparams, *args): + raise NotImplementedError("To be defined in subclass") - def testConsistency(self): - self.assertTrue(os.path.isfile(os.path.join(os.path.join(BIN), CLIENT))) - self.assertTrue(os.path.isfile(os.path.join(os.path.join(BIN), SERVER))) - - def testClientUsage(self): - self.assertRaises(ExitException, _exec_client, - (CLIENT, "-h",)) - self.assertLogged("Usage: " + CLIENT) - self.assertLogged("Report bugs to ") - self.pruneLog() - self.assertRaises(ExitException, _exec_client, - (CLIENT, "-vq", "-V",)) - self.assertLogged("Fail2Ban v" + fail2bancmdline.version) - - @with_tmpdir - def testClientDump(self, tmp): - # use here the stock configuration (if possible) - startparams = _start_params(tmp, True) - self.assertRaises(ExitException, _exec_client, - ((CLIENT,) + startparams + ("-vvd",))) - self.assertLogged("Loading files") - self.assertLogged("logtarget") - - @with_tmpdir - @with_kill_srv - def testClientStartBackgroundInside(self, tmp): - # use once the stock configuration (to test starting also) - startparams = _start_params(tmp, True) - # start: - self.assertRaises(ExitException, _exec_client, - (CLIENT, "-b") + startparams + ("start",)) - # wait for server (socket and ready): - self._wait_for_srv(tmp, True, startparams=startparams) - self.assertLogged("Server ready") - self.assertLogged("Exit with code 0") - try: - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("echo", "TEST-ECHO",)) - self.assertRaises(FailExitException, _exec_client, - (CLIENT,) + startparams + ("~~unknown~cmd~failed~~",)) - self.pruneLog() - # start again (should fail): - self.assertRaises(FailExitException, _exec_client, - (CLIENT, "-b") + startparams + ("start",)) - self.assertLogged("Server already running") - finally: - self.pruneLog() - # stop: - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("stop",)) - self.assertLogged("Shutdown successful") - self.assertLogged("Exit with code 0") - - self.pruneLog() - # stop again (should fail): - self.assertRaises(FailExitException, _exec_client, - (CLIENT,) + startparams + ("stop",)) - self.assertLogged("Failed to access socket path") - self.assertLogged("Is fail2ban running?") - - @with_tmpdir - @with_kill_srv - def testClientStartBackgroundCall(self, tmp): - global INTERACT - startparams = _start_params(tmp, logtarget=tmp+"/f2b.log") - # start (in new process, using the same python version): - cmd = (sys.executable, os.path.join(os.path.join(BIN), CLIENT)) - logSys.debug('Start %s ...', cmd) - cmd = cmd + startparams + ("--async", "start",) - ret = Utils.executeCmd(cmd, timeout=MAX_WAITTIME, shell=False, output=True) - self.assertTrue(len(ret) and ret[0]) - # wait for server (socket and ready): - self._wait_for_srv(tmp, True, startparams=cmd) - self.assertLogged("Server ready") - self.pruneLog() - try: - # echo from client (inside): - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("echo", "TEST-ECHO",)) - self.assertLogged("TEST-ECHO") - self.assertLogged("Exit with code 0") - self.pruneLog() - # interactive client chat with started server: - INTERACT += [ - "echo INTERACT-ECHO", - "status", - "exit" - ] - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("-i",)) - self.assertLogged("INTERACT-ECHO") - self.assertLogged("Status", "Number of jail:") - self.assertLogged("Exit with code 0") - self.pruneLog() - # test reload and restart over interactive client: - INTERACT += [ - "reload", - "restart", - "exit" - ] - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("-i",)) - self.assertLogged("Reading config files:") - self.assertLogged("Shutdown successful") - self.assertLogged("Server ready") - self.assertLogged("Exit with code 0") - self.pruneLog() - # test reload missing jail (interactive): - INTERACT += [ - "reload ~~unknown~jail~fail~~", - "exit" - ] - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("-i",)) - self.assertLogged("Failed during configuration: No section: '~~unknown~jail~fail~~'") - self.pruneLog() - # test reload missing jail (direct): - self.assertRaises(FailExitException, _exec_client, - (CLIENT,) + startparams + ("reload", "~~unknown~jail~fail~~")) - self.assertLogged("Failed during configuration: No section: '~~unknown~jail~fail~~'") - self.assertLogged("Exit with code -1") - self.pruneLog() - finally: - self.pruneLog() - # stop: - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("stop",)) - self.assertLogged("Shutdown successful") - self.assertLogged("Exit with code 0") - - def _testClientStartForeground(self, tmp, startparams, phase): + # + # Common tests + # + def _testStartForeground(self, tmp, startparams, phase): # start and wait to end (foreground): - logSys.debug("-- start of test worker") + logSys.debug("start of test worker") phase['start'] = True - self.assertRaises(fail2bancmdline.ExitException, _exec_client, - (CLIENT, "-f") + startparams + ("start",)) + self.execSuccess(("-f",) + startparams, "start") # end : phase['end'] = True - logSys.debug("-- end of test worker") + logSys.debug("end of test worker") @with_tmpdir - def testClientStartForeground(self, tmp): + def testStartForeground(self, tmp): + # intended to be ran only in subclasses th = None + phase = dict() try: # started directly here, so prevent overwrite test cases logger with "INHERITED" startparams = _start_params(tmp, logtarget="INHERITED") # because foreground block execution - start it in thread: - phase = dict() - th = Thread(name="_TestCaseWorker", - target=Fail2banClientTest._testClientStartForeground, args=(self, tmp, startparams, phase)) + th = Thread( + name="_TestCaseWorker", + target=self._testStartForeground, + args=(tmp, startparams, phase) + ) th.daemon = True th.start() try: @@ -415,25 +327,149 @@ class Fail2banClientTest(Fail2banClientServerBase): self._wait_for_srv(tmp, True, startparams=startparams) self.pruneLog() # several commands to server: - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("ping",)) - self.assertRaises(FailExitException, _exec_client, - (CLIENT,) + startparams + ("~~unknown~cmd~failed~~",)) - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("echo", "TEST-ECHO",)) + self.execSuccess(startparams, "ping") + self.execFailed(startparams, "~~unknown~cmd~failed~~") + self.execSuccess(startparams, "echo", "TEST-ECHO") finally: self.pruneLog() # stop: - self.assertRaises(ExitException, _exec_client, - (CLIENT,) + startparams + ("stop",)) + self.execSuccess(startparams, "stop") # wait for end: Utils.wait_for(lambda: phase.get('end', None) is not None, MAX_WAITTIME) self.assertTrue(phase.get('end', None)) self.assertLogged("Shutdown successful", "Exiting Fail2ban") finally: - _kill_srv(tmp) if th: - th.join() + # we start client/server directly in current process (new thread), + # so don't kill (same process) - if success, just wait for end of worker: + if phase.get('end', None): + th.join() + + +class Fail2banClientTest(Fail2banClientServerBase): + + def execSuccess(self, startparams, *args): + self.assertRaises(ExitException, _exec_client, + ((CLIENT,) + startparams + args)) + + def execFailed(self, startparams, *args): + self.assertRaises(FailExitException, _exec_client, + ((CLIENT,) + startparams + args)) + + def testConsistency(self): + self.assertTrue(isfile(pjoin(BIN, CLIENT))) + self.assertTrue(isfile(pjoin(BIN, SERVER))) + + def testClientUsage(self): + self.execSuccess((), "-h") + self.assertLogged("Usage: " + CLIENT) + self.assertLogged("Report bugs to ") + self.pruneLog() + self.execSuccess((), "-vq", "-V") + self.assertLogged("Fail2Ban v" + fail2bancmdline.version) + + @with_tmpdir + def testClientDump(self, tmp): + # use here the stock configuration (if possible) + startparams = _start_params(tmp, True) + self.execSuccess(startparams, "-vvd") + self.assertLogged("Loading files") + self.assertLogged("logtarget") + + @with_tmpdir + @with_kill_srv + def testClientStartBackgroundInside(self, tmp): + # use once the stock configuration (to test starting also) + startparams = _start_params(tmp, True) + # start: + self.execSuccess(("-b",) + startparams, "start") + # wait for server (socket and ready): + self._wait_for_srv(tmp, True, startparams=startparams) + self.assertLogged("Server ready") + self.assertLogged("Exit with code 0") + try: + self.execSuccess(startparams, "echo", "TEST-ECHO") + self.execFailed(startparams, "~~unknown~cmd~failed~~") + self.pruneLog() + # start again (should fail): + self.execFailed(("-b",) + startparams, "start") + self.assertLogged("Server already running") + finally: + self.pruneLog() + # stop: + self.execSuccess(startparams, "stop") + self.assertLogged("Shutdown successful") + self.assertLogged("Exit with code 0") + + self.pruneLog() + # stop again (should fail): + self.execFailed(startparams, "stop") + self.assertLogged("Failed to access socket path") + self.assertLogged("Is fail2ban running?") + + @with_tmpdir + @with_kill_srv + def testClientStartBackgroundCall(self, tmp): + global INTERACT + startparams = _start_params(tmp, logtarget=pjoin(tmp, "f2b.log")) + # start (in new process, using the same python version): + cmd = (sys.executable, pjoin(BIN, CLIENT)) + logSys.debug('Start %s ...', cmd) + cmd = cmd + startparams + ("--async", "start",) + ret = Utils.executeCmd(cmd, timeout=MAX_WAITTIME, shell=False, output=True) + self.assertTrue(len(ret) and ret[0]) + # wait for server (socket and ready): + self._wait_for_srv(tmp, True, startparams=cmd) + self.assertLogged("Server ready") + self.pruneLog() + try: + # echo from client (inside): + self.execSuccess(startparams, "echo", "TEST-ECHO") + self.assertLogged("TEST-ECHO") + self.assertLogged("Exit with code 0") + self.pruneLog() + # interactive client chat with started server: + INTERACT += [ + "echo INTERACT-ECHO", + "status", + "exit" + ] + self.execSuccess(startparams, "-i") + self.assertLogged("INTERACT-ECHO") + self.assertLogged("Status", "Number of jail:") + self.assertLogged("Exit with code 0") + self.pruneLog() + # test reload and restart over interactive client: + INTERACT += [ + "reload", + "restart", + "exit" + ] + self.execSuccess(startparams, "-i") + self.assertLogged("Reading config files:") + self.assertLogged("Shutdown successful") + self.assertLogged("Server ready") + self.assertLogged("Exit with code 0") + self.pruneLog() + # test reload missing jail (interactive): + INTERACT += [ + "reload ~~unknown~jail~fail~~", + "exit" + ] + self.execSuccess(startparams, "-i") + self.assertLogged("Failed during configuration: No section: '~~unknown~jail~fail~~'") + self.pruneLog() + # test reload missing jail (direct): + self.execFailed(startparams, "reload", "~~unknown~jail~fail~~") + self.assertLogged("Failed during configuration: No section: '~~unknown~jail~fail~~'") + self.assertLogged("Exit with code -1") + self.pruneLog() + finally: + self.pruneLog() + # stop: + self.execSuccess(startparams, "stop") + self.assertLogged("Shutdown successful") + self.assertLogged("Exit with code 0") @with_tmpdir @with_kill_srv @@ -442,34 +478,33 @@ class Fail2banClientTest(Fail2banClientServerBase): startparams = _start_params(tmp, logtarget="INHERITED") ## wrong config directory - self.assertRaises(FailExitException, _exec_client, - (CLIENT, "--async", "-c", tmp+"/miss", "start",)) - self.assertLogged("Base configuration directory " + tmp+"/miss" + " does not exist") + self.execFailed((), + "--async", "-c", pjoin(tmp, "miss"), "start") + self.assertLogged("Base configuration directory " + pjoin(tmp, "miss") + " does not exist") self.pruneLog() ## wrong socket - self.assertRaises(FailExitException, _exec_client, - (CLIENT, "--async", "-c", tmp+"/config", "-s", tmp+"/miss/f2b.sock", "start",)) - self.assertLogged("There is no directory " + tmp+"/miss" + " to contain the socket file") + self.execFailed((), + "--async", "-c", pjoin(tmp, "config"), "-s", pjoin(tmp, "miss/f2b.sock"), "start") + self.assertLogged("There is no directory " + pjoin(tmp, "miss") + " to contain the socket file") self.pruneLog() ## not running - self.assertRaises(FailExitException, _exec_client, - (CLIENT, "-c", tmp+"/config", "-s", tmp+"/f2b.sock", "reload",)) + self.execFailed((), + "-c", pjoin(tmp, "config"), "-s", pjoin(tmp, "f2b.sock"), "reload") self.assertLogged("Could not find server") self.pruneLog() ## already exists: - open(tmp+"/f2b.sock", 'a').close() - self.assertRaises(FailExitException, _exec_client, - (CLIENT, "--async", "-c", tmp+"/config", "-s", tmp+"/f2b.sock", "start",)) + open(pjoin(tmp, "f2b.sock"), 'a').close() + self.execFailed((), + "--async", "-c", pjoin(tmp, "config"), "-s", pjoin(tmp, "f2b.sock"), "start") self.assertLogged("Fail2ban seems to be in unexpected state (not running but the socket exists)") self.pruneLog() - os.remove(tmp+"/f2b.sock") + os.remove(pjoin(tmp, "f2b.sock")) ## wrong option: - self.assertRaises(FailExitException, _exec_client, - (CLIENT, "-s",)) + self.execFailed((), "-s") self.assertLogged("Usage: ") self.pruneLog() @@ -487,9 +522,16 @@ class Fail2banClientTest(Fail2banClientServerBase): class Fail2banServerTest(Fail2banClientServerBase): + def execSuccess(self, startparams, *args): + self.assertRaises(ExitException, _exec_server, + ((SERVER,) + startparams + args)) + + def execFailed(self, startparams, *args): + self.assertRaises(FailExitException, _exec_server, + ((SERVER,) + startparams + args)) + def testServerUsage(self): - self.assertRaises(ExitException, _exec_server, - (SERVER, "-h",)) + self.execSuccess((), "-h") self.assertLogged("Usage: " + SERVER) self.assertLogged("Report bugs to ") @@ -497,9 +539,9 @@ class Fail2banServerTest(Fail2banClientServerBase): @with_kill_srv def testServerStartBackground(self, tmp): # to prevent fork of test-cases process, start server in background via command: - startparams = _start_params(tmp, logtarget=tmp+"/f2b.log") + startparams = _start_params(tmp, logtarget=pjoin(tmp, "f2b.log")) # start (in new process, using the same python version): - cmd = (sys.executable, os.path.join(os.path.join(BIN), SERVER)) + cmd = (sys.executable, pjoin(BIN, SERVER)) logSys.debug('Start %s ...', cmd) cmd = cmd + startparams + ("-b",) ret = Utils.executeCmd(cmd, timeout=MAX_WAITTIME, shell=False, output=True) @@ -509,68 +551,15 @@ class Fail2banServerTest(Fail2banClientServerBase): self.assertLogged("Server ready") self.pruneLog() try: - self.assertRaises(ExitException, _exec_server, - (SERVER,) + startparams + ("echo", "TEST-ECHO",)) - self.assertRaises(FailExitException, _exec_server, - (SERVER,) + startparams + ("~~unknown~cmd~failed~~",)) + self.execSuccess(startparams, "echo", "TEST-ECHO") + self.execFailed(startparams, "~~unknown~cmd~failed~~") finally: self.pruneLog() # stop: - self.assertRaises(ExitException, _exec_server, - (SERVER,) + startparams + ("stop",)) + self.execSuccess(startparams, "stop") self.assertLogged("Shutdown successful") self.assertLogged("Exit with code 0") - def _testServerStartForeground(self, tmp, startparams, phase): - # start and wait to end (foreground): - logSys.debug("-- start of test worker") - phase['start'] = True - self.assertRaises(fail2bancmdline.ExitException, _exec_server, - (SERVER, "-f") + startparams + ("start",)) - # end : - phase['end'] = True - logSys.debug("-- end of test worker") - - @with_tmpdir - def testServerStartForeground(self, tmp): - th = None - try: - # started directly here, so prevent overwrite test cases logger with "INHERITED" - startparams = _start_params(tmp, logtarget="INHERITED") - # because foreground block execution - start it in thread: - phase = dict() - th = Thread(name="_TestCaseWorker", - target=Fail2banServerTest._testServerStartForeground, args=(self, tmp, startparams, phase)) - th.daemon = True - th.start() - try: - # wait for start thread: - Utils.wait_for(lambda: phase.get('start', None) is not None, MAX_WAITTIME) - self.assertTrue(phase.get('start', None)) - # wait for server (socket and ready): - self._wait_for_srv(tmp, True, startparams=startparams) - self.pruneLog() - # several commands to server: - self.assertRaises(ExitException, _exec_server, - (SERVER,) + startparams + ("ping",)) - self.assertRaises(FailExitException, _exec_server, - (SERVER,) + startparams + ("~~unknown~cmd~failed~~",)) - self.assertRaises(ExitException, _exec_server, - (SERVER,) + startparams + ("echo", "TEST-ECHO",)) - finally: - self.pruneLog() - # stop: - self.assertRaises(ExitException, _exec_server, - (SERVER,) + startparams + ("stop",)) - # wait for end: - Utils.wait_for(lambda: phase.get('end', None) is not None, MAX_WAITTIME) - self.assertTrue(phase.get('end', None)) - self.assertLogged("Shutdown successful", "Exiting Fail2ban") - finally: - _kill_srv(tmp) - if th: - th.join() - @with_tmpdir @with_kill_srv def testServerFailStart(self, tmp): @@ -578,21 +567,48 @@ class Fail2banServerTest(Fail2banClientServerBase): startparams = _start_params(tmp, logtarget="INHERITED") ## wrong config directory - self.assertRaises(FailExitException, _exec_server, - (SERVER, "-c", tmp+"/miss",)) - self.assertLogged("Base configuration directory " + tmp+"/miss" + " does not exist") + self.execFailed((), + "-c", pjoin(tmp, "miss")) + self.assertLogged("Base configuration directory " + pjoin(tmp, "miss") + " does not exist") self.pruneLog() ## wrong socket - self.assertRaises(FailExitException, _exec_server, - (SERVER, "-c", tmp+"/config", "-x", "-s", tmp+"/miss/f2b.sock",)) - self.assertLogged("There is no directory " + tmp+"/miss" + " to contain the socket file") + self.execFailed((), + "-c", pjoin(tmp, "config"), "-x", "-s", pjoin(tmp, "miss/f2b.sock")) + self.assertLogged("There is no directory " + pjoin(tmp, "miss") + " to contain the socket file") self.pruneLog() ## already exists: - open(tmp+"/f2b.sock", 'a').close() - self.assertRaises(FailExitException, _exec_server, - (SERVER, "-c", tmp+"/config", "-s", tmp+"/f2b.sock",)) + open(pjoin(tmp, "f2b.sock"), 'a').close() + self.execFailed((), + "-c", pjoin(tmp, "config"), "-s", pjoin(tmp, "f2b.sock")) self.assertLogged("Fail2ban seems to be in unexpected state (not running but the socket exists)") self.pruneLog() - os.remove(tmp+"/f2b.sock") + os.remove(pjoin(tmp, "f2b.sock")) + + @with_tmpdir + def testKillAfterStart(self, tmp): + try: + # to prevent fork of test-cases process, start server in background via command: + startparams = _start_params(tmp, logtarget=pjoin(tmp, "f2b.log")) + # start (in new process, using the same python version): + cmd = (sys.executable, pjoin(BIN, SERVER)) + logSys.debug('Start %s ...', cmd) + cmd = cmd + startparams + ("-b",) + ret = Utils.executeCmd(cmd, timeout=MAX_WAITTIME, shell=False, output=True) + self.assertTrue(len(ret) and ret[0]) + # wait for server (socket and ready): + self._wait_for_srv(tmp, True, startparams=cmd) + self.assertLogged("Server ready") + self.pruneLog() + logSys.debug('Kill server ... %s', tmp) + finally: + self.assertTrue(_kill_srv(tmp)) + # wait for end (kill was successful): + Utils.wait_for(lambda: not isfile(pjoin(tmp, "f2b.pid")), MAX_WAITTIME) + self.assertFalse(isfile(pjoin(tmp, "f2b.pid"))) + self.assertLogged("cleanup: kill ready") + self.pruneLog() + # again: + self.assertTrue(_kill_srv(tmp)) + self.assertLogged("cleanup: no pidfile for") diff --git a/fail2ban/tests/files/logs/asterisk b/fail2ban/tests/files/logs/asterisk index aa32a290..d17d93a1 100644 --- a/fail2ban/tests/files/logs/asterisk +++ b/fail2ban/tests/files/logs/asterisk @@ -67,3 +67,18 @@ Nov 4 18:30:40 localhost asterisk[32229]: NOTICE[32257]: chan_sip.c:23417 in han [2016-01-28 10:34:31] NOTICE[3477][C-000003c3] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '0+441772285407' rejected because extension not found in context 'default'. # failJSON: { "time": "2016-01-28T10:34:33", "match": true , "host": "1.2.3.4" } [2016-01-28 10:34:33] NOTICE[3477][C-000003c3] chan_sip.c: Call from '' (1.2.3.4:10836) to extension '' rejected because extension not found in context 'my-context'. + +# Failed authentication with pjsip on Asterisk 13+ +# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" } +[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" ' failed for '1.2.3.4:48336' (callid: 276666022) - No matching endpoint found +# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" } +[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" ' failed for '1.2.3.4:48336' (callid: 276666022) - Not match Endpoint ACL +# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" } +[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" ' failed for '1.2.3.4:48336' (callid: 276666022) - Not match Endpoint Contact ACL +# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" } +[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" ' failed for '1.2.3.4:48336' (callid: 276666022) - Failed to authenticate +# failJSON: { "time": "2016-05-23T10:18:16", "match": true , "host": "1.2.3.4" } +[2016-05-23 10:18:16] NOTICE[19388] res_pjsip/pjsip_distributor.c: Request from '"1000" ' failed for '1.2.3.4:48336' (callid: 276666022) - Error to authenticate +# Failed authentication with pjsip on Asterisk 13+ +# failJSON: { "time": "2016-06-08T23:40:26", "match": true , "host": "2.3.4.5" } +[2016-06-08 23:40:26] NOTICE[32497] res_pjsip/pjsip_distributor.c: Request from '"317" ' failed for '2.3.4.5:5089' (callid: 206f178f-896564cb-57573f49@1.2.3.4) - No matching endpoint found diff --git a/fail2ban/tests/files/logs/exim b/fail2ban/tests/files/logs/exim index a3b287d4..9053bf8d 100644 --- a/fail2ban/tests/files/logs/exim +++ b/fail2ban/tests/files/logs/exim @@ -48,10 +48,14 @@ 2016-03-18 00:34:06 [7513] SMTP protocol error in "AUTH LOGIN" H=(ylmf-pc) [45.32.34.167]:60723 I=[172.89.0.6]:587 AUTH command used when not advertised # failJSON: { "time": "2016-03-19T18:40:44", "match": true , "host": "92.45.204.170" } 2016-03-19 18:40:44 [26221] SMTP protocol error in "AUTH LOGIN aW5mb0BtYW5iYXQub3Jn" H=([127.0.0.1]) [92.45.204.170]:14243 I=[172.89.0.6]:587 AUTH command used when not advertised +# failJSON: { "time": "2016-05-17T06:25:27", "match": true , "host": "69.10.61.61", "desc": "from gh-1430" } +2016-05-17 06:25:27 SMTP protocol error in "AUTH LOGIN" H=(ylmf-pc) [69.10.61.61] AUTH command used when not advertised # failJSON: { "time": "2016-03-21T06:38:05", "match": true , "host": "49.212.207.15" } 2016-03-21 06:38:05 [5718] no MAIL in SMTP connection from www3005.sakura.ne.jp [49.212.207.15]:28890 I=[172.89.0.6]:25 D=21s C=EHLO,STARTTLS # failJSON: { "time": "2016-03-21T06:57:36", "match": true , "host": "122.165.71.116" } 2016-03-21 06:57:36 [5908] no MAIL in SMTP connection from [122.165.71.116]:2056 I=[172.89.0.6]:25 D=10s +# failJSON: { "time": "2016-03-21T06:57:36", "match": true , "host": "122.165.71.116" } +2016-03-21 06:57:36 [5908] no MAIL in SMTP connection from [122.165.71.116] I=[172.89.0.6]:25 D=10s # failJSON: { "time": "2016-03-21T04:07:49", "match": true , "host": "174.137.147.204" } 2016-03-21 04:07:49 [25874] 1ahr79-0006jK-G9 SMTP connection from (voyeur.webair.com) [174.137.147.204]:44884 I=[172.89.0.6]:25 closed by DROP in ACL # failJSON: { "time": "2016-03-21T04:33:13", "match": true , "host": "206.214.71.53" } diff --git a/fail2ban/tests/servertestcase.py b/fail2ban/tests/servertestcase.py index 508b4ba7..af3a2908 100644 --- a/fail2ban/tests/servertestcase.py +++ b/fail2ban/tests/servertestcase.py @@ -159,6 +159,11 @@ class Transmitter(TransmitterBase): self.server = TestServer() super(Transmitter, self).setUp() + def testServerIsNotStarted(self): + # so far isStarted only tested but not used otherwise + # and here we don't really .start server + self.assertFalse(self.server.isStarted()) + def testStopServer(self): self.assertEqual(self.transm.proceed(["stop"]), (0, None)) @@ -1014,6 +1019,7 @@ class LoggingTests(LogCaptureTestCase): server = TestServer() try: server.start(sock_name, pidfile_name, force=False) + self.assertFalse(server.isStarted()) self.assertLogged("Server already running") finally: server.quit() diff --git a/fail2ban/tests/utils.py b/fail2ban/tests/utils.py index 7e5f4a29..3d5f4c7d 100644 --- a/fail2ban/tests/utils.py +++ b/fail2ban/tests/utils.py @@ -54,10 +54,9 @@ if not CONFIG_DIR: else: CONFIG_DIR = '/etc/fail2ban' -# In not installed env (setup, test-cases) use fail2ban modules from main directory: -if 1 or os.environ.get('PYTHONPATH', None) is None: - os.putenv('PYTHONPATH', os.path.dirname(os.path.dirname(os.path.dirname( - os.path.abspath(__file__))))) +# During the test cases (or setup) use fail2ban modules from main directory: +os.putenv('PYTHONPATH', os.path.dirname(os.path.dirname(os.path.dirname( + os.path.abspath(__file__))))) class F2B(optparse.Values): def __init__(self, opts={}): diff --git a/man/fail2ban-client.1 b/man/fail2ban-client.1 index ec5db2d2..0da5fb02 100644 --- a/man/fail2ban-client.1 +++ b/man/fail2ban-client.1 @@ -1,12 +1,12 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. -.TH FAIL2BAN-CLIENT "1" "March 2016" "fail2ban-client v0.9.4" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1. +.TH FAIL2BAN-CLIENT "1" "July 2016" "fail2ban-client v0.10.0a1" "User Commands" .SH NAME fail2ban-client \- configure and control the server .SH SYNOPSIS .B fail2ban-client -[\fI\,OPTIONS\/\fR] \fI\,\/\fR +[\fIOPTIONS\fR] \fI\fR .SH DESCRIPTION -Fail2Ban v0.9.4 reads log file that contains password failure report +Fail2Ban v0.10.0a1 reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. .SH OPTIONS .TP @@ -19,6 +19,13 @@ socket path \fB\-p\fR pidfile path .TP +\fB\-\-loglevel\fR +logging level +.HP +\fB\-\-logtarget\fR |STDOUT|STDERR|SYSLOG +.HP +\fB\-\-syslogsocket\fR auto| +.TP \fB\-d\fR dump configuration. For debugging .TP @@ -38,7 +45,13 @@ force execution of the server (remove socket file) start server in background (default) .TP \fB\-f\fR -start server in foreground (note that the client forks once itself) +start server in foreground +.TP +\fB\-\-async\fR +start server in async mode (for internal usage only, don't read configuration) +.TP +\fB\-\-timeout\fR +timeout to wait for the server (for internal usage only, don't read configuration) .TP \fB\-h\fR, \fB\-\-help\fR display this help message @@ -52,8 +65,12 @@ BASIC \fBstart\fR starts the server and the jails .TP +\fBrestart\fR +restarts the server +.TP \fBreload\fR -reloads the configuration +reloads the configuration without +restart .TP \fBreload \fR reloads the jail @@ -69,6 +86,10 @@ server \fBping\fR tests if the server is alive .TP +\fBecho\fR +for internal usage, returns back +and outputs a given string +.TP \fBhelp\fR return this output .TP @@ -227,8 +248,9 @@ for \fBset maxlines \fR sets the number of to buffer for regex search for -.TP -\fBset addaction [ ]\fR +.IP +set addaction [ ] +.IP adds a new action named for . Optionally for a Python based action, a and @@ -240,38 +262,45 @@ removes the action from .IP COMMAND ACTION CONFIGURATION -.TP -\fBset action actionstart \fR +.IP +set action actionstart +.IP sets the start command of the action for -.TP -\fBset action actionstop sets the stop command of the\fR +.IP +set action actionstop sets the stop command of the +.IP action for -.TP -\fBset action actioncheck \fR +.IP +set action actioncheck +.IP sets the check command of the action for .TP \fBset action actionban \fR sets the ban command of the action for -.TP -\fBset action actionunban \fR +.IP +set action actionunban +.IP sets the unban command of the action for -.TP -\fBset action timeout \fR +.IP +set action timeout +.IP sets as the command timeout in seconds for the action for .IP GENERAL ACTION CONFIGURATION -.TP -\fBset action \fR +.IP +set action +.IP sets the of for the action for -.TP -\fBset action [ ]\fR +.IP +set action [ ] +.IP calls the with for the action for @@ -376,9 +405,6 @@ gets the value of for the action for .SH FILES \fI/etc/fail2ban/*\fR -.SH AUTHOR -Written by Cyril Jaquier . -Many contributions by Yaroslav O. Halchenko . .SH "REPORTING BUGS" Report bugs to https://github.com/fail2ban/fail2ban/issues .SH COPYRIGHT diff --git a/man/fail2ban-regex.1 b/man/fail2ban-regex.1 index 740b461c..f954cb3a 100644 --- a/man/fail2ban-regex.1 +++ b/man/fail2ban-regex.1 @@ -1,10 +1,10 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. -.TH FAIL2BAN-REGEX "1" "March 2016" "fail2ban-regex 0.9.4" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1. +.TH FAIL2BAN-REGEX "1" "July 2016" "fail2ban-regex 0.10.0a1" "User Commands" .SH NAME fail2ban-regex \- test Fail2ban "failregex" option .SH SYNOPSIS .B fail2ban-regex -[\fI\,OPTIONS\/\fR] \fI\, \/\fR[\fI\,IGNOREREGEX\/\fR] +[\fIOPTIONS\fR] \fI \fR[\fIIGNOREREGEX\fR] .SH DESCRIPTION Fail2Ban reads log file that contains password failure report and bans the corresponding IP addresses using firewall rules. @@ -16,7 +16,7 @@ string a string representing a log line .TP filename -path to a log file (\fI\,/var/log/auth.log\/\fP) +path to a log file (\fI/var/log/auth.log\fP) .TP "systemd\-journal" search systemd journal (systemd\-python required) @@ -42,20 +42,23 @@ show program's version number and exit \fB\-h\fR, \fB\-\-help\fR show this help message and exit .TP -\fB\-d\fR DATEPATTERN, \fB\-\-datepattern\fR=\fI\,DATEPATTERN\/\fR +\fB\-d\fR DATEPATTERN, \fB\-\-datepattern\fR=\fIDATEPATTERN\fR set custom pattern used to match date/times .TP -\fB\-e\fR ENCODING, \fB\-\-encoding\fR=\fI\,ENCODING\/\fR +\fB\-e\fR ENCODING, \fB\-\-encoding\fR=\fIENCODING\fR File encoding. Default: system locale .TP -\fB\-L\fR MAXLINES, \fB\-\-maxlines\fR=\fI\,MAXLINES\/\fR +\fB\-r\fR, \fB\-\-raw\fR +Raw hosts, don't resolve dns +.TP +\fB\-L\fR MAXLINES, \fB\-\-maxlines\fR=\fIMAXLINES\fR maxlines for multi\-line regex .TP -\fB\-m\fR JOURNALMATCH, \fB\-\-journalmatch\fR=\fI\,JOURNALMATCH\/\fR +\fB\-m\fR JOURNALMATCH, \fB\-\-journalmatch\fR=\fIJOURNALMATCH\fR journalctl style matches overriding filter file. "systemd\-journal" only .TP -\fB\-l\fR LOG_LEVEL, \fB\-\-log\-level\fR=\fI\,LOG_LEVEL\/\fR +\fB\-l\fR LOG_LEVEL, \fB\-\-log\-level\fR=\fILOG_LEVEL\fR Log level for the Fail2Ban logger to use .TP \fB\-v\fR, \fB\-\-verbose\fR diff --git a/man/fail2ban-server.1 b/man/fail2ban-server.1 index 90ec1d80..3b970024 100644 --- a/man/fail2ban-server.1 +++ b/man/fail2ban-server.1 @@ -1,24 +1,17 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. -.TH FAIL2BAN-SERVER "1" "March 2016" "fail2ban-server v0.9.4" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1. +.TH FAIL2BAN-SERVER "1" "July 2016" "fail2ban-server v0.10.0a1" "User Commands" .SH NAME fail2ban-server \- start the server .SH SYNOPSIS .B fail2ban-server -[\fI\,OPTIONS\/\fR] +[\fIOPTIONS\fR] .SH DESCRIPTION -Fail2Ban v0.9.4 reads log file that contains password failure report +Fail2Ban v0.10.0a1 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 -fail2ban\-client instead. The default behaviour is to start the server -in background. .SH OPTIONS .TP -\fB\-b\fR -start in background -.TP -\fB\-f\fR -start in foreground +\fB\-c\fR +configuration directory .TP \fB\-s\fR socket path @@ -26,17 +19,45 @@ socket path \fB\-p\fR pidfile path .TP +\fB\-\-loglevel\fR +logging level +.HP +\fB\-\-logtarget\fR |STDOUT|STDERR|SYSLOG +.HP +\fB\-\-syslogsocket\fR auto| +.TP +\fB\-d\fR +dump configuration. For debugging +.TP +\fB\-i\fR +interactive mode +.TP +\fB\-v\fR +increase verbosity +.TP +\fB\-q\fR +decrease verbosity +.TP \fB\-x\fR force execution of the server (remove socket file) .TP +\fB\-b\fR +start server in background (default) +.TP +\fB\-f\fR +start server in foreground +.TP +\fB\-\-async\fR +start server in async mode (for internal usage only, don't read configuration) +.TP +\fB\-\-timeout\fR +timeout to wait for the server (for internal usage only, don't read configuration) +.TP \fB\-h\fR, \fB\-\-help\fR display this help message .TP \fB\-V\fR, \fB\-\-version\fR print the version -.SH AUTHOR -Written by Cyril Jaquier . -Many contributions by Yaroslav O. Halchenko . .SH "REPORTING BUGS" Report bugs to https://github.com/fail2ban/fail2ban/issues .SH COPYRIGHT diff --git a/man/fail2ban-testcases.1 b/man/fail2ban-testcases.1 index dc0fee32..9089d1ed 100644 --- a/man/fail2ban-testcases.1 +++ b/man/fail2ban-testcases.1 @@ -1,10 +1,10 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. -.TH FAIL2BAN-TESTCASES "1" "March 2016" "fail2ban-testcases 0.9.4" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1. +.TH FAIL2BAN-TESTCASES "1" "July 2016" "fail2ban-testcases 0.10.0a1" "User Commands" .SH NAME fail2ban-testcases \- run Fail2Ban unit-tests .SH SYNOPSIS .B fail2ban-testcases -[\fI\,OPTIONS\/\fR] [\fI\,regexps\/\fR] +[\fIOPTIONS\fR] [\fIregexps\fR] .SH DESCRIPTION Script to run Fail2Ban tests battery .SH OPTIONS @@ -15,12 +15,26 @@ show program's version number and exit \fB\-h\fR, \fB\-\-help\fR show this help message and exit .TP -\fB\-l\fR LOG_LEVEL, \fB\-\-log\-level\fR=\fI\,LOG_LEVEL\/\fR +\fB\-l\fR LOG_LEVEL, \fB\-\-log\-level\fR=\fILOG_LEVEL\fR Log level for the logger to use during running tests .TP \fB\-n\fR, \fB\-\-no\-network\fR Do not run tests that require the network .TP +\fB\-g\fR, \fB\-\-no\-gamin\fR +Do not run tests that require the gamin +.TP +\fB\-m\fR, \fB\-\-memory\-db\fR +Run database tests using memory instead of file +.TP +\fB\-f\fR, \fB\-\-fast\fR +Try to increase speed of the tests, decreasing of wait +intervals, memory database +.TP +\fB\-i\fR, \fB\-\-ignore\fR +negate [regexps] filter to ignore tests matched +specified regexps +.TP \fB\-t\fR, \fB\-\-log\-traceback\fR Enrich log\-messages with compressed tracebacks .TP