mirror of https://github.com/fail2ban/fail2ban
Add extended info to status output using Cyrmu
parent
cfaa76a355
commit
60ac0a1a17
|
@ -12,6 +12,8 @@ before_install:
|
|||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then travis_retry sudo apt-get update -qq; fi
|
||||
install:
|
||||
- travis_retry pip install pyinotify
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2* || $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then travis_retry pip install dnspython; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3* || $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then travis_retry pip install dnspython3; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then travis_retry sudo apt-get install -qq python-gamin; cp /usr/share/pyshared/gamin.py /usr/lib/pyshared/python2.7/_gamin.so $VIRTUAL_ENV/lib/python2.7/site-packages/; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then cd ..; travis_retry pip install -q coveralls; cd -; fi
|
||||
script:
|
||||
|
|
|
@ -41,7 +41,11 @@ ver. 0.9.2 (2014/XX/XXX) - wanna-be-released
|
|||
- Monit config for fail2ban in /files/monit
|
||||
- New actions:
|
||||
- action.d/firewallcmd-multiport and action.d/firewallcmd-allports Thanks Donald Yandt
|
||||
|
||||
- New status commands:
|
||||
- fail2ban-client status <jail> extended
|
||||
- prints Cymru data (ASN, Country RIR) per banned IP
|
||||
- Requires dnspython or dnspython3
|
||||
|
||||
- Enhancements:
|
||||
* Enable multiport for firewallcmd-new action. Closes gh-834
|
||||
* files/debian-initd migrated from the debian branch and should be
|
||||
|
|
|
@ -55,6 +55,7 @@ protocol = [
|
|||
["start <JAIL>", "starts the jail <JAIL>"],
|
||||
["stop <JAIL>", "stops the jail <JAIL>. The jail is removed"],
|
||||
["status <JAIL>", "gets the current status of <JAIL>"],
|
||||
["status <JAIL> extended", "gets the current status of <JAIL> with extended info"],
|
||||
['', "JAIL CONFIGURATION", ""],
|
||||
["set <JAIL> idle on|off", "sets the idle state of <JAIL>"],
|
||||
["set <JAIL> addignoreip <IP>", "adds <IP> to the ignore list of <JAIL>"],
|
||||
|
|
|
@ -372,9 +372,20 @@ class Actions(JailThread, Mapping):
|
|||
|
||||
@property
|
||||
def status(self):
|
||||
"""Status of active bans, and total ban counts.
|
||||
"""Status of current and total ban counts and current banned IP list.
|
||||
"""
|
||||
ret = [("Currently banned", self.__banManager.size()),
|
||||
("Total banned", self.__banManager.getBanTotal()),
|
||||
("Banned IP list", self.__banManager.getBanList())]
|
||||
return ret
|
||||
|
||||
@property
|
||||
def statusExtended(self):
|
||||
"""Jail status plus banned IPs' ASN, Country and RIR
|
||||
"""
|
||||
cymru_info = self.__banManager.getBanListExtendedCymruInfo()
|
||||
ret = self.status +\
|
||||
[("Banned ASN list", self.__banManager.geBanListExtendedASN(cymru_info)),
|
||||
("Banned Country list", self.__banManager.geBanListExtendedCountry(cymru_info)),
|
||||
("Banned RIR list", self.__banManager.geBanListExtendedRIR(cymru_info))]
|
||||
return ret
|
||||
|
|
|
@ -26,6 +26,9 @@ __license__ = "GPL"
|
|||
|
||||
from threading import Lock
|
||||
|
||||
import dns.exception
|
||||
import dns.resolver
|
||||
|
||||
from .ticket import BanTicket
|
||||
from .mytime import MyTime
|
||||
from ..helpers import getLogger
|
||||
|
@ -118,6 +121,115 @@ class BanManager:
|
|||
finally:
|
||||
self.__lock.release()
|
||||
|
||||
##
|
||||
# Returns normalized value
|
||||
#
|
||||
# @return value or "unknown" if value is None or empty string
|
||||
|
||||
@staticmethod
|
||||
def handleBlankResult(value):
|
||||
if value is None or len(value) == 0:
|
||||
return "unknown"
|
||||
else:
|
||||
return value
|
||||
|
||||
##
|
||||
# Returns Cymru DNS query information
|
||||
#
|
||||
# @return {"asn": [], "country": [], "rir": []} dict for self.__banList IPs
|
||||
|
||||
def getBanListExtendedCymruInfo(self):
|
||||
self.__lock.acquire()
|
||||
return_dict = {"asn": [], "country": [], "rir": []}
|
||||
try:
|
||||
for banData in self.__banList:
|
||||
ip = banData.getIP()
|
||||
# Reference: http://www.team-cymru.org/Services/ip-to-asn.html#dns
|
||||
# TODO: IPv6 compatibility
|
||||
reversed_ip = ".".join(reversed(ip.split(".")))
|
||||
question = "%s.origin.asn.cymru.com" % reversed_ip
|
||||
try:
|
||||
answers = dns.resolver.query(question, "TXT")
|
||||
for rdata in answers:
|
||||
asn, net, country, rir, changed =\
|
||||
[answer.strip("'\" ") for answer in rdata.to_text().split("|")]
|
||||
asn = self.handleBlankResult(asn)
|
||||
country = self.handleBlankResult(country)
|
||||
rir = self.handleBlankResult(rir)
|
||||
return_dict["asn"].append(self.handleBlankResult(asn))
|
||||
return_dict["country"].append(self.handleBlankResult(country))
|
||||
return_dict["rir"].append(self.handleBlankResult(rir))
|
||||
except dns.resolver.NXDOMAIN:
|
||||
return_dict["asn"].append("nxdomain")
|
||||
return_dict["country"].append("nxdomain")
|
||||
return_dict["rir"].append("nxdomain")
|
||||
except dns.exception.DNSException as dnse:
|
||||
logSys.error("Unhandled DNSException querying Cymru for %s TXT" % question)
|
||||
logSys.exception(dnse)
|
||||
except Exception as e:
|
||||
logSys.error("Unhandled Exception querying Cymru for %s TXT" % question)
|
||||
logSys.exception(e)
|
||||
except Exception as e:
|
||||
logSys.error("Failure looking up extended Cymru info")
|
||||
logSys.exception(e)
|
||||
finally:
|
||||
self.__lock.release()
|
||||
return return_dict
|
||||
|
||||
##
|
||||
# Returns list of Banned ASNs from Cymru info
|
||||
#
|
||||
# Use getBanListExtendedCymruInfo() to provide cymru_info
|
||||
#
|
||||
# @return list of Banned ASNs
|
||||
|
||||
def geBanListExtendedASN(self, cymru_info):
|
||||
self.__lock.acquire()
|
||||
try:
|
||||
return [asn for asn in cymru_info["asn"]]
|
||||
except Exception as e:
|
||||
logSys.error("Failed to lookup ASN")
|
||||
logSys.exception(e)
|
||||
return []
|
||||
finally:
|
||||
self.__lock.release()
|
||||
|
||||
##
|
||||
# Returns list of Banned Countries from Cymru info
|
||||
#
|
||||
# Use getBanListExtendedCymruInfo() to provide cymru_info
|
||||
#
|
||||
# @return list of Banned Countries
|
||||
|
||||
def geBanListExtendedCountry(self, cymru_info):
|
||||
self.__lock.acquire()
|
||||
try:
|
||||
return [country for country in cymru_info["country"]]
|
||||
except Exception as e:
|
||||
logSys.error("Failed to lookup Country")
|
||||
logSys.exception(e)
|
||||
return []
|
||||
finally:
|
||||
self.__lock.release()
|
||||
|
||||
##
|
||||
# Returns list of Banned RIRs from Cymru info
|
||||
#
|
||||
# Use getBanListExtendedCymruInfo() to provide cymru_info
|
||||
#
|
||||
# @return list of Banned RIRs
|
||||
|
||||
def geBanListExtendedRIR(self, cymru_info):
|
||||
self.__lock.acquire()
|
||||
try:
|
||||
return [rir for rir in cymru_info["rir"]]
|
||||
except Exception as e:
|
||||
logSys.error("Failed to lookup RIR")
|
||||
logSys.exception(e)
|
||||
return []
|
||||
finally:
|
||||
self.__lock.release()
|
||||
|
||||
##
|
||||
# Create a ban ticket.
|
||||
#
|
||||
|
|
|
@ -183,6 +183,15 @@ class Jail:
|
|||
("Actions", self.actions.status),
|
||||
]
|
||||
|
||||
@property
|
||||
def statusExtended(self):
|
||||
"""The extended status of the jail.
|
||||
"""
|
||||
return [
|
||||
("Filter", self.filter.status),
|
||||
("Actions", self.actions.statusExtended),
|
||||
]
|
||||
|
||||
def putFailTicket(self, ticket):
|
||||
"""Add a fail ticket to the jail.
|
||||
|
||||
|
|
|
@ -72,6 +72,12 @@ class JailThread(Thread):
|
|||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def statusExtended(self): # pragma: no cover - abstract
|
||||
"""Abstract - Should provide extended status information.
|
||||
"""
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
"""Sets active flag and starts thread.
|
||||
"""
|
||||
|
|
|
@ -322,7 +322,10 @@ class Server:
|
|||
|
||||
def statusJail(self, name):
|
||||
return self.__jails[name].status
|
||||
|
||||
|
||||
def statusJailExtended(self, name):
|
||||
return self.__jails[name].statusExtended
|
||||
|
||||
# Logging
|
||||
|
||||
##
|
||||
|
|
|
@ -333,5 +333,10 @@ class Transmitter:
|
|||
elif len(command) == 1:
|
||||
name = command[0]
|
||||
return self.__server.statusJail(name)
|
||||
elif len(command) == 2:
|
||||
name = command[0]
|
||||
if command[1] == "extended":
|
||||
return self.__server.statusJailExtended(name)
|
||||
else:
|
||||
raise Exception("Invalid command (invalid status extension)")
|
||||
raise Exception("Invalid command (no status)")
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ from ..server.banmanager import BanManager
|
|||
from ..server.ticket import BanTicket
|
||||
|
||||
class AddFailure(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.__ticket = BanTicket('193.168.0.128', 1167605999.0)
|
||||
|
@ -39,19 +38,58 @@ class AddFailure(unittest.TestCase):
|
|||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
|
||||
def testAdd(self):
|
||||
self.assertEqual(self.__banManager.size(), 1)
|
||||
|
||||
|
||||
def testAddDuplicate(self):
|
||||
self.assertFalse(self.__banManager.addBanTicket(self.__ticket))
|
||||
self.assertEqual(self.__banManager.size(), 1)
|
||||
|
||||
|
||||
def testInListOK(self):
|
||||
ticket = BanTicket('193.168.0.128', 1167605999.0)
|
||||
self.assertTrue(self.__banManager._inBanList(ticket))
|
||||
|
||||
|
||||
def testInListNOK(self):
|
||||
ticket = BanTicket('111.111.1.111', 1167605999.0)
|
||||
self.assertFalse(self.__banManager._inBanList(ticket))
|
||||
|
||||
|
||||
|
||||
class StatusExtendedCymruInfo(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Call before every test case."""
|
||||
self.__ban_ip = "93.184.216.34"
|
||||
self.__asn = "15133"
|
||||
self.__country = "EU"
|
||||
self.__rir = "ripencc"
|
||||
self.__ticket = BanTicket(self.__ban_ip, 1167605999.0)
|
||||
self.__banManager = BanManager()
|
||||
self.assertTrue(self.__banManager.addBanTicket(self.__ticket))
|
||||
|
||||
def tearDown(self):
|
||||
"""Call after every test case."""
|
||||
|
||||
def testCymruInfo(self):
|
||||
cymru_info = self.__banManager.getBanListExtendedCymruInfo()
|
||||
if "assertDictEqual" in dir(self):
|
||||
self.assertDictEqual(cymru_info, {"asn": [self.__asn], "country": [self.__country], "rir": [self.__rir]})
|
||||
else:
|
||||
# Python 2.6 does not support assertDictEqual()
|
||||
self.assertEqual(cymru_info["asn"], [self.__asn])
|
||||
self.assertEqual(cymru_info["country"], [self.__country])
|
||||
self.assertEqual(cymru_info["rir"], [self.__rir])
|
||||
|
||||
def testCymruInfoASN(self):
|
||||
self.assertEqual(
|
||||
self.__banManager.geBanListExtendedASN(self.__banManager.getBanListExtendedCymruInfo()),
|
||||
[self.__asn])
|
||||
|
||||
def testCymruInfoCountry(self):
|
||||
self.assertEqual(
|
||||
self.__banManager.geBanListExtendedCountry(self.__banManager.getBanListExtendedCymruInfo()),
|
||||
[self.__country])
|
||||
|
||||
def testCymruInfoRIR(self):
|
||||
self.assertEqual(
|
||||
self.__banManager.geBanListExtendedRIR(self.__banManager.getBanListExtendedCymruInfo()),
|
||||
[self.__rir])
|
||||
|
|
|
@ -474,6 +474,27 @@ class Transmitter(TransmitterBase):
|
|||
)
|
||||
)
|
||||
|
||||
def testJailStatusExtended(self):
|
||||
self.assertEqual(self.transm.proceed(["status", self.jailName, "extended"]),
|
||||
(0,
|
||||
[
|
||||
('Filter', [
|
||||
('Currently failed', 0),
|
||||
('Total failed', 0),
|
||||
('File list', [])]
|
||||
),
|
||||
('Actions', [
|
||||
('Currently banned', 0),
|
||||
('Total banned', 0),
|
||||
('Banned IP list', []),
|
||||
('Banned ASN list', []),
|
||||
('Banned Country list', []),
|
||||
('Banned RIR list', [])]
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
def testAction(self):
|
||||
action = "TestCaseAction"
|
||||
cmdList = [
|
||||
|
@ -601,6 +622,10 @@ class Transmitter(TransmitterBase):
|
|||
self.assertEqual(
|
||||
self.transm.proceed(["status", "INVALID", "COMMAND"])[0],1)
|
||||
|
||||
def testStatusJailExtendedNOK(self):
|
||||
self.assertEqual(
|
||||
self.transm.proceed(["status", self.jailName, "INVALID_COMMAND"])[0],1)
|
||||
|
||||
def testJournalMatch(self):
|
||||
if not filtersystemd: # pragma: no cover
|
||||
if sys.version_info >= (2, 7):
|
||||
|
|
|
@ -107,6 +107,7 @@ def gatherTests(regexps=None, no_network=False):
|
|||
tests.addTest(unittest.makeSuite(failmanagertestcase.AddFailure))
|
||||
# BanManager
|
||||
tests.addTest(unittest.makeSuite(banmanagertestcase.AddFailure))
|
||||
tests.addTest(unittest.makeSuite(banmanagertestcase.StatusExtendedCymruInfo))
|
||||
# ClientReaders
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.ConfigReaderTest))
|
||||
tests.addTest(unittest.makeSuite(clientreadertestcase.JailReaderTest))
|
||||
|
|
Loading…
Reference in New Issue