check_ossec.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/usr/bin/env python
  2. # Author: Jon Schipp <jschipp@illinois.edu, jonschipp@gmail.com>
  3. # 1.) Check that all OSSEC services are running
  4. # $ ./check_ossec.py -T status
  5. # 2.) Check status of all OSSEC agents except one
  6. # $ ./check_ossec.py -T connected --skip www2.company.com
  7. # 3.) Check status of specific OSSEC agents
  8. # $ ./check_ossec.py -T connected --agents www1.company.com,www2.company.com
  9. # 4.) Report critical if more than 3 agents are offline and warning if at least 1 is offline.
  10. # $ ./check_ossec.py -T connected -c 3 -w 1
  11. # 5.) Check that a syscheck scan as completed for all agents in the last 12 hours, warning if 6
  12. # $ ./check_ossec.py -T syscheck -c 12 -w 6
  13. # 6.) Check that a rootcheck scan as completed for agent in the last 4 hours, warning if 2
  14. # $ ./check_ossec.py -T rootcheck --agents www2.company.com -c 4 -w 2
  15. import sys
  16. import argparse
  17. import os
  18. import subprocess
  19. import datetime
  20. import time
  21. # Nagios exit codes
  22. NAGIOS_OK = 0
  23. NAGIOS_WARNING = 1
  24. NAGIOS_CRITICAL = 2
  25. NAGIOS_UNKNOWN = 3
  26. STATUS_MSG = {
  27. 0: 'OK',
  28. 1: 'WARNING',
  29. 2: 'CRITICAL',
  30. 3: 'UNKNOWN',
  31. }
  32. status_checks=['connected', 'syscheck', 'rootcheck', 'status']
  33. def arguments():
  34. global path
  35. # Defaults
  36. crit = 1
  37. warn = 1
  38. path = '/var/ossec'
  39. parser = argparse.ArgumentParser(description='Check OSSEC Configuration')
  40. parser.add_argument("-s", "--skip", type=str, help="Items to skip from check")
  41. parser.add_argument("-a", "--agents", type=str, help="Check status of agents (def: all) (sep:,) e.g. www1,www2")
  42. parser.add_argument("-T", "--type", type=str, help="Type of check", choices=status_checks)
  43. parser.add_argument("-p", "--path", type=str, help="Path of OSSEC directory (def: /var/ossec)")
  44. parser.add_argument("-c", "--critical", type=int, help="Critical value in count for checks")
  45. parser.add_argument("-w", "--warning", type=int, help="Warning value in count for checks")
  46. args = parser.parse_args()
  47. option = args.type
  48. if args.critical:
  49. crit = args.critical
  50. if args.warning:
  51. warn = args.warning
  52. if args.skip:
  53. skip = args.skip.split(",")
  54. else:
  55. skip = "None"
  56. if args.agents:
  57. agents = args.agents.split(",")
  58. else:
  59. agents = False
  60. if args.path:
  61. path = args.path
  62. return option, agents, skip, crit, warn
  63. def threshold(inactive_agents, crit, warn):
  64. if inactive_agents >= crit:
  65. return NAGIOS_CRITICAL
  66. if inactive_agents >= warn:
  67. return NAGIOS_WARNING
  68. return NAGIOS_OK
  69. def is_ossec(path):
  70. if os.path.isdir(path):
  71. files = [
  72. 'etc/ossec.conf',
  73. 'etc/shared/agent.conf',
  74. 'bin/syscheck_control',
  75. 'bin/rootcheck_control',
  76. 'bin/agent_control'
  77. ]
  78. for f in files:
  79. fp = path + '/' + f
  80. if not os.path.isfile(fp):
  81. print "Error: Installation missing file %s" % fp
  82. return False
  83. return True
  84. def get_output_dict(cmd, arg):
  85. c=0
  86. data={}
  87. command = path + '/' + cmd
  88. result = subprocess.Popen([command,arg],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  89. set_result = set(result.stdout)
  90. for i in set_result:
  91. data[c]= [i.split(',')]
  92. c += 1
  93. return data
  94. def get_output_set(cmd, arg):
  95. command = path + '/' + cmd
  96. result = subprocess.Popen([command,arg],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  97. data = set(result.stdout)
  98. return data
  99. def is_server():
  100. filename = '/etc/ossec-init.conf'
  101. try:
  102. with open(filename, 'r') as f:
  103. contents = f.readlines()
  104. install_type = contents[3].strip().split('=')[1]
  105. if install_type == '"server"':
  106. return True
  107. else:
  108. print "ERROR: Unable to detect OSSEC server: %s" % install_type
  109. return False
  110. except IOError:
  111. print "ERROR: Cannot open file %s. Is OSSEC installed?" % filename
  112. exit(NAGIOS_UNKNOWN)
  113. def open_queue(filename, service):
  114. try:
  115. with open(filename, 'r') as f:
  116. for i in f.readlines():
  117. if service == 'syscheck':
  118. if 'Starting syscheck scan.' in i:
  119. syscheck_start_ts = i[1:11]
  120. return syscheck_start_ts
  121. if service == 'rootcheck':
  122. if 'Starting rootcheck scan.' in i:
  123. rootcheck_start_ts = i[1:11]
  124. return rootcheck_start_ts
  125. else:
  126. return False
  127. except IOError:
  128. print "ERROR: Cannot read queue file %s" % filename
  129. exit(NAGIOS_UNKNOWN)
  130. def older_than(ts, name, crit_ts, warn_ts):
  131. if not ts:
  132. print "%s: %s: LastCheckTime: Unknown" % (STATUS_MSG[NAGIOS_UNKNOWN], name)
  133. return NAGIOS_UNKNOWN
  134. service_timestamp = datetime.datetime.fromtimestamp(float(ts))
  135. if service_timestamp < crit_ts:
  136. print "%s: %s: LastCheckTime: %s" % (STATUS_MSG[NAGIOS_CRITICAL], name, service_timestamp)
  137. return NAGIOS_CRITICAL
  138. elif service_timestamp < warn_ts:
  139. print "%s: %s: LastCheckTime: %s" % (STATUS_MSG[NAGIOS_WARNING], name, service_timestamp)
  140. return NAGIOS_WARNING
  141. else:
  142. return NAGIOS_OK
  143. def check_connected(agents, skip, crit, warn):
  144. data = get_output_dict('bin/agent_control', '-l')
  145. c=0
  146. inactive_agents = 0
  147. active = [ 'Active', 'Active/Local' ]
  148. notactive = {}
  149. for i in data:
  150. line = data[c][0]
  151. c += 1
  152. # Check for lines with fields we need
  153. # Extract agent name and status message
  154. if len(line) == 4:
  155. name=line[1][6:].lstrip()
  156. status=line[3].lstrip().rstrip()
  157. # If --agents is specified only check for specified agents
  158. if agents:
  159. if name not in agents:
  160. continue
  161. else:
  162. # If --agents is not specified check all except in skip
  163. if name in skip:
  164. continue
  165. if status not in active:
  166. notactive[name] = status
  167. inactive_agents += 1
  168. # Check if inactive agents were fonud
  169. if notactive:
  170. exit_code = threshold(inactive_agents, crit, warn)
  171. print "%s: %d agents not connected" % (STATUS_MSG[exit_code], len(notactive))
  172. for k,v in notactive.items():
  173. print "Name: %s, Status: %s" % (k,v)
  174. return exit_code
  175. else:
  176. print "%s: All agents connected" % STATUS_MSG[NAGIOS_OK]
  177. return NAGIOS_OK
  178. def check_service(service, agents, skip, crit, warn):
  179. dir = os.path.join(path,'queue/rootcheck')
  180. crit_ts = datetime.datetime.now() - datetime.timedelta(hours=crit)
  181. warn_ts = datetime.datetime.now() - datetime.timedelta(hours=warn)
  182. status_list = []
  183. for queue in os.listdir(dir):
  184. name = queue.strip('()').split()[0].rstrip(')')
  185. if agents:
  186. if name not in agents:
  187. continue
  188. else:
  189. if name == 'rootcheck':
  190. continue
  191. if name in skip:
  192. continue
  193. f = os.path.join(dir, queue)
  194. if os.path.isfile(f):
  195. ts = open_queue(f, service)
  196. status = older_than(ts, name, crit_ts, warn_ts)
  197. status_list.append(status)
  198. exit_code = max(status_list)
  199. if exit_code == NAGIOS_OK:
  200. print "%s: Agent %s runtimes are up to date" % (STATUS_MSG[NAGIOS_OK],service)
  201. return exit_code
  202. def check_status(agents, skip):
  203. data = get_output_set('bin/ossec-control', 'status')
  204. not_running=[]
  205. for i in data:
  206. # If --skip is used skip these
  207. if i in skip:
  208. continue
  209. # Add names of services not running to list
  210. if 'not running' in i:
  211. not_running.append(i)
  212. continue
  213. # Test for entries in list. Entries mean something wasn't running
  214. if not_running:
  215. print "%s: Some services running" % STATUS_MSG[NAGIOS_CRITICAL]
  216. for i in not_running:
  217. print i.rstrip()
  218. return NAGIOS_CRITICAL
  219. else:
  220. print "%s: All services running" % STATUS_MSG[NAGIOS_OK]
  221. return NAGIOS_OK
  222. def main():
  223. option, agents, skip, crit, warn = arguments()
  224. if not is_ossec(path):
  225. exit(NAGIOS_UNKNOWN)
  226. if not is_server():
  227. exit(NAGIOS_UNKNOWN)
  228. if option == "connected":
  229. exit(check_connected(agents, skip, crit, warn))
  230. elif option == "syscheck":
  231. exit(check_service(option, agents, skip, crit, warn))
  232. elif option == "rootcheck":
  233. exit(check_service(option, agents, skip, crit, warn))
  234. elif option == "status":
  235. exit(check_status(agents, skip))
  236. else:
  237. print "Invalid type option"
  238. exit(NAGIOS_UNKNOWN)
  239. if __name__ == "__main__":
  240. main()