Merge tag '0.10.0a1' into 0.10-full

pull/1460/head
sebres 2016-07-15 10:32:42 +02:00
commit 922213f3d9
21 changed files with 569 additions and 432 deletions

View File

@ -88,10 +88,14 @@ ver. 0.9.5 (2016/XX/XXX) - wanna-be-released
added new parameter `__date_ambit` added new parameter `__date_ambit`
* gentoo-initd fixed --pidfile bug: `--pidfile` is option of start-stop-daemon, * gentoo-initd fixed --pidfile bug: `--pidfile` is option of start-stop-daemon,
not argument of fail2ban (see gh-1434) 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 Features:
* New Actions: * New Actions:
- action.d/firewallcmd-rich-rules and action.d/firewallcmd-rich-logging (gh-1367) - action.d/firewallcmd-rich-rules and action.d/firewallcmd-rich-logging (gh-1367)
- Enhancements: - Enhancements:
* Extreme speedup of all sqlite database operations (gh-1436), * Extreme speedup of all sqlite database operations (gh-1436),
by using of following sqlite options: 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 - (temp_store = MEMORY) temporary tables and indices are kept in memory
* journald journalmatch for pure-ftpd (gh-1362) * journald journalmatch for pure-ftpd (gh-1362)
* Add additional regex filter for dovecot ldap authentication failures (gh-1370) * 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 ver. 0.9.4 (2016/03/08) - for-you-ladies

View File

@ -13,9 +13,12 @@ config/action.d/complain.conf
config/action.d/dshield.conf config/action.d/dshield.conf
config/action.d/dummy.conf config/action.d/dummy.conf
config/action.d/firewallcmd-allports.conf config/action.d/firewallcmd-allports.conf
config/action.d/firewallcmd-common.conf
config/action.d/firewallcmd-ipset.conf config/action.d/firewallcmd-ipset.conf
config/action.d/firewallcmd-multiport.conf config/action.d/firewallcmd-multiport.conf
config/action.d/firewallcmd-new.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/hostsdeny.conf
config/action.d/ipfilter.conf config/action.d/ipfilter.conf
config/action.d/ipfw.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/iptables-xt_recent-echo.conf
config/action.d/mail-buffered.conf config/action.d/mail-buffered.conf
config/action.d/mail.conf config/action.d/mail.conf
config/action.d/mail-whois-common.conf
config/action.d/mail-whois.conf config/action.d/mail-whois.conf
config/action.d/mail-whois-lines.conf config/action.d/mail-whois-lines.conf
config/action.d/mynetwatchman.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-lines.conf
config/action.d/sendmail-whois-matches.conf config/action.d/sendmail-whois-matches.conf
config/action.d/shorewall.conf config/action.d/shorewall.conf
config/action.d/shorewall-ipset-proto6.conf
config/action.d/smtp.py config/action.d/smtp.py
config/action.d/symbiosis-blacklist-allports.conf config/action.d/symbiosis-blacklist-allports.conf
config/action.d/ufw.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-nohome.conf
config/filter.d/apache-noscript.conf config/filter.d/apache-noscript.conf
config/filter.d/apache-overflows.conf config/filter.d/apache-overflows.conf
config/filter.d/apache-pass.conf
config/filter.d/apache-shellshock.conf config/filter.d/apache-shellshock.conf
config/filter.d/assp.conf config/filter.d/assp.conf
config/filter.d/asterisk.conf config/filter.d/asterisk.conf
@ -79,17 +85,18 @@ config/filter.d/cyrus-imap.conf
config/filter.d/directadmin.conf config/filter.d/directadmin.conf
config/filter.d/dovecot.conf config/filter.d/dovecot.conf
config/filter.d/dropbear.conf config/filter.d/dropbear.conf
config/filter.d/drupal-auth.conf
config/filter.d/ejabberd-auth.conf config/filter.d/ejabberd-auth.conf
config/filter.d/exim-common.conf config/filter.d/exim-common.conf
config/filter.d/exim.conf config/filter.d/exim.conf
config/filter.d/exim-spam.conf config/filter.d/exim-spam.conf
config/filter.d/freeswitch.conf config/filter.d/freeswitch.conf
config/filter.d/froxlor-auth.conf
config/filter.d/groupoffice.conf config/filter.d/groupoffice.conf
config/filter.d/gssftpd.conf config/filter.d/gssftpd.conf
config/filter.d/guacamole.conf config/filter.d/guacamole.conf
config/filter.d/haproxy-http-auth.conf config/filter.d/haproxy-http-auth.conf
config/filter.d/horde.conf config/filter.d/horde.conf
config/filter.d/ignorecommands
config/filter.d/ignorecommands/apache-fakegooglebot config/filter.d/ignorecommands/apache-fakegooglebot
config/filter.d/kerio.conf config/filter.d/kerio.conf
config/filter.d/lighttpd-auth.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/selinux-ssh.conf
config/filter.d/sendmail-auth.conf config/filter.d/sendmail-auth.conf
config/filter.d/sendmail-reject.conf config/filter.d/sendmail-reject.conf
config/filter.d/sendmail-spam.conf
config/filter.d/sieve.conf config/filter.d/sieve.conf
config/filter.d/sogo-auth.conf config/filter.d/sogo-auth.conf
config/filter.d/solid-pop3d.conf config/filter.d/solid-pop3d.conf
@ -148,7 +154,6 @@ config/paths-osx.conf
CONTRIBUTING.md CONTRIBUTING.md
COPYING COPYING
DEVELOP DEVELOP
doc/run-rootless.txt
fail2ban-2to3 fail2ban-2to3
fail2ban/client/actionreader.py fail2ban/client/actionreader.py
fail2ban/client/beautifier.py fail2ban/client/beautifier.py
@ -185,7 +190,6 @@ fail2ban/server/filterpyinotify.py
fail2ban/server/filtersystemd.py fail2ban/server/filtersystemd.py
fail2ban/server/__init__.py fail2ban/server/__init__.py
fail2ban/server/ipdns.py fail2ban/server/ipdns.py
fail2ban/server/iso8601.py
fail2ban/server/jail.py fail2ban/server/jail.py
fail2ban/server/jails.py fail2ban/server/jails.py
fail2ban/server/jailthread.py fail2ban/server/jailthread.py
@ -203,21 +207,19 @@ fail2ban/tests/action_d/test_smtp.py
fail2ban/tests/actionstestcase.py fail2ban/tests/actionstestcase.py
fail2ban/tests/actiontestcase.py fail2ban/tests/actiontestcase.py
fail2ban/tests/banmanagertestcase.py fail2ban/tests/banmanagertestcase.py
fail2ban/tests/clientreadertestcase.py
fail2ban/tests/clientbeautifiertestcase.py fail2ban/tests/clientbeautifiertestcase.py
fail2ban/tests/clientreadertestcase.py
fail2ban/tests/config/action.d/brokenaction.conf fail2ban/tests/config/action.d/brokenaction.conf
fail2ban/tests/config/fail2ban.conf fail2ban/tests/config/fail2ban.conf
fail2ban/tests/config/filter.d/simple.conf fail2ban/tests/config/filter.d/simple.conf
fail2ban/tests/config/filter.d/test.conf fail2ban/tests/config/filter.d/test.conf
fail2ban/tests/config/filter.d/test.local fail2ban/tests/config/filter.d/test.local
fail2ban/tests/config/filter.d/zzz-generic-example.conf
fail2ban/tests/config/jail.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/databasetestcase.py
fail2ban/tests/datedetectortestcase.py fail2ban/tests/datedetectortestcase.py
fail2ban/tests/dummyjail.py fail2ban/tests/dummyjail.py
fail2ban/tests/fail2banclienttestcase.py
fail2ban/tests/fail2banregextestcase.py fail2ban/tests/fail2banregextestcase.py
fail2ban/tests/failmanagertestcase.py fail2ban/tests/failmanagertestcase.py
fail2ban/tests/files/action.d/action_checkainfo.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/3proxy
fail2ban/tests/files/logs/apache-auth fail2ban/tests/files/logs/apache-auth
fail2ban/tests/files/logs/apache-badbots fail2ban/tests/files/logs/apache-badbots
fail2ban/tests/files/logs/apache-botscripts
fail2ban/tests/files/logs/apache-botsearch fail2ban/tests/files/logs/apache-botsearch
fail2ban/tests/files/logs/apache-fakegooglebot fail2ban/tests/files/logs/apache-fakegooglebot
fail2ban/tests/files/logs/apache-modsecurity fail2ban/tests/files/logs/apache-modsecurity
fail2ban/tests/files/logs/apache-nohome fail2ban/tests/files/logs/apache-nohome
fail2ban/tests/files/logs/apache-noscript fail2ban/tests/files/logs/apache-noscript
fail2ban/tests/files/logs/apache-overflows fail2ban/tests/files/logs/apache-overflows
fail2ban/tests/files/logs/apache-pass
fail2ban/tests/files/logs/apache-shellshock fail2ban/tests/files/logs/apache-shellshock
fail2ban/tests/files/logs/assp fail2ban/tests/files/logs/assp
fail2ban/tests/files/logs/asterisk fail2ban/tests/files/logs/asterisk
@ -270,10 +272,12 @@ fail2ban/tests/files/logs/cyrus-imap
fail2ban/tests/files/logs/directadmin fail2ban/tests/files/logs/directadmin
fail2ban/tests/files/logs/dovecot fail2ban/tests/files/logs/dovecot
fail2ban/tests/files/logs/dropbear fail2ban/tests/files/logs/dropbear
fail2ban/tests/files/logs/drupal-auth
fail2ban/tests/files/logs/ejabberd-auth fail2ban/tests/files/logs/ejabberd-auth
fail2ban/tests/files/logs/exim fail2ban/tests/files/logs/exim
fail2ban/tests/files/logs/exim-spam fail2ban/tests/files/logs/exim-spam
fail2ban/tests/files/logs/freeswitch fail2ban/tests/files/logs/freeswitch
fail2ban/tests/files/logs/froxlor-auth
fail2ban/tests/files/logs/groupoffice fail2ban/tests/files/logs/groupoffice
fail2ban/tests/files/logs/gssftpd fail2ban/tests/files/logs/gssftpd
fail2ban/tests/files/logs/guacamole fail2ban/tests/files/logs/guacamole
@ -309,7 +313,6 @@ fail2ban/tests/files/logs/screensharingd
fail2ban/tests/files/logs/selinux-ssh fail2ban/tests/files/logs/selinux-ssh
fail2ban/tests/files/logs/sendmail-auth fail2ban/tests/files/logs/sendmail-auth
fail2ban/tests/files/logs/sendmail-reject fail2ban/tests/files/logs/sendmail-reject
fail2ban/tests/files/logs/sendmail-spam
fail2ban/tests/files/logs/sieve fail2ban/tests/files/logs/sieve
fail2ban/tests/files/logs/sogo-auth fail2ban/tests/files/logs/sogo-auth
fail2ban/tests/files/logs/solid-pop3d 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/webmin-auth
fail2ban/tests/files/logs/wuftpd fail2ban/tests/files/logs/wuftpd
fail2ban/tests/files/logs/xinetd-fail fail2ban/tests/files/logs/xinetd-fail
fail2ban/tests/files/logs/zzz-generic-example
fail2ban/tests/files/testcase01.log fail2ban/tests/files/testcase01.log
fail2ban/tests/files/testcase02.log fail2ban/tests/files/testcase02.log
fail2ban/tests/files/testcase03.log fail2ban/tests/files/testcase03.log
@ -356,6 +360,8 @@ files/gentoo-confd
files/gentoo-initd files/gentoo-initd
files/ipmasq-ZZZzzz_fail2ban.rul files/ipmasq-ZZZzzz_fail2ban.rul
files/logwatch/fail2ban files/logwatch/fail2ban
files/logwatch/fail2ban-0.8.log
files/logwatch/fail2ban-0.9.log
files/macosx-initd files/macosx-initd
files/monit/fail2ban files/monit/fail2ban
files/nagios/check_fail2ban files/nagios/check_fail2ban
@ -373,6 +379,8 @@ man/fail2ban-regex.1
man/fail2ban-regex.h2m man/fail2ban-regex.h2m
man/fail2ban-server.1 man/fail2ban-server.1
man/fail2ban-server.h2m man/fail2ban-server.h2m
man/fail2ban-testcases.1
man/fail2ban-testcases.h2m
man/generate-man man/generate-man
man/jail.conf.5 man/jail.conf.5
README.md README.md

