mirror of https://github.com/fail2ban/fail2ban
Merge branch 'master' into 0.9
* master: (26 commits) DOC: added a note that coverage script is python-coverage on Debian systems Fixed typos Added additional Transmitter tests, and some associated fixes TODO: test filters/examples files add corresponding ChangeLog entry do catch all exception ENH: typo + head -1 has been deprecated for 10+ years. ENH: add help command BF: allow more than single word for command action[start,stop,ban,unban,check] and for setcinfo too BF: general Exception catch was excessive. Only IOError and OSError are possible and has different meanings Add development documentation and framework for code coverage measurement FSF address changes missing from previous refresh generated manpages (since 0.8.2 state) Downgrade log rotation detection message to DEBUG level from INFO. Closes: gh-129 BF: do not shutdown logging until all jails stop -- so move into Server.quit() BF: safeguard closing of log handlers + close in reverse order Added transmitter get cinfo option for action Fix for missing value in transmitter delaction Rewrite and enable server testcase for Transmitter ENH: adding more of diagnostic messages into -client while starting the daemon ...pull/165/merge
commit
abd56dbedf
|
@ -0,0 +1,4 @@
|
|||
|
||||
[run]
|
||||
branch = True
|
||||
omit = /usr*
|
|
@ -1,3 +1,6 @@
|
|||
*~
|
||||
build
|
||||
dist
|
||||
*.pyc
|
||||
htmlcov
|
||||
.coverage
|
||||
|
|
125
DEVELOP
125
DEVELOP
|
@ -24,14 +24,97 @@ Request feature. You can find more details on the Fail2Ban wiki
|
|||
Testing
|
||||
=======
|
||||
|
||||
Existing tests can be run by executing `fail2ban-testcases`.
|
||||
Existing tests can be run by executing `fail2ban-testcases`. This has options
|
||||
like --log-level that will probably be useful. `fail2ban-testcases --help` for
|
||||
full options.
|
||||
|
||||
Test cases should cover all usual cases, all exception cases and all inside
|
||||
/ outside boundary conditions.
|
||||
|
||||
Test cases should cover all branches. The coverage tool will help identify
|
||||
missing branches. Also see http://nedbatchelder.com/code/coverage/branch.html
|
||||
for more details.
|
||||
|
||||
Install the package python-coverage to visualise your test coverage. Run the
|
||||
following (note: on Debian-based systems, the script is called
|
||||
`python-coverage`):
|
||||
|
||||
coverage run fail2ban-testcases
|
||||
coverage html
|
||||
|
||||
Then look at htmlcov/index.html and see how much coverage your test cases
|
||||
exert over the codebase. Full coverage is a good thing however it may not be
|
||||
complete. Try to ensure tests cover as many independant paths through the
|
||||
code.
|
||||
|
||||
Manual Execution. To run in a development environment do:
|
||||
|
||||
./fail2ban-client -c config/ -s /tmp/f2b.sock -i start
|
||||
|
||||
some quick commands:
|
||||
|
||||
status
|
||||
add test pyinotify
|
||||
status test
|
||||
set test addaction iptables
|
||||
set test actionban iptables echo <ip> <cidr> >> /tmp/ban
|
||||
set test actionunban iptables echo <ip> <cidr> >> /tmp/unban
|
||||
get test actionban iptables
|
||||
get test actionunban iptables
|
||||
set test banip 192.168.2.2
|
||||
status test
|
||||
|
||||
|
||||
Documentation about creating tests (when tests are required and some guidelines
|
||||
for creating good tests) will be added soon.
|
||||
|
||||
Coding Standards
|
||||
================
|
||||
Coming Soon.
|
||||
================
|
||||
|
||||
Style
|
||||
-----
|
||||
|
||||
Please use tabs for now. Keep to 80 columns, at least for readable text.
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
Add tests. They should test all the code you add in a meaning way.
|
||||
|
||||
Coverage
|
||||
--------
|
||||
|
||||
Test coverage should always increase as you add code.
|
||||
|
||||
You may use "# pragma: no cover" in the code for branches of code that support
|
||||
older versions on python. For all other uses of "pragma: no cover" or
|
||||
"pragma: no branch" document the reason why its not covered. "I haven't written
|
||||
a test case" isn't a sufficient reason.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Ensure this documentation is up to date after changes. Also ensure that the man
|
||||
pages still are accurate. Ensure that there is sufficient documentation for
|
||||
your new features to be used.
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
Remove them and don't add any more.
|
||||
|
||||
Git
|
||||
---
|
||||
|
||||
Use the following tags in your commit messages:
|
||||
|
||||
'ENH:' for enhancements
|
||||
'BF:' for bug fixes
|
||||
'DOC:' for documentation fixes
|
||||
|
||||
Adding Actions
|
||||
--------------
|
||||
|
||||
If you add an action.d/*.conf file also add a example in config/jail.conf
|
||||
with enabled=false and maxretry=5 for ssh.
|
||||
|
||||
|
||||
Design
|
||||
|
@ -127,12 +210,14 @@ FileContainer
|
|||
.__pos
|
||||
Keeps the position pointer
|
||||
|
||||
|
||||
dnsutils.py
|
||||
~~~~~~~~~~~
|
||||
|
||||
DNSUtils
|
||||
|
||||
Utility class for DNS and IP handling
|
||||
|
||||
RF-Note: convert to functions within a separate submodule
|
||||
|
||||
|
||||
filter*.py
|
||||
~~~~~~~~~~
|
||||
|
@ -156,3 +241,29 @@ action.py
|
|||
~~~~~~~~~
|
||||
|
||||
Takes care about executing start/check/ban/unban/stop commands
|
||||
|
||||
|
||||
Releasing
|
||||
=========
|
||||
|
||||
Ensure the version is correct in ./common/version.py
|
||||
|
||||
Add/finalize the corresponding entry in the ChangeLog
|
||||
|
||||
# update man pages
|
||||
(cd man ; ./generate-man )
|
||||
|
||||
git commit -m 'update man pages for release' man/*
|
||||
|
||||
python setup.py check
|
||||
python setup.py sdist
|
||||
python setup.py bdist_rpm
|
||||
python setup.py upload
|
||||
|
||||
Run the following and update the wiki with output:
|
||||
|
||||
python -c 'import common.protocol; common.protocol.printWiki()'
|
||||
|
||||
email users and development list of release
|
||||
|
||||
TODO notifying distributors etc.
|
||||
|
|
2
MANIFEST
2
MANIFEST
|
@ -3,6 +3,8 @@ ChangeLog
|
|||
TODO
|
||||
THANKS
|
||||
COPYING
|
||||
DEVELOP
|
||||
doc/run-rootless.txt
|
||||
fail2ban-client
|
||||
fail2ban-server
|
||||
fail2ban-testcases
|
||||
|
|
4
README
4
README
|
@ -91,5 +91,5 @@ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Fail2Ban; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
|
||||
Suite 330, Boston, MA 02111-1307 USA
|
||||
Fail2Ban; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||
Street, Fifth Floor, Boston, MA 02110, USA
|
||||
|
|
3
TODO
3
TODO
|
@ -13,6 +13,9 @@ Legend:
|
|||
# partially done
|
||||
* done
|
||||
|
||||
- Run tests though all filters/examples files - (see sshd example file) as unit
|
||||
test
|
||||
|
||||
- Removed relative imports
|
||||
|
||||
- Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/
|
||||
|
|
|
@ -42,7 +42,8 @@ class Fail2banReader(ConfigReader):
|
|||
ConfigReader.read(self, "fail2ban")
|
||||
|
||||
def getEarlyOptions(self):
|
||||
opts = [["string", "socket", "/tmp/fail2ban.sock"]]
|
||||
opts = [["string", "socket", "/tmp/fail2ban.sock"],
|
||||
["string", "pidfile", "/var/run/fail2ban/fail2ban.pid"]]
|
||||
return ConfigReader.getOptions(self, "Definition", opts)
|
||||
|
||||
def getOptions(self):
|
||||
|
|
|
@ -40,6 +40,7 @@ protocol = [
|
|||
["stop", "stops all jails and terminate the server"],
|
||||
["status", "gets the current status of the server"],
|
||||
["ping", "tests if the server is alive"],
|
||||
["help", "return this output"],
|
||||
['', "LOGGING", ""],
|
||||
["set loglevel <LEVEL>", "sets logging level to <LEVEL>. 0 is minimal, 4 is debug"],
|
||||
["get loglevel", "gets the logging level"],
|
||||
|
@ -92,6 +93,7 @@ protocol = [
|
|||
["get <JAIL> actioncheck <ACT>", "gets the check command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> actionban <ACT>", "gets the ban command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> actionunban <ACT>", "gets the unban command for the action <ACT> for <JAIL>"],
|
||||
["get <JAIL> cinfo <ACT> <KEY>", "gets the value for <KEY> for the action <ACT> for <JAIL>"],
|
||||
]
|
||||
|
||||
##
|
||||
|
|
|
@ -116,7 +116,7 @@ actionunban = if [ -f <tmpfile>.first ]; then
|
|||
port = ???
|
||||
|
||||
# Option: userid
|
||||
# Notes.: Your DSheild user ID. Should be provided either in the jail config or
|
||||
# Notes.: Your DShield user ID. Should be provided either in the jail config or
|
||||
# in a .local file.
|
||||
# Register at https://secure.dshield.org/register.html
|
||||
# Values: [ NUM ] Default: 0
|
||||
|
@ -124,13 +124,13 @@ port = ???
|
|||
userid = 0
|
||||
|
||||
# Option: myip
|
||||
# Notes.: TThe target IP for the attack (your public IP). Should be provided
|
||||
# Notes.: The target IP for the attack (your public IP). Should be provided
|
||||
# either in the jail config or in a .local file unless your PUBLIC IP
|
||||
# is the first IP assigned to eth0
|
||||
# Values: [ an IP address ] Default: Tries to find the IP address of eth0,
|
||||
# which in most cases will be a private IP, and therefore incorrect
|
||||
#
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -n 1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: The protocol over which the attack is happening
|
||||
|
|
|
@ -53,7 +53,7 @@ actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ actionunban = ipset --test fail2ban-<name> <ip> && ipset --del fail2ban-<name> <
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the ipset
|
||||
# Default name of the ipset
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ actionunban = ipset del fail2ban-<name> <ip> -exist
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the ipset
|
||||
# Default name of the ipset
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ actionunban = iptables -D fail2ban-<name> -s <ip> -j fail2ban-<name>-log
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD
|
||||
#
|
||||
# Changing iptables rules requires root priviledges. If fail2ban is
|
||||
# Changing iptables rules requires root privileges. If fail2ban is
|
||||
# configured to run as root, firewall setup can be performed by
|
||||
# fail2ban automatically. However, if fail2ban is configured to run as
|
||||
# a normal user, the configuration must be done by some other means
|
||||
|
@ -65,7 +65,7 @@ actionunban = echo -<ip> > /proc/net/xt_recent/fail2ban-<name>
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ actionunban =
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ actionunban =
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ actionunban =
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -102,13 +102,13 @@ mnwlogin =
|
|||
mnwpass =
|
||||
|
||||
# Option: myip
|
||||
# Notes.: TThe target IP for the attack (your public IP). Should be overridden
|
||||
# Notes.: The target IP for the attack (your public IP). Should be overridden
|
||||
# either in the jail config or in a .local file unless your PUBLIC IP
|
||||
# is the first IP assigned to eth0
|
||||
# Values: [ an IP address ] Default: Tries to find the IP address of eth0,
|
||||
# which in most cases will be a private IP, and therefore incorrect
|
||||
#
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
myip = `ip -4 addr show dev eth0 | grep inet | head -n 1 | sed -r 's/.*inet ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*/\1/'`
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: The protocol over which the attack is happening
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Michael Gebetsroither
|
||||
#
|
||||
# This is for blocking whole hosts through blackhole routes.
|
||||
#
|
||||
# PRO:
|
||||
# - Works on all kernel versions and as no compatibility problems (back to debian lenny and WAY further).
|
||||
# - It's FAST for very large numbers of blocked ips.
|
||||
# - It's FAST because it Blocks traffic before it enters common iptables chains used for filtering.
|
||||
# - It's per host, ideal as action against ssh password bruteforcing to block further attack attempts.
|
||||
# - No additional software required beside iproute/iproute2
|
||||
#
|
||||
# CON:
|
||||
# - Blocking is per IP and NOT per service, but ideal as action against ssh password bruteforcing hosts
|
||||
|
||||
[Definition]
|
||||
actionban = ip route add <type> <ip>
|
||||
actionunban = ip route del <type> <ip>
|
||||
|
||||
# Type of blocking
|
||||
#
|
||||
# Type can be blackhole, unreachable and prohibit. Unreachable and prohibit correspond to the ICMP reject messages.
|
||||
|
||||
type = blackhole
|
|
@ -83,7 +83,7 @@ actionunban =
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ actionban = printf %%b "Subject: [Fail2Ban] <name>: banned <ip>
|
|||
Here are more information about <ip>:\n
|
||||
`/usr/bin/whois <ip>`\n\n
|
||||
Lines containing IP:<ip> in <logpath>\n
|
||||
`/bin/grep '\<<ip>\>' <logpath>`\n\n
|
||||
`grep '\<<ip>\>' <logpath>`\n\n
|
||||
Regards,\n
|
||||
Fail2Ban" | /usr/sbin/sendmail -f <sender> <dest>
|
||||
|
||||
|
@ -73,7 +73,7 @@ actionunban =
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ actionunban =
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ actionunban =
|
|||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
# Default name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
|
|
|
@ -40,3 +40,10 @@ logtarget = /var/log/fail2ban.log
|
|||
#
|
||||
socket = /var/run/fail2ban/fail2ban.sock
|
||||
|
||||
# Option: pidfile
|
||||
# Notes.: Set the PID file. This is used to store the process ID of the
|
||||
# fail2ban server.
|
||||
# Values: FILE Default: /var/run/fail2ban/fail2ban.sock
|
||||
#
|
||||
pidfile = /var/run/fail2ban/fail2ban.pid
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ badbots = atSpider/1\.0|autoemailspider|China Local Browse 2\.6|ContentSmartz|Da
|
|||
# Option: failregex
|
||||
# Notes.: Regexp to catch known spambots and software alike. Please verify
|
||||
# that it is your intent to block IPs which were driven by
|
||||
# abovementioned bots.
|
||||
# above mentioned bots.
|
||||
# Values: TEXT
|
||||
#
|
||||
failregex = ^<HOST> -.*"(GET|POST).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$
|
||||
|
|
|
@ -101,6 +101,17 @@ action = hostsdeny
|
|||
ignoreregex = for myuser from
|
||||
logpath = /var/log/sshd.log
|
||||
|
||||
# Here we use blackhole routes for not requiring any additional kernel support
|
||||
# to store large volumes of banned IPs
|
||||
|
||||
[ssh-route]
|
||||
|
||||
enabled = false
|
||||
filter = sshd
|
||||
action = route
|
||||
logpath = /var/log/sshd.log
|
||||
maxretry = 5
|
||||
|
||||
# Here we use a combination of Netfilter/Iptables and IPsets
|
||||
# for storing large volumes of banned IPs
|
||||
#
|
||||
|
@ -230,7 +241,7 @@ logpath = /var/log/lighttpd/error.log
|
|||
maxretry = 2
|
||||
|
||||
# Same as above for mod_auth
|
||||
# It catches wrong authentifications
|
||||
# It catches wrong authentications
|
||||
|
||||
[lighttpd-auth]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Fail2ban normally requires root priviledges to insert iptables rules
|
||||
Fail2ban normally requires root privileges to insert iptables rules
|
||||
through calls to /sbin/iptables and also to read the logfiles.
|
||||
Fail2ban can run as an unpriviledged user provided that those two
|
||||
capabilites are preserved. The idea is to run fail2ban as a normal
|
||||
Fail2ban can run as an unprivileged user provided that those two
|
||||
capabilities are preserved. The idea is to run fail2ban as a normal
|
||||
user (e.g. fail2ban) who belongs to a group which is allowed to read
|
||||
logfiles. The user should also be allowed to write to
|
||||
/proc/net/xt_recent/fail2ban-<name> (name is specified in the iptables
|
||||
|
@ -20,14 +20,14 @@ Another way to use xt_recent is by inserting the rules by writing to
|
|||
action. Files in /proc/net/xt_recent/ are protected by normal
|
||||
filesystem rules, so can be chown'ed and chmod'ed to be writable by a
|
||||
certain user. After the necessary iptables rules are inserted (which
|
||||
requires root priviledges), blacklisting can be perfomed by an
|
||||
unpriviledged user.
|
||||
requires root privileges), blacklisting can be performed by an
|
||||
unprivileged user.
|
||||
|
||||
Using fail2ban with xt_recent allows smarter filtering than normal
|
||||
iptables rules with the xt_recent module can provide.
|
||||
|
||||
The disadvantage is that fail2ban cannot perform the setup by itself,
|
||||
which would require the priviledge to call /sbin/iptables, and it must
|
||||
which would require the privilege to call /sbin/iptables, and it must
|
||||
be done through other means.
|
||||
|
||||
The primary advantage is obvious: it's generally better to run
|
||||
|
@ -46,7 +46,7 @@ some user and thus allow delisting IPs by helper administrators
|
|||
without the ability to mess up other iptables rules.
|
||||
|
||||
The xt_recent-echo jail can be used under the root user without
|
||||
further configuration. To run not as root, futher setup is necessary:
|
||||
further configuration. To run not as root, further setup is necessary:
|
||||
|
||||
- Create user:
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ class Fail2banClient:
|
|||
self.__conf["verbose"] = 1
|
||||
self.__conf["interactive"] = False
|
||||
self.__conf["socket"] = None
|
||||
self.__conf["pidfile"] = None
|
||||
|
||||
def dispVersion(self):
|
||||
print "Fail2Ban v" + version
|
||||
|
@ -84,6 +85,7 @@ class Fail2banClient:
|
|||
print "Options:"
|
||||
print " -c <DIR> configuration directory"
|
||||
print " -s <FILE> socket path"
|
||||
print " -p <FILE> pidfile path"
|
||||
print " -d dump configuration. For debugging"
|
||||
print " -i interactive mode"
|
||||
print " -v increase verbosity"
|
||||
|
@ -119,6 +121,8 @@ class Fail2banClient:
|
|||
self.__conf["conf"] = opt[1]
|
||||
elif opt[0] == "-s":
|
||||
self.__conf["socket"] = opt[1]
|
||||
elif opt[0] == "-p":
|
||||
self.__conf["pidfile"] = opt[1]
|
||||
elif opt[0] == "-d":
|
||||
self.__conf["dump"] = True
|
||||
elif opt[0] == "-v":
|
||||
|
@ -130,11 +134,11 @@ class Fail2banClient:
|
|||
elif opt[0] == "-i":
|
||||
self.__conf["interactive"] = True
|
||||
elif opt[0] in ["-h", "--help"]:
|
||||
self.dispUsage()
|
||||
sys.exit(0)
|
||||
elif opt[0] in ["-V", "--version"]:
|
||||
self.dispVersion()
|
||||
sys.exit(0)
|
||||
self.dispUsage()
|
||||
sys.exit(0)
|
||||
elif opt[0] in ["-V", "--version"]:
|
||||
self.dispVersion()
|
||||
sys.exit(0)
|
||||
|
||||
def __ping(self):
|
||||
return self.__processCmd([["ping"]], False)
|
||||
|
@ -181,8 +185,21 @@ class Fail2banClient:
|
|||
# Do not continue if configuration is not 100% valid
|
||||
if not ret:
|
||||
return False
|
||||
# verify that directory for the socket file exists
|
||||
socket_dir = os.path.dirname(self.__conf["socket"])
|
||||
if not os.path.exists(socket_dir):
|
||||
logSys.error(
|
||||
"There is no directory %s to contain the socket file %s."
|
||||
% (socket_dir, self.__conf["socket"]))
|
||||
return False
|
||||
if not os.access(socket_dir, os.W_OK | os.X_OK):
|
||||
logSys.error(
|
||||
"Directory %s exists but not accessible for writing"
|
||||
% (socket_dir,))
|
||||
return False
|
||||
# Start the server
|
||||
self.__startServerAsync(self.__conf["socket"],
|
||||
self.__conf["pidfile"],
|
||||
self.__conf["force"])
|
||||
try:
|
||||
# Wait for the server to start
|
||||
|
@ -191,10 +208,10 @@ class Fail2banClient:
|
|||
self.__processCmd(self.__stream, False)
|
||||
return True
|
||||
except ServerExecutionException:
|
||||
logSys.error("Could not start server. Maybe an old " +
|
||||
"socket file is still present. Try to " +
|
||||
"remove " + self.__conf["socket"] + ". If " +
|
||||
"you used fail2ban-client to start the " +
|
||||
logSys.error("Could not start server. Maybe an old "
|
||||
"socket file is still present. Try to "
|
||||
"remove " + self.__conf["socket"] + ". If "
|
||||
"you used fail2ban-client to start the "
|
||||
"server, adding the -x option will do it")
|
||||
return False
|
||||
elif len(cmd) == 1 and cmd[0] == "reload":
|
||||
|
@ -231,7 +248,7 @@ class Fail2banClient:
|
|||
#
|
||||
# Start the Fail2ban server in daemon mode.
|
||||
|
||||
def __startServerAsync(self, socket, force = False):
|
||||
def __startServerAsync(self, socket, pidfile, force = False):
|
||||
# Forks the current process.
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
|
@ -242,22 +259,26 @@ class Fail2banClient:
|
|||
# Set the socket path.
|
||||
args.append("-s")
|
||||
args.append(socket)
|
||||
# Set the pidfile
|
||||
args.append("-p")
|
||||
args.append(pidfile)
|
||||
# Force the execution if needed.
|
||||
if force:
|
||||
args.append("-x")
|
||||
try:
|
||||
# Use the current directory.
|
||||
exe = os.path.abspath(os.path.join(sys.path[0], self.SERVER))
|
||||
logSys.debug("Starting %r with args %r" % (exe, args))
|
||||
os.execv(exe, args)
|
||||
except OSError:
|
||||
try:
|
||||
# Use the PATH env.
|
||||
logSys.warning("Initial start attempt failed. Starting %r with the same args" % (self.SERVER,))
|
||||
os.execvp(self.SERVER, args)
|
||||
except OSError:
|
||||
print "Could not find %s" % self.SERVER
|
||||
logSys.error("Could not start %s" % self.SERVER)
|
||||
os.exit(-1)
|
||||
|
||||
|
||||
|
||||
def __waitOnServer(self):
|
||||
# Wait for the server to start
|
||||
cnt = 0
|
||||
|
@ -276,7 +297,7 @@ class Fail2banClient:
|
|||
delta = -1
|
||||
elif pos < 2:
|
||||
delta = 1
|
||||
# The server has 30 secondes to start.
|
||||
# The server has 30 seconds to start.
|
||||
if cnt >= 300:
|
||||
if self.__conf["verbose"] > 1:
|
||||
sys.stdout.write('\n')
|
||||
|
@ -297,7 +318,7 @@ class Fail2banClient:
|
|||
|
||||
# Reads the command line options.
|
||||
try:
|
||||
cmdOpts = 'hc:s:xdviqV'
|
||||
cmdOpts = 'hc:s:p:xdviqV'
|
||||
cmdLongOpts = ['help', 'version']
|
||||
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
|
||||
except getopt.GetoptError:
|
||||
|
@ -328,9 +349,11 @@ class Fail2banClient:
|
|||
|
||||
# Set socket path
|
||||
self.__configurator.readEarly()
|
||||
socket = self.__configurator.getEarlyOptions()
|
||||
conf = self.__configurator.getEarlyOptions()
|
||||
if self.__conf["socket"] == None:
|
||||
self.__conf["socket"] = socket["socket"]
|
||||
self.__conf["socket"] = conf["socket"]
|
||||
if self.__conf["pidfile"] == None:
|
||||
self.__conf["pidfile"] = conf["pidfile"]
|
||||
logSys.info("Using socket file " + self.__conf["socket"])
|
||||
|
||||
if self.__conf["dump"]:
|
||||
|
@ -357,7 +380,9 @@ class Fail2banClient:
|
|||
if cmd == "exit" or cmd == "quit":
|
||||
# Exit
|
||||
return True
|
||||
if not cmd == "":
|
||||
if cmd == "help":
|
||||
self.dispUsage()
|
||||
elif not cmd == "":
|
||||
self.__processCommand(shlex.split(cmd))
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print
|
||||
|
@ -395,7 +420,7 @@ class Fail2banClient:
|
|||
class ServerExecutionException(Exception):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == "__main__": # pragma: no cover - can't test main
|
||||
client = Fail2banClient()
|
||||
# Exit with correct return value
|
||||
if client.start(sys.argv):
|
||||
|
|
|
@ -54,6 +54,7 @@ class Fail2banServer:
|
|||
self.__conf["background"] = True
|
||||
self.__conf["force"] = False
|
||||
self.__conf["socket"] = "/var/run/fail2ban/fail2ban.sock"
|
||||
self.__conf["pidfile"] = "/var/run/fail2ban/fail2ban.pid"
|
||||
|
||||
def dispVersion(self):
|
||||
print "Fail2Ban v" + version
|
||||
|
@ -81,6 +82,7 @@ class Fail2banServer:
|
|||
print " -b start in background"
|
||||
print " -f start in foreground"
|
||||
print " -s <FILE> socket path"
|
||||
print " -p <FILE> pidfile path"
|
||||
print " -x force execution of the server (remove socket file)"
|
||||
print " -h, --help display this help message"
|
||||
print " -V, --version print the version"
|
||||
|
@ -97,6 +99,8 @@ class Fail2banServer:
|
|||
self.__conf["background"] = False
|
||||
if opt[0] == "-s":
|
||||
self.__conf["socket"] = opt[1]
|
||||
if opt[0] == "-p":
|
||||
self.__conf["pidfile"] = opt[1]
|
||||
if opt[0] == "-x":
|
||||
self.__conf["force"] = True
|
||||
if opt[0] in ["-h", "--help"]:
|
||||
|
@ -112,7 +116,7 @@ class Fail2banServer:
|
|||
|
||||
# Reads the command line options.
|
||||
try:
|
||||
cmdOpts = 'bfs:xhV'
|
||||
cmdOpts = 'bfs:p:xhV'
|
||||
cmdLongOpts = ['help', 'version']
|
||||
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
|
||||
except getopt.GetoptError:
|
||||
|
@ -123,7 +127,9 @@ class Fail2banServer:
|
|||
|
||||
try:
|
||||
self.__server = Server(self.__conf["background"])
|
||||
self.__server.start(self.__conf["socket"], self.__conf["force"])
|
||||
self.__server.start(self.__conf["socket"],
|
||||
self.__conf["pidfile"],
|
||||
self.__conf["force"])
|
||||
return True
|
||||
except Exception, e:
|
||||
logSys.exception(e)
|
||||
|
|
|
@ -77,10 +77,10 @@ verbosity = {'debug': 3,
|
|||
'fatal': 0,
|
||||
None: 1}[opts.log_level]
|
||||
|
||||
if opts.log_level is not None:
|
||||
if opts.log_level is not None: # pragma: no cover
|
||||
# so we had explicit settings
|
||||
logSys.setLevel(getattr(logging, opts.log_level.upper()))
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
# suppress the logging but it would leave unittests' progress dots
|
||||
# ticking, unless like with '-l fatal' which would be silent
|
||||
# unless error occurs
|
||||
|
@ -89,9 +89,9 @@ else:
|
|||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
# Custom log format for the verbose tests runs
|
||||
if verbosity > 1:
|
||||
if verbosity > 1: # pragma: no cover
|
||||
stdout.setFormatter(logging.Formatter(' %(asctime)-15s %(thread)s %(message)s'))
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
# just prefix with the space
|
||||
stdout.setFormatter(logging.Formatter(' %(message)s'))
|
||||
logSys.addHandler(stdout)
|
||||
|
@ -99,7 +99,7 @@ logSys.addHandler(stdout)
|
|||
#
|
||||
# Let know the version
|
||||
#
|
||||
if not opts.log_level or opts.log_level != 'fatal':
|
||||
if not opts.log_level or opts.log_level != 'fatal': # pragma: no cover
|
||||
print "Fail2ban %s test suite. Python %s. Please wait..." \
|
||||
% (version, str(sys.version).replace('\n', ''))
|
||||
|
||||
|
@ -107,9 +107,9 @@ if not opts.log_level or opts.log_level != 'fatal':
|
|||
#
|
||||
# Gather the tests
|
||||
#
|
||||
if not len(regexps):
|
||||
if not len(regexps): # pragma: no cover
|
||||
tests = unittest.TestSuite()
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
import re
|
||||
class FilteredTestSuite(unittest.TestSuite):
|
||||
_regexps = [re.compile(r) for r in regexps]
|
||||
|
@ -124,7 +124,7 @@ else:
|
|||
|
||||
# Server
|
||||
#tests.addTest(unittest.makeSuite(servertestcase.StartStop))
|
||||
#tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
|
||||
tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
|
||||
tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
|
||||
# FailManager
|
||||
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
||||
|
@ -160,13 +160,13 @@ filters = [FilterPoll] # always available
|
|||
try:
|
||||
from server.filtergamin import FilterGamin
|
||||
filters.append(FilterGamin)
|
||||
except Exception, e:
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping gamin backend testing. Got exception '%s'" % e
|
||||
|
||||
try:
|
||||
from server.filterpyinotify import FilterPyinotify
|
||||
filters.append(FilterPyinotify)
|
||||
except Exception, e:
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping pyinotify backend testing. Got exception '%s'" % e
|
||||
|
||||
for Filter_ in filters:
|
||||
|
@ -190,7 +190,7 @@ try:
|
|||
|
||||
tests_results = testRunner.run(tests)
|
||||
|
||||
finally:
|
||||
finally: # pragma: no cover
|
||||
# Just for the sake of it reset the TZ
|
||||
# yoh: move all this into setup/teardown methods within tests
|
||||
os.environ.pop('TZ')
|
||||
|
@ -198,5 +198,5 @@ finally:
|
|||
os.environ['TZ'] = old_TZ
|
||||
time.tzset()
|
||||
|
||||
if not tests_results.wasSuccessful():
|
||||
if not tests_results.wasSuccessful(): # pragma: no cover
|
||||
sys.exit(1)
|
||||
|
|
|
@ -49,5 +49,5 @@ details.
|
|||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with Fail2Ban; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
Boston, MA 02111-1307 USA
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110, USA
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||
.TH FAIL2BAN-CLIENT "1" "March 2008" "fail2ban-client v0.8.2" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
|
||||
.TH FAIL2BAN-CLIENT "1" "March 2013" "fail2ban-client v0.8.8" "User Commands"
|
||||
.SH NAME
|
||||
fail2ban-client \- configure and control the server
|
||||
.SH SYNOPSIS
|
||||
.B fail2ban-client
|
||||
[\fIOPTIONS\fR] \fI<COMMAND>\fR
|
||||
.SH DESCRIPTION
|
||||
Fail2Ban v0.8.2 reads log file that contains password failure report
|
||||
Fail2Ban v0.8.8 reads log file that contains password failure report
|
||||
and bans the corresponding IP addresses using firewall rules.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
|
@ -16,6 +16,9 @@ configuration directory
|
|||
\fB\-s\fR <FILE>
|
||||
socket path
|
||||
.TP
|
||||
\fB\-p\fR <FILE>
|
||||
pidfile path
|
||||
.TP
|
||||
\fB\-d\fR
|
||||
dump configuration. For debugging
|
||||
.TP
|
||||
|
@ -110,7 +113,7 @@ adds <FILE> to the monitoring list
|
|||
of <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> dellogpath <FILE>\fR
|
||||
removes <FILE> to the monitoring
|
||||
removes <FILE> from the monitoring
|
||||
list of <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> addfailregex <REGEX>\fR
|
||||
|
@ -140,6 +143,15 @@ back for <JAIL>
|
|||
sets the number of seconds <TIME>
|
||||
a host will be banned for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> usedns <VALUE>\fR
|
||||
sets the usedns mode for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> banip <IP>\fR
|
||||
manually Ban <IP> for <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> unbanip <IP>\fR
|
||||
manually Unban <IP> in <JAIL>
|
||||
.TP
|
||||
\fBset <JAIL> maxretry <RETRY>\fR
|
||||
sets the number of failures
|
||||
<RETRY> before banning the host
|
||||
|
@ -191,14 +203,6 @@ files for <JAIL>
|
|||
gets the list of ignored IP
|
||||
addresses for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> timeregex\fR
|
||||
gets the regular expression used
|
||||
for the time detection for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> timepattern\fR
|
||||
gets the pattern used for the time
|
||||
detection for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> failregex\fR
|
||||
gets the list of regular
|
||||
expressions which matches the
|
||||
|
@ -218,6 +222,9 @@ will look back for failures for
|
|||
gets the time a host is banned for
|
||||
<JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> usedns\fR
|
||||
gets the usedns setting for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> maxretry\fR
|
||||
gets the number of failures
|
||||
allowed for <JAIL>
|
||||
|
@ -245,15 +252,19 @@ action <ACT> for <JAIL>
|
|||
\fBget <JAIL> actionunban <ACT>\fR
|
||||
gets the unban command for the
|
||||
action <ACT> for <JAIL>
|
||||
.TP
|
||||
\fBget <JAIL> cinfo <ACT> <KEY>\fR
|
||||
gets the value for <KEY> for the
|
||||
action <ACT> for <JAIL>
|
||||
.SH FILES
|
||||
\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"
|
||||
Report bugs on https://github.com/fail2ban/fail2ban/issues
|
||||
Report bugs to https://github.com/fail2ban/fail2ban/issues
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2004-2008 Cyril Jaquier
|
||||
Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
|
||||
.br
|
||||
Copyright of modifications held by their respective authors.
|
||||
Licensed under the GNU General Public License v2 (GPL).
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||
.TH FAIL2BAN-REGEX "1" "March 2008" "fail2ban-regex v0.8.2" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
|
||||
.TH FAIL2BAN-REGEX "1" "March 2013" "fail2ban-regex v0.8.8" "User Commands"
|
||||
.SH NAME
|
||||
fail2ban-regex \- test Fail2ban "failregex" option
|
||||
.SH SYNOPSIS
|
||||
.B fail2ban-regex
|
||||
[\fIOPTIONS\fR] \fI<LOG> <REGEX> \fR[\fIIGNOREREGEX\fR]
|
||||
.SH DESCRIPTION
|
||||
Fail2Ban v0.8.2 reads log file that contains password failure report
|
||||
Fail2Ban v0.8.8 reads log file that contains password failure report
|
||||
and bans the corresponding IP addresses using firewall rules.
|
||||
.PP
|
||||
This tools can test regular expressions for "fail2ban".
|
||||
|
@ -17,6 +17,9 @@ display this help message
|
|||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
print the version
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
verbose output
|
||||
.SH LOG
|
||||
.TP
|
||||
\fBstring\fR
|
||||
|
@ -42,9 +45,9 @@ path to a filter file (filter.d/sshd.conf)
|
|||
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
|
||||
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||
.SH "REPORTING BUGS"
|
||||
Report bugs on https://github.com/fail2ban/fail2ban/issues
|
||||
Report bugs to https://github.com/fail2ban/fail2ban/issues
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2004-2008 Cyril Jaquier
|
||||
Copyright \(co 2004\-2008 Cyril Jaquier
|
||||
.br
|
||||
Copyright of modifications held by their respective authors.
|
||||
Licensed under the GNU General Public License v2 (GPL).
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||
.TH FAIL2BAN-SERVER "1" "March 2008" "fail2ban-server v0.8.2" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.10.
|
||||
.TH FAIL2BAN-SERVER "1" "March 2013" "fail2ban-server v0.8.8" "User Commands"
|
||||
.SH NAME
|
||||
fail2ban-server \- start the server
|
||||
.SH SYNOPSIS
|
||||
.B fail2ban-server
|
||||
[\fIOPTIONS\fR]
|
||||
.SH DESCRIPTION
|
||||
Fail2Ban v0.8.2 reads log file that contains password failure report
|
||||
Fail2Ban v0.8.8 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
|
||||
|
@ -23,6 +23,9 @@ start in foreground
|
|||
\fB\-s\fR <FILE>
|
||||
socket path
|
||||
.TP
|
||||
\fB\-p\fR <FILE>
|
||||
pidfile path
|
||||
.TP
|
||||
\fB\-x\fR
|
||||
force execution of the server (remove socket file)
|
||||
.TP
|
||||
|
@ -35,9 +38,9 @@ print the version
|
|||
Written by Cyril Jaquier <cyril.jaquier@fail2ban.org>.
|
||||
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||
.SH "REPORTING BUGS"
|
||||
Report bugs on https://github.com/fail2ban/fail2ban/issues
|
||||
Report bugs to https://github.com/fail2ban/fail2ban/issues
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2004-2008 Cyril Jaquier
|
||||
Copyright \(co 2004\-2008 Cyril Jaquier, 2008\- Fail2Ban Contributors
|
||||
.br
|
||||
Copyright of modifications held by their respective authors.
|
||||
Licensed under the GNU General Public License v2 (GPL).
|
||||
|
|
|
@ -277,8 +277,8 @@ class Action:
|
|||
# Executes a command with preliminary checks and substitutions.
|
||||
#
|
||||
# Before executing any commands, executes the "check" command first
|
||||
# in order to check if prerequirements are met. If this check fails,
|
||||
# it tries to restore a sane environnement before executing the real
|
||||
# in order to check if pre-requirements are met. If this check fails,
|
||||
# it tries to restore a sane environment before executing the real
|
||||
# command.
|
||||
# Replaces "aInfo" and "cInfo" in the query too.
|
||||
#
|
||||
|
|
|
@ -77,7 +77,8 @@ class Actions(JailThread):
|
|||
for action in self.__actions:
|
||||
if action.getName() == name:
|
||||
self.__actions.remove(action)
|
||||
break
|
||||
return
|
||||
raise KeyError("Invalid Action name: %s" % name)
|
||||
|
||||
##
|
||||
# Returns an action.
|
||||
|
@ -91,7 +92,7 @@ class Actions(JailThread):
|
|||
for action in self.__actions:
|
||||
if action.getName() == name:
|
||||
return action
|
||||
raise KeyError
|
||||
raise KeyError("Invalid Action name")
|
||||
|
||||
##
|
||||
# Returns the last defined action.
|
||||
|
@ -131,7 +132,7 @@ class Actions(JailThread):
|
|||
# Unban the IP.
|
||||
self.__unBan(ticket)
|
||||
return ip
|
||||
return 'None'
|
||||
raise ValueError("IP %s is not banned" % ip)
|
||||
|
||||
##
|
||||
# Main loop.
|
||||
|
|
|
@ -142,7 +142,7 @@ class AsyncServer(asyncore.dispatcher):
|
|||
if sys.version_info >= (2, 6): # if python 2.6 or greater...
|
||||
logSys.debug("Detected Python 2.6 or greater. asyncore.loop() not using poll")
|
||||
asyncore.loop(use_poll = False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
logSys.debug("NOT Python 2.6/3.* - asyncore.loop() using poll")
|
||||
asyncore.loop(use_poll = True)
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ logSys = logging.getLogger("fail2ban.filter")
|
|||
# Log reader class.
|
||||
#
|
||||
# This class reads a log file and detects login failures or anything else
|
||||
# that matches a given regular expression. This class is instanciated by
|
||||
# that matches a given regular expression. This class is instantiated by
|
||||
# a Jail object.
|
||||
|
||||
class Filter(JailThread):
|
||||
|
@ -99,6 +99,7 @@ class Filter(JailThread):
|
|||
self.__failRegex.append(regex)
|
||||
except RegexException, e:
|
||||
logSys.error(e)
|
||||
raise e
|
||||
|
||||
|
||||
def delFailRegex(self, index):
|
||||
|
@ -123,7 +124,7 @@ class Filter(JailThread):
|
|||
# Add the regular expression which matches the failure.
|
||||
#
|
||||
# The regular expression can also match any other pattern than failures
|
||||
# and thus can be used for many purporse.
|
||||
# and thus can be used for many purpose.
|
||||
# @param value the regular expression
|
||||
|
||||
def addIgnoreRegex(self, value):
|
||||
|
@ -132,6 +133,7 @@ class Filter(JailThread):
|
|||
self.__ignoreRegex.append(regex)
|
||||
except RegexException, e:
|
||||
logSys.error(e)
|
||||
raise e
|
||||
|
||||
def delIgnoreRegex(self, index):
|
||||
try:
|
||||
|
@ -234,7 +236,7 @@ class Filter(JailThread):
|
|||
# file has been modified and looks for failures.
|
||||
# @return True when the thread exits nicely
|
||||
|
||||
def run(self):
|
||||
def run(self): # pragma: no cover
|
||||
raise Exception("run() is abstract")
|
||||
|
||||
##
|
||||
|
@ -249,7 +251,7 @@ class Filter(JailThread):
|
|||
self.failManager.addFailure(FailTicket(ip, unixTime))
|
||||
|
||||
# Perform the banning of the IP now.
|
||||
try:
|
||||
try: # pragma: no branch - exception is the only way out
|
||||
while True:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
|
@ -401,7 +403,7 @@ class Filter(JailThread):
|
|||
failList.append([ip, date])
|
||||
# We matched a regex, it is enough to stop.
|
||||
break
|
||||
except RegexException, e:
|
||||
except RegexException, e: # pragma: no cover - unsure if reachable
|
||||
logSys.error(e)
|
||||
return failList
|
||||
|
||||
|
@ -442,7 +444,7 @@ class FileFilter(Filter):
|
|||
|
||||
def _addLogPath(self, path):
|
||||
# nothing to do by default
|
||||
# to be overriden by backends
|
||||
# to be overridden by backends
|
||||
pass
|
||||
|
||||
|
||||
|
@ -461,7 +463,7 @@ class FileFilter(Filter):
|
|||
|
||||
def _delLogPath(self, path):
|
||||
# nothing to do by default
|
||||
# to be overriden by backends
|
||||
# to be overridden by backends
|
||||
pass
|
||||
|
||||
##
|
||||
|
@ -505,10 +507,19 @@ class FileFilter(Filter):
|
|||
# Try to open log file.
|
||||
try:
|
||||
container.open()
|
||||
except Exception, e:
|
||||
# see http://python.org/dev/peps/pep-3151/
|
||||
except IOError, e:
|
||||
logSys.error("Unable to open %s" % filename)
|
||||
logSys.exception(e)
|
||||
return False
|
||||
except OSError, e: # pragma: no cover - requires race condition to tigger this
|
||||
logSys.error("Error opening %s" % filename)
|
||||
logSys.exception(e)
|
||||
return False
|
||||
except OSError, e: # pragma: no cover - Requires implemention error in FileContainer to generate
|
||||
logSys.error("Internal errror in FileContainer open method - please report as a bug to https://github.com/fail2ban/fail2ban/issues")
|
||||
logSys.exception(e)
|
||||
return False
|
||||
|
||||
while True:
|
||||
line = container.readline()
|
||||
|
@ -535,7 +546,7 @@ class FileFilter(Filter):
|
|||
try:
|
||||
import hashlib
|
||||
md5sum = hashlib.md5
|
||||
except ImportError:
|
||||
except ImportError: # pragma: no cover
|
||||
# hashlib was introduced in Python 2.5. For compatibility with those
|
||||
# elderly Pythons, import from md5
|
||||
import md5
|
||||
|
@ -578,7 +589,7 @@ class FileContainer:
|
|||
stats = os.fstat(self.__handler.fileno())
|
||||
# Compare hash and inode
|
||||
if self.__hash != myHash or self.__ino != stats.st_ino:
|
||||
logSys.info("Log rotation detected for %s" % self.__filename)
|
||||
logSys.debug("Log rotation detected for %s" % self.__filename)
|
||||
self.__hash = myHash
|
||||
self.__ino = stats.st_ino
|
||||
self.__pos = 0
|
||||
|
|
|
@ -39,7 +39,7 @@ logSys = logging.getLogger("fail2ban.filter")
|
|||
# Log reader class.
|
||||
#
|
||||
# This class reads a log file and detects login failures or anything else
|
||||
# that matches a given regular expression. This class is instanciated by
|
||||
# that matches a given regular expression. This class is instantiated by
|
||||
# a Jail object.
|
||||
|
||||
class FilterPoll(FileFilter):
|
||||
|
|
|
@ -40,8 +40,6 @@ logSys = logging.getLogger("fail2ban.server")
|
|||
|
||||
class Server:
|
||||
|
||||
PID_FILE = "/var/run/fail2ban/fail2ban.pid"
|
||||
|
||||
def __init__(self, daemon = False):
|
||||
self.__loggingLock = Lock()
|
||||
self.__lock = RLock()
|
||||
|
@ -59,7 +57,7 @@ class Server:
|
|||
logSys.debug("Caught signal %d. Exiting" % signum)
|
||||
self.quit()
|
||||
|
||||
def start(self, sock, force = False):
|
||||
def start(self, sock, pidfile, force = False):
|
||||
logSys.info("Starting Fail2ban v" + version.version)
|
||||
|
||||
# Install signal handlers
|
||||
|
@ -79,8 +77,8 @@ class Server:
|
|||
|
||||
# Creates a PID file.
|
||||
try:
|
||||
logSys.debug("Creating PID file %s" % Server.PID_FILE)
|
||||
pidFile = open(Server.PID_FILE, 'w')
|
||||
logSys.debug("Creating PID file %s" % pidfile)
|
||||
pidFile = open(pidfile, 'w')
|
||||
pidFile.write("%s\n" % os.getpid())
|
||||
pidFile.close()
|
||||
except IOError, e:
|
||||
|
@ -94,17 +92,11 @@ class Server:
|
|||
logSys.error("Could not start server: %s", e)
|
||||
# Removes the PID file.
|
||||
try:
|
||||
logSys.debug("Remove PID file %s" % Server.PID_FILE)
|
||||
os.remove(Server.PID_FILE)
|
||||
logSys.debug("Remove PID file %s" % pidfile)
|
||||
os.remove(pidfile)
|
||||
except OSError, e:
|
||||
logSys.error("Unable to remove PID file: %s" % e)
|
||||
logSys.info("Exiting Fail2ban")
|
||||
# Shutdowns the logging.
|
||||
try:
|
||||
self.__loggingLock.acquire()
|
||||
logging.shutdown()
|
||||
finally:
|
||||
self.__loggingLock.release()
|
||||
|
||||
def quit(self):
|
||||
# Stop communication first because if jail's unban action
|
||||
|
@ -114,8 +106,17 @@ class Server:
|
|||
# are exiting)
|
||||
# See https://github.com/fail2ban/fail2ban/issues/7
|
||||
self.__asyncServer.stop()
|
||||
|
||||
# Now stop all the jails
|
||||
self.stopAllJail()
|
||||
|
||||
# Only now shutdown the logging.
|
||||
try:
|
||||
self.__loggingLock.acquire()
|
||||
logging.shutdown()
|
||||
finally:
|
||||
self.__loggingLock.release()
|
||||
|
||||
|
||||
def addJail(self, name, backend):
|
||||
self.__jails.add(name, backend)
|
||||
|
@ -375,11 +376,20 @@ class Server:
|
|||
logSys.error("Unable to log to " + target)
|
||||
logSys.info("Logging to previous target " + self.__logTarget)
|
||||
return False
|
||||
# Removes previous handlers
|
||||
for handler in logging.getLogger("fail2ban").handlers:
|
||||
# Closes the handler.
|
||||
# Removes previous handlers -- in reverse order since removeHandler
|
||||
# alter the list in-place and that can confuses the iterable
|
||||
for handler in logging.getLogger("fail2ban").handlers[::-1]:
|
||||
# Remove the handler.
|
||||
logging.getLogger("fail2ban").removeHandler(handler)
|
||||
handler.close()
|
||||
# And try to close -- it might be closed already
|
||||
try:
|
||||
handler.flush()
|
||||
handler.close()
|
||||
except ValueError:
|
||||
if sys.version_info >= (2,6):
|
||||
raise
|
||||
# is known to be thrown after logging was shutdown once
|
||||
# with older Pythons -- seems to be safe to ignore there
|
||||
# tell the handler to use this format
|
||||
hdlr.setFormatter(formatter)
|
||||
logging.getLogger("fail2ban").addHandler(hdlr)
|
||||
|
|
|
@ -112,14 +112,18 @@ class Transmitter:
|
|||
return self.__server.getLogLevel()
|
||||
elif name == "logtarget":
|
||||
value = command[1]
|
||||
self.__server.setLogTarget(value)
|
||||
return self.__server.getLogTarget()
|
||||
if self.__server.setLogTarget(value):
|
||||
return self.__server.getLogTarget()
|
||||
else:
|
||||
raise Exception("Failed to change log target")
|
||||
# Jail
|
||||
elif command[1] == "idle":
|
||||
if command[2] == "on":
|
||||
self.__server.setIdleJail(name, True)
|
||||
elif command[2] == "off":
|
||||
self.__server.setIdleJail(name, False)
|
||||
else:
|
||||
raise Exception("Invalid idle option, must be 'yes' or 'no'")
|
||||
return self.__server.getIdleJail(name)
|
||||
# Filter
|
||||
elif command[1] == "addignoreip":
|
||||
|
@ -187,12 +191,13 @@ class Transmitter:
|
|||
self.__server.addAction(name, value)
|
||||
return self.__server.getLastAction(name).getName()
|
||||
elif command[1] == "delaction":
|
||||
value = command[2]
|
||||
self.__server.delAction(name, value)
|
||||
return None
|
||||
elif command[1] == "setcinfo":
|
||||
act = command[2]
|
||||
key = command[3]
|
||||
value = command[4]
|
||||
value = " ".join(command[4:])
|
||||
self.__server.setCInfo(name, act, key, value)
|
||||
return self.__server.getCInfo(name, act, key)
|
||||
elif command[1] == "delcinfo":
|
||||
|
@ -202,27 +207,27 @@ class Transmitter:
|
|||
return None
|
||||
elif command[1] == "actionstart":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionStart(name, act, value)
|
||||
return self.__server.getActionStart(name, act)
|
||||
elif command[1] == "actionstop":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionStop(name, act, value)
|
||||
return self.__server.getActionStop(name, act)
|
||||
elif command[1] == "actioncheck":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionCheck(name, act, value)
|
||||
return self.__server.getActionCheck(name, act)
|
||||
elif command[1] == "actionban":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionBan(name, act, value)
|
||||
return self.__server.getActionBan(name, act)
|
||||
elif command[1] == "actionunban":
|
||||
act = command[2]
|
||||
value = command[3]
|
||||
value = " ".join(command[3:])
|
||||
self.__server.setActionUnban(name, act, value)
|
||||
return self.__server.getActionUnban(name, act)
|
||||
raise Exception("Invalid command (no set action or not yet implemented)")
|
||||
|
@ -271,12 +276,16 @@ class Transmitter:
|
|||
elif command[1] == "actionunban":
|
||||
act = command[2]
|
||||
return self.__server.getActionUnban(name, act)
|
||||
elif command[1] == "cinfo":
|
||||
act = command[2]
|
||||
key = command[3]
|
||||
return self.__server.getCInfo(name, act, key)
|
||||
raise Exception("Invalid command (no get action or not yet implemented)")
|
||||
|
||||
def status(self, command):
|
||||
if len(command) == 0:
|
||||
return self.__server.status()
|
||||
else:
|
||||
elif len(command) == 1:
|
||||
name = command[0]
|
||||
return self.__server.statusJail(name)
|
||||
raise Exception("Invalid command (no status)")
|
||||
|
|
50
setup.py
50
setup.py
|
@ -36,33 +36,33 @@ to reject the IP address or executes user defined
|
|||
commands.'''
|
||||
|
||||
setup(
|
||||
name = "fail2ban",
|
||||
version = version,
|
||||
description = "Ban IPs that make too many password failure",
|
||||
long_description = longdesc,
|
||||
author = "Cyril Jaquier",
|
||||
author_email = "cyril.jaquier@fail2ban.org",
|
||||
url = "http://www.fail2ban.org",
|
||||
license = "GPL",
|
||||
platforms = "Posix",
|
||||
name = "fail2ban",
|
||||
version = version,
|
||||
description = "Ban IPs that make too many password failure",
|
||||
long_description = longdesc,
|
||||
author = "Cyril Jaquier",
|
||||
author_email = "cyril.jaquier@fail2ban.org",
|
||||
url = "http://www.fail2ban.org",
|
||||
license = "GPL",
|
||||
platforms = "Posix",
|
||||
scripts = [
|
||||
'fail2ban-client',
|
||||
'fail2ban-server',
|
||||
'fail2ban-client',
|
||||
'fail2ban-server',
|
||||
'fail2ban-regex'
|
||||
],
|
||||
],
|
||||
packages = [
|
||||
'common',
|
||||
'client',
|
||||
'common',
|
||||
'client',
|
||||
'server'
|
||||
],
|
||||
],
|
||||
data_files = [
|
||||
('/etc/fail2ban',
|
||||
('/etc/fail2ban',
|
||||
glob("config/*.conf")
|
||||
),
|
||||
('/etc/fail2ban/filter.d',
|
||||
),
|
||||
('/etc/fail2ban/filter.d',
|
||||
glob("config/filter.d/*.conf")
|
||||
),
|
||||
('/etc/fail2ban/action.d',
|
||||
),
|
||||
('/etc/fail2ban/action.d',
|
||||
glob("config/action.d/*.conf")
|
||||
),
|
||||
('/var/run/fail2ban',
|
||||
|
@ -78,20 +78,20 @@ elements = {
|
|||
"/etc/":
|
||||
[
|
||||
"fail2ban.conf"
|
||||
],
|
||||
],
|
||||
"/usr/bin/":
|
||||
[
|
||||
"fail2ban.py"
|
||||
],
|
||||
],
|
||||
"/usr/lib/fail2ban/firewall/":
|
||||
[
|
||||
"iptables.py",
|
||||
"ipfwadm.py",
|
||||
"iptables.py",
|
||||
"ipfwadm.py",
|
||||
"ipfw.py"
|
||||
],
|
||||
"/usr/lib/fail2ban/":
|
||||
[
|
||||
"version.py",
|
||||
"version.py",
|
||||
"protocol.py"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -99,11 +99,11 @@ def _copy_lines_between_files(fin, fout, n=None, skip=0, mode='a', terminal_line
|
|||
|
||||
Returns open fout
|
||||
"""
|
||||
if sys.version_info[:2] <= (2,4):
|
||||
if sys.version_info[:2] <= (2,4): # pragma: no cover
|
||||
# on old Python st_mtime is int, so we should give at least 1 sec so
|
||||
# polling filter could detect the change
|
||||
time.sleep(1)
|
||||
if isinstance(fin, str):
|
||||
if isinstance(fin, str): # pragma: no branch - only used with str in test cases
|
||||
fin = open(fin, 'r')
|
||||
if isinstance(fout, str):
|
||||
fout = open(fout, mode)
|
||||
|
@ -353,7 +353,7 @@ def get_monitor_failures_testcase(Filter_):
|
|||
_killfile(self.file, self.name)
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self): # pragma: no cover - will only show up if unexpected exception is thrown
|
||||
return "MonitorFailures%s(%s)" \
|
||||
% (Filter_, hasattr(self, 'name') and self.name or 'tempfile')
|
||||
|
||||
|
|
|
@ -27,8 +27,9 @@ __date__ = "$Date$"
|
|||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest, socket, time
|
||||
import unittest, socket, time, tempfile, os
|
||||
from server.server import Server
|
||||
from common.exceptions import UnknownJailException
|
||||
|
||||
class StartStop(unittest.TestCase):
|
||||
|
||||
|
@ -55,76 +56,430 @@ class Transmitter(unittest.TestCase):
|
|||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.__server = Server()
|
||||
self.__transm = self.__server._Server__transm
|
||||
self.__server.setLogTarget("/dev/null")
|
||||
self.__server.setLogLevel(0)
|
||||
self.__server.start(False)
|
||||
sock_fd, sock_name = tempfile.mkstemp('fail2ban.sock', 'transmitter')
|
||||
os.close(sock_fd)
|
||||
pidfile_fd, pidfile_name = tempfile.mkstemp(
|
||||
'fail2ban.pid', 'transmitter')
|
||||
os.close(pidfile_fd)
|
||||
self.__server.start(sock_name, pidfile_name, force=False)
|
||||
self.jailName = "TestJail1"
|
||||
self.__server.addJail(self.jailName, "auto")
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
self.__server.quit()
|
||||
|
||||
def testSetActionOK(self):
|
||||
name = "TestCase"
|
||||
cmdList = [["add", name],
|
||||
["set", name, "actionstart", "Action Start"],
|
||||
["set", name, "actionstop", "Action Stop"],
|
||||
["set", name, "actioncheck", "Action Check"],
|
||||
["set", name, "actionban", "Action Ban"],
|
||||
["set", name, "actionunban", "Action Unban"],
|
||||
["quit"]]
|
||||
|
||||
outList = [(0, name),
|
||||
(0, 'Action Start'),
|
||||
(0, 'Action Stop'),
|
||||
(0, 'Action Check'),
|
||||
(0, 'Action Ban'),
|
||||
(0, 'Action Unban'),
|
||||
(0, None)]
|
||||
|
||||
cnt = 0
|
||||
for cmd in cmdList:
|
||||
self.assertEqual(self.__server.transm.proceed(cmd), outList[cnt])
|
||||
cnt += 1
|
||||
|
||||
def testSetActionNOK(self):
|
||||
name = "TestCase"
|
||||
cmdList = [["addd", name],
|
||||
["set", name, "test"],
|
||||
["prout prout", "Stop"],
|
||||
["fail2ban", "sucks"],
|
||||
["set"],
|
||||
["_/&%", "@*+%&"],
|
||||
[" quit"]]
|
||||
|
||||
outList = [1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1]
|
||||
|
||||
cnt = 0
|
||||
for cmd in cmdList:
|
||||
msg = self.__server.transm.proceed(cmd)
|
||||
self.assertEqual(msg[0], outList[cnt])
|
||||
cnt += 1
|
||||
|
||||
def testJail(self):
|
||||
name = "TestCase"
|
||||
cmdList = [["add", name],
|
||||
["set", name, "logpath", "testcases/files/testcase01.log"],
|
||||
["set", name, "timeregex", "\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}"],
|
||||
["set", name, "timepattern", "%b %d %H:%M:%S"],
|
||||
["set", name, "failregex", "Authentication failure"],
|
||||
["start", name],
|
||||
["stop", name],
|
||||
["quit"]]
|
||||
|
||||
for cmd in cmdList:
|
||||
self.__server.transm.proceed(cmd)
|
||||
if cmd == ["start", name]:
|
||||
time.sleep(2)
|
||||
jail = self.__server.jails[name]
|
||||
self.assertEqual(jail.getFilter().failManager.size(), 0)
|
||||
self.assertEqual(jail.getAction().banManager.size(), 2)
|
||||
|
||||
|
||||
def setGetTest(self, cmd, inValue, outValue=None, jail=None):
|
||||
setCmd = ["set", cmd, inValue]
|
||||
getCmd = ["get", cmd]
|
||||
if jail is not None:
|
||||
setCmd.insert(1, jail)
|
||||
getCmd.insert(1, jail)
|
||||
if outValue is None:
|
||||
outValue = inValue
|
||||
|
||||
self.assertEqual(self.__transm.proceed(setCmd), (0, outValue))
|
||||
self.assertEqual(self.__transm.proceed(getCmd), (0, outValue))
|
||||
|
||||
def setGetTestNOK(self, cmd, inValue, jail=None):
|
||||
setCmd = ["set", cmd, inValue]
|
||||
getCmd = ["get", cmd]
|
||||
if jail is not None:
|
||||
setCmd.insert(1, jail)
|
||||
getCmd.insert(1, jail)
|
||||
|
||||
# Get initial value before trying invalid value
|
||||
initValue = self.__transm.proceed(getCmd)[1]
|
||||
self.assertEqual(self.__transm.proceed(setCmd)[0], 1)
|
||||
# Check after failed set that value is same as previous
|
||||
self.assertEqual(self.__transm.proceed(getCmd), (0, initValue))
|
||||
|
||||
def jailAddDelTest(self, cmd, values, jail):
|
||||
cmdAdd = "add" + cmd
|
||||
cmdDel = "del" + cmd
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]), (0, []))
|
||||
for n, value in enumerate(values):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdAdd, value]),
|
||||
(0, values[:n+1]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
(0, values[:n+1]))
|
||||
for n, value in enumerate(values):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdDel, value]),
|
||||
(0, values[n+1:]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
(0, values[n+1:]))
|
||||
|
||||
def jailAddDelRegexTest(self, cmd, inValues, outValues, jail):
|
||||
cmdAdd = "add" + cmd
|
||||
cmdDel = "del" + cmd
|
||||
|
||||
if outValues is None:
|
||||
outValues = inValues
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]), (0, []))
|
||||
for n, value in enumerate(inValues):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdAdd, value]),
|
||||
(0, outValues[:n+1]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
(0, outValues[:n+1]))
|
||||
for n, value in enumerate(inValues):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", jail, cmdDel, 0]), # First item
|
||||
(0, outValues[n+1:]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", jail, cmd]),
|
||||
(0, outValues[n+1:]))
|
||||
|
||||
def testStopServer(self):
|
||||
self.assertEqual(self.__transm.proceed(["stop"]), (0, None))
|
||||
|
||||
def testPing(self):
|
||||
self.assertEqual(self.__transm.proceed(["ping"]), (0, "pong"))
|
||||
|
||||
def testSleep(self):
|
||||
t0 = time.time()
|
||||
self.assertEqual(self.__transm.proceed(["sleep", "1"]), (0, None))
|
||||
t1 = time.time()
|
||||
# Approx 1 second delay
|
||||
self.assertAlmostEqual(t1 - t0, 1, places=2)
|
||||
|
||||
def testLogTarget(self):
|
||||
logTargets = []
|
||||
for _ in xrange(3):
|
||||
tmpFile = tempfile.mkstemp("fail2ban", "transmitter")
|
||||
logTargets.append(tmpFile[1])
|
||||
os.close(tmpFile[0])
|
||||
for logTarget in logTargets:
|
||||
self.setGetTest("logtarget", logTarget)
|
||||
|
||||
# If path is invalid, do not change logtarget
|
||||
value = "/this/path/should/not/exist"
|
||||
self.setGetTestNOK("logtarget", value)
|
||||
|
||||
self.__transm.proceed(["set", "/dev/null"])
|
||||
for logTarget in logTargets:
|
||||
os.remove(logTarget)
|
||||
|
||||
def testLogLevel(self):
|
||||
self.setGetTest("loglevel", "4", 4)
|
||||
self.setGetTest("loglevel", "2", 2)
|
||||
self.setGetTest("loglevel", "-1", -1)
|
||||
self.setGetTestNOK("loglevel", "Bird")
|
||||
|
||||
def testAddJail(self):
|
||||
jail2 = "TestJail2"
|
||||
jail3 = "TestJail3"
|
||||
jail4 = "TestJail4"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", jail2, "polling"]), (0, jail2))
|
||||
self.assertEqual(self.__transm.proceed(["add", jail3]), (0, jail3))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", jail4, "invalid backend"])[0], 1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", jail4, "auto"]), (0, jail4))
|
||||
# Duplicate Jail
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", self.jailName, "polling"])[0], 1)
|
||||
# All name is reserved
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["add", "all", "polling"])[0], 1)
|
||||
|
||||
def testStartStopJail(self):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["start", self.jailName]), (0, None))
|
||||
time.sleep(1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["stop", self.jailName]), (0, None))
|
||||
self.assertRaises(
|
||||
UnknownJailException, self.__server.isAlive, self.jailName)
|
||||
|
||||
def testStartStopAllJail(self):
|
||||
self.__server.addJail("TestJail2", "auto")
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["start", self.jailName]), (0, None))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["start", "TestJail2"]), (0, None))
|
||||
self.assertEqual(self.__transm.proceed(["stop", "all"]), (0, None))
|
||||
time.sleep(1)
|
||||
self.assertRaises(
|
||||
UnknownJailException, self.__server.isAlive, self.jailName)
|
||||
self.assertRaises(
|
||||
UnknownJailException, self.__server.isAlive, "TestJail2")
|
||||
|
||||
def testJailIdle(self):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "idle", "on"]),
|
||||
(0, True))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "idle", "off"]),
|
||||
(0, False))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "idle", "CAT"])[0],
|
||||
1)
|
||||
|
||||
def testJailFindTime(self):
|
||||
self.setGetTest("findtime", "120", 120, jail=self.jailName)
|
||||
self.setGetTest("findtime", "60", 60, jail=self.jailName)
|
||||
self.setGetTest("findtime", "-60", -60, jail=self.jailName)
|
||||
self.setGetTestNOK("findtime", "Dog", jail=self.jailName)
|
||||
|
||||
def testJailBanTime(self):
|
||||
self.setGetTest("bantime", "600", 600, jail=self.jailName)
|
||||
self.setGetTest("bantime", "50", 50, jail=self.jailName)
|
||||
self.setGetTest("bantime", "-50", -50, jail=self.jailName)
|
||||
self.setGetTestNOK("bantime", "Cat", jail=self.jailName)
|
||||
|
||||
def testJailUseDNS(self):
|
||||
self.setGetTest("usedns", "yes", jail=self.jailName)
|
||||
self.setGetTest("usedns", "warn", jail=self.jailName)
|
||||
self.setGetTest("usedns", "no", jail=self.jailName)
|
||||
|
||||
# Safe default should be "no"
|
||||
value = "Fish"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "usedns", value]),
|
||||
(0, "no"))
|
||||
|
||||
def testJailBanIP(self):
|
||||
self.__server.startJail(self.jailName) # Jail must be started
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "banip", "127.0.0.1"]),
|
||||
(0, "127.0.0.1"))
|
||||
time.sleep(1) # Give chance to ban
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "banip", "Badger"]),
|
||||
(0, "Badger")) #NOTE: Is IP address validated? Is DNS Lookup done?
|
||||
time.sleep(1) # Give chance to ban
|
||||
# Unban IP
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "unbanip", "127.0.0.1"]),
|
||||
(0, "127.0.0.1"))
|
||||
# Unban IP which isn't banned
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "unbanip", "192.168.1.1"])[0],1)
|
||||
|
||||
def testJailMaxRetry(self):
|
||||
self.setGetTest("maxretry", "5", 5, jail=self.jailName)
|
||||
self.setGetTest("maxretry", "2", 2, jail=self.jailName)
|
||||
self.setGetTest("maxretry", "-2", -2, jail=self.jailName)
|
||||
self.setGetTestNOK("maxretry", "Duck", jail=self.jailName)
|
||||
|
||||
def testJailLogPath(self):
|
||||
self.jailAddDelTest(
|
||||
"logpath",
|
||||
[
|
||||
"testcases/files/testcase01.log",
|
||||
"testcases/files/testcase02.log",
|
||||
"testcases/files/testcase03.log",
|
||||
],
|
||||
self.jailName
|
||||
)
|
||||
# Try duplicates
|
||||
value = "testcases/files/testcase04.log"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addlogpath", value]),
|
||||
(0, [value]))
|
||||
# Will silently ignore duplicate
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addlogpath", value]),
|
||||
(0, [value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", self.jailName, "logpath"]),
|
||||
(0, [value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "dellogpath", value]),
|
||||
(0, []))
|
||||
|
||||
# Invalid file
|
||||
value = "this_file_shouldn't_exist"
|
||||
result = self.__transm.proceed(
|
||||
["set", self.jailName, "addlogpath", value])
|
||||
self.assertTrue(isinstance(result[1], IOError))
|
||||
|
||||
def testJailIgnoreIP(self):
|
||||
self.jailAddDelTest(
|
||||
"ignoreip",
|
||||
[
|
||||
"127.0.0.1",
|
||||
"192.168.1.1",
|
||||
"8.8.8.8",
|
||||
],
|
||||
self.jailName
|
||||
)
|
||||
|
||||
# Try duplicates
|
||||
value = "127.0.0.1"
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addignoreip", value]),
|
||||
(0, [value]))
|
||||
# Will allow duplicate
|
||||
#NOTE: Should duplicates be allowed, or silent ignore like logpath?
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addignoreip", value]),
|
||||
(0, [value, value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", self.jailName, "ignoreip"]),
|
||||
(0, [value, value]))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "delignoreip", value]),
|
||||
(0, [value]))
|
||||
|
||||
def testJailRegex(self):
|
||||
self.jailAddDelRegexTest("failregex",
|
||||
[
|
||||
"user john at <HOST>",
|
||||
"Admin user login from <HOST>",
|
||||
"failed attempt from <HOST> again",
|
||||
],
|
||||
[
|
||||
"user john at (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)",
|
||||
"Admin user login from (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)",
|
||||
"failed attempt from (?:::f{4,6}:)?(?P<host>[\w\-.^_]+) again",
|
||||
],
|
||||
self.jailName
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "addfailregex", "No host regex"])[0],
|
||||
1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "addfailregex", 654])[0],
|
||||
1)
|
||||
|
||||
def testJailIgnoreRegex(self):
|
||||
self.jailAddDelRegexTest("ignoreregex",
|
||||
[
|
||||
"user john",
|
||||
"Admin user login from <HOST>",
|
||||
"Dont match me!",
|
||||
],
|
||||
[
|
||||
"user john",
|
||||
"Admin user login from (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)",
|
||||
"Dont match me!",
|
||||
],
|
||||
self.jailName
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "addignoreregex", "Invalid [regex"])[0],
|
||||
1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "addignoreregex", 50])[0],
|
||||
1)
|
||||
|
||||
def testStatus(self):
|
||||
jails = [self.jailName]
|
||||
self.assertEqual(self.__transm.proceed(["status"]),
|
||||
(0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
|
||||
self.__server.addJail("TestJail2", "auto")
|
||||
jails.append("TestJail2")
|
||||
self.assertEqual(self.__transm.proceed(["status"]),
|
||||
(0, [('Number of jail', len(jails)), ('Jail list', ", ".join(jails))]))
|
||||
|
||||
def testJailStatus(self):
|
||||
self.assertEqual(self.__transm.proceed(["status", self.jailName]),
|
||||
(0,
|
||||
[
|
||||
('filter', [
|
||||
('Currently failed', 0),
|
||||
('Total failed', 0),
|
||||
('File list', [])]
|
||||
),
|
||||
('action', [
|
||||
('Currently banned', 0),
|
||||
('Total banned', 0),
|
||||
('IP list', [])]
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
def testAction(self):
|
||||
action = "TestCaseAction"
|
||||
cmdList = [
|
||||
"actionstart",
|
||||
"actionstop",
|
||||
"actioncheck",
|
||||
"actionban",
|
||||
"actionunban",
|
||||
]
|
||||
cmdValueList = [
|
||||
"Action Start",
|
||||
"Action Stop",
|
||||
"Action Check",
|
||||
"Action Ban",
|
||||
"Action Unban",
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "addaction", action]),
|
||||
(0, action))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", self.jailName, "addaction", action]),
|
||||
(0, action))
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, cmd, action, value]),
|
||||
(0, value))
|
||||
for cmd, value in zip(cmdList, cmdValueList):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", self.jailName, cmd, action]),
|
||||
(0, value))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "setcinfo", action, "KEY", "VALUE"]),
|
||||
(0, "VALUE"))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["get", self.jailName, "cinfo", action, "KEY"]),
|
||||
(0, "VALUE"))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["get", self.jailName, "cinfo", action, "InvalidKey"])[0],
|
||||
1)
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "delcinfo", action, "KEY"]),
|
||||
(0, None))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", self.jailName, "delaction", action]),
|
||||
(0, None))
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(
|
||||
["set", self.jailName, "delaction", "Doesn't exist"])[0],1)
|
||||
|
||||
def testNOK(self):
|
||||
self.assertEqual(self.__transm.proceed(["INVALID", "COMMAND"])[0],1)
|
||||
|
||||
def testSetNOK(self):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["set", "INVALID", "COMMAND"])[0],1)
|
||||
|
||||
def testGetNOK(self):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["get", "INVALID", "COMMAND"])[0],1)
|
||||
|
||||
def testStatusNOK(self):
|
||||
self.assertEqual(
|
||||
self.__transm.proceed(["status", "INVALID", "COMMAND"])[0],1)
|
||||
|
|
Loading…
Reference in New Issue