#!/usr/bin/python

# Example for output from agent
# ---------------------------------------------------------
#<<<fail2ban>>>
#Detected jails:         ssh-route  pure-ftpd
#Status for the jail: ssh-route
#|- filter
#|  |- File list:        /var/log/messages 
#|  |- Currently failed: 0
#|  `- Total failed:     16131
#`- action
#   |- Currently banned: 110
#   |  `- IP list:       14.47.81.102 122.225.109.118 218.2.0.132 122.225.109.110 60.173.26.165 218.2.0.127 61.174.51.203 122.225.109.209 122.225.109.210 144.0.0.49 122.224.34.54 115.239.248.90 216.53.210.153 122.225.109.196 115.239.248.57 123.157.150.57 115.239.248.51 123.157.150.51 115.239.248.50 123.157.150.50 91.240.163.39 122.225.109.112 61.174.51.201 220.177.198.93 58.83.146.252 61.174.51.223 61.174.51.222 61.174.51.194 76.72.166.98 61.174.48.17 122.225.109.213 61.147.103.169 144.0.0.58 1.93.25.234 61.174.51.197 220.177.198.28 183.136.214.133 111.74.238.219 222.186.34.121 222.186.58.242 222.186.58.205 220.177.198.39 222.186.58.241 61.174.51.217 61.174.51.216 183.57.57.157 122.225.109.201 124.232.147.152 62.75.187.189 61.174.51.199 122.225.109.104 60.173.10.75 122.225.109.109 122.225.109.194 202.109.143.56 122.225.109.199 61.147.81.213 122.225.109.211 60.173.9.11 61.174.51.219 111.74.239.35 122.225.109.218 61.174.51.218 122.225.109.103 218.2.0.130 222.186.34.119 122.225.109.215 144.0.0.25 139.0.12.151 122.225.109.197 72.9.226.2 122.225.109.212 122.225.109.208 175.22.14.71 218.2.0.133 222.122.30.51 77.221.144.95 201.65.176.66 61.174.50.251 80.241.46.147 68.178.128.147 61.143.236.193 61.174.51.200 103.241.181.36 121.78.90.119 119.147.214.35 144.0.0.68 122.225.109.106 61.174.50.229 61.174.51.207 222.186.26.251 113.200.114.230 212.83.150.74 203.147.88.202 189.203.240.68 202.109.143.42 111.74.238.153 212.129.39.90 113.240.234.241 222.186.34.238 61.174.50.165 218.2.0.135 122.225.109.195 61.174.51.204 61.174.51.196 212.129.12.76 122.225.109.100 198.71.58.200 122.225.109.198 60.190.71.52  
#   `- Total banned:     1755 
#Status for the jail: pure-ftpd 
#|- filter 
#|  |- File list:        /var/log/syslog  
#|  |- Currently failed: 1 
#|  `- Total failed:     1840 
#`- action 
#   |- Currently banned: 2 
#   |  `- IP list:       192.210.53.22 216.99.146.195 
#   `- Total banned:     125
# ---------------------------------------------------------

def inventory_fail2ban(checkname, info):
	#Detected jails:         roundcube-auth  ssh-route  pure-ftpd
	line = info[0]
	if len(line) > 2:
		for jail in line[2:]:
			yield jail, {}