View File

@ -190,7 +190,7 @@ Post Release
Add the following to the top of the ChangeLog:: 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: - Fixes:

View File

@ -122,7 +122,7 @@ if verbosity > 1: # pragma: no cover
if verbosity > 3: if verbosity > 3:
fmt = ' | %(module)15.15s-%(levelno)-2d: %(funcName)-20.20s |' + fmt fmt = ' | %(module)15.15s-%(levelno)-2d: %(funcName)-20.20s |' + fmt
if verbosity > 2: 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: else:
fmt = ' %(asctime)-15s %(thread)X %(levelname)-5.5s' + fmt fmt = ' %(asctime)-15s %(thread)X %(levelname)-5.5s' + fmt
# #

View File

@ -27,6 +27,7 @@ failregex = ^%(__prefix_line)s%(log_prefix)s Registration from '[^']*' failed fo
^%(__prefix_line)s%(log_prefix)s hacking attempt detected '<HOST>'$ ^%(__prefix_line)s%(log_prefix)s hacking attempt detected '<HOST>'$
^%(__prefix_line)s%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|<unknown>)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)/<HOST>/\d+"(,Challenge="[\w/]+")?(,ReceivedChallenge="\w+")?(,Response="\w+",ExpectedResponse="\w*")?(,ReceivedHash="[\da-f]+")?(,ACLName="\w+")?$ ^%(__prefix_line)s%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="([\d-]+|%(iso8601)s)",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="(\d*|<unknown>)",SessionID=".+",LocalAddress="IPV[46]/(UDP|TCP|WS)/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UDP|TCP|WS)/<HOST>/\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 <HOST>"$ ^%(__prefix_line)s%(log_prefix)s "Rejecting unknown SIP connection from <HOST>"$
^%(__prefix_line)s%(log_prefix)s Request (?:'[^']*' )?from '[^']*' failed for '<HOST>(?::\d+)?'\s\(callid: [^\)]*\) - (?:No matching endpoint found|Not match Endpoint(?: Contact)? ACL|(?:Failed|Error) to authenticate)\s*$
ignoreregex = ignoreregex =

View File

