mirror of https://github.com/fail2ban/fail2ban
MISC: merge from master
commit
d5291517a7
|
@ -63,13 +63,15 @@ ver. 0.8.11 (2013/XX/XXX) - loves-unittests
|
|||
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
|
||||
closes gh-266. hostsdeny supports daemon_list now too.
|
||||
- New Features:
|
||||
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.
|
||||
Christophe Carles & Daniel Black
|
||||
* filter.d/perdition.conf -- filter added
|
||||
- Enhancements:
|
||||
Daniel Black
|
||||
* filter.d/{asterisk,assp,dovecot,proftpd}.conf -- regex hardening
|
||||
|
|
4
DEVELOP
4
DEVELOP
|
@ -253,6 +253,10 @@ 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
|
||||
--------------
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ REQ: Create /etc/fail2ban/jail.local containing:
|
|||
|
||||
enabled = true
|
||||
filter = sshd
|
||||
action = hostsdeny
|
||||
action = hostsdeny[daemon_list=sshd]
|
||||
sendmail-whois[name=SSH, dest=you@example.com]
|
||||
ignoreregex = for myuser from
|
||||
logpath = /var/adm/auth.log
|
||||
|
@ -119,6 +119,4 @@ GOTCHAS AND FIXMES
|
|||
|
||||
* Fail2ban adds lines like these to /etc/hosts.deny:
|
||||
|
||||
ALL: 1.2.3.4
|
||||
|
||||
wouldn't it be better to just block sshd?
|
||||
sshd: 1.2.3.4
|
||||
|
|
1
THANKS
1
THANKS
|
@ -11,6 +11,7 @@ Axel Thimm
|
|||
Bill Heaton
|
||||
Carlos Alberto Lopez Perez
|
||||
Christian Rauch
|
||||
Christophe Carles
|
||||
Christoph Haas
|
||||
Christos Psonis
|
||||
Daniel B. Cid
|
||||
|
|
|
@ -341,7 +341,7 @@ class Fail2banRegex(object):
|
|||
|
||||
def process(self, test_lines):
|
||||
|
||||
for line in test_lines:
|
||||
for line_no, line in enumerate(test_lines):
|
||||
if line.startswith('#') or not line.strip():
|
||||
# skip comment and empty lines
|
||||
continue
|
||||
|
@ -357,6 +357,9 @@ class Fail2banRegex(object):
|
|||
self._line_stats.missed_lines.append(line)
|
||||
self._line_stats.tested += 1
|
||||
|
||||
if line_no % 10 == 0:
|
||||
self._filter.dateDetector.sortTemplate()
|
||||
|
||||
def printLines(self, ltype):
|
||||
lstats = self._line_stats
|
||||
assert(len(lstats.missed_lines) == lstats.tested - (lstats.matched + lstats.ignored))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Cyril Jaquier
|
||||
# Edited for cross platform by: James Stout, Yaroslav Halchenko and Daniel Black
|
||||
#
|
||||
#
|
||||
|
||||
|
@ -31,7 +32,7 @@ actioncheck =
|
|||
# Values: CMD
|
||||
#
|
||||
actionban = IP=<ip> &&
|
||||
printf %%b "ALL: $IP\n" >> <file>
|
||||
printf %%b "<daemon_list>: $IP\n" >> <file>
|
||||
|
||||
# Option: actionunban
|
||||
# Notes.: command executed when unbanning an IP. Take care that the
|
||||
|
@ -39,7 +40,7 @@ actionban = IP=<ip> &&
|
|||
# Tags: See jail.conf(5) man page
|
||||
# Values: CMD
|
||||
#
|
||||
actionunban = echo "/ALL: <ip>$/<br>d<br>w<br>q" | ed <file>
|
||||
actionunban = echo "/^<daemon_list>: <ip>$/<br>d<br>w<br>q" | ed <file>
|
||||
|
||||
[Init]
|
||||
|
||||
|
@ -48,3 +49,9 @@ actionunban = echo "/ALL: <ip>$/<br>d<br>w<br>q" | ed <file>
|
|||
# Values: STR Default: /etc/hosts.deny
|
||||
#
|
||||
file = /etc/hosts.deny
|
||||
|
||||
# Option: daemon_list
|
||||
# Notes: The list of services that this action will deny. See the man page
|
||||
# for hosts.deny/hosts_access. Default is all services.
|
||||
# Values: STR Default: ALL
|
||||
daemon_list = ALL
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Fail2Ban configuration file
|
||||
#
|
||||
# Author: Christophe Carles and Daniel Black
|
||||
#
|
||||
#
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
|
||||
_daemon=perdition.\S+
|
||||
|
||||
failregex = ^%(__prefix_line)sAuth: <HOST>:\d+->(\d{1,3}\.){3}\d{1,3}:\d+ client-secure=\S+ authorisation_id=NONE authentication_id=".+" server="\S+" protocol=\S+ server-secure=\S+ status="failed: (local authentication failure|Re-Authentication Failure)"$
|
||||
^%(__prefix_line)sFatal Error reading authentication information from client <HOST>:\d+->(\d{1,3}\.){3}\d{1,3}:\d+: Exiting child$
|
|
@ -198,7 +198,7 @@ logpath = /root/path/to/assp/logs/maillog.txt
|
|||
[sshd-tcpwrapper]
|
||||
|
||||
filter = sshd
|
||||
action = hostsdeny
|
||||
action = hostsdeny[daemon_list=sshd]
|
||||
sendmail-whois[name=SSH, dest=you@example.com]
|
||||
ignoreregex = for myuser from
|
||||
logpath = /var/log/sshd.log
|
||||
|
@ -540,3 +540,10 @@ enabled = false
|
|||
filter = exim-spam
|
||||
action = iptables-multiport[name=exim-spam,port="25,465,587"]
|
||||
logpath = /var/log/exim/mainlog
|
||||
|
||||
[perdition]
|
||||
enabled = false
|
||||
filter = perdition
|
||||
action = iptables-multiport[name=perdition,port="110,143,993,995"]
|
||||
logpath = /var/log/maillog
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Author: Cyril Jaquier
|
||||
#
|
||||
#
|
||||
|
||||
__author__ = "Cyril Jaquier"
|
||||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
|
@ -32,7 +32,7 @@ from jailreader import JailReader
|
|||
logSys = logging.getLogger(__name__)
|
||||
|
||||
class JailsReader(ConfigReader):
|
||||
|
||||
|
||||
def __init__(self, force_enable=False, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
|
@ -44,17 +44,25 @@ class JailsReader(ConfigReader):
|
|||
ConfigReader.__init__(self, **kwargs)
|
||||
self.__jails = list()
|
||||
self.__force_enable = force_enable
|
||||
|
||||
|
||||
def read(self):
|
||||
return ConfigReader.read(self, "jail")
|
||||
|
||||
def getOptions(self, section = None):
|
||||
|
||||
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, basedir=self.getBaseDir(), force_enable=self.__force_enable)
|
||||
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:
|
||||
|
@ -62,23 +70,10 @@ 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, basedir=self.getBaseDir(), force_enable=self.__force_enable)
|
||||
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, allow_no_files=False):
|
||||
"""Convert read before __opts and jails to the commands stream
|
||||
|
||||
|
@ -99,6 +94,6 @@ class JailsReader(ConfigReader):
|
|||
# Start jails
|
||||
for jail in self.__jails:
|
||||
stream.append(["start", jail.getName()])
|
||||
|
||||
|
||||
return stream
|
||||
|
||||
|
||||
|
|
|
@ -62,6 +62,9 @@ class DateTemplate:
|
|||
|
||||
def incHits(self):
|
||||
self.__hits += 1
|
||||
|
||||
def resetHits(self):
|
||||
self.__hits = 0
|
||||
|
||||
def matchDate(self, line):
|
||||
dateMatch = self.__cRegex.search(line)
|
||||
|
|
|
@ -47,7 +47,7 @@ class Ticket:
|
|||
|
||||
def __str__(self):
|
||||
return "%s: ip=%s time=%s #attempts=%d" % \
|
||||
(self.__class__, self.__ip, self.__time, self.__attempt)
|
||||
(self.__class__.__name__.split('.')[-1], self.__ip, self.__time, self.__attempt)
|
||||
|
||||
|
||||
def setIP(self, value):
|
||||
|
@ -59,12 +59,6 @@ class Ticket:
|
|||
def getIP(self):
|
||||
return self.__ip
|
||||
|
||||
def setFile(self, value):
|
||||
self.__file = value
|
||||
|
||||
def getFile(self):
|
||||
return self.__file
|
||||
|
||||
def setTime(self, value):
|
||||
self.__time = value
|
||||
|
||||
|
|
|
@ -24,8 +24,7 @@ __author__ = "Cyril Jaquier"
|
|||
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
|
||||
__license__ = "GPL"
|
||||
|
||||
import unittest
|
||||
|
||||
import unittest, calendar, datetime, re, pprint
|
||||
from fail2ban.server.datedetector import DateDetector
|
||||
from fail2ban.server.datetemplate import DateTemplate
|
||||
from fail2ban.tests.utils import setUpMyTime, tearDownMyTime
|
||||
|
@ -127,6 +126,45 @@ class DateDetectorTest(unittest.TestCase):
|
|||
self.__datedetector.getTime('2012/10/11 02:37:17 [error] 18434#0')[:6],
|
||||
m1)
|
||||
|
||||
def testDateDetectorTemplateOverlap(self):
|
||||
patterns = [template.getPattern()
|
||||
for template in self.__datedetector.getTemplates()
|
||||
if hasattr(template, "getPattern")]
|
||||
|
||||
year = 2008 # Leap year, 08 for %y can be confused with both %d and %m
|
||||
def iterDates(year):
|
||||
for month in xrange(1, 13):
|
||||
for day in xrange(2, calendar.monthrange(year, month)[1]+1, 9):
|
||||
for hour in xrange(0, 24, 6):
|
||||
for minute in xrange(0, 60, 15):
|
||||
for second in xrange(0, 60, 15): # Far enough?
|
||||
yield datetime.datetime(
|
||||
year, month, day, hour, minute, second)
|
||||
|
||||
overlapedTemplates = set()
|
||||
for date in iterDates(year):
|
||||
for pattern in patterns:
|
||||
datestr = date.strftime(pattern)
|
||||
datestrs = set([
|
||||
datestr,
|
||||
re.sub(r"(\s)0", r"\1 ", datestr),
|
||||
re.sub(r"(\s)0", r"\1", datestr)])
|
||||
for template in self.__datedetector.getTemplates():
|
||||
template.resetHits()
|
||||
for datestr in datestrs:
|
||||
if template.matchDate(datestr): # or getDate?
|
||||
template.incHits()
|
||||
|
||||
matchedTemplates = [template
|
||||
for template in self.__datedetector.getTemplates()
|
||||
if template.getHits() > 0]
|
||||
assert matchedTemplates != [] # Should match at least one
|
||||
if len(matchedTemplates) > 1:
|
||||
overlapedTemplates.add((pattern, tuple(sorted(template.getName()
|
||||
for template in matchedTemplates))))
|
||||
if overlapedTemplates:
|
||||
print "WARNING: The following date templates overlap:"
|
||||
pprint.pprint(overlapedTemplates)
|
||||
|
||||
# def testDefaultTempate(self):
|
||||
# self.__datedetector.setDefaultRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}")
|
||||
|
|
|
@ -79,6 +79,20 @@ class AddFailure(unittest.TestCase):
|
|||
ticket = self.__failManager.toBan()
|
||||
self.assertEqual(ticket.getIP(), "193.168.0.128")
|
||||
self.assertTrue(isinstance(ticket.getIP(), str))
|
||||
|
||||
# finish with rudimentary tests of the ticket
|
||||
# verify consistent str
|
||||
ticket_str = str(ticket)
|
||||
self.assertEqual(
|
||||
ticket_str,
|
||||
'FailTicket: ip=193.168.0.128 time=1167605999.0 #attempts=5')
|
||||
# and some get/set-ers otherwise not tested
|
||||
ticket.setTime(1000002000.0)
|
||||
self.assertEqual(ticket.getTime(), 1000002000.0)
|
||||
# and str() adjusted correspondingly
|
||||
self.assertEqual(
|
||||
str(ticket),
|
||||
'FailTicket: ip=193.168.0.128 time=1000002000.0 #attempts=5')
|
||||
|
||||
def testbanNOK(self):
|
||||
self.__failManager.setMaxRetry(10)
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
# and https://github.com/fail2ban/fail2ban/issues/126
|
||||
# failJSON: { "time": "2005-02-21T09:21:54", "match": true , "host": "192.0.43.10" }
|
||||
Feb 21 09:21:54 xxx postfix/smtpd[14398]: NOQUEUE: reject: RCPT from example.com[192.0.43.10]: 450 4.7.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo=
|
||||
# failJSON: { "time": "2005-07-12T07:47:48", "match": true , "host": "1.2.3.4" }
|
||||
Jul 12 07:47:48 saturn postfix/smtpd[8738]: NOQUEUE: reject: RCPT from 1-2-3-4-example.com[1.2.3.4]: 554 5.7.1 <smtp@example.com>: Relay access denied; from=<john@example.com> to=<smtp@example.org> proto=SMTP helo=<198.51.100.17>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# failJSON: { "time": "2005-07-18T16:07:18", "match": true , "host": "192.168.8.100" }
|
||||
Jul 18 16:07:18 ares perdition.imaps[3194]: Auth: 192.168.8.100:2274->193.48.191.9:993 client-secure=ssl authorisation_id=NONE authentication_id="carles" server="imap.biotoul.fr:993" protocol=IMAP4S server-secure=ssl status="failed: Re-Authentication Failure"
|
||||
# failJSON: { "time": "2005-07-18T16:08:58", "match": true , "host": "192.168.8.100" }
|
||||
Jul 18 16:08:58 ares perdition.imaps[3194]: Fatal Error reading authentication information from client 192.168.8.100:2274->193.48.191.9:993: Exiting child
|
|
@ -0,0 +1,2 @@
|
|||
# failJSON: { "time": "2004-12-01T20:36:56", "match": true , "host": "1.2.3.4" }
|
||||
Dec 1 20:36:56 mail sieve[23713]: badlogin: example.com[1.2.3.4] PLAIN authentication failure
|
Loading…
Reference in New Issue