mirror of https://github.com/fail2ban/fail2ban
commit
a82e3dc1e7
73
CHANGELOG
73
CHANGELOG
|
@ -4,9 +4,78 @@
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
|
|
||||||
=============================================================
|
=============================================================
|
||||||
Fail2Ban (version 0.6.1) 2006/03/16
|
Fail2Ban (version 0.7.4) 2006/11/01
|
||||||
=============================================================
|
=============================================================
|
||||||
|
|
||||||
|
ver. 0.7.4 (2006/11/01) - beta
|
||||||
|
----------
|
||||||
|
- Improved configuration files. Thanks to Yaroslav Halchenko
|
||||||
|
- Added man page for "fail2ban-regex"
|
||||||
|
- Moved ban/unban messages from "info" level to "warn"
|
||||||
|
- Added "-s" option to specify the socket path and "socket"
|
||||||
|
option in "fail2ban.conf"
|
||||||
|
- Added "backend" option in "jail.conf"
|
||||||
|
- Added more filters/actions and jail samples. Thanks to Nick
|
||||||
|
Munger, Christoph Haas
|
||||||
|
- Improved testing framework
|
||||||
|
- Fixed a bug in the return code handling of the executed
|
||||||
|
commands. Thanks to Yaroslav Halchenko
|
||||||
|
- Signal handling. There is a bug with join() and signal in
|
||||||
|
Python
|
||||||
|
- Better debugging output for "fail2ban-regex"
|
||||||
|
- Added support for more date format
|
||||||
|
- cPickle does not work with Python 2.5. Use pickle instead
|
||||||
|
(performance is not a problem in our case)
|
||||||
|
|
||||||
|
ver. 0.7.3 (2006/09/28) - beta
|
||||||
|
----------
|
||||||
|
- Added man pages. Thanks to Yaroslav Halchenko
|
||||||
|
- Added wildcard support for "logpath"
|
||||||
|
- Added Gamin (file and directory monitoring system) support
|
||||||
|
- (Re)added "ignoreip" option
|
||||||
|
- Added more concurrency protection
|
||||||
|
- First attempt at solving bug #1457620 (locale issue)
|
||||||
|
- Performance improvements
|
||||||
|
- (Re)added permanent banning with banTime < 0
|
||||||
|
- Added DNS support to "ignoreip". Feature Request #1285859
|
||||||
|
|
||||||
|
ver. 0.7.2 (2006/09/10) - beta
|
||||||
|
----------
|
||||||
|
- Refactoring and code cleanup
|
||||||
|
- Improved client output
|
||||||
|
- Added more get/set commands
|
||||||
|
- Added more configuration templates
|
||||||
|
- Removed "logpath" and "maxretry" from filter templates.
|
||||||
|
They must be defined in jail.conf now
|
||||||
|
- Added interactive mode. Use "-i"
|
||||||
|
- Added a date detector. "timeregex" and "timepattern" are no
|
||||||
|
more needed
|
||||||
|
- Added "fail2ban-regex". This is a tool to help finding
|
||||||
|
"failregex"
|
||||||
|
- Improved server communication. Start a new thread for each
|
||||||
|
incoming request. Fail2ban is not really thread-safe yet
|
||||||
|
|
||||||
|
ver. 0.7.1 (2006/08/23) - alpha
|
||||||
|
----------
|
||||||
|
- Fixed daemon mode bug
|
||||||
|
- Added Gentoo init.d script
|
||||||
|
- Fixed path bug when trying to start "fail2ban-server"
|
||||||
|
- Fixed reload command
|
||||||
|
|
||||||
|
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
|
ver. 0.6.1 (2006/03/16) - stable
|
||||||
----------
|
----------
|
||||||
- Added permanent banning. Set banTime to a negative value to
|
- Added permanent banning. Set banTime to a negative value to
|
||||||
|
@ -22,7 +91,7 @@ ver. 0.6.1 (2006/03/16) - stable
|
||||||
- Added parsing of timestamp in TAI64N format (#1275325).
|
- Added parsing of timestamp in TAI64N format (#1275325).
|
||||||
Thanks to Mark Edgington
|
Thanks to Mark Edgington
|
||||||
- Added patch #1382936 (Default formatted syslog logging).
|
- Added patch #1382936 (Default formatted syslog logging).
|
||||||
Thanks to Patrick Börjesson
|
Thanks to Patrick B<EFBFBD>rjesson
|
||||||
- Removed 192.168.0.0/16 from ignoreip. Attacks could also
|
- Removed 192.168.0.0/16 from ignoreip. Attacks could also
|
||||||
come from the local network.
|
come from the local network.
|
||||||
- Robust startup: if iptables module does not get fully
|
- Robust startup: if iptables module does not get fully
|
||||||
|
|
2
PKG-INFO
2
PKG-INFO
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 1.0
|
Metadata-Version: 1.0
|
||||||
Name: fail2ban
|
Name: fail2ban
|
||||||
Version: 0.6.1
|
Version: 0.7.4
|
||||||
Summary: Ban IPs that make too many password failure
|
Summary: Ban IPs that make too many password failure
|
||||||
Home-page: http://fail2ban.sourceforge.net
|
Home-page: http://fail2ban.sourceforge.net
|
||||||
Author: Cyril Jaquier
|
Author: Cyril Jaquier
|
||||||
|
|
131
README
131
README
|
@ -4,7 +4,7 @@
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
|
|
||||||
=============================================================
|
=============================================================
|
||||||
Fail2Ban (version 0.6.1) 2006/03/16
|
Fail2Ban (version 0.7.4) 2006/11/01
|
||||||
=============================================================
|
=============================================================
|
||||||
|
|
||||||
Fail2Ban scans log files like /var/log/pwdfail and bans IP
|
Fail2Ban scans log files like /var/log/pwdfail and bans IP
|
||||||
|
@ -13,57 +13,26 @@ rules to reject the IP address. These rules can be defined by
|
||||||
the user. Fail2Ban can read multiple log files such as sshd
|
the user. Fail2Ban can read multiple log files such as sshd
|
||||||
or Apache web server ones.
|
or Apache web server ones.
|
||||||
|
|
||||||
This is my first Python program. Moreover, English is not my
|
Documentation, FAQ, HOWTOs are available on the project
|
||||||
mother tongue...
|
website: http://fail2ban.sourceforge.net
|
||||||
|
|
||||||
|
|
||||||
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:
|
Installation:
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Require: python-2.4 (http://www.python.org)
|
Required:
|
||||||
|
>=python-2.4 (http://www.python.org)
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
>=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin)
|
||||||
|
|
||||||
To install, just do:
|
To install, just do:
|
||||||
|
|
||||||
> tar xvfj fail2ban-0.6.1.tar.bz2
|
> tar xvfj fail2ban-0.7.4.tar.bz2
|
||||||
> cd fail2ban-0.6.1
|
> cd fail2ban-0.7.4
|
||||||
> python setup.py install
|
> python setup.py install
|
||||||
|
|
||||||
This will install Fail2Ban into /usr/lib/fail2ban. The
|
This will install Fail2Ban into /usr/lib/fail2ban. The
|
||||||
fail2ban executable is placed into /usr/bin.
|
executable scripts are placed into /usr/bin.
|
||||||
|
|
||||||
Gentoo: ebuilds are available on the website.
|
Gentoo: ebuilds are available on the website.
|
||||||
Debian: Fail2Ban is in Debian unstable.
|
Debian: Fail2Ban is in Debian unstable.
|
||||||
|
@ -71,50 +40,46 @@ RedHat: packages are available on the website.
|
||||||
|
|
||||||
Fail2Ban should now be correctly installed. Just type:
|
Fail2Ban should now be correctly installed. Just type:
|
||||||
|
|
||||||
> fail2ban -h
|
> fail2ban-client -h
|
||||||
|
|
||||||
to see if everything is alright. You can configure fail2ban
|
to see if everything is alright.
|
||||||
with a config file. Different kind of configuration files are
|
|
||||||
available:
|
|
||||||
|
|
||||||
iptables: copy config/fail2ban.conf.iptables to
|
|
||||||
/etc/fail2ban.conf
|
|
||||||
hosts.deny: copy config/fail2ban.conf.hostsdeny to
|
|
||||||
/etc/fail2ban.conf
|
|
||||||
shorewall: copy config/fail2ban.conf.shorewall to
|
|
||||||
/etc/fail2ban.conf
|
|
||||||
|
|
||||||
Do not forget to edit fail2ban.conf to meet your needs.
|
|
||||||
|
|
||||||
You can use the initd script available in config/. Copy
|
|
||||||
<dist>-initd to /etc/init.d/fail2ban. Gentoo users must copy
|
|
||||||
gentoo-confd to /etc/conf.d/fail2ban. You can start fail2ban:
|
|
||||||
|
|
||||||
> /etc/init.d/fail2ban start
|
|
||||||
|
|
||||||
Gentoo users can add it to the default runlevel:
|
|
||||||
|
|
||||||
> rc-update add fail2ban default
|
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
You can configure fail2ban using the file /etc/fail2ban.conf
|
You can configure fail2ban using the files in /etc/fail2ban
|
||||||
or using command line options. Command line options override
|
or using command line. Here are the available command line
|
||||||
the value stored in fail2ban.conf. Here are the command line
|
options (not complete yet):
|
||||||
options:
|
|
||||||
|
|
||||||
-b start in background
|
Options:
|
||||||
-c <FILE> read configuration file FILE
|
-c <DIR> configuration directory
|
||||||
-p <FILE> create PID lock in FILE
|
-s <FILE> socket path
|
||||||
-h display this help message
|
-d dump configuration. For debugging
|
||||||
-i <IP(s)> IP(s) to ignore
|
-i interactive mode
|
||||||
-k kill a currently running instance
|
-v increase verbosity
|
||||||
-r <VALUE> allow a max of VALUE password failure [maxfailures]
|
-q decrease verbosity
|
||||||
-t <TIME> ban IP for TIME seconds [bantime]
|
-x force execution of the server
|
||||||
-f <TIME> lifetime in seconds of failed entry [findtime]
|
-h, --help display this help message
|
||||||
-v verbose. Use twice for greater effect
|
-V, --version print the version
|
||||||
-V print software version
|
|
||||||
|
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> [BACKEND] create <JAIL> using [BACKEND]
|
||||||
|
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>
|
||||||
|
|
||||||
Contact:
|
Contact:
|
||||||
--------
|
--------
|
||||||
|
@ -126,14 +91,14 @@ Website: http://fail2ban.sourceforge.net
|
||||||
|
|
||||||
Cyril Jaquier: <lostcontrol@users.sourceforge.net>
|
Cyril Jaquier: <lostcontrol@users.sourceforge.net>
|
||||||
|
|
||||||
|
|
||||||
Thanks:
|
Thanks:
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker,
|
Kévin Drapel, Marvin Rouge, Sireyessire, Robert Edeker,
|
||||||
Tom Pike, Iain Lea, Andrey G. Grozin, Yaroslav Halchenko,
|
Tom Pike, Iain Lea, Andrey G. Grozin, Yaroslav Halchenko,
|
||||||
Jonathan Kamens, Stephen Gildea, Markus Hoffmann, Mark
|
Jonathan Kamens, Stephen Gildea, Markus Hoffmann, Mark
|
||||||
Edgington, Patrick Börjesson, kojiro, zugeschmiert
|
Edgington, Patrick Börjesson, kojiro, zugeschmiert, Tyler,
|
||||||
|
Nick Munger, Christoph Haas
|
||||||
|
|
||||||
License:
|
License:
|
||||||
--------
|
--------
|
||||||
|
|
104
TODO
104
TODO
|
@ -4,92 +4,36 @@
|
||||||
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
|_| \__,_|_|_/___|_.__/\__,_|_||_|
|
||||||
|
|
||||||
=============================================================
|
=============================================================
|
||||||
ToDo $Revision: 1.11 $
|
ToDo $Revision: 446 $
|
||||||
=============================================================
|
=============================================================
|
||||||
|
|
||||||
See Feature Request Tracking System at SourceForge.net
|
Legend:
|
||||||
|
- not yet done
|
||||||
|
? maybe
|
||||||
|
# partially done
|
||||||
|
* done
|
||||||
|
|
||||||
|
- Add gettext support (I8N)
|
||||||
|
|
||||||
|
- Fix the cPickle issue with Python 2.5
|
||||||
|
|
||||||
|
- Multiline log reading
|
||||||
|
|
||||||
|
- Improve communication. (asyncore, asynchat??)
|
||||||
|
|
||||||
|
- Improve execution of action. Why does subprocess.call
|
||||||
|
deadlock with multi-jails?
|
||||||
|
|
||||||
|
# see Feature Request Tracking System at SourceForge.net
|
||||||
|
|
||||||
- improve installation process (better prefix support)
|
- improve installation process (better prefix support)
|
||||||
|
|
||||||
- improve documentation and website for user
|
# improve documentation and website for user
|
||||||
|
|
||||||
- use Doxygen
|
# better return values in function
|
||||||
|
|
||||||
- use PyLint to check the code
|
? restart automatically the daemon if an exception occurs.
|
||||||
|
|
||||||
- better configuration files
|
- do not close socket after a send
|
||||||
|
|
||||||
- add a check to see if the time of the log messages is
|
# refactoring in server.py, actions.py, filter.py
|
||||||
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.
|
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1 $
|
# $Revision: 433 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.1 $"
|
__version__ = "$Revision: 433 $"
|
||||||
__date__ = "$Date: 2005/03/06 17:51:24 $"
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__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, fileName):
|
||||||
|
self.__file = fileName
|
||||||
|
|
||||||
|
def getFile(self):
|
||||||
|
return self.__file
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
self.__name = name
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.__name
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return 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,129 @@
|
||||||
|
# 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: 288 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 288 $"
|
||||||
|
__date__ = "$Date: 2006-08-22 23:59:51 +0200 (Tue, 22 Aug 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from server.jails import UnknownJailException
|
||||||
|
from server.jails import DuplicateJailException
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.client.config")
|
||||||
|
|
||||||
|
##
|
||||||
|
# Beautify the output of the client.
|
||||||
|
#
|
||||||
|
# Fail2ban server only return unformatted return codes which need to be
|
||||||
|
# converted into user readable messages.
|
||||||
|
|
||||||
|
class Beautifier:
|
||||||
|
|
||||||
|
def __init__(self, cmd = None):
|
||||||
|
self.__inputCmd = cmd
|
||||||
|
|
||||||
|
def setInputCmd(self, cmd):
|
||||||
|
self.__inputCmd = cmd
|
||||||
|
|
||||||
|
def getInputCmd(self):
|
||||||
|
return self.__inputCmd
|
||||||
|
|
||||||
|
def beautify(self, response):
|
||||||
|
logSys.debug("Beautify " + `response` + " with " + `self.__inputCmd`)
|
||||||
|
inC = self.__inputCmd
|
||||||
|
msg = response
|
||||||
|
try:
|
||||||
|
if inC[0] == "ping":
|
||||||
|
msg = "Server replied: " + response
|
||||||
|
elif inC[0] == "start":
|
||||||
|
msg = "Jail started"
|
||||||
|
elif inC[0] == "stop":
|
||||||
|
if len(inC) == 1:
|
||||||
|
if response == None:
|
||||||
|
msg = "Shutdown successful"
|
||||||
|
else:
|
||||||
|
if response == None:
|
||||||
|
msg = "Jail stopped"
|
||||||
|
elif inC[0] == "add":
|
||||||
|
msg = "Added jail " + response
|
||||||
|
elif inC[0:1] == ['status']:
|
||||||
|
if len(inC) > 1:
|
||||||
|
msg = "Status for the jail: " + inC[1] + "\n"
|
||||||
|
msg = msg + "|- " + response[0][0] + "\n"
|
||||||
|
msg = msg + "| |- " + response[0][1][0][0] + ":\t\t" + `response[0][1][0][1]` + "\n"
|
||||||
|
msg = msg + "| `- " + response[0][1][1][0] + ":\t\t" + `response[0][1][1][1]` + "\n"
|
||||||
|
msg = msg + "`- " + response[1][0] + "\n"
|
||||||
|
msg = msg + " |- " + response[1][1][0][0] + ":\t\t" + `response[1][1][0][1]` + "\n"
|
||||||
|
msg = msg + " `- " + response[1][1][1][0] + ":\t\t" + `response[1][1][1][1]`
|
||||||
|
else:
|
||||||
|
msg = "Status\n"
|
||||||
|
msg = msg + "|- " + response[0][0] + ":\t" + `response[0][1]` + "\n"
|
||||||
|
msg = msg + "`- " + response[1][0] + ":\t\t" + response[1][1]
|
||||||
|
elif inC[1] == "logtarget":
|
||||||
|
msg = "Current logging target is:\n"
|
||||||
|
msg = msg + "`- " + response
|
||||||
|
elif inC[1:2] == ['loglevel']:
|
||||||
|
msg = "Current logging level is "
|
||||||
|
if response == 1:
|
||||||
|
msg = msg + "ERROR"
|
||||||
|
elif response == 2:
|
||||||
|
msg = msg + "WARN"
|
||||||
|
elif response == 3:
|
||||||
|
msg = msg + "INFO"
|
||||||
|
elif response == 4:
|
||||||
|
msg = msg + "DEBUG"
|
||||||
|
else:
|
||||||
|
msg = msg + `response`
|
||||||
|
elif inC[2] in ("logpath", "addlogpath", "dellogpath"):
|
||||||
|
if len(response) == 0:
|
||||||
|
msg = "No file is currently monitored"
|
||||||
|
else:
|
||||||
|
msg = "Current monitored log file(s):\n"
|
||||||
|
for path in response[:-1]:
|
||||||
|
msg = msg + "|- " + path + "\n"
|
||||||
|
msg = msg + "`- " + response[len(response)-1]
|
||||||
|
elif inC[2] in ("ignoreip", "addignoreip", "delignoreip"):
|
||||||
|
if len(response) == 0:
|
||||||
|
msg = "No IP address/network is ignored"
|
||||||
|
else:
|
||||||
|
msg = "These IP addresses/networks are ignored:\n"
|
||||||
|
for ip in response[:-1]:
|
||||||
|
msg = msg + "|- " + ip + "\n"
|
||||||
|
msg = msg + "`- " + response[len(response)-1]
|
||||||
|
except Exception:
|
||||||
|
logSys.warn("Beautifier error. Please report the error")
|
||||||
|
logSys.error("Beautify " + `response` + " with " + `self.__inputCmd` +
|
||||||
|
" failed")
|
||||||
|
msg = msg + `response`
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def beautifyError(self, response):
|
||||||
|
logSys.debug("Beautify (error) " + `response` + " with " + `self.__inputCmd`)
|
||||||
|
msg = response
|
||||||
|
if isinstance(response, UnknownJailException):
|
||||||
|
msg = "Sorry but the jail '" + response[0] + "' does not exist"
|
||||||
|
elif isinstance(response, IndexError):
|
||||||
|
msg = "Sorry but the command is invalid"
|
||||||
|
elif isinstance(response, DuplicateJailException):
|
||||||
|
msg = "The jail '" + response[0] + "' already exists"
|
||||||
|
return msg
|
|
@ -0,0 +1,97 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import logging, os
|
||||||
|
from ConfigParser import SafeConfigParser
|
||||||
|
from ConfigParser import NoOptionError, NoSectionError
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.client.config")
|
||||||
|
|
||||||
|
class ConfigReader(SafeConfigParser):
|
||||||
|
|
||||||
|
BASE_DIRECTORY = "/etc/fail2ban/"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
SafeConfigParser.__init__(self)
|
||||||
|
self.__opts = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setBaseDir(folderName):
|
||||||
|
path = folderName.rstrip('/')
|
||||||
|
ConfigReader.BASE_DIRECTORY = path + '/'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getBaseDir():
|
||||||
|
return ConfigReader.BASE_DIRECTORY
|
||||||
|
|
||||||
|
def read(self, filename):
|
||||||
|
basename = ConfigReader.BASE_DIRECTORY + filename
|
||||||
|
logSys.debug("Reading " + basename)
|
||||||
|
bConf = basename + ".conf"
|
||||||
|
bLocal = basename + ".local"
|
||||||
|
if os.path.exists(bConf) or os.path.exists(bLocal):
|
||||||
|
SafeConfigParser.read(self, [bConf, bLocal])
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logSys.error(bConf + " and " + bLocal + " do not exist")
|
||||||
|
return False
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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)
|
||||||
|
values[option[1]] = option[2]
|
||||||
|
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,76 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__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()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setBaseDir(folderName):
|
||||||
|
ConfigReader.setBaseDir(folderName)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getBaseDir():
|
||||||
|
return ConfigReader.getBaseDir()
|
||||||
|
|
||||||
|
def readEarly(self):
|
||||||
|
self.__fail2ban.read()
|
||||||
|
|
||||||
|
def readAll(self):
|
||||||
|
self.readEarly()
|
||||||
|
self.__jails.read()
|
||||||
|
|
||||||
|
def getEarlyOptions(self):
|
||||||
|
return self.__fail2ban.getEarlyOptions()
|
||||||
|
|
||||||
|
def getAllOptions(self):
|
||||||
|
self.__fail2ban.getOptions()
|
||||||
|
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,58 @@
|
||||||
|
# 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: 443 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 443 $"
|
||||||
|
__date__ = "$Date: 2006-11-01 00:36:59 +0100 (Wed, 01 Nov 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
|
import socket
|
||||||
|
|
||||||
|
class CSocket:
|
||||||
|
|
||||||
|
END_STRING = "<F2B_END_COMMAND>"
|
||||||
|
|
||||||
|
def __init__(self, sock = "/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(sock)
|
||||||
|
|
||||||
|
def send(self, msg):
|
||||||
|
# Convert every list member to string
|
||||||
|
obj = dumps(map(str, msg), HIGHEST_PROTOCOL)
|
||||||
|
self.__csock.send(obj + CSocket.END_STRING)
|
||||||
|
ret = self.receive(self.__csock)
|
||||||
|
self.__csock.close()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def receive(sock):
|
||||||
|
msg = ''
|
||||||
|
while msg.rfind(CSocket.END_STRING) == -1:
|
||||||
|
chunk = sock.recv(6)
|
||||||
|
if chunk == '':
|
||||||
|
raise RuntimeError, "socket connection broken"
|
||||||
|
msg = msg + chunk
|
||||||
|
return loads(msg)
|
|
@ -0,0 +1,58 @@
|
||||||
|
# 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: 407 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 407 $"
|
||||||
|
__date__ = "$Date: 2006-10-09 20:05:13 +0200 (Mon, 09 Oct 2006) $"
|
||||||
|
__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 getEarlyOptions(self):
|
||||||
|
opts = [["string", "socket", "/tmp/fail2ban.sock"]]
|
||||||
|
return ConfigReader.getOptions(self, "Definition", opts)
|
||||||
|
|
||||||
|
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,71 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__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, fileName, name):
|
||||||
|
ConfigReader.__init__(self)
|
||||||
|
self.__file = fileName
|
||||||
|
self.__name = name
|
||||||
|
|
||||||
|
def setFile(self, fileName):
|
||||||
|
self.__file = fileName
|
||||||
|
|
||||||
|
def getFile(self):
|
||||||
|
return self.__file
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
self.__name = name
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.__name
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return ConfigReader.read(self, "filter.d/" + self.__file)
|
||||||
|
|
||||||
|
def getOptions(self, pOpts):
|
||||||
|
opts = [["string", "timeregex", None],
|
||||||
|
["string", "timepattern", None],
|
||||||
|
["string", "failregex", ""]]
|
||||||
|
self.__opts = ConfigReader.getOptions(self, "Definition", opts, pOpts)
|
||||||
|
|
||||||
|
def convert(self):
|
||||||
|
stream = list()
|
||||||
|
for opt in self.__opts:
|
||||||
|
if 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]])
|
||||||
|
return stream
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
# 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: 438 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 438 $"
|
||||||
|
__date__ = "$Date: 2006-10-31 00:02:05 +0100 (Tue, 31 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import logging, re, glob
|
||||||
|
|
||||||
|
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"],
|
||||||
|
["string", "logpath", "/var/log/messages"],
|
||||||
|
["string", "backend", "auto"],
|
||||||
|
["int", "maxretry", 3],
|
||||||
|
["int", "maxtime", 600],
|
||||||
|
["int", "bantime", 600],
|
||||||
|
["string", "ignoreip", None],
|
||||||
|
["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)
|
||||||
|
ret = self.__filter.read()
|
||||||
|
if ret:
|
||||||
|
self.__filter.getOptions(self.__opts)
|
||||||
|
else:
|
||||||
|
logSys.error("Unable to read the filter")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Read action
|
||||||
|
for act in self.__opts["action"].split('\n'):
|
||||||
|
try:
|
||||||
|
splitAct = JailReader.splitAction(act)
|
||||||
|
action = ActionReader(splitAct, self.__name)
|
||||||
|
ret = action.read()
|
||||||
|
if ret:
|
||||||
|
action.getOptions(self.__opts)
|
||||||
|
self.__actions.append(action)
|
||||||
|
else:
|
||||||
|
raise AttributeError("Unable to read action")
|
||||||
|
except AttributeError, e:
|
||||||
|
logSys.error("Error in action definition " + act)
|
||||||
|
logSys.debug(e)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def convert(self):
|
||||||
|
stream = []
|
||||||
|
for opt in self.__opts:
|
||||||
|
if opt == "logpath":
|
||||||
|
for path in self.__opts[opt].split("\n"):
|
||||||
|
pathList = glob.glob(path)
|
||||||
|
if len(pathList) == 0:
|
||||||
|
logSys.error("No file found for " + path)
|
||||||
|
for p in pathList:
|
||||||
|
stream.append(["set", self.__name, "addlogpath", p])
|
||||||
|
elif opt == "backend":
|
||||||
|
backend = self.__opts[opt]
|
||||||
|
elif opt == "maxretry":
|
||||||
|
stream.append(["set", self.__name, "maxretry", self.__opts[opt]])
|
||||||
|
elif opt == "ignoreip":
|
||||||
|
for ip in self.__opts[opt].split():
|
||||||
|
stream.append(["set", self.__name, "addignoreip", ip])
|
||||||
|
elif opt == "maxtime":
|
||||||
|
stream.append(["set", self.__name, "maxtime", 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())
|
||||||
|
stream.insert(0, ["add", self.__name, backend])
|
||||||
|
return stream
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def splitAction(action):
|
||||||
|
m = JailReader.actionCRE.match(action)
|
||||||
|
d = dict()
|
||||||
|
if not m.group(2) == None:
|
||||||
|
for param in m.group(2).split(','):
|
||||||
|
p = param.split('=')
|
||||||
|
try:
|
||||||
|
d[p[0].strip()] = p[1].strip()
|
||||||
|
except IndexError:
|
||||||
|
logSys.error("Invalid argument %s in '%s'" % (p, m.group(2)))
|
||||||
|
return [m.group(1), d]
|
|
@ -0,0 +1,71 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__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()
|
||||||
|
ret = jail.getOptions()
|
||||||
|
if ret:
|
||||||
|
if jail.isEnabled():
|
||||||
|
# We only add enabled jails
|
||||||
|
self.__jails.append(jail)
|
||||||
|
else:
|
||||||
|
logSys.error("Errors in jail '" + sec + "'. Skipping...")
|
||||||
|
|
||||||
|
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,65 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 394 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: fwstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstart = touch <tmpfile>
|
||||||
|
|
||||||
|
# Option: fwend
|
||||||
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstop = rm -f <tmpfile>
|
||||||
|
|
||||||
|
# Option: fwcheck
|
||||||
|
# Notes.: command executed once before each fwban command
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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,66 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Nick Munger
|
||||||
|
# Modified by: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 254 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: fwstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstart =
|
||||||
|
|
||||||
|
|
||||||
|
# Option: fwend
|
||||||
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstop =
|
||||||
|
|
||||||
|
|
||||||
|
# Option: fwcheck
|
||||||
|
# Notes.: command executed once before each fwban command
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
actionban = ipfw add deny tcp from <ip> to <localhost> <port>
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
actionunban = ipfw delete `ipfw list | grep -i <ip> | awk '{print $1;}'`
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Option: port
|
||||||
|
# Notes.: specifies port to monitor
|
||||||
|
# Values: [ NUM | STRING ]
|
||||||
|
#
|
||||||
|
port = ssh
|
||||||
|
|
||||||
|
# Option: localhost
|
||||||
|
# Notes.: the local IP address of the network interface
|
||||||
|
# Values: IP
|
||||||
|
#
|
||||||
|
localhost = 127.0.0.1
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 394 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: fwstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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,70 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 254 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: fwstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
actionunban =
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Defaut name of the chain
|
||||||
|
#
|
||||||
|
name = default
|
||||||
|
|
||||||
|
# Destinataire of the mail
|
||||||
|
#
|
||||||
|
dest = root
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 254 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: fwstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
actionunban =
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
|
||||||
|
# Defaut name of the chain
|
||||||
|
#
|
||||||
|
name = default
|
||||||
|
|
||||||
|
# Destinataire of the mail
|
||||||
|
#
|
||||||
|
dest = root
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 394 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: fwstart
|
||||||
|
# Notes.: command executed once at the start of Fail2Ban.
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstart =
|
||||||
|
|
||||||
|
# Option: fwend
|
||||||
|
# Notes.: command executed once at the end of Fail2Ban
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
actionstop =
|
||||||
|
|
||||||
|
# Option: fwcheck
|
||||||
|
# Notes.: command executed once before each fwban command
|
||||||
|
# Values: CMD
|
||||||
|
#
|
||||||
|
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
|
||||||
|
#
|
||||||
|
actionban = shorewall reject <ip>
|
||||||
|
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
actionunban = shorewall allow <ip>
|
|
@ -1,73 +0,0 @@
|
||||||
#! /bin/sh
|
|
||||||
#
|
|
||||||
# Fail2Ban init.d file - to be launched on boot
|
|
||||||
#
|
|
||||||
# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
|
|
||||||
# Modified for Debian
|
|
||||||
# by Ian Murdock <imurdock@gnu.ai.mit.edu>.
|
|
||||||
# Adjusted for Fail2Ban
|
|
||||||
# by Yaroslav Halchenko <debian@onerussian.com>.
|
|
||||||
#
|
|
||||||
# Version: $Id: debian-initd,v 1.2 2005/11/20 17:07:47 lostcontrol Exp $
|
|
||||||
#
|
|
||||||
|
|
||||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
|
||||||
DAEMON=/usr/bin/fail2ban
|
|
||||||
NAME=fail2ban
|
|
||||||
DESC=fail2ban
|
|
||||||
PIDFILE=/var/run/$NAME.pid
|
|
||||||
|
|
||||||
test -x $DAEMON || exit 0
|
|
||||||
|
|
||||||
# Include fail2ban defaults if available
|
|
||||||
if [ -f /etc/default/fail2ban ] ; then
|
|
||||||
. /etc/default/fail2ban
|
|
||||||
fi
|
|
||||||
DAEMON_OPTS=$FAIL2BAN_OPTS
|
|
||||||
set -e
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
echo -n "Starting $DESC: "
|
|
||||||
[ -f $PIDFILE ] && [ ! -d /proc/`cat $PIDFILE` ] && rm -f $PIDFILE
|
|
||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE \
|
|
||||||
-b --exec $DAEMON -- $DAEMON_OPTS
|
|
||||||
echo "$NAME."
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
echo -n "Stopping $DESC: "
|
|
||||||
start-stop-daemon --stop --quiet --pidfile $PIDFILE
|
|
||||||
echo "$NAME."
|
|
||||||
;;
|
|
||||||
restart|force-reload)
|
|
||||||
echo -n "Restarting $DESC: "
|
|
||||||
( $0 stop )
|
|
||||||
sleep 1
|
|
||||||
$0 start
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
echo -n "Status of $DESC: "
|
|
||||||
if [ ! -e "$PIDFILE" ]; then
|
|
||||||
echo "$NAME is not running."
|
|
||||||
exit 3
|
|
||||||
fi
|
|
||||||
if [ ! -r "$PIDFILE" ]; then
|
|
||||||
echo "$PIDFILE not readable, status of $NAME unknown."
|
|
||||||
exit 4
|
|
||||||
fi
|
|
||||||
if [ -d /proc/`cat "$PIDFILE"` ]; then
|
|
||||||
echo "$NAME is running."
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
echo "$NAME is not running but $PIDFILE exists."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
N=/etc/init.d/$NAME
|
|
||||||
echo "Usage: $N {start|stop|restart|force-reload|status}" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit 0
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 412 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
# Option: socket
|
||||||
|
# Notes.: Set the socket file. This is used to communication with the
|
||||||
|
# daemon.
|
||||||
|
# Values: FILE Default: /tmp/fail2ban.sock
|
||||||
|
#
|
||||||
|
socket = /tmp/fail2ban.sock
|
||||||
|
|
|
@ -1,327 +0,0 @@
|
||||||
# Fail2Ban configuration file
|
|
||||||
#
|
|
||||||
# $Revision: 1.2 $
|
|
||||||
#
|
|
||||||
# 2005.06.21 modified for readability Iain Lea iain@bricbrac.de
|
|
||||||
|
|
||||||
[DEFAULT]
|
|
||||||
# Option: background
|
|
||||||
# Notes.: start fail2ban as a daemon. Output is redirect to logfile.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
background = false
|
|
||||||
|
|
||||||
# Option: logtargets
|
|
||||||
# Notes.: log targets. Space separated list of logging targets.
|
|
||||||
# Values: STDERR SYSLOG file Default: /var/log/fail2ban.log
|
|
||||||
#
|
|
||||||
logtargets = /var/log/fail2ban.log
|
|
||||||
|
|
||||||
# Option: syslog-target
|
|
||||||
# Notes.: where to find syslog facility if logtarget SYSLOG.
|
|
||||||
# Values: SOCKET HOST HOST:PORT Default: /dev/log
|
|
||||||
#
|
|
||||||
syslog-target = /dev/log
|
|
||||||
|
|
||||||
# Option: syslog-facility
|
|
||||||
# Notes.: which syslog facility to use if logtarget SYSLOG.
|
|
||||||
# Values: NUM Default: 1
|
|
||||||
#
|
|
||||||
syslog-facility = 1
|
|
||||||
|
|
||||||
# Option: pidlock
|
|
||||||
# Notes.: path of the PID lock file (must be able to write to file).
|
|
||||||
# Values: FILE Default: /var/run/fail2ban.pid
|
|
||||||
#
|
|
||||||
pidlock = /var/run/fail2ban.pid
|
|
||||||
|
|
||||||
# Option: maxfailures
|
|
||||||
# Notes.: number of failures before IP gets banned.
|
|
||||||
# Values: NUM Default: 5
|
|
||||||
#
|
|
||||||
maxfailures = 5
|
|
||||||
|
|
||||||
# Option: bantime
|
|
||||||
# Notes.: number of seconds an IP will be banned. If set to a negative
|
|
||||||
# value, IP will never be unbanned (permanent banning).
|
|
||||||
# Values: NUM Default: 600
|
|
||||||
#
|
|
||||||
bantime = 600
|
|
||||||
|
|
||||||
# Option: findtime
|
|
||||||
# Notes.: lifetime in seconds of a "failed" log entry.
|
|
||||||
# Values: NUM Default: 600
|
|
||||||
#
|
|
||||||
findtime = 600
|
|
||||||
|
|
||||||
# Option: ignoreip
|
|
||||||
# Notes.: space separated list of IP's to be ignored by fail2ban.
|
|
||||||
# You can use CIDR mask in order to specify a range.
|
|
||||||
# Example: ignoreip = 192.168.0.1/24 123.45.235.65
|
|
||||||
# Values: IP Default:
|
|
||||||
#
|
|
||||||
ignoreip =
|
|
||||||
|
|
||||||
# Option: cmdstart
|
|
||||||
# Notes.: command executed once at the start of Fail2Ban
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
cmdstart =
|
|
||||||
|
|
||||||
# Option: cmdend
|
|
||||||
# Notes.: command executed once at the end of Fail2Ban.
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
cmdend =
|
|
||||||
|
|
||||||
# Option: polltime
|
|
||||||
# Notes.: number of seconds fail2ban sleeps between iterations.
|
|
||||||
# Values: NUM Default: 1
|
|
||||||
#
|
|
||||||
polltime = 1
|
|
||||||
|
|
||||||
# Option: reinittime
|
|
||||||
# Notes.: minimal number of seconds between the re-initialization of
|
|
||||||
# firewalls due to external changes in their rules (see fwcheck)
|
|
||||||
# Values: NUM Default: 100
|
|
||||||
#
|
|
||||||
reinittime = 10
|
|
||||||
|
|
||||||
# Option: maxreinits
|
|
||||||
# Notes.: maximal number of re-initialization of firewalls due to external
|
|
||||||
# changes. -1 stays for infinite, so only reinittime is of importance
|
|
||||||
# Values: NUM Default: -1
|
|
||||||
#
|
|
||||||
maxreinits = -1
|
|
||||||
|
|
||||||
# NOTE: Interpolations
|
|
||||||
#
|
|
||||||
# fwstart, as well as fwend, fwcheck, fwban, fwunban, use interpolations
|
|
||||||
# so %(__name__)s will be substituted by a name of each section
|
|
||||||
# (unless the option is overriden in a section).
|
|
||||||
# If you are going to use interpolations in your setup, please make
|
|
||||||
# sure that you specified options port and protocol (which also has
|
|
||||||
# an option in DEFAULT).
|
|
||||||
#
|
|
||||||
|
|
||||||
# Option: hostsdeny
|
|
||||||
# Notes.: hosts.deny file path.
|
|
||||||
# Values: STR Default: /etc/hosts.deny
|
|
||||||
#
|
|
||||||
hostsdeny = /etc/hosts.deny
|
|
||||||
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
fwban = IP=<ip> && echo "ALL: $IP" >> %(hostsdeny)s
|
|
||||||
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
fwunban = IP=<ip> && sed -i.old s/ALL:\ $IP// %(hostsdeny)s
|
|
||||||
|
|
||||||
[MAIL]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable mail notification when banning an IP address.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Option: host
|
|
||||||
# Notes.: host running the mail server.
|
|
||||||
# Values: STR Default: localhost
|
|
||||||
#
|
|
||||||
host = localhost
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: port of the mail server.
|
|
||||||
# Values: INT Default: 25
|
|
||||||
#
|
|
||||||
port = 25
|
|
||||||
|
|
||||||
# Option: user
|
|
||||||
# Notes.: the username for smtp-server if authentification is required.
|
|
||||||
# if user is empty, no authentification is done.
|
|
||||||
# Values: STR Default:
|
|
||||||
#
|
|
||||||
user =
|
|
||||||
|
|
||||||
# Option: password
|
|
||||||
# Notes.: the smtp-user's password if authentification is required.
|
|
||||||
# Values: STR Default:
|
|
||||||
#
|
|
||||||
password =
|
|
||||||
|
|
||||||
# Option: from
|
|
||||||
# Notes.: e-mail address of the sender.
|
|
||||||
# Values: MAIL Default: fail2ban
|
|
||||||
#
|
|
||||||
from = fail2ban
|
|
||||||
|
|
||||||
# Option: to
|
|
||||||
# Notes.: e-mail addresses of the receiver. Addresses are space
|
|
||||||
# separated.
|
|
||||||
# Values: MAIL Default: root
|
|
||||||
#
|
|
||||||
to = root
|
|
||||||
|
|
||||||
# Option: localtime
|
|
||||||
# Notes.: report local time (including timezone) or GMT
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
localtime = true
|
|
||||||
|
|
||||||
# Option: subject
|
|
||||||
# Notes.: subject of the e-mail.
|
|
||||||
# Tags: <section> active section (eg ssh, apache, etc)
|
|
||||||
# <ip> IP address
|
|
||||||
# <failures> number of failures
|
|
||||||
# <failtime> unix timestamp of the last failure
|
|
||||||
# Values: TEXT Default: [Fail2Ban] <section>: Banned <ip>
|
|
||||||
#
|
|
||||||
subject = [Fail2Ban] <section>: Banned <ip>
|
|
||||||
|
|
||||||
# Option: message
|
|
||||||
# Notes.: message of the e-mail.
|
|
||||||
# Tags: <section> active section (eg ssh, apache, etc)
|
|
||||||
# <ip> IP address
|
|
||||||
# <failures> number of failures
|
|
||||||
# <failtime> unix timestamp of the last failure
|
|
||||||
# <br> new line
|
|
||||||
# Values: TEXT Default:
|
|
||||||
#
|
|
||||||
message = Hi,<br>
|
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
|
||||||
<failures> attempts against <section>.<br>
|
|
||||||
Regards,<br>
|
|
||||||
Fail2Ban
|
|
||||||
|
|
||||||
# You can define a new section for each log file to check for
|
|
||||||
# password failure. Each section has to define the following
|
|
||||||
# options: logfile, fwban, fwunban, timeregex, timepattern,
|
|
||||||
# failregex.
|
|
||||||
|
|
||||||
|
|
||||||
[Apache]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
[VSFTPD]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Option: logfile
|
|
||||||
# Notes.: logfile to monitor.
|
|
||||||
# Values: FILE Default: /var/log/secure
|
|
||||||
#
|
|
||||||
logfile = /var/log/vsftpd.log
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = ftp
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
[SSH]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: true
|
|
||||||
#
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
# Option: logfile
|
|
||||||
# Notes.: logfile to monitor.
|
|
||||||
# Values: FILE Default: /var/log/secure
|
|
||||||
#
|
|
||||||
logfile = /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 password|Invalid user
|
|
|
@ -1,361 +0,0 @@
|
||||||
# Fail2Ban configuration file
|
|
||||||
#
|
|
||||||
# $Revision: 1.2.2.1 $
|
|
||||||
#
|
|
||||||
# 2005.06.21 modified for readability Iain Lea iain@bricbrac.de
|
|
||||||
|
|
||||||
[DEFAULT]
|
|
||||||
# Option: background
|
|
||||||
# Notes.: start fail2ban as a daemon. Output is redirect to logfile.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
background = false
|
|
||||||
|
|
||||||
# Option: logtargets
|
|
||||||
# Notes.: log targets. Space separated list of logging targets.
|
|
||||||
# Values: STDERR SYSLOG file Default: /var/log/fail2ban.log
|
|
||||||
#
|
|
||||||
logtargets = /var/log/fail2ban.log
|
|
||||||
|
|
||||||
# Option: syslog-target
|
|
||||||
# Notes.: where to find syslog facility if logtarget SYSLOG.
|
|
||||||
# Values: SOCKET HOST HOST:PORT Default: /dev/log
|
|
||||||
#
|
|
||||||
syslog-target = /dev/log
|
|
||||||
|
|
||||||
# Option: syslog-facility
|
|
||||||
# Notes.: which syslog facility to use if logtarget SYSLOG.
|
|
||||||
# Values: NUM Default: 1
|
|
||||||
#
|
|
||||||
syslog-facility = 1
|
|
||||||
|
|
||||||
# Option: pidlock
|
|
||||||
# Notes.: path of the PID lock file (must be able to write to file).
|
|
||||||
# Values: FILE Default: /var/run/fail2ban.pid
|
|
||||||
#
|
|
||||||
pidlock = /var/run/fail2ban.pid
|
|
||||||
|
|
||||||
# Option: maxfailures
|
|
||||||
# Notes.: number of failures before IP gets banned.
|
|
||||||
# Values: NUM Default: 5
|
|
||||||
#
|
|
||||||
maxfailures = 5
|
|
||||||
|
|
||||||
# Option: bantime
|
|
||||||
# Notes.: number of seconds an IP will be banned. If set to a negative
|
|
||||||
# value, IP will never be unbanned (permanent banning).
|
|
||||||
# Values: NUM Default: 600
|
|
||||||
#
|
|
||||||
bantime = 600
|
|
||||||
|
|
||||||
# Option: findtime
|
|
||||||
# Notes.: lifetime in seconds of a "failed" log entry.
|
|
||||||
# Values: NUM Default: 600
|
|
||||||
#
|
|
||||||
findtime = 600
|
|
||||||
|
|
||||||
# Option: ignoreip
|
|
||||||
# Notes.: space separated list of IP's to be ignored by fail2ban.
|
|
||||||
# You can use CIDR mask in order to specify a range.
|
|
||||||
# Example: ignoreip = 192.168.0.1/24 123.45.235.65
|
|
||||||
# Values: IP Default:
|
|
||||||
#
|
|
||||||
ignoreip =
|
|
||||||
|
|
||||||
# Option: cmdstart
|
|
||||||
# Notes.: command executed once at the start of Fail2Ban
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
cmdstart =
|
|
||||||
|
|
||||||
# Option: cmdend
|
|
||||||
# Notes.: command executed once at the end of Fail2Ban.
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
cmdend =
|
|
||||||
|
|
||||||
# Option: polltime
|
|
||||||
# Notes.: number of seconds fail2ban sleeps between iterations.
|
|
||||||
# Values: NUM Default: 1
|
|
||||||
#
|
|
||||||
polltime = 1
|
|
||||||
|
|
||||||
# Option: reinittime
|
|
||||||
# Notes.: minimal number of seconds between the re-initialization of
|
|
||||||
# firewalls due to external changes in their rules (see fwcheck)
|
|
||||||
# Values: NUM Default: 100
|
|
||||||
#
|
|
||||||
reinittime = 10
|
|
||||||
|
|
||||||
# Option: maxreinits
|
|
||||||
# Notes.: maximal number of re-initialization of firewalls due to external
|
|
||||||
# changes. -1 stays for infinite, so only reinittime is of importance
|
|
||||||
# Values: NUM Default: -1
|
|
||||||
#
|
|
||||||
maxreinits = -1
|
|
||||||
|
|
||||||
# NOTE: Interpolations
|
|
||||||
#
|
|
||||||
# fwstart, as well as fwend, fwcheck, fwban, fwunban, use interpolations
|
|
||||||
# so %(__name__)s will be substituted by a name of each section
|
|
||||||
# (unless the option is overriden in a section).
|
|
||||||
# If you are going to use interpolations in your setup, please make
|
|
||||||
# sure that you specified options port and protocol (which also has
|
|
||||||
# an option in DEFAULT).
|
|
||||||
#
|
|
||||||
|
|
||||||
# Option: protocol
|
|
||||||
# Notes.: internally used by config reader for interpolations.
|
|
||||||
# Values: [ tcp | udp | icmp | all ] Default: tcp
|
|
||||||
#
|
|
||||||
protocol = tcp
|
|
||||||
|
|
||||||
# Option: fwstart
|
|
||||||
# Notes.: command executed once at the start of Fail2Ban.
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
fwstart = iptables -N fail2ban-%(__name__)s
|
|
||||||
iptables -A fail2ban-%(__name__)s -j RETURN
|
|
||||||
iptables -I INPUT -p %(protocol)s --dport %(port)s -j fail2ban-%(__name__)s
|
|
||||||
|
|
||||||
# Option: fwend
|
|
||||||
# Notes.: command executed once at the end of Fail2Ban
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
fwend = iptables -D INPUT -p %(protocol)s --dport %(port)s -j fail2ban-%(__name__)s
|
|
||||||
iptables -F fail2ban-%(__name__)s
|
|
||||||
iptables -X fail2ban-%(__name__)s
|
|
||||||
|
|
||||||
# Option: fwcheck
|
|
||||||
# Notes.: command executed once before each fwban command
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
fwcheck = iptables -L INPUT | grep -q fail2ban-%(__name__)s
|
|
||||||
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
fwban = iptables -I fail2ban-%(__name__)s 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
|
|
||||||
#
|
|
||||||
fwunban = iptables -D fail2ban-%(__name__)s -s <ip> -j DROP
|
|
||||||
|
|
||||||
[MAIL]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable mail notification when banning an IP address.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Option: host
|
|
||||||
# Notes.: host running the mail server.
|
|
||||||
# Values: STR Default: localhost
|
|
||||||
#
|
|
||||||
host = localhost
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: port of the mail server.
|
|
||||||
# Values: INT Default: 25
|
|
||||||
#
|
|
||||||
port = 25
|
|
||||||
|
|
||||||
# Option: user
|
|
||||||
# Notes.: the username for smtp-server if authentification is required.
|
|
||||||
# if user is empty, no authentification is done.
|
|
||||||
# Values: STR Default:
|
|
||||||
#
|
|
||||||
user =
|
|
||||||
|
|
||||||
# Option: password
|
|
||||||
# Notes.: the smtp-user's password if authentification is required.
|
|
||||||
# Values: STR Default:
|
|
||||||
#
|
|
||||||
password =
|
|
||||||
|
|
||||||
# Option: from
|
|
||||||
# Notes.: e-mail address of the sender.
|
|
||||||
# Values: MAIL Default: fail2ban
|
|
||||||
#
|
|
||||||
from = fail2ban
|
|
||||||
|
|
||||||
# Option: to
|
|
||||||
# Notes.: e-mail addresses of the receiver. Addresses are space
|
|
||||||
# separated.
|
|
||||||
# Values: MAIL Default: root
|
|
||||||
#
|
|
||||||
to = root
|
|
||||||
|
|
||||||
# Option: localtime
|
|
||||||
# Notes.: report local time (including timezone) or GMT
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
localtime = true
|
|
||||||
|
|
||||||
# Option: subject
|
|
||||||
# Notes.: subject of the e-mail.
|
|
||||||
# Tags: <section> active section (eg ssh, apache, etc)
|
|
||||||
# <ip> IP address
|
|
||||||
# <failures> number of failures
|
|
||||||
# <failtime> unix timestamp of the last failure
|
|
||||||
# Values: TEXT Default: [Fail2Ban] <section>: Banned <ip>
|
|
||||||
#
|
|
||||||
subject = [Fail2Ban] <section>: Banned <ip>
|
|
||||||
|
|
||||||
# Option: message
|
|
||||||
# Notes.: message of the e-mail.
|
|
||||||
# Tags: <section> active section (eg ssh, apache, etc)
|
|
||||||
# <ip> IP address
|
|
||||||
# <failures> number of failures
|
|
||||||
# <failtime> unix timestamp of the last failure
|
|
||||||
# <br> new line
|
|
||||||
# Values: TEXT Default:
|
|
||||||
#
|
|
||||||
message = Hi,<br>
|
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
|
||||||
<failures> attempts against <section>.<br>
|
|
||||||
Regards,<br>
|
|
||||||
Fail2Ban
|
|
||||||
|
|
||||||
# You can define a new section for each log file to check for
|
|
||||||
# password failure. Each section has to define the following
|
|
||||||
# options: logfile, fwban, fwunban, timeregex, timepattern,
|
|
||||||
# failregex.
|
|
||||||
|
|
||||||
|
|
||||||
[Apache]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Option: logfile
|
|
||||||
# Notes.: logfile to monitor.
|
|
||||||
# Values: FILE Default: /var/log/httpd/access_log
|
|
||||||
#
|
|
||||||
logfile = /var/log/httpd/access_log
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = http
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
[VSFTPD]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Option: logfile
|
|
||||||
# Notes.: logfile to monitor.
|
|
||||||
# Values: FILE Default: /var/log/secure
|
|
||||||
#
|
|
||||||
logfile = /var/log/vsftpd.log
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = ftp
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
[SSH]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: true
|
|
||||||
#
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
# Option: logfile
|
|
||||||
# Notes.: logfile to monitor.
|
|
||||||
# Values: FILE Default: /var/log/secure
|
|
||||||
#
|
|
||||||
logfile = /var/log/secure
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: specifies port to monitor
|
|
||||||
# Values: [ NUM | STRING ] Default:
|
|
||||||
#
|
|
||||||
port = ssh
|
|
||||||
|
|
||||||
# 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 password|Invalid user
|
|
|
@ -1,314 +0,0 @@
|
||||||
# Fail2Ban configuration file
|
|
||||||
#
|
|
||||||
# $Revision: 1.2 $
|
|
||||||
#
|
|
||||||
# 2005.06.21 modified for readability Iain Lea iain@bricbrac.de
|
|
||||||
|
|
||||||
[DEFAULT]
|
|
||||||
# Option: background
|
|
||||||
# Notes.: start fail2ban as a daemon. Output is redirect to logfile.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
background = false
|
|
||||||
|
|
||||||
# Option: logtargets
|
|
||||||
# Notes.: log targets. Space separated list of logging targets.
|
|
||||||
# Values: STDERR SYSLOG file Default: /var/log/fail2ban.log
|
|
||||||
#
|
|
||||||
logtargets = /var/log/fail2ban.log
|
|
||||||
|
|
||||||
# Option: syslog-target
|
|
||||||
# Notes.: where to find syslog facility if logtarget SYSLOG.
|
|
||||||
# Values: SOCKET HOST HOST:PORT Default: /dev/log
|
|
||||||
#
|
|
||||||
syslog-target = /dev/log
|
|
||||||
|
|
||||||
# Option: syslog-facility
|
|
||||||
# Notes.: which syslog facility to use if logtarget SYSLOG.
|
|
||||||
# Values: NUM Default: 1
|
|
||||||
#
|
|
||||||
syslog-facility = 1
|
|
||||||
|
|
||||||
# Option: pidlock
|
|
||||||
# Notes.: path of the PID lock file (must be able to write to file).
|
|
||||||
# Values: FILE Default: /var/run/fail2ban.pid
|
|
||||||
#
|
|
||||||
pidlock = /var/run/fail2ban.pid
|
|
||||||
|
|
||||||
# Option: maxfailures
|
|
||||||
# Notes.: number of failures before IP gets banned.
|
|
||||||
# Values: NUM Default: 5
|
|
||||||
#
|
|
||||||
maxfailures = 5
|
|
||||||
|
|
||||||
# Option: bantime
|
|
||||||
# Notes.: number of seconds an IP will be banned. If set to a negative
|
|
||||||
# value, IP will never be unbanned (permanent banning).
|
|
||||||
# Values: NUM Default: 600
|
|
||||||
#
|
|
||||||
bantime = 600
|
|
||||||
|
|
||||||
# Option: findtime
|
|
||||||
# Notes.: lifetime in seconds of a "failed" log entry.
|
|
||||||
# Values: NUM Default: 600
|
|
||||||
#
|
|
||||||
findtime = 600
|
|
||||||
|
|
||||||
# Option: ignoreip
|
|
||||||
# Notes.: space separated list of IP's to be ignored by fail2ban.
|
|
||||||
# You can use CIDR mask in order to specify a range.
|
|
||||||
# Example: ignoreip = 192.168.0.1/24 123.45.235.65
|
|
||||||
# Values: IP Default:
|
|
||||||
#
|
|
||||||
ignoreip =
|
|
||||||
|
|
||||||
# Option: cmdstart
|
|
||||||
# Notes.: command executed once at the start of Fail2Ban
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
cmdstart =
|
|
||||||
|
|
||||||
# Option: cmdend
|
|
||||||
# Notes.: command executed once at the end of Fail2Ban.
|
|
||||||
# Values: CMD Default:
|
|
||||||
#
|
|
||||||
cmdend =
|
|
||||||
|
|
||||||
# Option: polltime
|
|
||||||
# Notes.: number of seconds fail2ban sleeps between iterations.
|
|
||||||
# Values: NUM Default: 1
|
|
||||||
#
|
|
||||||
polltime = 1
|
|
||||||
|
|
||||||
# Option: reinittime
|
|
||||||
# Notes.: minimal number of seconds between the re-initialization of
|
|
||||||
# firewalls due to external changes in their rules (see fwcheck)
|
|
||||||
# Values: NUM Default: 100
|
|
||||||
#
|
|
||||||
reinittime = 10
|
|
||||||
|
|
||||||
# Option: maxreinits
|
|
||||||
# Notes.: maximal number of re-initialization of firewalls due to external
|
|
||||||
# changes. -1 stays for infinite, so only reinittime is of importance
|
|
||||||
# Values: NUM Default: -1
|
|
||||||
#
|
|
||||||
maxreinits = -1
|
|
||||||
|
|
||||||
# NOTE: Interpolations
|
|
||||||
#
|
|
||||||
# fwstart, as well as fwend, fwcheck, fwban, fwunban, use interpolations
|
|
||||||
# so %(__name__)s will be substituted by a name of each section
|
|
||||||
# (unless the option is overriden in a section).
|
|
||||||
# If you are going to use interpolations in your setup, please make
|
|
||||||
# sure that you specified options port and protocol (which also has
|
|
||||||
# an option in DEFAULT).
|
|
||||||
#
|
|
||||||
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
fwban = shorewall drop <ip>
|
|
||||||
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
fwunban = shorewall allow <ip>
|
|
||||||
|
|
||||||
[MAIL]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable mail notification when banning an IP address.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Option: host
|
|
||||||
# Notes.: host running the mail server.
|
|
||||||
# Values: STR Default: localhost
|
|
||||||
#
|
|
||||||
host = localhost
|
|
||||||
|
|
||||||
# Option: port
|
|
||||||
# Notes.: port of the mail server.
|
|
||||||
# Values: INT Default: 25
|
|
||||||
#
|
|
||||||
port = 25
|
|
||||||
|
|
||||||
# Option: user
|
|
||||||
# Notes.: the username for smtp-server if authentification is required.
|
|
||||||
# if user is empty, no authentification is done.
|
|
||||||
# Values: STR Default:
|
|
||||||
#
|
|
||||||
user =
|
|
||||||
|
|
||||||
# Option: password
|
|
||||||
# Notes.: the smtp-user's password if authentification is required.
|
|
||||||
# Values: STR Default:
|
|
||||||
#
|
|
||||||
password =
|
|
||||||
|
|
||||||
# Option: from
|
|
||||||
# Notes.: e-mail address of the sender.
|
|
||||||
# Values: MAIL Default: fail2ban
|
|
||||||
#
|
|
||||||
from = fail2ban
|
|
||||||
|
|
||||||
# Option: to
|
|
||||||
# Notes.: e-mail addresses of the receiver. Addresses are space
|
|
||||||
# separated.
|
|
||||||
# Values: MAIL Default: root
|
|
||||||
#
|
|
||||||
to = root
|
|
||||||
|
|
||||||
# Option: localtime
|
|
||||||
# Notes.: report local time (including timezone) or GMT
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
localtime = true
|
|
||||||
|
|
||||||
# Option: subject
|
|
||||||
# Notes.: subject of the e-mail.
|
|
||||||
# Tags: <section> active section (eg ssh, apache, etc)
|
|
||||||
# <ip> IP address
|
|
||||||
# <failures> number of failures
|
|
||||||
# <failtime> unix timestamp of the last failure
|
|
||||||
# Values: TEXT Default: [Fail2Ban] <section>: Banned <ip>
|
|
||||||
#
|
|
||||||
subject = [Fail2Ban] <section>: Banned <ip>
|
|
||||||
|
|
||||||
# Option: message
|
|
||||||
# Notes.: message of the e-mail.
|
|
||||||
# Tags: <section> active section (eg ssh, apache, etc)
|
|
||||||
# <ip> IP address
|
|
||||||
# <failures> number of failures
|
|
||||||
# <failtime> unix timestamp of the last failure
|
|
||||||
# <br> new line
|
|
||||||
# Values: TEXT Default:
|
|
||||||
#
|
|
||||||
message = Hi,<br>
|
|
||||||
The IP <ip> has just been banned by Fail2Ban after
|
|
||||||
<failures> attempts against <section>.<br>
|
|
||||||
Regards,<br>
|
|
||||||
Fail2Ban
|
|
||||||
|
|
||||||
# You can define a new section for each log file to check for
|
|
||||||
# password failure. Each section has to define the following
|
|
||||||
# options: logfile, fwban, fwunban, timeregex, timepattern,
|
|
||||||
# failregex.
|
|
||||||
|
|
||||||
|
|
||||||
[Apache]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
[VSFTPD]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: false
|
|
||||||
#
|
|
||||||
enabled = false
|
|
||||||
|
|
||||||
# Option: logfile
|
|
||||||
# Notes.: logfile to monitor.
|
|
||||||
# Values: FILE Default: /var/log/secure
|
|
||||||
#
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
[SSH]
|
|
||||||
# Option: enabled
|
|
||||||
# Notes.: enable monitoring for this section.
|
|
||||||
# Values: [true | false] Default: true
|
|
||||||
#
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
# Option: logfile
|
|
||||||
# Notes.: logfile to monitor.
|
|
||||||
# Values: FILE Default: /var/log/secure
|
|
||||||
#
|
|
||||||
logfile = /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 password|Invalid user
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 394 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failure messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = [[]client (?P<host>\S*)[]] user .*(?:: authentication failure|not found)
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 394 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failure messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = [[]client (?P<host>\S*)[]] File does not exist: .*(\.php|\.asp)
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Christoph Haas
|
||||||
|
# Modified by: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 267 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = LOGIN FAILED, ip=\[::ffff:(?P<host>\S*)\]$
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 267 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 267 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = reject: RCPT from (.*)\[(?P<host>\S*)\]: 554
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Yaroslav Halchenko
|
||||||
|
#
|
||||||
|
# $Revision: 331 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = USER \S+: no such user found from \S* ?\[(?P<host>\S+)\] to \S+\s*$
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 267 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = (?:[\d,.]+[\d,.] rblsmtpd: |421 badiprbl: ip )(?P<host>\S*)
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Yaroslav Halchenko
|
||||||
|
#
|
||||||
|
# $Revision: 331 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = : warning: [-._\w]+\[(?P<host>[.\d]+)\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed$
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 394 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
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,14 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 394 $
|
||||||
|
#
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
# Option: failregex
|
||||||
|
# Notes.: regex to match the password failures messages in the logfile.
|
||||||
|
# Values: TEXT
|
||||||
|
#
|
||||||
|
failregex = vsftpd: \(pam_unix\) authentication failure; .* rhost=(?P<host>\S*)
|
|
@ -1,23 +0,0 @@
|
||||||
# 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: 1.2 $
|
|
||||||
|
|
||||||
# Command line options for Fail2Ban. Refer to "fail2ban -h" for
|
|
||||||
# valid options.
|
|
||||||
FAIL2BAN_OPTS="-v"
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
# Fail2Ban configuration file
|
||||||
|
#
|
||||||
|
# Author: Cyril Jaquier
|
||||||
|
#
|
||||||
|
# $Revision: 421 $
|
||||||
|
#
|
||||||
|
|
||||||
|
# The DEFAULT allows a global definition of the options. They can be override
|
||||||
|
# in each jail afterwards.
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
|
||||||
|
# "ignoreip" can be an IP address, a CIDR mask or a DNS host
|
||||||
|
ignoreip = 127.0.0.1
|
||||||
|
bantime = 600
|
||||||
|
maxretry = 3
|
||||||
|
|
||||||
|
# "backend" specifies the backend used to get files modification. Available
|
||||||
|
# options are "gamin", "polling" and "auto".
|
||||||
|
backend = auto
|
||||||
|
|
||||||
|
|
||||||
|
# This jail corresponds to the standard configuration in Fail2ban 0.6.
|
||||||
|
# The mail-whois action send a notification e-mail with a whois request
|
||||||
|
# in the body.
|
||||||
|
|
||||||
|
[ssh-iptables]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = sshd
|
||||||
|
action = iptables[name=SSH, port=ssh, protocol=tcp]
|
||||||
|
mail-whois[name=SSH, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/sshd.log
|
||||||
|
maxretry = 5
|
||||||
|
|
||||||
|
[proftpd-iptables]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = proftpd
|
||||||
|
action = iptables[name=ProFTPD, port=ftp, protocol=tcp]
|
||||||
|
mail-whois[name=ProFTPD, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/proftpd/proftpd.log
|
||||||
|
maxretry = 6
|
||||||
|
|
||||||
|
# This jail forces the backend to "polling".
|
||||||
|
|
||||||
|
[sasl-iptables]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = sasl
|
||||||
|
backend = polling
|
||||||
|
action = iptables[name=sasl, port=smtp, protocol=tcp]
|
||||||
|
mail-whois[name=sasl, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/mail.log
|
||||||
|
|
||||||
|
# This one behaves like the previous and sends a report when the jail
|
||||||
|
# is stopped.
|
||||||
|
|
||||||
|
[ssh-iptables-report]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = sshd
|
||||||
|
action = iptables[name=SSH, port=ssh, protocol=tcp]
|
||||||
|
mail-whois[name=SSH, dest=yourmail@mail.com]
|
||||||
|
mail-report[dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/sshd.log
|
||||||
|
maxretry = 5
|
||||||
|
|
||||||
|
# Here we use TCP-Wrappers instead of Netfilter/Iptables.
|
||||||
|
|
||||||
|
[ssh-tcpwrapper]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = sshd
|
||||||
|
action = hostsdeny
|
||||||
|
mail-whois[name=SSH, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/sshd.log
|
||||||
|
|
||||||
|
# This jail demonstrates the use of wildcards in "logpath".
|
||||||
|
# Moreover, it is possible to give other files on a new line.
|
||||||
|
|
||||||
|
[apache-tcpwrapper]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = apache-auth
|
||||||
|
action = hostsdeny
|
||||||
|
logpath = /var/log/apache*/*access.log
|
||||||
|
/home/www/myhomepage/access.log
|
||||||
|
maxretry = 6
|
||||||
|
|
||||||
|
# The hosts.deny path can be defined with the "file" argument if it is
|
||||||
|
# not in /etc.
|
||||||
|
|
||||||
|
[postfix-tcpwrapper]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = postfix
|
||||||
|
action = hostsdeny[file=/not/a/standard/path/hosts.deny]
|
||||||
|
mail[name=Postfix, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/postfix.log
|
||||||
|
bantime = 300
|
||||||
|
|
||||||
|
# Do not ban anybody. Just report information about the remote host.
|
||||||
|
# A notification is sent at most every 600 seconds (bantime).
|
||||||
|
|
||||||
|
[vsftpd-notification]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = vsftpd
|
||||||
|
action = mail-whois[name=VSFTPD, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/vsftpd.log
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 1800
|
||||||
|
|
||||||
|
# Use shorewall instead of iptables.
|
||||||
|
|
||||||
|
[apache-shorewall]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = apache-noscript
|
||||||
|
action = shorewall
|
||||||
|
mail[name=Postfix, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/apache2/error_log
|
||||||
|
|
||||||
|
# This jail uses ipfw, the standard firewall on FreeBSD. The "ignoreip"
|
||||||
|
# option is overridden in this jail.
|
||||||
|
|
||||||
|
[ssh-ipfw]
|
||||||
|
|
||||||
|
enabled = false
|
||||||
|
filter = sshd
|
||||||
|
action = ipfw[localhost=192.168.0.1]
|
||||||
|
mail-whois[name=SSH, dest=yourmail@mail.com]
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
ignoreip = 168.192.0.1
|
|
@ -1,78 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# fail2ban
|
|
||||||
#
|
|
||||||
# chkconfig: 345 91 9
|
|
||||||
# description: if many unsuccessfull login attempts from some ip address \
|
|
||||||
# during a short period happen, this address is banned \
|
|
||||||
# by the firewall
|
|
||||||
#
|
|
||||||
# Author: Andrey G. Grozin
|
|
||||||
#
|
|
||||||
# $Revision: 1.2 $
|
|
||||||
|
|
||||||
# Source function library.
|
|
||||||
. /etc/init.d/functions
|
|
||||||
|
|
||||||
# Get config.
|
|
||||||
. /etc/sysconfig/network
|
|
||||||
|
|
||||||
# Check that networking is up.
|
|
||||||
[ "${NETWORKING}" = "no" ] && exit 0
|
|
||||||
[ -f /etc/fail2ban.conf ] || exit 0
|
|
||||||
|
|
||||||
FAIL2BAN="/usr/bin/fail2ban"
|
|
||||||
PIDFILE="/var/run/fail2ban.pid"
|
|
||||||
|
|
||||||
RETVAL=0
|
|
||||||
|
|
||||||
start() {
|
|
||||||
echo -n $"Starting fail2ban: "
|
|
||||||
"${FAIL2BAN}" -b > /dev/null
|
|
||||||
RETVAL=$?
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
if [ -f "${PIDFILE}" ]; then
|
|
||||||
echo -n $"Stopping fail2ban: "
|
|
||||||
"${FAIL2BAN}" -k > /dev/null
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
restart() {
|
|
||||||
stop
|
|
||||||
start
|
|
||||||
}
|
|
||||||
|
|
||||||
# See how we were called.
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
start
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
stop
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status fail2ban
|
|
||||||
RETVAL=$?
|
|
||||||
;;
|
|
||||||
reload)
|
|
||||||
restart
|
|
||||||
;;
|
|
||||||
restart)
|
|
||||||
restart
|
|
||||||
;;
|
|
||||||
condrestart)
|
|
||||||
if [ -f "${PIDFILE}" ]; then
|
|
||||||
restart
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo $"Usage: $0 {start|stop|status|restart|condrestart}"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit $RETVAL
|
|
|
@ -1,85 +0,0 @@
|
||||||
# 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: 1.6 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.6 $"
|
|
||||||
__date__ = "$Date: 2005/11/20 17:07:47 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from ConfigParser import *
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
class ConfigReader:
|
|
||||||
""" This class allow the handling of the configuration options.
|
|
||||||
The DEFAULT section contains the global information about
|
|
||||||
Fail2Ban. Each other section is for a different log file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, confPath):
|
|
||||||
self.confPath = confPath
|
|
||||||
self.configParser = SafeConfigParser()
|
|
||||||
|
|
||||||
def openConf(self):
|
|
||||||
""" Opens the configuration file.
|
|
||||||
"""
|
|
||||||
self.configParser.read(self.confPath)
|
|
||||||
|
|
||||||
def getSections(self):
|
|
||||||
""" Returns all the sections present in the configuration
|
|
||||||
file except the DEFAULT and MAIL sections.
|
|
||||||
"""
|
|
||||||
sections = self.configParser.sections()
|
|
||||||
sections.remove("MAIL")
|
|
||||||
logSys.debug("Found sections: " + `sections`)
|
|
||||||
return sections
|
|
||||||
|
|
||||||
# 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 getLogOptions(self, sec, options):
|
|
||||||
""" Gets all the options of a given section. The options
|
|
||||||
are defined in the optionValues list.
|
|
||||||
"""
|
|
||||||
values = dict()
|
|
||||||
for option in options:
|
|
||||||
try:
|
|
||||||
if option[0] == "bool":
|
|
||||||
v = self.configParser.getboolean(sec, option[1])
|
|
||||||
elif option[0] == "int":
|
|
||||||
v = self.configParser.getint(sec, option[1])
|
|
||||||
else:
|
|
||||||
v = self.configParser.get(sec, option[1])
|
|
||||||
|
|
||||||
values[option[1]] = v
|
|
||||||
except NoOptionError:
|
|
||||||
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
|
|
||||||
|
|
75
fail2ban
75
fail2ban
|
@ -1,75 +0,0 @@
|
||||||
#!/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: 1.7 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.7 $"
|
|
||||||
__date__ = "$Date: 2005/12/27 15:09:50 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import sys, traceback, logging, locale
|
|
||||||
|
|
||||||
# Set the locale with the user's default setting
|
|
||||||
try:
|
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
|
||||||
except Exception:
|
|
||||||
print "Unable to set locale to " + `locale.getdefaultlocale()`
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
import fail2ban
|
|
||||||
from utils.pidlock import PIDLock
|
|
||||||
|
|
||||||
# Get the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
# Get PID lock file instance
|
|
||||||
pidLock = PIDLock()
|
|
||||||
|
|
||||||
# Handle all the unhandled exceptions
|
|
||||||
try:
|
|
||||||
# Start the application
|
|
||||||
fail2ban.main()
|
|
||||||
except SystemExit:
|
|
||||||
# We called sys.exit(). Nothing wrong so just pass
|
|
||||||
pass
|
|
||||||
except Exception, e:
|
|
||||||
# Print the exception data
|
|
||||||
(type, value, tb) = sys.exc_info()
|
|
||||||
tbStack = traceback.extract_tb(tb)
|
|
||||||
logSys.error("Fail2Ban got an unhandled exception and died.")
|
|
||||||
logSys.error("Type: " + `type.__name__` + "\n" +
|
|
||||||
"Value: " + `e.args` + "\n" +
|
|
||||||
"TB: " + `tbStack`)
|
|
||||||
# Try to clean up after ourselves
|
|
||||||
# just for extreme caution - wrapping with try
|
|
||||||
try:
|
|
||||||
fail2ban.restoreFwRules()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
# Remove the PID lock file. Should close #1239562
|
|
||||||
pidLock.remove()
|
|
||||||
logging.shutdown()
|
|
|
@ -0,0 +1,341 @@
|
||||||
|
#!/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: 444 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 444 $"
|
||||||
|
__date__ = "$Date: 2006-11-01 23:03:48 +0100 (Wed, 01 Nov 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import sys, string, os, pickle, re, logging
|
||||||
|
import getopt, time, readline, shlex, socket
|
||||||
|
|
||||||
|
# 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
|
||||||
|
from client.beautifier import Beautifier
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.client")
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# @todo This class needs cleanup.
|
||||||
|
|
||||||
|
class Fail2banClient:
|
||||||
|
|
||||||
|
prompt = "fail2ban> "
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__argv = None
|
||||||
|
self.__stream = None
|
||||||
|
self.__configurator = Configurator()
|
||||||
|
self.__conf = dict()
|
||||||
|
self.__conf["conf"] = "/etc/fail2ban"
|
||||||
|
self.__conf["dump"] = False
|
||||||
|
self.__conf["force"] = False
|
||||||
|
self.__conf["verbose"] = 1
|
||||||
|
self.__conf["interactive"] = False
|
||||||
|
self.__conf["socket"] = None
|
||||||
|
|
||||||
|
def dispVersion(self):
|
||||||
|
print "Fail2Ban v" + version
|
||||||
|
print
|
||||||
|
print "Copyright (c) 2004-2006 Cyril Jaquier"
|
||||||
|
print "Copyright of modifications held by their respective authors."
|
||||||
|
print "Licensed under the GNU General Public License v2 (GPL)."
|
||||||
|
print
|
||||||
|
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
|
||||||
|
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
|
||||||
|
|
||||||
|
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 "Options:"
|
||||||
|
print " -c <DIR> configuration directory"
|
||||||
|
print " -s <FILE> socket path"
|
||||||
|
print " -d dump configuration. For debugging"
|
||||||
|
print " -i interactive mode"
|
||||||
|
print " -v increase verbosity"
|
||||||
|
print " -q decrease verbosity"
|
||||||
|
print " -x force execution of the server"
|
||||||
|
print " -h, --help display this help message"
|
||||||
|
print " -V, --version print the version"
|
||||||
|
print
|
||||||
|
print "Command:"
|
||||||
|
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> [BACKEND] create <JAIL> using [BACKEND]"
|
||||||
|
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 "Report bugs to <lostcontrol@users.sourceforge.net>"
|
||||||
|
|
||||||
|
def dispInteractive(self):
|
||||||
|
print "Fail2Ban v" + version + " reads log file that contains password failure report"
|
||||||
|
print "and bans the corresponding IP addresses using firewall rules."
|
||||||
|
print
|
||||||
|
|
||||||
|
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] == "-s":
|
||||||
|
self.__conf["socket"] = 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] == "-i":
|
||||||
|
self.__conf["interactive"] = True
|
||||||
|
elif opt[0] in ["-h", "--help"]:
|
||||||
|
self.dispUsage()
|
||||||
|
sys.exit(0)
|
||||||
|
elif opt[0] in ["-V", "--version"]:
|
||||||
|
self.dispVersion()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def __ping(self):
|
||||||
|
return self.__processCmd([["ping"]], False)
|
||||||
|
|
||||||
|
def __processCmd(self, cmd, showRet = True):
|
||||||
|
beautifier = Beautifier()
|
||||||
|
for c in cmd:
|
||||||
|
beautifier.setInputCmd(c)
|
||||||
|
try:
|
||||||
|
client = CSocket(self.__conf["socket"])
|
||||||
|
ret = client.send(c)
|
||||||
|
if ret[0] == 0:
|
||||||
|
logSys.debug("OK : " + `ret[1]`)
|
||||||
|
if showRet:
|
||||||
|
print beautifier.beautify(ret[1])
|
||||||
|
else:
|
||||||
|
logSys.debug("NOK: " + `ret[1].args`)
|
||||||
|
print beautifier.beautifyError(ret[1])
|
||||||
|
return False
|
||||||
|
except socket.error:
|
||||||
|
if showRet:
|
||||||
|
logSys.error("Unable to contact server. Is it running?")
|
||||||
|
return False
|
||||||
|
except Exception, e:
|
||||||
|
if showRet:
|
||||||
|
logSys.error(e)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# Process a command line.
|
||||||
|
#
|
||||||
|
# Process one command line and exit.
|
||||||
|
# @param cmd the command line
|
||||||
|
|
||||||
|
def __processCommand(self, cmd):
|
||||||
|
if len(cmd) == 1 and cmd[0] == "start":
|
||||||
|
if self.__ping():
|
||||||
|
logSys.error("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:
|
||||||
|
try:
|
||||||
|
# Use the PATH env
|
||||||
|
os.execvp("fail2ban-server", args)
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
# Use the current directory
|
||||||
|
os.execv("fail2ban-server", args)
|
||||||
|
except OSError:
|
||||||
|
print "Could not find fail2ban-server"
|
||||||
|
os.exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
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:s:xdviqV'
|
||||||
|
cmdLongOpts = ['help', 'version']
|
||||||
|
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
|
||||||
|
except getopt.GetoptError:
|
||||||
|
self.dispUsage()
|
||||||
|
return False
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Set the configuration path
|
||||||
|
self.__configurator.setBaseDir(self.__conf["conf"])
|
||||||
|
|
||||||
|
# Set socket path
|
||||||
|
self.__configurator.readEarly()
|
||||||
|
socket = self.__configurator.getEarlyOptions()
|
||||||
|
if self.__conf["socket"] == None:
|
||||||
|
self.__conf["socket"] = socket["socket"]
|
||||||
|
logSys.info("Using socket file " + self.__conf["socket"])
|
||||||
|
|
||||||
|
if self.__conf["dump"]:
|
||||||
|
self.__readConfig()
|
||||||
|
self.dumpConfig(self.__stream)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Interactive mode
|
||||||
|
if self.__conf["interactive"]:
|
||||||
|
try:
|
||||||
|
ret = True
|
||||||
|
if len(args) > 0:
|
||||||
|
ret = self.__processCommand(args)
|
||||||
|
if ret:
|
||||||
|
readline.parse_and_bind("tab: complete")
|
||||||
|
self.dispInteractive()
|
||||||
|
while True:
|
||||||
|
cmd = raw_input(self.prompt)
|
||||||
|
if cmd == "exit" or cmd == "quit":
|
||||||
|
# Exit
|
||||||
|
return True
|
||||||
|
if not cmd == "":
|
||||||
|
self.__processCommand(shlex.split(cmd))
|
||||||
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
print
|
||||||
|
return True
|
||||||
|
# Single command mode
|
||||||
|
else:
|
||||||
|
if len(args) < 1:
|
||||||
|
self.dispUsage()
|
||||||
|
return False
|
||||||
|
return self.__processCommand(args)
|
||||||
|
|
||||||
|
def __readConfig(self):
|
||||||
|
# Read the configuration
|
||||||
|
self.__configurator.readAll()
|
||||||
|
self.__configurator.getAllOptions()
|
||||||
|
self.__configurator.convertToProtocol()
|
||||||
|
self.__stream = self.__configurator.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)
|
|
@ -0,0 +1,164 @@
|
||||||
|
#!/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: 300 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 300 $"
|
||||||
|
__date__ = "$Date: 2006-08-23 21:53:09 +0200 (Wed, 23 Aug 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import locale, getopt, sys, time, logging, gc
|
||||||
|
|
||||||
|
# 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.filter import Filter
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.regex")
|
||||||
|
|
||||||
|
class Fail2banRegex:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__filter = Filter(None)
|
||||||
|
# Setup logging
|
||||||
|
logging.getLogger("fail2ban").handlers = []
|
||||||
|
self.__hdlr = logging.StreamHandler(sys.stdout)
|
||||||
|
# set a format which is simpler for console use
|
||||||
|
formatter = logging.Formatter("%(message)s")
|
||||||
|
# tell the handler to use this format
|
||||||
|
self.__hdlr.setFormatter(formatter)
|
||||||
|
logging.getLogger("fail2ban").addHandler(self.__hdlr)
|
||||||
|
logging.getLogger("fail2ban").setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
def dispVersion(self):
|
||||||
|
print "Fail2Ban v" + version
|
||||||
|
print
|
||||||
|
print "Copyright (c) 2004-2006 Cyril Jaquier"
|
||||||
|
print "Copyright of modifications held by their respective authors."
|
||||||
|
print "Licensed under the GNU General Public License v2 (GPL)."
|
||||||
|
print
|
||||||
|
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
|
||||||
|
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
|
||||||
|
|
||||||
|
def dispUsage(self):
|
||||||
|
print "Usage: "+sys.argv[0]+" <logline> <failregex>"
|
||||||
|
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 "This tools can test and benchmark your regular expressions for the \"failregex\""
|
||||||
|
print "option."
|
||||||
|
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] in ["-h", "--help"]:
|
||||||
|
self.dispUsage()
|
||||||
|
sys.exit(0)
|
||||||
|
elif opt[0] in ["-V", "--version"]:
|
||||||
|
self.dispVersion()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def setRegex(self, value):
|
||||||
|
print
|
||||||
|
self.__filter.setFailRegex(value)
|
||||||
|
|
||||||
|
def testRegex(self, line):
|
||||||
|
print
|
||||||
|
try:
|
||||||
|
logging.getLogger("fail2ban").setLevel(logging.DEBUG)
|
||||||
|
ret = self.__filter.findFailure(line)
|
||||||
|
print
|
||||||
|
logging.getLogger("fail2ban").setLevel(logging.CRITICAL)
|
||||||
|
except IndexError:
|
||||||
|
print "Sorry, but no <host> found in regex"
|
||||||
|
return False
|
||||||
|
if len(ret) == 0:
|
||||||
|
print "Sorry, no match"
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print "Success, the following data were found:"
|
||||||
|
timeTuple = time.localtime(ret[0][1])
|
||||||
|
print "Date: " + time.strftime("%a %b %d %H:%M:%S %Y", timeTuple)
|
||||||
|
ipList = ""
|
||||||
|
for i in ret:
|
||||||
|
ipList = ipList + " " + i[0]
|
||||||
|
print "IP :" + ipList
|
||||||
|
print
|
||||||
|
print "Date template hits:"
|
||||||
|
for template in self.__filter.dateDetector.getTemplates():
|
||||||
|
print `template.getHits()` + " hit: " + template.getName()
|
||||||
|
print
|
||||||
|
print "Benchmark. Executing 1000..."
|
||||||
|
gc.disable()
|
||||||
|
total = 0
|
||||||
|
maxValue = 0
|
||||||
|
maxPos = 0
|
||||||
|
minValue = 99999999
|
||||||
|
minPos = 0
|
||||||
|
for i in range(1000):
|
||||||
|
start = time.time()
|
||||||
|
ret = self.__filter.findFailure(line)
|
||||||
|
end = time.time()
|
||||||
|
diff = (end - start) * 1000
|
||||||
|
total = total + diff
|
||||||
|
minValue = min(minValue, diff)
|
||||||
|
if minValue == diff:
|
||||||
|
minPos = i
|
||||||
|
maxValue = max(maxValue, diff)
|
||||||
|
if maxValue == diff:
|
||||||
|
maxPos = i
|
||||||
|
gc.enable()
|
||||||
|
print "Performance"
|
||||||
|
print "Avg: " + `total / 1000` + " ms"
|
||||||
|
print "Max: " + `maxValue` + " ms (Run " + `maxPos` + ")"
|
||||||
|
print "Min: " + `minValue` + " ms (Run " + `minPos` + ")"
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
regex = Fail2banRegex()
|
||||||
|
# Reads the command line options.
|
||||||
|
try:
|
||||||
|
cmdOpts = 'hV'
|
||||||
|
cmdLongOpts = ['help', 'version']
|
||||||
|
optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts)
|
||||||
|
except getopt.GetoptError:
|
||||||
|
regex.dispUsage()
|
||||||
|
sys.exit(-1)
|
||||||
|
# Process command line
|
||||||
|
regex.getCmdLineOptions(optList)
|
||||||
|
# We need exactly 3 parameters
|
||||||
|
if len(sys.argv) <> 3:
|
||||||
|
regex.dispUsage()
|
||||||
|
sys.exit(-1)
|
||||||
|
else:
|
||||||
|
regex.setRegex(sys.argv[2])
|
||||||
|
ret = regex.testRegex(sys.argv[1])
|
||||||
|
if ret:
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
sys.exit(-1)
|
|
@ -0,0 +1,133 @@
|
||||||
|
#!/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: 406 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 406 $"
|
||||||
|
__date__ = "$Date: 2006-10-05 00:17:53 +0200 (Thu, 05 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import 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
|
||||||
|
|
||||||
|
##
|
||||||
|
# \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
|
||||||
|
self.__conf["socket"] = "/tmp/fail2ban.sock"
|
||||||
|
|
||||||
|
def dispVersion(self):
|
||||||
|
print "Fail2Ban v" + version
|
||||||
|
print
|
||||||
|
print "Copyright (c) 2004-2006 Cyril Jaquier"
|
||||||
|
print "Copyright of modifications held by their respective authors."
|
||||||
|
print "Licensed under the GNU General Public License v2 (GPL)."
|
||||||
|
print
|
||||||
|
print "Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>."
|
||||||
|
print "Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>."
|
||||||
|
|
||||||
|
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 "Only use this command for debugging purpose. Start the server with"
|
||||||
|
print "fail2ban-client instead."
|
||||||
|
print
|
||||||
|
print "Options:"
|
||||||
|
print " -b start in background"
|
||||||
|
print " -f start in foreground"
|
||||||
|
print " -s <FILE> socket path"
|
||||||
|
print " -x force execution of the server"
|
||||||
|
print " -h, --help display this help message"
|
||||||
|
print " -V, --version print the version"
|
||||||
|
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] == "-b":
|
||||||
|
self.__conf["background"] = True
|
||||||
|
if opt[0] == "-f":
|
||||||
|
self.__conf["background"] = False
|
||||||
|
if opt[0] == "-s":
|
||||||
|
self.__conf["socket"] = opt[1]
|
||||||
|
if opt[0] == "-x":
|
||||||
|
self.__conf["force"] = True
|
||||||
|
if opt[0] in ["-h", "--help"]:
|
||||||
|
self.dispUsage()
|
||||||
|
sys.exit(0)
|
||||||
|
if opt[0] in ["-V", "--version"]:
|
||||||
|
self.dispVersion()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def start(self, argv):
|
||||||
|
# Command line options
|
||||||
|
self.__argv = argv
|
||||||
|
|
||||||
|
# Reads the command line options.
|
||||||
|
try:
|
||||||
|
cmdOpts = 'bfs:xhV'
|
||||||
|
cmdLongOpts = ['help', 'version']
|
||||||
|
optList, args = getopt.getopt(self.__argv[1:], cmdOpts, cmdLongOpts)
|
||||||
|
except getopt.GetoptError:
|
||||||
|
self.dispUsage()
|
||||||
|
|
||||||
|
self.__getCmdLineOptions(optList)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.__server = Server(self.__conf["background"])
|
||||||
|
self.__server.start(self.__conf["socket"], self.__conf["force"])
|
||||||
|
return True
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
self.__server.quit()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
server = Fail2banServer()
|
||||||
|
if server.start(sys.argv):
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
sys.exit(-1)
|
|
@ -0,0 +1,79 @@
|
||||||
|
#!/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: 429 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 429 $"
|
||||||
|
__date__ = "$Date: 2006-10-23 22:13:21 +0200 (Mon, 23 Oct 2006) $"
|
||||||
|
__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
|
||||||
|
from testcases import datedetectortestcase
|
||||||
|
from testcases import actiontestcase
|
||||||
|
from server.mytime import MyTime
|
||||||
|
|
||||||
|
# Set the time to a fixed, known value
|
||||||
|
# Sun Aug 14 12:00:00 CEST 2005
|
||||||
|
MyTime.setTime(1124013600)
|
||||||
|
|
||||||
|
# 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))
|
||||||
|
tests.addTest(unittest.makeSuite(actiontestcase.ExecuteAction))
|
||||||
|
# FailManager
|
||||||
|
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
||||||
|
# BanManager
|
||||||
|
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
|
||||||
|
# ClientReader
|
||||||
|
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
|
||||||
|
# DateDetector
|
||||||
|
tests.addTest(unittest.makeSuite(datedetectortestcase.DateDetectorTest))
|
||||||
|
|
||||||
|
# Tests runner
|
||||||
|
testRunner = unittest.TextTestRunner()
|
||||||
|
testRunner.run(tests)
|
542
fail2ban.py
542
fail2ban.py
|
@ -1,542 +0,0 @@
|
||||||
# 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
|
|
||||||
# Modified by: Yaroslav Halchenko (SYSLOG, findtime)
|
|
||||||
#
|
|
||||||
# $Revision: 1.24 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.24 $"
|
|
||||||
__date__ = "$Date: 2006/01/22 11:10:29 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import time, sys, getopt, os, string, signal, logging, logging.handlers, copy
|
|
||||||
from ConfigParser import *
|
|
||||||
|
|
||||||
from version import version
|
|
||||||
from firewall.firewall import Firewall
|
|
||||||
from logreader.logreader import LogReader
|
|
||||||
from confreader.configreader import ConfigReader
|
|
||||||
from utils.mail import Mail
|
|
||||||
from utils.pidlock import PIDLock
|
|
||||||
from utils.dns import *
|
|
||||||
from utils.process import *
|
|
||||||
|
|
||||||
# Get the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
# Get PID lock file instance
|
|
||||||
pidLock = PIDLock()
|
|
||||||
|
|
||||||
# Global variables
|
|
||||||
logFwList = list()
|
|
||||||
conf = dict()
|
|
||||||
|
|
||||||
def dispUsage():
|
|
||||||
""" Prints Fail2Ban command line options and exits
|
|
||||||
"""
|
|
||||||
print "Usage: "+sys.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 " -c <FILE> read configuration file FILE"
|
|
||||||
print " -p <FILE> create PID lock in FILE"
|
|
||||||
print " -h display this help message"
|
|
||||||
print " -i <IP(s)> IP(s) to ignore"
|
|
||||||
print " -k kill a currently running instance"
|
|
||||||
print " -r <VALUE> allow a max of VALUE password failure [maxfailures]"
|
|
||||||
print " -t <TIME> ban IP for TIME seconds [bantime]"
|
|
||||||
print " -f <TIME> lifetime in seconds of failed entry [findtime]"
|
|
||||||
print " -v verbose. Use twice for greater effect"
|
|
||||||
print " -V print software version"
|
|
||||||
print
|
|
||||||
print "Report bugs to <lostcontrol@users.sourceforge.net>"
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def dispVersion():
|
|
||||||
""" Prints Fail2Ban version and exits
|
|
||||||
"""
|
|
||||||
print sys.argv[0]+" "+version
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def checkForRoot():
|
|
||||||
""" Check for root user.
|
|
||||||
"""
|
|
||||||
uid = `os.getuid()`
|
|
||||||
if uid == '0':
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
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`)
|
|
||||||
killApp()
|
|
||||||
|
|
||||||
def setFwMustCheck(value):
|
|
||||||
""" Set the mustCheck value of the firewalls (True/False)
|
|
||||||
"""
|
|
||||||
for element in logFwList:
|
|
||||||
element[2].setMustCheck(value)
|
|
||||||
|
|
||||||
def initializeFwRules():
|
|
||||||
""" Initializes firewalls by running cmdstart and then
|
|
||||||
fwstart for each section
|
|
||||||
"""
|
|
||||||
# Execute global start command
|
|
||||||
executeCmd(conf["cmdstart"], conf["debug"])
|
|
||||||
# Execute start command of each section
|
|
||||||
for element in logFwList:
|
|
||||||
element[2].initialize(conf["debug"])
|
|
||||||
|
|
||||||
def reBan():
|
|
||||||
""" For each section asks the Firewall to reban known IPs
|
|
||||||
"""
|
|
||||||
for element in logFwList:
|
|
||||||
element[2].reBan(conf["debug"])
|
|
||||||
|
|
||||||
def restoreFwRules():
|
|
||||||
""" Flush the ban list
|
|
||||||
"""
|
|
||||||
logSys.warn("Restoring firewall rules...")
|
|
||||||
try:
|
|
||||||
for element in logFwList:
|
|
||||||
# Execute end command of each section
|
|
||||||
element[2].restore(conf["debug"])
|
|
||||||
# Execute global end command
|
|
||||||
executeCmd(conf["cmdend"], conf["debug"])
|
|
||||||
except ExternalError:
|
|
||||||
# nothing bad really - we can survive :-)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def killApp():
|
|
||||||
""" Flush the ban list, remove the PID lock file and exit
|
|
||||||
nicely.
|
|
||||||
"""
|
|
||||||
# Restore Fw rules
|
|
||||||
restoreFwRules()
|
|
||||||
# Remove the PID lock
|
|
||||||
pidLock.remove()
|
|
||||||
logSys.info("Exiting...")
|
|
||||||
logging.shutdown()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def getCmdLineOptions(optList):
|
|
||||||
""" Gets the command line options
|
|
||||||
"""
|
|
||||||
for opt in optList:
|
|
||||||
if opt[0] == "-v":
|
|
||||||
conf["verbose"] = conf["verbose"] + 1
|
|
||||||
if opt[0] == "-b":
|
|
||||||
conf["background"] = True
|
|
||||||
if opt[0] == "-d":
|
|
||||||
conf["debug"] = True
|
|
||||||
if opt[0] == "-t":
|
|
||||||
try:
|
|
||||||
conf["bantime"] = int(opt[1])
|
|
||||||
except ValueError:
|
|
||||||
logSys.warn("banTime must be an integer")
|
|
||||||
logSys.warn("Using default value")
|
|
||||||
if opt[0] == "-f":
|
|
||||||
try:
|
|
||||||
conf["findtime"] = int(opt[1])
|
|
||||||
except ValueError:
|
|
||||||
logSys.warn("findTime must be an integer")
|
|
||||||
logSys.warn("Using default value")
|
|
||||||
if opt[0] == "-i":
|
|
||||||
conf["ignoreip"] = opt[1]
|
|
||||||
if opt[0] == "-r":
|
|
||||||
conf["maxfailures"] = int(opt[1])
|
|
||||||
if opt[0] == "-p":
|
|
||||||
conf["pidlock"] = opt[1]
|
|
||||||
if opt[0] == "-k":
|
|
||||||
conf["kill"] = True
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" Fail2Ban main function
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Add the default logging handler
|
|
||||||
stdout = logging.StreamHandler(sys.stdout)
|
|
||||||
logSys.addHandler(stdout)
|
|
||||||
|
|
||||||
# Default formatter
|
|
||||||
formatterstring='%(levelname)s: %(message)s'
|
|
||||||
formatter = logging.Formatter('%(asctime)s ' + formatterstring)
|
|
||||||
stdout.setFormatter(formatter)
|
|
||||||
|
|
||||||
conf["kill"] = False
|
|
||||||
conf["debug"] = False
|
|
||||||
conf["verbose"] = 0
|
|
||||||
conf["conffile"] = "/etc/fail2ban.conf"
|
|
||||||
|
|
||||||
# Reads the command line options.
|
|
||||||
try:
|
|
||||||
cmdOpts = 'hvVbdkc:t:i:r:p:'
|
|
||||||
cmdLongOpts = ['help','version']
|
|
||||||
optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts)
|
|
||||||
except getopt.GetoptError:
|
|
||||||
dispUsage()
|
|
||||||
|
|
||||||
# Pre-parsing of command line options for the -c option
|
|
||||||
for opt in optList:
|
|
||||||
if opt[0] == "-c":
|
|
||||||
conf["conffile"] = opt[1]
|
|
||||||
if opt[0] in ["-h", "--help"]:
|
|
||||||
dispUsage()
|
|
||||||
if opt[0] in ["-V", "--version"]:
|
|
||||||
dispVersion()
|
|
||||||
|
|
||||||
# Reads the config file and create a LogReader instance for
|
|
||||||
# each log file to check.
|
|
||||||
confReader = ConfigReader(conf["conffile"])
|
|
||||||
confReader.openConf()
|
|
||||||
|
|
||||||
# Options
|
|
||||||
optionValues = (["bool", "background", False],
|
|
||||||
["str", "logtargets", "/var/log/fail2ban.log"],
|
|
||||||
["str", "syslog-target", "/dev/log"],
|
|
||||||
["int", "syslog-facility", 1],
|
|
||||||
["str", "pidlock", "/var/run/fail2ban.pid"],
|
|
||||||
["int", "maxfailures", 5],
|
|
||||||
["int", "bantime", 600],
|
|
||||||
["int", "findtime", 600],
|
|
||||||
["str", "ignoreip", ""],
|
|
||||||
["int", "polltime", 1],
|
|
||||||
["str", "cmdstart", ""],
|
|
||||||
["str", "cmdend", ""],
|
|
||||||
["int", "reinittime", 100],
|
|
||||||
["int", "maxreinits", 100])
|
|
||||||
|
|
||||||
# Gets global configuration options
|
|
||||||
conf.update(confReader.getLogOptions("DEFAULT", optionValues))
|
|
||||||
|
|
||||||
# Gets command line options
|
|
||||||
getCmdLineOptions(optList)
|
|
||||||
|
|
||||||
# PID lock
|
|
||||||
pidLock.setPath(conf["pidlock"])
|
|
||||||
|
|
||||||
# Now we can kill properly a running instance if needed
|
|
||||||
if conf["kill"]:
|
|
||||||
pid = pidLock.exists()
|
|
||||||
if pid:
|
|
||||||
killPID(int(pid))
|
|
||||||
logSys.warn("Killed Fail2Ban with PID "+pid)
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
logSys.error("No running Fail2Ban found")
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# Start Fail2Ban in daemon mode
|
|
||||||
if conf["background"]:
|
|
||||||
retCode = createDaemon()
|
|
||||||
signal.signal(signal.SIGTERM, sigTERMhandler)
|
|
||||||
if not retCode:
|
|
||||||
logSys.error("Unable to start daemon")
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# Process some options
|
|
||||||
# First setup Log targets
|
|
||||||
# Bug fix for #1234699
|
|
||||||
os.umask(0077)
|
|
||||||
for target in conf["logtargets"].split():
|
|
||||||
# target formatter
|
|
||||||
# By default global formatter is taken. Is different for SYSLOG
|
|
||||||
tformatter = formatter
|
|
||||||
if target == "STDERR":
|
|
||||||
hdlr = logging.StreamHandler(sys.stderr)
|
|
||||||
elif target == "SYSLOG":
|
|
||||||
# SYSLOG target can be either
|
|
||||||
# a socket (file, so it starts with /)
|
|
||||||
# or hostname
|
|
||||||
# or hostname:port
|
|
||||||
syslogtargets = re.findall("(/[\w/]*)|([^/ ][^: ]*)(:(\d+)){,1}",
|
|
||||||
conf["syslog-target"])
|
|
||||||
# we are waiting for a single match
|
|
||||||
syslogtargets = syslogtargets[0]
|
|
||||||
|
|
||||||
# assign facility if it was defined
|
|
||||||
if conf["syslog-facility"] < 0:
|
|
||||||
facility = handlers.SysLogHandler.LOG_USER
|
|
||||||
else:
|
|
||||||
facility = conf["syslog-facility"]
|
|
||||||
|
|
||||||
if len(syslogtargets) == 0: # everything default
|
|
||||||
hdlr = logging.handlers.SysLogHandler()
|
|
||||||
else:
|
|
||||||
if not ( syslogtargets[0] == "" ): # got socket
|
|
||||||
syslogtarget = syslogtargets[0]
|
|
||||||
else: # got hostname and maybe a port
|
|
||||||
if syslogtargets[3] == "": # no port specified
|
|
||||||
port = 514
|
|
||||||
else:
|
|
||||||
port = int(syslogtargets[3])
|
|
||||||
syslogtarget = (syslogtargets[1], port)
|
|
||||||
hdlr = logging.handlers.SysLogHandler(syslogtarget, facility)
|
|
||||||
tformatter = logging.Formatter("%(asctime)s %(name)s " +
|
|
||||||
formatterstring, "%b %e %T");
|
|
||||||
else:
|
|
||||||
# Target should be a file
|
|
||||||
try:
|
|
||||||
open(target, "a")
|
|
||||||
hdlr = logging.FileHandler(target)
|
|
||||||
except IOError:
|
|
||||||
logSys.error("Unable to log to " + target)
|
|
||||||
continue
|
|
||||||
# Set formatter and add handler to logger
|
|
||||||
hdlr.setFormatter(tformatter)
|
|
||||||
logSys.addHandler(hdlr)
|
|
||||||
|
|
||||||
# Verbose level
|
|
||||||
if conf["verbose"]:
|
|
||||||
logSys.warn("Verbose level is "+`conf["verbose"]`)
|
|
||||||
if conf["verbose"] == 1:
|
|
||||||
logSys.setLevel(logging.INFO)
|
|
||||||
elif conf["verbose"] > 1:
|
|
||||||
logSys.setLevel(logging.DEBUG)
|
|
||||||
if conf["verbose"] > 2:
|
|
||||||
formatterstring = ('%(levelname)s: [%(filename)s (%(lineno)d)] ' +
|
|
||||||
'%(message)s')
|
|
||||||
formatter = logging.Formatter("%(asctime)s " + formatterstring)
|
|
||||||
stdout.setFormatter(formatter)
|
|
||||||
|
|
||||||
# Debug mode. Should only be used by developers
|
|
||||||
if conf["debug"]:
|
|
||||||
logSys.warn("DEBUG MODE: FIREWALL COMMANDS ARE _NOT_ EXECUTED BUT " +
|
|
||||||
"ONLY DISPLAYED IN THE LOG MESSAGES")
|
|
||||||
|
|
||||||
# Ignores IP list
|
|
||||||
ignoreIPList = conf["ignoreip"].split(' ')
|
|
||||||
|
|
||||||
# Checks for root user. This is necessary because log files
|
|
||||||
# are owned by root and firewall needs root access.
|
|
||||||
if not checkForRoot():
|
|
||||||
logSys.error("You must be root")
|
|
||||||
if not conf["debug"]:
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# Checks that no instance of Fail2Ban is currently running.
|
|
||||||
pid = pidLock.exists()
|
|
||||||
if pid:
|
|
||||||
logSys.error("Fail2Ban already running with PID "+pid)
|
|
||||||
sys.exit(-1)
|
|
||||||
else:
|
|
||||||
ret = pidLock.create()
|
|
||||||
if not ret:
|
|
||||||
# Unable to create PID lock. Exit
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
logSys.debug("ConfFile is " + conf["conffile"])
|
|
||||||
logSys.debug("BanTime is " + `conf["bantime"]`)
|
|
||||||
logSys.debug("FindTime is " + `conf["findtime"]`)
|
|
||||||
logSys.debug("MaxFailure is " + `conf["maxfailures"]`)
|
|
||||||
|
|
||||||
# Options
|
|
||||||
optionValues = (["bool", "enabled", False],
|
|
||||||
["str", "host", "localhost"],
|
|
||||||
["int", "port", "25"],
|
|
||||||
["str", "from", "root"],
|
|
||||||
["str", "to", "root"],
|
|
||||||
["str", "user", ''],
|
|
||||||
["str", "password", ''],
|
|
||||||
["bool", "localtime", False],
|
|
||||||
["str", "subject", "[Fail2Ban] Banned <ip>"],
|
|
||||||
["str", "message", "Fail2Ban notification"])
|
|
||||||
|
|
||||||
# Gets global configuration options
|
|
||||||
mailConf = confReader.getLogOptions("MAIL", optionValues)
|
|
||||||
|
|
||||||
# Create mailer if enabled
|
|
||||||
if mailConf["enabled"]:
|
|
||||||
logSys.debug("Mail enabled")
|
|
||||||
mail = Mail(mailConf["host"], mailConf["port"])
|
|
||||||
mail.setFromAddr(mailConf["from"])
|
|
||||||
mail.setUser(mailConf["user"])
|
|
||||||
mail.setPassword(mailConf["password"])
|
|
||||||
mail.setToAddr(mailConf["to"])
|
|
||||||
mail.setLocalTimeFlag(mailConf["localtime"])
|
|
||||||
logSys.debug("to: " + mailConf["to"] + " from: " + mailConf["from"])
|
|
||||||
|
|
||||||
# Options
|
|
||||||
optionValues = (["bool", "enabled", False],
|
|
||||||
["str", "logfile", "/dev/null"],
|
|
||||||
["int", "maxfailures", conf["maxfailures"]],
|
|
||||||
["int", "bantime", conf["bantime"]],
|
|
||||||
["int", "findtime", conf["findtime"]],
|
|
||||||
["str", "timeregex", ""],
|
|
||||||
["str", "timepattern", ""],
|
|
||||||
["str", "failregex", ""],
|
|
||||||
["str", "fwstart", ""],
|
|
||||||
["str", "fwend", ""],
|
|
||||||
["str", "fwban", ""],
|
|
||||||
["str", "fwunban", ""],
|
|
||||||
["str", "fwcheck", ""])
|
|
||||||
|
|
||||||
logSys.info("Fail2Ban v" + version + " is running")
|
|
||||||
|
|
||||||
# Gets the options of each sections
|
|
||||||
for t in confReader.getSections():
|
|
||||||
l = confReader.getLogOptions(t, optionValues)
|
|
||||||
if l["enabled"]:
|
|
||||||
# Creates a logreader object
|
|
||||||
lObj = LogReader(l["logfile"], l["timeregex"], l["timepattern"],
|
|
||||||
l["failregex"], l["maxfailures"], l["findtime"])
|
|
||||||
# Creates a firewall object
|
|
||||||
fObj = Firewall(l["fwstart"], l["fwend"], l["fwban"], l["fwunban"],
|
|
||||||
l["fwcheck"], l["bantime"])
|
|
||||||
# "Name" the firewall
|
|
||||||
fObj.setSection(t)
|
|
||||||
# Links them into a list. I'm not really happy
|
|
||||||
# with this :/
|
|
||||||
logFwList.append([t, lObj, fObj, dict(), l])
|
|
||||||
|
|
||||||
# We add 127.0.0.1 to the ignore list has we do not want
|
|
||||||
# to be ban ourself.
|
|
||||||
for element in logFwList:
|
|
||||||
element[1].addIgnoreIP("127.0.0.1")
|
|
||||||
while len(ignoreIPList) > 0:
|
|
||||||
ip = ignoreIPList.pop()
|
|
||||||
# Bug fix for #1239557
|
|
||||||
if isValidIP(ip):
|
|
||||||
for element in logFwList:
|
|
||||||
element[1].addIgnoreIP(ip)
|
|
||||||
else:
|
|
||||||
logSys.warn(ip + " is not a valid IP address")
|
|
||||||
|
|
||||||
# Startup loop -- necessary to avoid crash if it takes time for iptables
|
|
||||||
# to startup. To avoid introduction of new config options, reusing
|
|
||||||
# maxreinits and polltime.
|
|
||||||
reinits = 0
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
initializeFwRules()
|
|
||||||
break
|
|
||||||
except ExternalError, e:
|
|
||||||
reinits += 1
|
|
||||||
logSys.warn(e)
|
|
||||||
if conf["maxreinits"] < 0 or (reinits < conf["maxreinits"]):
|
|
||||||
logSys.warn("#%d attempt to initialize the firewalls" % reinits)
|
|
||||||
else:
|
|
||||||
logSys.error("Exiting: Too many attempts to initialize the " +
|
|
||||||
"firewall")
|
|
||||||
killApp()
|
|
||||||
time.sleep(conf["polltime"])
|
|
||||||
|
|
||||||
# try to reinit once if it fails immediately
|
|
||||||
lastReinitTime = time.time() - conf["reinittime"] - 1
|
|
||||||
reinits = 0
|
|
||||||
# Main loop
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
sys.stdout.flush()
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
# Checks if some IP have to be remove from ban
|
|
||||||
# list.
|
|
||||||
for element in logFwList:
|
|
||||||
element[2].checkForUnBan(conf["debug"])
|
|
||||||
|
|
||||||
# If the log file has not been modified since the
|
|
||||||
# last time, we sleep for 1 second. This is active
|
|
||||||
# polling so not very effective.
|
|
||||||
modList = list()
|
|
||||||
for element in logFwList:
|
|
||||||
if element[1].isModified():
|
|
||||||
modList.append(element)
|
|
||||||
|
|
||||||
if len(modList) == 0:
|
|
||||||
time.sleep(conf["polltime"])
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Gets the failure list from the log file. For a given IP,
|
|
||||||
# takes only the service which has the most password failures.
|
|
||||||
for element in modList:
|
|
||||||
e = element[1].getFailures()
|
|
||||||
for key in e.iterkeys():
|
|
||||||
if element[3].has_key(key):
|
|
||||||
element[3][key] = (element[3][key][0] + e[key][0],
|
|
||||||
e[key][1])
|
|
||||||
else:
|
|
||||||
element[3][key] = (e[key][0], e[key][1])
|
|
||||||
|
|
||||||
# Remove the oldest failure attempts from the global list.
|
|
||||||
# We iterate the failure list and ban IP that make
|
|
||||||
# *retryAllowed* login failures.
|
|
||||||
unixTime = time.time()
|
|
||||||
for element in logFwList:
|
|
||||||
fails = element[3].copy()
|
|
||||||
findTime = element[1].getFindTime()
|
|
||||||
for attempt in fails:
|
|
||||||
failTime = fails[attempt][1]
|
|
||||||
if failTime < unixTime - findTime:
|
|
||||||
del element[3][attempt]
|
|
||||||
elif fails[attempt][0] >= element[1].getMaxRetry():
|
|
||||||
aInfo = {"section": element[0],
|
|
||||||
"ip": attempt,
|
|
||||||
"failures": element[3][attempt][0],
|
|
||||||
"failtime": failTime}
|
|
||||||
logSys.info(element[0] + ": " + aInfo["ip"] +
|
|
||||||
" has " + `aInfo["failures"]` +
|
|
||||||
" login failure(s). Banned.")
|
|
||||||
element[2].addBanIP(aInfo, conf["debug"])
|
|
||||||
# Send a mail notification
|
|
||||||
if 'mail' in locals():
|
|
||||||
mail.sendmail(mailConf["subject"],
|
|
||||||
mailConf["message"], aInfo)
|
|
||||||
del element[3][attempt]
|
|
||||||
except ExternalError, e:
|
|
||||||
# Something wrong while dealing with Iptables.
|
|
||||||
# May be chain got removed?
|
|
||||||
reinits += 1
|
|
||||||
logSys.error(e)
|
|
||||||
if ((unixTime - lastReinitTime > conf["reinittime"]) and
|
|
||||||
((conf["maxreinits"] < 0) or (reinits < conf["maxreinits"]))):
|
|
||||||
logSys.warn("#%d reinitialization of firewalls"%reinits)
|
|
||||||
lastReinitTime = unixTime
|
|
||||||
else:
|
|
||||||
logSys.error("Exiting: reinits follow too often, or too many " +
|
|
||||||
"reinit attempts")
|
|
||||||
killApp()
|
|
||||||
# We already failed runCheck so disable it until
|
|
||||||
# restoring a safe state
|
|
||||||
setFwMustCheck(False)
|
|
||||||
# save firewalls to keep a list of IPs for rebanning
|
|
||||||
logFwListCopy = copy.deepcopy(logFwList)
|
|
||||||
try:
|
|
||||||
# restore as much as possible
|
|
||||||
restoreFwRules()
|
|
||||||
# reinitialize all the chains
|
|
||||||
initializeFwRules()
|
|
||||||
# restore the lists of baned IPs
|
|
||||||
logFwList.__init__(logFwListCopy)
|
|
||||||
# reBan known IPs
|
|
||||||
reBan()
|
|
||||||
# Now we can enable the runCheck test again
|
|
||||||
setFwMustCheck(True)
|
|
||||||
except ExternalError:
|
|
||||||
raise ExternalError("Big Oops happened: situation is out of " +
|
|
||||||
"control. Something is wrong with your " +
|
|
||||||
"setup. Please check your settings")
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
# When the user press <ctrl>+<c> we exit nicely.
|
|
||||||
killApp()
|
|
|
@ -19,9 +19,9 @@
|
||||||
#
|
#
|
||||||
# $Revision: 1.2 $
|
# $Revision: 1.2 $
|
||||||
|
|
||||||
opts="start stop restart showlog"
|
opts="start stop restart reload showlog"
|
||||||
|
|
||||||
FAIL2BAN="/usr/bin/fail2ban"
|
FAIL2BAN="/usr/bin/fail2ban-client"
|
||||||
|
|
||||||
depend() {
|
depend() {
|
||||||
need net
|
need net
|
||||||
|
@ -31,18 +31,20 @@ depend() {
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
ebegin "Starting fail2ban"
|
ebegin "Starting fail2ban"
|
||||||
${FAIL2BAN} -b ${FAIL2BAN_OPTS} > /dev/null
|
${FAIL2BAN} start &> /dev/null
|
||||||
eend $? "Failed to start fail2ban"
|
eend $? "Failed to start fail2ban"
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
ebegin "Stopping fail2ban"
|
ebegin "Stopping fail2ban"
|
||||||
${FAIL2BAN} -k > /dev/null
|
${FAIL2BAN} stop &> /dev/null
|
||||||
eend $? "Failed to stop fail2ban"
|
eend $? "Failed to stop fail2ban"
|
||||||
}
|
}
|
||||||
|
|
||||||
zap() {
|
reload() {
|
||||||
rm /var/run/fail2ban.pid
|
ebegin "Reloading fail2ban"
|
||||||
|
${FAIL2BAN} reload > /dev/null
|
||||||
|
eend $? "Failed to reload fail2ban"
|
||||||
}
|
}
|
||||||
|
|
||||||
showlog(){
|
showlog(){
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# chkconfig: 345 92 08
|
||||||
|
# description: Fail2ban daemon
|
||||||
|
# http://fail2ban.sourceforge.net/wiki/index.php/Main_Page
|
||||||
|
# process name: fail2ban-server
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Author: Tyler Owen
|
||||||
|
#
|
||||||
|
|
||||||
|
# Source function library.
|
||||||
|
. /etc/init.d/functions
|
||||||
|
|
||||||
|
# Check that the config file exists
|
||||||
|
[ -f /etc/fail2ban/fail2ban.conf ] || exit 0
|
||||||
|
|
||||||
|
FAIL2BAN="/usr/bin/fail2ban-client"
|
||||||
|
|
||||||
|
RETVAL=0
|
||||||
|
|
||||||
|
getpid() {
|
||||||
|
#pid=`ps -ef | grep fail2ban-|grep -v grep|grep -v bash|awk '{print $2}'`
|
||||||
|
pid=`ps -ef | grep fail2ban-|grep -v grep|awk '{print $2}'`
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
echo -n $"Starting fail2ban: "
|
||||||
|
getpid
|
||||||
|
if [ -z "$pid"]; then
|
||||||
|
$FAIL2BAN start > /dev/null
|
||||||
|
RETVAL=$?
|
||||||
|
fi
|
||||||
|
if [ $RETVAL -eq 0 ]; then
|
||||||
|
touch /var/lock/subsys/fail2ban
|
||||||
|
echo_success
|
||||||
|
else
|
||||||
|
echo_failure
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
echo -n $"Stopping fail2ban: "
|
||||||
|
getpid
|
||||||
|
RETVAL=$?
|
||||||
|
if [ -n "$pid" ]; then
|
||||||
|
$FAIL2BAN stop > /dev/null
|
||||||
|
fi
|
||||||
|
getpid
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
rm -f /var/lock/subsys/fail2ban
|
||||||
|
echo_success
|
||||||
|
else
|
||||||
|
echo_failure
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
# See how we were called.
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
getpid
|
||||||
|
if [ -n "$pid" ]; then
|
||||||
|
echo "Fail2ban (pid $pid) is running..."
|
||||||
|
else
|
||||||
|
RETVAL=1
|
||||||
|
echo "Fail2ban is stopped"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
start
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo $"Usage: $0 {start|stop|status|restart}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $RETVAL
|
|
@ -1,192 +0,0 @@
|
||||||
# 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: 1.10 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.10 $"
|
|
||||||
__date__ = "$Date: 2005/12/16 23:48:52 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import time, os, logging, re
|
|
||||||
|
|
||||||
from utils.process import executeCmd
|
|
||||||
from utils.strings import replaceTag
|
|
||||||
# unfortunately but I have to bring ExternalError in especially for
|
|
||||||
# flushBanList: if one of IPs got flushed manually outside or something, we
|
|
||||||
# might endup with not "full" flush unless we handle exception within the loop
|
|
||||||
from utils.process import ExternalError
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
class Firewall:
|
|
||||||
""" Manages the ban list and executes the command that ban
|
|
||||||
the IP.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, startRule, endRule, banRule, unBanRule, checkRule,
|
|
||||||
banTime):
|
|
||||||
self.banRule = banRule
|
|
||||||
self.unBanRule = unBanRule
|
|
||||||
self.checkRule = checkRule
|
|
||||||
self.startRule = startRule
|
|
||||||
self.endRule = endRule
|
|
||||||
self.banTime = banTime
|
|
||||||
self.banList = dict()
|
|
||||||
self.section = ""
|
|
||||||
self.mustCheck = True
|
|
||||||
|
|
||||||
def setSection(self, section):
|
|
||||||
""" Set optional section name for clarify of logging
|
|
||||||
"""
|
|
||||||
self.section = section
|
|
||||||
|
|
||||||
def getMustCheck(self):
|
|
||||||
""" Return true if the runCheck test is executed
|
|
||||||
"""
|
|
||||||
return self.mustCheck
|
|
||||||
|
|
||||||
def setMustCheck(self, value):
|
|
||||||
""" Enable or disable the execution of runCheck test
|
|
||||||
"""
|
|
||||||
self.mustCheck = value
|
|
||||||
|
|
||||||
def initialize(self, debug):
|
|
||||||
logSys.debug("%s: Initialize firewall rules"%self.section)
|
|
||||||
executeCmd(self.startRule, debug)
|
|
||||||
|
|
||||||
def restore(self, debug):
|
|
||||||
logSys.debug("%s: Restore firewall rules"%self.section)
|
|
||||||
try:
|
|
||||||
self.flushBanList(debug)
|
|
||||||
executeCmd(self.endRule, debug)
|
|
||||||
except ExternalError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def addBanIP(self, aInfo, debug):
|
|
||||||
""" Bans an IP.
|
|
||||||
"""
|
|
||||||
ip = aInfo["ip"]
|
|
||||||
if not self.inBanList(ip):
|
|
||||||
crtTime = time.time()
|
|
||||||
if self.banTime < 0:
|
|
||||||
banMsg = "Ban (permanent)"
|
|
||||||
else:
|
|
||||||
banMsg = "Ban (%d s)"%self.banTime
|
|
||||||
logSys.warn("%s: %s "%(self.section, banMsg) + ip)
|
|
||||||
self.banList[ip] = crtTime
|
|
||||||
aInfo["bantime"] = crtTime
|
|
||||||
self.runCheck(debug)
|
|
||||||
cmd = self.banIP(aInfo)
|
|
||||||
if executeCmd(cmd, debug):
|
|
||||||
raise ExternalError("Firewall: execution of fwban command " +
|
|
||||||
"'%s' failed"%cmd)
|
|
||||||
else:
|
|
||||||
self.runCheck(debug)
|
|
||||||
logSys.error("%s: "%self.section+ip+" already in ban list")
|
|
||||||
|
|
||||||
def delBanIP(self, aInfo, debug):
|
|
||||||
""" Unban an IP.
|
|
||||||
"""
|
|
||||||
ip = aInfo["ip"]
|
|
||||||
if self.inBanList(ip):
|
|
||||||
logSys.warn("%s: Unban "%self.section + ip)
|
|
||||||
del self.banList[ip]
|
|
||||||
self.runCheck(debug)
|
|
||||||
executeCmd(self.unBanIP(aInfo), debug)
|
|
||||||
else:
|
|
||||||
logSys.error("%s: "%self.section+ip+" not in ban list")
|
|
||||||
|
|
||||||
def reBan(self, debug):
|
|
||||||
""" Re-Bans known IPs.
|
|
||||||
TODO: implement "failures" and "failtime"
|
|
||||||
"""
|
|
||||||
for ip in self.banList:
|
|
||||||
aInfo = {"ip": ip,
|
|
||||||
"bantime":self.banList[ip]}
|
|
||||||
logSys.warn("%s: ReBan "%self.section + ip)
|
|
||||||
# next piece is similar to the on in addBanIp
|
|
||||||
# so might be one more function will not hurt
|
|
||||||
self.runCheck(debug)
|
|
||||||
executeCmd(self.banIP(aInfo), debug)
|
|
||||||
|
|
||||||
def inBanList(self, ip):
|
|
||||||
""" Checks if IP is in ban list.
|
|
||||||
"""
|
|
||||||
return self.banList.has_key(ip)
|
|
||||||
|
|
||||||
def runCheck(self, debug):
|
|
||||||
""" Runs fwcheck command and throws an exception if it returns non-0
|
|
||||||
result
|
|
||||||
"""
|
|
||||||
if self.mustCheck:
|
|
||||||
executeCmd(self.checkRule, debug)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def checkForUnBan(self, debug):
|
|
||||||
""" Check for IP to remove from ban list. If banTime is smaller than
|
|
||||||
zero, IP will be never removed.
|
|
||||||
"""
|
|
||||||
if self.banTime < 0:
|
|
||||||
# Permanent banning
|
|
||||||
return
|
|
||||||
banListTemp = self.banList.copy()
|
|
||||||
for element in banListTemp.iteritems():
|
|
||||||
btime = element[1]
|
|
||||||
if btime < time.time()-self.banTime:
|
|
||||||
aInfo = {"ip": element[0],
|
|
||||||
"bantime": btime,
|
|
||||||
"unbantime": time.time()}
|
|
||||||
self.delBanIP(aInfo, debug)
|
|
||||||
|
|
||||||
def flushBanList(self, debug):
|
|
||||||
""" Flushes the ban list and of course the firewall rules.
|
|
||||||
Called when fail2ban exits.
|
|
||||||
"""
|
|
||||||
banListTemp = self.banList.copy()
|
|
||||||
for element in banListTemp.iteritems():
|
|
||||||
aInfo = {"ip": element[0],
|
|
||||||
"bantime": element[1],
|
|
||||||
"unbantime": time.time()}
|
|
||||||
try:
|
|
||||||
self.delBanIP(aInfo, debug)
|
|
||||||
except ExternalError:
|
|
||||||
# we must let it fail here in the loop, or we don't
|
|
||||||
# flush properly
|
|
||||||
pass
|
|
||||||
|
|
||||||
def banIP(self, aInfo):
|
|
||||||
""" Returns query to ban IP.
|
|
||||||
"""
|
|
||||||
query = replaceTag(self.banRule, aInfo)
|
|
||||||
return query
|
|
||||||
|
|
||||||
def unBanIP(self, aInfo):
|
|
||||||
""" Returns query to unban IP.
|
|
||||||
"""
|
|
||||||
query = replaceTag(self.unBanRule, aInfo)
|
|
||||||
return query
|
|
||||||
|
|
||||||
def viewBanList(self):
|
|
||||||
""" Prints the ban list on screen. Usefull for debugging.
|
|
||||||
"""
|
|
||||||
for element in self.banList.iteritems():
|
|
||||||
print element
|
|
|
@ -1,225 +0,0 @@
|
||||||
# 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: 1.16 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.16 $"
|
|
||||||
__date__ = "$Date: 2006/01/03 15:13:04 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import os, sys, time, re, logging
|
|
||||||
|
|
||||||
from utils.dns import *
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
class LogReader:
|
|
||||||
""" Reads a log file and reports information about IP that make password
|
|
||||||
failure, bad user or anything else that is considered as doubtful login
|
|
||||||
attempt.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, logPath, timeregex, timepattern, failregex,
|
|
||||||
maxRetry, findTime):
|
|
||||||
self.logPath = logPath
|
|
||||||
self.maxRetry = maxRetry
|
|
||||||
self.timeregex = timeregex
|
|
||||||
self.timepattern = timepattern
|
|
||||||
self.failregex = failregex
|
|
||||||
self.findTime = findTime
|
|
||||||
self.ignoreIpList = []
|
|
||||||
self.lastModTime = 0
|
|
||||||
self.lastPos = 0
|
|
||||||
self.lastDate = 0
|
|
||||||
self.logStats = None
|
|
||||||
|
|
||||||
def getMaxRetry(self):
|
|
||||||
""" Gets the maximum number of failures
|
|
||||||
"""
|
|
||||||
return self.maxRetry
|
|
||||||
|
|
||||||
def getFindTime(self):
|
|
||||||
""" Gets the find time.
|
|
||||||
"""
|
|
||||||
return self.findTime
|
|
||||||
|
|
||||||
def addIgnoreIP(self, ip):
|
|
||||||
""" Adds an IP to the ignore list.
|
|
||||||
"""
|
|
||||||
logSys.debug("Add "+ip+" to ignore list")
|
|
||||||
self.ignoreIpList.append(ip)
|
|
||||||
|
|
||||||
def inIgnoreIPList(self, ip):
|
|
||||||
""" Checks if IP is in the ignore list.
|
|
||||||
"""
|
|
||||||
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])
|
|
||||||
a = cidr(s[0], s[1])
|
|
||||||
b = cidr(ip, s[1])
|
|
||||||
if a == b:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def openLogFile(self):
|
|
||||||
""" Opens the log file specified on init.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
fileHandler = open(self.logPath)
|
|
||||||
except OSError:
|
|
||||||
logSys.error("Unable to open "+self.logPath)
|
|
||||||
|
|
||||||
return fileHandler
|
|
||||||
|
|
||||||
def isModified(self):
|
|
||||||
""" Checks if the log file has been modified using os.stat().
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
||||||
def setFilePos(self, file):
|
|
||||||
""" 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.
|
|
||||||
"""
|
|
||||||
line = file.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)
|
|
||||||
file.seek(self.lastPos)
|
|
||||||
|
|
||||||
def getFailures(self):
|
|
||||||
""" Gets all the failure in the log file which are
|
|
||||||
newer than time.time()-self.findTime.
|
|
||||||
|
|
||||||
Returns a dict with the IP, the number of failure
|
|
||||||
and the latest failure time.
|
|
||||||
"""
|
|
||||||
ipList = dict()
|
|
||||||
logSys.debug(self.logPath)
|
|
||||||
logFile = self.openLogFile()
|
|
||||||
self.setFilePos(logFile)
|
|
||||||
lastLine = None
|
|
||||||
for line in logFile:
|
|
||||||
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)
|
|
||||||
if ipList.has_key(ip):
|
|
||||||
ipList[ip] = (ipList[ip][0]+1, unixTime)
|
|
||||||
else:
|
|
||||||
ipList[ip] = (1, unixTime)
|
|
||||||
self.lastPos = logFile.tell()
|
|
||||||
if lastLine:
|
|
||||||
self.lastDate = self.getTime(lastLine)
|
|
||||||
logFile.close()
|
|
||||||
return ipList
|
|
||||||
|
|
||||||
def findFailure(self, line):
|
|
||||||
""" Finds the failure in line. Uses the failregex pattern
|
|
||||||
to find it and timeregex in order to find the logging
|
|
||||||
time.
|
|
||||||
|
|
||||||
Returns a dict with IP and timestamp.
|
|
||||||
"""
|
|
||||||
failList = list()
|
|
||||||
match = re.search(self.failregex, line)
|
|
||||||
if match:
|
|
||||||
timeMatch = re.search(self.timeregex, match.string)
|
|
||||||
if timeMatch:
|
|
||||||
date = self.getUnixTime(timeMatch.group())
|
|
||||||
ipMatch = textToIp(match.string)
|
|
||||||
if ipMatch:
|
|
||||||
for ip in ipMatch:
|
|
||||||
failList.append([ip, date])
|
|
||||||
return failList
|
|
||||||
|
|
||||||
def hasTime(self, line):
|
|
||||||
""" Return true if the line contains a date
|
|
||||||
"""
|
|
||||||
timeMatch = re.search(self.timeregex, line)
|
|
||||||
if timeMatch:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getTime(self, line):
|
|
||||||
""" Gets the time of a log message.
|
|
||||||
"""
|
|
||||||
date = 0
|
|
||||||
timeMatch = re.search(self.timeregex, line)
|
|
||||||
if timeMatch:
|
|
||||||
date = self.getUnixTime(timeMatch.group())
|
|
||||||
return date
|
|
||||||
|
|
||||||
def getUnixTime(self, value):
|
|
||||||
""" Returns the Unix timestamp of the given value.
|
|
||||||
Pattern should describe the date construction of
|
|
||||||
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
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||||
|
.TH FAIL2BAN-CLIENT "1" "November 2006" "fail2ban-client v0.7.4" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
fail2ban-client \- configure and control the server
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B fail2ban-client
|
||||||
|
[\fIOPTIONS\fR]... \fI<COMMAND>\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Fail2Ban v0.7.4 reads log file that contains password failure report
|
||||||
|
and bans the corresponding IP addresses using firewall rules.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-c\fR <DIR>
|
||||||
|
configuration directory
|
||||||
|
.TP
|
||||||
|
\fB\-s\fR <FILE>
|
||||||
|
socket path
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR
|
||||||
|
dump configuration. For debugging
|
||||||
|
.TP
|
||||||
|
\fB\-i\fR
|
||||||
|
interactive mode
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR
|
||||||
|
increase verbosity
|
||||||
|
.TP
|
||||||
|
\fB\-q\fR
|
||||||
|
decrease verbosity
|
||||||
|
.TP
|
||||||
|
\fB\-x\fR
|
||||||
|
force execution of the server
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR, \fB\-\-help\fR
|
||||||
|
display this help message
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR, \fB\-\-version\fR
|
||||||
|
print the version
|
||||||
|
.SH COMMAND
|
||||||
|
.TP
|
||||||
|
start
|
||||||
|
start the server and the jails
|
||||||
|
.TP
|
||||||
|
reload
|
||||||
|
reload the configuration
|
||||||
|
.TP
|
||||||
|
stop
|
||||||
|
stop all jails and terminate the server
|
||||||
|
.TP
|
||||||
|
status
|
||||||
|
get the current status
|
||||||
|
.TP
|
||||||
|
set loglevel <LEVEL>
|
||||||
|
set loglevel to <LEVEL>
|
||||||
|
.TP
|
||||||
|
get loglevel
|
||||||
|
get loglevel
|
||||||
|
.TP
|
||||||
|
set logtarget <TARGET>
|
||||||
|
set log target to <TARGET>
|
||||||
|
.TP
|
||||||
|
get logtarget
|
||||||
|
get log target
|
||||||
|
.TP
|
||||||
|
add <JAIL> [BACKEND]
|
||||||
|
create <JAIL> using [BACKEND]
|
||||||
|
.TP
|
||||||
|
set <JAIL> <CMD>
|
||||||
|
set the <CMD> value for <JAIL>
|
||||||
|
.TP
|
||||||
|
get <JAIL> <CMD>
|
||||||
|
get the <CMD> value for <JAIL>
|
||||||
|
.TP
|
||||||
|
start <JAIL>
|
||||||
|
start <JAIL>
|
||||||
|
.TP
|
||||||
|
stop <JAIL>
|
||||||
|
stop <JAIL>. The jail is removed
|
||||||
|
.TP
|
||||||
|
status <JAIL>
|
||||||
|
get the current status of <JAIL>
|
||||||
|
.SH FILES
|
||||||
|
\fI/etc/fail2ban/*\fR
|
||||||
|
.SH AUTHOR
|
||||||
|
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>.
|
||||||
|
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
Report bugs to <lostcontrol@users.sourceforge.net>
|
||||||
|
.SH COPYRIGHT
|
||||||
|
Copyright \(co 2004-2006 Cyril Jaquier
|
||||||
|
.br
|
||||||
|
Copyright of modifications held by their respective authors.
|
||||||
|
Licensed under the GNU General Public License v2 (GPL).
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.br
|
||||||
|
fail2ban-server(1)
|
|
@ -0,0 +1,12 @@
|
||||||
|
Include file for help2man man page
|
||||||
|
$Id: $
|
||||||
|
|
||||||
|
[name]
|
||||||
|
fail2ban-client \- configure and control the server
|
||||||
|
|
||||||
|
[files]
|
||||||
|
\fI/etc/fail2ban/*\fR
|
||||||
|
|
||||||
|
[see also]
|
||||||
|
.br
|
||||||
|
fail2ban-server(1)
|
|
@ -0,0 +1,27 @@
|
||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||||
|
.TH FAIL2BAN-REGEX "1" "November 2006" "fail2ban-regex v0.7.4" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
fail2ban-regex \- test Fail2ban "failregex" option
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B fail2ban-regex
|
||||||
|
\fI<logline> <failregex>\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Fail2Ban v0.7.4 reads log file that contains password failure report
|
||||||
|
and bans the corresponding IP addresses using firewall rules.
|
||||||
|
.PP
|
||||||
|
This tools can test and benchmark your regular expressions for the "failregex"
|
||||||
|
option.
|
||||||
|
.SH AUTHOR
|
||||||
|
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>.
|
||||||
|
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
Report bugs to <lostcontrol@users.sourceforge.net>
|
||||||
|
.SH COPYRIGHT
|
||||||
|
Copyright \(co 2004-2006 Cyril Jaquier
|
||||||
|
.br
|
||||||
|
Copyright of modifications held by their respective authors.
|
||||||
|
Licensed under the GNU General Public License v2 (GPL).
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.br
|
||||||
|
fail2ban-client(1)
|
||||||
|
fail2ban-server(1)
|
|
@ -0,0 +1,10 @@
|
||||||
|
Include file for help2man man page
|
||||||
|
$Id: $
|
||||||
|
|
||||||
|
[name]
|
||||||
|
fail2ban-regex \- test Fail2ban "failregex" option
|
||||||
|
|
||||||
|
[see also]
|
||||||
|
.br
|
||||||
|
fail2ban-client(1)
|
||||||
|
fail2ban-server(1)
|
|
@ -0,0 +1,45 @@
|
||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.36.
|
||||||
|
.TH FAIL2BAN-SERVER "1" "November 2006" "fail2ban-server v0.7.4" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
fail2ban-server \- start the server
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B fail2ban-server
|
||||||
|
[\fIOPTIONS\fR]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Fail2Ban v0.7.4 reads log file that contains password failure report
|
||||||
|
and bans the corresponding IP addresses using firewall rules.
|
||||||
|
.PP
|
||||||
|
Only use this command for debugging purpose. Start the server with
|
||||||
|
fail2ban\-client instead.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-b\fR
|
||||||
|
start in background
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR
|
||||||
|
start in foreground
|
||||||
|
.TP
|
||||||
|
\fB\-s\fR <FILE>
|
||||||
|
socket path
|
||||||
|
.TP
|
||||||
|
\fB\-x\fR
|
||||||
|
force execution of the server
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR, \fB\-\-help\fR
|
||||||
|
display this help message
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR, \fB\-\-version\fR
|
||||||
|
print the version
|
||||||
|
.SH AUTHOR
|
||||||
|
Written by Cyril Jaquier <lostcontrol@users.sourceforge.net>.
|
||||||
|
Many contributions by Yaroslav O. Halchenko <debian@onerussian.com>.
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
Report bugs to <lostcontrol@users.sourceforge.net>
|
||||||
|
.SH COPYRIGHT
|
||||||
|
Copyright \(co 2004-2006 Cyril Jaquier
|
||||||
|
.br
|
||||||
|
Copyright of modifications held by their respective authors.
|
||||||
|
Licensed under the GNU General Public License v2 (GPL).
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.br
|
||||||
|
fail2ban-client(1)
|
|
@ -0,0 +1,9 @@
|
||||||
|
Include file for help2man man page
|
||||||
|
$Id: $
|
||||||
|
|
||||||
|
[name]
|
||||||
|
fail2ban-server \- start the server
|
||||||
|
|
||||||
|
[see also]
|
||||||
|
.br
|
||||||
|
fail2ban-client(1)
|
|
@ -1,58 +0,0 @@
|
||||||
.\"
|
|
||||||
.TH "FAIL2BAN" "8" "July 2005" "Cyril Jaquier" "System administration tools"
|
|
||||||
.SH "NAME"
|
|
||||||
fail2ban \- bans IP that makes too many password failures
|
|
||||||
.SH "SYNOPSIS"
|
|
||||||
.B fail2ban
|
|
||||||
[\fIOPTIONS\fR]
|
|
||||||
.SH "DESCRIPTION"
|
|
||||||
\fBFail2Ban\fR reads log file that contains password failure report
|
|
||||||
and bans the corresponding IP addresses using firewall rules. It updates
|
|
||||||
firewall rules to reject the IP address.
|
|
||||||
.SH "OPTIONS"
|
|
||||||
.TP
|
|
||||||
\fB\-b\fR
|
|
||||||
start in background
|
|
||||||
.TP
|
|
||||||
\fB\-c\fR \fIFILE\fR
|
|
||||||
read configuration file \fIFILE\fR
|
|
||||||
.TP
|
|
||||||
\fB\-p\fR \fIFILE\fR
|
|
||||||
create PID lock in \fIFILE\fR
|
|
||||||
.TP
|
|
||||||
\fB\-h, \-\-help\fR
|
|
||||||
display this help message
|
|
||||||
.TP
|
|
||||||
\fB\-i\fR \fIIP\fR
|
|
||||||
\fIIP\fR(s) to ignore
|
|
||||||
.TP
|
|
||||||
\fB\-k\fR
|
|
||||||
kill a currently running Fail2Ban instance
|
|
||||||
.TP
|
|
||||||
\fB\-r\fR \fIVALUE\fR
|
|
||||||
allow a max of \fIVALUE\fR password failure [maxfailures]
|
|
||||||
.TP
|
|
||||||
\fB\-t\fR \fITIME\fR
|
|
||||||
ban IP for \fITIME\fR seconds [bantime]
|
|
||||||
.TP
|
|
||||||
\fB\-f\fR \fITIME\fR
|
|
||||||
lifetime in seconds of failed entry [findtime]
|
|
||||||
.TP
|
|
||||||
\fB\-v\fR
|
|
||||||
verbose. Use twice for greater effect
|
|
||||||
.TP
|
|
||||||
\fB\-V, \-\-version\fR
|
|
||||||
print software version
|
|
||||||
.SH "FILES"
|
|
||||||
.I /etc/fail2ban.conf
|
|
||||||
.RS
|
|
||||||
The configuration file. See \fBfail2ban.conf\fR(5) for further details.
|
|
||||||
.SH "REPORTING BUGS"
|
|
||||||
Please report bugs at http://sourceforge.net/projects/fail2ban/
|
|
||||||
via bug tracker
|
|
||||||
.SH "AUTHOR"
|
|
||||||
Cyril Jaquier <lostcontrol@users.sourceforge.net>
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
.TP
|
|
||||||
See
|
|
||||||
.BR "http://fail2ban.sourceforge.net/".
|
|
|
@ -1,20 +0,0 @@
|
||||||
.\"
|
|
||||||
.TH "FAIL2BAN.CONF" "5" "July 2005" "Cyril Jaquier" "System administration tools"
|
|
||||||
.SH "NAME"
|
|
||||||
fail2ban.conf \- configuration data for fail2ban
|
|
||||||
.SH "DESCRIPTION"
|
|
||||||
\fB/etc/fail2ban.conf\fR contains data about the general configuration of fail2ban, the mail notification and services to monitor.
|
|
||||||
.SH "VARIABLES"
|
|
||||||
Please look at the file itself
|
|
||||||
.SH "FILES"
|
|
||||||
.I /etc/fail2ban.conf
|
|
||||||
.SH "REPORTING BUGS"
|
|
||||||
Please report bugs at http://sourceforge.net/projects/fail2ban/
|
|
||||||
via bug tracker
|
|
||||||
.SH "AUTHOR"
|
|
||||||
Cyril Jaquier <lostcontrol@users.sourceforge.net>
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
.BR fail2ban (8)
|
|
||||||
.TP
|
|
||||||
See
|
|
||||||
.BR "http://fail2ban.sourceforge.net/".
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# fail2ban-client
|
||||||
|
echo -n "Generating fail2ban-client "
|
||||||
|
help2man --section=1 --no-info --include=fail2ban-client.h2m --output fail2ban-client.1 ../fail2ban-client
|
||||||
|
echo "[done]"
|
||||||
|
echo -n "Patching fail2ban-client "
|
||||||
|
sed -i -e 's/.SS "Command:"/.SH COMMAND/' fail2ban-client.1
|
||||||
|
echo "[done]"
|
||||||
|
|
||||||
|
# fail2ban-server
|
||||||
|
echo -n "Generating fail2ban-server "
|
||||||
|
help2man --section=1 --no-info --include=fail2ban-server.h2m --output fail2ban-server.1 ../fail2ban-server
|
||||||
|
echo "[done]"
|
||||||
|
|
||||||
|
# fail2ban-regex
|
||||||
|
echo -n "Generating fail2ban-regex "
|
||||||
|
help2man --section=1 --no-info --include=fail2ban-regex.h2m --output fail2ban-regex.1 ../fail2ban-regex
|
||||||
|
echo "[done]"
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1 $
|
# $Revision: 433 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.1 $"
|
__version__ = "$Revision: 433 $"
|
||||||
__date__ = "$Date: 2004/10/10 13:33:40 $"
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
|
@ -0,0 +1,226 @@
|
||||||
|
# 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: 434 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 434 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:49:31 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import logging, os
|
||||||
|
#from subprocess import call
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
try:
|
||||||
|
# The following line gives deadlock with multiple jails
|
||||||
|
#retcode = call(realCmd, shell=True)
|
||||||
|
retcode = os.system(realCmd)
|
||||||
|
if retcode == 0:
|
||||||
|
logSys.debug("%s returned successfully" % realCmd)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logSys.error("%s returned %x" % (realCmd, retcode))
|
||||||
|
except OSError, e:
|
||||||
|
logSys.error("%s failed with %s" % (realCmd, e))
|
||||||
|
return False
|
|
@ -0,0 +1,182 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from banmanager import BanManager
|
||||||
|
from jailthread import JailThread
|
||||||
|
from action import Action
|
||||||
|
from mytime import MyTime
|
||||||
|
import time, logging
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
## 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.getIdle():
|
||||||
|
#logSys.debug(self.jail.getName() + ": action")
|
||||||
|
ret = self.__checkBan()
|
||||||
|
if not ret:
|
||||||
|
self.__checkUnBan()
|
||||||
|
time.sleep(self.getSleepTime())
|
||||||
|
else:
|
||||||
|
time.sleep(self.getSleepTime())
|
||||||
|
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):
|
||||||
|
ticket = self.jail.getFailTicket()
|
||||||
|
if ticket != False:
|
||||||
|
aInfo = dict()
|
||||||
|
bTicket = BanManager.createBanTicket(ticket)
|
||||||
|
aInfo["ip"] = bTicket.getIP()
|
||||||
|
aInfo["failures"] = bTicket.getAttempt()
|
||||||
|
logSys.warn("[%s] Ban %s" % (self.jail.getName(), 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):
|
||||||
|
for ticket in self.__banManager.unBanList(MyTime.time()):
|
||||||
|
aInfo = dict()
|
||||||
|
aInfo["ip"] = ticket.getIP()
|
||||||
|
logSys.warn("[%s] Unban %s" % (self.jail.getName(), 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.warn("[%s] Unban %s" % (self.jail.getName(), 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,210 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from banticket import BanTicket
|
||||||
|
from threading import Lock
|
||||||
|
from mytime import MyTime
|
||||||
|
import 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.__lock.acquire()
|
||||||
|
self.__banTime = int(value)
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the ban time.
|
||||||
|
#
|
||||||
|
# Get the amount of time an IP address get banned.
|
||||||
|
# @return the time
|
||||||
|
|
||||||
|
def getBanTime(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__banTime
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set the total number of banned address.
|
||||||
|
#
|
||||||
|
# @param value total number
|
||||||
|
|
||||||
|
def setBanTotal(self, value):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__banTotal = value
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the total number of banned address.
|
||||||
|
#
|
||||||
|
# @return the total number
|
||||||
|
|
||||||
|
def getBanTotal(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__banTotal
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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 = MyTime.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):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
if not self.__inBanList(ticket):
|
||||||
|
self.__banList.append(ticket)
|
||||||
|
self.__banTotal += 1
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return len(self.__banList)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
uBList = list()
|
||||||
|
# Permanent banning
|
||||||
|
if self.__banTime < 0:
|
||||||
|
return uBList
|
||||||
|
for ticket in self.__banList:
|
||||||
|
if ticket.getTime() < time - self.__banTime:
|
||||||
|
uBList.append(ticket)
|
||||||
|
self.__delBanTicket(ticket)
|
||||||
|
return uBList
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Flush the ban list.
|
||||||
|
#
|
||||||
|
# Get the ban list and initialize it with an empty one.
|
||||||
|
# @return the complete ban list
|
||||||
|
|
||||||
|
def flushBanList(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
uBList = self.__banList
|
||||||
|
self.__banList = list()
|
||||||
|
return uBList
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
|
@ -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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import 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,145 @@
|
||||||
|
# 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: 321 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 321 $"
|
||||||
|
__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import time, logging
|
||||||
|
|
||||||
|
from datetemplate import DateTemplate
|
||||||
|
from datestrptime import DateStrptime
|
||||||
|
from datetai64n import DateTai64n
|
||||||
|
from dateepoch import DateEpoch
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.filter.datedetector")
|
||||||
|
|
||||||
|
class DateDetector:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__lock = Lock()
|
||||||
|
self.__templates = list()
|
||||||
|
self.__defTemplate = DateTemplate()
|
||||||
|
|
||||||
|
def addDefaultTemplate(self):
|
||||||
|
# standard
|
||||||
|
template = DateStrptime()
|
||||||
|
template.setName("Month Day Hour:Minute:Second")
|
||||||
|
template.setRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
|
||||||
|
template.setPattern("%b %d %H:%M:%S")
|
||||||
|
self.__templates.append(template)
|
||||||
|
# asctime
|
||||||
|
template = DateStrptime()
|
||||||
|
template.setName("Weekday Month Day Hour:Minute:Second Year")
|
||||||
|
template.setRegex("\S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}")
|
||||||
|
template.setPattern("%a %b %d %H:%M:%S %Y")
|
||||||
|
self.__templates.append(template)
|
||||||
|
# simple date
|
||||||
|
template = DateStrptime()
|
||||||
|
template.setName("Year/Month/Day Hour:Minute:Second")
|
||||||
|
template.setRegex("\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}")
|
||||||
|
template.setPattern("%Y/%m/%d %H:%M:%S")
|
||||||
|
self.__templates.append(template)
|
||||||
|
# Apache format [31/Oct/2006:09:22:55 -0000]
|
||||||
|
template = DateStrptime()
|
||||||
|
template.setName("Day/Month/Year:Hour:Minute:Second")
|
||||||
|
template.setRegex("\d{2}/\S{3}/\d{4}:\d{2}:\d{2}:\d{2}")
|
||||||
|
template.setPattern("%d/%b/%Y:%H:%M:%S")
|
||||||
|
self.__templates.append(template)
|
||||||
|
# TAI64N
|
||||||
|
template = DateTai64n()
|
||||||
|
template.setName("TAI64N")
|
||||||
|
self.__templates.append(template)
|
||||||
|
# Epoch
|
||||||
|
template = DateEpoch()
|
||||||
|
template.setName("Epoch")
|
||||||
|
self.__templates.append(template)
|
||||||
|
|
||||||
|
def getTemplates(self):
|
||||||
|
return self.__templates
|
||||||
|
|
||||||
|
def setDefaultRegex(self, value):
|
||||||
|
self.__defTemplate.setRegex(value)
|
||||||
|
|
||||||
|
def getDefaultRegex(self):
|
||||||
|
return self.__defTemplate.getRegex()
|
||||||
|
|
||||||
|
def setDefaultPattern(self, value):
|
||||||
|
self.__defTemplate.setPattern(value)
|
||||||
|
|
||||||
|
def getDefaultPattern(self):
|
||||||
|
return self.__defTemplate.getPattern()
|
||||||
|
|
||||||
|
def matchTime(self, line):
|
||||||
|
if self.__defTemplate.isValid():
|
||||||
|
return self.__defTemplate.matchDate(line)
|
||||||
|
else:
|
||||||
|
self.__lock.acquire()
|
||||||
|
for template in self.__templates:
|
||||||
|
match = template.matchDate(line)
|
||||||
|
if not match == None:
|
||||||
|
self.__lock.release()
|
||||||
|
return match
|
||||||
|
self.__lock.release()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getTime(self, line):
|
||||||
|
if self.__defTemplate.isValid():
|
||||||
|
try:
|
||||||
|
date = self.__defTemplate.getDate(line)
|
||||||
|
return date
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
self.__lock.acquire()
|
||||||
|
for template in self.__templates:
|
||||||
|
try:
|
||||||
|
date = template.getDate(line)
|
||||||
|
if date == None:
|
||||||
|
continue
|
||||||
|
template.incHits()
|
||||||
|
self.__lock.release()
|
||||||
|
return date
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
self.__lock.release()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getUnixTime(self, line):
|
||||||
|
date = self.getTime(line)
|
||||||
|
if date == None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return time.mktime(date)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sort the template lists using the hits score. This method is not called
|
||||||
|
# in this object and thus should be called from time to time.
|
||||||
|
|
||||||
|
def sortTemplate(self):
|
||||||
|
self.__lock.acquire()
|
||||||
|
logSys.debug("Sorting the template list")
|
||||||
|
self.__templates.sort(cmp = lambda x, y: cmp(x.getHits(), y.getHits()),
|
||||||
|
reverse=True)
|
||||||
|
self.__lock.release()
|
|
@ -0,0 +1,44 @@
|
||||||
|
# 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: 321 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 321 $"
|
||||||
|
__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from datetemplate import DateTemplate
|
||||||
|
|
||||||
|
class DateEpoch(DateTemplate):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
DateTemplate.__init__(self)
|
||||||
|
# We already know the format for TAI64N
|
||||||
|
self.setRegex("^\d{10}(\.\d{6})?")
|
||||||
|
|
||||||
|
def getDate(self, line):
|
||||||
|
date = None
|
||||||
|
dateMatch = self.matchDate(line)
|
||||||
|
if dateMatch:
|
||||||
|
# extract part of format which represents seconds since epoch
|
||||||
|
date = list(time.gmtime(float(dateMatch.group())))
|
||||||
|
return date
|
|
@ -0,0 +1,84 @@
|
||||||
|
# -*- coding: utf8 -*-
|
||||||
|
# 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: 321 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 321 $"
|
||||||
|
__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from mytime import MyTime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from datetemplate import DateTemplate
|
||||||
|
|
||||||
|
##
|
||||||
|
# Use strptime() to parse a date. Our current locale is the 'C'
|
||||||
|
# one because we do not set the locale explicitly. This is POSIX
|
||||||
|
# standard.
|
||||||
|
|
||||||
|
class DateStrptime(DateTemplate):
|
||||||
|
|
||||||
|
TABLE = dict()
|
||||||
|
TABLE["Jan"] = []
|
||||||
|
TABLE["Feb"] = [u"Fév"]
|
||||||
|
TABLE["Mar"] = [u"Mär"]
|
||||||
|
TABLE["Apr"] = ["Avr"]
|
||||||
|
TABLE["May"] = ["Mai"]
|
||||||
|
TABLE["Jun"] = []
|
||||||
|
TABLE["Jul"] = []
|
||||||
|
TABLE["Aug"] = ["Aou"]
|
||||||
|
TABLE["Sep"] = []
|
||||||
|
TABLE["Oct"] = ["Okt"]
|
||||||
|
TABLE["Nov"] = []
|
||||||
|
TABLE["Dec"] = [u"Déc", "Dez"]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
DateTemplate.__init__(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def convertLocale(date):
|
||||||
|
for t in DateStrptime.TABLE:
|
||||||
|
for m in DateStrptime.TABLE[t]:
|
||||||
|
if date.find(m) >= 0:
|
||||||
|
return date.replace(m, t)
|
||||||
|
return date
|
||||||
|
|
||||||
|
def getDate(self, line):
|
||||||
|
date = None
|
||||||
|
dateMatch = self.matchDate(line)
|
||||||
|
if dateMatch:
|
||||||
|
try:
|
||||||
|
# Try first with 'C' locale
|
||||||
|
date = list(time.strptime(dateMatch.group(), self.getPattern()))
|
||||||
|
except ValueError:
|
||||||
|
# Try to convert date string to 'C' locale
|
||||||
|
conv = self.convertLocale(dateMatch.group())
|
||||||
|
date = list(time.strptime(conv, self.getPattern()))
|
||||||
|
if date[0] < 2000:
|
||||||
|
# There is probably no year field in the logs
|
||||||
|
date[0] = MyTime.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) > MyTime.time():
|
||||||
|
date[0] -= 1
|
||||||
|
return date
|
|
@ -0,0 +1,46 @@
|
||||||
|
# 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: 321 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 321 $"
|
||||||
|
__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from datetemplate import DateTemplate
|
||||||
|
|
||||||
|
class DateTai64n(DateTemplate):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
DateTemplate.__init__(self)
|
||||||
|
# We already know the format for TAI64N
|
||||||
|
self.setRegex("@[0-9a-f]{24}")
|
||||||
|
|
||||||
|
def getDate(self, line):
|
||||||
|
date = None
|
||||||
|
dateMatch = self.matchDate(line)
|
||||||
|
if dateMatch:
|
||||||
|
# extract part of format which represents seconds since epoch
|
||||||
|
value = dateMatch.group()
|
||||||
|
seconds_since_epoch = value[2:17]
|
||||||
|
date = list(time.gmtime(int(seconds_since_epoch, 16)))
|
||||||
|
return date
|
|
@ -0,0 +1,71 @@
|
||||||
|
# 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: 321 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 321 $"
|
||||||
|
__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
class DateTemplate:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__name = ""
|
||||||
|
self.__regex = ""
|
||||||
|
self.__cRegex = None
|
||||||
|
self.__pattern = ""
|
||||||
|
self.__hits = 0
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
self.__name = name
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.__name
|
||||||
|
|
||||||
|
def setRegex(self, regex):
|
||||||
|
self.__regex = regex
|
||||||
|
self.__cRegex = re.compile(regex)
|
||||||
|
|
||||||
|
def getRegex(self):
|
||||||
|
return self.__regex
|
||||||
|
|
||||||
|
def setPattern(self, pattern):
|
||||||
|
self.__pattern = pattern
|
||||||
|
|
||||||
|
def getPattern(self):
|
||||||
|
return self.__pattern
|
||||||
|
|
||||||
|
def isValid(self):
|
||||||
|
return self.__regex != "" and self.__pattern != ""
|
||||||
|
|
||||||
|
def incHits(self):
|
||||||
|
self.__hits = self.__hits + 1
|
||||||
|
|
||||||
|
def getHits(self):
|
||||||
|
return self.__hits
|
||||||
|
|
||||||
|
def matchDate(self, line):
|
||||||
|
dateMatch = self.__cRegex.search(line)
|
||||||
|
return dateMatch
|
||||||
|
|
||||||
|
def getDate(self, line):
|
||||||
|
raise Exception("matchDate() is abstract")
|
|
@ -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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import 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,132 @@
|
||||||
|
# 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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from faildata import FailData
|
||||||
|
from failticket import FailTicket
|
||||||
|
from threading import Lock
|
||||||
|
import 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.__lock.acquire()
|
||||||
|
self.__failTotal = value
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getFailTotal(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__failTotal
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def setMaxRetry(self, value):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__maxRetry = value
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getMaxRetry(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__maxRetry
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def setMaxTime(self, value):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__maxTime = value
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getMaxTime(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__maxTime
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
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):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return len(self.__failList)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
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):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
for ip in self.__failList:
|
||||||
|
data = self.__failList[ip]
|
||||||
|
if data.getRetry() >= self.__maxRetry:
|
||||||
|
self.__delFailure(ip)
|
||||||
|
# Create a FailTicket from BanData
|
||||||
|
failTicket = FailTicket(ip, data.getLastTime())
|
||||||
|
failTicket.setAttempt(data.getRetry())
|
||||||
|
return failTicket
|
||||||
|
raise FailManagerEmpty
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
class FailManagerEmpty(Exception):
|
||||||
|
pass
|
||||||
|
|
|
@ -16,25 +16,22 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.2 $
|
# $Revision: 382 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.2 $"
|
__version__ = "$Revision: 382 $"
|
||||||
__date__ = "$Date: 2005/11/20 17:07:47 $"
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from ticket import Ticket
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
# Gets the instance of the logger.
|
||||||
logSys = logging.getLogger("fail2ban")
|
logSys = logging.getLogger("fail2ban")
|
||||||
|
|
||||||
def replaceTag(query, aInfo):
|
class FailTicket(Ticket):
|
||||||
""" Replace tags in query
|
|
||||||
"""
|
def __init__(self, ip, time):
|
||||||
string = query
|
Ticket.__init__(self, ip, time)
|
||||||
for tag in aInfo:
|
|
||||||
string = string.replace('<'+tag+'>', `aInfo[tag]`)
|
|
||||||
# New line
|
|
||||||
string = string.replace('<br>', '\n')
|
|
||||||
return string
|
|
|
@ -0,0 +1,528 @@
|
||||||
|
# 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: 440 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 440 $"
|
||||||
|
__date__ = "$Date: 2006-10-31 23:24:34 +0100 (Tue, 31 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from failmanager import FailManager
|
||||||
|
from failticket import FailTicket
|
||||||
|
from jailthread import JailThread
|
||||||
|
from datedetector import DateDetector
|
||||||
|
from mytime import MyTime
|
||||||
|
|
||||||
|
import logging, re, sre_constants
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
## The jail which contains this filter.
|
||||||
|
self.jail = jail
|
||||||
|
## The failures manager.
|
||||||
|
self.failManager = FailManager()
|
||||||
|
self.modified = False
|
||||||
|
## The log file handler.
|
||||||
|
self.__crtHandler = None
|
||||||
|
self.__crtFilename = None
|
||||||
|
## The log file path.
|
||||||
|
self.__logPath = []
|
||||||
|
## 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 last position of the file.
|
||||||
|
self.__lastPos = dict()
|
||||||
|
## The last date in tht log file.
|
||||||
|
self.__lastDate = dict()
|
||||||
|
|
||||||
|
self.dateDetector = DateDetector()
|
||||||
|
self.dateDetector.addDefaultTemplate()
|
||||||
|
logSys.info("Created Filter")
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add a log file path
|
||||||
|
#
|
||||||
|
# @param path log file path
|
||||||
|
|
||||||
|
def addLogPath(self, path):
|
||||||
|
self.getLogPath().append(path)
|
||||||
|
# Initialize default values
|
||||||
|
self.__lastDate[path] = 0
|
||||||
|
self.__lastPos[path] = 0
|
||||||
|
|
||||||
|
##
|
||||||
|
# Delete a log path
|
||||||
|
#
|
||||||
|
# @param path the log file to delete
|
||||||
|
|
||||||
|
def delLogPath(self, path):
|
||||||
|
self.getLogPath().remove(path)
|
||||||
|
del self.__lastDate[path]
|
||||||
|
del self.__lastPos[path]
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the log file path
|
||||||
|
#
|
||||||
|
# @return log file path
|
||||||
|
|
||||||
|
def getLogPath(self):
|
||||||
|
return self.__logPath
|
||||||
|
|
||||||
|
##
|
||||||
|
# Check whether path is already monitored.
|
||||||
|
#
|
||||||
|
# @param path The path
|
||||||
|
# @return True if the path is already monitored else False
|
||||||
|
|
||||||
|
def containsLogPath(self, path):
|
||||||
|
try:
|
||||||
|
self.getLogPath().index(path)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set the regular expression which matches the time.
|
||||||
|
#
|
||||||
|
# @param value the regular expression
|
||||||
|
|
||||||
|
def setTimeRegex(self, value):
|
||||||
|
self.dateDetector.setDefaultRegex(value)
|
||||||
|
logSys.info("Set default regex = %s" % value)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the regular expression which matches the time.
|
||||||
|
#
|
||||||
|
# @return the regular expression
|
||||||
|
|
||||||
|
def getTimeRegex(self):
|
||||||
|
return self.dateDetector.getDefaultRegex()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set the time pattern.
|
||||||
|
#
|
||||||
|
# @param value the time pattern
|
||||||
|
|
||||||
|
def setTimePattern(self, value):
|
||||||
|
self.dateDetector.setDefaultPattern(value)
|
||||||
|
logSys.info("Set default pattern = %s" % value)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the time pattern.
|
||||||
|
#
|
||||||
|
# @return the time pattern
|
||||||
|
|
||||||
|
def getTimePattern(self):
|
||||||
|
return self.dateDetector.getDefaultPattern()
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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):
|
||||||
|
try:
|
||||||
|
self.__failRegexObj = re.compile(value)
|
||||||
|
self.__failRegex = value
|
||||||
|
logSys.info("Set failregex = %s" % value)
|
||||||
|
except sre_constants.error:
|
||||||
|
logSys.error("Unable to compile regular expression " + 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):
|
||||||
|
raise Exception("run() is abstract")
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add an IP/DNS to the ignore list.
|
||||||
|
#
|
||||||
|
# IP addresses in the ignore list are not taken into account
|
||||||
|
# when finding failures. CIDR mask and DNS are also accepted.
|
||||||
|
# @param ip IP address to ignore
|
||||||
|
|
||||||
|
def addIgnoreIP(self, ip):
|
||||||
|
logSys.debug("Add " + ip + " to ignore list")
|
||||||
|
self.__ignoreIpList.append(ip)
|
||||||
|
|
||||||
|
def delIgnoreIP(self, ip):
|
||||||
|
logSys.debug("Remove " + ip + " from ignore list")
|
||||||
|
self.__ignoreIpList.remove(ip)
|
||||||
|
|
||||||
|
def getIgnoreIP(self):
|
||||||
|
return self.__ignoreIpList
|
||||||
|
|
||||||
|
##
|
||||||
|
# Check if IP address/DNS is in the ignore list.
|
||||||
|
#
|
||||||
|
# Check if the given IP address matches an IP address/DNS 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:
|
||||||
|
# An empty string is always false
|
||||||
|
if i == "":
|
||||||
|
return False
|
||||||
|
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:
|
||||||
|
# Check if IP in DNS
|
||||||
|
ips = DNSUtils.dnsToIp(i)
|
||||||
|
if ip in ips:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
if a == b:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
##
|
||||||
|
# Open the log file.
|
||||||
|
|
||||||
|
def __openLogFile(self, filename):
|
||||||
|
""" Opens the log file specified on init.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.__crtFilename = filename
|
||||||
|
self.__crtHandler = open(filename)
|
||||||
|
logSys.debug("Opened " + filename)
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
logSys.error("Unable to open " + filename)
|
||||||
|
except IOError:
|
||||||
|
logSys.error("Unable to read " + filename +
|
||||||
|
". Please check permissions")
|
||||||
|
return False
|
||||||
|
|
||||||
|
##
|
||||||
|
# Close the log file.
|
||||||
|
|
||||||
|
def __closeLogFile(self):
|
||||||
|
self.__crtFilename = None
|
||||||
|
self.__crtHandler.close()
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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.__crtHandler.readline()
|
||||||
|
lastDate = self.__lastDate[self.__crtFilename]
|
||||||
|
lineDate = self.dateDetector.getUnixTime(line)
|
||||||
|
if lastDate < lineDate:
|
||||||
|
logSys.debug("Date " + `lastDate` + " is smaller than " + `lineDate`)
|
||||||
|
logSys.debug("Log rotation detected for " + self.__crtFilename)
|
||||||
|
self.__lastPos[self.__crtFilename] = 0
|
||||||
|
lastPos = self.__lastPos[self.__crtFilename]
|
||||||
|
logSys.debug("Setting file position to " + `lastPos` + " for " +
|
||||||
|
self.__crtFilename)
|
||||||
|
self.__crtHandler.seek(lastPos)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the file position.
|
||||||
|
|
||||||
|
def __getFilePos(self):
|
||||||
|
return self.__crtHandler.tell()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Gets all the failure in the log file.
|
||||||
|
#
|
||||||
|
# Gets all the failure in the log file which are newer than
|
||||||
|
# MyTime.time()-self.findTime. When a failure is detected, a FailTicket
|
||||||
|
# is created and is added to the FailManager.
|
||||||
|
|
||||||
|
def getFailures(self, filename):
|
||||||
|
ret = self.__openLogFile(filename)
|
||||||
|
if not ret:
|
||||||
|
logSys.error("Unable to get failures in " + filename)
|
||||||
|
return False
|
||||||
|
self.__setFilePos()
|
||||||
|
lastLine = None
|
||||||
|
for line in self.__crtHandler:
|
||||||
|
if not self.isActive():
|
||||||
|
# The jail has been stopped
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
# Decode line to UTF-8
|
||||||
|
line = line.decode('utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
pass
|
||||||
|
if not self.dateDetector.matchTime(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 < MyTime.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[filename] = self.__getFilePos()
|
||||||
|
if lastLine:
|
||||||
|
self.__lastDate[filename] = self.dateDetector.getUnixTime(lastLine)
|
||||||
|
self.__closeLogFile()
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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()
|
||||||
|
if self.__failRegexObj == None:
|
||||||
|
logSys.error("No failregex is set")
|
||||||
|
else:
|
||||||
|
match = self.__failRegexObj.search(line)
|
||||||
|
if match:
|
||||||
|
date = self.dateDetector.getUnixTime(match.string)
|
||||||
|
if date == None:
|
||||||
|
logSys.debug("Found a match but no valid date/time found "
|
||||||
|
+ "for " + match.string + ". Please contact "
|
||||||
|
+ "the author in order to get support for "
|
||||||
|
+ "this format")
|
||||||
|
else:
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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:
|
||||||
|
|
||||||
|
DNS_CRE = re.compile("(?:(?:\w|-)+\.){2,}\w+")
|
||||||
|
IP_CRE = 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.DNS_CRE.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.IP_CRE.match(text)
|
||||||
|
if match:
|
||||||
|
return match
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def isValidIP(string):
|
||||||
|
""" Return true if str is a valid IP
|
||||||
|
"""
|
||||||
|
s = string.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(string):
|
||||||
|
""" Convert a string IPv4 address into an unsigned integer.
|
||||||
|
"""
|
||||||
|
return struct.unpack("!L", socket.inet_aton(string))[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,120 @@
|
||||||
|
# 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: 418 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 418 $"
|
||||||
|
__date__ = "$Date: 2006-10-19 00:30:57 +0200 (Thu, 19 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from failmanager import FailManagerEmpty
|
||||||
|
from filter import Filter
|
||||||
|
from mytime import MyTime
|
||||||
|
|
||||||
|
import time, logging, gamin
|
||||||
|
|
||||||
|
# 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 FilterGamin(Filter):
|
||||||
|
|
||||||
|
##
|
||||||
|
# Constructor.
|
||||||
|
#
|
||||||
|
# Initialize the filter object with default values.
|
||||||
|
# @param jail the jail object
|
||||||
|
|
||||||
|
def __init__(self, jail):
|
||||||
|
Filter.__init__(self, jail)
|
||||||
|
# Gamin monitor
|
||||||
|
self.monitor = gamin.WatchMonitor()
|
||||||
|
logSys.info("Created FilterGamin")
|
||||||
|
|
||||||
|
|
||||||
|
def callback(self, path, event):
|
||||||
|
logSys.debug("Got event: " + `event` + " for " + path)
|
||||||
|
if event in (gamin.GAMCreated, gamin.GAMChanged, gamin.GAMExists):
|
||||||
|
logSys.debug("File changed: " + path)
|
||||||
|
self.getFailures(path)
|
||||||
|
self.modified = True
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add a log file path
|
||||||
|
#
|
||||||
|
# @param path log file path
|
||||||
|
|
||||||
|
def addLogPath(self, path):
|
||||||
|
if self.containsLogPath(path):
|
||||||
|
logSys.error(path + " already exists")
|
||||||
|
else:
|
||||||
|
self.monitor.watch_file(path, self.callback)
|
||||||
|
Filter.addLogPath(self, path)
|
||||||
|
logSys.info("Added logfile = %s" % path)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Delete a log path
|
||||||
|
#
|
||||||
|
# @param path the log file to delete
|
||||||
|
|
||||||
|
def delLogPath(self, path):
|
||||||
|
if not self.containsLogPath(path):
|
||||||
|
logSys.error(path + " is not monitored")
|
||||||
|
else:
|
||||||
|
self.monitor.stop_watch(path)
|
||||||
|
Filter.delLogPath(self, path)
|
||||||
|
logSys.info("Removed logfile = %s" % path)
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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.getIdle():
|
||||||
|
# We cannot block here because we want to be able to
|
||||||
|
# exit.
|
||||||
|
if self.monitor.event_pending():
|
||||||
|
self.monitor.handle_events()
|
||||||
|
|
||||||
|
if self.modified:
|
||||||
|
try:
|
||||||
|
ticket = self.failManager.toBan()
|
||||||
|
self.jail.putFailTicket(ticket)
|
||||||
|
except FailManagerEmpty:
|
||||||
|
self.failManager.cleanup(MyTime.time())
|
||||||
|
self.dateDetector.sortTemplate()
|
||||||
|
self.modified = False
|
||||||
|
time.sleep(self.getSleepTime())
|
||||||
|
else:
|
||||||
|
time.sleep(self.getSleepTime())
|
||||||
|
logSys.debug(self.jail.getName() + ": filter terminated")
|
||||||
|
return True
|
|
@ -0,0 +1,140 @@
|
||||||
|
# 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: 354 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 354 $"
|
||||||
|
__date__ = "$Date: 2006-09-13 23:31:22 +0200 (Wed, 13 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from failmanager import FailManagerEmpty
|
||||||
|
from filter import Filter
|
||||||
|
from mytime import MyTime
|
||||||
|
|
||||||
|
import time, logging, os
|
||||||
|
|
||||||
|
# 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 FilterPoll(Filter):
|
||||||
|
|
||||||
|
##
|
||||||
|
# Constructor.
|
||||||
|
#
|
||||||
|
# Initialize the filter object with default values.
|
||||||
|
# @param jail the jail object
|
||||||
|
|
||||||
|
def __init__(self, jail):
|
||||||
|
Filter.__init__(self, jail)
|
||||||
|
## The time of the last modification of the file.
|
||||||
|
self.__lastModTime = dict()
|
||||||
|
self.__file404Cnt = dict()
|
||||||
|
logSys.info("Created FilterPoll")
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add a log file path
|
||||||
|
#
|
||||||
|
# @param path log file path
|
||||||
|
|
||||||
|
def addLogPath(self, path):
|
||||||
|
if self.containsLogPath(path):
|
||||||
|
logSys.error(path + " already exists")
|
||||||
|
else:
|
||||||
|
self.__lastModTime[path] = 0
|
||||||
|
self.__file404Cnt[path] = 0
|
||||||
|
Filter.addLogPath(self, path)
|
||||||
|
logSys.info("Added logfile = %s" % path)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Delete a log path
|
||||||
|
#
|
||||||
|
# @param path the log file to delete
|
||||||
|
|
||||||
|
def delLogPath(self, path):
|
||||||
|
if not self.containsLogPath(path):
|
||||||
|
logSys.error(path + " is not monitored")
|
||||||
|
else:
|
||||||
|
del self.__lastModTime[path]
|
||||||
|
del self.__file404Cnt[path]
|
||||||
|
Filter.delLogPath(self, path)
|
||||||
|
logSys.info("Removed logfile = %s" % path)
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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.getIdle():
|
||||||
|
# Get file modification
|
||||||
|
for f in self.getLogPath():
|
||||||
|
if self.isModified(f):
|
||||||
|
self.getFailures(f)
|
||||||
|
self.modified = True
|
||||||
|
|
||||||
|
if self.modified:
|
||||||
|
try:
|
||||||
|
ticket = self.failManager.toBan()
|
||||||
|
self.jail.putFailTicket(ticket)
|
||||||
|
except FailManagerEmpty:
|
||||||
|
self.failManager.cleanup(MyTime.time())
|
||||||
|
self.dateDetector.sortTemplate()
|
||||||
|
self.modified = False
|
||||||
|
time.sleep(self.getSleepTime())
|
||||||
|
else:
|
||||||
|
time.sleep(self.getSleepTime())
|
||||||
|
logSys.debug(self.jail.getName() + ": filter terminated")
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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, filename):
|
||||||
|
try:
|
||||||
|
logStats = os.stat(filename)
|
||||||
|
self.__file404Cnt[filename] = 0
|
||||||
|
if self.__lastModTime[filename] == logStats.st_mtime:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
logSys.debug(filename + " has been modified")
|
||||||
|
self.__lastModTime[filename] = logStats.st_mtime
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
logSys.error("Unable to get stat on " + filename)
|
||||||
|
self.__file404Cnt[filename] = self.__file404Cnt[filename] + 1
|
||||||
|
if self.__file404Cnt[filename] > 2:
|
||||||
|
logSys.warn("Too much read error. Set the jail idle")
|
||||||
|
self.jail.setIdle(True)
|
||||||
|
self.__file404Cnt[filename] = 0
|
||||||
|
return False
|
|
@ -0,0 +1,149 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import Queue, logging
|
||||||
|
|
||||||
|
from actions import Actions
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.jail")
|
||||||
|
|
||||||
|
class Jail:
|
||||||
|
|
||||||
|
def __init__(self, name, backend = "auto"):
|
||||||
|
self.__lock = Lock()
|
||||||
|
self.__name = name
|
||||||
|
self.__queue = Queue.Queue()
|
||||||
|
self.__filter = None
|
||||||
|
if backend == "polling":
|
||||||
|
self.__initPoller()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.__initGamin()
|
||||||
|
except ImportError:
|
||||||
|
self.__initPoller()
|
||||||
|
self.__action = Actions(self)
|
||||||
|
|
||||||
|
def __initPoller(self):
|
||||||
|
logSys.info("Using poller")
|
||||||
|
from filterpoll import FilterPoll
|
||||||
|
self.__filter = FilterPoll(self)
|
||||||
|
|
||||||
|
def __initGamin(self):
|
||||||
|
# Try to import gamin
|
||||||
|
import gamin
|
||||||
|
logSys.info("Using Gamin")
|
||||||
|
from filtergamin import FilterGamin
|
||||||
|
self.__filter = FilterGamin(self)
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__name = name
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__name
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getFilter(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__filter
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getAction(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__action
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def putFailTicket(self, ticket):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__queue.put(ticket)
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getFailTicket(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
try:
|
||||||
|
return self.__queue.get(False)
|
||||||
|
except Queue.Empty:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__filter.start()
|
||||||
|
self.__action.start()
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__filter.stop()
|
||||||
|
self.__action.stop()
|
||||||
|
self.__lock.release()
|
||||||
|
self.__filter.join()
|
||||||
|
self.__action.join()
|
||||||
|
|
||||||
|
def isActive(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
isActive0 = self.__filter.isActive()
|
||||||
|
isActive1 = self.__action.isActive()
|
||||||
|
return isActive0 or isActive1
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def setIdle(self, value):
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__filter.setIdle(value)
|
||||||
|
self.__action.setIdle(value)
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getIdle(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__filter.getIdle() or self.__action.getIdle()
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getStatus(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
fStatus = self.__filter.status()
|
||||||
|
aStatus = self.__action.status()
|
||||||
|
ret = [("filter", fStatus),
|
||||||
|
("action", aStatus)]
|
||||||
|
return ret
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
|
@ -0,0 +1,107 @@
|
||||||
|
# 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: 354 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 354 $"
|
||||||
|
__date__ = "$Date: 2006-09-13 23:31:22 +0200 (Wed, 13 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
|
||||||
|
from jail import Jail
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
class Jails:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__lock = Lock()
|
||||||
|
self.__jails = dict()
|
||||||
|
|
||||||
|
def add(self, name, backend):
|
||||||
|
self.__lock.acquire()
|
||||||
|
if self.__jails.has_key(name):
|
||||||
|
self.__lock.release()
|
||||||
|
raise DuplicateJailException(name)
|
||||||
|
else:
|
||||||
|
self.__jails[name] = Jail(name, backend)
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def remove(self, name):
|
||||||
|
self.__lock.acquire()
|
||||||
|
if self.__jails.has_key(name):
|
||||||
|
del self.__jails[name]
|
||||||
|
self.__lock.release()
|
||||||
|
else:
|
||||||
|
self.__lock.release()
|
||||||
|
raise UnknownJailException(name)
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
if self.__jails.has_key(name):
|
||||||
|
jail = self.__jails[name]
|
||||||
|
return jail
|
||||||
|
else:
|
||||||
|
raise UnknownJailException(name)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getAction(self, name):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
if self.__jails.has_key(name):
|
||||||
|
action = self.__jails[name].getAction()
|
||||||
|
return action
|
||||||
|
else:
|
||||||
|
raise UnknownJailException(name)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getFilter(self, name):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
if self.__jails.has_key(name):
|
||||||
|
action = self.__jails[name].getFilter()
|
||||||
|
return action
|
||||||
|
else:
|
||||||
|
raise UnknownJailException(name)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def getAll(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return self.__jails.copy()
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
return len(self.__jails)
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateJailException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class UnknownJailException(Exception):
|
||||||
|
pass
|
|
@ -0,0 +1,118 @@
|
||||||
|
# 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: 433 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 433 $"
|
||||||
|
__date__ = "$Date: 2006-10-24 21:40:51 +0200 (Tue, 24 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.server")
|
||||||
|
|
||||||
|
class JailThread(Thread):
|
||||||
|
|
||||||
|
##
|
||||||
|
# Constructor.
|
||||||
|
#
|
||||||
|
# Initialize the filter object with default values.
|
||||||
|
# @param jail the jail object
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
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
|
|
@ -16,10 +16,35 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1 $
|
# $Revision: 321 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.1 $"
|
__version__ = "$Revision: 321 $"
|
||||||
__date__ = "$Date: 2005/02/18 13:26:41 $"
|
__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
class MyTime:
|
||||||
|
|
||||||
|
myTime = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setTime(t):
|
||||||
|
MyTime.myTime = t
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def time():
|
||||||
|
if MyTime.myTime == None:
|
||||||
|
return time.time()
|
||||||
|
else:
|
||||||
|
return MyTime.myTime
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def gmtime():
|
||||||
|
if MyTime.myTime == None:
|
||||||
|
return time.gmtime()
|
||||||
|
else:
|
||||||
|
return time.gmtime(MyTime.myTime)
|
||||||
|
|
|
@ -0,0 +1,378 @@
|
||||||
|
# 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: 436 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 436 $"
|
||||||
|
__date__ = "$Date: 2006-10-30 23:47:30 +0100 (Mon, 30 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from jails import Jails
|
||||||
|
from transmitter import Transmitter
|
||||||
|
from ssocket import SSocket
|
||||||
|
from ssocket import SSocketErrorException
|
||||||
|
import logging, logging.handlers, sys, os, signal
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.server")
|
||||||
|
|
||||||
|
class Server:
|
||||||
|
|
||||||
|
def __init__(self, daemon = False):
|
||||||
|
self.__jails = Jails()
|
||||||
|
self.__daemon = daemon
|
||||||
|
self.__transm = Transmitter(self)
|
||||||
|
self.__socket = SSocket(self.__transm)
|
||||||
|
self.__logLevel = 3
|
||||||
|
self.__logTarget = "STDOUT"
|
||||||
|
# Set logging level
|
||||||
|
self.setLogLevel(self.__logLevel)
|
||||||
|
self.setLogTarget(self.__logTarget)
|
||||||
|
|
||||||
|
def __sigTERMhandler(self, signum, frame):
|
||||||
|
logSys.debug("Caught signal %d. Exiting" % signum)
|
||||||
|
self.quit()
|
||||||
|
|
||||||
|
def start(self, sock, force = False):
|
||||||
|
logSys.info("Starting Fail2ban")
|
||||||
|
|
||||||
|
# Install signal handlers
|
||||||
|
signal.signal(signal.SIGTERM, self.__sigTERMhandler)
|
||||||
|
signal.signal(signal.SIGINT, self.__sigTERMhandler)
|
||||||
|
|
||||||
|
# First set the mask to only allow access to owner
|
||||||
|
os.umask(0077)
|
||||||
|
if self.__daemon:
|
||||||
|
ret = self.__createDaemon()
|
||||||
|
if ret:
|
||||||
|
logSys.info("Daemon started")
|
||||||
|
else:
|
||||||
|
logSys.error("Could not create daemon")
|
||||||
|
raise ServerInitializationError("Could not create daemon")
|
||||||
|
# Start the communication
|
||||||
|
logSys.debug("Starting communication")
|
||||||
|
try:
|
||||||
|
self.__socket.initialize(sock, force)
|
||||||
|
self.__socket.start()
|
||||||
|
# Workaround (???) for join() bug.
|
||||||
|
# https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1167930&group_id=5470
|
||||||
|
while self.__socket.isAlive():
|
||||||
|
self.__socket.join(1)
|
||||||
|
except SSocketErrorException:
|
||||||
|
logSys.error("Could not start server")
|
||||||
|
logSys.info("Exiting Fail2ban")
|
||||||
|
|
||||||
|
def quit(self):
|
||||||
|
self.stopAllJail()
|
||||||
|
# Stop communication
|
||||||
|
self.__socket.stop()
|
||||||
|
|
||||||
|
def addJail(self, name, backend):
|
||||||
|
self.__jails.add(name, backend)
|
||||||
|
|
||||||
|
def delJail(self, name):
|
||||||
|
self.__jails.remove(name)
|
||||||
|
|
||||||
|
def startJail(self, name):
|
||||||
|
if not self.isActive(name):
|
||||||
|
self.__jails.get(name).start()
|
||||||
|
|
||||||
|
def stopJail(self, name):
|
||||||
|
if self.isActive(name):
|
||||||
|
self.__jails.get(name).stop()
|
||||||
|
self.delJail(name)
|
||||||
|
|
||||||
|
def stopAllJail(self):
|
||||||
|
for jail in self.__jails.getAll():
|
||||||
|
self.stopJail(jail)
|
||||||
|
|
||||||
|
def isActive(self, name):
|
||||||
|
return self.__jails.get(name).isActive()
|
||||||
|
|
||||||
|
def setIdleJail(self, name, value):
|
||||||
|
self.__jails.get(name).setIdle(value)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getIdleJail(self, name):
|
||||||
|
return self.__jails.get(name).getIdle()
|
||||||
|
|
||||||
|
# Filter
|
||||||
|
def addIgnoreIP(self, name, ip):
|
||||||
|
self.__jails.getFilter(name).addIgnoreIP(ip)
|
||||||
|
|
||||||
|
def delIgnoreIP(self, name, ip):
|
||||||
|
self.__jails.getFilter(name).delIgnoreIP(ip)
|
||||||
|
|
||||||
|
def getIgnoreIP(self, name):
|
||||||
|
return self.__jails.getFilter(name).getIgnoreIP()
|
||||||
|
|
||||||
|
def addLogPath(self, name, fileName):
|
||||||
|
self.__jails.getFilter(name).addLogPath(fileName)
|
||||||
|
|
||||||
|
def delLogPath(self, name, fileName):
|
||||||
|
self.__jails.getFilter(name).delLogPath(fileName)
|
||||||
|
|
||||||
|
def getLogPath(self, name):
|
||||||
|
return self.__jails.getFilter(name).getLogPath()
|
||||||
|
|
||||||
|
def setTimeRegex(self, name, value):
|
||||||
|
self.__jails.getFilter(name).setTimeRegex(value)
|
||||||
|
|
||||||
|
def getTimeRegex(self, name):
|
||||||
|
return self.__jails.getFilter(name).getTimeRegex()
|
||||||
|
|
||||||
|
def setTimePattern(self, name, value):
|
||||||
|
self.__jails.getFilter(name).setTimePattern(value)
|
||||||
|
|
||||||
|
def getTimePattern(self, name):
|
||||||
|
return self.__jails.getFilter(name).getTimePattern()
|
||||||
|
|
||||||
|
def setFindTime(self, name, value):
|
||||||
|
self.__jails.getFilter(name).setFindTime(value)
|
||||||
|
|
||||||
|
def getFindTime(self, name):
|
||||||
|
return self.__jails.getFilter(name).getFindTime()
|
||||||
|
|
||||||
|
def setFailRegex(self, name, value):
|
||||||
|
self.__jails.getFilter(name).setFailRegex(value)
|
||||||
|
|
||||||
|
def getFailRegex(self, name):
|
||||||
|
return self.__jails.getFilter(name).getFailRegex()
|
||||||
|
|
||||||
|
def setMaxRetry(self, name, value):
|
||||||
|
self.__jails.getFilter(name).setMaxRetry(value)
|
||||||
|
|
||||||
|
def getMaxRetry(self, name):
|
||||||
|
return self.__jails.getFilter(name).getMaxRetry()
|
||||||
|
|
||||||
|
def setMaxTime(self, name, value):
|
||||||
|
self.__jails.getFilter(name).setMaxTime(value)
|
||||||
|
|
||||||
|
def getMaxTime(self, name):
|
||||||
|
return self.__jails.getFilter(name).getMaxTime()
|
||||||
|
|
||||||
|
# Action
|
||||||
|
def addAction(self, name, value):
|
||||||
|
self.__jails.getAction(name).addAction(value)
|
||||||
|
|
||||||
|
def getLastAction(self, name):
|
||||||
|
return self.__jails.getAction(name).getLastAction()
|
||||||
|
|
||||||
|
def delAction(self, name, value):
|
||||||
|
self.__jails.getAction(name).delAction(value)
|
||||||
|
|
||||||
|
def setCInfo(self, name, action, key, value):
|
||||||
|
self.__jails.getAction(name).getAction(action).setCInfo(key, value)
|
||||||
|
|
||||||
|
def getCInfo(self, name, action, key):
|
||||||
|
return self.__jails.getAction(name).getAction(action).getCInfo(key)
|
||||||
|
|
||||||
|
def delCInfo(self, name, action, key):
|
||||||
|
self.__jails.getAction(name).getAction(action).delCInfo(key)
|
||||||
|
|
||||||
|
def setBanTime(self, name, value):
|
||||||
|
self.__jails.getAction(name).setBanTime(value)
|
||||||
|
|
||||||
|
def getBanTime(self, name):
|
||||||
|
return self.__jails.getAction(name).getBanTime()
|
||||||
|
|
||||||
|
def setActionStart(self, name, action, value):
|
||||||
|
self.__jails.getAction(name).getAction(action).setActionStart(value)
|
||||||
|
|
||||||
|
def getActionStart(self, name, action):
|
||||||
|
return self.__jails.getAction(name).getAction(action).getActionStart()
|
||||||
|
|
||||||
|
def setActionStop(self, name, action, value):
|
||||||
|
self.__jails.getAction(name).getAction(action).setActionStop(value)
|
||||||
|
|
||||||
|
def getActionStop(self, name, action):
|
||||||
|
return self.__jails.getAction(name).getAction(action).getActionStop()
|
||||||
|
|
||||||
|
def setActionCheck(self, name, action, value):
|
||||||
|
self.__jails.getAction(name).getAction(action).setActionCheck(value)
|
||||||
|
|
||||||
|
def getActionCheck(self, name, action):
|
||||||
|
return self.__jails.getAction(name).getAction(action).getActionCheck()
|
||||||
|
|
||||||
|
def setActionBan(self, name, action, value):
|
||||||
|
self.__jails.getAction(name).getAction(action).setActionBan(value)
|
||||||
|
|
||||||
|
def getActionBan(self, name, action):
|
||||||
|
return self.__jails.getAction(name).getAction(action).getActionBan()
|
||||||
|
|
||||||
|
def setActionUnban(self, name, action, value):
|
||||||
|
self.__jails.getAction(name).getAction(action).setActionUnban(value)
|
||||||
|
|
||||||
|
def getActionUnban(self, name, action):
|
||||||
|
return self.__jails.getAction(name).getAction(action).getActionUnban()
|
||||||
|
|
||||||
|
# Status
|
||||||
|
def status(self):
|
||||||
|
jailList = ''
|
||||||
|
for jail in self.__jails.getAll():
|
||||||
|
jailList += jail + ', '
|
||||||
|
length = len(jailList)
|
||||||
|
if not length == 0:
|
||||||
|
jailList = jailList[:length-2]
|
||||||
|
ret = [("Number of jail", self.__jails.size()),
|
||||||
|
("Jail list", jailList)]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def statusJail(self, name):
|
||||||
|
return self.__jails.get(name).getStatus()
|
||||||
|
|
||||||
|
# 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":
|
||||||
|
hdlr = logging.handlers.SysLogHandler()
|
||||||
|
elif target == "STDOUT":
|
||||||
|
hdlr = logging.StreamHandler(sys.stdout)
|
||||||
|
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("%(asctime)s %(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
|
||||||
|
|
||||||
|
def __createDaemon(self):
|
||||||
|
""" Detach a process from the controlling terminal and run it in the
|
||||||
|
background as a daemon.
|
||||||
|
|
||||||
|
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Fork a child process so the parent can exit. This will return control
|
||||||
|
# to the command line or shell. This is required so that the new process
|
||||||
|
# is guaranteed not to be a process group leader. We have this guarantee
|
||||||
|
# because the process GID of the parent is inherited by the child, but
|
||||||
|
# the child gets a new PID, making it impossible for its PID to equal its
|
||||||
|
# PGID.
|
||||||
|
pid = os.fork()
|
||||||
|
except OSError, e:
|
||||||
|
return((e.errno, e.strerror)) # ERROR (return a tuple)
|
||||||
|
|
||||||
|
if pid == 0: # The first child.
|
||||||
|
|
||||||
|
# Next we call os.setsid() to become the session leader of this new
|
||||||
|
# session. The process also becomes the process group leader of the
|
||||||
|
# new process group. Since a controlling terminal is associated with a
|
||||||
|
# session, and this new session has not yet acquired a controlling
|
||||||
|
# terminal our process now has no controlling terminal. This shouldn't
|
||||||
|
# fail, since we're guaranteed that the child is not a process group
|
||||||
|
# leader.
|
||||||
|
os.setsid()
|
||||||
|
|
||||||
|
# When the first child terminates, all processes in the second child
|
||||||
|
# are sent a SIGHUP, so it's ignored.
|
||||||
|
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Fork a second child to prevent zombies. Since the first child is
|
||||||
|
# a session leader without a controlling terminal, it's possible for
|
||||||
|
# it to acquire one by opening a terminal in the future. This second
|
||||||
|
# fork guarantees that the child is no longer a session leader, thus
|
||||||
|
# preventing the daemon from ever acquiring a controlling terminal.
|
||||||
|
pid = os.fork() # Fork a second child.
|
||||||
|
except OSError, e:
|
||||||
|
return((e.errno, e.strerror)) # ERROR (return a tuple)
|
||||||
|
|
||||||
|
if (pid == 0): # The second child.
|
||||||
|
# Ensure that the daemon doesn't keep any directory in use. Failure
|
||||||
|
# to do this could make a filesystem unmountable.
|
||||||
|
os.chdir("/")
|
||||||
|
else:
|
||||||
|
os._exit(0) # Exit parent (the first child) of the second child.
|
||||||
|
else:
|
||||||
|
os._exit(0) # Exit parent of the first child.
|
||||||
|
|
||||||
|
# Close all open files. Try the system configuration variable, SC_OPEN_MAX,
|
||||||
|
# for the maximum number of open files to close. If it doesn't exist, use
|
||||||
|
# the default value (configurable).
|
||||||
|
try:
|
||||||
|
maxfd = os.sysconf("SC_OPEN_MAX")
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
maxfd = 256 # default maximum
|
||||||
|
|
||||||
|
for fd in range(0, maxfd):
|
||||||
|
try:
|
||||||
|
os.close(fd)
|
||||||
|
except OSError: # ERROR (ignore)
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Redirect the standard file descriptors to /dev/null.
|
||||||
|
os.open("/dev/null", os.O_RDONLY) # standard input (0)
|
||||||
|
os.open("/dev/null", os.O_RDWR) # standard output (1)
|
||||||
|
os.open("/dev/null", os.O_RDWR) # standard error (2)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ServerInitializationError(Exception):
|
||||||
|
pass
|
|
@ -0,0 +1,133 @@
|
||||||
|
# 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: 443 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 443 $"
|
||||||
|
__date__ = "$Date: 2006-11-01 00:36:59 +0100 (Wed, 01 Nov 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
# cPickle generates an exception with Python 2.5
|
||||||
|
#from cPickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
|
from pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||||
|
import socket, logging, os, os.path
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.comm")
|
||||||
|
|
||||||
|
class SSocket(Thread):
|
||||||
|
|
||||||
|
END_STRING = "<F2B_END_COMMAND>"
|
||||||
|
|
||||||
|
def __init__(self, transmitter):
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.__transmit = transmitter
|
||||||
|
self.__isRunning = False
|
||||||
|
self.__socket = "/tmp/fail2ban.sock"
|
||||||
|
self.__ssock = None
|
||||||
|
logSys.debug("Created SSocket")
|
||||||
|
|
||||||
|
def initialize(self, sock = "/tmp/fail2ban.sock", force = False):
|
||||||
|
self.__socket = sock
|
||||||
|
# Remove socket
|
||||||
|
if os.path.exists(sock):
|
||||||
|
logSys.error("Fail2ban seems to be already running")
|
||||||
|
if force:
|
||||||
|
logSys.warn("Forcing execution of the server")
|
||||||
|
os.remove(sock)
|
||||||
|
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)
|
||||||
|
# Do not use a blocking socket as there is problem at shutdown.
|
||||||
|
# Use a timeout instead. Daemon exits at most 'timeout' seconds
|
||||||
|
# after the command.
|
||||||
|
self.__ssock.settimeout(1)
|
||||||
|
# Bind the socket to a public host and a well-known port
|
||||||
|
#self.__ssock.bind(("localhost", 2222))
|
||||||
|
self.__ssock.bind(sock)
|
||||||
|
# Become a server socket
|
||||||
|
self.__ssock.listen(5)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.__isRunning = True
|
||||||
|
while self.__isRunning:
|
||||||
|
try:
|
||||||
|
(csock, address) = self.__ssock.accept()
|
||||||
|
thread = SocketWorker(csock, self.__transmit)
|
||||||
|
thread.start()
|
||||||
|
except socket.timeout:
|
||||||
|
# Do nothing here
|
||||||
|
pass
|
||||||
|
self.__ssock.close()
|
||||||
|
# Remove socket
|
||||||
|
if os.path.exists(self.__socket):
|
||||||
|
logSys.debug("Removed socket file " + self.__socket)
|
||||||
|
os.remove(self.__socket)
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
class SocketWorker(Thread):
|
||||||
|
|
||||||
|
def __init__(self, csock, transmitter):
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.__csock = csock
|
||||||
|
self.__transmit = transmitter
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
logSys.debug("Starting new thread to handle the request")
|
||||||
|
msg = self.__receive(self.__csock)
|
||||||
|
msg = self.__transmit.proceed(msg)
|
||||||
|
self.__send(self.__csock, msg)
|
||||||
|
self.__csock.close()
|
||||||
|
logSys.debug("Connection closed")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __send(sock, msg):
|
||||||
|
obj = dumps(msg, HIGHEST_PROTOCOL)
|
||||||
|
sock.send(obj + SSocket.END_STRING)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __receive(sock):
|
||||||
|
msg = ''
|
||||||
|
while msg.rfind(SSocket.END_STRING) == -1:
|
||||||
|
chunk = sock.recv(6)
|
||||||
|
if chunk == '':
|
||||||
|
raise RuntimeError, "socket connection broken"
|
||||||
|
msg = msg + chunk
|
||||||
|
return 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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import 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,260 @@
|
||||||
|
# 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: 409 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 409 $"
|
||||||
|
__date__ = "$Date: 2006-10-16 21:42:50 +0200 (Mon, 16 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
from threading import Lock
|
||||||
|
import logging, time
|
||||||
|
|
||||||
|
# Gets the instance of the logger.
|
||||||
|
logSys = logging.getLogger("fail2ban.comm")
|
||||||
|
|
||||||
|
class Transmitter:
|
||||||
|
|
||||||
|
def __init__(self, server):
|
||||||
|
self.__lock = Lock()
|
||||||
|
self.__server = server
|
||||||
|
|
||||||
|
def proceed(self, action):
|
||||||
|
# Deserialize object
|
||||||
|
try:
|
||||||
|
self.__lock.acquire()
|
||||||
|
logSys.debug("Action: " + `action`)
|
||||||
|
try:
|
||||||
|
ret = self.__actionHandler(action)
|
||||||
|
ack = 0, ret
|
||||||
|
except Exception, e:
|
||||||
|
logSys.warn("Invalid command: " + `action`)
|
||||||
|
ack = 1, e
|
||||||
|
return ack
|
||||||
|
finally:
|
||||||
|
self.__lock.release()
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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")
|
||||||
|
try:
|
||||||
|
backend = action[2]
|
||||||
|
except IndexError:
|
||||||
|
backend = "auto"
|
||||||
|
self.__server.addJail(name, backend)
|
||||||
|
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] == "addignoreip":
|
||||||
|
value = action[2]
|
||||||
|
self.__server.addIgnoreIP(name, value)
|
||||||
|
return self.__server.getIgnoreIP(name)
|
||||||
|
elif action[1] == "delignoreip":
|
||||||
|
value = action[2]
|
||||||
|
self.__server.delIgnoreIP(name, value)
|
||||||
|
return self.__server.getIgnoreIP(name)
|
||||||
|
elif action[1] == "addlogpath":
|
||||||
|
value = action[2:]
|
||||||
|
for path in value:
|
||||||
|
self.__server.addLogPath(name, path)
|
||||||
|
return self.__server.getLogPath(name)
|
||||||
|
elif action[1] == "dellogpath":
|
||||||
|
value = action[2]
|
||||||
|
self.__server.delLogPath(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] == "findtime":
|
||||||
|
value = action[2]
|
||||||
|
self.__server.setFindTime(name, int(value))
|
||||||
|
return self.__server.getFindTime(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 or not yet implemented)")
|
||||||
|
|
||||||
|
def __actionGet(self, action):
|
||||||
|
name = action[0]
|
||||||
|
# Logging
|
||||||
|
if name == "loglevel":
|
||||||
|
return self.__server.getLogLevel()
|
||||||
|
elif name == "logtarget":
|
||||||
|
return self.__server.getLogTarget()
|
||||||
|
# Filter
|
||||||
|
elif action[1] == "logpath":
|
||||||
|
return self.__server.getLogPath(name)
|
||||||
|
elif action[1] == "ignoreip":
|
||||||
|
return self.__server.getIgnoreIP(name)
|
||||||
|
elif action[1] == "timeregex":
|
||||||
|
return self.__server.getTimeRegex(name)
|
||||||
|
elif action[1] == "timepattern":
|
||||||
|
return self.__server.getTimePattern(name)
|
||||||
|
elif action[1] == "failregex":
|
||||||
|
return self.__server.getFailRegex(name)
|
||||||
|
elif action[1] == "maxtime":
|
||||||
|
return self.__server.getMaxTime(name)
|
||||||
|
elif action[1] == "findtime":
|
||||||
|
return self.__server.getFindTime(name)
|
||||||
|
elif action[1] == "maxretry":
|
||||||
|
return self.__server.getMaxRetry(name)
|
||||||
|
# Action
|
||||||
|
elif action[1] == "bantime":
|
||||||
|
return self.__server.getBanTime(name)
|
||||||
|
elif action[1] == "addaction":
|
||||||
|
return self.__server.getLastAction(name).getName()
|
||||||
|
elif action[1] == "actionstart":
|
||||||
|
act = action[2]
|
||||||
|
return self.__server.getActionStart(name, act)
|
||||||
|
elif action[1] == "actionstop":
|
||||||
|
act = action[2]
|
||||||
|
return self.__server.getActionStop(name, act)
|
||||||
|
elif action[1] == "actioncheck":
|
||||||
|
act = action[2]
|
||||||
|
return self.__server.getActionCheck(name, act)
|
||||||
|
elif action[1] == "actionban":
|
||||||
|
act = action[2]
|
||||||
|
return self.__server.getActionBan(name, act)
|
||||||
|
elif action[1] == "actionunban":
|
||||||
|
act = action[2]
|
||||||
|
return self.__server.getActionUnban(name, act)
|
||||||
|
raise Exception("Invalid command (no get action or not yet implemented)")
|
||||||
|
|
||||||
|
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)")
|
||||||
|
|
55
setup.py
55
setup.py
|
@ -18,11 +18,11 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.6 $
|
# $Revision: 413 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.6 $"
|
__version__ = "$Revision: 413 $"
|
||||||
__date__ = "$Date: 2006/01/22 11:08:42 $"
|
__date__ = "$Date: 2006-10-17 23:13:11 +0200 (Tue, 17 Oct 2006) $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ from distutils.core import setup
|
||||||
from version import version
|
from version import version
|
||||||
from os.path import isfile, join
|
from os.path import isfile, join
|
||||||
from sys import exit, argv
|
from sys import exit, argv
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
longdesc = '''
|
longdesc = '''
|
||||||
Fail2Ban scans log files like /var/log/pwdfail or
|
Fail2Ban scans log files like /var/log/pwdfail or
|
||||||
|
@ -48,17 +49,49 @@ setup(
|
||||||
url = "http://fail2ban.sourceforge.net",
|
url = "http://fail2ban.sourceforge.net",
|
||||||
license = "GPL",
|
license = "GPL",
|
||||||
platforms = "Posix",
|
platforms = "Posix",
|
||||||
scripts = ['fail2ban'],
|
scripts = [
|
||||||
py_modules = ['fail2ban', 'version'],
|
'fail2ban-client',
|
||||||
packages = ['firewall', 'logreader', 'confreader', 'utils']
|
'fail2ban-server',
|
||||||
|
'fail2ban-regex'
|
||||||
|
],
|
||||||
|
py_modules = ['version'],
|
||||||
|
packages = [
|
||||||
|
'client',
|
||||||
|
'server'
|
||||||
|
],
|
||||||
|
data_files = [
|
||||||
|
('/etc/fail2ban',
|
||||||
|
glob("config/*.conf")
|
||||||
|
),
|
||||||
|
('/etc/fail2ban/filter.d',
|
||||||
|
glob("config/filter.d/*.conf")
|
||||||
|
),
|
||||||
|
('/etc/fail2ban/action.d',
|
||||||
|
glob("config/action.d/*.conf")
|
||||||
|
)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Do some checks after installation
|
# Do some checks after installation
|
||||||
# Search for obsolete files.
|
# Search for obsolete files.
|
||||||
obsoleteFiles = []
|
obsoleteFiles = []
|
||||||
elements = {"/usr/bin/": ["fail2ban.py"],
|
elements = {
|
||||||
"/usr/lib/fail2ban/firewall/": ["iptables.py", "ipfwadm.py",
|
"/etc/":
|
||||||
"ipfw.py"]}
|
[
|
||||||
|
"fail2ban.conf"
|
||||||
|
],
|
||||||
|
"/usr/bin/":
|
||||||
|
[
|
||||||
|
"fail2ban.py"
|
||||||
|
],
|
||||||
|
"/usr/lib/fail2ban/firewall/":
|
||||||
|
[
|
||||||
|
"iptables.py",
|
||||||
|
"ipfwadm.py",
|
||||||
|
"ipfw.py"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
for dir in elements:
|
for dir in elements:
|
||||||
for f in elements[dir]:
|
for f in elements[dir]:
|
||||||
path = join(dir, f)
|
path = join(dir, f)
|
||||||
|
@ -77,6 +110,6 @@ if obsoleteFiles:
|
||||||
# Update config file
|
# Update config file
|
||||||
if argv[1] == "install":
|
if argv[1] == "install":
|
||||||
print
|
print
|
||||||
print "Please do not forget to update your configuration file."
|
print "Please do not forget to update your configuration files."
|
||||||
print "Use config/fail2ban.conf.* as example."
|
print "They are in /etc/fail2ban/."
|
||||||
print
|
print
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.1 $
|
# $Revision: 253 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.1 $"
|
__version__ = "$Revision: 253 $"
|
||||||
__date__ = "$Date: 2004/10/09 15:33:33 $"
|
__date__ = "$Date: 2006-07-17 00:21:58 +0200 (Mon, 17 Jul 2006) $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
|
@ -0,0 +1,47 @@
|
||||||
|
# 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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import unittest, time
|
||||||
|
from server.action import Action
|
||||||
|
|
||||||
|
class ExecuteAction(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Call before every test case."""
|
||||||
|
self.__action = Action("Test")
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Call after every test case."""
|
||||||
|
self.__action.execActionStop()
|
||||||
|
|
||||||
|
def testExecuteActionBan(self):
|
||||||
|
self.__action.setActionStart("touch /tmp/fail2ban.test")
|
||||||
|
self.__action.setActionStop("rm -f /tmp/fail2ban.test")
|
||||||
|
self.__action.setActionBan("echo -n")
|
||||||
|
self.__action.setActionCheck("[ -e /tmp/fail2ban.test ]")
|
||||||
|
|
||||||
|
self.assertTrue(self.__action.execActionBan(None))
|
||||||
|
|
|
@ -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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__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,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: 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, time
|
||||||
|
from server.datedetector import DateDetector
|
||||||
|
from server.datetemplate import DateTemplate
|
||||||
|
|
||||||
|
class DateDetectorTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Call before every test case."""
|
||||||
|
self.__datedetector = DateDetector()
|
||||||
|
self.__datedetector.addDefaultTemplate()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Call after every test case."""
|
||||||
|
|
||||||
|
def testGetEpochTime(self):
|
||||||
|
log = "1138049999 [sshd] error: PAM: Authentication failure"
|
||||||
|
date = [2006, 1, 23, 20, 59, 59, 0, 23, 0]
|
||||||
|
dateUnix = 1138046399.0
|
||||||
|
|
||||||
|
self.assertEqual(self.__datedetector.getTime(log), date)
|
||||||
|
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
|
||||||
|
|
||||||
|
def testGetTime(self):
|
||||||
|
log = "Jan 23 21:59:59 [sshd] error: PAM: Authentication failure"
|
||||||
|
date = [2005, 1, 23, 21, 59, 59, 1, 23, -1]
|
||||||
|
dateUnix = 1106513999.0
|
||||||
|
|
||||||
|
self.assertEqual(self.__datedetector.getTime(log), date)
|
||||||
|
self.assertEqual(self.__datedetector.getUnixTime(log), dateUnix)
|
||||||
|
|
|
@ -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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__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,19 @@
|
||||||
|
Déc 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Déc 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Déc 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from failed.dns.ch
|
||||||
|
Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from failed.dns.ch
|
||||||
|
Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from failed.dns.ch
|
||||||
|
Dez 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Dez 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Dez 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
De 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
De 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
De 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128
|
||||||
|
Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10
|
||||||
|
Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10
|
||||||
|
Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10
|
||||||
|
Dec 31 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 87.142.124.10
|
|
@ -0,0 +1,13 @@
|
||||||
|
Aug 14 11:51:00 i60p295 sshd[11437]: input_userauth_request: illegal user test123
|
||||||
|
Aug 14 11:52:00 i60p295 sshd[11437]: Failed password for illegal user test123 from ::ffff:66.38.192.238 port 51381 ssh2
|
||||||
|
Aug 14 11:53:00 i60p295 sshd[11437]: Connection closed by ::ffff:66.38.192.238
|
||||||
|
Aug 14 11:53:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:55:59 i60p295 sshd[12365]: Postponed keyboard-interactive for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:56:01 i60p295 sshd[12365]: Postponed keyboard-interactive/pam for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:57:01 i60p295 sshd[12365]: Accepted keyboard-interactive/pam for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:57:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:59:01 i60p295 sshd[12365]: Accepted keyboard-interactive/pam for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:59:01 i60p295 sshd[12365]: Accepted keyboard-interactive/pam for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
||||||
|
Aug 14 11:59:01 i60p295 sshd[12365]: Accepted keyboard-interactive/pam for roehl from ::ffff:141.3.81.106 port 51332 ssh2
|
|
@ -0,0 +1,9 @@
|
||||||
|
Aug 14 11:53:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aug 14 11:54:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aug 14 11:55:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aou 14 11:56:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aou 14 11:57:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aoü 14 11:58:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aug 14 11:59:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aug 14 12:50:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
||||||
|
Aug 14 12:51:04 HOSTNAME courieresmtpd: error,relay=::ffff:203.162.223.135,from=<firozquarl@aclunc.org>,to=<BOGUSUSER@HOSTEDDOMAIN.org>: 550 User unknown.
|
|
@ -0,0 +1,15 @@
|
||||||
|
Sep 21 22:03:07 [sshd] Invalid user toto from 212.41.96.185
|
||||||
|
1124012400 [sshd] Invalid user fuck from 212.41.96.185
|
||||||
|
Sep 21 21:03:38 [sshd] Invalid user toto from 212.41.96.185
|
||||||
|
1124012500 [sshd] Invalid user fuck from 212.41.96.185
|
||||||
|
Sep 21 21:03:46 [sshd] Invalid user toto from 212.41.96.185
|
||||||
|
Aug 14 11:58:48 [sshd] Invalid user fuck from 212.41.96.185
|
||||||
|
Aug 14 11:59:58 [sshd] Invalid user toto from 212.41.96.185
|
||||||
|
Sep 21 21:04:03 [sshd] Invalid user fuck from 212.41.96.185
|
||||||
|
- Last output repeated twice -
|
||||||
|
2005/08/14 11:57:00 [sshd] Invalid user toto from 212.41.96.186
|
||||||
|
2005/08/14 11:58:00 [sshd] Invalid user fuck from 212.41.96.186
|
||||||
|
2005/08/14 11:59:00 [sshd] Invalid user toto from 212.41.96.186
|
||||||
|
2005/08/14 12:00:00 [sshd] Invalid user fuck from 212.41.96.186
|
||||||
|
- Last output repeated twice -
|
||||||
|
Sep 21 21:09:01 [sshd] Invalid user toto from 212.41.96.185
|
|
@ -0,0 +1,168 @@
|
||||||
|
# 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: 437 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 437 $"
|
||||||
|
__date__ = "$Date: 2006-10-30 23:48:52 +0100 (Mon, 30 Oct 2006) $"
|
||||||
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
|
__license__ = "GPL"
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from server.filterpoll import FilterPoll
|
||||||
|
from server.filter import Filter
|
||||||
|
from server.failmanager import FailManager
|
||||||
|
from server.failmanager import FailManagerEmpty
|
||||||
|
|
||||||
|
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))
|
||||||
|
# Test DNS
|
||||||
|
self.__filter.addIgnoreIP("www.epfl.ch")
|
||||||
|
self.assertTrue(self.__filter.inIgnoreIPList("128.178.50.12"))
|
||||||
|
|
||||||
|
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))
|
||||||
|
# Test DNS
|
||||||
|
self.__filter.addIgnoreIP("www.epfl.ch")
|
||||||
|
self.assertFalse(self.__filter.inIgnoreIPList("127.177.50.10"))
|
||||||
|
|
||||||
|
|
||||||
|
class LogFile(unittest.TestCase):
|
||||||
|
|
||||||
|
FILENAME = "testcases/files/testcase01.log"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Call before every test case."""
|
||||||
|
self.__filter = FilterPoll(None)
|
||||||
|
self.__filter.addLogPath(LogFile.FILENAME)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Call after every test case."""
|
||||||
|
|
||||||
|
#def testOpen(self):
|
||||||
|
# self.__filter.openLogFile(LogFile.FILENAME)
|
||||||
|
|
||||||
|
def testIsModified(self):
|
||||||
|
self.assertTrue(self.__filter.isModified(LogFile.FILENAME))
|
||||||
|
|
||||||
|
|
||||||
|
class GetFailures(unittest.TestCase):
|
||||||
|
|
||||||
|
FILENAME_01 = "testcases/files/testcase01.log"
|
||||||
|
FILENAME_02 = "testcases/files/testcase02.log"
|
||||||
|
FILENAME_03 = "testcases/files/testcase03.log"
|
||||||
|
FILENAME_04 = "testcases/files/testcase04.log"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Call before every test case."""
|
||||||
|
self.__filter = Filter(None)
|
||||||
|
self.__filter.setActive(True)
|
||||||
|
# TODO Test this
|
||||||
|
#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")
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Call after every test case."""
|
||||||
|
|
||||||
|
def testGetFailures01(self):
|
||||||
|
output = ('193.168.0.128', 3, 1124013599.0)
|
||||||
|
|
||||||
|
self.__filter.addLogPath(GetFailures.FILENAME_01)
|
||||||
|
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*)")
|
||||||
|
|
||||||
|
self.__filter.getFailures(GetFailures.FILENAME_01)
|
||||||
|
|
||||||
|
ticket = self.__filter.failManager.toBan()
|
||||||
|
|
||||||
|
attempts = ticket.getAttempt()
|
||||||
|
date = ticket.getTime()
|
||||||
|
ip = ticket.getIP()
|
||||||
|
found = (ip, attempts, date)
|
||||||
|
|
||||||
|
self.assertEqual(found, output)
|
||||||
|
|
||||||
|
def testGetFailures02(self):
|
||||||
|
output = ('141.3.81.106', 4, 1124013539.0)
|
||||||
|
|
||||||
|
self.__filter.addLogPath(GetFailures.FILENAME_02)
|
||||||
|
self.__filter.setFailRegex("Failed .* (?:::f{4,6}:)(?P<host>\S*)")
|
||||||
|
|
||||||
|
self.__filter.getFailures(GetFailures.FILENAME_02)
|
||||||
|
|
||||||
|
ticket = self.__filter.failManager.toBan()
|
||||||
|
|
||||||
|
attempts = ticket.getAttempt()
|
||||||
|
date = ticket.getTime()
|
||||||
|
ip = ticket.getIP()
|
||||||
|
found = (ip, attempts, date)
|
||||||
|
|
||||||
|
self.assertEqual(found, output)
|
||||||
|
|
||||||
|
def testGetFailures03(self):
|
||||||
|
output = ('203.162.223.135', 6, 1124013544.0)
|
||||||
|
|
||||||
|
self.__filter.addLogPath(GetFailures.FILENAME_03)
|
||||||
|
self.__filter.setFailRegex("error,relay=(?:::f{4,6}:)?(?P<host>\S*),.*550 User unknown")
|
||||||
|
|
||||||
|
self.__filter.getFailures(GetFailures.FILENAME_03)
|
||||||
|
|
||||||
|
ticket = self.__filter.failManager.toBan()
|
||||||
|
|
||||||
|
attempts = ticket.getAttempt()
|
||||||
|
date = ticket.getTime()
|
||||||
|
ip = ticket.getIP()
|
||||||
|
found = (ip, attempts, date)
|
||||||
|
|
||||||
|
self.assertEqual(found, output)
|
||||||
|
|
||||||
|
def testGetFailures04(self):
|
||||||
|
output = [('212.41.96.186', 4, 1124013600.0),
|
||||||
|
('212.41.96.185', 4, 1124013598.0)]
|
||||||
|
|
||||||
|
self.__filter.addLogPath(GetFailures.FILENAME_04)
|
||||||
|
self.__filter.setFailRegex("Invalid user .* (?P<host>\S*)")
|
||||||
|
|
||||||
|
self.__filter.getFailures(GetFailures.FILENAME_04)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for i in range(2):
|
||||||
|
ticket = self.__filter.failManager.toBan()
|
||||||
|
attempts = ticket.getAttempt()
|
||||||
|
date = ticket.getTime()
|
||||||
|
ip = ticket.getIP()
|
||||||
|
found = (ip, attempts, date)
|
||||||
|
self.assertEqual(found, output[i])
|
||||||
|
except FailManagerEmpty:
|
||||||
|
pass
|
||||||
|
|
|
@ -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: 382 $
|
||||||
|
|
||||||
|
__author__ = "Cyril Jaquier"
|
||||||
|
__version__ = "$Revision: 382 $"
|
||||||
|
__date__ = "$Date: 2006-09-25 19:03:48 +0200 (Mon, 25 Sep 2006) $"
|
||||||
|
__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)
|
||||||
|
|
102
utils/dns.py
102
utils/dns.py
|
@ -1,102 +0,0 @@
|
||||||
# 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: 1.8 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.8 $"
|
|
||||||
__date__ = "$Date: 2005/11/20 17:07:47 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import os, re, socket, struct
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
def textToDns(text):
|
|
||||||
""" Search for possible DNS in an arbitrary text.
|
|
||||||
Thanks to Tom Pike.
|
|
||||||
"""
|
|
||||||
match = re.findall("(?:(?:\w|-)+\.){2,}\w+", text)
|
|
||||||
if match:
|
|
||||||
return match
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def searchIP(text):
|
|
||||||
""" Search if an IP address if directly available and return
|
|
||||||
it.
|
|
||||||
"""
|
|
||||||
match = re.findall("(?:\d{1,3}\.){3}\d{1,3}", text)
|
|
||||||
if match:
|
|
||||||
return match
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def textToIp(text):
|
|
||||||
""" Return the IP of DNS found in a given text.
|
|
||||||
"""
|
|
||||||
ipList = list()
|
|
||||||
# Search for plain IP
|
|
||||||
plainIP = searchIP(text)
|
|
||||||
for element in plainIP:
|
|
||||||
if isValidIP(element):
|
|
||||||
ipList.append(element)
|
|
||||||
if not ipList:
|
|
||||||
# Try to get IP from possible DNS
|
|
||||||
dnsList = textToDns(text)
|
|
||||||
for element in dnsList:
|
|
||||||
dns = dnsToIp(element)
|
|
||||||
for e in dns:
|
|
||||||
ipList.append(e)
|
|
||||||
return ipList
|
|
||||||
|
|
||||||
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 & addr2bin(i)
|
|
||||||
|
|
||||||
def addr2bin(str):
|
|
||||||
""" Convert a string IPv4 address into an unsigned integer.
|
|
||||||
"""
|
|
||||||
return struct.unpack("!L", socket.inet_aton(str))[0]
|
|
||||||
|
|
||||||
def bin2addr(addr):
|
|
||||||
""" Convert a numeric IPv4 address into string n.n.n.n form.
|
|
||||||
"""
|
|
||||||
return socket.inet_ntoa(struct.pack("!L", addr))
|
|
|
@ -1,91 +0,0 @@
|
||||||
# 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: 1.3 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.3 $"
|
|
||||||
__date__ = "$Date: 2006/01/03 15:13:41 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import logging, smtplib, email.Utils
|
|
||||||
|
|
||||||
from utils.strings import replaceTag
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
class Mail:
|
|
||||||
""" Mailer class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, host, port = 25):
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.localTimeFlag = False
|
|
||||||
|
|
||||||
def setFromAddr(self, fromAddr):
|
|
||||||
""" Set from: address
|
|
||||||
"""
|
|
||||||
self.fromAddr = fromAddr
|
|
||||||
|
|
||||||
def setUser(self, user):
|
|
||||||
""" Set smtpuser
|
|
||||||
"""
|
|
||||||
self.user = user
|
|
||||||
|
|
||||||
def setPassword(self, password):
|
|
||||||
""" Set smtppassword
|
|
||||||
"""
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
def setToAddr(self, toAddr):
|
|
||||||
""" Set to: address
|
|
||||||
"""
|
|
||||||
self.toAddr = toAddr.split()
|
|
||||||
|
|
||||||
def setLocalTimeFlag(self, localTimeFlag):
|
|
||||||
""" Set to: address
|
|
||||||
"""
|
|
||||||
self.localTimeFlag = localTimeFlag
|
|
||||||
|
|
||||||
def sendmail(self, subject, message, aInfo):
|
|
||||||
""" Send an email using smtplib
|
|
||||||
"""
|
|
||||||
subj = replaceTag(subject, aInfo)
|
|
||||||
msg = replaceTag(message, aInfo)
|
|
||||||
|
|
||||||
mail = ("From: %s\r\nTo: %s\r\nDate: %s\r\nSubject: %s\r\n\r\n" %
|
|
||||||
(self.fromAddr, ", ".join(self.toAddr),
|
|
||||||
email.Utils.formatdate(localtime = self.localTimeFlag),
|
|
||||||
subj)) + msg
|
|
||||||
|
|
||||||
try:
|
|
||||||
server = smtplib.SMTP(self.host, self.port)
|
|
||||||
#server.set_debuglevel(1)
|
|
||||||
if not self.user == '':
|
|
||||||
server.login(self.user, self.password)
|
|
||||||
server.sendmail(self.fromAddr, self.toAddr, mail)
|
|
||||||
logSys.debug("Email sent to " + `self.toAddr`)
|
|
||||||
server.quit()
|
|
||||||
except Exception, e:
|
|
||||||
logSys.error("Unable to send mail to " + self.host + ":" +
|
|
||||||
`self.port` + " from " + self.fromAddr + " to " +
|
|
||||||
`self.toAddr` + ": " + `e` + ": " + `e.args`)
|
|
||||||
|
|
109
utils/pidlock.py
109
utils/pidlock.py
|
@ -1,109 +0,0 @@
|
||||||
# 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: 1.2 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.2 $"
|
|
||||||
__date__ = "$Date: 2005/11/20 17:07:47 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import os, logging
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
class PIDLock:
|
|
||||||
""" Manages the PID lock file.
|
|
||||||
|
|
||||||
The following class shows how to implement the singleton pattern[1] in
|
|
||||||
Python. A singleton is a class that makes sure only one instance of it
|
|
||||||
is ever created. Typically such classes are used to manage resources
|
|
||||||
that by their very nature can only exist once.
|
|
||||||
|
|
||||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52558
|
|
||||||
"""
|
|
||||||
|
|
||||||
class __impl:
|
|
||||||
""" Implementation of the singleton interface """
|
|
||||||
|
|
||||||
def setPath(self, path):
|
|
||||||
""" Set PID lock file path.
|
|
||||||
"""
|
|
||||||
self.path = path
|
|
||||||
|
|
||||||
def create(self):
|
|
||||||
""" Create PID lock.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
fileHandler = open(self.path, mode='w')
|
|
||||||
pid = os.getpid()
|
|
||||||
fileHandler.write(`pid` + '\n')
|
|
||||||
fileHandler.close()
|
|
||||||
logSys.debug("Created PID lock (" + `pid` + ") in " + self.path)
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
logSys.error("Unable to create PID lock " + self.path)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def remove(self):
|
|
||||||
""" Remove PID lock.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
os.remove(self.path)
|
|
||||||
logSys.debug("Removed PID lock " + self.path)
|
|
||||||
except OSError:
|
|
||||||
logSys.error("Unable to remove PID lock " + self.path)
|
|
||||||
except AttributeError:
|
|
||||||
# AttributeError if self.path wasn't specified yet
|
|
||||||
logSys.debug("PID lock not removed because not defined yet")
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
""" Returns the current PID if Fail2Ban is running or False
|
|
||||||
if no instance found.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
fileHandler = open(self.path)
|
|
||||||
pid = fileHandler.readline()
|
|
||||||
fileHandler.close()
|
|
||||||
return pid
|
|
||||||
except IOError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# storage for the instance reference
|
|
||||||
__instance = None
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
""" Create singleton instance """
|
|
||||||
# Check whether we already have an instance
|
|
||||||
if PIDLock.__instance is None:
|
|
||||||
# Create and remember instance
|
|
||||||
PIDLock.__instance = PIDLock.__impl()
|
|
||||||
|
|
||||||
# Store instance reference as the only member in the handle
|
|
||||||
self.__dict__['_PIDLock__instance'] = PIDLock.__instance
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
""" Delegate access to implementation """
|
|
||||||
return getattr(self.__instance, attr)
|
|
||||||
|
|
||||||
def __setattr__(self, attr, value):
|
|
||||||
""" Delegate access to implementation """
|
|
||||||
return setattr(self.__instance, attr, value)
|
|
||||||
|
|
137
utils/process.py
137
utils/process.py
|
@ -1,137 +0,0 @@
|
||||||
# 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: 1.2 $
|
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
|
||||||
__version__ = "$Revision: 1.2 $"
|
|
||||||
__date__ = "$Date: 2005/11/20 17:07:47 $"
|
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
|
||||||
__license__ = "GPL"
|
|
||||||
|
|
||||||
import os, logging, signal
|
|
||||||
|
|
||||||
# Gets the instance of the logger.
|
|
||||||
logSys = logging.getLogger("fail2ban")
|
|
||||||
|
|
||||||
class ExternalError(UserWarning):
|
|
||||||
""" Exception to warn about failed fwcheck or fwban command
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def createDaemon():
|
|
||||||
""" Detach a process from the controlling terminal and run it in the
|
|
||||||
background as a daemon.
|
|
||||||
|
|
||||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Fork a child process so the parent can exit. This will return control
|
|
||||||
# to the command line or shell. This is required so that the new process
|
|
||||||
# is guaranteed not to be a process group leader. We have this guarantee
|
|
||||||
# because the process GID of the parent is inherited by the child, but
|
|
||||||
# the child gets a new PID, making it impossible for its PID to equal its
|
|
||||||
# PGID.
|
|
||||||
pid = os.fork()
|
|
||||||
except OSError, e:
|
|
||||||
return((e.errno, e.strerror)) # ERROR (return a tuple)
|
|
||||||
|
|
||||||
if (pid == 0): # The first child.
|
|
||||||
|
|
||||||
# Next we call os.setsid() to become the session leader of this new
|
|
||||||
# session. The process also becomes the process group leader of the
|
|
||||||
# new process group. Since a controlling terminal is associated with a
|
|
||||||
# session, and this new session has not yet acquired a controlling
|
|
||||||
# terminal our process now has no controlling terminal. This shouldn't
|
|
||||||
# fail, since we're guaranteed that the child is not a process group
|
|
||||||
# leader.
|
|
||||||
os.setsid()
|
|
||||||
|
|
||||||
# When the first child terminates, all processes in the second child
|
|
||||||
# are sent a SIGHUP, so it's ignored.
|
|
||||||
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Fork a second child to prevent zombies. Since the first child is
|
|
||||||
# a session leader without a controlling terminal, it's possible for
|
|
||||||
# it to acquire one by opening a terminal in the future. This second
|
|
||||||
# fork guarantees that the child is no longer a session leader, thus
|
|
||||||
# preventing the daemon from ever acquiring a controlling terminal.
|
|
||||||
pid = os.fork() # Fork a second child.
|
|
||||||
except OSError, e:
|
|
||||||
return((e.errno, e.strerror)) # ERROR (return a tuple)
|
|
||||||
|
|
||||||
if (pid == 0): # The second child.
|
|
||||||
# Ensure that the daemon doesn't keep any directory in use. Failure
|
|
||||||
# to do this could make a filesystem unmountable.
|
|
||||||
os.chdir("/")
|
|
||||||
else:
|
|
||||||
os._exit(0) # Exit parent (the first child) of the second child.
|
|
||||||
else:
|
|
||||||
os._exit(0) # Exit parent of the first child.
|
|
||||||
|
|
||||||
# Close all open files. Try the system configuration variable, SC_OPEN_MAX,
|
|
||||||
# for the maximum number of open files to close. If it doesn't exist, use
|
|
||||||
# the default value (configurable).
|
|
||||||
try:
|
|
||||||
maxfd = os.sysconf("SC_OPEN_MAX")
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
maxfd = 256 # default maximum
|
|
||||||
|
|
||||||
for fd in range(0, maxfd):
|
|
||||||
try:
|
|
||||||
os.close(fd)
|
|
||||||
except OSError: # ERROR (ignore)
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Redirect the standard file descriptors to /dev/null.
|
|
||||||
os.open("/dev/null", os.O_RDONLY) # standard input (0)
|
|
||||||
os.open("/dev/null", os.O_RDWR) # standard output (1)
|
|
||||||
os.open("/dev/null", os.O_RDWR) # standard error (2)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def killPID(pid):
|
|
||||||
""" Kills the process with the given PID using the
|
|
||||||
INT signal (same effect as <ctrl>+<c>).
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return os.kill(pid, 2)
|
|
||||||
except OSError:
|
|
||||||
logSys.error("Can not kill process " + `pid` + ". Please check that " +
|
|
||||||
"Fail2Ban is not running and remove the file " +
|
|
||||||
"'/tmp/fail2ban.pid'")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def executeCmd(cmd, debug):
|
|
||||||
""" Executes an OS command.
|
|
||||||
"""
|
|
||||||
if cmd == "":
|
|
||||||
logSys.debug("Nothing to do")
|
|
||||||
return None
|
|
||||||
|
|
||||||
logSys.debug(cmd)
|
|
||||||
if not debug:
|
|
||||||
retval = os.system(cmd)
|
|
||||||
if not retval == 0:
|
|
||||||
logSys.error("'" + cmd + "' returned " + `retval`)
|
|
||||||
raise ExternalError("Execution of command '%s' failed" % cmd)
|
|
||||||
return retval
|
|
||||||
else:
|
|
||||||
return None
|
|
|
@ -16,12 +16,12 @@
|
||||||
|
|
||||||
# Author: Cyril Jaquier
|
# Author: Cyril Jaquier
|
||||||
#
|
#
|
||||||
# $Revision: 1.15 $
|
# $Revision: 446 $
|
||||||
|
|
||||||
__author__ = "Cyril Jaquier"
|
__author__ = "Cyril Jaquier"
|
||||||
__version__ = "$Revision: 1.15 $"
|
__version__ = "$Revision: 446 $"
|
||||||
__date__ = "$Date: 2006/03/15 23:07:12 $"
|
__date__ = "$Date: 2006-11-01 23:13:44 +0100 (Wed, 01 Nov 2006) $"
|
||||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||||
__license__ = "GPL"
|
__license__ = "GPL"
|
||||||
|
|
||||||
version = "0.6.1"
|
version = "0.7.4"
|
||||||
|
|
Loading…
Reference in New Issue