@ -9,8 +9,8 @@ after = exim-common.local
[Definition] [Definition]
host_info = H=([\w.-]+ )?(\(\S+\) )?\[<HOST>\](:\d+)? (I=\[\S+\]:\d+ )?(U=\S+ )?(P=e?smtp )? host_info = (?:H=([\w.-]+ )?(?:\(\S+\) )?)?\[<HOST>\](?::\d+)? (?:I=\[\S+\](:\d+)? )?(?:U=\S+ )?(?:P=e?smtp )?
pid = ( \[\d+\])? pid = (?: \[\d+\])?
# DEV Notes: # DEV Notes:
# From exim source code: ./src/receive.c:add_host_info_for_log # From exim source code: ./src/receive.c:add_host_info_for_log

View File

@ -14,13 +14,13 @@ before = exim-common.conf
[Definition] [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*$ 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+\) \[<HOST>\](:\d+)?( I=\[\S+\](:\d+)?)?: 535 Incorrect authentication data( \(set_id=.*\)|: \d+ Time\(s\))?\s*$ ^%(pid)s \w+ authenticator failed for (\S+ )?\(\S+\) \[<HOST>\](?::\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 %(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 protocol synchronization error \([^)]*\): rejected (?:connection from|"\S+") %(host_info)s(?:next )?input=".*"\s*$
^%(pid)s SMTP call from \S+ \[<HOST>\](:\d+)? (I=\[\S+\](:\d+)? )?dropped: too many nonmail commands \(last was "\S+"\)\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*)" H=(|\S* )(|\(\S*\) )\[<HOST>\]\:\d+ I=\[\S*\]\:\d+ AUTH command used when not advertised\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>\]\:\d+ I=\[\S*\]\:\d+ D=\d+s(| C=\S*)\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>\]\:\d+ I=\[\S*\]\:\d+ closed by DROP in ACL\s*$ ^%(pid)s \S+ SMTP connection from (?:\S* )?(?:\(\S*\) )?%(host_info)sclosed by DROP in ACL\s*$
ignoreregex = ignoreregex =

View File

@ -35,7 +35,7 @@ from ..version import version
from .csocket import CSocket from .csocket import CSocket
from .beautifier import Beautifier from .beautifier import Beautifier
from .fail2bancmdline import Fail2banCmdLine, ServerExecutionException, ExitException, \ from .fail2bancmdline import Fail2banCmdLine, ServerExecutionException, ExitException, \
logSys, PRODUCTION, exit, output logSys, exit, output
PROMPT = "fail2ban> " PROMPT = "fail2ban> "
@ -227,11 +227,11 @@ class Fail2banClient(Fail2banCmdLine, Thread):
# prepare: read config, check configuration is valid, etc.: # prepare: read config, check configuration is valid, etc.:
if phase is not None: if phase is not None:
phase['start'] = True phase['start'] = True
logSys.debug('-- client phase %s', phase) logSys.debug(' client phase %s', phase)
stream = self.__prepareStartServer() stream = self.__prepareStartServer()
if phase is not None: if phase is not None:
phase['ready'] = phase['start'] = (True if stream else False) phase['ready'] = phase['start'] = (True if stream else False)
logSys.debug('-- client phase %s', phase) logSys.debug(' client phase %s', phase)
if not stream: if not stream:
return False return False
# configure server with config stream: # configure server with config stream:
@ -361,19 +361,16 @@ class Fail2banClient(Fail2banCmdLine, Thread):
# Interactive mode # Interactive mode
if self._conf.get("interactive", False): if self._conf.get("interactive", False):
# no readline in test: try:
if PRODUCTION: # pragma: no cover import readline
try: except ImportError:
import readline raise ServerExecutionException("Readline not available")
except ImportError:
raise ServerExecutionException("Readline not available")
try: try:
ret = True ret = True
if len(args) > 0: if len(args) > 0:
ret = self.__processCommand(args) ret = self.__processCommand(args)
if ret: if ret:
if PRODUCTION: # pragma: no cover readline.parse_and_bind("tab: complete")
readline.parse_and_bind("tab: complete")
self.dispInteractive() self.dispInteractive()
while True: while True:
cmd = input_command() cmd = input_command()
@ -411,11 +408,9 @@ class Fail2banClient(Fail2banCmdLine, Thread):
signal.signal(s, sh) signal.signal(s, sh)
##
# Wonderful visual :)
#
class _VisualWait: class _VisualWait:
"""Small progress indication (as "wonderful visual") during waiting process
"""
pos = 0 pos = 0
delta = 1 delta = 1
def __init__(self, maxpos=10): def __init__(self, maxpos=10):
@ -427,6 +422,8 @@ class _VisualWait:
sys.stdout.write('\r'+(' '*(35+self.maxpos))+'\r') sys.stdout.write('\r'+(' '*(35+self.maxpos))+'\r')
sys.stdout.flush() sys.stdout.flush()
def heartbeat(self): def heartbeat(self):
"""Show or step for progress indicator
"""
if not self.pos: if not self.pos:
sys.stdout.write("\nINFO [#" + (' '*self.maxpos) + "] Waiting on the server...\r\x1b[8C") sys.stdout.write("\nINFO [#" + (' '*self.maxpos) + "] Waiting on the server...\r\x1b[8C")
self.pos += self.delta self.pos += self.delta
@ -441,6 +438,8 @@ class _VisualWait:
elif self.pos < 2: elif self.pos < 2:
self.delta = 1 self.delta = 1
class _NotVisualWait: class _NotVisualWait:
"""Mockup for invisible progress indication (not verbose)
"""
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, *args): def __exit__(self, *args):
@ -449,6 +448,8 @@ class _NotVisualWait:
pass pass
def VisualWait(verbose, *args, **kwargs): def VisualWait(verbose, *args, **kwargs):
"""Wonderful visual progress indication (if verbose)
"""
return _VisualWait(*args, **kwargs) if verbose > 1 else _NotVisualWait() return _VisualWait(*args, **kwargs) if verbose > 1 else _NotVisualWait()

View File

@ -42,6 +42,7 @@ PRODUCTION = True
MAX_WAITTIME = 30 MAX_WAITTIME = 30
class Fail2banCmdLine(): class Fail2banCmdLine():
def __init__(self): def __init__(self):
@ -83,9 +84,6 @@ class Fail2banCmdLine():
output("Copyright (c) 2004-2008 Cyril Jaquier, 2008- Fail2Ban Contributors") output("Copyright (c) 2004-2008 Cyril Jaquier, 2008- Fail2Ban Contributors")
output("Copyright of modifications held by their respective authors.") output("Copyright of modifications held by their respective authors.")
output("Licensed under the GNU General Public License v2 (GPL).") output("Licensed under the GNU General Public License v2 (GPL).")
output("")
output("Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.")
output("Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.")
def dispUsage(self): def dispUsage(self):
""" Prints Fail2Ban command line options and exits """ Prints Fail2Ban command line options and exits
@ -187,7 +185,7 @@ class Fail2banCmdLine():
if ret is not None: if ret is not None:
return ret 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 if initial and PRODUCTION: # pragma: no cover - can't test
verbose = self._conf["verbose"] verbose = self._conf["verbose"]
@ -259,14 +257,26 @@ class Fail2banCmdLine():
output(c) output(c)
return True 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 @staticmethod
def exit(code=0): # pragma: no cover - can't test def _exit(code=0):
logSys.debug("Exit with code %s", code) if hasattr(os, '_exit') and os._exit:
if os._exit:
os._exit(code) os._exit(code)
else: else:
sys.exit(code) 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: # global exit handler:
exit = Fail2banCmdLine.exit exit = Fail2banCmdLine.exit

View File

