diff --git a/.travis.yml b/.travis.yml index e655e7c1..2d091754 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,14 @@ python: - "2.5" - "2.6" - "2.7" +before_install: + - sudo apt-get update -qq install: - - "pip install pyinotify" + - pip install pyinotify + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then sudo apt-get install -qq python-gamin; fi + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then pip install -q coveralls; fi script: - - python ./fail2ban-testcases + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PYTHONPATH="$PYTHONPATH:/usr/share/pyshared:/usr/lib/pyshared/python2.7"; fi + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coverage run --rcfile=.travis_coveragerc fail2ban-testcases; else python ./fail2ban-testcases; fi +after_success: + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi diff --git a/.travis_coveragerc b/.travis_coveragerc new file mode 100644 index 00000000..ac4a15d5 --- /dev/null +++ b/.travis_coveragerc @@ -0,0 +1,6 @@ + +[run] +branch = True +omit = + /usr/* + /home/travis/virtualenv/* diff --git a/ChangeLog b/ChangeLog index eabd6e81..b10931be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,28 +4,132 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ================================================================================ -Fail2Ban (version 0.8.8) 2012/12/06 +Fail2Ban (version 0.8.9) 2013/04/XX ================================================================================ +ver. 0.8.9 (2013/04/XX) - wanna-be-stable +---------- + +Although primarily a bugfix release, it incorporates many new +enhancements, few new features, but more importantly -- quite extended +tests battery with current 94% coverage. This release incorporates +more than a 100 of non-merge commits from 14 contributors (sorted by +number of commits): Yaroslav Halchenko, Daniel Black, Steven Hiscocks, +ArndRa, hamilton5, pigsyn, Erwan Ben Souiden, Michael Gebetsroither, +Orion Poplawski, Artur Penttinen, sebres, Nicolas Collignon, Pascal +Borreli, blotus: + +- Fixes: + Yaroslav Halchenko + * [6f4dad46] Documentation python-2.4 is the minimium version. + * [1eb23cf8] do not rely on scripts being under /usr -- might differ eg on + Fedora. Closes gh-112. Thanks to Camusensei for the bug report. + * [bf4d4af1] Changes for atomic writes. Thanks to Steven Hiscocks for + insight. Closes gh-103. + * [ab044b75] delay check for the existence of config directory until read. + * [3b4084d4] fixing up for handling of TAI64N timestamps. + * [154aa38e] do not shutdown logging until all jails stop. + * [f2156604] pyinotify -- monitor IN_MOVED_TO events. Closes gh-184. + Thanks to Jon Foster for report and troubleshooting. + Orion Poplawski + * [e4aedfdc00] pyinotify - use bitwise op on masks and do not try tracking + newly created directories. + Nicolas Collignon + * [39667ff6] Avoid leaking file descriptors. Closes gh-167. + Sergey Brester + * [b6bb2f88 and d17b4153] invalid date recognition, irregular because of + sorting template list. + Steven Hiscocks + * [7a442f07] When changing log target with python2.{4,5} handle KeyError. + Closes gh-147, gh-148. + * [b6a68f51] Fix delaction on server side. Closes gh-124. + Daniel Black + * [f0610c01] Allow more that a one word command when changing and Action via + the fail2ban-client. Closes gh-134. + * [945ad3d9] Fix dates on email actions to work in different locals. Closes + gh-70. Thanks to iGeorgeX for the idea. + blotus + * [96eb8986] ' and " should also be escaped in action tags Closes gh-109 +- New features: + Yaroslav Halchenko + * [9ba27353] Add support for jail.d/{confilefile} and fail2ban.d/{configfile} + to provide additional flexibility to system adminstrators. Thanks to + beilber for the idea. Closes gh-114. + * [3ce53e87] Add exim filter. + Erwan Ben Souiden + * [d7d5228] add nagios integration documentation and script to ensure + fail2ban is running. Closes gh-166. + Artur Penttinen + * [29d0df5] Add mysqld filter. Closes gh-152. + ArndRaphael Brandes + * [bba3fd8] Add Sogo filter. Closes gh-117. + Michael Gebetsriother + * [f9b78ba] Add action route to block at routing level. + Teodor Micu & Yaroslav Halchenko + * [5f2d383] Add roundcube auth filter. Closes Debian bug #699442. + Daniel Black + * [be06b1b] Add action for iptables-ipsets. Closes gh-102. + Soulard Morgan + * [f336d9f] Add filter for webmin. Closes gh-99. +- Enhancements: + Enrico Labedzki + * [24a8d07] Added new date format for ASSP SMTP Proxy. + Steven Hiscocks + * [3d6791f] Ensure restart of Actions after a check fails occurs + consistently. Closes gh-172. + * [MANY] Improvements to test cases, travis, and code coverage (coveralls). + * [b36835f] Add get cinfo to fail2ban-client. Closes gh-124. + * [ce3ab34] Added ability to specify PID file. + Orion Poplawski + * [ddebcab] Enhance fail2ban.service definition dependencies and Pidfile. + Closes gh-142. + Yaroslav Halchenko + * [MANY] Lots of improvements to log messages, man pages and test cases. + * [91d5736] Postfix filter improvements - empty helo, from and rcpt to. + Closes gh-126. Bug report by Michael Heuberger. + * [40c5a2d] adding more of diagnostic messages into -client while starting + the daemon. + * [8e63d4c] Compare against None with 'is' instead of '=='. + Daniel Black + * [3aeb1a9] Add jail.conf manual page. Closes gh-143. + * [MANY] man page edits. + * [7cd6dab] Added help command to fail2ban-client. + * [c8c7b0b,23bbc60] Better logging of log file read errors. + * [3665e6d] Added code coverage to development process. + * [41b9f7b,32d10e9,39750b8] More complete ssh filter rules to match openssh + source. Also include BSD changes. + * [1d9abd1] Action files can have tags in definition that refer to other + tags. + Pascal Borreli + * [a2b29b4] Fixed lots of typos in config files and documentation. + hamilton5 + * [7ede1e8] Update dovecot filter config. + Romain Riviere + * [0ac8746] Enhance named-refused filter for views. + +Special Kudos also go to Fabian Wenk, Arturo 'Buanzo' Busleiman, Tom +Hendrikx and other TBN heroes supporting users on fail2ban-users +mailing list and IRC. + ver. 0.8.8 (2012/12/06) - stable ---------- - Fixes: Alan Jenkins * [8c38907] Removed 'POSSIBLE BREAK-IN ATTEMPT' from sshd filter to avoid - banning due to misconfigured DNS. Close gh-64 + banning due to misconfigured DNS. Closes gh-64 Yaroslav Halchenko * [83109bc] IMPORTANT: escape the content of (if used in custom action files) since its value could contain arbitrary symbols. Thanks for discovery go to the NBS System security team - * [0935566,5becaf8] Various python 2.4 and 2.5 compatibility fixes. Close gh-83 + * [0935566,5becaf8] Various python 2.4 and 2.5 compatibility fixes. Closes gh-83 * [b159eab] do not enable pyinotify backend if pyinotify < 0.8.3 * [37a2e59] store IP as a base, non-unicode str to avoid spurious messages - in the console. Close gh-91 + in the console. Closes gh-91 - New features: David Engeset * [2d672d1,6288ec2] 'unbanip' command for the client + avoidance of touching - the log file to take 'banip' or 'unbanip' in effect. Close gh-81, gh-86 + the log file to take 'banip' or 'unbanip' in effect. Closes gh-81, gh-86 Yaroslav Halchenko - Enhancements: * [2d66f31] replaced uninformative "Invalid command" message with warning log diff --git a/DEVELOP b/DEVELOP index 623aee12..6ccb162e 100644 --- a/DEVELOP +++ b/DEVELOP @@ -21,6 +21,19 @@ would like to add to Fail2Ban, the best way to do so it to use the GitHub Pull Request feature. You can find more details on the Fail2Ban wiki (http://www.fail2ban.org/wiki/index.php/Get_Involved) +Pull Requests +============= + +When submitting pull requests on GitHub we ask you to: +* Clearly describe the problem you're solving; +* Don't introduce regressions that will make it hard for systems adminstrators + to update; +* If adding a major feature rebase your changes on master and get to a single commit; +* Include test cases (see below); +* Include sample logs (if relevant); +* Include a change to the relevant section of the ChangeLog; and +* Include yourself in THANKS if not already there. + Testing ======= @@ -249,10 +262,53 @@ Takes care about executing start/check/ban/unban/stop commands Releasing ========= +# Check distribution patches and see if they can be included + + * https://apps.fedoraproject.org/packages/fail2ban/sources + * http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-analyzer/fail2ban/ + * http://svnweb.freebsd.org/ports/head/security/py-fail2ban/ + * https://build.opensuse.org/package/show?package=fail2ban&project=openSUSE%3AFactory + * http://sophie.zarb.org/sources/fail2ban (Mageia) + * https://trac.macports.org/browser/trunk/dports/security/fail2ban + +# Check distribution outstanding bugs + + * https://github.com/fail2ban/fail2ban/issues?sort=updated&state=open + * http://bugs.debian.org/cgi-bin/pkgreport.cgi?dist=unstable;package=fail2ban + * http://bugs.sabayon.org/buglist.cgi?quicksearch=net-analyzer%2Ffail2ban + * https://bugs.gentoo.org/buglist.cgi?query_format=advanced&short_desc=fail2ban&bug_status=UNCONFIRMED&bug_status=CONFIRMED&bug_status=IN_PROGRESS&short_desc_type=allwords + * https://bugzilla.redhat.com/buglist.cgi?query_format=advanced&bug_status=NEW&bug_status=ASSIGNED&component=fail2ban&classification=Red%20Hat&classification=Fedora + * http://www.freebsd.org/cgi/query-pr-summary.cgi?text=fail2ban + +# Provide a release sample to distributors + + * Debian: Yaroslav Halchenko + http://packages.qa.debian.org/f/fail2ban.html + * FreeBSD: Christoph Theis theis@gmx.at>, Nick Hilliard + http://svnweb.freebsd.org/ports/head/security/py-fail2ban/Makefile?view=markup + * Fedora: Axel Thimm + https://apps.fedoraproject.org/packages/fail2ban + * Gentoo: netmon@gentoo.org + http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-analyzer/fail2ban/metadata.xml?view=markup + * openSUSE: Stephan Kulow + https://build.opensuse.org/package/users?package=fail2ban&project=openSUSE%3AFactory + * Mac Ports: @Malbrouck on github (gh-49) + https://trac.macports.org/browser/trunk/dports/security/fail2ban/Portfile + +# Wait for feedback from distributors + # Ensure the version is correct in ./common/version.py # Add/finalize the corresponding entry in the ChangeLog + To generate a list of committers use e.g. + + git shortlog -sn 0.8.8.. | sed -e 's,^[ 0-9\t]*,,g' | tr '\n' '\|' | sed -e 's:|:, :g' + + Ensure the top of the ChangeLog has the right version and current date. + + Ensure the top entry of the ChangeLog has the right version and current date. + # Update man pages (cd man ; ./generate-man ) @@ -275,4 +331,15 @@ Releasing # Email users and development list of release -TODO notifying distributors etc. +# notify distributors + +Post Release +============ + +Add the following to the top of the ChangeLog + +ver. 0.8.9 (2013/XX/XXX) - wanna-be-stable +- Fixes +- New Features +- Enhancements + diff --git a/MANIFEST b/MANIFEST index 28063b83..5eb71080 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,4 +1,4 @@ -README +README.md ChangeLog TODO THANKS @@ -139,6 +139,7 @@ files/macosx-initd files/solaris-fail2ban.xml files/solaris-svc-fail2ban files/suse-initd +files/fail2ban-logrotate files/cacti/fail2ban_stats.sh files/cacti/cacti_host_template_fail2ban.xml files/cacti/README diff --git a/README b/README deleted file mode 100644 index e926509e..00000000 --- a/README +++ /dev/null @@ -1,95 +0,0 @@ - __ _ _ ___ _ - / _|__ _(_) |_ ) |__ __ _ _ _ - | _/ _` | | |/ /| '_ \/ _` | ' \ - |_| \__,_|_|_/___|_.__/\__,_|_||_| - -================================================================================ -Fail2Ban (version 0.8.8) 2012/07/31 -================================================================================ - -Fail2Ban scans log files like /var/log/pwdfail and bans IP that makes too many -password failures. It updates firewall rules to reject the IP address. These -rules can be defined by the user. Fail2Ban can read multiple log files such as -sshd or Apache web server ones. - -This README is a quick introduction to Fail2ban. More documentation, FAQ, HOWTOs -are available on the project website: http://www.fail2ban.org - -Installation: -------------- - -Required: - >=python-2.3 (http://www.python.org) - -Optional: - pyinotify: - >=linux-2.6.13 - >=python-2.4 - >=pyinotify-0.8.3 (https://github.com/seb-m/pyinotify) - Gamin: - >=gamin-0.0.21 (http://www.gnome.org/~veillard/gamin) - -To install, just do: - -> tar xvfj fail2ban-0.8.8.tar.bz2 -> cd fail2ban-0.8.8 -> python setup.py install - -This will install Fail2Ban into /usr/share/fail2ban. The executable scripts are -placed into /usr/bin. - -It is possible that Fail2ban is already packaged for your distribution. In this -case, you should use it. - -Fail2Ban should be correctly installed now. Just type: - -> fail2ban-client -h - -to see if everything is alright. You should always use fail2ban-client and never -call fail2ban-server directly. - -Configuration: --------------- - -You can configure Fail2ban using the files in /etc/fail2ban. It is possible to -configure the server using commands sent to it by fail2ban-client. The available -commands are described in the man page of fail2ban-client. Please refer to it or -to the website: http://www.fail2ban.org - -Contact: --------- - -Website: http://www.fail2ban.org - -You need some new features, you found bugs: visit -https://github.com/fail2ban/fail2ban/issues -and if your issue is not yet known -- file a bug report. - -If you would like to troubleshoot or discuss: join the mailing list -https://lists.sourceforge.net/lists/listinfo/fail2ban-users - -If you just appreciate this program: send kudos to the original author -(Cyril Jaquier: ) or the mailing list -https://lists.sourceforge.net/lists/listinfo/fail2ban-users - - -Thanks: -------- - -See THANKS file. - -License: --------- - -Fail2Ban is free software; you can redistribute it and/or modify it under the -terms of the GNU General Public License as published by the Free Software -Foundation; either version 2 of the License, or (at your option) any later -version. - -Fail2Ban is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -Fail2Ban; if not, write to the Free Software Foundation, Inc., 51 Franklin -Street, Fifth Floor, Boston, MA 02110, USA diff --git a/README.Solaris b/README.Solaris new file mode 100644 index 00000000..49056062 --- /dev/null +++ b/README.Solaris @@ -0,0 +1,141 @@ +# vim:tw=80:ft=txt + +README FOR SOLARIS INSTALLATIONS + +By Roy Sigurd Karlsbakk + +ABOUT + +This readme is meant for those wanting to install fail2ban on Solaris 10, +OpenSolaris, OpenIndiana etc. To some degree it may as well be useful for +users of older Solaris versions and Nexenta, but don't rely on it. + +READ ME FIRST + +If I use the term Solaris, I am talking about any Solaris dialect, that is, the +official Sun/Oracle ones or derivates. If I describe an OS as +"OpenSolaris-based", it means it's either OpenSolaris, OpenIndiana or one of the +other, but /not/ the Nexenta family, since this only uses the OpenSolaris/ +IllumOS kernel and not the userland. If I say Solaris 10, I mean Solaris 10 and +perhaps, if you're lucky and have some good gods on your side, it may also apply +to Solaris 9 or even 8 and hopefully in the new Solaris 11 whenever that may be +released. Quoted lines of code, settings et cetera are indented with two spaces. +This does _not_ mean you should use that indentation, especially in config files +where they can be harmful. Optional settings are prefixed with OPT: while +required settings are prefixed with REQ:. If no prefix is found, regard it as a +required setting. + +INSTALLATION ON SOLARIS + +The installation is straight forward on Solaris as well as on linux/bsd/etc. +./setup.py install installs the general packages in /usr/bin on OpenSolaris- +based distros or (at least on this box) under /usr/sfw/bin on Solaris 10. In +the files/ directory you will find the file solaris-fail2ban.xml containing the +Solaris service. To install this, run the following command as root (or with +sudo): + + svccfg import files/solaris-fail2ban.xml + +This should normally without giving an error. If you get an error, deal with it, +and please post any relevant info (or fixes?) to the fail2ban mailing list. +Next install the service handler - copy the script in and allow it to be executed: + + cp files/solaris-svc-fail2ban /lib/svc/method/svc-fail2ban + chmod +x /lib/svc/method/svc-fail2ban + +CONFIGURE SYSLOG + +For some reason, a default Solaris installation does not log ssh login attempts, +and since fail2ban works by monitoring logs, enabling this logging is rather +important for it to work. To enable this, edit /etc/syslog.conf and add a line +at the end: + + auth.info /var/adm/auth.log + +Save the file and exit, and run + + touch /var/adm/auth.log + +The Solaris system logger will _not_ create a non-existing file. Now, restart +the system logger. + + svcadm restart system-log + +Try to ssh into localhost with ssh asdf@localhost and enter an invalid password. +Make sure this is logged in the above file. When done, you may configure +fail2ban. + +FAIL2BAN CONFIGURATION + +OPT: Create /etc/fail2ban/fail2ban.local containing: + +# Fail2Ban main configuration file +# +# Comments: use '#' for comment lines and ';' for inline comments +# +# Changes: in most of the cases you should not modify this +# file, but provide customizations in fail2ban.local file, e.g.: +# +# [Definition] +# loglevel = 4 +# +[Definition] + +# Option: logtarget +# Notes.: Set the log target. This could be a file, SYSLOG, STDERR or STDOUT. +# Only one log target can be specified. +# If you change logtarget from the default value and you are +# using logrotate -- also adjust or disable rotation in the +# corresponding configuration file +# (e.g. /etc/logrotate.d/fail2ban on Debian systems) +# Values: STDOUT STDERR SYSLOG file Default: /var/log/fail2ban.log +# +logtarget = /var/adm/fail2ban.log + + +REQ: Create /etc/fail2ban/jail.local containing: + +[ssh-tcpwrapper] + +enabled = true +filter = sshd +action = hostsdeny + sendmail-whois[name=SSH, dest=you@example.com] +ignoreregex = for myuser from +logpath = /var/adm/auth.log + +Set the sendmail dest address to something useful or drop the line to stop it spamming you. +Set 'myuser' to your username to avoid banning yourself or drop it. + +START (OR RESTART) FAIL2BAN + +Enable the fail2ban service with + + svcadm enable fail2ban + +When done, check that all services are running well + + svcs -xv + +GOTCHAS AND FIXMES + +* It seems the installation may be starting fail2ban automatically. If this is + done, fail2ban will not start, but no errors will be returned from svcs + (above). Check if it's running with 'ps -ef | grep fail2ban' and manually kill + the PID if it is. Re-enable fail2ban and try again + + svcadm disable fail2ban + svcadm enable fail2ban + +* If svcs -xv says that fail2ban failed to start or svcs says it's in maintenance mode + chcek /var/svc/log/network-fail2ban:default.log for clues. + Check permissions on /var/adm, /var/adm/auth.log /var/adm/fail2ban.log and /var/run/fail2ban + You may need to: + + sudo mkdir /var/run/fail2ban + +* Fail2ban adds lines like these to /etc/hosts.deny: + + ALL: 1.2.3.4 + + wouldn't it be better to just block sshd? diff --git a/README.md b/README.md new file mode 100644 index 00000000..04f8b349 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ + __ _ _ ___ _ + / _|__ _(_) |_ ) |__ __ _ _ _ + | _/ _` | | |/ /| '_ \/ _` | ' \ + |_| \__,_|_|_/___|_.__/\__,_|_||_| + v0.8.8 2012/07/31 + +## Fail2Ban: ban hosts that cause multiple authentication errors + +Fail2Ban scans log files like /var/log/pwdfail and bans IP that makes too many +password failures. It updates firewall rules to reject the IP address. These +rules can be defined by the user. Fail2Ban can read multiple log files such as +sshd or Apache web server ones. + +This README is a quick introduction to Fail2ban. More documentation, FAQ, HOWTOs +are available in fail2ban(1) manpage and on the website http://www.fail2ban.org + +Installation: +------------- + +**It is possible that Fail2ban is already packaged for your distribution. In +this case, you should use it instead.** + +Required: +- [Python >= 2.4](http://www.python.org) + +Optional: +- [pyinotify >= 0.8.3](https://github.com/seb-m/pyinotify) + - Linux >= 2.6.13 +- [gamin >= 0.0.21](http://www.gnome.org/~veillard/gamin) + +To install, just do: + + tar xvfj fail2ban-0.8.8.tar.bz2 + cd fail2ban-0.8.8 + python setup.py install + +This will install Fail2Ban into /usr/share/fail2ban. The executable scripts are +placed into /usr/bin, and configuration under /etc/fail2ban. + +Fail2Ban should be correctly installed now. Just type: + + fail2ban-client -h + +to see if everything is alright. You should always use fail2ban-client and +never call fail2ban-server directly. + +Configuration: +-------------- + +You can configure Fail2Ban using the files in /etc/fail2ban. It is possible to +configure the server using commands sent to it by fail2ban-client. The +available commands are described in the fail2ban-client(1) manpage. Also see +fail2ban(1) manpage for further references and find even more documentation on +the website: http://www.fail2ban.org + +Code status: +------------ + +* [![tests status](https://secure.travis-ci.org/fail2ban/fail2ban.png)](https://travis-ci.org/fail2ban/fail2ban) travis-ci.org (master branch) + +* [![Coverage Status](https://coveralls.io/repos/fail2ban/fail2ban/badge.png?branch=master)](https://coveralls.io/r/fail2ban/fail2ban) + +Contact: +-------- + +### You need some new features, you found bugs? +visit [Issues](https://github.com/fail2ban/fail2ban/issues) +and if your issue is not yet known -- file a bug report. + +### You would like to troubleshoot or discuss? +join the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users) + +### You would like to contribute (new filters/actions/code/documentation)? +send a pull request + +### You just appreciate this program: +send kudos to the original author ([Cyril Jaquier](mailto: Cyril Jaquier ) +or better to the [mailing list](https://lists.sourceforge.net/lists/listinfo/fail2ban-users) +since Fail2Ban is "community-driven" for years now. + +Thanks: +------- + +See [THANKS](https://github.com/fail2ban/fail2ban/blob/master/THANKS) file. + +License: +-------- + +Fail2Ban is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +Fail2Ban is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +Fail2Ban; if not, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110, USA diff --git a/THANKS b/THANKS index f207d71e..9545d43a 100644 --- a/THANKS +++ b/THANKS @@ -16,6 +16,7 @@ Daniel B. Cid Daniel Black David Nutter Eric Gerbier +Enrico Labedzki Guillaume Delvit Hanno 'Rince' Wagner Iain Lea diff --git a/TODO b/TODO index 61bdc093..33263d3e 100644 --- a/TODO +++ b/TODO @@ -13,6 +13,8 @@ Legend: # partially done * done +- more detailed explaination in DEVELOP for new developers (eg. howto build this HEX numbers in ChangeLog) + - Run tests though all filters/examples files - (see sshd example file) as unit test diff --git a/client/__init__.py b/client/__init__.py index c448827b..3de9058c 100644 --- a/client/__init__.py +++ b/client/__init__.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/client/actionreader.py b/client/actionreader.py index 9ad1ef28..c1a64245 100644 --- a/client/actionreader.py +++ b/client/actionreader.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/client/beautifier.py b/client/beautifier.py index 7e48016c..8e690656 100644 --- a/client/beautifier.py +++ b/client/beautifier.py @@ -56,10 +56,10 @@ class Beautifier: msg = "Jail started" elif inC[0] == "stop": if len(inC) == 1: - if response == None: + if response is None: msg = "Shutdown successful" else: - if response == None: + if response is None: msg = "Jail stopped" elif inC[0] == "add": msg = "Added jail " + response diff --git a/client/configparserinc.py b/client/configparserinc.py index 7ac8b4a5..df5af1ac 100644 --- a/client/configparserinc.py +++ b/client/configparserinc.py @@ -19,7 +19,6 @@ # Author: Yaroslav Halchenko # Modified: Cyril Jaquier -# $Revision$ __author__ = 'Yaroslav Halhenko' __revision__ = '$Revision$' diff --git a/client/configreader.py b/client/configreader.py index 9028df48..9fb6b3eb 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # Modified by: Yaroslav Halchenko (SafeConfigParserWithIncludes) -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" @@ -46,15 +45,15 @@ class ConfigReader(SafeConfigParserWithIncludes): def setBaseDir(self, basedir): if basedir is None: basedir = ConfigReader.DEFAULT_BASEDIR # stock system location - if not (os.path.exists(basedir) and os.access(basedir, os.R_OK | os.X_OK)): - raise ValueError("Base configuration directory %s either does not exist " - "or is not accessible" % basedir) self._basedir = basedir.rstrip('/') def getBaseDir(self): return self._basedir def read(self, filename): + if not os.path.exists(self._basedir): + raise ValueError("Base configuration directory %s does not exist " + % self._basedir) basename = os.path.join(self._basedir, filename) logSys.debug("Reading configs for %s under %s " % (basename, self._basedir)) config_files = [ basename + ".conf", @@ -65,27 +64,20 @@ class ConfigReader(SafeConfigParserWithIncludes): # possible further customizations under a .conf.d directory config_dir = basename + '.d' - if os.path.exists(config_dir): - if os.path.isdir(config_dir) and os.access(config_dir, os.X_OK | os.R_OK): - # files must carry .conf suffix as well - config_files += sorted(glob.glob('%s/*.conf' % config_dir)) - else: - logSys.warn("%s exists but not a directory or not accessible" - % config_dir) + config_files += sorted(glob.glob('%s/*.conf' % config_dir)) - # check if files are accessible, warn if any is not accessible - # and remove it from the list - config_files_accessible = [] - for f in config_files: - if os.access(f, os.R_OK): - config_files_accessible.append(f) - else: - logSys.warn("%s exists but not accessible - skipping" % f) - - if len(config_files_accessible): + if len(config_files): # at least one config exists and accessible - SafeConfigParserWithIncludes.read(self, config_files_accessible) - return True + logSys.debug("Reading config files: " + ', '.join(config_files)) + config_files_read = SafeConfigParserWithIncludes.read(self, config_files) + missed = [ cf for cf in config_files if cf not in config_files_read ] + if missed: + logSys.error("Could not read config files: " + ', '.join(missed)) + if config_files_read: + return True + logSys.error("Found no accessible config files for %r under %s" % + ( filename, self.getBaseDir() )) + return False else: logSys.error("Found no accessible config files for %r " % filename + (["under %s" % self.getBaseDir(), @@ -113,7 +105,7 @@ class ConfigReader(SafeConfigParserWithIncludes): v = self.getint(sec, option[1]) else: v = self.get(sec, option[1]) - if not pOptions == None and option[1] in pOptions: + if not pOptions is None and option[1] in pOptions: continue values[option[1]] = v except NoSectionError, e: @@ -121,7 +113,7 @@ class ConfigReader(SafeConfigParserWithIncludes): logSys.error(e) values[option[1]] = option[2] except NoOptionError: - if not option[2] == None: + if not option[2] is None: logSys.warn("'%s' not defined in '%s'. Using default one: %r" % (option[1], sec, option[2])) values[option[1]] = option[2] diff --git a/client/configurator.py b/client/configurator.py index 2097fd54..fc588558 100644 --- a/client/configurator.py +++ b/client/configurator.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/client/csocket.py b/client/csocket.py index 6e014e23..c6f318f2 100644 --- a/client/csocket.py +++ b/client/csocket.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/client/fail2banreader.py b/client/fail2banreader.py index c8f42976..026076fa 100644 --- a/client/fail2banreader.py +++ b/client/fail2banreader.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/client/filterreader.py b/client/filterreader.py index 7dba3579..b8c47558 100644 --- a/client/filterreader.py +++ b/client/filterreader.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/client/jailreader.py b/client/jailreader.py index ad69bfa0..d6f678ee 100644 --- a/client/jailreader.py +++ b/client/jailreader.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/client/jailsreader.py b/client/jailsreader.py index 91e178d6..f87794dd 100644 --- a/client/jailsreader.py +++ b/client/jailsreader.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/common/__init__.py b/common/__init__.py index c448827b..3de9058c 100644 --- a/common/__init__.py +++ b/common/__init__.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/common/helpers.py b/common/helpers.py index 6115b971..3c830138 100644 --- a/common/helpers.py +++ b/common/helpers.py @@ -20,7 +20,6 @@ # Author: Cyril Jaquier # Author: Arturo 'Buanzo' Busleiman # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/common/protocol.py b/common/protocol.py index 1083a94b..ccd44398 100644 --- a/common/protocol.py +++ b/common/protocol.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/common/version.py b/common/version.py index 2a1c0d0b..df3b97c3 100644 --- a/common/version.py +++ b/common/version.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier, Yaroslav Halchenko" __copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" diff --git a/config/action.d/dshield.conf b/config/action.d/dshield.conf index 151db28f..ca68e638 100644 --- a/config/action.d/dshield.conf +++ b/config/action.d/dshield.conf @@ -25,7 +25,6 @@ # configured at DShield), and // (to # configure how often the buffer is flushed). # -# $Revision$ [Definition] diff --git a/config/action.d/dummy.conf b/config/action.d/dummy.conf index ea59881a..20507c0b 100644 --- a/config/action.d/dummy.conf +++ b/config/action.d/dummy.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/action.d/hostsdeny.conf b/config/action.d/hostsdeny.conf index b04f2adb..50a4545c 100644 --- a/config/action.d/hostsdeny.conf +++ b/config/action.d/hostsdeny.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] @@ -40,7 +39,7 @@ actionban = IP= && # Tags: See jail.conf(5) man page # Values: CMD # -actionunban = IP= && sed -i.old /ALL:\ $IP/d +actionunban = IP= && sed /ALL:\ $IP/d > .new && mv .new [Init] diff --git a/config/action.d/ipfw.conf b/config/action.d/ipfw.conf index 62612307..3a56415d 100644 --- a/config/action.d/ipfw.conf +++ b/config/action.d/ipfw.conf @@ -3,7 +3,6 @@ # Author: Nick Munger # Modified by: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/action.d/iptables-allports.conf b/config/action.d/iptables-allports.conf index a02ba63d..50f49d82 100644 --- a/config/action.d/iptables-allports.conf +++ b/config/action.d/iptables-allports.conf @@ -4,7 +4,6 @@ # Modified: Yaroslav O. Halchenko # made active on all ports from original iptables.conf # -# $Revision$ # [Definition] diff --git a/config/action.d/iptables-multiport-log.conf b/config/action.d/iptables-multiport-log.conf index 49958013..b79983fc 100644 --- a/config/action.d/iptables-multiport-log.conf +++ b/config/action.d/iptables-multiport-log.conf @@ -7,7 +7,6 @@ # make "fail2ban--log" chain to log and drop # insert a jump to fail2ban- from -I if proto/port match # -# $Revision$ # [Definition] diff --git a/config/action.d/iptables-multiport.conf b/config/action.d/iptables-multiport.conf index ab0ee8de..f799289f 100644 --- a/config/action.d/iptables-multiport.conf +++ b/config/action.d/iptables-multiport.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # Modified by Yaroslav Halchenko for multiport banning -# $Revision$ # [Definition] diff --git a/config/action.d/iptables-new.conf b/config/action.d/iptables-new.conf index 12f398c7..f22916e1 100644 --- a/config/action.d/iptables-new.conf +++ b/config/action.d/iptables-new.conf @@ -4,7 +4,6 @@ # Copied from iptables.conf and modified by Yaroslav Halchenko # to fullfill the needs of bugreporter dbts#350746. # -# $Revision$ # [Definition] diff --git a/config/action.d/iptables-xt_recent-echo.conf b/config/action.d/iptables-xt_recent-echo.conf index 887311be..4fb397a2 100644 --- a/config/action.d/iptables-xt_recent-echo.conf +++ b/config/action.d/iptables-xt_recent-echo.conf @@ -2,7 +2,6 @@ # # Author: Zbigniew Jędrzejewski-Szmek # -# $Revision: 1 $ # [Definition] diff --git a/config/action.d/iptables.conf b/config/action.d/iptables.conf index a3412f6b..662b41c0 100644 --- a/config/action.d/iptables.conf +++ b/config/action.d/iptables.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/action.d/mail-buffered.conf b/config/action.d/mail-buffered.conf index 94a60e3b..7ff17cf2 100644 --- a/config/action.d/mail-buffered.conf +++ b/config/action.d/mail-buffered.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/action.d/mail-whois-lines.conf b/config/action.d/mail-whois-lines.conf index 2120432e..d30e266d 100644 --- a/config/action.d/mail-whois-lines.conf +++ b/config/action.d/mail-whois-lines.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # Modified-By: Yaroslav Halchenko to include grepping on IP over log files -# $Revision$ # [Definition] diff --git a/config/action.d/mail-whois.conf b/config/action.d/mail-whois.conf index 3293c7a2..f58ae535 100644 --- a/config/action.d/mail-whois.conf +++ b/config/action.d/mail-whois.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/action.d/mail.conf b/config/action.d/mail.conf index f9942e10..f9a54979 100644 --- a/config/action.d/mail.conf +++ b/config/action.d/mail.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/action.d/mynetwatchman.conf b/config/action.d/mynetwatchman.conf index 06f16db6..5245a4e3 100644 --- a/config/action.d/mynetwatchman.conf +++ b/config/action.d/mynetwatchman.conf @@ -24,7 +24,6 @@ # Another useful configuration value is , if you don't have wget # installed (an example config for curl is given below) # -# $Revision$ [Definition] diff --git a/config/action.d/sendmail-buffered.conf b/config/action.d/sendmail-buffered.conf index ce4479e8..bec1e91c 100644 --- a/config/action.d/sendmail-buffered.conf +++ b/config/action.d/sendmail-buffered.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/action.d/sendmail-whois-lines.conf b/config/action.d/sendmail-whois-lines.conf index a4b53b97..bc5074c6 100644 --- a/config/action.d/sendmail-whois-lines.conf +++ b/config/action.d/sendmail-whois-lines.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] @@ -12,7 +11,7 @@ # Values: CMD # actionstart = printf %%b "Subject: [Fail2Ban] : started - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -25,7 +24,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] : started # Values: CMD # actionstop = printf %%b "Subject: [Fail2Ban] : stopped - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -46,7 +45,7 @@ actioncheck = # Values: CMD # actionban = printf %%b "Subject: [Fail2Ban] : banned - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n diff --git a/config/action.d/sendmail-whois.conf b/config/action.d/sendmail-whois.conf index f63fd36f..0d1fd97e 100644 --- a/config/action.d/sendmail-whois.conf +++ b/config/action.d/sendmail-whois.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] @@ -12,7 +11,7 @@ # Values: CMD # actionstart = printf %%b "Subject: [Fail2Ban] : started - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -25,7 +24,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] : started # Values: CMD # actionstop = printf %%b "Subject: [Fail2Ban] : stopped - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -46,7 +45,7 @@ actioncheck = # Values: CMD # actionban = printf %%b "Subject: [Fail2Ban] : banned - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n diff --git a/config/action.d/sendmail.conf b/config/action.d/sendmail.conf index 569b3bfa..8054050d 100644 --- a/config/action.d/sendmail.conf +++ b/config/action.d/sendmail.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] @@ -12,7 +11,7 @@ # Values: CMD # actionstart = printf %%b "Subject: [Fail2Ban] : started - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -25,7 +24,7 @@ actionstart = printf %%b "Subject: [Fail2Ban] : started # Values: CMD # actionstop = printf %%b "Subject: [Fail2Ban] : stopped - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n @@ -46,7 +45,7 @@ actioncheck = # Values: CMD # actionban = printf %%b "Subject: [Fail2Ban] : banned - Date: `date -u +"%%a, %%d %%h %%Y %%T +0000"` + Date: `LC_TIME=C date -u +"%%a, %%d %%h %%Y %%T +0000"` From: Fail2Ban <> To: \n Hi,\n diff --git a/config/action.d/shorewall.conf b/config/action.d/shorewall.conf index aca3a256..31c1dce5 100644 --- a/config/action.d/shorewall.conf +++ b/config/action.d/shorewall.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # # The default Shorewall configuration is with "BLACKLISTNEWONLY=Yes" (see # file /etc/shorewall/shorewall.conf). This means that when Fail2ban adds a diff --git a/config/fail2ban.conf b/config/fail2ban.conf index e759513b..1888eddb 100644 --- a/config/fail2ban.conf +++ b/config/fail2ban.conf @@ -43,7 +43,7 @@ socket = /var/run/fail2ban/fail2ban.sock # Option: pidfile # Notes.: Set the PID file. This is used to store the process ID of the # fail2ban server. -# Values: FILE Default: /var/run/fail2ban/fail2ban.sock +# Values: FILE Default: /var/run/fail2ban/fail2ban.pid # pidfile = /var/run/fail2ban/fail2ban.pid diff --git a/config/filter.d/apache-auth.conf b/config/filter.d/apache-auth.conf index 962fb2e3..66f6a1d6 100644 --- a/config/filter.d/apache-auth.conf +++ b/config/filter.d/apache-auth.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/apache-badbots.conf b/config/filter.d/apache-badbots.conf index 1c60676d..f9c79472 100644 --- a/config/filter.d/apache-badbots.conf +++ b/config/filter.d/apache-badbots.conf @@ -5,7 +5,6 @@ # # Author: Yaroslav Halchenko # -# $Revision$ # [Definition] diff --git a/config/filter.d/apache-nohome.conf b/config/filter.d/apache-nohome.conf index b6a00005..6e738c68 100644 --- a/config/filter.d/apache-nohome.conf +++ b/config/filter.d/apache-nohome.conf @@ -2,7 +2,6 @@ # # Author: Yaroslav O. Halchenko # -# $Revision$ # [Definition] diff --git a/config/filter.d/apache-noscript.conf b/config/filter.d/apache-noscript.conf index 4746fbfb..5b48cb32 100644 --- a/config/filter.d/apache-noscript.conf +++ b/config/filter.d/apache-noscript.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/apache-overflows.conf b/config/filter.d/apache-overflows.conf index 4567f7da..e25b79a4 100644 --- a/config/filter.d/apache-overflows.conf +++ b/config/filter.d/apache-overflows.conf @@ -2,7 +2,6 @@ # # Author: Tim Connors # -# $Revision$ # [Definition] diff --git a/config/filter.d/assp.conf b/config/filter.d/assp.conf new file mode 100644 index 00000000..b1bfc082 --- /dev/null +++ b/config/filter.d/assp.conf @@ -0,0 +1,33 @@ +# Fail2Ban configuration file +# for Anti-Spam SMTP Proxy Server also known as ASSP +# Honmepage: http://www.magicvillage.de/~Fritz_Borgstedt/assp/0003D91C-8000001C/ +# ProjektSite: http://sourceforge.net/projects/assp/?source=directory +# +# Author: Enrico Labedzki (enrico.labedzki@deiwos.de) +# + +[Definition] + +# Option: failregex +# Notes.: regex to match the SMTP failure messages in the logfile. The +# host must be matched by a group named "host". The tag "" can +# be used for standard IP/hostname matching and is only an alias for +# (?:::f{4,6}:)?(?P\S+) +# Values: TEXT +# +# Examples: Apr-27-13 02:33:09 Blocking 217.194.197.97 - too much AUTH errors (41); +# Dec-29-12 17:10:31 [SSL-out] 200.247.87.82 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol; +# Dec-30-12 04:01:47 [SSL-out] 81.82.232.66 max sender authentication errors (5) exceeded +__assp_actions = (dropping|refusing) + +failregex = max sender authentication errors \(\d{,3}\) exceeded -- %(__assp_actions)s connection - after reply: \d{3} \d{1}\.\d{1}.\d{1} Error: authentication failed: [a-zA-Z0-9]+;$ + SSL negotiation with client failed: SSL accept attempt failed with unknown error.*:unknown protocol;$ + Blocking - too much AUTH errors \(\d{,3}\);$ + + +# Option: ignoreregex +# Notes.: regex to ignore. If this regex matches, the line is ignored. +# Values: TEXT +# +ignoreregex = + diff --git a/config/filter.d/asterisk.conf b/config/filter.d/asterisk.conf index 73947a67..9ed69804 100644 --- a/config/filter.d/asterisk.conf +++ b/config/filter.d/asterisk.conf @@ -2,7 +2,6 @@ # # Author: Xavier Devlamynck # -# $Revision$ # diff --git a/config/filter.d/common.conf b/config/filter.d/common.conf index 18bf41c5..66b004c6 100644 --- a/config/filter.d/common.conf +++ b/config/filter.d/common.conf @@ -3,7 +3,6 @@ # # Author: Yaroslav Halchenko # -# $Revision$ # [INCLUDES] @@ -28,6 +27,10 @@ __pid_re = (?:\[\d+\]) # EXAMPLES: pam_rhosts_auth, [sshd], pop(pam_unix) __daemon_re = [\[\(]?%(_daemon)s(?:\(\S+\))?[\]\)]?:? +# extra daemon info +# EXAMPLE: [ID 800047 auth.info] +__daemon_extra_re = (?:\[ID \d+ \S+\]) + # Combinations of daemon name and PID # EXAMPLES: sshd[31607], pop(pam_unix)[4920] __daemon_combs_re = (?:%(__pid_re)s?:\s+%(__daemon_re)s|%(__daemon_re)s%(__pid_re)s?:) @@ -43,5 +46,5 @@ __hostname = \S+ # # [hostname] [vserver tag] daemon_id spaces # this can be optional (for instance if we match named native log files) -__prefix_line = \s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s* +__prefix_line = \s*(?:%(__hostname)s )?(?:%(__kernel_prefix)s )?(?:@vserver_\S+ )?%(__daemon_combs_re)s?\s%(__daemon_extra_re)s?\s* diff --git a/config/filter.d/courierlogin.conf b/config/filter.d/courierlogin.conf index b8710ac3..20731e5d 100644 --- a/config/filter.d/courierlogin.conf +++ b/config/filter.d/courierlogin.conf @@ -3,7 +3,6 @@ # Author: Christoph Haas # Modified by: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/couriersmtp.conf b/config/filter.d/couriersmtp.conf index f0d696ff..6c0cf5ff 100644 --- a/config/filter.d/couriersmtp.conf +++ b/config/filter.d/couriersmtp.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/cyrus-imap.conf b/config/filter.d/cyrus-imap.conf index 3a8734ee..758f75de 100644 --- a/config/filter.d/cyrus-imap.conf +++ b/config/filter.d/cyrus-imap.conf @@ -2,7 +2,6 @@ # # Author: Jan Wagner # -# $Revision$ # [Definition] diff --git a/config/filter.d/dovecot.conf b/config/filter.d/dovecot.conf index 18451e42..d7fb6e6d 100644 --- a/config/filter.d/dovecot.conf +++ b/config/filter.d/dovecot.conf @@ -2,7 +2,6 @@ # # Author: Martin Waschbuesch # -# $Revision$ # [Definition] diff --git a/config/filter.d/dropbear.conf b/config/filter.d/dropbear.conf index 1309cc41..c822d08c 100644 --- a/config/filter.d/dropbear.conf +++ b/config/filter.d/dropbear.conf @@ -3,7 +3,6 @@ # Author: Francis Russell # Zak B. Elep # -# $Revision$ # # More information: http://bugs.debian.org/546913 diff --git a/config/filter.d/exim.conf b/config/filter.d/exim.conf index 8bf4fc5f..b846e992 100644 --- a/config/filter.d/exim.conf +++ b/config/filter.d/exim.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/gssftpd.conf b/config/filter.d/gssftpd.conf index 8c166309..e6c2e84a 100644 --- a/config/filter.d/gssftpd.conf +++ b/config/filter.d/gssftpd.conf @@ -2,7 +2,6 @@ # # Author: Kevin Zembower (copied from wsftpd.conf) # -# $Revision$ # [Definition] diff --git a/config/filter.d/mysqld-auth.conf b/config/filter.d/mysqld-auth.conf new file mode 100644 index 00000000..197c8232 --- /dev/null +++ b/config/filter.d/mysqld-auth.conf @@ -0,0 +1,31 @@ +# Fail2Ban configuration file for unsuccesfull MySQL authentication attempts +# +# Authors: Artur Penttinen +# Yaroslav O. Halchenko +# + +[INCLUDES] + +# Read common prefixes. If any customizations available -- read them from +# common.local +before = common.conf + + +[Definition] + +#_daemon = mysqld + +# Option: failregex +# Notes.: regex to match the password failures messages in the logfile. The +# host must be matched by a group named "host". The tag "" can +# be used for standard IP/hostname matching and is only an alias for +# (?:::f{4,6}:)?(?P[\w\-.^_]+) +# Values: TEXT +# 130322 11:26:54 [Warning] Access denied for user 'root'@'127.0.0.1' (using password: YES) +failregex = Access denied for user '\w+'@'' (to database '[^']*'|\(using password: (YES|NO)\))*\s*$ + +# Option: ignoreregex +# Notes.: regex to ignore. If this regex matches, the line is ignored. +# Values: TEXT +# +ignoreregex = diff --git a/config/filter.d/named-refused.conf b/config/filter.d/named-refused.conf index ebf7681a..1cdc626e 100644 --- a/config/filter.d/named-refused.conf +++ b/config/filter.d/named-refused.conf @@ -4,7 +4,6 @@ # # Author: Yaroslav Halchenko # -# $Revision$ # [Definition] @@ -26,7 +25,7 @@ __line_prefix=(?:\s\S+ %(__daemon_combs_re)s\s+)? # Notes.: regex to match the password failures messages in the logfile. # Values: TEXT # -failregex = %(__line_prefix)sclient #.+: query(?: \(cache\))? '.*' denied\s*$ +failregex = %(__line_prefix)sclient #\S+: (view (internal|external): )?query(?: \(cache\))? '.*' denied\s*$ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. diff --git a/config/filter.d/pam-generic.conf b/config/filter.d/pam-generic.conf index 702f8ab0..eaeb122f 100644 --- a/config/filter.d/pam-generic.conf +++ b/config/filter.d/pam-generic.conf @@ -2,7 +2,6 @@ # # Author: Yaroslav Halchenko # -# $Revision$ # [Definition] diff --git a/config/filter.d/postfix.conf b/config/filter.d/postfix.conf index d2dc4a0c..f92c3619 100644 --- a/config/filter.d/postfix.conf +++ b/config/filter.d/postfix.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/proftpd.conf b/config/filter.d/proftpd.conf index 55a15da9..f28e2d4b 100644 --- a/config/filter.d/proftpd.conf +++ b/config/filter.d/proftpd.conf @@ -2,7 +2,6 @@ # # Author: Yaroslav Halchenko # -# $Revision$ # [Definition] diff --git a/config/filter.d/pure-ftpd.conf b/config/filter.d/pure-ftpd.conf index 8066ae00..de46461d 100644 --- a/config/filter.d/pure-ftpd.conf +++ b/config/filter.d/pure-ftpd.conf @@ -3,7 +3,6 @@ # Author: Cyril Jaquier # Modified: Yaroslav Halchenko for pure-ftpd # -# $Revision$ # [Definition] diff --git a/config/filter.d/qmail.conf b/config/filter.d/qmail.conf index 4d7acd6f..04feb2b7 100644 --- a/config/filter.d/qmail.conf +++ b/config/filter.d/qmail.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/sasl.conf b/config/filter.d/sasl.conf index 4be847ee..6c4aeba7 100644 --- a/config/filter.d/sasl.conf +++ b/config/filter.d/sasl.conf @@ -2,7 +2,6 @@ # # Author: Yaroslav Halchenko # -# $Revision$ # [Definition] diff --git a/config/filter.d/sieve.conf b/config/filter.d/sieve.conf index 00e9daf1..866b4228 100644 --- a/config/filter.d/sieve.conf +++ b/config/filter.d/sieve.conf @@ -2,7 +2,6 @@ # # Author: Jan Wagner # -# $Revision$ # [Definition] diff --git a/config/filter.d/sshd-ddos.conf b/config/filter.d/sshd-ddos.conf index 266594ba..58698ced 100644 --- a/config/filter.d/sshd-ddos.conf +++ b/config/filter.d/sshd-ddos.conf @@ -2,6 +2,13 @@ # # Author: Yaroslav Halchenko # +# The regex here also relates to a exploit: +# +# http://www.securityfocus.com/bid/17958/exploit +# The example code here shows the pushing of the exploit straight after +# reading the server version. This is where the client version string normally +# pushed. As such the server will read this unparsible information as +# "Did not receive identification string". [INCLUDES] diff --git a/config/filter.d/sshd.conf b/config/filter.d/sshd.conf index e4339c78..18ac6668 100644 --- a/config/filter.d/sshd.conf +++ b/config/filter.d/sshd.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [INCLUDES] @@ -23,14 +22,16 @@ _daemon = sshd # (?:::f{4,6}:)?(?P[\w\-.^_]+) # Values: TEXT # -failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from \s*$ +failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from ( via \S+)?\s*$ ^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from \s*$ - ^%(__prefix_line)sFailed (?:password|publickey) for .* from (?: port \d*)?(?: ssh\d*)?\s*$ + ^%(__prefix_line)sFailed \S+ for .* from (?: port \d*)?(?: ssh\d*)?\s*$ ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM \s*$ ^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from \s*$ ^%(__prefix_line)sUser .+ from not allowed because not listed in AllowUsers\s*$ ^%(__prefix_line)sUser .+ from not allowed because listed in DenyUsers\s*$ + ^%(__prefix_line)sUser .+ from not allowed because not in any group\s*$ ^%(__prefix_line)srefused connect from \S+ \(\)\s*$ + ^%(__prefix_line)sUser .+ from not allowed because a group is listed in DenyGroups\s*$ ^%(__prefix_line)sUser .+ from not allowed because none of user's groups are listed in AllowGroups\s*$ # Option: ignoreregex diff --git a/config/filter.d/vsftpd.conf b/config/filter.d/vsftpd.conf index 4fc25777..259e2c82 100644 --- a/config/filter.d/vsftpd.conf +++ b/config/filter.d/vsftpd.conf @@ -2,7 +2,6 @@ # # Author: Cyril Jaquier # -# $Revision$ # [Definition] diff --git a/config/filter.d/webmin-auth.conf b/config/filter.d/webmin-auth.conf index b1df45dc..67f6e73f 100644 --- a/config/filter.d/webmin-auth.conf +++ b/config/filter.d/webmin-auth.conf @@ -3,7 +3,6 @@ # Author: Cyril Jaquier # Rule by : Delvit Guillaume # -# $Revision$ # [Definition] diff --git a/config/filter.d/wuftpd.conf b/config/filter.d/wuftpd.conf index 6f266fb5..3351d258 100644 --- a/config/filter.d/wuftpd.conf +++ b/config/filter.d/wuftpd.conf @@ -2,7 +2,6 @@ # # Author: Yaroslav Halchenko # -# $Revision$ # [Definition] diff --git a/config/filter.d/xinetd-fail.conf b/config/filter.d/xinetd-fail.conf index e1c1e108..4ff5bfde 100644 --- a/config/filter.d/xinetd-fail.conf +++ b/config/filter.d/xinetd-fail.conf @@ -2,7 +2,6 @@ # # Author: Guido Bozzetto # -# $Revision$ # [Definition] diff --git a/config/jail.conf b/config/jail.conf index 29af88c0..8b82d1d7 100644 --- a/config/jail.conf +++ b/config/jail.conf @@ -46,10 +46,10 @@ maxretry = 3 backend = auto # "usedns" specifies if jails should trust hostnames in logs, -# warn when reverse DNS lookups are performed, or ignore all hostnames in logs +# warn when DNS lookups are performed, or ignore all hostnames in logs # -# yes: if a hostname is encountered, a reverse DNS lookup will be performed. -# warn: if a hostname is encountered, a reverse DNS lookup will be performed, +# yes: if a hostname is encountered, a DNS lookup will be performed. +# warn: if a hostname is encountered, a DNS lookup will be performed, # but it will be logged as a warning. # no: if a hostname is encountered, will not be used for banning, # but it will be logged as info. @@ -89,6 +89,13 @@ action = iptables[name=sasl, port=smtp, protocol=tcp] sendmail-whois[name=sasl, dest=you@example.com] logpath = /var/log/mail.log +# ASSP SMTP Proxy Jail +[assp] +enabled = false +filter = assp +action = iptables-multiport[name=assp,port="25,465,587"] +logpath = /root/path/to/assp/logs/maillog.txt + # Here we use TCP-Wrappers instead of Netfilter/Iptables. "ignoreregex" is # used to avoid banning the user "myuser". @@ -345,6 +352,19 @@ action = iptables-multiport[name=asterisk-udp, port="5060,5061", protocol=udp] logpath = /var/log/asterisk/messages maxretry = 10 +# To log wrong MySQL access attempts add to /etc/my.cnf: +# log-error=/var/log/mysqld.log +# log-warning = 2 +[mysqld-iptables] + +enabled = false +filter = mysqld-auth +action = iptables[name=mysql, port=3306, protocol=tcp] + sendmail-whois[name=MySQL, dest=root, sender=fail2ban@example.com] +logpath = /var/log/mysqld.log +maxretry = 5 + + # Jail for more extended banning of persistent abusers # !!! WARNING !!! # Make sure that your loglevel specified in fail2ban.conf/.local diff --git a/fail2ban-client b/fail2ban-client index 061208dd..de8519f4 100755 --- a/fail2ban-client +++ b/fail2ban-client @@ -63,7 +63,7 @@ class Fail2banClient: self.__conf["interactive"] = False self.__conf["socket"] = None self.__conf["pidfile"] = None - + def dispVersion(self): print "Fail2Ban v" + version print @@ -73,7 +73,7 @@ class Fail2banClient: print print "Written by Cyril Jaquier ." print "Many contributions by Yaroslav O. Halchenko ." - + def dispUsage(self): """ Prints Fail2Ban command line options and exits """ @@ -95,17 +95,17 @@ class Fail2banClient: print " -V, --version print the version" print print "Command:" - + # Prints the protocol printFormatted() - + print print "Report bugs to https://github.com/fail2ban/fail2ban/issues" - + 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 + print def __sigTERMhandler(self, signum, frame): # Print a new line because we probably come from wait @@ -139,10 +139,10 @@ class Fail2banClient: 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: @@ -167,7 +167,7 @@ class Fail2banClient: logSys.error(e) return False return True - + ## # Process a command line. # @@ -241,13 +241,13 @@ class Fail2banClient: return False else: return self.__processCmd([cmd]) - - + + ## # Start Fail2Ban server. # # Start the Fail2ban server in daemon mode. - + def __startServerAsync(self, socket, pidfile, force = False): # Forks the current process. pid = os.fork() @@ -278,7 +278,7 @@ class Fail2banClient: except OSError: logSys.error("Could not start %s" % self.SERVER) os.exit(-1) - + def __waitOnServer(self): # Wait for the server to start cnt = 0 @@ -306,16 +306,16 @@ class Fail2banClient: cnt += 1 if self.__conf["verbose"] > 1: sys.stdout.write('\n') - - + + def start(self, argv): # Command line options self.__argv = argv - + # Install signal handlers signal.signal(signal.SIGTERM, self.__sigTERMhandler) signal.signal(signal.SIGINT, self.__sigTERMhandler) - + # Reads the command line options. try: cmdOpts = 'hc:s:p:xdviqV' @@ -324,9 +324,9 @@ class Fail2banClient: except getopt.GetoptError: self.dispUsage() return False - + self.__getCmdLineOptions(optList) - + verbose = self.__conf["verbose"] if verbose <= 0: logSys.setLevel(logging.ERROR) @@ -346,13 +346,13 @@ class Fail2banClient: # Set the configuration path self.__configurator.setBaseDir(self.__conf["conf"]) - + # Set socket path self.__configurator.readEarly() conf = self.__configurator.getEarlyOptions() - if self.__conf["socket"] == None: + if self.__conf["socket"] is None: self.__conf["socket"] = conf["socket"] - if self.__conf["pidfile"] == None: + if self.__conf["pidfile"] is None: self.__conf["pidfile"] = conf["pidfile"] logSys.info("Using socket file " + self.__conf["socket"]) @@ -360,7 +360,7 @@ class Fail2banClient: ret = self.__readConfig() self.dumpConfig(self.__stream) return ret - + # Interactive mode if self.__conf["interactive"]: try: @@ -401,14 +401,14 @@ class Fail2banClient: self.__configurator.convertToProtocol() self.__stream = self.__configurator.getConfigStream() return ret - + def __readJailConfig(self, jail): self.__configurator.readAll() ret = self.__configurator.getOptions(jail) self.__configurator.convertToProtocol() self.__stream = self.__configurator.getConfigStream() return ret - + #@staticmethod def dumpConfig(cmd): for c in cmd: diff --git a/fail2ban-regex b/fail2ban-regex index f9bc72c1..a0a90b05 100755 --- a/fail2ban-regex +++ b/fail2ban-regex @@ -50,24 +50,24 @@ class RegexStat: def __str__(self): return "%s(%r) %d failed: %s" \ % (self.__class__, self.__failregex, self.__stats, self.__ipList) - + def inc(self): self.__stats += 1 - + def getStats(self): return self.__stats def getFailRegex(self): return self.__failregex - + def appendIP(self, value): self.__ipList.extend(value) - + def getIPList(self): return self.__ipList class Fail2banRegex: - + test = None CONFIG_DEFAULTS = {'configpath' : "/etc/fail2ban/"} @@ -87,7 +87,7 @@ class Fail2banRegex: self.__logging_level = self.__verbose and logging.DEBUG or logging.WARN logging.getLogger("fail2ban").addHandler(self.__hdlr) logging.getLogger("fail2ban").setLevel(logging.ERROR) - + #@staticmethod def dispVersion(): print "Fail2Ban v" + version @@ -99,7 +99,7 @@ class Fail2banRegex: print "Written by Cyril Jaquier ." print "Many contributions by Yaroslav O. Halchenko ." dispVersion = staticmethod(dispVersion) - + #@staticmethod def dispUsage(): print "Usage: "+sys.argv[0]+" [OPTIONS] [IGNOREREGEX]" @@ -128,7 +128,7 @@ class Fail2banRegex: print print "Report bugs to https://github.com/fail2ban/fail2ban/issues" dispUsage = staticmethod(dispUsage) - + def getCmdLineOptions(self, optList): """ Gets the command line options """ @@ -204,7 +204,7 @@ class Fail2banRegex: print "Use regex line : " + stripReg self.__failregex = [RegexStat(value)] return True - + def testIgnoreRegex(self, line): found = False for regex in self.__ignoreregex: @@ -221,7 +221,7 @@ class Fail2banRegex: finally: self.__filter.delIgnoreRegex(0) logging.getLogger("fail2ban").setLevel(self.__logging_level) - + def testRegex(self, line): found = False for regex in self.__ignoreregex: @@ -251,7 +251,7 @@ class Fail2banRegex: logging.getLogger("fail2ban").setLevel(logging.CRITICAL) for regex in self.__ignoreregex: self.__filter.delIgnoreRegex(0) - + def printStats(self): print print "Results" @@ -300,20 +300,20 @@ class Fail2banRegex: print " %s (%s)%s" % ( ip[0], timeString, ip[2] and " (already matched)" or "") print - + print "Date template hits:" for template in self.__filter.dateDetector.getTemplates(): if self.__verbose or template.getHits(): print `template.getHits()` + " hit(s): " + template.getName() print - + print "Success, the total number of match is " + str(total) print print "However, look at the above section 'Running tests' which could contain important" print "information." return True - + if __name__ == "__main__": fail2banRegex = Fail2banRegex() # Reads the command line options. diff --git a/fail2ban-server b/fail2ban-server index 81db58bd..404a1ced 100755 --- a/fail2ban-server +++ b/fail2ban-server @@ -46,7 +46,7 @@ logSys = logging.getLogger("fail2ban") # Its first goal was to protect a SSH server. class Fail2banServer: - + def __init__(self): self.__server = None self.__argv = None @@ -55,7 +55,7 @@ class Fail2banServer: self.__conf["force"] = False self.__conf["socket"] = "/var/run/fail2ban/fail2ban.sock" self.__conf["pidfile"] = "/var/run/fail2ban/fail2ban.pid" - + def dispVersion(self): print "Fail2Ban v" + version print @@ -65,7 +65,7 @@ class Fail2banServer: print print "Written by Cyril Jaquier ." print "Many contributions by Yaroslav O. Halchenko ." - + def dispUsage(self): """ Prints Fail2Ban command line options and exits """ @@ -88,7 +88,7 @@ class Fail2banServer: print " -V, --version print the version" print print "Report bugs to https://github.com/fail2ban/fail2ban/issues" - + def __getCmdLineOptions(self, optList): """ Gets the command line options """ @@ -109,11 +109,11 @@ class Fail2banServer: 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:p:xhV' @@ -122,9 +122,9 @@ class Fail2banServer: except getopt.GetoptError: self.dispUsage() sys.exit(-1) - + self.__getCmdLineOptions(optList) - + try: self.__server = Server(self.__conf["background"]) self.__server.start(self.__conf["socket"], @@ -135,7 +135,7 @@ class Fail2banServer: logSys.exception(e) self.__server.quit() return False - + if __name__ == "__main__": server = Fail2banServer() if server.start(sys.argv): diff --git a/files/cacti/fail2ban_stats.sh b/files/cacti/fail2ban_stats.sh index 4d29854f..2c2e368a 100644 --- a/files/cacti/fail2ban_stats.sh +++ b/files/cacti/fail2ban_stats.sh @@ -25,7 +25,6 @@ # # Author: Cyril Jaquier # -# $Revision$ FAIL2BAN="fail2ban-client" diff --git a/files/fail2ban-logrotate b/files/fail2ban-logrotate new file mode 100644 index 00000000..67c6364a --- /dev/null +++ b/files/fail2ban-logrotate @@ -0,0 +1,18 @@ +# +# Gentoo: +# http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-analyzer/fail2ban/files/fail2ban-logrotate?view=markup +# +# Debian: +# https://github.com/fail2ban/fail2ban/blob/debian/debian/fail2ban.logrotate +# +# Fedora view: +# http://pkgs.fedoraproject.org/cgit/fail2ban.git/tree/fail2ban-logrotate + +/var/log/fail2ban.log { + rotate 7 + missingok + compress + postrotate + /usr/bin/fail2ban-client set logtarget /var/log/fail2ban.log 1>/dev/null || true + endscript +} diff --git a/files/fail2ban-tmpfiles.conf b/files/fail2ban-tmpfiles.conf new file mode 100644 index 00000000..3fd783f3 --- /dev/null +++ b/files/fail2ban-tmpfiles.conf @@ -0,0 +1 @@ +D /var/run/fail2ban 0755 root root - \ No newline at end of file diff --git a/files/fail2ban.service b/files/fail2ban.service new file mode 100644 index 00000000..9c44042b --- /dev/null +++ b/files/fail2ban.service @@ -0,0 +1,14 @@ +[Unit] +Description=Fail2ban Service +After=syslog.target network.target + +[Service] +Type=forking +ExecStart=/usr/bin/fail2ban-client -x start +ExecStop=/usr/bin/fail2ban-client stop +ExecReload=/usr/bin/fail2ban-client reload +PIDFile=/var/run/fail2ban/fail2ban.pid +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/files/gentoo-initd b/files/gentoo-initd index 4f4486ca..b56d4bdb 100755 --- a/files/gentoo-initd +++ b/files/gentoo-initd @@ -17,7 +17,6 @@ # # Author: Sireyessire, Cyril Jaquier # -# $Revision$ extra_started_commands="reload showlog" diff --git a/files/nagios/README b/files/nagios/README new file mode 100644 index 00000000..28e84495 --- /dev/null +++ b/files/nagios/README @@ -0,0 +1,104 @@ +Description +----------- +This plugin checks if the fail2ban server is running and how many IPs are currently banned. +You can use this plugin to monitor all the jails or just a specific jail. + + +How to use +---------- +Just have to run the following command: + $ ./check_fail2ban --help + +If you need to use this script with NRPE you just have to do the +following steps: + +1 allow your user to run the script with the sudo rights. Just add + something like that in your /etc/sudoers (use visudo) : + nagios ALL=(ALL) NOPASSWD: //check_fail2ban + +2 then just add this kind of line in your NRPE config file : + command[check_fail2ban]=/usr/bin/sudo //check_fail2ban + +3 don't forget to restart your NRPE daemon + +/!\ be careful to let no one able to update the check_fail2ban ;) +------------------------------------------------------------------------------ + + +Notes (from f2ban.txt) +----- +It seems that Fail2ban is currently not working, please login and check + +HELP: + +1.) stop the Service +/etc/init.d/fail2ban stop + +2.) delete the socket if available +rm /tmp/fail2ban.sock + +3.) start the Service +/etc/init.d/fail2ban start + +4.) check if fail2ban is working +fail2ban-client ping +Answer should be "pong" + +5.) if the answer is not "pong" run away or CRY FOR HELP ;-) + + +Help +---- + +Usage: //check_fail2ban [-p] [-D "CHECK FAIL2BAN ACTIVITY"] [-v] [-c 2] [-w 1] [-s //socket] [-P /usr/bin/fail2ban-client] + +Options: + -h, --help + Print detailed help screen + -V, --version + Print version information + -D, --display=STRING + To modify the output display + default is "CHECK FAIL2BAN ACTIVITY" + -P, --path-fail2ban_client=STRING + Specify the path to the tw_cli binary + default value is /usr/bin/fail2ban-client + -c, --critical=INT + Specify a critical threshold + default is 2 + -w, --warning=INT + Specify a warning threshold + default is 1 + -s, --socket=STRING + Specify a socket path + default is unset + -p, --perfdata + If you want to activate the perfdata output + -v, --verbose + Show details for command-line debugging (Nagios may truncate the output) + + +Example +------- + +# for a specific jail +$ ./check_fail2ban --verbose -p -j ssh -w 1 -c 5 -P /usr/bin/fail2ban-client +DEBUG : fail2ban_client_path: /usr/bin/fail2ban-client +DEBUG : /usr/bin/fail2ban-client exists and is executable +DEBUG : final fail2ban command: /usr/bin/fail2ban-client +DEBUG : warning threshold : 1, critical threshold : 5 +DEBUG : it seems the connection with the fail2ban server is ok +CHECK FAIL2BAN ACTIVITY - OK - 0 current banned IP(s) for the specific jail ssh | currentBannedIP=0 + +# for all the current jails +$ ./check_fail2ban --verbose -p -w 1 -c 5 -P /usr/bin/fail2ban-client +DEBUG : fail2ban_client_path: /usr/bin/fail2ban-client +DEBUG : /usr/bin/fail2ban-client exists and is executable +DEBUG : final fail2ban command: /usr/bin/fail2ban-client +DEBUG : warning threshold : 1, critical threshold : 5 +DEBUG : it seems the connection with the fail2ban server is ok +DEBUG : jails list: apache, ssh-ddos, ssh +DEBUG : the jail apache has currently 0 banned IPs +DEBUG : the jail ssh-ddos has currently 0 banned IPs +DEBUG : the jail ssh has currently 0 banned IPs +CHECK FAIL2BAN ACTIVITY - OK - 3 detected jails with 0 current banned IP(s) | currentBannedIP=0 diff --git a/files/nagios/check_fail2ban b/files/nagios/check_fail2ban index 2b38e8a9..77a63393 100755 --- a/files/nagios/check_fail2ban +++ b/files/nagios/check_fail2ban @@ -1,105 +1,346 @@ -#!/bin/bash +#!/usr/bin/perl + +# ------------------------------------------------------- +# -=- -=- +# ------------------------------------------------------- # -# Usage: ./check_fail2ban -############################################################################################### -# Description: -# This plugin will check the status of Fail2ban. +# Description : This plugin checks if the fail2ban server is running +# and how many IPs are currently banned. +# # -# Created: 2008-10-25 (Sebastian Mueller) +# inspired by the work of Sebastian Mueller - http://www.elchtest.eu +# # -# Changes: 2008-10-26 fixed some issues (Sebastian Mueller) -# Changes: 2009-01-25 add the second check, when server is not replying and the -# process is hang-up (Sebastian Mueller) +# Version : 0.1 +# ------------------------------------------------------- +# In : +# - see the How to use section # -# please visit my website http://www.elchtest.eu or my personal WIKI http://wiki.elchtest.eu +# Out : +# - only print on the standard output # -################################################################################################ -# if you have any questions, send a mail to linux@krabbe-offline.de +# Features : +# - perfdata output +# - works with only a specific jail # -# this script is for my personal use. read the script before running/using it!!! +# Fix Me/Todo : +# - too many things ;) but let me know what do you think about it # +# #################################################################### + +# #################################################################### +# GPL v2 +# This program 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. # -# YOU HAVE BEEN WARNED. THIS MAY DESTROY YOUR MACHINE. I ACCEPT NO RESPONSIBILITY. -############################################################################################### +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# #################################################################### + +# #################################################################### +# How to use : +# ------------ +# +# Just have to run the following command: +# $ ./check_fail2ban --help +# +# If you need to use this script with NRPE you just have to do the +# following steps: +# +# 1 allow your user to run the script with the sudo rights. Just add +# something like that in your /etc/sudoers (use visudo) : +# nagios ALL=(ALL) NOPASSWD: //check_fail2ban +# +# 2 then just add this kind of line in your NRPE config file : +# command[check_fail2ban]=/usr/bin/sudo //check_fail2ban +# +# 3 don't forget to restart your NRPE daemon +# +# +# /!\ be careful to let no one able to update the check_fail2ban ;) +# ------------------------------------------------------------------------------ +# +# #################################################################### + +# #################################################################### +# Changelog : +# ----------- +# +# -------------------------------------------------------------------- +# Date:12/03/2013 Version:0.1 Author:Erwan Ben Souiden +# >> creation +# #################################################################### + +# #################################################################### +# Don't touch anything under this line! +# You shall not pass - Gandalf is watching you +# #################################################################### + +use strict; +use warnings; +use Getopt::Long qw(:config no_ignore_case); + +# Generic variables +# ----------------- +my $version = '0.1'; +my $author = 'Erwan Labynocle Ben Souiden'; +my $a_mail = 'erwan@aleikoum.net'; +my $script_name = 'check_fail2ban'; +my $verbose_value = 0; +my $version_value = 0; +my $more_value = 0; +my $help_value = 0; +my $perfdata_value = 0; +my %ERRORS=('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4); + +# Plugin default variables +# ------------------------ +my $display = 'CHECK FAIL2BAN ACTIVITY'; +my ($critical,$warning) = (2,1); +my $fail2ban_client_path = '/usr/bin/fail2ban-client'; +my $fail2ban_socket = ''; +my $jail_specific = ''; + +GetOptions ( + 'P=s' => \ $fail2ban_client_path, + 'path-fail2ban_client=s' => \ $fail2ban_client_path, + 'j=s' => \ $jail_specific, + 'jail=s' => \ $jail_specific, + 'w=i' => \ $warning, + 'warning=i' => \ $warning, + 'socket=s' => \ $fail2ban_socket, + 'S=s' => \ $fail2ban_socket, + 'c=i' => \ $critical, + 'critical=i' => \ $critical, + 'V' => \ $version_value, + 'version' => \ $version_value, + 'h' => \ $help_value, + 'H' => \ $help_value, + 'help' => \ $help_value, + 'display=s' => \ $display, + 'D=s' => \ $display, + 'perfdata' => \ $perfdata_value, + 'p' => \ $perfdata_value, + 'v' => \ $verbose_value, + 'verbose' => \ $verbose_value +); + +print_usage() if ($help_value); +print_version() if ($version_value); -SECOND_CHECK=0 -STATE_OK=0 -STATE_CRITICAL=2 - -###################################################################### -# Read the Status from fail2ban-client -###################################################################### -check_processes_fail2ban() -{ - - F2B=`sudo -u root fail2ban-client ping | awk -F " " '{print $3}'` - exit_fail2ban=0 - - if [[ $F2B = "pong" ]]; then - exit_fail2ban=$STATE_OK - else - exit_fail2ban=$STATE_CRITICAL - fi +# Syntax check of your specified options +# -------------------------------------- +print "DEBUG : fail2ban_client_path: $fail2ban_client_path\n" if ($verbose_value); +if (($fail2ban_client_path eq "")) { + print $display.'- one or more following arguments are missing: fail2ban_client_path'."\n"; + exit $ERRORS{"UNKNOWN"}; } -###################################################################### -# first check in the Background, PID will be killed when no response -# after 10 seconds, might be possible, otherwise the script will be -# present in your memory all the time -###################################################################### -check_processes_fail2ban & -pid=$! +if(! -x $fail2ban_client_path) { + print $display.' - '.$fail2ban_client_path.' is not executable by you'."\n"; + exit $ERRORS{"UNKNOWN"}; +} +print "DEBUG : $fail2ban_client_path exists and is executable\n" if ($verbose_value); -typeset -i i=0 -while ps $pid >/dev/null -do - sleep 1 - i=$i+1 -if [ $i -ge 10 ] - then - kill $pid - SECOND_CHECK=1 - exit_fail2ban=$STATE_CRITICAL - break -fi -done +my $fail2ban_cmd = $fail2ban_client_path; +$fail2ban_cmd .= " -s $fail2ban_socket" if ($fail2ban_socket); -###################################################################### -# when the Server response (does not mean the FAIL2BAN is working) -# in the first step, then it will run again and test the Service -# and provide the real status -###################################################################### +print "DEBUG : final fail2ban command: $fail2ban_cmd\n" if ($verbose_value); + +print "DEBUG : warning threshold : $warning, critical threshold : $critical\n" if ($verbose_value); +if (($critical < 0) or ($warning < 0) or ($critical < $warning)) { + print $display.' - the thresholds must be integers and the critical threshold higher or equal than the warning threshold'."\n"; + exit $ERRORS{"UNKNOWN"}; +} + +# Core script +# ----------- +my ($how_many_jail,$how_many_banned,$return_print,$plugstate) = (0,0,"","OK"); -if [ $SECOND_CHECK -eq 0 ]; then - check_processes_fail2ban - elif [ $SECOND_CHECK -eq 1 ]; then - exit_fail2ban=$STATE_CRITICAL -fi +### Test the connection to the fail2ban server +my @command_output = `$fail2ban_cmd ping`; +my $return_code = $?; +if ($return_code) { + print $display.'CRITICAL - non-zero exit code during testing fail2ban-client ping, check if the server is running and if you have the good permissions'; + exit $ERRORS{"CRITICAL"}; +} +else { + print "DEBUG : it seems the connection with the fail2ban server is ok\n" if ($verbose_value); +} +### Only if you specify one jail +if ($jail_specific) { + my $current_ban_number = currently_ban("$fail2ban_cmd","$jail_specific"); + if ($current_ban_number == -1) { + print $display.' - CRITICAL - impossible to retrieve info about the jail '.$jail_specific; + exit $ERRORS{"CRITICAL"}; + } + else { + $how_many_banned = int($current_ban_number); + $return_print = $how_many_banned.' current banned IP(s) for the specific jail '.$jail_specific; + } +} +### To analyze all the jail +else { + # Retrieve the jails list + my @jail_list = obtain_jail_list("$fail2ban_cmd"); + if ($jail_list[0] eq "-1") { + print $display.' - CRITICAL - impossible to retrieve the jail list'."\n"; + exit $ERRORS{"CRITICAL"}; + } -###################################################################### -# Main Menu -###################################################################### + foreach (@jail_list) { + $how_many_jail ++; + + my $jail_name = $_; + $jail_name =~ tr/ //ds; + + my $current_ban_number = currently_ban("$fail2ban_cmd","$jail_name"); + if ($current_ban_number == -1) { + print "DEBUG : problem to parse the current banned IPs for jail $jail_name\n" if ($verbose_value); + } + else { + print "DEBUG : the jail $jail_name has currently $current_ban_number banned IPs\n" if ($verbose_value); + $how_many_banned += int($current_ban_number); + } + } + $return_print = $how_many_jail.' detected jails with '.$how_many_banned.' current banned IP(s)'; +} + +### Final +$plugstate = "CRITICAL" if ($how_many_banned >= $critical); +$plugstate = "WARNING" if (($how_many_banned >= $warning) && ($how_many_banned < $critical)); + +$return_print = $display." - ".$plugstate." - ".$return_print; +$return_print .= " | currentBannedIP=$how_many_banned" if ($perfdata_value); + +print $return_print; +exit $ERRORS{"$plugstate"}; -final_exit=$exit_fail2ban -if [ $final_exit -eq 0 ]; then - echo "SYSTEM OK - Fail2ban is working normally" - exitstatus=$STATE_OK -elif [ $final_exit -ne "0" ]; then - echo "SYSTEM WARNING - Fail2Ban is not working" -###################################################################### -# If don't have a Nagios Server for monitoring, remove the comment and -# add your Mail Address. You can check it with a Cron Job once an hour. -# put a txt file on your server and describe how to fix the issue, this -# could be attached to the mail. -###################################################################### -# mutt -s "FAIL2BAN NOT WORKING" your@example.com < /home/f2ban.txt +# #################################################################### +# function 1 : display the help +# ----------------------------- +sub print_usage { + print </$script_name [-p] [-D "$display"] [-v] [-c 2] [-w 1] [-s //socket] [-P /usr/bin/fail2ban-client] + +Options: + -h, --help + Print detailed help screen + -V, --version + Print version information + -D, --display=STRING + To modify the output display + default is "CHECK FAIL2BAN ACTIVITY" + -P, --path-fail2ban_client=STRING + Specify the path to the tw_cli binary + default value is /usr/bin/fail2ban-client + -c, --critical=INT + Specify a critical threshold + default is 2 + -w, --warning=INT + Specify a warning threshold + default is 1 + -s, --socket=STRING + Specify a socket path + default is unset + -p, --perfdata + If you want to activate the perfdata output + -v, --verbose + Show details for command-line debugging (Nagios may truncate the output) + +Send email to $a_mail if you have questions +regarding use of this software. To submit patches or suggest improvements, +send email to $a_mail +This plugin has been created by $author + +Hope you will enjoy it ;) + +Remember : + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +EOT + exit $ERRORS{"UNKNOWN"}; +} + +# function 2 : display version information +# ---------------------------------------- +sub print_version { + print <. All the elements of [Init] are tags that are replaced in all action commands. Tags can be added by the -\fBfail2ban-client\fR using the setctag command. +\fBfail2ban-client\fR using the setctag command. \fB
\fR is a tag that is always a new line (\\n). More than a single command is allowed to be specified. Each command needs to be on a separate line and indented with whitespaces without blank lines. The following example defines two commands to be executed. diff --git a/server/__init__.py b/server/__init__.py index c448827b..2b76f4b6 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -19,10 +19,7 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" diff --git a/server/action.py b/server/action.py index 5fde3ae1..3f3033fd 100644 --- a/server/action.py +++ b/server/action.py @@ -17,18 +17,12 @@ # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Author: Cyril Jaquier -# -# $Revision$ - -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__author__ = "Cyril Jaquier and Fail2Ban Contributors" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" __license__ = "GPL" import logging, os -import threading +import threading, re #from subprocess import call # Gets the instance of the logger. @@ -143,6 +137,10 @@ class Action: # @return True if the command succeeded def execActionStart(self): + if self.__cInfo: + if not Action.substituteRecursiveTags(self.__cInfo): + logSys.error("Cinfo/definitions contain self referencing definitions and cannot be resolved") + return False startCmd = Action.replaceTag(self.__actionStart, self.__cInfo) return Action.executeCmd(startCmd) @@ -242,6 +240,38 @@ class Action: stopCmd = Action.replaceTag(self.__actionStop, self.__cInfo) return Action.executeCmd(stopCmd) + ## + # Sort out tag definitions within other tags + # + # so: becomes: + # a = 3 a = 3 + # b = _3 b = 3_3 + # @param tags, a dictionary + # @returns tags altered or False if there is a recursive definition + #@staticmethod + def substituteRecursiveTags(tags): + t = re.compile(r'<([^ >]+)>') + for tag, value in tags.iteritems(): + value = str(value) + m = t.search(value) + while m: + if m.group(1) == tag: + # recursive definitions are bad + return False + else: + if tags.has_key(m.group(1)): + value = value[0:m.start()] + tags[m.group(1)] + value[m.end():] + m = t.search(value, m.start()) + else: + # Missing tags are ok so we just continue on searching. + # cInfo can contain aInfo elements like and valid shell + # constructs like . + m = t.search(value, m.start() + 1) + tags[tag] = value + return tags + substituteRecursiveTags = staticmethod(substituteRecursiveTags) + + #@staticmethod def escapeTag(tag): for c in '\\#&;`|*?~<>^()[]{}$\n\'"': if c in tag: @@ -297,16 +327,14 @@ class Action: 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) + self.execActionStop() + self.execActionStart() if not Action.executeCmd(checkCmd): logSys.fatal("Unable to restore environment") return False # Replace tags - if not aInfo == None: + if not aInfo is None: realCmd = Action.replaceTag(cmd, aInfo) else: realCmd = cmd diff --git a/server/actions.py b/server/actions.py index ddcc83d6..4f538b7b 100644 --- a/server/actions.py +++ b/server/actions.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" diff --git a/server/asyncserver.py b/server/asyncserver.py index 66b2b53f..87f91633 100644 --- a/server/asyncserver.py +++ b/server/asyncserver.py @@ -19,17 +19,14 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" from pickle import dumps, loads, HIGHEST_PROTOCOL from common import helpers -import asyncore, asynchat, socket, os, logging, sys, traceback +import asyncore, asynchat, socket, os, logging, sys, traceback, fcntl # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.server") @@ -107,6 +104,7 @@ class AsyncServer(asyncore.dispatcher): except TypeError: logSys.warning("Type error") return + AsyncServer.__markCloseOnExec(conn) # Creates an instance of the handler class to handle the # request/response on the incoming connection. RequestHandler(conn, self.__transmitter) @@ -134,6 +132,7 @@ class AsyncServer(asyncore.dispatcher): self.bind(sock) except Exception: raise AsyncServerException("Unable to bind socket %s" % self.__sock) + AsyncServer.__markCloseOnExec(self.socket) self.listen(1) # Sets the init flag. self.__init = True @@ -159,6 +158,18 @@ class AsyncServer(asyncore.dispatcher): os.remove(self.__sock) logSys.debug("Socket shutdown") + ## + # Marks socket as close-on-exec to avoid leaking file descriptors when + # running actions involving command execution. + + # @param sock: socket file. + + #@staticmethod + def __markCloseOnExec(sock): + fd = sock.fileno() + flags = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC) + __markCloseOnExec = staticmethod(__markCloseOnExec) ## # AsyncServerException is used to wrap communication exceptions. diff --git a/server/banmanager.py b/server/banmanager.py index 596dc4e9..88a01ea2 100644 --- a/server/banmanager.py +++ b/server/banmanager.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -148,7 +145,7 @@ class BanManager: def addBanTicket(self, ticket): try: self.__lock.acquire() - if not self.__inBanList(ticket): + if not self._inBanList(ticket): self.__banList.append(ticket) self.__banTotal += 1 return True @@ -177,7 +174,7 @@ class BanManager: # @param ticket the ticket # @return True if a ticket already exists - def __inBanList(self, ticket): + def _inBanList(self, ticket): for i in self.__banList: if ticket.getIP() == i.getIP(): return True diff --git a/server/datedetector.py b/server/datedetector.py index c013d551..0c8b4df2 100644 --- a/server/datedetector.py +++ b/server/datedetector.py @@ -17,13 +17,7 @@ # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Author: Cyril Jaquier -# -# $Revision$ - -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" +__author__ = "Cyril Jaquier and Fail2Ban Contributors" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -155,6 +149,18 @@ class DateDetector: template.setRegex("^<\d{2}/\d{2}/\d{2}@\d{2}:\d{2}:\d{2}>") template.setPattern("<%m/%d/%y@%H:%M:%S>") self._appendTemplate(template) + # MySQL: 130322 11:46:11 + template = DateStrptime() + template.setName("MonthDayYear Hour:Minute:Second") + template.setRegex("^\d{2}\d{2}\d{2} +\d{1,2}:\d{2}:\d{2}") + template.setPattern("%y%m%d %H:%M:%S") + self._appendTemplate(template) + # ASSP: Apr-27-13 02:33:06 + template = DateStrptime() + template.setName("Month-Day-Year Hour:Minute:Second") + template.setRegex("^[a-zA-Z]{3}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}") + template.setPattern("%b-%d-%y %H:%M:%S") + self._appendTemplate(template) finally: self.__lock.release() @@ -191,10 +197,7 @@ class DateDetector: def getUnixTime(self, line): date = self.getTime(line) - if date == None: - return None - else: - return time.mktime(date) + return date and time.mktime(date) ## # Sort the template lists using the hits score. This method is not called diff --git a/server/datetemplate.py b/server/datetemplate.py index 51b8bb1e..8c49aa15 100644 --- a/server/datetemplate.py +++ b/server/datetemplate.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -65,7 +62,7 @@ class DateTemplate: def matchDate(self, line): dateMatch = self.__cRegex.search(line) - if not dateMatch == None: + if not dateMatch is None: self.__hits += 1 return dateMatch @@ -218,3 +215,4 @@ class DateISO8601(DateTemplate): value = dateMatch.group() date = list(iso8601.parse_date(value).timetuple()) return date + diff --git a/server/faildata.py b/server/faildata.py index 1f0bda04..3d943434 100644 --- a/server/faildata.py +++ b/server/faildata.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" diff --git a/server/failmanager.py b/server/failmanager.py index 02f16ce3..57d7fced 100644 --- a/server/failmanager.py +++ b/server/failmanager.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" diff --git a/server/failregex.py b/server/failregex.py index b194d472..957c77db 100644 --- a/server/failregex.py +++ b/server/failregex.py @@ -17,13 +17,7 @@ # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Author: Cyril Jaquier -# -# $Revision$ - __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -125,7 +119,7 @@ class FailRegex(Regex): def getHost(self): host = self._matchCache.group("host") - if host == None: + if host is None: # Gets a few information. s = self._matchCache.string r = self._matchCache.re diff --git a/server/filter.py b/server/filter.py index f842c269..754a2a08 100644 --- a/server/filter.py +++ b/server/filter.py @@ -17,14 +17,8 @@ # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Author: Cyril Jaquier -# -# $Revision$ - -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__author__ = "Cyril Jaquier and Fail2Ban Contributors" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2013 Yaroslav Halchenko" __license__ = "GPL" from failmanager import FailManagerEmpty @@ -360,7 +354,7 @@ class Filter(JailThread): if failRegex.hasMatched(): # The failregex matched. date = self.dateDetector.getUnixTime(timeLine) - if date == None: + if date is None: logSys.debug("Found a match for %r but no valid date/time " "found for %r. Please file a detailed issue on" " https://github.com/fail2ban/fail2ban/issues " @@ -473,7 +467,7 @@ class FileFilter(Filter): def getFailures(self, filename): container = self.getFileContainer(filename) - if container == None: + if container is None: logSys.error("Unable to get failures in " + filename) return False # Try to open log file. @@ -554,7 +548,8 @@ class FileContainer: self.__handler = open(self.__filename) # Set the file descriptor to be FD_CLOEXEC fd = self.__handler.fileno() - fcntl.fcntl(fd, fcntl.F_SETFD, fd | fcntl.FD_CLOEXEC) + flags = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) firstLine = self.__handler.readline() # Computes the MD5 of the first line. myHash = md5sum(firstLine).digest() @@ -569,12 +564,12 @@ class FileContainer: self.__handler.seek(self.__pos) def readline(self): - if self.__handler == None: + if self.__handler is None: return "" return self.__handler.readline() def close(self): - if not self.__handler == None: + if not self.__handler is None: # Saves the last position. self.__pos = self.__handler.tell() # Closes the file. diff --git a/server/filtergamin.py b/server/filtergamin.py index cff5aa54..196396c5 100644 --- a/server/filtergamin.py +++ b/server/filtergamin.py @@ -27,7 +27,7 @@ from failmanager import FailManagerEmpty from filter import FileFilter from mytime import MyTime -import time, logging, gamin +import time, logging, gamin, fcntl # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.filter") @@ -52,6 +52,9 @@ class FilterGamin(FileFilter): self.__modified = False # Gamin monitor self.monitor = gamin.WatchMonitor() + fd = self.monitor.get_fd() + flags = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, flags|fcntl.FD_CLOEXEC) logSys.debug("Created FilterGamin") diff --git a/server/filterpoll.py b/server/filterpoll.py index f0e23ac1..8a6a88e7 100644 --- a/server/filterpoll.py +++ b/server/filterpoll.py @@ -21,8 +21,6 @@ # __author__ = "Cyril Jaquier, Yaroslav Halchenko" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko" __license__ = "GPL" diff --git a/server/filterpyinotify.py b/server/filterpyinotify.py index e86498b0..4c270d2e 100644 --- a/server/filterpyinotify.py +++ b/server/filterpyinotify.py @@ -66,7 +66,7 @@ class FilterPyinotify(FileFilter): def callback(self, event, origin=''): logSys.debug("%sCallback for Event: %s", origin, event) path = event.pathname - if event.mask & pyinotify.IN_CREATE: + if event.mask & ( pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO ): # skip directories altogether if event.mask & pyinotify.IN_ISDIR: logSys.debug("Ignoring creation of directory %s", path) @@ -130,7 +130,7 @@ class FilterPyinotify(FileFilter): if not (path_dir in self.__watches): # we need to watch also the directory for IN_CREATE self.__watches.update( - self.__monitor.add_watch(path_dir, pyinotify.IN_CREATE)) + self.__monitor.add_watch(path_dir, pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO)) logSys.debug("Added monitor for the parent directory %s", path_dir) self._addFileWatcher(path) diff --git a/server/jailthread.py b/server/jailthread.py index 343ea7e2..11d1a82b 100644 --- a/server/jailthread.py +++ b/server/jailthread.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" diff --git a/server/mytime.py b/server/mytime.py index 286f3d2c..8ae85184 100644 --- a/server/mytime.py +++ b/server/mytime.py @@ -17,13 +17,7 @@ # along with Fail2Ban; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Author: Cyril Jaquier -# -# $Revision$ - __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -61,7 +55,7 @@ class MyTime: #@staticmethod def time(): - if MyTime.myTime == None: + if MyTime.myTime is None: return time.time() else: return MyTime.myTime @@ -74,14 +68,14 @@ class MyTime: #@staticmethod def gmtime(): - if MyTime.myTime == None: + if MyTime.myTime is None: return time.gmtime() else: return time.gmtime(MyTime.myTime) gmtime = staticmethod(gmtime) def localtime(x=None): - if MyTime.myTime == None or x is not None: + if MyTime.myTime is None or x is not None: return time.localtime(x) else: return time.localtime(MyTime.myTime) diff --git a/server/server.py b/server/server.py index a0824f1d..a8cf1c2f 100644 --- a/server/server.py +++ b/server/server.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -66,7 +63,7 @@ class Server: # First set the mask to only allow access to owner os.umask(0077) - if self.__daemon: + if self.__daemon: # pragma: no cover logSys.info("Starting in daemon mode") ret = self.__createDaemon() if ret: @@ -379,7 +376,7 @@ class Server: try: handler.flush() handler.close() - except (ValueError, KeyError): + except (ValueError, KeyError): # pragma: no cover if sys.version_info >= (2,6): raise # is known to be thrown after logging was shutdown once @@ -388,7 +385,7 @@ class Server: hdlr.setFormatter(formatter) logging.getLogger("fail2ban").addHandler(hdlr) # Does not display this message at startup. - if not self.__logTarget == None: + if not self.__logTarget is None: logSys.info("Changed logging target to %s for Fail2ban v%s" % (target, version.version)) # Sets the logging target. @@ -404,7 +401,7 @@ class Server: finally: self.__loggingLock.release() - def __createDaemon(self): + def __createDaemon(self): # pragma: no cover """ Detach a process from the controlling terminal and run it in the background as a daemon. diff --git a/server/ticket.py b/server/ticket.py index c03761c1..8826f26b 100644 --- a/server/ticket.py +++ b/server/ticket.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" diff --git a/server/transmitter.py b/server/transmitter.py index dc121f93..deaf9adf 100644 --- a/server/transmitter.py +++ b/server/transmitter.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -123,7 +120,7 @@ class Transmitter: elif command[2] == "off": self.__server.setIdleJail(name, False) else: - raise Exception("Invalid idle option, must be 'yes' or 'no'") + raise Exception("Invalid idle option, must be 'on' or 'off'") return self.__server.getIdleJail(name) # Filter elif command[1] == "addignoreip": diff --git a/setup.cfg b/setup.cfg index 74c22b25..02cd5d5d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,6 +8,6 @@ formats=bztar release = 1 packager = Yaroslav Halchenko , Daniel Black doc_files = DEVELOP - README + README.md THANKS doc/run-rootless.txt diff --git a/setup.py b/setup.py index 784999a2..1b6268bf 100755 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ setup( '' ), ('/usr/share/doc/fail2ban', - ['README', 'DEVELOP', 'doc/run-rootless.txt'] + ['README.md', 'DEVELOP', 'doc/run-rootless.txt'] ) ] ) diff --git a/testcases/__init__.py b/testcases/__init__.py index c448827b..3de9058c 100644 --- a/testcases/__init__.py +++ b/testcases/__init__.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/testcases/actiontestcase.py b/testcases/actiontestcase.py index b8292c27..e8a03abf 100644 --- a/testcases/actiontestcase.py +++ b/testcases/actiontestcase.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" @@ -61,6 +60,47 @@ class ExecuteAction(unittest.TestCase): def _is_logged(self, s): return s in self._log.getvalue() + def testSubstituteRecursiveTags(self): + aInfo = { + 'HOST': "192.0.2.0", + 'ABC': "123 ", + 'xyz': "890 ", + } + # Recursion is bad + self.assertFalse(Action.substituteRecursiveTags({'A': ''})) + self.assertFalse(Action.substituteRecursiveTags({'A': '', 'B': ''})) + self.assertFalse(Action.substituteRecursiveTags({'A': '', 'B': '', 'C': ''})) + # missing tags are ok + self.assertEquals(Action.substituteRecursiveTags({'A': ''}), {'A': ''}) + self.assertEquals(Action.substituteRecursiveTags({'A': ' ','X':'fun'}), {'A': ' fun', 'X':'fun'}) + self.assertEquals(Action.substituteRecursiveTags({'A': ' ', 'B': 'cool'}), {'A': ' cool', 'B': 'cool'}) + # rest is just cool + self.assertEquals(Action.substituteRecursiveTags(aInfo), + { 'HOST': "192.0.2.0", + 'ABC': '123 192.0.2.0', + 'xyz': '890 123 192.0.2.0', + }) + + def testReplaceTag(self): + aInfo = { + 'HOST': "192.0.2.0", + 'ABC': "123", + 'xyz': "890", + } + self.assertEqual( + self.__action.replaceTag("Text
text", aInfo), + "Text\ntext") + self.assertEqual( + self.__action.replaceTag("Text text", aInfo), + "Text 192.0.2.0 text") + self.assertEqual( + self.__action.replaceTag("Text text ABC", aInfo), + "Text 890 text 123 ABC") + self.assertEqual( + self.__action.replaceTag("", + {'matches': "some >char< should \< be[ escap}ed&"}), + r"some \>char\< should \\\< be\[ escap\}ed\&") + def testExecuteActionBan(self): self.__action.setActionStart("touch /tmp/fail2ban.test") self.__action.setActionStop("rm -f /tmp/fail2ban.test") diff --git a/testcases/banmanagertestcase.py b/testcases/banmanagertestcase.py index 7d389eed..58651573 100644 --- a/testcases/banmanagertestcase.py +++ b/testcases/banmanagertestcase.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" @@ -49,11 +48,11 @@ class AddFailure(unittest.TestCase): self.assertFalse(self.__banManager.addBanTicket(self.__ticket)) self.assertEqual(self.__banManager.size(), 1) - def _testInListOK(self): + def testInListOK(self): ticket = BanTicket('193.168.0.128', 1167605999.0) - self.assertTrue(self.__banManager.inBanList(ticket)) + self.assertTrue(self.__banManager._inBanList(ticket)) - def _testInListNOK(self): + def testInListNOK(self): ticket = BanTicket('111.111.1.111', 1167605999.0) - self.assertFalse(self.__banManager.inBanList(ticket)) + self.assertFalse(self.__banManager._inBanList(ticket)) diff --git a/testcases/clientreadertestcase.py b/testcases/clientreadertestcase.py index 3e606785..fad16f04 100644 --- a/testcases/clientreadertestcase.py +++ b/testcases/clientreadertestcase.py @@ -114,7 +114,8 @@ class JailsReaderTest(unittest.TestCase): def testProvidingBadBasedir(self): if not os.path.exists('/XXX'): - self.assertRaises(ValueError, JailsReader, basedir='/XXX') + reader = JailsReader(basedir='/XXX') + self.assertRaises(ValueError, reader.read) def testReadStockJailConf(self): jails = JailsReader(basedir='config') # we are running tests from root project dir atm diff --git a/testcases/datedetectortestcase.py b/testcases/datedetectortestcase.py index 64af1fab..4b38fb12 100644 --- a/testcases/datedetectortestcase.py +++ b/testcases/datedetectortestcase.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" @@ -84,6 +83,8 @@ class DateDetectorTest(unittest.TestCase): "2005-01-23T21:59:59.252Z", #ISO 8601 "2005-01-23T21:59:59-05:00Z", #ISO 8601 with TZ "<01/23/05@21:59:59>", + "050123 21:59:59", # MySQL + "Jan-23-05 21:59:59", # ASSP like ): log = sdate + "[sshd] error: PAM: Authentication failure" # exclude diff --git a/testcases/failmanagertestcase.py b/testcases/failmanagertestcase.py index ffee4ff1..de23219c 100644 --- a/testcases/failmanagertestcase.py +++ b/testcases/failmanagertestcase.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" diff --git a/testcases/files/logs/assp b/testcases/files/logs/assp new file mode 100644 index 00000000..99363001 --- /dev/null +++ b/testcases/files/logs/assp @@ -0,0 +1,13 @@ +Apr-07-13 07:08:36 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol; +Apr-07-13 07:08:36 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol; +Apr-07-13 07:10:37 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol; +Apr-07-13 07:12:37 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol; +Apr-07-13 07:14:36 [SSL-out] 68.171.223.68 SSL negotiation with client failed: SSL accept attempt failed with unknown errorerror:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol; +Apr-27-13 02:25:09 Blocking 217.194.197.97 - too much AUTH errors (8); +Apr-27-13 02:25:09 Blocking 217.194.197.97 - too much AUTH errors (9); +Apr-27-13 02:25:09 Blocking 217.194.197.97 - too much AUTH errors (10); +Apr-27-13 02:25:10 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6; +Apr-27-13 02:25:10 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6; +Apr-27-13 02:25:10 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6; +Apr-27-13 02:25:11 [SSL-out] 217.194.197.97 max sender authentication errors (5) exceeded -- dropping connection - after reply: 535 5.7.8 Error: authentication failed: UGFzc3dvcmQ6; + diff --git a/testcases/files/logs/mysqld.log b/testcases/files/logs/mysqld.log new file mode 100644 index 00000000..b3a73078 --- /dev/null +++ b/testcases/files/logs/mysqld.log @@ -0,0 +1,6 @@ +130324 0:04:00 [Warning] Access denied for user 'root'@'192.168.1.35' (using password: NO) +130324 8:24:09 [Warning] Access denied for user 'root'@'220.95.238.171' (using password: NO) +130324 17:56:13 [Warning] Access denied for user 'root'@'61.160.223.112' (using password: NO) +130324 17:56:14 [Warning] Access denied for user 'root'@'61.160.223.112' (using password: YES) +130324 19:01:39 [Warning] Access denied for user 'root'@'61.147.108.35' (using password: NO) +130324 19:01:40 [Warning] Access denied for user 'root'@'61.147.108.35' (using password: YES) diff --git a/testcases/files/logs/named-refused b/testcases/files/logs/named-refused index 6608ae2f..130e7417 100644 --- a/testcases/files/logs/named-refused +++ b/testcases/files/logs/named-refused @@ -3,3 +3,4 @@ Jul 24 14:16:56 raid5 named[3935]: client 62.123.164.113#32768: query 'ricreig.c Jul 24 14:17:13 raid5 named[3935]: client 148.160.29.6#33081: query (cache) 'geo-mueller.de/NS/IN' denied Jul 24 14:20:25 raid5 named[3935]: client 148.160.29.6#33081: query (cache) 'shivaree.de/NS/IN' denied Jul 24 14:23:36 raid5 named[3935]: client 148.160.29.6#33081: query (cache) 'mietberatung.de/NS/IN' denied +Jul 24 14:23:36 raid5 named[3935]: client 62.109.4.89#9334: view external: query (cache) './NS/IN' denied diff --git a/testcases/files/logs/sshd b/testcases/files/logs/sshd index 8e6c1273..5dab1606 100644 --- a/testcases/files/logs/sshd +++ b/testcases/files/logs/sshd @@ -1,6 +1,6 @@ #1 Jun 21 16:47:48 digital-mlhhyiqscv sshd[13709]: error: PAM: Authentication failure for myhlj1374 from 192.030.0.6 -May 29 20:56:52 imago sshd[28732]: error: PAM: Authentication failure for stefanor from www.onerussian.com +May 29 20:56:52 imago sshd[28732]: error: PAM: Authentication failure for stefanor from example.com #2 Feb 25 14:34:10 belka sshd[31602]: Failed password for invalid user ROOT from 194.117.26.69 port 50273 ssh2 @@ -13,10 +13,10 @@ Jan 5 01:31:41 www sshd[1643]: ROOT LOGIN REFUSED FROM ::ffff:1.2.3.4 #4 Jul 20 14:42:11 localhost sshd[22708]: Invalid user ftp from 211.114.51.213 - #5 new filter introduced after looking at 44087D8C.9090407@bluewin.ch -Mar 3 00:17:22 [sshd] User root from 210.188.220.49 not allowed because not listed in AllowUsers -Feb 25 14:34:11 belka sshd[31607]: User root from ferrari.inescn.pt not allowed because not listed in AllowUsers +# yoh: added ':' after [sshd] since the case without is not really common any more +Mar 3 00:17:22 [sshd]: User root from 211.188.220.49 not allowed because not listed in AllowUsers +Feb 25 14:34:11 belka sshd[31607]: User root from example.com not allowed because not listed in AllowUsers #6 ew filter introduced thanks to report Guido Bozzetto Nov 11 23:33:27 Server sshd[5174]: refused connect from _U2FsdGVkX19P3BCJmFBHhjLza8BcMH06WCUVwttMHpE=_@::ffff:218.249.210.161 (::ffff:218.249.210.161) @@ -29,5 +29,20 @@ Oct 15 19:51:35 server sshd[7592]: Address 1.2.3.4 maps to 1234.bbbbbb.com, but #8 DenyUsers https://github.com/fail2ban/fail2ban/issues/47 Apr 16 22:01:15 al-ribat sshd[5154]: User root from 46.45.128.3 not allowed because listed in DenyUsers -# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=648020 -Nov 8 11:19:38 bar sshd[25427]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=1.2.3.6 +#9 OpenSolaris patch - pull https://github.com/fail2ban/fail2ban/pull/182 +Mar 29 05:59:23 dusky sshd[20878]: [ID 800047 auth.info] Failed keyboard-interactive for from 205.186.180.55 port 42742 ssh2 +Mar 29 05:20:09 dusky sshd[19558]: [ID 800047 auth.info] Failed keyboard-interactive for james from 205.186.180.30 port 54520 ssh2 + +#10 OSX syslog error +Apr 29 17:16:20 Jamess-iMac.local sshd[62312]: error: PAM: authentication error for james from example.com via 192.168.1.201 +Apr 29 20:11:08 Jamess-iMac.local sshd[63814]: [ID 800047 auth.info] Failed keyboard-interactive for from 205.186.180.35 port 42742 ssh2 +Apr 29 20:12:08 Jamess-iMac.local sshd[63814]: [ID 800047 auth.info] Failed keyboard-interactive for james from 205.186.180.22 port 54520 ssh2 +Apr 29 20:13:08 Jamess-iMac.local sshd[63814]: Failed keyboard-interactive for james from 205.186.180.42 port 54520 ssh2 +Apr 29 20:14:08 Jamess-iMac.local sshd[63814]: Failed keyboard-interactive for from 205.186.180.44 port 42742 ssh2 +Apr 30 01:42:12 Jamess-iMac.local sshd[2554]: Failed keyboard-interactive/pam for invalid user jamedds from 205.186.180.77 port 33723 ssh2 +Apr 29 12:53:38 Jamess-iMac.local sshd[47831]: error: PAM: authentication failure for james from 205.186.180.88 via 192.168.1.201 +Apr 29 13:53:38 Jamess-iMac.local sshd[47831]: error: PAM: Authentication failure for james from 205.186.180.99 via 192.168.1.201 +Apr 29 15:53:38 Jamess-iMac.local sshd[47831]: error: PAM: Authentication error for james from 205.186.180.100 via 192.168.1.201 +Apr 29 16:53:38 Jamess-iMac.local sshd[47831]: error: PAM: authentication error for james from 205.186.180.101 via 192.168.1.201 +Apr 29 17:53:38 Jamess-iMac.local sshd[47831]: error: PAM: authentication error for james from 205.186.180.102 +Apr 29 18:53:38 Jamess-iMac.local sshd[47831]: error: PAM: authentication error for james from 205.186.180.103 diff --git a/testcases/filtertestcase.py b/testcases/filtertestcase.py index a20a932d..00946f90 100644 --- a/testcases/filtertestcase.py +++ b/testcases/filtertestcase.py @@ -22,6 +22,7 @@ __copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko" __license__ = "GPL" +from __builtin__ import open as fopen import unittest import os import sys @@ -38,6 +39,20 @@ from server.failmanager import FailManagerEmpty # Useful helpers # +# yoh: per Steven Hiscocks's insight while troubleshooting +# https://github.com/fail2ban/fail2ban/issues/103#issuecomment-15542836 +# adding a sufficiently large buffer might help to guarantee that +# writes happen atomically. +def open(*args): + """Overload built in open so we could assure sufficiently large buffer + + Explicit .flush would be needed to assure that changes leave the buffer + """ + if len(args) == 2: + # ~50kB buffer should be sufficient for all tests here. + args = args + (50000,) + return fopen(*args) + def _killfile(f, name): try: f.close() @@ -48,6 +63,11 @@ def _killfile(f, name): except: pass + # there might as well be the .bak file + if os.path.exists(name + '.bak'): + _killfile(None, name + '.bak') + + def _sleep_4_poll(): """PollFilter relies on file timestamps - so we might need to sleep to guarantee that they differ @@ -327,7 +347,9 @@ def get_monitor_failures_testcase(Filter_): """Generator of TestCase's for different filters/backends """ - _, testclass_name = tempfile.mkstemp('fail2ban', 'monitorfailures') + # add Filter_'s name so we could easily identify bad cows + testclass_name = tempfile.mktemp( + 'fail2ban', 'monitorfailures_%s' % (Filter_.__name__,)) class MonitorFailures(unittest.TestCase): count = 0 @@ -436,7 +458,7 @@ def get_monitor_failures_testcase(Filter_): self.file = _copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=14, mode='w') # Poll might need more time - self.assertTrue(self.isEmpty(2 + int(isinstance(self.filter, FilterPoll))*4)) + self.assertTrue(self.isEmpty(4 + int(isinstance(self.filter, FilterPoll))*2)) self.assertRaises(FailManagerEmpty, self.filter.failManager.toBan) self.assertEqual(self.filter.failManager.getFailTotal(), 2) @@ -453,6 +475,40 @@ def get_monitor_failures_testcase(Filter_): self.assertEqual(self.filter.failManager.getFailTotal(), 6) + def _test_move_into_file(self, interim_kill=False): + # if we move a new file into the location of an old (monitored) file + self.file1 = _copy_lines_between_files(GetFailures.FILENAME_01, self.name, + n=100) + # make sure that it is monitored first + self.assert_correct_last_attempt(GetFailures.FAILURES_01) + self.assertEqual(self.filter.failManager.getFailTotal(), 3) + + if interim_kill: + _killfile(None, self.name) + time.sleep(0.2) # let them know + + # now create a new one to override old one + self.file = _copy_lines_between_files(GetFailures.FILENAME_01, + self.name + '.new', n=100) + os.rename(self.name + '.new', self.name) + self.assert_correct_last_attempt(GetFailures.FAILURES_01) + self.assertEqual(self.filter.failManager.getFailTotal(), 6) + + # and to make sure that it now monitored for changes + _copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=100) + self.assert_correct_last_attempt(GetFailures.FAILURES_01) + self.assertEqual(self.filter.failManager.getFailTotal(), 9) + + + def test_move_into_file(self): + self._test_move_into_file(interim_kill=False) + + def test_move_into_file_after_removed(self): + # exactly as above test + remove file explicitly + # to test against possible drop-out of the file from monitoring + self._test_move_into_file(interim_kill=True) + + def test_new_bogus_file(self): # to make sure that watching whole directory does not effect _copy_lines_between_files(GetFailures.FILENAME_01, self.name, n=100) diff --git a/testcases/servertestcase.py b/testcases/servertestcase.py index ffb057a9..9a285553 100644 --- a/testcases/servertestcase.py +++ b/testcases/servertestcase.py @@ -19,7 +19,6 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" __version__ = "$Revision$" @@ -504,7 +503,9 @@ class TransmitterLogging(TransmitterBase): def testLogLevel(self): self.setGetTest("loglevel", "4", 4) + self.setGetTest("loglevel", "3", 3) self.setGetTest("loglevel", "2", 2) + self.setGetTest("loglevel", "1", 1) self.setGetTest("loglevel", "-1", -1) self.setGetTest("loglevel", "0", 0) self.setGetTestNOK("loglevel", "Bird") diff --git a/testcases/sockettestcase.py b/testcases/sockettestcase.py index 4cd5a687..cd13c772 100644 --- a/testcases/sockettestcase.py +++ b/testcases/sockettestcase.py @@ -19,7 +19,6 @@ # Author: Steven Hiscocks # -# $Revision$ __author__ = "Steven Hiscocks" __version__ = "$Revision$"