Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a58d16e37f | ||
|
|
5ebdf11894 |
+205
@@ -0,0 +1,205 @@
|
||||
__ _ _ ___ _
|
||||
/ _|__ _(_) |_ ) |__ __ _ _ _
|
||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||
|
||||
=============================================================
|
||||
Fail2Ban (version 0.7.0) 2006/08/23
|
||||
=============================================================
|
||||
|
||||
ver. 0.7.0 (2006/08/23) - alpha
|
||||
----------
|
||||
- Almost a complete rewrite :) Fail2ban design is really
|
||||
better (IMHO). There is a lot of new features
|
||||
- Client/Server architecture
|
||||
- Multithreading. Each jail has its own threads: one for the
|
||||
log reading and another for the actions
|
||||
- Execute several actions
|
||||
- Split configuration files. They are more readable and easy
|
||||
to use
|
||||
- failregex uses group (<host>) now. This feature was already
|
||||
present in the Debian package
|
||||
- lots of things...
|
||||
|
||||
ver. 0.6.1 (2006/03/16) - stable
|
||||
----------
|
||||
- Added permanent banning. Set banTime to a negative value to
|
||||
enable this feature (-1 is perfect). Thanks to Mannone
|
||||
- Fixed locale bug. Thanks to Fernando José
|
||||
- Fixed crash when time format does not match data
|
||||
- Propagated patch from Debian to fix fail2ban search path
|
||||
addition to the path search list: now it is added first.
|
||||
Thanks to Nick Craig-Wood
|
||||
- Added SMTP authentification for mail notification. Thanks
|
||||
to Markus Hoffmann
|
||||
- Removed debug mode as it is confusing for people
|
||||
- Added parsing of timestamp in TAI64N format (#1275325).
|
||||
Thanks to Mark Edgington
|
||||
- Added patch #1382936 (Default formatted syslog logging).
|
||||
Thanks to Patrick Börjesson
|
||||
- Removed 192.168.0.0/16 from ignoreip. Attacks could also
|
||||
come from the local network.
|
||||
- Robust startup: if iptables module does not get fully
|
||||
initialized after startup of fail2ban, fail2ban will do
|
||||
"maxreinit" attempts to initialize its own firewall. It
|
||||
will sleep between attempts for "polltime" number of
|
||||
seconds (closes Debian: #334272). Thanks to Yaroslav
|
||||
Halchenko
|
||||
- Added "interpolations" in fail2ban.conf. This is provided
|
||||
by the ConfigParser module. Old configuration files still
|
||||
work. Thanks to Yaroslav Halchenko
|
||||
- Added initial support for hosts.deny and shorewall. Need
|
||||
more testing. Please test. Thanks to kojiro from Gentoo
|
||||
forum for hosts.deny support
|
||||
- Added support for vsftpd. Thanks to zugeschmiert
|
||||
|
||||
ver. 0.6.0 (2005/11/20) - stable
|
||||
----------
|
||||
- Propagated patches introduced by Debian maintainer
|
||||
(Yaroslav Halchenko):
|
||||
* Added an option to report local time (including timezone)
|
||||
or GMT in mail notification.
|
||||
|
||||
ver. 0.5.5 (2005/10/26) - beta
|
||||
----------
|
||||
- Propagated patches introduced by Debian maintainer
|
||||
(Yaroslav Halchenko):
|
||||
* Introduced fwcheck option to verify consistency of the
|
||||
chains. Implemented automatic restart of fail2ban main
|
||||
function in case check of fwban or fwunban command failed
|
||||
(closes: #329163, #331695). (Introduced patch was further
|
||||
adjusted by upstream author).
|
||||
* Added -f command line parameter for [findtime].
|
||||
* Added a cleanup of firewall rules on emergency shutdown
|
||||
when unknown exception is catched.
|
||||
* Fail2ban should not crash now if a wrong file name is
|
||||
specified in config.
|
||||
* reordered code a bit so that log targets are setup right
|
||||
after background and then only loglevel (verbose, debug)
|
||||
is processed, so the warning could be seen in the logs
|
||||
* Added a keyword <section> in parsing of the subject and
|
||||
the body of an email sent out by fail2ban (closes:
|
||||
#330311)
|
||||
|
||||
ver. 0.5.4 (2005/09/13) - beta
|
||||
----------
|
||||
- Fixed bug #1286222.
|
||||
- Propagated patches introduced by Debian maintainer
|
||||
(Yaroslav Halchenko):
|
||||
* Fixed handling of SYSLOG logging target. Now it can log
|
||||
to any SYSLOG target and facility as directed by the
|
||||
config
|
||||
* Format of SYSLOG entries fixed to look closer to standard
|
||||
* Fixed errata in config/gentoo-confd
|
||||
* Introduced findtime configuration variable to control the
|
||||
lifetime of caught "failed" log entries
|
||||
|
||||
ver. 0.5.3 (2005/09/08) - beta
|
||||
----------
|
||||
- Fixed a bug when overriding "maxfailures" or "bantime".
|
||||
Thanks to Yaroslav Halchenko
|
||||
- Added more debug output if an error occurs when sending
|
||||
mail. Thanks to Stephen Gildea
|
||||
- Renamed "maxretry" to "maxfailures" and changed default
|
||||
value to 5. Thanks to Stephen Gildea
|
||||
- Hopefully fixed bug #1256075
|
||||
- Fixed bug #1262345
|
||||
- Fixed exception handling in PIDLock
|
||||
- Removed warning when using "-V" or "-h" with no config
|
||||
file. Thanks to Yaroslav Halchenko
|
||||
- Removed "-i eth0" from config file. Thanks to Yaroslav
|
||||
Halchenko
|
||||
|
||||
ver. 0.5.2 (2005/08/06) - beta
|
||||
----------
|
||||
- Better PID lock file handling. Should close #1239562
|
||||
- Added man pages
|
||||
- Removed log4py dependency. Use logging module instead
|
||||
- "maxretry" and "bantime" can be overridden in each section
|
||||
- Fixed bug #1246278 (excessive memory usage)
|
||||
- Fixed crash on wrong option value in configuration file
|
||||
- Changed custom chains to lowercase
|
||||
|
||||
ver. 0.5.1 (2005/07/23) - beta
|
||||
----------
|
||||
- Fixed bugs #1241756, #1239557
|
||||
- Added log targets in configuration file. Removed -l option
|
||||
- Changed iptables rules in order to create a separated chain
|
||||
for each section
|
||||
- Fixed static banList in firewall.py
|
||||
- Added an initd script for Debian. Thanks to Yaroslav
|
||||
Halchenko
|
||||
- Check for obsolete files after install
|
||||
|
||||
ver. 0.5.0 (2005/07/12) - beta
|
||||
----------
|
||||
- Added support for CIDR mask in ignoreip
|
||||
- Added mail notification support
|
||||
- Fixed bug #1234699
|
||||
- Added tags replacement in rules definition. Should allow a
|
||||
clean solution for Feature Request #1229479
|
||||
- Removed "interface" and "firewall" options
|
||||
- Added start and end commands in the configuration file.
|
||||
Thanks to Yaroslav Halchenko
|
||||
- Added firewall rules definition in the configuration file
|
||||
- Cleaned fail2ban.py
|
||||
- Added an initd script for RedHat/Fedora. Thanks to Andrey
|
||||
G. Grozin
|
||||
|
||||
ver. 0.4.1 (2005/06/30) - stable
|
||||
----------
|
||||
- Fixed textToDNS method which generated wrong matches for
|
||||
"rhost=12-xyz...". Thanks to Tom Pike
|
||||
- fail2ban.conf modified for readability. Thanks to Iain Lea
|
||||
- Added an initd script for Gentoo
|
||||
- Changed default PID lock file location from /tmp to
|
||||
/var/run
|
||||
|
||||
ver. 0.4.0 (2005/04/24) - stable
|
||||
----------
|
||||
- Fixed textToDNS which did not recognize strings like
|
||||
"12-345-67-890.abcd.mnopqr.xyz"
|
||||
|
||||
ver. 0.3.1 (2005/03/31) - beta
|
||||
----------
|
||||
- Corrected level of messages
|
||||
- Added DNS lookup support
|
||||
- Improved parsing speed. Only parse the new log messages
|
||||
- Added a second verbose level (-vv)
|
||||
|
||||
ver. 0.3.0 (2005/02/24) - beta
|
||||
----------
|
||||
- Re-writting of parts of the code in order to handle several
|
||||
log files with different rules
|
||||
- Removed sshd.py because it is no more needed
|
||||
- Fixed a bug when exiting with IP in the ban list
|
||||
- Added PID lock file
|
||||
- Improved some parts of the code
|
||||
- Added ipfw-start-rule option (thanks to Robert Edeker)
|
||||
- Added -k option which kills a currently running Fail2Ban
|
||||
|
||||
ver. 0.1.2 (2004/11/21) - beta
|
||||
----------
|
||||
- Add ipfw and ipfwadm support. The rules are taken from
|
||||
BlockIt. Thanks to Robert Edeker
|
||||
- Add -e option which allows to set the interface. Thanks to
|
||||
Robert Edeker who reminded me this
|
||||
- Small code cleaning
|
||||
|
||||
ver. 0.1.1 (2004/10/23) - beta
|
||||
----------
|
||||
- Add SIGTERM handler in order to exit nicely when in daemon
|
||||
mode
|
||||
- Add -r option which allows to set the maximum number of
|
||||
login failures
|
||||
- Remove the Metalog class as the log file are not so syslog
|
||||
daemon specific
|
||||
- Rewrite log reader to be service centered. Sshd support
|
||||
added. Match "Failed password" and "Illegal user"
|
||||
- Add /etc/fail2ban.conf configuration support
|
||||
- Code documentation
|
||||
|
||||
|
||||
ver. 0.1.0 (2004/10/12) - alpha
|
||||
----------
|
||||
- Initial release
|
||||
@@ -0,0 +1,51 @@
|
||||
README
|
||||
CHANGELOG
|
||||
TODO
|
||||
fail2ban-client
|
||||
fail2ban-server
|
||||
fail2ban-testcases
|
||||
client/configreader.py
|
||||
client/jailreader.py
|
||||
client/fail2banreader.py
|
||||
client/jailsreader.py
|
||||
client/filterreader.py
|
||||
client/actionreader.py
|
||||
client/__init__.py
|
||||
client/configurator.py
|
||||
client/csocket.py
|
||||
server/ssocket.py
|
||||
server/banticket.py
|
||||
server/filter.py
|
||||
server/server.py
|
||||
server/failticket.py
|
||||
server/actions.py
|
||||
server/faildata.py
|
||||
server/failmanager.py
|
||||
server/jailthread.py
|
||||
server/transmitter.py
|
||||
server/action.py
|
||||
server/ticket.py
|
||||
server/jail.py
|
||||
server/__init__.py
|
||||
server/banmanager.py
|
||||
setup.py
|
||||
setup.cfg
|
||||
testcases/banmanagertestcase.py
|
||||
testcases/failmanagertestcase.py
|
||||
testcases/clientreadertestcase.py
|
||||
testcases/filtertestcase.py
|
||||
testcases/__init__.py
|
||||
testcases/servertestcase.py
|
||||
testcases/files/testcase01.log
|
||||
testcases/files/testcase02.log
|
||||
version.py
|
||||
config/jail.conf
|
||||
config/filter.d/vsftpd.conf
|
||||
config/filter.d/apache-auth.conf
|
||||
config/filter.d/sshd.conf
|
||||
config/action.d/iptables.conf
|
||||
config/action.d/mail-whois.conf
|
||||
config/action.d/dummy.conf
|
||||
config/action.d/mail.conf
|
||||
config/action.d/hostsdeny.conf
|
||||
config/fail2ban.conf
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
__ _ _ ___ _
|
||||
/ _|__ _(_) |_ ) |__ __ _ _ _
|
||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||
|
||||
=============================================================
|
||||
Fail2Ban (version 0.7.0) 2006/08/23
|
||||
=============================================================
|
||||
|
||||
Fail2Ban scans log files like /var/log/pwdfail and bans IP
|
||||
that makes too many password failures. It updates firewall
|
||||
rules to reject the IP address. These rules can be defined by
|
||||
the user. Fail2Ban can read multiple log files such as sshd
|
||||
or Apache web server ones.
|
||||
|
||||
This is my first Python program. Moreover, English is not my
|
||||
mother tongue...
|
||||
|
||||
|
||||
More details:
|
||||
-------------
|
||||
|
||||
Fail2Ban is rather simple. I have a home server connected to
|
||||
the Internet which runs apache, samba, sshd, ... I see in my
|
||||
logs that people are trying to log into my box using "manual"
|
||||
brute force or scripts. They try 10, 20 and sometimes more
|
||||
user/password (without success anyway). In order to
|
||||
discourage these script kiddies, I wanted that sshd refuse
|
||||
login from a specific ip after 3 password failures. After
|
||||
some Google searches, I found that sshd was not able of that.
|
||||
So I search for a script or program that do it. I found
|
||||
nothing :-( So I decide to write mine and to learn Python :-)
|
||||
|
||||
For each sections defined in the configuration file, Fail2Ban
|
||||
tries to find lines which match the failregex. Then it
|
||||
retrieves the message time using timeregex and timepattern.
|
||||
It finally gets the ip and if it has already done 3 or more
|
||||
password failures in the last banTime, the ip is banned for
|
||||
banTime using a firewall rule. This rule is set by the user
|
||||
in the configuration file. Thus, Fail2Ban can be adapted for
|
||||
lots of firewall. After banTime, the rule is deleted. Notice
|
||||
that if no "plain" ip is available, Fail2Ban try to do DNS
|
||||
lookup in order to found one or several ip's to ban.
|
||||
|
||||
Sections can be freely added so it is possible to monitor
|
||||
several daemons at the same time.
|
||||
|
||||
Runs on my server and does its job rather well :-) The idea
|
||||
is to make fail2ban usable with daemons and services that
|
||||
require a login (sshd, telnetd, ...) and with different
|
||||
firewalls.
|
||||
|
||||
|
||||
Installation:
|
||||
-------------
|
||||
|
||||
Require: python-2.4 (http://www.python.org)
|
||||
|
||||
To install, just do:
|
||||
|
||||
> tar xvfj fail2ban-0.7.0.tar.bz2
|
||||
> cd fail2ban-0.7.0
|
||||
> python setup.py install
|
||||
|
||||
This will install Fail2Ban into /usr/lib/fail2ban. The
|
||||
executable scripts are placed into /usr/bin.
|
||||
|
||||
Gentoo: ebuilds are available on the website.
|
||||
Debian: Fail2Ban is in Debian unstable.
|
||||
RedHat: packages are available on the website.
|
||||
|
||||
Fail2Ban should now be correctly installed. Just type:
|
||||
|
||||
> fail2ban-client -h
|
||||
|
||||
to see if everything is alright.
|
||||
|
||||
Configuration:
|
||||
--------------
|
||||
|
||||
You can configure fail2ban using the files in /etc/fail2ban
|
||||
or using command line. Here are the available command line
|
||||
options (not complete yet):
|
||||
|
||||
<COMMAND>
|
||||
|
||||
start start the server and the jails
|
||||
reload reload the configuration
|
||||
stop stop all jails and terminate the
|
||||
server
|
||||
status get the current status
|
||||
|
||||
set loglevel <LEVEL> set loglevel to <LEVEL>
|
||||
get loglevel get loglevel
|
||||
set logtarget <TARGET> set log target to <TARGET>
|
||||
get logtarget get log target
|
||||
|
||||
add <JAIL> create <JAIL>
|
||||
set <JAIL> <CMD> set the <CMD> value for <JAIL>
|
||||
get <JAIL> <CMD> get the <CMD> value for <JAIL>
|
||||
start <JAIL> start <JAIL>
|
||||
stop <JAIL> stop <JAIL>. The jail is removed
|
||||
status <JAIL> get the current status of <JAIL>
|
||||
|
||||
[OPTIONS]
|
||||
|
||||
-c <DIR> configuration directory
|
||||
-d dump configuration. For debugging
|
||||
-v increase verbosity
|
||||
-q decrease verbosity
|
||||
-x force execution of the server
|
||||
-h display this help message
|
||||
|
||||
Contact:
|
||||
--------
|
||||
|
||||
You need some new features, you found bugs or you just
|
||||
appreciate this program, you can contact me at :
|
||||
|
||||
Website: http://fail2ban.sourceforge.net
|
||||
|
||||
Cyril Jaquier: <lostcontrol@users.sourceforge.net>
|
||||
|
||||
|
||||
Thanks:
|
||||
-------
|
||||
|
||||
Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker,
|
||||
Tom Pike, Iain Lea, Andrey G. Grozin, Yaroslav Halchenko,
|
||||
Jonathan Kamens, Stephen Gildea, Markus Hoffmann, Mark
|
||||
Edgington, Patrick Börjesson, kojiro, zugeschmiert
|
||||
|
||||
License:
|
||||
--------
|
||||
|
||||
Fail2Ban is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Fail2Ban is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY 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
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
__ _ _ ___ _
|
||||
/ _|__ _(_) |_ ) |__ __ _ _ _
|
||||
| _/ _` | | |/ /| '_ \/ _` | ' \
|
||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||
|
||||
=============================================================
|
||||
ToDo $Revision$
|
||||
=============================================================
|
||||
|
||||
Legend:
|
||||
- not yet done
|
||||
? maybe
|
||||
# partially done
|
||||
* done
|
||||
|
||||
- correct handling of threads (join???)
|
||||
|
||||
- signal handling (ctrl-c, etc)
|
||||
|
||||
- add a reload option to fail2ban-client
|
||||
|
||||
- see Feature Request Tracking System at SourceForge.net
|
||||
|
||||
* findall in dns.py should be no more needed
|
||||
|
||||
* remove utils/ directory
|
||||
|
||||
- improve installation process (better prefix support)
|
||||
|
||||
# improve documentation and website for user
|
||||
|
||||
* use Doxygen
|
||||
|
||||
- use PyLint to check the code
|
||||
|
||||
* better configuration files
|
||||
|
||||
- add a check to see if the time of the log messages is
|
||||
correctly detected (valid regexp)
|
||||
|
||||
? use Gentoo Portage style for scripts.
|
||||
- banning engines script in /etc/fail2ban/scripts.d
|
||||
Example: /etc/fail2ban/scripts.d/iptables
|
||||
Will be mostly bash scripting which is more "user
|
||||
friendly".
|
||||
- split configuration files in /etc/fail2ban/services.d
|
||||
for log files
|
||||
Example: /etc/fail2ban/services.d/apache
|
||||
Mainly regular expressions.
|
||||
- template for common regex in /etc/fail2ban/templates.d
|
||||
Example: /etc/fail2ban/templates.d/date
|
||||
Mainly regular expressions.
|
||||
|
||||
* remove debug mode (root check)
|
||||
|
||||
# better return values in function
|
||||
|
||||
? use more email.Utils in mail.py
|
||||
|
||||
? add gettext support. Is this really needed for a server
|
||||
utility?
|
||||
|
||||
* send an email when fail2ban is running
|
||||
|
||||
* add multithreading. Python threading is not really
|
||||
efficient. However, fail2ban could benefit of it. We could
|
||||
use threads like this:
|
||||
- one thread which check for host to unban.
|
||||
- one thread per file to watch. This will allow things like
|
||||
different polling time for each file.
|
||||
<srv> is read-only (we only read log files) thus no locks
|
||||
are required. However, <meth> is read-write and must take
|
||||
care of concurrency in case of multithreading.
|
||||
|
||||
- add FAM/Gamin support. Should be quite efficient with
|
||||
threading. Take care that handle_one_event() release the
|
||||
Python lock.
|
||||
|
||||
# add a test framework. We could use unittest which is in
|
||||
Python since 2.1. It should be possible to run all tests
|
||||
automatically.
|
||||
|
||||
* add client/server using socket. Something similar to
|
||||
gdesklets. DBUS seems to be designed for desktop use.
|
||||
- fail2ban start -> start the daemon.
|
||||
- fail2ban stop -> stop the daemon.
|
||||
- fail2ban add <srv> <meth> -> add <srv> monitoring with
|
||||
<meth> ban method (iptables, hosts.deny, etc).
|
||||
- fail2ban del <srv> -> remove <srv> monitoring.
|
||||
- fail2ban status <srv> -> query current fail2ban status.
|
||||
Should return infos like a ban counter. Could be graph
|
||||
with rrdtool.
|
||||
- fail2ban pause <srv> -> suspend monitoring.
|
||||
- fail2ban resume <srv> -> resume monitoring.
|
||||
- fail2ban list -> list available services.
|
||||
- fail2ban flush <srv> -> flush the <srv> ban list.
|
||||
|
||||
* remove PID file.
|
||||
|
||||
* remove most of the command lines options if possible.
|
||||
|
||||
- add the possibility to specify wildcard in log files.
|
||||
Example: logfile = /var/log/apache2/access-*.log
|
||||
Should we start one thread per file or just one thread per
|
||||
serivce?
|
||||
|
||||
- autodetect date format in log file. Match the most popular
|
||||
format and sort them using the hit ratio. Should avoid
|
||||
user problem with regex and not have a big impact on perfs.
|
||||
|
||||
- restart automatically the daemon if an exception occurs.
|
||||
|
||||
- do not close socket after a send
|
||||
|
||||
- refactoring in server.py, actions.py, filter.py
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
@@ -0,0 +1,90 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from configreader import ConfigReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class ActionReader(ConfigReader):
|
||||
|
||||
def __init__(self, action, name):
|
||||
ConfigReader.__init__(self)
|
||||
self.file = action[0]
|
||||
self.cInfo = action[1]
|
||||
self.name = name
|
||||
|
||||
def setFile(self, file):
|
||||
self.file = file
|
||||
|
||||
def getFile(self):
|
||||
return self.file
|
||||
|
||||
def setName(self, name):
|
||||
self.name = name
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "action.d/" + self.file)
|
||||
|
||||
def getOptions(self, pOpts):
|
||||
opts = [["string", "actionstart", ""],
|
||||
["string", "actionstop", ""],
|
||||
["string", "actioncheck", ""],
|
||||
["string", "actionban", ""],
|
||||
["string", "actionunban", ""]]
|
||||
self.opts = ConfigReader.getOptions(self, "Definition", opts, pOpts)
|
||||
|
||||
if self.has_section("Init"):
|
||||
for opt in self.options("Init"):
|
||||
if not self.cInfo.has_key(opt):
|
||||
self.cInfo[opt] = self.get("Init", opt)
|
||||
|
||||
def convert(self):
|
||||
head = ["set", self.name]
|
||||
stream = list()
|
||||
stream.append(head + ["addaction", self.file])
|
||||
for opt in self.opts:
|
||||
if opt == "actionstart":
|
||||
stream.append(head + ["actionstart", self.file, self.opts[opt]])
|
||||
elif opt == "actionstop":
|
||||
stream.append(head + ["actionstop", self.file, self.opts[opt]])
|
||||
elif opt == "actioncheck":
|
||||
stream.append(head + ["actioncheck", self.file, self.opts[opt]])
|
||||
elif opt == "actionban":
|
||||
stream.append(head + ["actionban", self.file, self.opts[opt]])
|
||||
elif opt == "actionunban":
|
||||
stream.append(head + ["actionunban", self.file, self.opts[opt]])
|
||||
# cInfo
|
||||
if self.cInfo:
|
||||
for p in self.cInfo:
|
||||
stream.append(head + ["setcinfo", self.file, p, self.cInfo[p]])
|
||||
|
||||
return stream
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from ConfigParser import *
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class ConfigReader(SafeConfigParser):
|
||||
|
||||
basedir = "/etc/fail2ban/"
|
||||
|
||||
def __init__(self):
|
||||
SafeConfigParser.__init__(self)
|
||||
self.opts = None
|
||||
|
||||
@staticmethod
|
||||
def setBaseDir(dir):
|
||||
global basedir
|
||||
path = dir.rstrip('/')
|
||||
basedir = path + '/'
|
||||
|
||||
@staticmethod
|
||||
def getBaseDir():
|
||||
global basedir
|
||||
return basedir
|
||||
|
||||
def read(self, filename):
|
||||
global basedir
|
||||
basename = basedir + filename
|
||||
logSys.debug("Reading " + basename)
|
||||
SafeConfigParser.read(self, [basename + ".conf", basename + ".local"])
|
||||
|
||||
##
|
||||
# Read the options.
|
||||
#
|
||||
# Read the given option in the configuration file. Default values
|
||||
# are used...
|
||||
# Each optionValues entry is composed of an array with:
|
||||
# 0 -> the type of the option
|
||||
# 1 -> the name of the option
|
||||
# 2 -> the default value for the option
|
||||
def getOptions(self, sec, options, pOptions = None):
|
||||
values = dict()
|
||||
for option in options:
|
||||
try:
|
||||
if option[0] == "bool":
|
||||
v = self.getboolean(sec, option[1])
|
||||
elif option[0] == "int":
|
||||
v = self.getint(sec, option[1])
|
||||
else:
|
||||
v = self.get(sec, option[1])
|
||||
if not pOptions == None and option[1] in pOptions:
|
||||
continue
|
||||
values[option[1]] = v
|
||||
except NoSectionError, e:
|
||||
# No "Definition" section or wrong basedir
|
||||
logSys.error(e)
|
||||
except NoOptionError:
|
||||
if not option[2] == None:
|
||||
logSys.warn("No '" + option[1] + "' defined in '" + sec + "'")
|
||||
values[option[1]] = option[2]
|
||||
except ValueError:
|
||||
logSys.warn("Wrong value for '" + option[1] + "' in '" + sec +
|
||||
"'. Using default one: '" + `option[2]` + "'")
|
||||
values[option[1]] = option[2]
|
||||
return values
|
||||
@@ -0,0 +1,68 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from configreader import ConfigReader
|
||||
from fail2banreader import Fail2banReader
|
||||
from jailsreader import JailsReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class Configurator:
|
||||
|
||||
def __init__(self):
|
||||
self.settings = dict()
|
||||
self.streams = dict()
|
||||
self.fail2ban = Fail2banReader()
|
||||
self.jails = JailsReader()
|
||||
|
||||
def setBaseDir(self, dir):
|
||||
ConfigReader.setBaseDir(dir)
|
||||
|
||||
def getBaseDir(self):
|
||||
return ConfigReader.getBaseDir()
|
||||
|
||||
def readAll(self):
|
||||
self.fail2ban.read()
|
||||
self.jails.read()
|
||||
|
||||
def getAllOptions(self):
|
||||
self.settings["general"] = self.fail2ban.getOptions()
|
||||
self.settings["jails"] = self.jails.getOptions()
|
||||
|
||||
def convertToProtocol(self):
|
||||
self.streams["general"] = self.fail2ban.convert()
|
||||
self.streams["jails"] = self.jails.convert()
|
||||
|
||||
def getConfigStream(self):
|
||||
cmds = list()
|
||||
for opt in self.streams["general"]:
|
||||
cmds.append(opt)
|
||||
for opt in self.streams["jails"]:
|
||||
cmds.append(opt)
|
||||
return cmds
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import socket, pickle
|
||||
|
||||
class CSocket:
|
||||
|
||||
def __init__(self):
|
||||
self.socketFile = "/tmp/fail2ban.sock"
|
||||
# Create an INET, STREAMing socket
|
||||
#self.csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.csock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
#self.csock.connect(("localhost", 2222))
|
||||
self.csock.connect(self.socketFile)
|
||||
|
||||
def send(self, msg):
|
||||
# Convert every list member to string
|
||||
obj = pickle.dumps(map(str, msg))
|
||||
self.csock.send(obj + "<F2B_END_COMMAND>")
|
||||
ret = self.receive(self.csock)
|
||||
self.csock.close()
|
||||
return ret
|
||||
|
||||
def receive(self, socket):
|
||||
msg = ''
|
||||
while msg.rfind("<F2B_END_COMMAND>") == -1:
|
||||
chunk = socket.recv(6)
|
||||
if chunk == '':
|
||||
raise RuntimeError, "socket connection broken"
|
||||
msg = msg + chunk
|
||||
return pickle.loads(msg)
|
||||
@@ -0,0 +1,54 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from configreader import ConfigReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class Fail2banReader(ConfigReader):
|
||||
|
||||
def __init__(self):
|
||||
ConfigReader.__init__(self)
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "fail2ban")
|
||||
|
||||
def getOptions(self):
|
||||
opts = [["int", "loglevel", 1],
|
||||
["string", "logtarget", "STDERR"]]
|
||||
self.opts = ConfigReader.getOptions(self, "Definition", opts)
|
||||
|
||||
def convert(self):
|
||||
stream = list()
|
||||
for opt in self.opts:
|
||||
if opt == "loglevel":
|
||||
stream.append(["set", "loglevel", self.opts[opt]])
|
||||
elif opt == "logtarget":
|
||||
stream.append(["set", "logtarget", self.opts[opt]])
|
||||
return stream
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from configreader import ConfigReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class FilterReader(ConfigReader):
|
||||
|
||||
def __init__(self, file, name):
|
||||
ConfigReader.__init__(self)
|
||||
self.file = file
|
||||
self.name = name
|
||||
|
||||
def setFile(self, file):
|
||||
self.file = file
|
||||
|
||||
def getFile(self):
|
||||
return self.file
|
||||
|
||||
def setName(self, name):
|
||||
self.name = name
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "filter.d/" + self.file)
|
||||
|
||||
def getOptions(self, pOpts):
|
||||
opts = [["string", "logpath", "/var/log/sshd.log"],
|
||||
["string", "timeregex", ""],
|
||||
["string", "timepattern", ""],
|
||||
["string", "failregex", ""],
|
||||
["int", "maxtime", 600],
|
||||
["int", "maxretry", 3]]
|
||||
self.opts = ConfigReader.getOptions(self, "Definition", opts, pOpts)
|
||||
|
||||
def convert(self):
|
||||
stream = list()
|
||||
for opt in self.opts:
|
||||
if opt == "logpath":
|
||||
stream.append(["set", self.name, "logpath", self.opts[opt]])
|
||||
elif opt == "timeregex":
|
||||
stream.append(["set", self.name, "timeregex", self.opts[opt]])
|
||||
elif opt == "timepattern":
|
||||
stream.append(["set", self.name, "timepattern", self.opts[opt]])
|
||||
elif opt == "failregex":
|
||||
stream.append(["set", self.name, "failregex", self.opts[opt]])
|
||||
elif opt == "maxtime":
|
||||
stream.append(["set", self.name, "maxtime", self.opts[opt]])
|
||||
elif opt == "maxretry":
|
||||
stream.append(["set", self.name, "maxretry", self.opts[opt]])
|
||||
return stream
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging, re
|
||||
from configreader import ConfigReader
|
||||
from filterreader import FilterReader
|
||||
from actionreader import ActionReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class JailReader(ConfigReader):
|
||||
|
||||
actionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$")
|
||||
|
||||
def __init__(self, name):
|
||||
ConfigReader.__init__(self)
|
||||
self.name = name
|
||||
self.filter = None
|
||||
self.actions = list()
|
||||
|
||||
def setName(self, value):
|
||||
self.name = value
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "jail")
|
||||
|
||||
def isEnabled(self):
|
||||
return self.opts["enabled"]
|
||||
|
||||
def getOptions(self):
|
||||
opts = [["bool", "enabled", "false"],
|
||||
["int", "maxretry", None],
|
||||
["int", "bantime", 600],
|
||||
["string", "filter", ""],
|
||||
["string", "action", ""]]
|
||||
self.opts = ConfigReader.getOptions(self, self.name, opts)
|
||||
|
||||
if self.isEnabled():
|
||||
# Read filter
|
||||
self.filter = FilterReader(self.opts["filter"], self.name)
|
||||
self.filter.read()
|
||||
self.filter.getOptions(self.opts)
|
||||
|
||||
# Read action
|
||||
for act in self.opts["action"].split('\n'):
|
||||
try:
|
||||
splitAct = JailReader.splitAction(act)
|
||||
action = ActionReader(splitAct, self.name)
|
||||
action.read()
|
||||
action.getOptions(self.opts)
|
||||
self.actions.append(action)
|
||||
except AttributeError, e:
|
||||
logSys.error("Error in action definition " + act)
|
||||
logSys.debug(e)
|
||||
|
||||
def convert(self):
|
||||
stream = [["add", self.name]]
|
||||
for opt in self.opts:
|
||||
if opt == "maxretry":
|
||||
stream.append(["set", self.name, "maxretry", self.opts[opt]])
|
||||
elif opt == "bantime":
|
||||
stream.append(["set", self.name, "bantime", self.opts[opt]])
|
||||
stream.extend(self.filter.convert())
|
||||
for action in self.actions:
|
||||
stream.extend(action.convert())
|
||||
return stream
|
||||
|
||||
@staticmethod
|
||||
def splitAction(action):
|
||||
m = JailReader.actionCRE.match(action)
|
||||
d = dict()
|
||||
if m.group(2) <> None:
|
||||
for param in m.group(2).split(','):
|
||||
p = param.split('=')
|
||||
d[p[0].strip()] = p[1].strip()
|
||||
return [m.group(1), d]
|
||||
@@ -0,0 +1,73 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import logging
|
||||
from configreader import ConfigReader
|
||||
from jailreader import JailReader
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client.config")
|
||||
|
||||
class JailsReader(ConfigReader):
|
||||
|
||||
def __init__(self):
|
||||
ConfigReader.__init__(self)
|
||||
self.jails = list()
|
||||
|
||||
def read(self):
|
||||
ConfigReader.read(self, "jail")
|
||||
|
||||
def getOptions(self):
|
||||
opts = []
|
||||
self.opts = ConfigReader.getOptions(self, "Definition", opts)
|
||||
|
||||
for sec in self.sections():
|
||||
jail = JailReader(sec)
|
||||
jail.read()
|
||||
jail.getOptions()
|
||||
if jail.isEnabled():
|
||||
# We only add enabled jails
|
||||
self.jails.append(jail)
|
||||
|
||||
def getFilterOptions(self, file):
|
||||
filter = FilterReader(file)
|
||||
filter.read()
|
||||
return filter.getOptions()
|
||||
|
||||
def convert(self):
|
||||
stream = list()
|
||||
for opt in self.opts:
|
||||
if opt == "":
|
||||
stream.append([])
|
||||
# Convert jails
|
||||
for jail in self.jails:
|
||||
stream.extend(jail.convert())
|
||||
# Start jails
|
||||
for jail in self.jails:
|
||||
stream.append(["start", jail.getName()])
|
||||
|
||||
return stream
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: fwstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstart = touch /tmp/fail2ban.dummy
|
||||
echo "<init>" >> /tmp/fail2ban.dummy
|
||||
|
||||
# Option: fwend
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstop = rm /tmp/fail2ban.dummy
|
||||
|
||||
# Option: fwcheck
|
||||
# Notes.: command executed once before each fwban command
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actioncheck =
|
||||
|
||||
# Option: fwban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <failtime> unix timestamp of the last failure
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
# Default: iptables -I INPUT 1 -s <ip> -j DROP
|
||||
#
|
||||
actionban = echo "+<ip>" >> /tmp/fail2ban.dummy
|
||||
|
||||
# Option: fwunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# <unbantime> unix timestamp of the unban time
|
||||
# Values: CMD
|
||||
# Default: iptables -D INPUT -s <ip> -j DROP
|
||||
#
|
||||
actionunban = echo "-<ip>" >> /tmp/fail2ban.dummy
|
||||
|
||||
[Init]
|
||||
|
||||
init = 123
|
||||
@@ -0,0 +1,66 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: fwstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstart = touch <tmpfile>
|
||||
|
||||
# Option: fwend
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstop = rm -f <tmpfile>
|
||||
|
||||
# Option: fwcheck
|
||||
# Notes.: command executed once before each fwban command
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actioncheck =
|
||||
|
||||
# Option: fwban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <failtime> unix timestamp of the last failure
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
# Default: iptables -I INPUT 1 -s <ip> -j DROP
|
||||
#
|
||||
actionban = IP=<ip> &&
|
||||
echo "ALL: $IP" >> <file>
|
||||
|
||||
# Option: fwunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# <unbantime> unix timestamp of the unban time
|
||||
# Values: CMD
|
||||
# Default: iptables -D INPUT -s <ip> -j DROP
|
||||
#
|
||||
actionunban = IP=<ip> &&
|
||||
grep -v "ALL: $IP" <file> > <tmpfile> &&
|
||||
mv <tmpfile> <file>
|
||||
|
||||
[Init]
|
||||
|
||||
# Option: file
|
||||
# Notes.: hosts.deny file path.
|
||||
# Values: STR Default: /etc/hosts.deny
|
||||
#
|
||||
file = /etc/hosts.deny
|
||||
|
||||
# Option: file
|
||||
# Notes.: hosts.deny temporary file path.
|
||||
# Values: STR Default: /etc/hostsdeny.failban
|
||||
#
|
||||
tmpfile = /tmp/hosts.deny.tmp
|
||||
@@ -0,0 +1,71 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: fwstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstart = iptables -N fail2ban-<name>
|
||||
iptables -A fail2ban-<name> -j RETURN
|
||||
iptables -I INPUT -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
|
||||
# Option: fwend
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstop = iptables -D INPUT -p <protocol> --dport <port> -j fail2ban-<name>
|
||||
iptables -F fail2ban-<name>
|
||||
iptables -X fail2ban-<name>
|
||||
|
||||
# Option: fwcheck
|
||||
# Notes.: command executed once before each fwban command
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actioncheck = iptables -L INPUT | grep -q fail2ban-<name>
|
||||
|
||||
# Option: fwban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <failtime> unix timestamp of the last failure
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
# Default: iptables -I INPUT 1 -s <ip> -j DROP
|
||||
#
|
||||
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
|
||||
|
||||
# Option: fwunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# <unbantime> unix timestamp of the unban time
|
||||
# Values: CMD
|
||||
# Default: iptables -D INPUT -s <ip> -j DROP
|
||||
#
|
||||
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
# Option: port
|
||||
# Notes.: specifies port to monitor
|
||||
# Values: [ NUM | STRING ] Default:
|
||||
#
|
||||
port = ssh
|
||||
|
||||
# Option: protocol
|
||||
# Notes.: internally used by config reader for interpolations.
|
||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
||||
#
|
||||
protocol = tcp
|
||||
@@ -0,0 +1,71 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision: 254 $
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: fwstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstart = echo -en "Hi,\n
|
||||
The jail <name> has been started successfuly.\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
|
||||
|
||||
# Option: fwend
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstop = echo -en "Hi,\n
|
||||
The jail <name> has been stopped.\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
|
||||
|
||||
# Option: fwcheck
|
||||
# Notes.: command executed once before each fwban command
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actioncheck =
|
||||
|
||||
# Option: fwban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <failtime> unix timestamp of the last failure
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
# Default: iptables -I INPUT 1 -s <ip> -j DROP
|
||||
#
|
||||
actionban = echo -en "Hi,\n
|
||||
The IP <ip> has just been banned by Fail2Ban after
|
||||
<failures> attempts against <name>.\n\n
|
||||
Here are more information about <ip>:\n
|
||||
`whois <ip>`\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip>" <dest>
|
||||
|
||||
# Option: fwunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# <unbantime> unix timestamp of the unban time
|
||||
# Values: CMD
|
||||
# Default: iptables -D INPUT -s <ip> -j DROP
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
# Destinataire of the mail
|
||||
#
|
||||
dest = root
|
||||
@@ -0,0 +1,69 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision: 254 $
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: fwstart
|
||||
# Notes.: command executed once at the start of Fail2Ban.
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstart = echo -en "Hi,\n
|
||||
The jail <name> has been started successfuly.\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: started" <dest>
|
||||
|
||||
# Option: fwend
|
||||
# Notes.: command executed once at the end of Fail2Ban
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actionstop = echo -en "Hi,\n
|
||||
The jail <name> has been stopped.\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: stopped" <dest>
|
||||
|
||||
# Option: fwcheck
|
||||
# Notes.: command executed once before each fwban command
|
||||
# Values: CMD Default:
|
||||
#
|
||||
actioncheck =
|
||||
|
||||
# Option: fwban
|
||||
# Notes.: command executed when banning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <failures> number of failures
|
||||
# <failtime> unix timestamp of the last failure
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# Values: CMD
|
||||
# Default: iptables -I INPUT 1 -s <ip> -j DROP
|
||||
#
|
||||
actionban = echo -en "Hi,\n
|
||||
The IP <ip> has just been banned by Fail2Ban after
|
||||
<failures> attempts against <name>.\n
|
||||
Regards,\n
|
||||
Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip>" <dest>
|
||||
|
||||
# Option: fwunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
# command is executed with Fail2Ban user rights.
|
||||
# Tags: <ip> IP address
|
||||
# <bantime> unix timestamp of the ban time
|
||||
# <unbantime> unix timestamp of the unban time
|
||||
# Values: CMD
|
||||
# Default: iptables -D INPUT -s <ip> -j DROP
|
||||
#
|
||||
actionunban =
|
||||
|
||||
[Init]
|
||||
|
||||
# Defaut name of the chain
|
||||
#
|
||||
name = default
|
||||
|
||||
# Destinataire of the mail
|
||||
#
|
||||
dest = root
|
||||
@@ -0,0 +1,24 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: loglevel
|
||||
# Notes.: Set the log level output.
|
||||
# 1 = ERROR
|
||||
# 2 = WARN
|
||||
# 3 = INFO
|
||||
# 4 = DEBUG
|
||||
# Values: NUM Default: 3
|
||||
#
|
||||
loglevel = 3
|
||||
|
||||
# Option: logtarget
|
||||
# Notes.: Set the log target. This could be a file, SYSLOG, STDERR.
|
||||
# Values: STDERR SYSLOG file Default: /var/log/fail2ban.log
|
||||
#
|
||||
logtarget = /var/log/fail2ban.log
|
||||
@@ -0,0 +1,42 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: maxretry
|
||||
# Notes.: number of failures before IP gets banned.
|
||||
# Values: NUM Default: 5
|
||||
#
|
||||
maxretry = 5
|
||||
|
||||
# Option: logfile
|
||||
# Notes.: logfile to monitor.
|
||||
# Values: FILE Default: /var/log/httpd/access_log
|
||||
#
|
||||
logfile = /var/log/httpd/access_log
|
||||
|
||||
# Option: timeregex
|
||||
# Notes.: regex to match timestamp in Apache logfile. For TAI64N format,
|
||||
# use timeregex = @[0-9a-f]{24}
|
||||
# Values: [Wed Jan 05 15:08:01 2005]
|
||||
# Default: \S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}
|
||||
#
|
||||
timeregex = \S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}
|
||||
|
||||
# Option: timepattern
|
||||
# Notes.: format used in "timeregex" fields definition. Note that '%' must be
|
||||
# escaped with '%' (see http://rgruet.free.fr/PQR2.3.html#timeModule).
|
||||
# For TAI64N format, use timepattern = tai64n
|
||||
# Values: TEXT Default: %%a %%b %%d %%H:%%M:%%S %%Y
|
||||
#
|
||||
timepattern = %%a %%b %%d %%H:%%M:%%S %%Y
|
||||
|
||||
# Option: failregex
|
||||
# Notes.: regex to match the password failure messages in the logfile.
|
||||
# Values: TEXT Default: authentication failure|user .* not found
|
||||
#
|
||||
failregex = authentication failure|user .* not found
|
||||
@@ -0,0 +1,43 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: maxretry
|
||||
# Notes.: number of failures before IP gets banned.
|
||||
# Values: NUM Default: 5
|
||||
#
|
||||
maxretry = 5
|
||||
|
||||
# Option: logpath
|
||||
# Notes.: logfile to monitor.
|
||||
# Values: FILE Default: /var/log/secure
|
||||
#
|
||||
logpath = /var/log/secure
|
||||
|
||||
# Option: timeregex
|
||||
# Notes.: regex to match timestamp in SSH logfile. For TAI64N format,
|
||||
# use timeregex = @[0-9a-f]{24}
|
||||
# Values: [Mar 7 17:53:28]
|
||||
# Default: \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}
|
||||
#
|
||||
timeregex = \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}
|
||||
|
||||
# Option: timepattern
|
||||
# Notes.: format used in "timeregex" fields definition. Note that '%' must be
|
||||
# escaped with '%' (see http://rgruet.free.fr/PQR2.3.html#timeModule).
|
||||
# For TAI64N format, use timepattern = tai64n
|
||||
# Values: TEXT Default: %%b %%d %%H:%%M:%%S
|
||||
#
|
||||
timepattern = %%b %%d %%H:%%M:%%S
|
||||
|
||||
# Option: failregex
|
||||
# Notes.: regex to match the password failures messages in the logfile.
|
||||
# Values: TEXT Default: Authentication failure|Failed password|Invalid user
|
||||
#
|
||||
failregex = (?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) (?:::f{4,6}:)?(?P<host>\S*)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[Definition]
|
||||
|
||||
# Option: maxretry
|
||||
# Notes.: number of failures before IP gets banned.
|
||||
# Values: NUM Default: 5
|
||||
#
|
||||
maxretry = 5
|
||||
|
||||
# Option: logfile
|
||||
# Notes.: logfile to monitor.
|
||||
# Values: FILE Default: /var/log/vsftpd.log
|
||||
#
|
||||
logfile = /var/log/vsftpd.log
|
||||
|
||||
# Option: timeregex
|
||||
# Notes.: regex to match timestamp in VSFTPD logfile.
|
||||
# Values: [Mar 7 17:53:28]
|
||||
# Default: \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}
|
||||
#
|
||||
timeregex = \S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}
|
||||
|
||||
# Option: timepattern
|
||||
# Notes.: format used in "timeregex" fields definition. Note that '%' must be
|
||||
# escaped with '%' (see http://rgruet.free.fr/PQR2.3.html#timeModule)
|
||||
# Values: TEXT Default: %%b %%d %%H:%%M:%%S
|
||||
#
|
||||
timepattern = %%b %%d %%H:%%M:%%S
|
||||
|
||||
# Option: failregex
|
||||
# Notes.: regex to match the password failures messages in the logfile.
|
||||
# Values: TEXT Default: Authentication failure|Failed password|Invalid user
|
||||
#
|
||||
failregex = FAIL LOGIN
|
||||
@@ -0,0 +1,15 @@
|
||||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
[dummy]
|
||||
|
||||
enabled = false
|
||||
filter = sshd
|
||||
action = hostsdeny[file=/tmp/hosts.deny]
|
||||
mail[name=SSH, dest=cyril.jaquier@bluewin.ch]
|
||||
maxretry = 2
|
||||
bantime = 10
|
||||
+1237
File diff suppressed because it is too large
Load Diff
Executable
+269
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import sys, string, os, pickle, re, logging, getopt, time
|
||||
|
||||
# Inserts our own modules path first in the list
|
||||
# fix for bug #343821
|
||||
sys.path.insert(1, "/usr/lib/fail2ban")
|
||||
|
||||
# Now we can import our modules
|
||||
from version import version
|
||||
from client.csocket import CSocket
|
||||
from client.configurator import Configurator
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.client")
|
||||
|
||||
##
|
||||
#
|
||||
# @todo This class needs cleanup.
|
||||
|
||||
class Fail2banClient:
|
||||
|
||||
def __init__(self):
|
||||
self.argv = None
|
||||
self.stream = None
|
||||
self.conf = dict()
|
||||
self.conf["conf"] = "/etc/fail2ban"
|
||||
self.conf["dump"] = False
|
||||
self.conf["force"] = False
|
||||
self.conf["verbose"] = 2
|
||||
|
||||
def dispUsage(self):
|
||||
""" Prints Fail2Ban command line options and exits
|
||||
"""
|
||||
print "Usage: "+self.argv[0]+" [OPTIONS] <COMMAND>"
|
||||
print
|
||||
print "Fail2Ban v" + version + " reads log file that contains password failure report"
|
||||
print "and bans the corresponding IP addresses using firewall rules."
|
||||
print
|
||||
print " <COMMAND>"
|
||||
print
|
||||
print " start start the server and the jails"
|
||||
print " reload reload the configuration"
|
||||
print " stop stop all jails and terminate the server"
|
||||
print " status get the current status"
|
||||
print
|
||||
print " set loglevel <LEVEL> set loglevel to <LEVEL>"
|
||||
print " get loglevel get loglevel"
|
||||
print " set logtarget <TARGET> set log target to <TARGET>"
|
||||
print " get logtarget get log target"
|
||||
print
|
||||
print " add <JAIL> create <JAIL>"
|
||||
print " set <JAIL> <CMD> set the <CMD> value for <JAIL>"
|
||||
print " get <JAIL> <CMD> get the <CMD> value for <JAIL>"
|
||||
print " start <JAIL> start <JAIL>"
|
||||
print " stop <JAIL> stop <JAIL>. The jail is removed"
|
||||
print " status <JAIL> get the current status of <JAIL>"
|
||||
print
|
||||
print " [OPTIONS]"
|
||||
print
|
||||
print " -c <DIR> configuration directory"
|
||||
print " -d dump configuration. For debugging"
|
||||
print " -v increase verbosity"
|
||||
print " -q decrease verbosity"
|
||||
print " -x force execution of the server"
|
||||
print " -h display this help message"
|
||||
print
|
||||
print "Report bugs to <lostcontrol@users.sourceforge.net>"
|
||||
|
||||
def getCmdLineOptions(self, optList):
|
||||
""" Gets the command line options
|
||||
"""
|
||||
for opt in optList:
|
||||
if opt[0] == "-c":
|
||||
self.conf["conf"] = opt[1]
|
||||
elif opt[0] == "-d":
|
||||
self.conf["dump"] = True
|
||||
elif opt[0] == "-v":
|
||||
self.conf["verbose"] = self.conf["verbose"] + 1
|
||||
elif opt[0] == "-q":
|
||||
self.conf["verbose"] = self.conf["verbose"] - 1
|
||||
elif opt[0] == "-x":
|
||||
self.conf["force"] = True
|
||||
elif opt[0] in ["-h", "--help"]:
|
||||
self.dispUsage()
|
||||
sys.exit(0)
|
||||
|
||||
def ping(self):
|
||||
return self.processCmd([["ping"]], False)
|
||||
|
||||
@staticmethod
|
||||
def processCmd(cmd, showRet = True):
|
||||
for c in cmd:
|
||||
try:
|
||||
client = CSocket()
|
||||
except Exception, e:
|
||||
if showRet:
|
||||
logSys.error(e)
|
||||
logSys.error("Arrggh... Start the server first")
|
||||
return False
|
||||
ret = client.send(c)
|
||||
if ret[0] == 0:
|
||||
logSys.debug("OK : " + `ret[1]`)
|
||||
if showRet:
|
||||
print `ret[1]`
|
||||
else:
|
||||
logSys.error("NOK: " + `ret[1].args`)
|
||||
return False
|
||||
return True
|
||||
|
||||
##
|
||||
# Process a command line.
|
||||
#
|
||||
# Process one command line and exit.
|
||||
# @param cmd the command line
|
||||
|
||||
def processCommand(self, cmd):
|
||||
if self.conf["dump"]:
|
||||
self.readConfig()
|
||||
self.dumpConfig(self.stream)
|
||||
return True
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
self.dispUsage()
|
||||
return False
|
||||
|
||||
if len(cmd) == 1 and cmd[0] == "start":
|
||||
if self.ping():
|
||||
logSys.info("Server already running")
|
||||
return False
|
||||
else:
|
||||
self.startServerAsync(self.conf["force"])
|
||||
# Read the config while the server is starting
|
||||
self.readConfig()
|
||||
try:
|
||||
# Wait for the server to start
|
||||
self.waitOnServer()
|
||||
# Configure the server
|
||||
self.processCmd(self.stream, False)
|
||||
return True
|
||||
except ServerExecutionException:
|
||||
logSys.error("Could not start server. Try -x option")
|
||||
return False
|
||||
elif len(cmd) == 1 and cmd[0] == "reload":
|
||||
if self.ping():
|
||||
self.readConfig()
|
||||
self.processCmd(['stop', 'all'], False)
|
||||
# Configure the server
|
||||
return self.processCmd(self.stream, False)
|
||||
else:
|
||||
logSys.error("Could not find server")
|
||||
return False
|
||||
else:
|
||||
return self.processCmd([cmd])
|
||||
|
||||
|
||||
##
|
||||
# Start Fail2Ban server.
|
||||
#
|
||||
# Start the Fail2ban server in daemon mode.
|
||||
|
||||
def startServerAsync(self, force = False):
|
||||
args = list()
|
||||
|
||||
args.append("fail2ban-server")
|
||||
args.append("-b")
|
||||
if force:
|
||||
args.append("-x")
|
||||
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
os.execv("fail2ban-server", args)
|
||||
|
||||
|
||||
def waitOnServer(self):
|
||||
# Wait for the server to start
|
||||
cnt = 0
|
||||
while not self.ping():
|
||||
if cnt > 10:
|
||||
raise ServerExecutionException("Failed to start server")
|
||||
time.sleep(0.1)
|
||||
cnt = cnt + 1
|
||||
|
||||
|
||||
def start(self, argv):
|
||||
# Command line options
|
||||
self.argv = argv
|
||||
|
||||
# Reads the command line options.
|
||||
try:
|
||||
cmdOpts = 'hc:xdvq'
|
||||
cmdLongOpts = ['help']
|
||||
optList, args = getopt.getopt(self.argv[1:], cmdOpts, cmdLongOpts)
|
||||
except getopt.GetoptError:
|
||||
self.dispUsage()
|
||||
|
||||
self.getCmdLineOptions(optList)
|
||||
|
||||
verbose = self.conf["verbose"]
|
||||
if verbose <= 0:
|
||||
logSys.setLevel(logging.ERROR)
|
||||
elif verbose == 1:
|
||||
logSys.setLevel(logging.WARN)
|
||||
elif verbose == 2:
|
||||
logSys.setLevel(logging.INFO)
|
||||
else:
|
||||
logSys.setLevel(logging.DEBUG)
|
||||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
# set a format which is simpler for console use
|
||||
formatter = logging.Formatter('%(levelname)-6s %(message)s')
|
||||
# tell the handler to use this format
|
||||
stdout.setFormatter(formatter)
|
||||
logSys.addHandler(stdout)
|
||||
|
||||
return self.processCommand(args)
|
||||
|
||||
def readConfig(self):
|
||||
# Read the configuration
|
||||
cfg = Configurator()
|
||||
cfg.setBaseDir(self.conf["conf"])
|
||||
cfg.readAll()
|
||||
cfg.getAllOptions()
|
||||
cfg.convertToProtocol()
|
||||
self.stream = cfg.getConfigStream()
|
||||
|
||||
@staticmethod
|
||||
def dumpConfig(cmd):
|
||||
for c in cmd:
|
||||
print c
|
||||
return True
|
||||
|
||||
|
||||
class ServerExecutionException(Exception):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
client = Fail2banClient()
|
||||
# Exit with correct return value
|
||||
if client.start(sys.argv):
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(-1)
|
||||
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import locale, getopt, sys
|
||||
|
||||
# Inserts our own modules path first in the list
|
||||
# fix for bug #343821
|
||||
sys.path.insert(1, "/usr/lib/fail2ban")
|
||||
|
||||
from version import version
|
||||
from server.server import Server
|
||||
from utils.process import *
|
||||
|
||||
##
|
||||
# \mainpage Fail2Ban
|
||||
#
|
||||
# \section Introduction
|
||||
#
|
||||
# Fail2ban is designed to protect your server against brute force attacks.
|
||||
# Its first goal was to protect a SSH server.
|
||||
|
||||
class Fail2banServer:
|
||||
|
||||
def __init__(self):
|
||||
self.server = None
|
||||
self.argv = None
|
||||
self.conf = dict()
|
||||
self.conf["background"] = True
|
||||
self.conf["force"] = False
|
||||
|
||||
def dispUsage(self):
|
||||
""" Prints Fail2Ban command line options and exits
|
||||
"""
|
||||
print "Usage: "+self.argv[0]+" [OPTIONS]"
|
||||
print
|
||||
print "Fail2Ban v" + version + " reads log file that contains password failure report"
|
||||
print "and bans the corresponding IP addresses using firewall rules."
|
||||
print
|
||||
print " -b start in background"
|
||||
print " -f start in foreground"
|
||||
print " -x force execution of the server"
|
||||
print " -h display this help message"
|
||||
print
|
||||
print "Report bugs to <lostcontrol@users.sourceforge.net>"
|
||||
sys.exit(0)
|
||||
|
||||
def getCmdLineOptions(self, optList):
|
||||
""" Gets the command line options
|
||||
"""
|
||||
for opt in optList:
|
||||
if opt[0] == "-b":
|
||||
self.conf["background"] = True
|
||||
if opt[0] == "-f":
|
||||
self.conf["background"] = False
|
||||
if opt[0] == "-x":
|
||||
self.conf["force"] = True
|
||||
if opt[0] in ["-h", "--help"]:
|
||||
self.dispUsage()
|
||||
|
||||
#def sigTERMhandler(signum, frame):
|
||||
# """ Handles the TERM signal when in daemon mode in order to
|
||||
# exit properly.
|
||||
# """
|
||||
# logSys.debug("Signal handler called with sig "+`signum`)
|
||||
# server.quit()
|
||||
|
||||
def start(self, argv):
|
||||
# Command line options
|
||||
self.argv = argv
|
||||
|
||||
# Reads the command line options.
|
||||
try:
|
||||
cmdOpts = 'bfxh'
|
||||
cmdLongOpts = ['help']
|
||||
optList, args = getopt.getopt(self.argv[1:], cmdOpts, cmdLongOpts)
|
||||
except getopt.GetoptError:
|
||||
self.dispUsage()
|
||||
|
||||
self.getCmdLineOptions(optList)
|
||||
|
||||
if self.conf["background"]:
|
||||
retCode = createDaemon()
|
||||
#signal.signal(signal.SIGTERM, sigTERMhandler)
|
||||
if not retCode:
|
||||
print "Unable to start daemon"
|
||||
sys.exit(-1)
|
||||
|
||||
try:
|
||||
self.server = Server()
|
||||
self.server.start(self.conf["force"])
|
||||
except Exception, e:
|
||||
print e
|
||||
self.server.quit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
server = Fail2banServer()
|
||||
server.start(sys.argv)
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
|
||||
import unittest, logging, sys
|
||||
|
||||
# Inserts our own modules path first in the list
|
||||
# fix for bug #343821
|
||||
sys.path.insert(1, "/usr/lib/fail2ban")
|
||||
|
||||
from version import version
|
||||
from testcases import banmanagertestcase
|
||||
from testcases import clientreadertestcase
|
||||
from testcases import failmanagertestcase
|
||||
from testcases import filtertestcase
|
||||
from testcases import servertestcase
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
logSys.addHandler(stdout)
|
||||
logSys.setLevel(logging.FATAL)
|
||||
|
||||
print "Fail2ban " + version + " test suite. Please wait..."
|
||||
|
||||
tests = unittest.TestSuite()
|
||||
|
||||
# Filter
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.IgnoreIP))
|
||||
tests.addTest(unittest.makeSuite(filtertestcase.LogFile))
|
||||
#tests.addTest(unittest.makeSuite(filtertestcase.GetFailures))
|
||||
# Server
|
||||
#tests.addTest(unittest.makeSuite(servertestcase.StartStop))
|
||||
#tests.addTest(unittest.makeSuite(servertestcase.Transmitter))
|
||||
# FailManager
|
||||
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
||||
# BanManager
|
||||
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
|
||||
# ClientReader
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
|
||||
|
||||
# Tests runner
|
||||
testRunner = unittest.TextTestRunner()
|
||||
testRunner.run(tests)
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
kill `ps ax|grep fail2ban|grep -v grep|awk '{print $1}'`
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
@@ -0,0 +1,223 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import time, logging, os
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.actions.action")
|
||||
|
||||
##
|
||||
# Execute commands.
|
||||
#
|
||||
# This class reads the failures from the Jail queue and decide if an
|
||||
# action has to be taken. A BanManager take care of the banned IP
|
||||
# addresses.
|
||||
|
||||
class Action:
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.cInfo = dict()
|
||||
## Command executed in order to initialize the system.
|
||||
self.actionStart = ''
|
||||
## Command executed when an IP address gets banned.
|
||||
self.actionBan = ''
|
||||
## Command executed when an IP address gets removed.
|
||||
self.actionUnban = ''
|
||||
## Command executed in order to check requirements.
|
||||
self.actionCheck = ''
|
||||
## Command executed in order to stop the system.
|
||||
self.actionStop = ''
|
||||
logSys.debug("Created Action")
|
||||
|
||||
def setName(self, name):
|
||||
self.name = name
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def setCInfo(self, key, value):
|
||||
self.cInfo[key] = value
|
||||
|
||||
def getCInfo(self, key):
|
||||
return self.cInfo[key]
|
||||
|
||||
def delCInfo(self, key):
|
||||
del self.cInfo[key]
|
||||
|
||||
##
|
||||
# Set the "start" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionStart(self, value):
|
||||
self.actionStart = value
|
||||
logSys.info("Set actionStart = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "start" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionStart(self):
|
||||
return self.actionStart
|
||||
|
||||
def execActionStart(self):
|
||||
startCmd = Action.replaceTag(self.actionStart, self.cInfo)
|
||||
return Action.executeCmd(startCmd)
|
||||
|
||||
##
|
||||
# Set the "ban" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionBan(self, value):
|
||||
self.actionBan = value
|
||||
logSys.info("Set actionBan = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "ban" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionBan(self):
|
||||
return self.actionBan
|
||||
|
||||
def execActionBan(self, aInfo):
|
||||
return self.processCmd(self.actionBan, aInfo);
|
||||
|
||||
##
|
||||
# Set the "unban" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionUnban(self, value):
|
||||
self.actionUnban = value
|
||||
logSys.info("Set actionUnban = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "unban" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionUnban(self):
|
||||
return self.actionUnban
|
||||
|
||||
def execActionUnban(self, aInfo):
|
||||
return self.processCmd(self.actionUnban, aInfo);
|
||||
|
||||
##
|
||||
# Set the "check" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionCheck(self, value):
|
||||
self.actionCheck = value
|
||||
logSys.info("Set actionCheck = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "check" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionCheck(self):
|
||||
return self.actionCheck
|
||||
|
||||
##
|
||||
# Set the "stop" command.
|
||||
#
|
||||
# @param value the command
|
||||
|
||||
def setActionStop(self, value):
|
||||
self.actionStop = value
|
||||
logSys.info("Set actionStop = %s" % value)
|
||||
|
||||
##
|
||||
# Get the "stop" command.
|
||||
#
|
||||
# @return the command
|
||||
|
||||
def getActionStop(self):
|
||||
return self.actionStop
|
||||
|
||||
def execActionStop(self):
|
||||
stopCmd = Action.replaceTag(self.actionStop, self.cInfo)
|
||||
return Action.executeCmd(stopCmd)
|
||||
|
||||
@staticmethod
|
||||
def replaceTag(query, aInfo):
|
||||
""" Replace tags in query
|
||||
"""
|
||||
string = query
|
||||
for tag in aInfo:
|
||||
string = string.replace('<' + tag + '>', str(aInfo[tag]))
|
||||
# New line
|
||||
string = string.replace("<br>", '\n')
|
||||
return string
|
||||
|
||||
def processCmd(self, cmd, aInfo = None):
|
||||
""" Executes an OS command.
|
||||
"""
|
||||
if cmd == "":
|
||||
logSys.debug("Nothing to do")
|
||||
return True
|
||||
|
||||
checkCmd = Action.replaceTag(self.actionCheck, self.cInfo)
|
||||
if not Action.executeCmd(checkCmd):
|
||||
logSys.error("Invariant check failed. Trying to restore a sane" +
|
||||
" environment")
|
||||
stopCmd = Action.replaceTag(self.actionStop, self.cInfo)
|
||||
Action.executeCmd(stopCmd)
|
||||
startCmd = Action.replaceTag(self.actionStart, self.cInfo)
|
||||
Action.executeCmd(startCmd)
|
||||
if not Action.executeCmd(checkCmd):
|
||||
logSys.fatal("Unable to restore environment")
|
||||
return False
|
||||
|
||||
# Replace tags
|
||||
if not aInfo == None:
|
||||
realCmd = Action.replaceTag(cmd, aInfo)
|
||||
else:
|
||||
realCmd = cmd
|
||||
|
||||
# Replace static fields
|
||||
realCmd = Action.replaceTag(realCmd, self.cInfo)
|
||||
|
||||
return Action.executeCmd(realCmd)
|
||||
|
||||
@staticmethod
|
||||
def executeCmd(realCmd):
|
||||
logSys.debug(realCmd)
|
||||
retval = os.system(realCmd)
|
||||
#if not retval == 0:
|
||||
# logSys.error("'" + cmd + "' returned " + `retval`)
|
||||
# raise Exception("Execution of command '%s' failed" % cmd)
|
||||
if retval == 0:
|
||||
return True
|
||||
else:
|
||||
logSys.error("%s returned %x" % (realCmd, retval))
|
||||
return False
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from banmanager import BanManager
|
||||
from failmanager import FailManager, FailManagerEmpty
|
||||
from jailthread import JailThread
|
||||
from action import Action
|
||||
import time, logging, os
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.actions")
|
||||
|
||||
##
|
||||
# Execute commands.
|
||||
#
|
||||
# This class reads the failures from the Jail queue and decide if an
|
||||
# action has to be taken. A BanManager take care of the banned IP
|
||||
# addresses.
|
||||
|
||||
class Actions(JailThread):
|
||||
|
||||
##
|
||||
# Constructor.
|
||||
#
|
||||
# Initialize the filter object with default values.
|
||||
# @param jail the jail object
|
||||
|
||||
def __init__(self, jail):
|
||||
JailThread.__init__(self, jail)
|
||||
## The jail which contains this action.
|
||||
self.jail = jail
|
||||
self.actions = list()
|
||||
## The ban manager.
|
||||
self.banManager = BanManager()
|
||||
|
||||
def addAction(self, name):
|
||||
action = Action(name)
|
||||
self.actions.append(action)
|
||||
|
||||
def delAction(self, name):
|
||||
for action in self.actions:
|
||||
if action.getName() == name:
|
||||
self.actions.remove(action)
|
||||
break
|
||||
|
||||
def getAction(self, name):
|
||||
for action in self.actions:
|
||||
if action.getName() == name:
|
||||
return action
|
||||
raise KeyError
|
||||
|
||||
def getLastAction(self):
|
||||
action = self.actions.pop()
|
||||
self.actions.append(action)
|
||||
return action
|
||||
|
||||
##
|
||||
# Set the ban time.
|
||||
#
|
||||
# @param value the time
|
||||
|
||||
def setBanTime(self, value):
|
||||
self.banManager.setBanTime(value)
|
||||
logSys.info("Set banTime = %s" % value)
|
||||
|
||||
##
|
||||
# Get the ban time.
|
||||
#
|
||||
# @return the time
|
||||
|
||||
def getBanTime(self):
|
||||
return self.banManager.getBanTime()
|
||||
|
||||
##
|
||||
# Main loop.
|
||||
#
|
||||
# This function is the main loop of the thread. It checks the Jail
|
||||
# queue and executes commands when an IP address is banned.
|
||||
# @return True when the thread exits nicely
|
||||
|
||||
def run(self):
|
||||
for action in self.actions:
|
||||
action.execActionStart()
|
||||
self.setActive(True)
|
||||
while self.isActive():
|
||||
if not self.isIdle:
|
||||
#logSys.debug(self.jail.getName() + ": action")
|
||||
ret = self.checkBan()
|
||||
if not ret:
|
||||
self.checkUnBan()
|
||||
time.sleep(self.sleepTime)
|
||||
else:
|
||||
time.sleep(self.sleepTime)
|
||||
self.flushBan()
|
||||
for action in self.actions:
|
||||
action.execActionStop()
|
||||
logSys.debug(self.jail.getName() + ": action terminated")
|
||||
return True
|
||||
|
||||
##
|
||||
# Check for IP address to ban.
|
||||
#
|
||||
# Look in the Jail queue for FailTicket. If a ticket is available,
|
||||
# it executes the "ban" command and add a ticket to the BanManager.
|
||||
# @return True if an IP address get banned
|
||||
|
||||
def checkBan(self):
|
||||
logSys.debug("Check for IP address to ban")
|
||||
ticket = self.jail.getFailTicket()
|
||||
if ticket != False:
|
||||
aInfo = dict()
|
||||
bTicket = BanManager.createBanTicket(ticket)
|
||||
aInfo["ip"] = bTicket.getIP()
|
||||
aInfo["failures"] = bTicket.getAttempt()
|
||||
logSys.info("Ban %s" % aInfo["ip"])
|
||||
for action in self.actions:
|
||||
action.execActionBan(aInfo)
|
||||
self.banManager.addBanTicket(bTicket)
|
||||
return True
|
||||
return False
|
||||
|
||||
##
|
||||
# Check for IP address to unban.
|
||||
#
|
||||
# Unban IP address which are outdated.
|
||||
|
||||
def checkUnBan(self):
|
||||
logSys.debug("Check for IP address to unban")
|
||||
for ticket in self.banManager.unBanList(time.time()):
|
||||
aInfo = dict()
|
||||
aInfo["ip"] = ticket.getIP()
|
||||
logSys.info("Unban %s" % aInfo["ip"])
|
||||
for action in self.actions:
|
||||
action.execActionUnban(aInfo)
|
||||
|
||||
##
|
||||
# Flush the ban list.
|
||||
#
|
||||
# Unban all IP address which are still in the banning list.
|
||||
|
||||
def flushBan(self):
|
||||
logSys.debug("Flush ban list")
|
||||
for ticket in self.banManager.flushBanList():
|
||||
aInfo = dict()
|
||||
aInfo["ip"] = ticket.getIP()
|
||||
logSys.info("Unban %s" % aInfo["ip"])
|
||||
for action in self.actions:
|
||||
action.execActionUnban(aInfo)
|
||||
|
||||
##
|
||||
# Get the status of the filter.
|
||||
#
|
||||
# Get some informations about the filter state such as the total
|
||||
# number of failures.
|
||||
# @return a list with tuple
|
||||
|
||||
def status(self):
|
||||
ret = [("Currently banned", self.banManager.size()),
|
||||
("Total banned", self.banManager.getBanTotal())]
|
||||
return ret
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from banticket import BanTicket
|
||||
from threading import Lock
|
||||
import time, logging
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.action")
|
||||
|
||||
##
|
||||
# Banning Manager.
|
||||
#
|
||||
# Manage the banned IP addresses. Convert FailTicket to BanTicket.
|
||||
# This class is mainly used by the Action class.
|
||||
|
||||
class BanManager:
|
||||
|
||||
##
|
||||
# Constructor.
|
||||
#
|
||||
# Initialize members with default values.
|
||||
|
||||
def __init__(self):
|
||||
## Mutex used to protect the ban list.
|
||||
self.lock = Lock()
|
||||
## The ban list.
|
||||
self.banList = list()
|
||||
## The amount of time an IP address gets banned.
|
||||
self.banTime = 600
|
||||
## Total number of banned IP address
|
||||
self.banTotal = 0
|
||||
|
||||
##
|
||||
# Set the ban time.
|
||||
#
|
||||
# Set the amount of time an IP address get banned.
|
||||
# @param value the time
|
||||
|
||||
def setBanTime(self, value):
|
||||
self.banTime = int(value)
|
||||
|
||||
##
|
||||
# Get the ban time.
|
||||
#
|
||||
# Get the amount of time an IP address get banned.
|
||||
# @return the time
|
||||
|
||||
def getBanTime(self):
|
||||
return self.banTime
|
||||
|
||||
##
|
||||
# Set the total number of banned address.
|
||||
#
|
||||
# @param value total number
|
||||
|
||||
def setBanTotal(self, value):
|
||||
self.banTotal = value
|
||||
|
||||
##
|
||||
# Get the total number of banned address.
|
||||
#
|
||||
# @return the total number
|
||||
|
||||
def getBanTotal(self):
|
||||
return self.banTotal
|
||||
|
||||
##
|
||||
# Create a ban ticket.
|
||||
#
|
||||
# Create a BanTicket from a FailTicket. The timestamp of the BanTicket
|
||||
# is the current time. This is a static method.
|
||||
# @param ticket the FailTicket
|
||||
# @return a BanTicket
|
||||
|
||||
@staticmethod
|
||||
def createBanTicket(ticket):
|
||||
ip = ticket.getIP()
|
||||
#lastTime = ticket.getTime()
|
||||
lastTime = time.time()
|
||||
banTicket = BanTicket(ip, lastTime)
|
||||
banTicket.setAttempt(ticket.getAttempt())
|
||||
return banTicket
|
||||
|
||||
##
|
||||
# Add a ban ticket.
|
||||
#
|
||||
# Add a BanTicket instance into the ban list.
|
||||
# @param ticket the ticket
|
||||
# @return True if the IP address is not in the ban list
|
||||
|
||||
def addBanTicket(self, ticket):
|
||||
self.lock.acquire()
|
||||
if not self.inBanList(ticket):
|
||||
self.banList.append(ticket)
|
||||
self.banTotal += 1
|
||||
self.lock.release()
|
||||
return True
|
||||
self.lock.release()
|
||||
return False
|
||||
|
||||
##
|
||||
# Delete a ban ticket.
|
||||
#
|
||||
# Remove a BanTicket from the ban list.
|
||||
# @param ticket the ticket
|
||||
|
||||
def delBanTicket(self, ticket):
|
||||
self.banList.remove(ticket)
|
||||
|
||||
##
|
||||
# Get the size of the ban list.
|
||||
#
|
||||
# @return the size
|
||||
|
||||
def size(self):
|
||||
return len(self.banList)
|
||||
|
||||
##
|
||||
# Check if a ticket is in the list.
|
||||
#
|
||||
# Check if a BanTicket with a given IP address is already in the
|
||||
# ban list.
|
||||
# @param ticket the ticket
|
||||
# @return True if a ticket already exists
|
||||
|
||||
def inBanList(self, ticket):
|
||||
for i in self.banList:
|
||||
if ticket.getIP() == i.getIP():
|
||||
return True
|
||||
return False
|
||||
|
||||
##
|
||||
# Get the list of IP address to unban.
|
||||
#
|
||||
# Return a list of BanTicket which need to be unbanned.
|
||||
# @param time the time
|
||||
# @return the list of ticket to unban
|
||||
# @todo Check the delete operation
|
||||
|
||||
def unBanList(self, time):
|
||||
uBList = list()
|
||||
self.lock.acquire()
|
||||
for ticket in self.banList:
|
||||
if ticket.getTime() < time - self.banTime:
|
||||
uBList.append(ticket)
|
||||
self.delBanTicket(ticket)
|
||||
self.lock.release()
|
||||
return uBList
|
||||
|
||||
##
|
||||
# Flush the ban list.
|
||||
#
|
||||
# Get the ban list and initialize it with an empty one.
|
||||
# @return the complete ban list
|
||||
|
||||
def flushBanList(self):
|
||||
self.lock.acquire()
|
||||
uBList = self.banList
|
||||
self.banList = list()
|
||||
self.lock.release()
|
||||
return uBList
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import time, logging
|
||||
from ticket import Ticket
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
||||
##
|
||||
# Ban Ticket.
|
||||
#
|
||||
# This class extends the Ticket class. It is mainly used by the BanManager.
|
||||
|
||||
class BanTicket(Ticket):
|
||||
|
||||
##
|
||||
# Constructor.
|
||||
#
|
||||
# Call the Ticket (parent) constructor and initialize default
|
||||
# values.
|
||||
# @param ip the IP address
|
||||
# @param time the ban time
|
||||
|
||||
def __init__(self, ip, time):
|
||||
Ticket.__init__(self, ip, time)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import time, logging
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
||||
class FailData:
|
||||
|
||||
def __init__(self):
|
||||
self.retry = 0
|
||||
self.lastTime = 0
|
||||
|
||||
def setRetry(self, value):
|
||||
self.retry = value
|
||||
|
||||
def getRetry(self):
|
||||
return self.retry
|
||||
|
||||
def inc(self):
|
||||
self.retry += 1
|
||||
|
||||
def setLastTime(self, value):
|
||||
if value > self.lastTime:
|
||||
self.lastTime = value
|
||||
|
||||
def getLastTime(self):
|
||||
return self.lastTime
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from faildata import FailData
|
||||
from failticket import FailTicket
|
||||
from threading import Lock
|
||||
import time, logging
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.filter")
|
||||
|
||||
class FailManager:
|
||||
|
||||
def __init__(self):
|
||||
self.lock = Lock()
|
||||
self.failList = dict()
|
||||
self.maxRetry = 3
|
||||
self.maxTime = 600
|
||||
self.failTotal = 0
|
||||
|
||||
def setFailTotal(self, value):
|
||||
self.failTotal = value
|
||||
|
||||
def getFailTotal(self):
|
||||
return self.failTotal
|
||||
|
||||
def setMaxRetry(self, value):
|
||||
self.maxRetry = value
|
||||
|
||||
def getMaxRetry(self):
|
||||
return self.maxRetry
|
||||
|
||||
def setMaxTime(self, value):
|
||||
self.maxTime = value
|
||||
|
||||
def getMaxTime(self):
|
||||
return self.maxTime
|
||||
|
||||
def addFailure(self, ticket):
|
||||
self.lock.acquire()
|
||||
ip = ticket.getIP()
|
||||
unixTime = ticket.getTime()
|
||||
if self.failList.has_key(ip):
|
||||
fData = self.failList[ip]
|
||||
fData.inc()
|
||||
fData.setLastTime(unixTime)
|
||||
else:
|
||||
fData = FailData()
|
||||
fData.inc()
|
||||
fData.setLastTime(unixTime)
|
||||
self.failList[ip] = fData
|
||||
self.failTotal += 1
|
||||
self.lock.release()
|
||||
|
||||
def size(self):
|
||||
return len(self.failList)
|
||||
|
||||
def cleanup(self, time):
|
||||
self.lock.acquire()
|
||||
tmp = self.failList.copy()
|
||||
for item in tmp:
|
||||
if tmp[item].getLastTime() < time - self.maxTime:
|
||||
self.delFailure(item)
|
||||
self.lock.release()
|
||||
|
||||
def delFailure(self, ip):
|
||||
if self.failList.has_key(ip):
|
||||
del self.failList[ip]
|
||||
|
||||
def toBan(self):
|
||||
self.lock.acquire()
|
||||
for ip in self.failList:
|
||||
data = self.failList[ip]
|
||||
if data.getRetry() >= self.maxRetry:
|
||||
self.delFailure(ip)
|
||||
self.lock.release()
|
||||
# Create a FailTicket from BanData
|
||||
failTicket = FailTicket(ip, data.getLastTime())
|
||||
failTicket.setAttempt(data.getRetry())
|
||||
return failTicket
|
||||
self.lock.release()
|
||||
raise FailManagerEmpty
|
||||
|
||||
class FailManagerEmpty(Exception):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import time, logging
|
||||
from ticket import Ticket
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
||||
class FailTicket(Ticket):
|
||||
|
||||
def __init__(self, ip, time):
|
||||
Ticket.__init__(self, ip, time)
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from failmanager import FailManager
|
||||
from failmanager import FailManagerEmpty
|
||||
from failticket import FailTicket
|
||||
from jailthread import JailThread
|
||||
import time, logging, os, re, sys, socket
|
||||
|
||||
# Gets the instance of the logger.
|
||||
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
|
||||
# a Jail object.
|
||||
|
||||
class Filter(JailThread):
|
||||
|
||||
##
|
||||
# Constructor.
|
||||
#
|
||||
# Initialize the filter object with default values.
|
||||
# @param jail the jail object
|
||||
|
||||
def __init__(self, jail):
|
||||
JailThread.__init__(self, jail)
|
||||
## The jail which contains this filter.
|
||||
self.jail = jail
|
||||
## The failures manager.
|
||||
self.failManager = FailManager()
|
||||
## The log file handler.
|
||||
self.fileHandler = None
|
||||
## The log file path.
|
||||
self.logPath = ''
|
||||
## The regular expression matching the date.
|
||||
self.timeRegex = ''
|
||||
self.timeRegexObj = None
|
||||
## The pattern matching the date.
|
||||
self.timePattern = ''
|
||||
## The regular expression matching the failure.
|
||||
self.failRegex = ''
|
||||
self.failRegexObj = None
|
||||
## The amount of time to look back.
|
||||
self.findTime = 6000
|
||||
## The ignore IP list.
|
||||
self.ignoreIpList = []
|
||||
## The time of the last modification of the file.
|
||||
self.lastModTime = 0
|
||||
## The last position of the file.
|
||||
self.lastPos = 0
|
||||
## The last date in tht log file.
|
||||
self.lastDate = 0
|
||||
## The file statistics.
|
||||
self.logStats = None
|
||||
logSys.info("Created Filter")
|
||||
|
||||
##
|
||||
# Set the log file path
|
||||
#
|
||||
# @param value log file path
|
||||
|
||||
def setLogPath(self, value):
|
||||
self.logPath = value
|
||||
logSys.info("Set logfile = %s" % value)
|
||||
|
||||
##
|
||||
# Get the log file path
|
||||
#
|
||||
# @return log file path
|
||||
|
||||
def getLogPath(self):
|
||||
return self.logPath
|
||||
|
||||
##
|
||||
# Set the regular expression which matches the time.
|
||||
#
|
||||
# @param value the regular expression
|
||||
|
||||
def setTimeRegex(self, value):
|
||||
self.timeRegex = value
|
||||
self.timeRegexObj = re.compile(value)
|
||||
logSys.info("Set timeregex = %s" % value)
|
||||
|
||||
##
|
||||
# Get the regular expression which matches the time.
|
||||
#
|
||||
# @return the regular expression
|
||||
|
||||
def getTimeRegex(self):
|
||||
return self.timeRegex
|
||||
|
||||
##
|
||||
# Set the time pattern.
|
||||
#
|
||||
# @param value the time pattern
|
||||
|
||||
def setTimePattern(self, value):
|
||||
self.timePattern = value
|
||||
logSys.info("Set timepattern = %s" % value)
|
||||
|
||||
##
|
||||
# Get the time pattern.
|
||||
#
|
||||
# @return the time pattern
|
||||
|
||||
def getTimePattern(self):
|
||||
return self.timePattern
|
||||
|
||||
##
|
||||
# Set 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.
|
||||
# @param value the regular expression
|
||||
|
||||
def setFailRegex(self, value):
|
||||
self.failRegex = value
|
||||
self.failRegexObj = re.compile(value)
|
||||
logSys.info("Set failregex = %s" % value)
|
||||
|
||||
##
|
||||
# Get the regular expression which matches the failure.
|
||||
#
|
||||
# @return the regular expression
|
||||
|
||||
def getFailRegex(self):
|
||||
return self.failRegex
|
||||
|
||||
##
|
||||
# Set the time needed to find a failure.
|
||||
#
|
||||
# This value tells the filter how long it has to take failures into
|
||||
# account.
|
||||
# @param value the time
|
||||
|
||||
def setFindTime(self, value):
|
||||
self.findTime = value
|
||||
logSys.info("Set findtime = %s" % value)
|
||||
|
||||
##
|
||||
# Get the time needed to find a failure.
|
||||
#
|
||||
# @return the time
|
||||
|
||||
def getFindTime(self):
|
||||
return self.findTime
|
||||
|
||||
##
|
||||
# Set the maximum retry value.
|
||||
#
|
||||
# @param value the retry value
|
||||
|
||||
def setMaxRetry(self, value):
|
||||
self.failManager.setMaxRetry(value)
|
||||
logSys.info("Set maxRetry = %s" % value)
|
||||
|
||||
##
|
||||
# Get the maximum retry value.
|
||||
#
|
||||
# @return the retry value
|
||||
|
||||
def getMaxRetry(self):
|
||||
return self.failManager.getMaxRetry()
|
||||
|
||||
##
|
||||
# Set the maximum time a failure stays in the list.
|
||||
#
|
||||
# @param value the maximum time
|
||||
|
||||
def setMaxTime(self, value):
|
||||
self.failManager.setMaxTime(value)
|
||||
logSys.info("Set maxTime = %s" % value)
|
||||
|
||||
##
|
||||
# Get the maximum time a failure stays in the list.
|
||||
#
|
||||
# @return the time value
|
||||
|
||||
def getMaxTime(self):
|
||||
return self.failManager.getMaxTime()
|
||||
|
||||
##
|
||||
# Main loop.
|
||||
#
|
||||
# This function is the main loop of the thread. It checks if the
|
||||
# file has been modified and looks for failures.
|
||||
# @return True when the thread exits nicely
|
||||
|
||||
def run(self):
|
||||
self.setActive(True)
|
||||
while self.isActive():
|
||||
if not self.isIdle:
|
||||
if self.isModified():
|
||||
self.getFailures()
|
||||
try:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
except FailManagerEmpty:
|
||||
self.failManager.cleanup(time.time())
|
||||
time.sleep(self.sleepTime)
|
||||
else:
|
||||
time.sleep(self.sleepTime)
|
||||
logSys.debug(self.jail.getName() + ": filter terminated")
|
||||
return True
|
||||
|
||||
##
|
||||
# Add an IP to the ignore list.
|
||||
#
|
||||
# IP addresses in the ignore list are not taken into account
|
||||
# when finding failures. CIDR mask are also accepted.
|
||||
# @param ip IP address to ignore
|
||||
|
||||
def addIgnoreIP(self, ip):
|
||||
logSys.debug("Add " + ip + " to ignore list")
|
||||
self.ignoreIpList.append(ip)
|
||||
|
||||
##
|
||||
# Check if IP address is in the ignore list.
|
||||
#
|
||||
# Check if the given IP address matches an IP address or a CIDR
|
||||
# mask in the ignore list.
|
||||
# @param ip IP address
|
||||
# @return True if IP address is in ignore list
|
||||
|
||||
def inIgnoreIPList(self, ip):
|
||||
for i in self.ignoreIpList:
|
||||
s = i.split('/', 1)
|
||||
# IP address without CIDR mask
|
||||
if len(s) == 1:
|
||||
s.insert(1, '32')
|
||||
s[1] = long(s[1])
|
||||
try:
|
||||
a = DNSUtils.cidr(s[0], s[1])
|
||||
b = DNSUtils.cidr(ip, s[1])
|
||||
except Exception:
|
||||
return False
|
||||
if a == b:
|
||||
return True
|
||||
return False
|
||||
|
||||
##
|
||||
# Open the log file.
|
||||
|
||||
def openLogFile(self):
|
||||
""" Opens the log file specified on init.
|
||||
"""
|
||||
try:
|
||||
self.fileHandler = open(self.logPath)
|
||||
except OSError:
|
||||
logSys.error("Unable to open "+self.logPath)
|
||||
|
||||
##
|
||||
# Close the log file.
|
||||
|
||||
def closeLogFile(self):
|
||||
self.fileHandler.close()
|
||||
|
||||
##
|
||||
# Checks if the log file has been modified.
|
||||
#
|
||||
# Checks if the log file has been modified using os.stat().
|
||||
# @return True if log file has been modified
|
||||
|
||||
def isModified(self):
|
||||
try:
|
||||
self.logStats = os.stat(self.logPath)
|
||||
if self.lastModTime == self.logStats.st_mtime:
|
||||
return False
|
||||
else:
|
||||
logSys.debug(self.logPath + " has been modified")
|
||||
self.lastModTime = self.logStats.st_mtime
|
||||
return True
|
||||
except OSError:
|
||||
logSys.error("Unable to get stat on " + self.logPath)
|
||||
return False
|
||||
|
||||
##
|
||||
# Set the file position.
|
||||
#
|
||||
# Sets the file position. We must take care of log file rotation
|
||||
# and reset the position to 0 in that case. Use the log message
|
||||
# timestamp in order to detect this.
|
||||
|
||||
def setFilePos(self):
|
||||
line = self.fileHandler.readline()
|
||||
if self.lastDate < self.getTime(line):
|
||||
logSys.debug("Date " + `self.lastDate` + " is " + "smaller than " +
|
||||
`self.getTime(line)`)
|
||||
logSys.debug("Log rotation detected for " + self.logPath)
|
||||
self.lastPos = 0
|
||||
|
||||
logSys.debug("Setting file position to " + `self.lastPos` + " for " +
|
||||
self.logPath)
|
||||
self.fileHandler.seek(self.lastPos)
|
||||
|
||||
##
|
||||
# Get the file position.
|
||||
|
||||
def getFilePos(self):
|
||||
return self.fileHandler.tell()
|
||||
|
||||
##
|
||||
# Gets all the failure in the log file.
|
||||
#
|
||||
# Gets all the failure in the log file which are newer than
|
||||
# time.time()-self.findTime. When a failure is detected, a FailTicket
|
||||
# is created and is added to the FailManager.
|
||||
|
||||
def getFailures(self):
|
||||
ipList = dict()
|
||||
logSys.debug(self.logPath)
|
||||
self.openLogFile()
|
||||
self.setFilePos()
|
||||
lastLine = None
|
||||
for line in self.fileHandler:
|
||||
try:
|
||||
# Try to convert UTF-8 string to Latin-1
|
||||
line = line.decode('utf-8').encode('latin-1')
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
if not self.hasTime(line):
|
||||
# There is no valid time in this line
|
||||
continue
|
||||
lastLine = line
|
||||
for element in self.findFailure(line):
|
||||
ip = element[0]
|
||||
unixTime = element[1]
|
||||
if unixTime < time.time()-self.findTime:
|
||||
break
|
||||
if self.inIgnoreIPList(ip):
|
||||
logSys.debug("Ignore "+ip)
|
||||
continue
|
||||
logSys.debug("Found "+ip)
|
||||
self.failManager.addFailure(FailTicket(ip, unixTime))
|
||||
self.lastPos = self.getFilePos()
|
||||
if lastLine:
|
||||
self.lastDate = self.getTime(lastLine)
|
||||
self.closeLogFile()
|
||||
|
||||
##
|
||||
# Finds the failure in a line.
|
||||
#
|
||||
# Uses the failregex pattern to find it and timeregex in order
|
||||
# to find the logging time.
|
||||
# @return a dict with IP and timestamp.
|
||||
|
||||
def findFailure(self, line):
|
||||
failList = list()
|
||||
match = self.failRegexObj.search(line)
|
||||
if match:
|
||||
timeMatch = self.timeRegexObj.search(match.string)
|
||||
if timeMatch:
|
||||
date = self.getUnixTime(timeMatch.group())
|
||||
try:
|
||||
ipMatch = DNSUtils.textToIp(match.group("host"))
|
||||
if ipMatch:
|
||||
for ip in ipMatch:
|
||||
failList.append([ip, date])
|
||||
except IndexError:
|
||||
logSys.error("There is no 'host' group in the rule. " +
|
||||
"Please correct your configuration.")
|
||||
return failList
|
||||
|
||||
##
|
||||
# Check is a line contains a valid date.
|
||||
#
|
||||
# @param line the line
|
||||
# @return True if the line contains a valid date
|
||||
|
||||
def hasTime(self, line):
|
||||
timeMatch = re.search(self.timeRegex, line)
|
||||
if timeMatch:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
##
|
||||
# Get the time of a log line.
|
||||
#
|
||||
# @param line the line
|
||||
# @return the timestamp of the log line
|
||||
|
||||
def getTime(self, line):
|
||||
date = 0
|
||||
timeMatch = re.search(self.timeRegex, line)
|
||||
if timeMatch:
|
||||
date = self.getUnixTime(timeMatch.group())
|
||||
return date
|
||||
|
||||
##
|
||||
# Get the Unix timestamp.
|
||||
#
|
||||
# Get the Unix timestamp of a given date. Pattern should describe the
|
||||
# date construction of value.
|
||||
# @param value the date
|
||||
# @return the Unix timestamp
|
||||
|
||||
def getUnixTime(self, value):
|
||||
try:
|
||||
# Check if the parsed value is in TAI64N format
|
||||
if not self.timePattern.lower() == "tai64n":
|
||||
date = list(time.strptime(value, self.timePattern))
|
||||
else:
|
||||
# extract part of format which represents seconds since epoch
|
||||
seconds_since_epoch = value[2:17]
|
||||
date = list(time.gmtime(int(seconds_since_epoch, 16)))
|
||||
except ValueError, e:
|
||||
logSys.error(e)
|
||||
logSys.error("Please check the format and your locale settings.")
|
||||
return None
|
||||
if date[0] < 2000:
|
||||
# There is probably no year field in the logs
|
||||
date[0] = time.gmtime()[0]
|
||||
# Bug fix for #1241756
|
||||
# If the date is greater than the current time, we suppose
|
||||
# that the log is not from this year but from the year before
|
||||
if time.mktime(date) > time.time():
|
||||
date[0] -= 1
|
||||
unixTime = time.mktime(date)
|
||||
return unixTime
|
||||
|
||||
##
|
||||
# Get the status of the filter.
|
||||
#
|
||||
# Get some informations about the filter state such as the total
|
||||
# number of failures.
|
||||
# @return a list with tuple
|
||||
|
||||
def status(self):
|
||||
ret = [("Currently failed", self.failManager.size()),
|
||||
("Total failed", self.failManager.getFailTotal())]
|
||||
return ret
|
||||
|
||||
|
||||
##
|
||||
# Utils class for DNS and IP handling.
|
||||
#
|
||||
# This class contains only static methods used to handle DNS and IP
|
||||
# addresses.
|
||||
|
||||
import socket, struct
|
||||
|
||||
class DNSUtils:
|
||||
|
||||
dnsCRE = re.compile("(?:(?:\w|-)+\.){2,}\w+")
|
||||
ipCRE = re.compile("(?:\d{1,3}\.){3}\d{1,3}")
|
||||
|
||||
@staticmethod
|
||||
def dnsToIp(dns):
|
||||
""" Convert a DNS into an IP address using the Python socket module.
|
||||
Thanks to Kevin Drapel.
|
||||
"""
|
||||
try:
|
||||
return socket.gethostbyname_ex(dns)[2]
|
||||
except socket.gaierror:
|
||||
return list()
|
||||
|
||||
@staticmethod
|
||||
def textToDns(text):
|
||||
""" Search for possible DNS in an arbitrary text.
|
||||
Thanks to Tom Pike.
|
||||
"""
|
||||
match = DNSUtils.dnsCRE.match(text)
|
||||
if match:
|
||||
return match
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def searchIP(text):
|
||||
""" Search if an IP address if directly available and return
|
||||
it.
|
||||
"""
|
||||
match = DNSUtils.ipCRE.match(text)
|
||||
if match:
|
||||
return match
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def isValidIP(str):
|
||||
""" Return true if str is a valid IP
|
||||
"""
|
||||
s = str.split('/', 1)
|
||||
try:
|
||||
socket.inet_aton(s[0])
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def textToIp(text):
|
||||
""" Return the IP of DNS found in a given text.
|
||||
"""
|
||||
ipList = list()
|
||||
# Search for plain IP
|
||||
plainIP = DNSUtils.searchIP(text)
|
||||
if not plainIP == None:
|
||||
plainIPStr = plainIP.group(0)
|
||||
if DNSUtils.isValidIP(plainIPStr):
|
||||
ipList.append(plainIPStr)
|
||||
if not ipList:
|
||||
# Try to get IP from possible DNS
|
||||
dns = DNSUtils.textToDns(text)
|
||||
if not dns == None:
|
||||
ip = DNSUtils.dnsToIp(dns.group(0))
|
||||
for e in ip:
|
||||
ipList.append(e)
|
||||
return ipList
|
||||
|
||||
@staticmethod
|
||||
def cidr(i, n):
|
||||
""" Convert an IP address string with a CIDR mask into a 32-bit
|
||||
integer.
|
||||
"""
|
||||
# 32-bit IPv4 address mask
|
||||
MASK = 0xFFFFFFFFL
|
||||
return ~(MASK >> n) & MASK & DNSUtils.addr2bin(i)
|
||||
|
||||
@staticmethod
|
||||
def addr2bin(str):
|
||||
""" Convert a string IPv4 address into an unsigned integer.
|
||||
"""
|
||||
return struct.unpack("!L", socket.inet_aton(str))[0]
|
||||
|
||||
@staticmethod
|
||||
def bin2addr(addr):
|
||||
""" Convert a numeric IPv4 address into string n.n.n.n form.
|
||||
"""
|
||||
return socket.inet_ntoa(struct.pack("!L", addr))
|
||||
@@ -0,0 +1,95 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from actions import Actions
|
||||
from filter import Filter
|
||||
import Queue
|
||||
|
||||
|
||||
class Jail:
|
||||
|
||||
def __init__(self, name, filter = None, action = None):
|
||||
self.name = name
|
||||
self.queue = Queue.Queue()
|
||||
self.filter = Filter(self)
|
||||
self.action = Actions(self)
|
||||
|
||||
def setName(self, name):
|
||||
self.name = name
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def setFilter(self, filter):
|
||||
self.filter = filter
|
||||
|
||||
def getFilter(self):
|
||||
return self.filter
|
||||
|
||||
def setAction(self, action):
|
||||
self.action = action
|
||||
|
||||
def getAction(self):
|
||||
return self.action
|
||||
|
||||
def putFailTicket(self, ticket):
|
||||
self.queue.put(ticket)
|
||||
|
||||
def getFailTicket(self):
|
||||
try:
|
||||
return self.queue.get(False)
|
||||
except Queue.Empty:
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
self.filter.start()
|
||||
self.action.start()
|
||||
|
||||
def stop(self):
|
||||
self.filter.stop()
|
||||
self.action.stop()
|
||||
self.filter.join()
|
||||
self.action.join()
|
||||
|
||||
def isActive(self):
|
||||
isActive0 = self.filter.isActive()
|
||||
isActive1 = self.action.isActive()
|
||||
return isActive0 or isActive1
|
||||
|
||||
def setIdle(self, value):
|
||||
self.filter.setIdle(value)
|
||||
self.action.setIdle(value)
|
||||
|
||||
def getIdle(self):
|
||||
return self.filter.getIdle() or self.action.getIdle()
|
||||
|
||||
def getStatus(self):
|
||||
fStatus = self.filter.status()
|
||||
aStatus = self.action.status()
|
||||
ret = [("filter", fStatus),
|
||||
("action", aStatus)]
|
||||
return ret
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from threading import Thread
|
||||
|
||||
class JailThread(Thread):
|
||||
|
||||
##
|
||||
# Constructor.
|
||||
#
|
||||
# Initialize the filter object with default values.
|
||||
# @param jail the jail object
|
||||
|
||||
def __init__(self, jail):
|
||||
Thread.__init__(self)
|
||||
## Control the state of the thread.
|
||||
self.isRunning = False
|
||||
## Control the idle state of the thread.
|
||||
self.isIdle = False
|
||||
## The time the thread sleeps in the loop.
|
||||
self.sleepTime = 1
|
||||
|
||||
##
|
||||
# Set the time that the thread sleeps.
|
||||
#
|
||||
# This value could also be called "polling time". A value of 1 is a
|
||||
# good one. This unit is "second"
|
||||
# @param value the polling time (second)
|
||||
|
||||
def setSleepTime(self, value):
|
||||
self.sleepTime = value
|
||||
logSys.info("Set sleeptime = " + value)
|
||||
|
||||
##
|
||||
# Get the time that the thread sleeps.
|
||||
#
|
||||
# @return the polling time
|
||||
|
||||
def getSleepTime(self):
|
||||
return self.sleeptime
|
||||
|
||||
##
|
||||
# Set the idle flag.
|
||||
#
|
||||
# This flag stops the check of the log file.
|
||||
# @param value boolean value
|
||||
|
||||
def setIdle(self, value):
|
||||
self.isIdle = value
|
||||
|
||||
##
|
||||
# Get the idle state.
|
||||
#
|
||||
# @return the idle state
|
||||
|
||||
def getIdle(self):
|
||||
return self.isIdle
|
||||
|
||||
##
|
||||
# Stop the thread.
|
||||
#
|
||||
# Stop the exection of the thread and quit.
|
||||
|
||||
def stop(self):
|
||||
self.isRunning = False
|
||||
|
||||
##
|
||||
# Set the isRunning flag.
|
||||
#
|
||||
# @param value True if the thread is running
|
||||
|
||||
def setActive(self, value):
|
||||
self.isRunning = value
|
||||
|
||||
##
|
||||
# Check if the thread is active.
|
||||
#
|
||||
# Check if the filter thread is running.
|
||||
# @return True if the thread is running
|
||||
|
||||
def isActive(self):
|
||||
return self.isRunning
|
||||
|
||||
##
|
||||
# Get the status of the thread
|
||||
#
|
||||
# Get some informations about the thread. This is an abstract method.
|
||||
# @return a list with tuple
|
||||
|
||||
def status(self):
|
||||
pass
|
||||
@@ -0,0 +1,359 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from jail import Jail
|
||||
from transmitter import Transmitter
|
||||
import locale, logging, sys
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.server")
|
||||
|
||||
class Server:
|
||||
|
||||
def __init__(self):
|
||||
self.jails = dict()
|
||||
self.transm = Transmitter(self)
|
||||
self.logLevel = 3
|
||||
self.logTarget = "STDERR"
|
||||
# Set logging level
|
||||
self.setLogLevel(self.logLevel)
|
||||
self.setLogTarget(self.logTarget)
|
||||
|
||||
def start(self, force):
|
||||
# Start the communication
|
||||
self.transm.start(force)
|
||||
|
||||
def quit(self):
|
||||
self.stopAllJail()
|
||||
self.transm.stop()
|
||||
|
||||
def addJail(self, name):
|
||||
self.jails[name] = Jail(name)
|
||||
|
||||
def delJail(self, name):
|
||||
if self.jails.has_key(name):
|
||||
del self.jails[name]
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def startJail(self, name):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].start()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def stopJail(self, name):
|
||||
if self.jails.has_key(name):
|
||||
if self.isActive(name):
|
||||
self.jails[name].stop()
|
||||
self.delJail(name)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def stopAllJail(self):
|
||||
for jail in self.jails.copy():
|
||||
self.stopJail(jail)
|
||||
|
||||
def isActive(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].isActive()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setIdleJail(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].setIdle(value)
|
||||
return True
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getIdleJail(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getIdle()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
# Filter
|
||||
def setLogPath(self, name, file):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getFilter().setLogPath(file)
|
||||
|
||||
def getLogPath(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getFilter().getLogPath()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setTimeRegex(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getFilter().setTimeRegex(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getTimeRegex(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getFilter().getTimeRegex()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setTimePattern(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getFilter().setTimePattern(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getTimePattern(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getFilter().getTimePattern()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setFailRegex(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getFilter().setFailRegex(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getFailRegex(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getFilter().getFailRegex()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setMaxRetry(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getFilter().setMaxRetry(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getMaxRetry(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getFilter().getMaxRetry()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setMaxTime(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getFilter().setMaxTime(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getMaxTime(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getFilter().getMaxTime()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
# Action
|
||||
def addAction(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().addAction(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getLastAction(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getLastAction()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def delAction(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().delAction(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setCInfo(self, name, action, key, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().getAction(action).setCInfo(key, value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getCInfo(self, name, action, key):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getAction(action).getCInfo(key)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def delCInfo(self, name, action, key):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().getAction(action).delCInfo(key)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setBanTime(self, name, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().setBanTime(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getBanTime(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getBanTime()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setActionStart(self, name, action, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().getAction(action).setActionStart(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getActionStart(self, name, action):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getAction(action).getActionStart()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setActionStop(self, name, action, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().getAction(action).setActionStop(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getActionStop(self, name, action):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getAction(action).getActionStop()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setActionCheck(self, name, action, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().getAction(action).setActionCheck(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getActionCheck(self, name, action):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getAction(action).getActionCheck()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setActionBan(self, name, action, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().getAction(action).setActionBan(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getActionBan(self, name, action):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getAction(action).getActionBan()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def setActionUnban(self, name, action, value):
|
||||
if self.jails.has_key(name):
|
||||
self.jails[name].getAction().getAction(action).setActionUnban(value)
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
def getActionUnban(self, name, action):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getAction().getAction(action).getActionUnban()
|
||||
else:
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
# Status
|
||||
def status(self):
|
||||
jailList = ''
|
||||
for jail in self.jails:
|
||||
jailList += jail + ', '
|
||||
length = len(jailList)
|
||||
if not length == 0:
|
||||
jailList = jailList[:length-2]
|
||||
ret = [("Number of jail", len(self.jails)),
|
||||
("Jail list", jailList)]
|
||||
return ret
|
||||
|
||||
def statusJail(self, name):
|
||||
if self.jails.has_key(name):
|
||||
return self.jails[name].getStatus()
|
||||
raise ServerUnknownJail(name)
|
||||
|
||||
# Logging
|
||||
|
||||
##
|
||||
# Set the logging level.
|
||||
#
|
||||
# Incrementing the value gives more messages.
|
||||
# 0 = FATAL
|
||||
# 1 = ERROR
|
||||
# 2 = WARNING
|
||||
# 3 = INFO
|
||||
# 4 = DEBUG
|
||||
# @param value the level
|
||||
|
||||
def setLogLevel(self, value):
|
||||
self.logLevel = value
|
||||
logLevel = logging.DEBUG
|
||||
if value == 0:
|
||||
logLevel = logging.FATAL
|
||||
elif value == 1:
|
||||
logLevel = logging.ERROR
|
||||
elif value == 2:
|
||||
logLevel = logging.WARNING
|
||||
elif value == 3:
|
||||
logLevel = logging.INFO
|
||||
logging.getLogger("fail2ban").setLevel(logLevel)
|
||||
|
||||
##
|
||||
# Get the logging level.
|
||||
#
|
||||
# @see setLogLevel
|
||||
# @return the log level
|
||||
|
||||
def getLogLevel(self):
|
||||
return self.logLevel
|
||||
|
||||
def setLogTarget(self, target):
|
||||
# Remove previous handler
|
||||
logging.getLogger("fail2ban").handlers = []
|
||||
self.logTarget = target
|
||||
if target == "SYSLOG":
|
||||
logSys.error("Not yet implemented")
|
||||
return False
|
||||
elif target == "STDERR":
|
||||
hdlr = logging.StreamHandler(sys.stderr)
|
||||
else:
|
||||
# Target should be a file
|
||||
try:
|
||||
open(target, "a")
|
||||
hdlr = logging.FileHandler(target)
|
||||
except IOError:
|
||||
logSys.error("Unable to log to " + target)
|
||||
return False
|
||||
# set a format which is simpler for console use
|
||||
formatter = logging.Formatter('%(name)-16s: %(levelname)-6s %(message)s')
|
||||
# tell the handler to use this format
|
||||
hdlr.setFormatter(formatter)
|
||||
logging.getLogger("fail2ban").addHandler(hdlr)
|
||||
return True
|
||||
|
||||
def getLogTarget(self):
|
||||
return self.logTarget
|
||||
|
||||
class ServerUnknownJail(Exception):
|
||||
pass
|
||||
@@ -0,0 +1,112 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from threading import Thread
|
||||
import socket, time, logging, pickle, os, os.path
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.comm")
|
||||
|
||||
class SSocket(Thread):
|
||||
|
||||
def __init__(self, transmitter):
|
||||
Thread.__init__(self)
|
||||
self.socketFile = "/tmp/fail2ban.sock"
|
||||
self.transmit = transmitter
|
||||
self.isRunning = False
|
||||
logSys.debug("Created SSocket")
|
||||
|
||||
def initialize(self, force = False):
|
||||
# Remove socket
|
||||
if os.path.exists(self.socketFile):
|
||||
logSys.error("Fail2ban seems to be already running")
|
||||
if force:
|
||||
logSys.warn("Forcing execution of the server")
|
||||
os.remove(self.socketFile)
|
||||
else:
|
||||
raise SSocketErrorException("Server already running")
|
||||
# Create an INET, STREAMing socket
|
||||
#self.ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.ssock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
#self.ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.ssock.setblocking(False)
|
||||
# Bind the socket to a public host and a well-known port
|
||||
#self.ssock.bind(("localhost", 2222))
|
||||
self.ssock.bind(self.socketFile)
|
||||
# Become a server socket
|
||||
self.ssock.listen(1)
|
||||
|
||||
def run(self):
|
||||
self.isRunning = True
|
||||
stime = 1.0
|
||||
while self.isRunning:
|
||||
try:
|
||||
# Accept connections from outside
|
||||
(csock, address) = self.ssock.accept()
|
||||
stime /= 10
|
||||
logSys.debug("Connection accepted")
|
||||
msg = self.receive(csock)
|
||||
msg = self.transmit.proceed(msg)
|
||||
self.send(csock, msg)
|
||||
csock.close()
|
||||
except Exception:
|
||||
time.sleep(stime)
|
||||
stime += 0.05
|
||||
if stime > 1.0:
|
||||
stime = 1.0
|
||||
self.ssock.close()
|
||||
# Remove socket
|
||||
if os.path.exists(self.socketFile):
|
||||
logSys.debug("Removed socket file " + self.socketFile)
|
||||
os.remove(self.socketFile)
|
||||
logSys.debug("Socket shutdown")
|
||||
return True
|
||||
|
||||
##
|
||||
# Stop the thread.
|
||||
#
|
||||
# Set the isRunning flag to False.
|
||||
# @bug It seems to be some concurrency problem with this flag
|
||||
|
||||
def stop(self):
|
||||
self.isRunning = False
|
||||
|
||||
def send(self, socket, msg):
|
||||
obj = pickle.dumps(msg)
|
||||
socket.send(obj + "<F2B_END_COMMAND>")
|
||||
|
||||
def receive(self, socket):
|
||||
msg = ''
|
||||
while msg.rfind("<F2B_END_COMMAND>") == -1:
|
||||
chunk = socket.recv(6)
|
||||
if chunk == '':
|
||||
raise RuntimeError, "socket connection broken"
|
||||
msg = msg + chunk
|
||||
return pickle.loads(msg)
|
||||
|
||||
|
||||
class SSocketErrorException(Exception):
|
||||
pass
|
||||
@@ -0,0 +1,56 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import time, logging
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban")
|
||||
|
||||
class Ticket:
|
||||
|
||||
def __init__(self, ip, time):
|
||||
self.ip = ip
|
||||
self.time = time
|
||||
self.attempt = 0
|
||||
|
||||
def setIP(self, value):
|
||||
self.ip = value
|
||||
|
||||
def getIP(self):
|
||||
return self.ip
|
||||
|
||||
def setTime(self, value):
|
||||
self.time = value
|
||||
|
||||
def getTime(self):
|
||||
return self.time
|
||||
|
||||
def setAttempt(self, value):
|
||||
self.attempt = value
|
||||
|
||||
def getAttempt(self):
|
||||
return self.attempt
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from ssocket import SSocket
|
||||
from ssocket import SSocketErrorException
|
||||
import re, pickle, logging
|
||||
|
||||
# Gets the instance of the logger.
|
||||
logSys = logging.getLogger("fail2ban.comm")
|
||||
|
||||
class Transmitter:
|
||||
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
self.socket = SSocket(self)
|
||||
|
||||
def start(self, force):
|
||||
try:
|
||||
self.socket.initialize(force)
|
||||
self.socket.start()
|
||||
except SSocketErrorException:
|
||||
logSys.error("Could not start server")
|
||||
|
||||
##
|
||||
# Stop the transmitter.
|
||||
#
|
||||
# @bug Fix the issue with join(). When using servertestcase.py, errors
|
||||
# happen which disappear when using join() in this function.
|
||||
|
||||
def stop(self):
|
||||
self.socket.stop()
|
||||
#self.socket.join()
|
||||
|
||||
def proceed(self, action):
|
||||
# Deserialize object
|
||||
logSys.debug("Action: " + `action`)
|
||||
try:
|
||||
ret = self.actionHandler(action)
|
||||
ack = 0, ret
|
||||
except Exception, e:
|
||||
logSys.warn("Invalid command")
|
||||
ack = 1, e
|
||||
return ack
|
||||
|
||||
##
|
||||
# Handle an action.
|
||||
#
|
||||
#
|
||||
|
||||
def actionHandler(self, action):
|
||||
if action[0] == "ping":
|
||||
return "pong"
|
||||
elif action[0] == "add":
|
||||
name = action[1]
|
||||
if name == "all":
|
||||
raise Exception("Reserved name")
|
||||
self.server.addJail(name)
|
||||
return name
|
||||
elif action[0] == "start":
|
||||
name = action[1]
|
||||
self.server.startJail(name)
|
||||
return None
|
||||
elif action[0] == "stop":
|
||||
if len(action) == 1:
|
||||
self.server.quit()
|
||||
elif action[1] == "all":
|
||||
self.server.stopAllJail()
|
||||
else:
|
||||
name = action[1]
|
||||
self.server.stopJail(name)
|
||||
return None
|
||||
elif action[0] == "sleep":
|
||||
value = action[1]
|
||||
time.sleep(int(value))
|
||||
return None
|
||||
elif action[0] == "set":
|
||||
return self.actionSet(action[1:])
|
||||
elif action[0] == "get":
|
||||
return self.actionGet(action[1:])
|
||||
elif action[0] == "status":
|
||||
return self.status(action[1:])
|
||||
raise Exception("Invalid command")
|
||||
|
||||
def actionSet(self, action):
|
||||
name = action[0]
|
||||
# Logging
|
||||
if name == "loglevel":
|
||||
value = int(action[1])
|
||||
self.server.setLogLevel(value)
|
||||
return self.server.getLogLevel()
|
||||
elif name == "logtarget":
|
||||
value = action[1]
|
||||
self.server.setLogTarget(value)
|
||||
return self.server.getLogTarget()
|
||||
# Jail
|
||||
elif action[1] == "idle":
|
||||
if action[2] == "on":
|
||||
self.server.setIdleJail(name, True)
|
||||
elif action[2] == "off":
|
||||
self.server.setIdleJail(name, False)
|
||||
return self.server.getIdleJail(name)
|
||||
# Filter
|
||||
elif action[1] == "logpath":
|
||||
value = action[2]
|
||||
self.server.setLogPath(name, value)
|
||||
return self.server.getLogPath(name)
|
||||
elif action[1] == "timeregex":
|
||||
value = action[2]
|
||||
self.server.setTimeRegex(name, value)
|
||||
return self.server.getTimeRegex(name)
|
||||
elif action[1] == "timepattern":
|
||||
value = action[2]
|
||||
self.server.setTimePattern(name, value)
|
||||
return self.server.getTimePattern(name)
|
||||
elif action[1] == "failregex":
|
||||
value = action[2]
|
||||
self.server.setFailRegex(name, value)
|
||||
return self.server.getFailRegex(name)
|
||||
elif action[1] == "maxtime":
|
||||
value = action[2]
|
||||
self.server.setMaxTime(name, int(value))
|
||||
return self.server.getMaxTime(name)
|
||||
elif action[1] == "maxretry":
|
||||
value = action[2]
|
||||
self.server.setMaxRetry(name, int(value))
|
||||
return self.server.getMaxRetry(name)
|
||||
# Action
|
||||
elif action[1] == "bantime":
|
||||
value = action[2]
|
||||
self.server.setBanTime(name, int(value))
|
||||
return self.server.getBanTime(name)
|
||||
elif action[1] == "addaction":
|
||||
value = action[2]
|
||||
self.server.addAction(name, value)
|
||||
return self.server.getLastAction(name).getName()
|
||||
elif action[1] == "delaction":
|
||||
self.server.delAction(name, value)
|
||||
return None
|
||||
elif action[1] == "setcinfo":
|
||||
act = action[2]
|
||||
key = action[3]
|
||||
value = action[4]
|
||||
self.server.setCInfo(name, act, key, value)
|
||||
return self.server.getCInfo(name, act, key)
|
||||
elif action[1] == "delcinfo":
|
||||
act = action[2]
|
||||
key = action[3]
|
||||
self.server.delCInfo(name, act, key)
|
||||
return None
|
||||
elif action[1] == "actionstart":
|
||||
act = action[2]
|
||||
value = action[3]
|
||||
self.server.setActionStart(name, act, value)
|
||||
return self.server.getActionStart(name, act)
|
||||
elif action[1] == "actionstop":
|
||||
act = action[2]
|
||||
value = action[3]
|
||||
self.server.setActionStop(name, act, value)
|
||||
return self.server.getActionStop(name, act)
|
||||
elif action[1] == "actioncheck":
|
||||
act = action[2]
|
||||
value = action[3]
|
||||
self.server.setActionCheck(name, act, value)
|
||||
return self.server.getActionCheck(name, act)
|
||||
elif action[1] == "actionban":
|
||||
act = action[2]
|
||||
value = action[3]
|
||||
self.server.setActionBan(name, act, value)
|
||||
return self.server.getActionBan(name, act)
|
||||
elif action[1] == "actionunban":
|
||||
act = action[2]
|
||||
value = action[3]
|
||||
self.server.setActionUnban(name, act, value)
|
||||
return self.server.getActionUnban(name, act)
|
||||
raise Exception("Invalid command (no set action)")
|
||||
|
||||
def actionGet(self, action):
|
||||
name = action[0]
|
||||
# Logging
|
||||
if name == "loglevel":
|
||||
return self.server.getLogLevel()
|
||||
# Filter
|
||||
if action[1] == "logpath":
|
||||
return self.server.getLogPath(name)
|
||||
raise Exception("Invalid command (no get action)")
|
||||
|
||||
def status(self, action):
|
||||
if len(action) == 0:
|
||||
return self.server.status()
|
||||
else:
|
||||
name = action[0]
|
||||
return self.server.statusJail(name)
|
||||
raise Exception("Invalid command (no status)")
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[install]
|
||||
install-purelib=/usr/lib/fail2ban
|
||||
|
||||
[sdist]
|
||||
formats=bztar
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
from distutils.core import setup
|
||||
from version import version
|
||||
from os.path import isfile, join
|
||||
from sys import exit, argv
|
||||
|
||||
longdesc = '''
|
||||
Fail2Ban scans log files like /var/log/pwdfail or
|
||||
/var/log/apache/error_log and bans IP that makes
|
||||
too many password failures. It updates firewall rules
|
||||
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 = "lostcontrol@users.sourceforge.net",
|
||||
url = "http://fail2ban.sourceforge.net",
|
||||
license = "GPL",
|
||||
platforms = "Posix",
|
||||
scripts = [
|
||||
'fail2ban-client',
|
||||
'fail2ban-server'
|
||||
],
|
||||
py_modules = ['version'],
|
||||
packages = [
|
||||
'client',
|
||||
'server'
|
||||
],
|
||||
data_files = [
|
||||
('/etc/fail2ban',
|
||||
[
|
||||
'config/fail2ban.conf',
|
||||
'config/jail.conf'
|
||||
]
|
||||
),
|
||||
('/etc/fail2ban/filter.d',
|
||||
[
|
||||
'config/filter.d/vsftpd.conf',
|
||||
'config/filter.d/apache-auth.conf',
|
||||
'config/filter.d/sshd.conf'
|
||||
]
|
||||
),
|
||||
('/etc/fail2ban/action.d',
|
||||
[
|
||||
'config/action.d/iptables.conf',
|
||||
'config/action.d/mail-whois.conf',
|
||||
'config/action.d/dummy.conf',
|
||||
'config/action.d/mail.conf',
|
||||
'config/action.d/hostsdeny.conf'
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
# Do some checks after installation
|
||||
# Search for obsolete files.
|
||||
obsoleteFiles = []
|
||||
elements = {
|
||||
"/etc/":
|
||||
[
|
||||
"fail2ban.conf"
|
||||
],
|
||||
"/usr/bin/":
|
||||
[
|
||||
"fail2ban.py"
|
||||
],
|
||||
"/usr/lib/fail2ban/firewall/":
|
||||
[
|
||||
"iptables.py",
|
||||
"ipfwadm.py",
|
||||
"ipfw.py"
|
||||
]
|
||||
}
|
||||
|
||||
for dir in elements:
|
||||
for f in elements[dir]:
|
||||
path = join(dir, f)
|
||||
if isfile(path):
|
||||
obsoleteFiles.append(path)
|
||||
if obsoleteFiles:
|
||||
print
|
||||
print "Obsolete files from previous Fail2Ban versions were found on " \
|
||||
"your system."
|
||||
print "Please delete them:"
|
||||
print
|
||||
for f in obsoleteFiles:
|
||||
print "\t" + f
|
||||
print
|
||||
|
||||
# Update config file
|
||||
if argv[1] == "install":
|
||||
print
|
||||
print "Please do not forget to update your configuration files."
|
||||
print "They are in /etc/fail2ban/."
|
||||
print
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
@@ -0,0 +1,56 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest, socket, time, pickle
|
||||
from server.banmanager import BanManager
|
||||
from server.banticket import BanTicket
|
||||
|
||||
class AddFailure(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.ticket = BanTicket('193.168.0.128', 1167605999.0)
|
||||
self.banManager = BanManager()
|
||||
self.assertTrue(self.banManager.addBanTicket(self.ticket))
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
def testAdd(self):
|
||||
self.assertEqual(self.banManager.size(), 1)
|
||||
|
||||
def testAddDuplicate(self):
|
||||
self.assertFalse(self.banManager.addBanTicket(self.ticket))
|
||||
self.assertEqual(self.banManager.size(), 1)
|
||||
|
||||
def testInListOK(self):
|
||||
ticket = BanTicket('193.168.0.128', 1167605999.0)
|
||||
self.assertTrue(self.banManager.inBanList(ticket))
|
||||
|
||||
def testInListNOK(self):
|
||||
ticket = BanTicket('111.111.1.111', 1167605999.0)
|
||||
self.assertFalse(self.banManager.inBanList(ticket))
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision: 253 $
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision: 253 $"
|
||||
__date__ = "$Date: 2006-07-17 00:21:58 +0200 (Mon, 17 Jul 2006) $"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest
|
||||
from client.jailreader import JailReader
|
||||
|
||||
class JailReaderTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
def testSplitAction(self):
|
||||
action = "mail-whois[name=SSH]"
|
||||
expected = ['mail-whois', {'name': 'SSH'}]
|
||||
result = JailReader.splitAction(action)
|
||||
self.assertEquals(expected, result)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest, socket, time, pickle
|
||||
from server.failmanager import FailManager
|
||||
from server.failmanager import FailManagerEmpty
|
||||
from server.failticket import FailTicket
|
||||
|
||||
class AddFailure(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.items = [['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['193.168.0.128', 1167605999.0],
|
||||
['87.142.124.10', 1167605999.0],
|
||||
['87.142.124.10', 1167605999.0],
|
||||
['87.142.124.10', 1167605999.0]]
|
||||
|
||||
self.failManager = FailManager()
|
||||
for i in self.items:
|
||||
self.failManager.addFailure(FailTicket(i[0], i[1]))
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
def testAdd(self):
|
||||
self.assertEqual(self.failManager.size(), 2)
|
||||
|
||||
def testDel(self):
|
||||
self.failManager.delFailure('193.168.0.128')
|
||||
self.failManager.delFailure('111.111.1.111')
|
||||
|
||||
self.assertEqual(self.failManager.size(), 1)
|
||||
|
||||
def testCleanupOK(self):
|
||||
timestamp = 1167606999.0
|
||||
self.failManager.cleanup(timestamp)
|
||||
self.assertEqual(self.failManager.size(), 0)
|
||||
|
||||
def testCleanupNOK(self):
|
||||
timestamp = 1167605990.0
|
||||
self.failManager.cleanup(timestamp)
|
||||
self.assertEqual(self.failManager.size(), 2)
|
||||
|
||||
def testbanOK(self):
|
||||
self.failManager.setMaxRetry(5)
|
||||
#ticket = FailTicket('193.168.0.128', None)
|
||||
ticket = self.failManager.toBan()
|
||||
self.assertEqual(ticket.getIP(), "193.168.0.128")
|
||||
|
||||
def testbanNOK(self):
|
||||
self.failManager.setMaxRetry(10)
|
||||
self.assertRaises(FailManagerEmpty, self.failManager.toBan)
|
||||
@@ -0,0 +1,18 @@
|
||||
Déc 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Déc 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Déc 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from failed.dns.ch
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from failed.dns.ch
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from failed.dns.ch
|
||||
Dez 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Dez 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Dez 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
De 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
De 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
De 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10
|
||||
Dec 31 23:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10
|
||||
@@ -0,0 +1,10 @@
|
||||
Mar 16 04:57:00 i60p295 sshd[11437]: input_userauth_request: illegal user test123
|
||||
Mar 16 04:57:00 i60p295 sshd[11437]: Failed password for illegal user test123 from ::ffff:66.38.192.238 port 51381 ssh2
|
||||
Mar 16 04:57:00 i60p295 sshd[11437]: Connection closed by ::ffff:66.38.192.238
|
||||
Mar 16 10:33:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||
Mar 16 10:33:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||
Mar 16 10:33:59 i60p295 sshd[12365]: Postponed keyboard-interactive for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||
Mar 16 10:34:01 i60p295 sshd[12365]: Postponed keyboard-interactive/pam for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||
Mar 16 10:34:01 i60p295 sshd[12365]: Accepted keyboard-interactive/pam for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||
Mär 16 10:33:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||
Mär 16 10:33:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||
@@ -0,0 +1,98 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest, socket
|
||||
from server.filter import Filter
|
||||
from server.failmanager import FailManager
|
||||
|
||||
class IgnoreIP(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.filter = Filter(None)
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
def testIgnoreIPOK(self):
|
||||
ipList = "127.0.0.1", "192.168.0.1", "255.255.255.255", "99.99.99.99"
|
||||
for ip in ipList:
|
||||
self.filter.addIgnoreIP(ip)
|
||||
self.assertTrue(self.filter.inIgnoreIPList(ip))
|
||||
|
||||
def testIgnoreIPNOK(self):
|
||||
ipList = "", "999.999.999.999", "abcdef", "192.168.0"
|
||||
for ip in ipList:
|
||||
self.filter.addIgnoreIP(ip)
|
||||
self.assertFalse(self.filter.inIgnoreIPList(ip))
|
||||
|
||||
|
||||
class LogFile(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.filter = Filter(None)
|
||||
self.filter.setLogPath("testcases/files/testcase01.log")
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
def testOpen(self):
|
||||
self.filter.openLogFile()
|
||||
|
||||
def testIsModified(self):
|
||||
self.filter.openLogFile()
|
||||
self.assertTrue(self.filter.isModified())
|
||||
|
||||
|
||||
class GetFailures(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.filter = Filter(None)
|
||||
self.filter.setLogPath("testcases/files/testcase01.log")
|
||||
self.filter.setTimeRegex("\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
|
||||
self.filter.setTimePattern("%b %d %H:%M:%S")
|
||||
self.filter.setFailRegex("(?:(?:Authentication failure|Failed [-/\w+]+) for(?: [iI](?:llegal|nvalid) user)?|[Ii](?:llegal|nvalid) user|ROOT LOGIN REFUSED) .*(?: from|FROM) (?:::f{4,6}:)?(?P<host>\S*)")
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
def testGetFailures(self):
|
||||
output = [('87.142.124.10', 3, 1167605999.0),
|
||||
('193.168.0.128', 3, 1167605999.0)]
|
||||
|
||||
self.filter.openLogFile()
|
||||
self.filter.getFailures()
|
||||
|
||||
found = []
|
||||
for ip in self.filter.failManager.failList:
|
||||
fData = self.filter.failManager.failList[ip]
|
||||
retry = fData.getRetry()
|
||||
lTime = fData.getLastTime()
|
||||
found.append((ip, retry, lTime))
|
||||
self.assertEqual(found, output)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest, socket, time
|
||||
from server.server import Server
|
||||
|
||||
class StartStop(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.server = Server()
|
||||
self.server.setLogLevel(0)
|
||||
self.server.start(False)
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
self.server.quit()
|
||||
|
||||
def testStartStopJail(self):
|
||||
name = "TestCase"
|
||||
self.server.addJail(name)
|
||||
self.server.startJail(name)
|
||||
time.sleep(1)
|
||||
self.server.stopJail(name)
|
||||
|
||||
|
||||
class Transmitter(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.server = Server()
|
||||
self.server.setLogLevel(0)
|
||||
self.server.start(False)
|
||||
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# This file is part of Fail2Ban.
|
||||
#
|
||||
# Fail2Ban is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Fail2Ban is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY 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
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
# $Revision$
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
version = "0.7.0"
|
||||
Reference in New Issue
Block a user