@ -46,7 +46,7 @@ class Fail2banServer(Fail2banCmdLine):
@staticmethod @staticmethod
def startServerDirect(conf, daemon=True): 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 from ..server.server import Server
server = None server = None
try: try:
@ -79,7 +79,7 @@ class Fail2banServer(Fail2banCmdLine):
frk = not conf["async"] and PRODUCTION frk = not conf["async"] and PRODUCTION
if frk: # pragma: no cover if frk: # pragma: no cover
pid = os.fork() 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: if pid == 0:
args = list() args = list()
args.append(SERVER) args.append(SERVER)

View File

@ -27,6 +27,9 @@ __license__ = "GPL"
import textwrap import textwrap
def output(s): def output(s):
"""Default output handler for printing protocol.
Used to ease mocking in the test cases.
"""
print(s) print(s)
## ##

View File

@ -88,6 +88,11 @@ class Server:
logSys.debug("Caught signal %d. Flushing logs" % signum) logSys.debug("Caught signal %d. Flushing logs" % signum)
self.flushLogs() 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={}): def start(self, sock, pidfile, force=False, observer=True, conf={}):
# 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)
@ -121,9 +126,10 @@ class Server:
# Install signal handlers # Install signal handlers
if _thread_name() == '_MainThread': if _thread_name() == '_MainThread':
for s in (signal.SIGTERM, signal.SIGINT, signal.SIGUSR1): for s in (signal.SIGTERM, signal.SIGINT):
self.__prev_signals[s] = signal.getsignal(s) self._rebindSignal(s, self.__sigTERMhandler)
signal.signal(s, self.__sigTERMhandler if s != signal.SIGUSR1 else self.__sigUSR1handler) self._rebindSignal(signal.SIGUSR1, self.__sigUSR1handler)
# Ensure unhandled exceptions are logged # Ensure unhandled exceptions are logged
sys.excepthook = excepthook sys.excepthook = excepthook
@ -389,7 +395,7 @@ class Server:
return self.__jails[name].getBanTimeExtra(opt) return self.__jails[name].getBanTimeExtra(opt)
def isStarted(self): def isStarted(self):
self.__asyncServer.isActive() return self.__asyncServer is not None and self.__asyncServer.isActive()
def isAlive(self, jailnum=None): def isAlive(self, jailnum=None):
if jailnum is not None and len(self.__jails) != jailnum: if jailnum is not None and len(self.__jails) != jailnum:
@ -437,7 +443,7 @@ class Server:
getLogger("fail2ban").setLevel(getattr(logging, value)) getLogger("fail2ban").setLevel(getattr(logging, value))
self.__logLevel = value self.__logLevel = value
except AttributeError: except AttributeError:
raise ValueError("Invalid log level") raise ValueError("Invalid log level %r" % value)
## ##
# Get the logging level. # Get the logging level.
@ -512,14 +518,14 @@ class Server:
# Is known to be thrown after logging was shutdown once # Is known to be thrown after logging was shutdown once
# with older Pythons -- seems to be safe to ignore there # with older Pythons -- seems to be safe to ignore there
# At least it was still failing on 2.6.2-0ubuntu1 (jaunty) # At least it was still failing on 2.6.2-0ubuntu1 (jaunty)
if (2,6,3) <= sys.version_info < (3,) or \ if (2, 6, 3) <= sys.version_info < (3,) or \
(3,2) <= sys.version_info: (3, 2) <= sys.version_info:
raise raise
# tell the handler to use this format # tell the handler to use this format
hdlr.setFormatter(formatter) hdlr.setFormatter(formatter)
logger.addHandler(hdlr) logger.addHandler(hdlr)
# Does not display this message at startup. # 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("Start Fail2ban v%s", version.version)
logSys.info( logSys.info(
"Changed logging target to %s for Fail2ban v%s" "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 # 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 # child process, and this makes sure that it is effect even if the parent
# terminates quickly. # terminates quickly.
for s in (signal.SIGHUP,): self._rebindSignal(signal.SIGHUP, signal.SIG_IGN)
self.__prev_signals[s] = signal.getsignal(s)
signal.signal(s, signal.SIG_IGN)
try: try:
# Fork a child process so the parent can exit. This will return control # Fork a child process so the parent can exit. This will return control

View File

@ -31,25 +31,30 @@ import time
import signal import signal
import unittest import unittest
from os.path import join as pjoin, isdir, isfile, exists, dirname
from functools import wraps from functools import wraps
from threading import Thread from threading import Thread
from ..client import fail2banclient, fail2banserver, fail2bancmdline 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 ..client.fail2banserver import Fail2banServer, exec_command_line as _exec_server
from .. import protocol from .. import protocol
from ..server import server from ..server import server
from ..server.utils import Utils 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_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" CLIENT = "fail2ban-client"
SERVER = "fail2ban-server" SERVER = "fail2ban-server"
BIN = os.path.dirname(Fail2banServer.getServerPath()) BIN = dirname(Fail2banServer.getServerPath())
MAX_WAITTIME = 30 if not unittest.F2B.fast else 5 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: # Several wrappers and settings for proper testing:
# #
fail2bancmdline.MAX_WAITTIME = MAX_WAITTIME-1 fail2bancmdline.MAX_WAITTIME = MAX_WAITTIME - 1
fail2bancmdline.logSys = \ fail2bancmdline.logSys = \
fail2banclient.logSys = \ fail2banclient.logSys = \
@ -72,63 +77,68 @@ fail2banclient.output = \
fail2banserver.output = \ fail2banserver.output = \
protocol.output = _test_output protocol.output = _test_output
def _test_exit(code=0):
logSys.debug("Exit with code %s", code) #
if code == 0: # Mocking .exit so we could test its correct operation.
raise ExitException() # Two custom exceptions will be assessed to be raised in the tests
else: #
raise FailExitException()
fail2bancmdline.exit = \ class ExitException(fail2bancmdline.ExitException):
fail2banclient.exit = \ """Exception upon a normal exit"""
fail2banserver.exit = _test_exit pass
class FailExitException(fail2bancmdline.ExitException):
"""Exception upon abnormal exit"""
pass
INTERACT = [] INTERACT = []
def _test_input_command(*args): def _test_input_command(*args):
if len(INTERACT): if len(INTERACT):
#logSys.debug('--- interact command: %r', INTERACT[0]) #logSys.debug('--- interact command: %r', INTERACT[0])
return INTERACT.pop(0) return INTERACT.pop(0)
else: else:
return "exit" return "exit"
fail2banclient.input_command = _test_input_command fail2banclient.input_command = _test_input_command
# prevents change logging params, log capturing, etc: # prevents change logging params, log capturing, etc:
fail2bancmdline.PRODUCTION = \ fail2bancmdline.PRODUCTION = \
fail2banclient.PRODUCTION = \
fail2banserver.PRODUCTION = False fail2banserver.PRODUCTION = False
class ExitException(fail2bancmdline.ExitException): def _out_file(fn):
pass """Helper which outputs content of the file at HEAVYDEBUG loglevels"""
class FailExitException(fail2bancmdline.ExitException):
pass
def _out_file(fn): # pragma: no cover
logSys.debug('---- ' + fn + ' ----') logSys.debug('---- ' + fn + ' ----')
for line in fileinput.input(fn): for line in fileinput.input(fn):
line = line.rstrip('\n') line = line.rstrip('\n')
logSys.debug(line) logSys.debug(line)
logSys.debug('-'*30) logSys.debug('-'*30)
def _start_params(tmp, use_stock=False, logtarget="/dev/null"): def _start_params(tmp, use_stock=False, logtarget="/dev/null"):
cfg = tmp+"/config" cfg = pjoin(tmp, "config")
if use_stock and STOCK: if use_stock and STOCK:
# copy config (sub-directories as alias): # copy config (sub-directories as alias):
def ig_dirs(dir, files): 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) shutil.copytree(STOCK_CONF_DIR, cfg, ignore=ig_dirs)
os.symlink(STOCK_CONF_DIR+"/action.d", cfg+"/action.d") os.symlink(pjoin(STOCK_CONF_DIR, "action.d"), pjoin(cfg, "action.d"))
os.symlink(STOCK_CONF_DIR+"/filter.d", cfg+"/filter.d") os.symlink(pjoin(STOCK_CONF_DIR, "filter.d"), pjoin(cfg, "filter.d"))
# replace fail2ban params (database with memory): # replace fail2ban params (database with memory):
r = re.compile(r'^dbfile\s*=') 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') line = line.rstrip('\n')
if r.match(line): if r.match(line):
line = "dbfile = :memory:" line = "dbfile = :memory:"
print(line) print(line)
# replace jail params (polling as backend to be fast in initialize): # replace jail params (polling as backend to be fast in initialize):
r = re.compile(r'^backend\s*=') 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') line = line.rstrip('\n')
if r.match(line): if r.match(line):
line = "backend = polling" line = "backend = polling"
@ -136,77 +146,83 @@ def _start_params(tmp, use_stock=False, logtarget="/dev/null"):
else: else:
# just empty config directory without anything (only fail2ban.conf/jail.conf): # just empty config directory without anything (only fail2ban.conf/jail.conf):
os.mkdir(cfg) os.mkdir(cfg)
f = open(cfg+"/fail2ban.conf", "w") f = open(pjoin(cfg, "fail2ban.conf"), "w")
f.write('\n'.join(( f.write('\n'.join((
"[Definition]", "[Definition]",
"loglevel = INFO", "loglevel = INFO",
"logtarget = " + logtarget, "logtarget = " + logtarget,
"syslogsocket = auto", "syslogsocket = auto",
"socket = "+tmp+"/f2b.sock", "socket = " + pjoin(tmp, "f2b.sock"),
"pidfile = "+tmp+"/f2b.pid", "pidfile = " + pjoin(tmp, "f2b.pid"),
"backend = polling", "backend = polling",
"dbfile = :memory:", "dbfile = :memory:",
"dbpurgeage = 1d", "dbpurgeage = 1d",
"", "",
))) )))
f.close() f.close()
f = open(cfg+"/jail.conf", "w") f = open(pjoin(cfg, "jail.conf"), "w")
f.write('\n'.join(( f.write('\n'.join((
"[INCLUDES]", "", "[INCLUDES]", "",
"[DEFAULT]", "", "[DEFAULT]", "",
"", "",
))) )))
f.close() f.close()
if logSys.level < logging.DEBUG: # if HEAVYDEBUG if logSys.level < logging.DEBUG: # if HEAVYDEBUG
_out_file(cfg+"/fail2ban.conf") _out_file(pjoin(cfg, "fail2ban.conf"))
_out_file(cfg+"/jail.conf") _out_file(pjoin(cfg, "jail.conf"))
# parameters (sock/pid and config, increase verbosity, set log, etc.): # parameters (sock/pid and config, increase verbosity, set log, etc.):
return ("-c", cfg, "-s", tmp+"/f2b.sock", "-p", tmp+"/f2b.pid", return (
"-vv", "--logtarget", logtarget, "--loglevel", "DEBUG", "--syslogsocket", "auto", "-c", cfg, "-s", pjoin(tmp, "f2b.sock"), "-p", pjoin(tmp, "f2b.pid"),
"--timeout", str(fail2bancmdline.MAX_WAITTIME), "-vv", "--logtarget", logtarget, "--loglevel", "DEBUG", "--syslogsocket", "auto",
"--timeout", str(fail2bancmdline.MAX_WAITTIME),
) )
def _kill_srv(pidfile): # pragma: no cover
def _pid_exists(pid): def _kill_srv(pidfile):
try: logSys.debug("cleanup: %r", (pidfile, isdir(pidfile)))
os.kill(pid, 0) if isdir(pidfile):
return True
except OSError:
return False
logSys.debug("-- cleanup: %r", (pidfile, os.path.isdir(pidfile)))
if os.path.isdir(pidfile):
piddir = pidfile piddir = pidfile
pidfile = piddir + "/f2b.pid" pidfile = pjoin(piddir, "f2b.pid")
if not os.path.isfile(pidfile): if not isfile(pidfile): # pragma: no cover
pidfile = piddir + "/fail2ban.pid" pidfile = pjoin(piddir, "fail2ban.pid")
if not os.path.isfile(pidfile):
logSys.debug("--- cleanup: no pidfile for %r", piddir) if not isfile(pidfile):
logSys.debug("cleanup: no pidfile for %r", piddir)
return True return True
f = pid = None f = pid = None
try: try:
logSys.debug("--- cleanup pidfile: %r", pidfile) logSys.debug("cleanup pidfile: %r", pidfile)
f = open(pidfile) f = open(pidfile)
pid = f.read().split()[1] pid = f.read()
pid = re.match(r'\S+', pid).group()
pid = int(pid) pid = int(pid)
logSys.debug("--- cleanup pid: %r", pid) except Exception as e: # pragma: no cover
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:
logSys.debug(e) logSys.debug(e)
return False
finally: finally:
if f is not None: if f is not None:
f.close() 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 return True
def with_kill_srv(f): def with_kill_srv(f):
"""Helper to decorate tests which receive in the last argument tmpdir to pass to kill_srv """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): class Fail2banClientServerBase(LogCaptureTestCase):
_orig_exit = Fail2banCmdLine._exit
def setUp(self): def setUp(self):
"""Call before every test case.""" """Call before every test case."""
LogCaptureTestCase.setUp(self) LogCaptureTestCase.setUp(self)
Fail2banCmdLine._exit = staticmethod(self._test_exit)
def tearDown(self): def tearDown(self):
"""Call after every test case.""" """Call after every test case."""
Fail2banCmdLine._exit = self._orig_exit
LogCaptureTestCase.tearDown(self) 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): def _wait_for_srv(self, tmp, ready=True, startparams=None):
try: try:
sock = tmp+"/f2b.sock" sock = pjoin(tmp, "f2b.sock")
# wait for server (socket): # 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: 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: if ready:
# wait for communication with worker ready: # wait for communication with worker ready:
ret = Utils.wait_for(lambda: "Server ready" in self.getLog(), MAX_WAITTIME) ret = Utils.wait_for(lambda: "Server ready" in self.getLog(), MAX_WAITTIME)
if not ret: if not ret:
raise Exception('Unexpected: Server ready was not found.\nStart failed: %r' % (startparams,)) raise Exception(
except: # pragma: no cover 'Unexpected: Server ready was not found.\nStart failed: %r'
log = tmp+"/f2b.log" % (startparams,)
if os.path.isfile(log): )
except: # pragma: no cover
log = pjoin(tmp, "f2b.log")
if isfile(log):
_out_file(log) _out_file(log)
else: else:
logSys.debug("No log file %s to examine details of error", log) logSys.debug("No log file %s to examine details of error", log)
raise 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))) # Common tests
self.assertTrue(os.path.isfile(os.path.join(os.path.join(BIN), SERVER))) #
def _testStartForeground(self, tmp, startparams, phase):
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):
# start and wait to end (foreground): # start and wait to end (foreground):
logSys.debug("-- start of test worker") logSys.debug("start of test worker")
phase['start'] = True phase['start'] = True
self.assertRaises(fail2bancmdline.ExitException, _exec_client, self.execSuccess(("-f",) + startparams, "start")
(CLIENT, "-f") + startparams + ("start",))
# end : # end :
phase['end'] = True phase['end'] = True
logSys.debug("-- end of test worker") logSys.debug("end of test worker")
@with_tmpdir @with_tmpdir
def testClientStartForeground(self, tmp): def testStartForeground(self, tmp):
# intended to be ran only in subclasses
th = None th = None
phase = dict()
try: try:
# started directly here, so prevent overwrite test cases logger with "INHERITED" # started directly here, so prevent overwrite test cases logger with "INHERITED"
startparams = _start_params(tmp, logtarget="INHERITED") startparams = _start_params(tmp, logtarget="INHERITED")
# because foreground block execution - start it in thread: # because foreground block execution - start it in thread:
phase = dict() th = Thread(
th = Thread(name="_TestCaseWorker", name="_TestCaseWorker",
target=Fail2banClientTest._testClientStartForeground, args=(self, tmp, startparams, phase)) target=self._testStartForeground,
args=(tmp, startparams, phase)
)
th.daemon = True th.daemon = True
th.start() th.start()
try: try:
@ -415,25 +327,149 @@ class Fail2banClientTest(Fail2banClientServerBase):
self._wait_for_srv(tmp, True, startparams=startparams) self._wait_for_srv(tmp, True, startparams=startparams)
self.pruneLog() self.pruneLog()
# several commands to server: # several commands to server:
self.assertRaises(ExitException, _exec_client, self.execSuccess(startparams, "ping")
(CLIENT,) + startparams + ("ping",)) self.execFailed(startparams, "~~unknown~cmd~failed~~")
self.assertRaises(FailExitException, _exec_client, self.execSuccess(startparams, "echo", "TEST-ECHO")
(CLIENT,) + startparams + ("~~unknown~cmd~failed~~",))
self.assertRaises(ExitException, _exec_client,
(CLIENT,) + startparams + ("echo", "TEST-ECHO",))
finally: finally:
self.pruneLog() self.pruneLog()
# stop: # stop:
self.assertRaises(ExitException, _exec_client, self.execSuccess(startparams, "stop")
(CLIENT,) + startparams + ("stop",))
# wait for end: # wait for end:
Utils.wait_for(lambda: phase.get('end', None) is not None, MAX_WAITTIME) Utils.wait_for(lambda: phase.get('end', None) is not None, MAX_WAITTIME)
self.assertTrue(phase.get('end', None)) self.assertTrue(phase.get('end', None))
self.assertLogged("Shutdown successful", "Exiting Fail2ban") self.assertLogged("Shutdown successful", "Exiting Fail2ban")
finally: finally:
_kill_srv(tmp)
if th: 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_tmpdir
@with_kill_srv @with_kill_srv
@ -442,34 +478,33 @@ class Fail2banClientTest(Fail2banClientServerBase):
startparams = _start_params(tmp, logtarget="INHERITED") startparams = _start_params(tmp, logtarget="INHERITED")
## wrong config directory ## wrong config directory
self.assertRaises(FailExitException, _exec_client, self.execFailed((),
(CLIENT, "--async", "-c", tmp+"/miss", "start",)) "--async", "-c", pjoin(tmp, "miss"), "start")
self.assertLogged("Base configuration directory " + tmp+"/miss" + " does not exist") self.assertLogged("Base configuration directory " + pjoin(tmp, "miss") + " does not exist")
self.pruneLog() self.pruneLog()
## wrong socket ## wrong socket
self.assertRaises(FailExitException, _exec_client, self.execFailed((),
(CLIENT, "--async", "-c", tmp+"/config", "-s", tmp+"/miss/f2b.sock", "start",)) "--async", "-c", pjoin(tmp, "config"), "-s", pjoin(tmp, "miss/f2b.sock"), "start")
self.assertLogged("There is no directory " + tmp+"/miss" + " to contain the socket file") self.assertLogged("There is no directory " + pjoin(tmp, "miss") + " to contain the socket file")
self.pruneLog() self.pruneLog()
## not running ## not running
self.assertRaises(FailExitException, _exec_client, self.execFailed((),
(CLIENT, "-c", tmp+"/config", "-s", tmp+"/f2b.sock", "reload",)) "-c", pjoin(tmp, "config"), "-s", pjoin(tmp, "f2b.sock"), "reload")
self.assertLogged("Could not find server") self.assertLogged("Could not find server")
self.pruneLog() self.pruneLog()
## already exists: ## already exists:
open(tmp+"/f2b.sock", 'a').close() open(pjoin(tmp, "f2b.sock"), 'a').close()
self.assertRaises(FailExitException, _exec_client, self.execFailed((),
(CLIENT, "--async", "-c", tmp+"/config", "-s", tmp+"/f2b.sock", "start",)) "--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.assertLogged("Fail2ban seems to be in unexpected state (not running but the socket exists)")
self.pruneLog() self.pruneLog()
os.remove(tmp+"/f2b.sock") os.remove(pjoin(tmp, "f2b.sock"))
## wrong option: ## wrong option:
self.assertRaises(FailExitException, _exec_client, self.execFailed((), "-s")
(CLIENT, "-s",))
self.assertLogged("Usage: ") self.assertLogged("Usage: ")
self.pruneLog() self.pruneLog()
@ -487,9 +522,16 @@ class Fail2banClientTest(Fail2banClientServerBase):
class Fail2banServerTest(Fail2banClientServerBase): class Fail2banServerTest(Fail2banClientServerBase):
def testServerUsage(self): def execSuccess(self, startparams, *args):
self.assertRaises(ExitException, _exec_server, self.assertRaises(ExitException, _exec_server,
(SERVER, "-h",)) ((SERVER,) + startparams + args))
def execFailed(self, startparams, *args):
self.assertRaises(FailExitException, _exec_server,
((SERVER,) + startparams + args))
def testServerUsage(self):
self.execSuccess((), "-h")
self.assertLogged("Usage: " + SERVER) self.assertLogged("Usage: " + SERVER)
self.assertLogged("Report bugs to ") self.assertLogged("Report bugs to ")
@ -497,9 +539,9 @@ class Fail2banServerTest(Fail2banClientServerBase):
@with_kill_srv @with_kill_srv
def testServerStartBackground(self, tmp): def testServerStartBackground(self, tmp):
# to prevent fork of test-cases process, start server in background via command: # 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): # 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) logSys.debug('Start %s ...', cmd)
cmd = cmd + startparams + ("-b",) cmd = cmd + startparams + ("-b",)
ret = Utils.executeCmd(cmd, timeout=MAX_WAITTIME, shell=False, output=True) ret = Utils.executeCmd(cmd, timeout=MAX_WAITTIME, shell=False, output=True)
@ -509,68 +551,15 @@ class Fail2banServerTest(Fail2banClientServerBase):
self.assertLogged("Server ready") self.assertLogged("Server ready")
self.pruneLog() self.pruneLog()
try: try:
self.assertRaises(ExitException, _exec_server, self.execSuccess(startparams, "echo", "TEST-ECHO")
(SERVER,) + startparams + ("echo", "TEST-ECHO",)) self.execFailed(startparams, "~~unknown~cmd~failed~~")
self.assertRaises(FailExitException, _exec_server,
(SERVER,) + startparams + ("~~unknown~cmd~failed~~",))
finally: finally:
self.pruneLog() self.pruneLog()
# stop: # stop:
self.assertRaises(ExitException, _exec_server, self.execSuccess(startparams, "stop")
(SERVER,) + startparams + ("stop",))
self.assertLogged("Shutdown successful") self.assertLogged("Shutdown successful")
self.assertLogged("Exit with code 0") 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_tmpdir
@with_kill_srv @with_kill_srv
def testServerFailStart(self, tmp): def testServerFailStart(self, tmp):
@ -578,21 +567,48 @@ class Fail2banServerTest(Fail2banClientServerBase):
startparams = _start_params(tmp, logtarget="INHERITED") startparams = _start_params(tmp, logtarget="INHERITED")
## wrong config directory ## wrong config directory
self.assertRaises(FailExitException, _exec_server, self.execFailed((),
(SERVER, "-c", tmp+"/miss",)) "-c", pjoin(tmp, "miss"))
self.assertLogged("Base configuration directory " + tmp+"/miss" + " does not exist") self.assertLogged("Base configuration directory " + pjoin(tmp, "miss") + " does not exist")
self.pruneLog() self.pruneLog()
## wrong socket ## wrong socket
self.assertRaises(FailExitException, _exec_server, self.execFailed((),
(SERVER, "-c", tmp+"/config", "-x", "-s", tmp+"/miss/f2b.sock",)) "-c", pjoin(tmp, "config"), "-x", "-s", pjoin(tmp, "miss/f2b.sock"))
self.assertLogged("There is no directory " + tmp+"/miss" + " to contain the socket file") self.assertLogged("There is no directory " + pjoin(tmp, "miss") + " to contain the socket file")
self.pruneLog() self.pruneLog()
## already exists: ## already exists:
open(tmp+"/f2b.sock", 'a').close() open(pjoin(tmp, "f2b.sock"), 'a').close()
self.assertRaises(FailExitException, _exec_server, self.execFailed((),
(SERVER, "-c", tmp+"/config", "-s", tmp+"/f2b.sock",)) "-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.assertLogged("Fail2ban seems to be in unexpected state (not running but the socket exists)")
self.pruneLog() 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")

View File

@ -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'. [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" } # 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'. [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" <sip:1000@10.0.0.1>' 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" <sip:1000@10.0.0.1>' 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" <sip:1000@10.0.0.1>' 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" <sip:1000@10.0.0.1>' 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" <sip:1000@10.0.0.1>' 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" <sip:317@1.2.3.4>' failed for '2.3.4.5:5089' (callid: 206f178f-896564cb-57573f49@1.2.3.4) - No matching endpoint found

View File

@ -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 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" } # 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 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" } # 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 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" } # 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 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" } # 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 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" } # failJSON: { "time": "2016-03-21T04:33:13", "match": true , "host": "206.214.71.53" }

View File

@ -159,6 +159,11 @@ class Transmitter(TransmitterBase):
self.server = TestServer() self.server = TestServer()
super(Transmitter, self).setUp() 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): def testStopServer(self):
self.assertEqual(self.transm.proceed(["stop"]), (0, None)) self.assertEqual(self.transm.proceed(["stop"]), (0, None))
@ -1014,6 +1019,7 @@ class LoggingTests(LogCaptureTestCase):
server = TestServer() server = TestServer()
try: try:
server.start(sock_name, pidfile_name, force=False) server.start(sock_name, pidfile_name, force=False)
self.assertFalse(server.isStarted())
self.assertLogged("Server already running") self.assertLogged("Server already running")
finally: finally:
server.quit() server.quit()

View File

@ -54,10 +54,9 @@ if not CONFIG_DIR:
else: else:
CONFIG_DIR = '/etc/fail2ban' CONFIG_DIR = '/etc/fail2ban'
# In not installed env (setup, test-cases) use fail2ban modules from main directory: # During the test cases (or setup) 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.putenv('PYTHONPATH', os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__)))))
os.path.abspath(__file__)))))
class F2B(optparse.Values): class F2B(optparse.Values):
def __init__(self, opts={}): def __init__(self, opts={}):

