diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..3bffd79a --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ + +[run] +branch = True +omit = /usr* diff --git a/.gitignore b/.gitignore index b25c15b8..c2e979e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ *~ +build +dist +*.pyc +htmlcov +.coverage +*.orig +*.rej diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..2d091754 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +# vim ft=yaml +# travis-ci.org definition for Fail2Ban build +language: python +python: + - "2.5" + - "2.6" + - "2.7" +before_install: + - sudo apt-get update -qq +install: + - 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: + - 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 fd4a14ca..de8607af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,9 +4,289 @@ |_| \__,_|_|_/___|_.__/\__,_|_||_| ================================================================================ -Fail2Ban (version 0.8.7.1) 2012/07/31 +Fail2Ban (version 0.8.10) 2013/06/12 ================================================================================ +ver. 0.8.11 (2013/XX/XXX) - loves-unittests +----------- + +- Fixes: + Daniel Black & Marcel Dopita + * filter.d/apache-auth -- fixed and apache auth samples provide. closes #286 + Yaroslav Halchenko + * filter.d/common.conf -- make colon after [daemon] optional. Closes gh-267 + * filter.d/apache-common.conf -- support apache 2.4 more detailed error + log format. Closes gh-268 + * Backends changes detection and parsing. Close gh-223 and gh-103: + - Polling backend: detect changes in the files not only based on + mtime, but also on the size and inode. It should allow for + better detection of changes and log rotations on busy servers, + older python 2.4, and file systems with precision of mtime only + up to a second (e.g. ext3). + - All backends, possible race condition: do not read from a file + initially reported empty. Originally could have lead to + accounting for detected log lines multiple times. + - Do not crash if executing a command in fail2ban-client interactive + mode has failed (e.g. due to incorrect syntax). Closes gh-353 + Daniel Black & Мернов Георгий + * filter.d/dovecot.conf -- Fix when no TLS enabled - line doesn't end in , + Daniel Black + * action.d/hostsdeny -- NOTE: new dependancy 'ed'. Switched to use 'ed' across + all platforms to ensure permissions are the same before and after a ban - + closes gh-266. hostsdeny supports daemon_list now too. + * filter.d/roundcube-auth - timezone offset can be positive or negative + * action.d/bsd-ipfw - action option unsed. Fixed to blocktype for + consistency. default to port unreach instead of deny + Rolf Fokkens + * action.d/dshield.conf and complain.conf -- reorder mailx arguments. + https://bugzilla.redhat.com/show_bug.cgi?id=998020 + John Doe (ache) + * action.d/bsd-ipfw.conf - invert actionstop logic to make exist status 0. + closes gh-343. + JP Espinosa (Reviewed by O.Poplawski) + * files/redhat-initd - rewritten to use stock init.d functions thus + avoiding problems with getpid. Also $network and iptables moved + to Should- rc init fields +- New Features: + Andy Fragen and Daniel Black + * filter.d/osx-ipfw.conf - ipfw action for OSX based on random rule + numbers. + Daniel Black & ykimon + * filter.d/3proxy.conf -- filter added + Daniel Black + * filter.d/exim-spam.conf -- a splitout of exim's spam regexes + with additions for greater control over filtering spam. + * add date expression for apache-2.4 - milliseconds + Christophe Carles & Daniel Black + * filter.d/perdition.conf -- filter added + +- Enhancements: + François Boulogne and Frédéric + * filter.d/lighttpd - auth regexs for lighttpd-1.4.31 + Daniel Black + * filter.d/{asterisk,assp,dovecot,proftpd}.conf -- regex hardening + and extra failure examples in sample logs + * filter.d/apache-auth - added expressions for mod_authz, mod_auth and + mod_auth_digest failures. + * filter.d/recidive -- support f2b syslog target and anchor regex at start + * filter.d/pam-generic - added syslog prefix. Disabled support for + linux-pam before version 0.99.2.0 (2005) + * filter.d/gssftpd - anchored regex at start + * filter.d/mysqld-auth.conf - mysql can use syslog + Daniel Black & Georgiy Mernov & ftoppi & Мернов Георгий + * filter.d/exim.conf -- regex hardening and extra failure examples in + sample logs + * filter.d/named-refused.conf - BIND 9.9.3 regex changes + Daniel Black & Sebastian Arcus + * filter.d/asterisk -- more regexes + Yaroslav Halchenko + * fail2ban-regex -- refactored to provide more details (missing and + ignored lines, control over logging, etc) while maintaining look&feel + * fail2ban-client -- log to standard error. Closes gh-264 + * Fail to configure if not a single log file was found for an + enabled jail. Closes gh-63 + * is now enforced to end with an alphanumeric + * filter.d/roundcube-auth.conf -- anchored version + * date matching - for standard asctime formats prefer more detailed + first (thus use year if available) + Alexander Dietrich + * action.d/sendmail-common.conf -- added common sendmail settings file + and made the sender display name configurable + Steven Hiscocks + * filter.d/dovecot - Addition of session, time values and possible blank + user + Zurd and Daniel Black + * filter/named-refused - added refused on zone transfer + * filter.d/{courier{login,smtp},proftpd,sieve,wuftpd,xinetd} - General + regex impovements + * IMPORTANT: 'lighttpd-fastcgi' filter has been renamed to 'suhosin', which + will require changing in jail.{conf,local} if using this filter. + Zurd + * filter.d/postfix - add filter for VRFY failures. closes gh-322. + Orion Poplawski + * fail2ban.d/ and jail.d/ directories are added to etc/fail2ban to facilitate + their use + +ver. 0.8.10 (2013/06/12) - wanna-be-secure +----------- + +Primarily bugfix and enhancements release, triggered by "bugs" in +apache- filters. If you are relying on listed below apache- filters, +upgrade asap and seek your distributions to patch their fail2ban +distribution with [6ccd5781]. + +- Fixes: Yaroslav Halchenko + * [6ccd5781] filter.d/apache-{auth,nohome,noscript,overflows} - anchor + failregex at the beginning (and where applicable at the end). + Addresses a possible DoS. Closes gh-248 + * action.d/{route,shorewall}.conf - blocktype must be defined + within [Init]. Closes gh-232 +- Enhancements + Yaroslav Halchenko + * jail.conf -- assure all jails have actions and remove unused + ports specifications + Terence Namusonge + * config/filter.d/roundcube-auth.conf -- support roundcube 0.9+ + Daniel Black + * files/suse-initd -- update to the copy from stock SUSE + silviogarbes & Daniel Black + * Updates to asterisk filter. Closes gh-227/gh-230. + Carlos Alberto Lopez Perez + * Updates to asterisk to include AUTH_UNKNOWN_DOMAIN. Closes gh-244. + +ver. 0.8.9 (2013/05/13) - wanna-be-stable +---------- + +Originally targeted as a bugfix release, it incorporated many new +enhancements, few new features, and more importantly -- quite extended +tests battery with current 94% coverage (from 56% of 0.8.8). + +This release introduces over 200 of non-merge commits from 16 +contributors (sorted by number of commits): Yaroslav Halchenko, Daniel +Black, Steven Hiscocks, James Stout, Orion Poplawski, Enrico Labedzki, +ArndRa, hamilton5, pigsyn, Erwan Ben Souiden, Michael Gebetsroither, +Artur Penttinen, blotus, sebres, Nicolas Collignon, Pascal Borreli. + +Special Kudos also go to Fabian Wenk, Arturo 'Buanzo' Busleiman, Tom +Hendrikx, Yehuda Katz and other TBN heroes supporting users on +fail2ban-users mailing list and IRC. + +- Fixes: Yaroslav Halchenko + * [6f4dad46] python-2.4 is the minimal version. + * [1eb23cf8] do not rely on scripts being under /usr -- might differ e.g. + 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 + Christoph Theis, Nick Hilliard, Daniel Black + * [b3bd877d,cde71080] Make syslog -v and syslog -vv formats work on FreeBSD +- 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. + Nick Munger, Ken Menzel, Daniel Black, Christoph Theis & Fabian Wenk + * [b6d0e8a] Add and enhance the bsd-ipfw action from + FreeBSD ports. + Soulard Morgan + * [f336d9f] Add filter for webmin. Closes gh-99. + Steven Hiscocks + * [..746c7d9] bash interactive shell completions for fail2ban-*'s + Nick Hilliard + * [0c5a9c5] Add pf action. +- 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 '=='. + * [6fef85f] Strip CR and LF while analyzing the log line + 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. + * [10886e7,cec5da2,adb991a] Change actions to response with ICMP port + unreachable rather than just a drop of the packet. + 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. + James Stout + * [..2143cdf] Solaris support enhancements: + - README.Solaris + - failregex'es tune ups (sshd.conf) + - hostsdeny: do not rely on support of '-i' in sed + +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. 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. 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. 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. Closes gh-81, gh-86 + Yaroslav Halchenko +- Enhancements: + * [2d66f31] replaced uninformative "Invalid command" message with warning log + exception why command actually failed + * [958a1b0] improved failregex to "support" auth.backend = "htdigest" + * [9e7a3b7] until we make it proper module -- adjusted sys.path only if + system-wide run + * [f52ba99] downgraded "already banned" from WARN to INFO level. Closes gh-79 + * [f105379] added hints into the log on some failure return codes (e.g. 0x7f00 + for this gh-87) + * Various others: travis-ci integration, script to run tests + against all available Python versions, etc + ver. 0.8.7.1 (2012/07/31) - stable ---------- diff --git a/DEVELOP b/DEVELOP index 025b58b4..61248393 100644 --- a/DEVELOP +++ b/DEVELOP @@ -21,17 +21,247 @@ 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) -Testing +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. + +Filters ======= -Existing tests can be run by executing `fail2ban-testcases`. +* Include sample logs with 1.2.3.4 used for IP addresses and + example.com/example.org used for DNS names +* Ensure sample log is provided in testcases/files/logs/ with same name as the + filter. Each log line should include match meta data for time & IP above + every line (see other sample log files for examples) +* Ensure regexs start with a ^ and are restrictive as possible. E.g. not .* if + \d+ is sufficient +* Use the functionality of regexs http://docs.python.org/2/library/re.html +* Take a look at the source code of the application. You may see optional or + extra log messages, or parts there of, that need to form part of your regex. + +If you only have a basic knowledge of regular repressions read +http://docs.python.org/2/library/re.html first. + +Filter Security +--------------- + +Poor filter regular expressions are suseptable to DoS attacks. + +When a remote user has the ability to introduce text that will match the +filter regex, such that the inserted text matches the part, they have the +ability to deny any host they choose. + +So the part must be anchored on text generated by the application, and not +the user, to a sufficient extent that the user cannot insert the entire text. + +Filters are matched against the log line with their date removed. + +Ideally filter regex should anchor to the beginning and end of the log line +however as more applications log at the beginning than the end, achoring the +beginning is more important. If the log file used by the application is shared +with other applications, like system logs, ensure the other application that +use that log file do not log user generated text at the beginning of the line, +or, if they do, ensure the regexs of the filter are sufficient to mitigate the +risk of insertion. + +When creating a regex that extends back to the begining remember the date part +has been removed within fail2ban so theres no need to match that. If the format +is like ' error 1.2.3.4 is evil' then you will need to match the < at +the start so here the regex would start like '^<> is evil$'. + +Some applications log spaces at the end. If you're not sure add \s*$ as the +end part of the regex. + +Examples of poor filters +------------------------ + +1. Too restrictive + +We find a log message: + + Apr-07-13 07:08:36 Invalid command fial2ban from 1.2.3.4 + +We make a failregex + + ^Invalid command \S+ from + +Now think evil. The user does the command 'blah from 1.2.3.44' + +The program diliently logs: + + Apr-07-13 07:08:36 Invalid command blah from 1.2.3.44 from 1.2.3.4 + +And fail2ban matches 1.2.3.44 as the IP that it ban. A DoS attack was successful. + +The fix here is that the command can be anything so .* is approprate. + + ^Invalid command .* from + +Here the .* will match until the end of the string. Then realise it has more to +match, i.e. "from " and go back until it find this. Then it will ban +1.2.3.4 correctly. Since the is always at the end, end the regex with a $. + + ^Invalid command .* from $ + +Note if we'd just had the expression: + + ^Invalid command \S+ from $ + +Then provided the user put a space in their command they would have never been +banned. + +2. Filter regex can match other user injected data + +From the apache vulnerability CVE-2013-2178 +( original ref: https://vndh.net/note:fail2ban-089-denial-service ). + +An example bad regex for apache: + + failregex = [[]client []] user .* not found + +Since the user can do a get request on: + + GET /[client%20192.168.0.1]%20user%20root%20not%20found HTTP/1.0 +Host: remote.site + +Now the log line will be: + + [Sat Jun 01 02:17:42 2013] [error] [client 192.168.33.1] File does not exist: /srv/http/site/[client 192.168.0.1] user root not found + +As this log line doesn't match other expressions hence it matches the above +regex and blocks 192.168.33.1 as a denial of service from the HTTP requester. + +3. Applicaiton generates two identical log messages with different meanings + +If the application generates the following two messages under different +circmstances: + + client : authentication failed + client : authentication failed + + +Then it's obvious that a regex of "^client : authentication +failed$" will still cause problems if the user can trigger the second +log message with a of 123.1.1.1. + +Here there's nothing to do except request/change the application so it logs +messages differently. + + +Code Testing +============ + +Existing tests can be run by executing `fail2ban-testcases`. This has options +like --log-level that will probably be useful. `fail2ban-testcases --help` for +full options. + +Test cases should cover all usual cases, all exception cases and all inside +/ outside boundary conditions. + +Test cases should cover all branches. The coverage tool will help identify +missing branches. Also see http://nedbatchelder.com/code/coverage/branch.html +for more details. + +Install the package python-coverage to visualise your test coverage. Run the +following (note: on Debian-based systems, the script is called +`python-coverage`): + +coverage run fail2ban-testcases +coverage html + +Then look at htmlcov/index.html and see how much coverage your test cases +exert over the codebase. Full coverage is a good thing however it may not be +complete. Try to ensure tests cover as many independent paths through the +code. + +Manual Execution. To run in a development environment do: + +./fail2ban-client -c config/ -s /tmp/f2b.sock -i start + +some quick commands: + +status +add test pyinotify +status test +set test addaction iptables +set test actionban iptables echo >> /tmp/ban +set test actionunban iptables echo >> /tmp/unban +get test actionban iptables +get test actionunban iptables +set test banip 192.168.2.2 +status test + -Documentation about creating tests (when tests are required and some guidelines -for creating good tests) will be added soon. Coding Standards ================ -Coming Soon. + +Style +----- + +Please use tabs for now. Keep to 80 columns, at least for readable text. + +Tests +----- + +Add tests. They should test all the code you add in a meaning way. + +Coverage +-------- + +Test coverage should always increase as you add code. + +You may use "# pragma: no cover" in the code for branches of code that support +older versions on python. For all other uses of "pragma: no cover" or +"pragma: no branch" document the reason why its not covered. "I haven't written +a test case" isn't a sufficient reason. + +Documentation +------------- + +Ensure this documentation is up to date after changes. Also ensure that the man +pages still are accurate. Ensure that there is sufficient documentation for +your new features to be used. + +Bugs +---- + +Remove them and don't add any more. + +Git +--- + +Use the following tags in your commit messages: + +'BF:' for bug fixes +'DOC:' for documentation fixes +'ENH:' for enhancements +'TST:' for commits concerning tests only (thus not touching the main code-base) + +Multiple tags could be joined with +, e.g. "BF+TST:". + +Use the text "closes #333"/"resolves #333 "/"fixes #333" where 333 represents +an issue that is closed. Other text and details in link below. +See: https://help.github.com/articles/closing-issues-via-commit-messages + +If merge resulted in conflicts, clarify what changes were done to +corresponding files in the 'Conflicts:' section of the merge commit +message. See e.g. https://github.com/fail2ban/fail2ban/commit/f5a8a8ac + +Adding Actions +-------------- + +If you add an action.d/*.conf file also add a example in config/jail.conf +with enabled=false and maxretry=5 for ssh. Design @@ -58,18 +288,20 @@ one):: RF-Note just a note which might be useful to address while doing RF JailThread -> Filter -> FileFilter -> {FilterPoll, FilterPyinotify, ...} - | | * FileContainer - | + FailManager - | + DateDetector - \- -> Actions - * Actions - + BanManager - + | * FileContainer + + FailManager + + DateDetector + + Jail (provided in __init__) which contains this Filter + (used for passing tickets from FailManager to Jail's __queue) Server + Jails * Jail - + Filter + + Filter (in __filter) * tickets (in __queue) + + Actions (in __action) + * Action + + BanManager + failmanager.py ~~~~~~~~~~~~~~ @@ -125,12 +357,14 @@ FileContainer .__pos Keeps the position pointer + +dnsutils.py +~~~~~~~~~~~ + DNSUtils Utility class for DNS and IP handling - RF-Note: convert to functions within a separate submodule - filter*.py ~~~~~~~~~~ @@ -147,10 +381,103 @@ one way or another provide except FailManagerEmpty: self.failManager.cleanup(MyTime.time()) -thus channeling "ban tickets" from their failManager to a +thus channeling "ban tickets" from their failManager to the corresponding jail. action.py ~~~~~~~~~ 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 ) + git commit -m 'update man pages for release' man/* + +# Make sure the tests pass + + ./fail2ban-testcases-all + +# Prepare/upload source and rpm binary distributions + + python setup.py check + python setup.py sdist + python setup.py bdist_rpm + python setup.py upload + +# Run the following and update the wiki with output: + + python -c 'import common.protocol; common.protocol.printWiki()' + +# Email users and development list of release + +# notify distributors + +Post Release +============ + +Add the following to the top of the ChangeLog + +ver. 0.8.12 (2013/XX/XXX) - wanna-be-released +----------- + +- Fixes: + +- New Features: + +- Enhancements: + + +and adjust common/version.py to carry .dev suffix to signal +a version under development. diff --git a/MANIFEST b/MANIFEST index eef145b6..f637dca0 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,8 +1,11 @@ -README +README.md +README.Solaris ChangeLog TODO THANKS COPYING +DEVELOP +doc/run-rootless.txt fail2ban-client fail2ban-server fail2ban-testcases @@ -40,6 +43,30 @@ server/banmanager.py server/datetemplate.py server/mytime.py server/failregex.py +testcases/files/testcase-usedns.log +testcases/files/logs/bsd/syslog-plain.txt +testcases/files/logs/bsd/syslog-v.txt +testcases/files/logs/bsd/syslog-vv.txt +testcases/files/logs/apache-overflows +testcases/files/logs/assp +testcases/files/logs/asterisk +testcases/files/logs/dovecot +testcases/files/logs/exim +testcases/files/logs/lighttpd +testcases/files/logs/mysqld.log +testcases/files/logs/named-refused +testcases/files/logs/pam-generic +testcases/files/logs/postfix +testcases/files/logs/proftpd +testcases/files/logs/pure-ftpd +testcases/files/logs/roundcube-auth +testcases/files/logs/sasl +testcases/files/logs/sogo-auth +testcases/files/logs/sshd +testcases/files/logs/sshd-ddos +testcases/files/logs/vsftpd +testcases/files/logs/webmin-auth +testcases/files/logs/wu-ftpd testcases/banmanagertestcase.py testcases/failmanagertestcase.py testcases/clientreadertestcase.py @@ -48,13 +75,17 @@ testcases/__init__.py testcases/datedetectortestcase.py testcases/actiontestcase.py testcases/servertestcase.py +testcases/sockettestcase.py testcases/files/testcase01.log testcases/files/testcase02.log testcases/files/testcase03.log testcases/files/testcase04.log +testcases/misctestcase.py +testcases/utils.py setup.py setup.cfg common/__init__.py +common/exceptions.py common/helpers.py common/version.py common/protocol.py @@ -70,7 +101,7 @@ config/filter.d/couriersmtp.conf config/filter.d/cyrus-imap.conf config/filter.d/exim.conf config/filter.d/gssftpd.conf -config/filter.d/lighttpd-fastcgi.conf +config/filter.d/suhosin.conf config/filter.d/named-refused.conf config/filter.d/postfix.conf config/filter.d/proftpd.conf @@ -86,6 +117,22 @@ config/filter.d/vsftpd.conf config/filter.d/webmin-auth.conf config/filter.d/wuftpd.conf config/filter.d/xinetd-fail.conf +config/filter.d/asterisk.conf +config/filter.d/dovecot.conf +config/filter.d/dropbear.conf +config/filter.d/lighttpd-auth.conf +config/filter.d/recidive.conf +config/filter.d/roundcube-auth.conf +config/filter.d/assp.conf +config/filter.d/mysqld-auth.conf +config/filter.d/sogo-auth.conf +config/action.d/bsd-ipfw.conf +config/action.d/dummy.conf +config/action.d/iptables-blocktype.conf +config/action.d/iptables-ipset-proto4.conf +config/action.d/iptables-ipset-proto6.conf +config/action.d/iptables-xt_recent-echo.conf +config/action.d/route.conf config/action.d/complain.conf config/action.d/dshield.conf config/action.d/hostsdeny.conf @@ -101,6 +148,7 @@ config/action.d/mail-buffered.conf config/action.d/mail-whois.conf config/action.d/mail-whois-lines.conf config/action.d/mynetwatchman.conf +config/action.d/pf.conf config/action.d/sendmail.conf config/action.d/sendmail-buffered.conf config/action.d/sendmail-whois.conf @@ -108,6 +156,8 @@ config/action.d/sendmail-whois-lines.conf config/action.d/shorewall.conf config/fail2ban.conf man/fail2ban-client.1 +man/fail2ban.1 +man/jail.conf.5 man/fail2ban-client.h2m man/fail2ban-server.1 man/fail2ban-server.h2m @@ -121,8 +171,14 @@ 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 files/nagios/check_fail2ban files/nagios/f2ban.txt +files/bash-completion +files/fail2ban-tmpfiles.conf +files/fail2ban.service +files/ipmasq-ZZZzzz_fail2ban.rul +files/nagios/README diff --git a/README b/README deleted file mode 100644 index bc365482..00000000 --- a/README +++ /dev/null @@ -1,95 +0,0 @@ - __ _ _ ___ _ - / _|__ _(_) |_ ) |__ __ _ _ _ - | _/ _` | | |/ /| '_ \/ _` | ' \ - |_| \__,_|_|_/___|_.__/\__,_|_||_| - -================================================================================ -Fail2Ban (version 0.8.7) 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.7.tar.bz2 -> cd fail2ban-0.8.7 -> 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., 59 Temple Place, -Suite 330, Boston, MA 02111-1307 USA diff --git a/README.Solaris b/README.Solaris new file mode 100644 index 00000000..e41e3811 --- /dev/null +++ b/README.Solaris @@ -0,0 +1,122 @@ +# 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 configuration file for logging fail2ban on Solaris +# +[Definition] + +logtarget = /var/adm/fail2ban.log + + +REQ: Create /etc/fail2ban/jail.local containing: + +[ssh-tcpwrapper] + +enabled = true +filter = sshd +action = hostsdeny[daemon_list=sshd] + 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 remove the line. + +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 + check /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: + + sshd: 1.2.3.4 diff --git a/README.md b/README.md new file mode 100644 index 00000000..05e92fd6 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ + __ _ _ ___ _ + / _|__ _(_) |_ ) |__ __ _ _ _ + | _/ _` | | |/ /| '_ \/ _` | ' \ + |_| \__,_|_|_/___|_.__/\__,_|_||_| + v0.8.10 2013/06/12 + +## 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.10.tar.bz2 + cd fail2ban-0.8.10 + 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?branch=master)](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 found a severe security vulnerability in Fail2Ban? +email details to fail2ban-vulnerabilities at lists dot sourceforge dot net . + +### 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. See +[Fail2Ban wiki](http://www.fail2ban.org/wiki/index.php/HOWTO_Seek_Help) +on further instructions. + +### 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 ff20d07d..ad8eed60 100644 --- a/THANKS +++ b/THANKS @@ -1,47 +1,71 @@ -Fail2Ban is an open source project with many contributions from its -users community. Below is an alphabetically sorted partial list of the -contributors to the project. If you have been left off, please let us -know (preferably send a pull request on github with the "fix") and you -will be added +Fail2Ban is an open source project which was conceived and originally +developed by Cyril Jaquier until 2010. Since then Fail2Ban grew into +a community-driven project with many contributions from its users. +Below is an alphabetically sorted partial list of the contributors to +the project. If you have been left off, please let us know +(preferably send a pull request on github with the "fix") and you will +be added Adrien Clerc +ache Andrey G. Grozin +Andy Fragen Arturo 'Buanzo' Busleiman Axel Thimm +Beau Raines Bill Heaton +Carlos Alberto Lopez Perez Christian Rauch +Christophe Carles Christoph Haas Christos Psonis +Cyril Jaquier Daniel B. Cid +Daniel Black David Nutter Eric Gerbier +Enrico Labedzki +ftoppi +François Boulogne +Frédéric +Georgiy Mernov Guillaume Delvit Hanno 'Rince' Wagner Iain Lea Jonathan Kamens Jonathan Underwood Joël Bertrand +JP Espinosa Justin Shore Kévin Drapel kojiro +Manuel Arostegui Ramirez +Marcel Dopita Mark Edgington Markus Hoffmann Marvin Rouge mEDI +Мернов Георгий Michael C. Haller Michael Hanselmann -NickMunger +Nick Munger Patrick Börjesson Raphaël Marichez René Berber Robert Edeker +Rolf Fokkens Russell Odom +Sebastian Arcus Sireyessire +silviogarbes Stephen Gildea +Steven Hiscocks Tom Pike Tyler Vaclav Misek Vincent Deffontaines Yaroslav Halchenko +ykimon Yehuda Katz zugeschmiert +Zurd diff --git a/TODO b/TODO index fc43dc2c..33263d3e 100644 --- a/TODO +++ b/TODO @@ -13,9 +13,14 @@ Legend: # partially done * done -- Removed relative imports +- more detailed explaination in DEVELOP for new developers (eg. howto build this HEX numbers in ChangeLog) -- Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/ +- Run tests though all filters/examples files - (see sshd example file) as unit + test + +* Removed relative imports + +* Cleanup fail2ban-client and fail2ban-server. Move code to server/ and client/ - Add timeout to external commands (signal alarm, watchdog thread, etc) diff --git a/client/__init__.py b/client/__init__.py index c448827b..2b76f4b6 100644 --- a/client/__init__.py +++ b/client/__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/client/actionreader.py b/client/actionreader.py index 581a1b3c..8f60b55b 100644 --- a/client/actionreader.py +++ b/client/actionreader.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -35,8 +32,8 @@ logSys = logging.getLogger("fail2ban.client.config") class ActionReader(ConfigReader): - def __init__(self, action, name): - ConfigReader.__init__(self) + def __init__(self, action, name, **kwargs): + ConfigReader.__init__(self, **kwargs) self.__file = action[0] self.__cInfo = action[1] self.__name = name diff --git a/client/beautifier.py b/client/beautifier.py index a75655e7..8e690656 100644 --- a/client/beautifier.py +++ b/client/beautifier.py @@ -17,20 +17,14 @@ # 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, Yaroslav Halchenko" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2013- Yaroslav Halchenko" __license__ = "GPL" -from server.jails import UnknownJailException -from server.jails import DuplicateJailException import logging +from common.exceptions import UnknownJailException, DuplicateJailException + # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.client.config") @@ -62,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..f5e124a1 100644 --- a/client/configparserinc.py +++ b/client/configparserinc.py @@ -19,11 +19,8 @@ # Author: Yaroslav Halchenko # Modified: Cyril Jaquier -# $Revision$ __author__ = 'Yaroslav Halhenko' -__revision__ = '$Revision$' -__date__ = '$Date$' __copyright__ = 'Copyright (c) 2007 Yaroslav Halchenko' __license__ = 'GPL' diff --git a/client/configreader.py b/client/configreader.py index 063484e8..3d3aff94 100644 --- a/client/configreader.py +++ b/client/configreader.py @@ -19,15 +19,12 @@ # Author: Cyril Jaquier # Modified by: Yaroslav Halchenko (SafeConfigParserWithIncludes) -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -import logging, os +import glob, logging, os from configparserinc import SafeConfigParserWithIncludes from ConfigParser import NoOptionError, NoSectionError @@ -35,36 +32,57 @@ from ConfigParser import NoOptionError, NoSectionError logSys = logging.getLogger("fail2ban.client.config") class ConfigReader(SafeConfigParserWithIncludes): + + DEFAULT_BASEDIR = '/etc/fail2ban' - BASE_DIRECTORY = "/etc/fail2ban/" - - def __init__(self): + def __init__(self, basedir=None): SafeConfigParserWithIncludes.__init__(self) + self.setBaseDir(basedir) self.__opts = None - #@staticmethod - def setBaseDir(folderName): - path = folderName.rstrip('/') - ConfigReader.BASE_DIRECTORY = path + '/' - setBaseDir = staticmethod(setBaseDir) - - #@staticmethod - def getBaseDir(): - return ConfigReader.BASE_DIRECTORY - getBaseDir = staticmethod(getBaseDir) + def setBaseDir(self, basedir): + if basedir is None: + basedir = ConfigReader.DEFAULT_BASEDIR # stock system location + self._basedir = basedir.rstrip('/') + + def getBaseDir(self): + return self._basedir def read(self, filename): - basename = ConfigReader.BASE_DIRECTORY + filename - logSys.debug("Reading " + basename) - bConf = basename + ".conf" - bLocal = basename + ".local" - if os.path.exists(bConf) or os.path.exists(bLocal): - SafeConfigParserWithIncludes.read(self, [bConf, bLocal]) - return True - else: - logSys.error(bConf + " and " + bLocal + " do not exist") + 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", + basename + ".local" ] + + # choose only existing ones + config_files = filter(os.path.exists, config_files) + + # possible further customizations under a .conf.d directory + config_dir = basename + '.d' + config_files += sorted(glob.glob('%s/*.conf' % config_dir)) + + if len(config_files): + # at least one config exists and accessible + 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(), + "among existing ones: " + ', '.join(config_files)][bool(len(config_files))])) + + return False + ## # Read the options. # @@ -85,7 +103,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: @@ -93,9 +111,9 @@ class ConfigReader(SafeConfigParserWithIncludes): logSys.error(e) values[option[1]] = option[2] except NoOptionError: - if not option[2] == None: - logSys.warn("'%s' not defined in '%s'. Using default value" - % (option[1], sec)) + 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] except ValueError: logSys.warn("Wrong value for '" + option[1] + "' in '" + sec + diff --git a/client/configurator.py b/client/configurator.py index 0baff2d8..d5f46305 100644 --- a/client/configurator.py +++ b/client/configurator.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -43,15 +40,19 @@ class Configurator: self.__fail2ban = Fail2banReader() self.__jails = JailsReader() - #@staticmethod - def setBaseDir(folderName): - ConfigReader.setBaseDir(folderName) - setBaseDir = staticmethod(setBaseDir) + def setBaseDir(self, folderName): + self.__fail2ban.setBaseDir(folderName) + self.__jails.setBaseDir(folderName) - #@staticmethod - def getBaseDir(): - return ConfigReader.getBaseDir() - getBaseDir = staticmethod(getBaseDir) + def getBaseDir(self): + fail2ban_basedir = self.__fail2ban.getBaseDir() + jails_basedir = self.__jails.getBaseDir() + if fail2ban_basedir != jails_basedir: + logSys.error("fail2ban.conf and jails.conf readers have differing " + "basedirs: %r and %r. " + "Returning the one for fail2ban.conf" + % (fail2ban_basedir, jails_basedir)) + return fail2ban_basedir def readEarly(self): self.__fail2ban.read() diff --git a/client/csocket.py b/client/csocket.py index 6e014e23..3d8362b5 100644 --- a/client/csocket.py +++ b/client/csocket.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/client/fail2banreader.py b/client/fail2banreader.py index ee097bd6..0171b457 100644 --- a/client/fail2banreader.py +++ b/client/fail2banreader.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -35,14 +32,15 @@ logSys = logging.getLogger("fail2ban.client.config") class Fail2banReader(ConfigReader): - def __init__(self): - ConfigReader.__init__(self) + def __init__(self, **kwargs): + ConfigReader.__init__(self, **kwargs) def read(self): ConfigReader.read(self, "fail2ban") def getEarlyOptions(self): - opts = [["string", "socket", "/tmp/fail2ban.sock"]] + opts = [["string", "socket", "/var/run/fail2ban/fail2ban.sock"], + ["string", "pidfile", "/var/run/fail2ban/fail2ban.pid"]] return ConfigReader.getOptions(self, "Definition", opts) def getOptions(self): diff --git a/client/filterreader.py b/client/filterreader.py index b7a72f9c..f75190f9 100644 --- a/client/filterreader.py +++ b/client/filterreader.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -35,8 +32,8 @@ logSys = logging.getLogger("fail2ban.client.config") class FilterReader(ConfigReader): - def __init__(self, fileName, name): - ConfigReader.__init__(self) + def __init__(self, fileName, name, **kwargs): + ConfigReader.__init__(self, **kwargs) self.__file = fileName self.__name = name diff --git a/client/jailreader.py b/client/jailreader.py index f66dc010..1a2be432 100644 --- a/client/jailreader.py +++ b/client/jailreader.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -40,10 +37,11 @@ class JailReader(ConfigReader): actionCRE = re.compile("^((?:\w|-|_|\.)+)(?:\[(.*)\])?$") - def __init__(self, name): - ConfigReader.__init__(self) + def __init__(self, name, force_enable=False, **kwargs): + ConfigReader.__init__(self, **kwargs) self.__name = name self.__filter = None + self.__force_enable = force_enable self.__actions = list() def setName(self, value): @@ -53,10 +51,10 @@ class JailReader(ConfigReader): return self.__name def read(self): - ConfigReader.read(self, "jail") + return ConfigReader.read(self, "jail") def isEnabled(self): - return self.__opts["enabled"] + return self.__force_enable or self.__opts["enabled"] def getOptions(self): opts = [["bool", "enabled", "false"], @@ -75,7 +73,8 @@ class JailReader(ConfigReader): if self.isEnabled(): # Read filter - self.__filter = FilterReader(self.__opts["filter"], self.__name) + self.__filter = FilterReader(self.__opts["filter"], self.__name, + basedir=self.getBaseDir()) ret = self.__filter.read() if ret: self.__filter.getOptions(self.__opts) @@ -86,8 +85,10 @@ class JailReader(ConfigReader): # Read action for act in self.__opts["action"].split('\n'): try: + if not act: # skip empty actions + continue splitAct = JailReader.splitAction(act) - action = ActionReader(splitAct, self.__name) + action = ActionReader(splitAct, self.__name, basedir=self.getBaseDir()) ret = action.read() if ret: action.getOptions(self.__opts) @@ -96,20 +97,36 @@ class JailReader(ConfigReader): raise AttributeError("Unable to read action") except Exception, e: logSys.error("Error in action definition " + act) - logSys.debug(e) + logSys.debug("Caught exception: %s" % (e,)) return False + if not len(self.__actions): + logSys.warn("No actions were defined for %s" % self.__name) return True - def convert(self): + def convert(self, allow_no_files=False): + """Convert read before __opts to the commands stream + + Parameters + ---------- + allow_missing : bool + Either to allow log files to be missing entirely. Primarily is + used for testing + """ + stream = [] for opt in self.__opts: if opt == "logpath": + found_files = 0 for path in self.__opts[opt].split("\n"): pathList = glob.glob(path) if len(pathList) == 0: - logSys.error("No file found for " + path) + logSys.error("No file(s) found for glob %s" % path) for p in pathList: + found_files += 1 stream.append(["set", self.__name, "addlogpath", p]) + if not (found_files or allow_no_files): + raise ValueError( + "Have not found any log file for %s jail" % self.__name) elif opt == "backend": backend = self.__opts[opt] elif opt == "maxretry": @@ -142,12 +159,20 @@ class JailReader(ConfigReader): def splitAction(action): m = JailReader.actionCRE.match(action) d = dict() - if not m.group(2) == None: + mgroups = m.groups() + if len(mgroups) == 2: + action_name, action_opts = mgroups + elif len(mgroups) == 1: + action_name, action_opts = mgroups[0], None + else: + raise ValueError("While reading action %s we should have got up to " + "2 groups. Got: %r" % (action, mgroups)) + if not action_opts is None: # Huge bad hack :( This method really sucks. TODO Reimplement it. actions = "" escapeChar = None allowComma = False - for c in m.group(2): + for c in action_opts: if c in ('"', "'") and not allowComma: # Start escapeChar = c @@ -172,6 +197,6 @@ class JailReader(ConfigReader): try: d[p[0].strip()] = p[1].strip() except IndexError: - logSys.error("Invalid argument %s in '%s'" % (p, m.group(2))) - return [m.group(1), d] + logSys.error("Invalid argument %s in '%s'" % (p, action_opts)) + return [action_name, d] splitAction = staticmethod(splitAction) diff --git a/client/jailsreader.py b/client/jailsreader.py index bedc5a3c..00c63e3c 100644 --- a/client/jailsreader.py +++ b/client/jailsreader.py @@ -18,12 +18,9 @@ # 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" @@ -35,21 +32,37 @@ from jailreader import JailReader logSys = logging.getLogger("fail2ban.client.config") class JailsReader(ConfigReader): - - def __init__(self): - ConfigReader.__init__(self) + + def __init__(self, force_enable=False, **kwargs): + """ + Parameters + ---------- + force_enable : bool, optional + Passed to JailReader to force enable the jails. + It is for internal use + """ + ConfigReader.__init__(self, **kwargs) self.__jails = list() - + self.__force_enable = force_enable + def read(self): - ConfigReader.read(self, "jail") - - def getOptions(self, section = None): + return ConfigReader.read(self, "jail") + + def getOptions(self, section=None): + """Reads configuration for jail(s) and adds enabled jails to __jails + """ opts = [] self.__opts = ConfigReader.getOptions(self, "Definition", opts) - if section: - # Get the options of a specific jail. - jail = JailReader(section) + if section is None: + sections = self.sections() + else: + sections = [ section ] + + # Get the options of all jails. + for sec in sections: + jail = JailReader(sec, basedir=self.getBaseDir(), + force_enable=self.__force_enable) jail.read() ret = jail.getOptions() if ret: @@ -57,34 +70,30 @@ class JailsReader(ConfigReader): # We only add enabled jails self.__jails.append(jail) else: - logSys.error("Errors in jail '%s'. Skipping..." % section) + logSys.error("Errors in jail %r. Skipping..." % sec) return False - else: - # Get the options of all jails. - for sec in self.sections(): - jail = JailReader(sec) - jail.read() - ret = jail.getOptions() - if ret: - if jail.isEnabled(): - # We only add enabled jails - self.__jails.append(jail) - else: - logSys.error("Errors in jail '" + sec + "'. Skipping...") - return False return True - - def convert(self): + + def convert(self, allow_no_files=False): + """Convert read before __opts and jails to the commands stream + + Parameters + ---------- + allow_missing : bool + Either to allow log files to be missing entirely. Primarily is + used for testing + """ + stream = list() for opt in self.__opts: if opt == "": stream.append([]) # Convert jails for jail in self.__jails: - stream.extend(jail.convert()) + stream.extend(jail.convert(allow_no_files=allow_no_files)) # Start jails for jail in self.__jails: stream.append(["start", jail.getName()]) - + return stream - + diff --git a/common/__init__.py b/common/__init__.py index c448827b..3eae8ee3 100644 --- a/common/__init__.py +++ b/common/__init__.py @@ -19,10 +19,12 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" + +import logging + +# Custom debug level +logging.HEAVYDEBUG = 5 diff --git a/common/exceptions.py b/common/exceptions.py new file mode 100644 index 00000000..7e933544 --- /dev/null +++ b/common/exceptions.py @@ -0,0 +1,36 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +# vi: set ft=python sts=4 ts=4 sw=4 noet : +"""Fail2Ban exceptions used by both client and server + +""" +# This file is part of Fail2Ban. +# +# Fail2Ban is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +__author__ = "Cyril Jaquier, Yaroslav Halchenko" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2011-2012 Yaroslav Halchenko" +__license__ = "GPL" + +# +# Jails +# +class DuplicateJailException(Exception): + pass + +class UnknownJailException(Exception): + pass + + + diff --git a/common/helpers.py b/common/helpers.py index 6115b971..74ea7a7a 100644 --- a/common/helpers.py +++ b/common/helpers.py @@ -17,25 +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 -# Author: Arturo 'Buanzo' Busleiman -# -# $Revision$ - -__author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" -__copyright__ = "Copyright (c) 2009 Cyril Jaquier" +__author__ = "Cyril Jaquier, Arturo 'Buanzo' Busleiman, Yaroslav Halchenko" __license__ = "GPL" def formatExceptionInfo(): - """ Author: Arturo 'Buanzo' Busleiman """ + """ Consistently format exception information """ import sys cla, exc = sys.exc_info()[:2] - excName = cla.__name__ - try: - excArgs = exc.__dict__["args"] - except KeyError: - excArgs = str(exc) - return (excName, excArgs) + return (cla.__name__, str(exc)) diff --git a/common/protocol.py b/common/protocol.py index db2b07eb..9309ce7f 100644 --- a/common/protocol.py +++ b/common/protocol.py @@ -19,11 +19,8 @@ # Author: Cyril Jaquier # -# $Revision$ __author__ = "Cyril Jaquier" -__version__ = "$Revision$" -__date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" @@ -40,6 +37,7 @@ protocol = [ ["stop", "stops all jails and terminate the server"], ["status", "gets the current status of the server"], ["ping", "tests if the server is alive"], +["help", "return this output"], ['', "LOGGING", ""], ["set loglevel ", "sets logging level to . 0 is minimal, 4 is debug"], ["get loglevel", "gets the logging level"], @@ -64,6 +62,7 @@ protocol = [ ["set bantime