def check_fail2ban(item, params, info):
	# [['Detected', 'jails:', 'ssh-route'], ['Status', 'for', 'the', 'jail:', 'ssh-route'], ['|-', 'filter'], ['|', '|-', 'File', 'list:', '/var/log/messages'], ['|', '|-', 'Currently', 'failed:', '0'], ['|', '`-', 'Total', 'failed:', '4202'], ['`-', 'action'], ['|-', 'Currently', 'banned:', '25'], ['|', '`-', 'IP', 'list:', '122.225.109.216', '61.174.51.224', '122.225.97.80', '122.225.97.92', '122.225.97.121', '218.2.0.125', '122.225.97.82', '122.225.97.107', '122.225.97.125', '61.174.51.214', '122.225.109.209', '122.225.97.72', '122.225.109.218', '61.174.50.245', '122.225.109.222', '218.2.0.128', '122.225.97.83', '218.2.0.135', '117.27.158.76', '122.225.109.210', '122.225.97.116', '122.225.109.116', '61.174.51.216', '122.225.109.110', '122.225.97.113'], ['`-', 'Total', 'banned:', '617']]

	#Status for the jail: pure-ftpd   
	#|- filter   
	#|  |- File list:        /var/log/syslog    
	#|  |- Currently failed: 1   
	#|  `- Total failed:     1840   
	#`- action   
	#   |- Currently banned: 2   
	#   |  `- IP list:       192.210.53.22 216.99.146.195 
	#   `- Total banned:     125

	# See if we have old or new type params, if so: convert
	if type(params) != dict:
		params = { "levels_fail" : params[0:2], "levels_ban" : params[2:4] }

	cfwarn, cfcrit, cbwarn, cbcrit = None, None, None, None

	if "levels_fail" in params:
		cfwarn, cfcrit = params["levels_fail"]
	if "levels_ban" in params:
		cbwarn, cbcrit = params["levels_ban"]

	# See if any checks are diabled (set to 0)
	if cfwarn != None and cfwarn == 0:
		cfwarn = None;
	if cfcrit != None and cfcrit == 0:
		cfcrit = None;
	if cbwarn != None and cbwarn == 0:
		cbwarn = None;
	if cbcrit != None and cbcrit == 0:
		cbcrit = None;

	cfreg = regex(r'Currently failed:\s*(\d+)')
	cbreg = regex(r'Currently banned:\s*(\d+)')
	tfreg = regex(r'Total failed:\s*(\d+)')
	tbreg = regex(r'Total banned:\s*(\d+)')

	curfail, totfail, curban, totban = 0, 0, 0, 0
	banlist = []
	ourstatus = 0
	for line in info:
		if len(line) == 5 and line[0] == 'Status':
			if line[4] == item:
				ourstatus = 1
			elif ourstatus == 1:
				break
		elif ourstatus == 1:
			l = " ".join(line)
			cfm = cfreg.search(l)
			if cfm:
				curfail = int(cfm.group(1))
				continue
			tfm = tfreg.search(l)
			if tfm:
				totfail = int(tfm.group(1))
				continue
			cbm = cbreg.search(l)
			if cbm:
				curban = int(cbm.group(1))
				continue
			tbm = tbreg.search(l)
			if tbm:
				totban = int(tbm.group(1))
				continue

	if ourstatus == 0:
		return (3, "UNKNOWN - jail status not found in agent output")

	perfdata = []
	perfdata.append( ( 'current_failed', curfail, cfwarn, cfcrit, 0, "" ) )
	perfdata.append( ( 'current_banned', curban, cbwarn, cbcrit, 0, "" ) )
	perfdata.append( ( 'total_failed', totfail, "", "", 0, "" ) )
	perfdata.append( ( 'total_banned', totban, "", "", 0, "" ) )

	if cbcrit != None and curban > cbcrit:
		return (2, "CRIT (ban level high! %d > %d) - %s active - %s failed (%s total), %s banned (%s total)" % (curban, cbcrit, item, curfail, totfail, curban, totban), perfdata)
	elif cbwarn != None and curban > cbwarn:
		return (1, "WARN (ban level high! %d > %d)- %s active - %s failed (%s total), %s banned (%s total)" % (curban, cbwarn, item, curfail, totfail, curban, totban), perfdata)
	elif cfcrit != None and curfail > cfcrit:
		return (2, "CRIT (fail level high! %d > %d) - %s active - %s failed (%s total), %s banned (%s total)" % (curfail, cfcrit, item, curfail, totfail, curban, totban), perfdata)
	elif cfwarn != None and curfail > cfwarn:
		return (1, "WARN (fail level high! %d > %d)- %s active - %s failed (%s total), %s banned (%s total)" % (curfail, cfwarn, item, curfail, totfail, curban, totban), perfdata)
	return (0, "OK - %s active - %s failed (%s total), %s banned (%s total)" % (item, curfail, totfail, curban, totban), perfdata)

factory_settings["fail2ban_default_params"] = {
	"levels_fail"     : (50, 100), # warn/crit for failure
	"levels_ban"      : (25, 50), # warn/crit for bans
}

check_info['fail2ban'] = {
    "check_function"          : check_fail2ban,
    "inventory_function"      : inventory_fail2ban,
    "service_description"     : "Jail %s",
    "has_perfdata"            : True,
    "group"                   : "fail2ban",
    "default_levels_variable" : "fail2ban_default_params",
}