View File

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
.TH FAIL2BAN-CLIENT "1" "March 2016" "fail2ban-client v0.9.4" "User Commands" .TH FAIL2BAN-CLIENT "1" "July 2016" "fail2ban-client v0.10.0a1" "User Commands"
.SH NAME .SH NAME
fail2ban-client \- configure and control the server fail2ban-client \- configure and control the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-client .B fail2ban-client
[\fI\,OPTIONS\/\fR] \fI\,<COMMAND>\/\fR [\fIOPTIONS\fR] \fI<COMMAND>\fR
.SH DESCRIPTION .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. and bans the corresponding IP addresses using firewall rules.
.SH OPTIONS .SH OPTIONS
.TP .TP
@ -19,6 +19,13 @@ socket path
\fB\-p\fR <FILE> \fB\-p\fR <FILE>
pidfile path pidfile path
.TP .TP
\fB\-\-loglevel\fR <LEVEL>
logging level
.HP
\fB\-\-logtarget\fR <FILE>|STDOUT|STDERR|SYSLOG
.HP
\fB\-\-syslogsocket\fR auto|<FILE>
.TP
\fB\-d\fR \fB\-d\fR
dump configuration. For debugging dump configuration. For debugging
.TP .TP
@ -38,7 +45,13 @@ force execution of the server (remove socket file)
start server in background (default) start server in background (default)
.TP .TP
\fB\-f\fR \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 .TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
display this help message display this help message
@ -52,8 +65,12 @@ BASIC
\fBstart\fR \fBstart\fR
starts the server and the jails starts the server and the jails
.TP .TP
\fBrestart\fR
restarts the server
.TP
\fBreload\fR \fBreload\fR
reloads the configuration reloads the configuration without
restart
.TP .TP
\fBreload <JAIL>\fR \fBreload <JAIL>\fR
reloads the jail <JAIL> reloads the jail <JAIL>
@ -69,6 +86,10 @@ server
\fBping\fR \fBping\fR
tests if the server is alive tests if the server is alive
.TP .TP
\fBecho\fR
for internal usage, returns back
and outputs a given string
.TP
\fBhelp\fR \fBhelp\fR
return this output return this output
.TP .TP
@ -227,8 +248,9 @@ for <JAIL>
\fBset <JAIL> maxlines <LINES>\fR \fBset <JAIL> maxlines <LINES>\fR
sets the number of <LINES> to sets the number of <LINES> to
buffer for regex search for <JAIL> buffer for regex search for <JAIL>
.TP .IP
\fBset <JAIL> addaction <ACT>[ <PYTHONFILE> <JSONKWARGS>]\fR set <JAIL> addaction <ACT>[ <PYTHONFILE> <JSONKWARGS>]
.IP
adds a new action named <ACT> for adds a new action named <ACT> for
<JAIL>. Optionally for a Python <JAIL>. Optionally for a Python
based action, a <PYTHONFILE> and based action, a <PYTHONFILE> and
@ -240,38 +262,45 @@ removes the action <ACT> from
<JAIL> <JAIL>
.IP .IP
COMMAND ACTION CONFIGURATION COMMAND ACTION CONFIGURATION
.TP .IP
\fBset <JAIL> action <ACT> actionstart <CMD>\fR set <JAIL> action <ACT> actionstart <CMD>
.IP
sets the start command <CMD> of sets the start command <CMD> of
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.TP .IP
\fBset <JAIL> action <ACT> actionstop <CMD> sets the stop command <CMD> of the\fR set <JAIL> action <ACT> actionstop <CMD> sets the stop command <CMD> of the
.IP
action <ACT> for <JAIL> action <ACT> for <JAIL>
.TP .IP
\fBset <JAIL> action <ACT> actioncheck <CMD>\fR set <JAIL> action <ACT> actioncheck <CMD>
.IP
sets the check command <CMD> of sets the check command <CMD> of
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.TP .TP
\fBset <JAIL> action <ACT> actionban <CMD>\fR \fBset <JAIL> action <ACT> actionban <CMD>\fR
sets the ban command <CMD> of the sets the ban command <CMD> of the
action <ACT> for <JAIL> action <ACT> for <JAIL>
.TP .IP
\fBset <JAIL> action <ACT> actionunban <CMD>\fR set <JAIL> action <ACT> actionunban <CMD>
.IP
sets the unban command <CMD> of sets the unban command <CMD> of
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.TP .IP
\fBset <JAIL> action <ACT> timeout <TIMEOUT>\fR set <JAIL> action <ACT> timeout <TIMEOUT>
.IP
sets <TIMEOUT> as the command sets <TIMEOUT> as the command
timeout in seconds for the action timeout in seconds for the action
<ACT> for <JAIL> <ACT> for <JAIL>
.IP .IP
GENERAL ACTION CONFIGURATION GENERAL ACTION CONFIGURATION
.TP .IP
\fBset <JAIL> action <ACT> <PROPERTY> <VALUE>\fR set <JAIL> action <ACT> <PROPERTY> <VALUE>
.IP
sets the <VALUE> of <PROPERTY> for sets the <VALUE> of <PROPERTY> for
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.TP .IP
\fBset <JAIL> action <ACT> <METHOD>[ <JSONKWARGS>]\fR set <JAIL> action <ACT> <METHOD>[ <JSONKWARGS>]
.IP
calls the <METHOD> with calls the <METHOD> with
<JSONKWARGS> for the action <ACT> <JSONKWARGS> for the action <ACT>
for <JAIL> for <JAIL>
@ -376,9 +405,6 @@ gets the value of <PROPERTY> for
the action <ACT> for <JAIL> the action <ACT> for <JAIL>
.SH FILES .SH FILES
\fI/etc/fail2ban/*\fR \fI/etc/fail2ban/*\fR
.SH AUTHOR
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Report bugs to https://github.com/fail2ban/fail2ban/issues Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT .SH COPYRIGHT

View File

@ -1,10 +1,10 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
.TH FAIL2BAN-REGEX "1" "March 2016" "fail2ban-regex 0.9.4" "User Commands" .TH FAIL2BAN-REGEX "1" "July 2016" "fail2ban-regex 0.10.0a1" "User Commands"
.SH NAME .SH NAME
fail2ban-regex \- test Fail2ban "failregex" option fail2ban-regex \- test Fail2ban "failregex" option
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-regex .B fail2ban-regex
[\fI\,OPTIONS\/\fR] \fI\,<LOG> <REGEX> \/\fR[\fI\,IGNOREREGEX\/\fR] [\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR]
.SH DESCRIPTION .SH DESCRIPTION
Fail2Ban reads log file that contains password failure report Fail2Ban reads log file that contains password failure report
and bans the corresponding IP addresses using firewall rules. and bans the corresponding IP addresses using firewall rules.
@ -16,7 +16,7 @@ string
a string representing a log line a string representing a log line
.TP .TP
filename filename
path to a log file (\fI\,/var/log/auth.log\/\fP) path to a log file (\fI/var/log/auth.log\fP)
.TP .TP
"systemd\-journal" "systemd\-journal"
search systemd journal (systemd\-python required) search systemd journal (systemd\-python required)
@ -42,20 +42,23 @@ show program's version number and exit
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
show this help message and exit show this help message and exit
.TP .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 set custom pattern used to match date/times
.TP .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 File encoding. Default: system locale
.TP .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 maxlines for multi\-line regex
.TP .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. journalctl style matches overriding filter file.
"systemd\-journal" only "systemd\-journal" only
.TP .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 Log level for the Fail2Ban logger to use
.TP .TP
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR

View File

@ -1,24 +1,17 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
.TH FAIL2BAN-SERVER "1" "March 2016" "fail2ban-server v0.9.4" "User Commands" .TH FAIL2BAN-SERVER "1" "July 2016" "fail2ban-server v0.10.0a1" "User Commands"
.SH NAME .SH NAME
fail2ban-server \- start the server fail2ban-server \- start the server
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-server .B fail2ban-server
[\fI\,OPTIONS\/\fR] [\fIOPTIONS\fR]
.SH DESCRIPTION .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. 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 .SH OPTIONS
.TP .TP
\fB\-b\fR \fB\-c\fR <DIR>
start in background configuration directory
.TP
\fB\-f\fR
start in foreground
.TP .TP
\fB\-s\fR <FILE> \fB\-s\fR <FILE>
socket path socket path
@ -26,17 +19,45 @@ socket path
\fB\-p\fR <FILE> \fB\-p\fR <FILE>
pidfile path pidfile path
.TP .TP
\fB\-\-loglevel\fR <LEVEL>
logging level
.HP
\fB\-\-logtarget\fR <FILE>|STDOUT|STDERR|SYSLOG
.HP
\fB\-\-syslogsocket\fR auto|<FILE>
.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 \fB\-x\fR
force execution of the server (remove socket file) force execution of the server (remove socket file)
.TP .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 \fB\-h\fR, \fB\-\-help\fR
display this help message display this help message
.TP .TP
\fB\-V\fR, \fB\-\-version\fR \fB\-V\fR, \fB\-\-version\fR
print the version print the version
.SH AUTHOR
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
.SH "REPORTING BUGS" .SH "REPORTING BUGS"
Report bugs to https://github.com/fail2ban/fail2ban/issues Report bugs to https://github.com/fail2ban/fail2ban/issues
.SH COPYRIGHT .SH COPYRIGHT

View File

@ -1,10 +1,10 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.2. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
.TH FAIL2BAN-TESTCASES "1" "March 2016" "fail2ban-testcases 0.9.4" "User Commands" .TH FAIL2BAN-TESTCASES "1" "July 2016" "fail2ban-testcases 0.10.0a1" "User Commands"
.SH NAME .SH NAME
fail2ban-testcases \- run Fail2Ban unit-tests fail2ban-testcases \- run Fail2Ban unit-tests
.SH SYNOPSIS .SH SYNOPSIS
.B fail2ban-testcases .B fail2ban-testcases
[\fI\,OPTIONS\/\fR] [\fI\,regexps\/\fR] [\fIOPTIONS\fR] [\fIregexps\fR]
.SH DESCRIPTION .SH DESCRIPTION
Script to run Fail2Ban tests battery Script to run Fail2Ban tests battery
.SH OPTIONS .SH OPTIONS
@ -15,12 +15,26 @@ show program's version number and exit
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
show this help message and exit show this help message and exit
.TP .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 Log level for the logger to use during running tests
.TP .TP
\fB\-n\fR, \fB\-\-no\-network\fR \fB\-n\fR, \fB\-\-no\-network\fR
Do not run tests that require the network Do not run tests that require the network
.TP .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 \fB\-t\fR, \fB\-\-log\-traceback\fR
Enrich log\-messages with compressed tracebacks Enrich log\-messages with compressed tracebacks
.TP .TP