From de59228835f09f5d5b9acf95e907833c2e1682bb Mon Sep 17 00:00:00 2001 From: alejandro <alejandro.sanchez@pandorafms.com> Date: Mon, 18 Jul 2022 16:23:50 +0200 Subject: [PATCH] adding plugin cisco ironports, python3 --- .../cisco_ironports/pandora_ironports.py | 410 ++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 pandora_plugins/cisco_ironports/pandora_ironports.py diff --git a/pandora_plugins/cisco_ironports/pandora_ironports.py b/pandora_plugins/cisco_ironports/pandora_ironports.py new file mode 100644 index 0000000000..b51da28606 --- /dev/null +++ b/pandora_plugins/cisco_ironports/pandora_ironports.py @@ -0,0 +1,410 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse,sys,requests +from requests.auth import HTTPBasicAuth +from ast import List +import xml.etree.ElementTree as ET +import os +from subprocess import * +from datetime import datetime +from xml.etree import ElementTree + +__author__ = "Alejandro Sánchez Carrion" +__copyright__ = "Copyright 2021, PandoraFMS" +__maintainer__ = "Projects department" +__status__ = "Production" +__version__ = "180722" + +description= f""" +Pandora_ironports tool ver {__version__} + +python3 pandora_ironports.py --url <url> -u <user> -p <password> [ --agent_name <agent_name> ] [ --as_agent_plugin <as_agent_plugin> ] [ --tentacle_port <tentacle_port> ] [ --tentacle_address <tentacle_address> ] [ -g <group> ] [ --data_dir <data dir> ] + +""" + +parser = argparse.ArgumentParser(description= description, formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--url', help='', required=True) +parser.add_argument('-u', '--user', help='username', required=True) +parser.add_argument('-p', '--password', help='password',required=True ) +parser.add_argument('--tentacle_port', help='tentacle port', default=41121) +parser.add_argument('--tentacle_address', help='tentacle adress', default=None) +parser.add_argument('--agent_name', help='Name of the agent', default= "Cisco_ironport") +parser.add_argument('-g', '--group', help='PandoraFMS destination group (default cisco)', default='Cisco') +parser.add_argument('--data_dir', help='PandoraFMS data dir (default: /var/spool/pandora/data_in/)', default='/var/spool/pandora/data_in/') +parser.add_argument('--as_agent_plugin', help='mode plugin', default=0,type=int) + + +args = parser.parse_args() + +### Pandora Tools ###------------------------------------------------------------------------------------------------------- +modules = [] + +config = { + "data_in": args.data_dir, + "group" : args.group +} + +######################################################################################### +# print_agent +######################################################################################### +def print_agent(agent, modules, data_dir="/var/spool/pandora/data_in/", log_modules= None, print_flag = None): + """Prints agent XML. Requires agent conf (dict) and modules (list) as arguments. + - Use print_flag to show modules' XML in STDOUT. + - Returns a tuple (xml, data_file). + """ + data_file=None + + header = "<?xml version='1.0' encoding='UTF-8'?>\n" + header += "<agent_data" + for dato in agent: + header += " " + str(dato) + "='" + str(agent[dato]) + "'" + header += ">\n" + xml = header + if modules : + for module in modules: + modules_xml = print_module(module) + xml += str(modules_xml) + if log_modules : + for log_module in log_modules: + modules_xml = print_log_module(log_module) + xml += str(modules_xml) + xml += "</agent_data>" + if not print_flag: + data_file = write_xml(xml, agent["agent_name"], data_dir) + else: + print(xml) + + return (xml,data_file) + +######################################################################################### +# print_module +######################################################################################### +def print_module(module, print_flag=None): + """Returns module in XML format. Accepts only {dict}.\n + - Only works with one module at a time: otherwise iteration is needed. + - Module "value" field accepts str type or [list] for datalists. + - Use print_flag to show modules' XML in STDOUT. + """ + data = dict(module) + module_xml = ("<module>\n" + "\t<name><![CDATA[" + str(data["name"]) + "]]></name>\n" + "\t<type>" + str(data["type"]) + "</type>\n" + ) + + if type(data["type"]) is not str and "string" not in data["type"]: #### Strip spaces if module not generic_data_string + data["value"] = data["value"].strip() + if isinstance(data["value"], list): # Checks if value is a list + module_xml += "\t<datalist>\n" + for value in data["value"]: + if type(value) is dict and "value" in value: + module_xml += "\t<data>\n" + module_xml += "\t\t<value><![CDATA[" + str(value["value"]) + "]]></value>\n" + if "timestamp" in value: + module_xml += "\t\t<timestamp><![CDATA[" + str(value["timestamp"]) + "]]></timestamp>\n" + module_xml += "\t</data>\n" + module_xml += "\t</datalist>\n" + else: + module_xml += "\t<data><![CDATA[" + str(data["value"]) + "]]></data>\n" + if "desc" in data: + module_xml += "\t<description><![CDATA[" + str(data["desc"]) + "]]></description>\n" + if "unit" in data: + module_xml += "\t<unit><![CDATA[" + str(data["unit"]) + "]]></unit>\n" + if "interval" in data: + module_xml += "\t<module_interval><![CDATA[" + str(data["interval"]) + "]]></module_interval>\n" + if "tags" in data: + module_xml += "\t<tags>" + str(data["tags"]) + "</tags>\n" + if "module_group" in data: + module_xml += "\t<module_group>" + str(data["module_group"]) + "</module_group>\n" + if "module_parent" in data: + module_xml += "\t<module_parent>" + str(data["module_parent"]) + "</module_parent>\n" + if "min_warning" in data: + module_xml += "\t<min_warning><![CDATA[" + str(data["min_warning"]) + "]]></min_warning>\n" + if "min_warning_forced" in data: + module_xml += "\t<min_warning_forced><![CDATA[" + str(data["min_warning_forced"]) + "]]></min_warning_forced>\n" + if "max_warning" in data: + module_xml += "\t<max_warning><![CDATA[" + str(data["max_warning"]) + "]]></max_warning>\n" + if "max_warning_forced" in data: + module_xml += "\t<max_warning_forced><![CDATA[" + str(data["max_warning_forced"]) + "]]></max_warning_forced>\n" + if "min_critical" in data: + module_xml += "\t<min_critical><![CDATA[" + str(data["min_critical"]) + "]]></min_critical>\n" + if "min_critical_forced" in data: + module_xml += "\t<min_critical_forced><![CDATA[" + str(data["min_critical_forced"]) + "]]></min_critical_forced>\n" + if "max_critical" in data: + module_xml += "\t<max_critical><![CDATA[" + str(data["max_critical"]) + "]]></max_critical>\n" + if "max_critical_forced" in data: + module_xml += "\t<max_critical_forced><![CDATA[" + str(data["max_critical_forced"]) + "]]></max_critical_forced>\n" + if "str_warning" in data: + module_xml += "\t<str_warning><![CDATA[" + str(data["str_warning"]) + "]]></str_warning>\n" + if "str_warning_forced" in data: + module_xml += "\t<str_warning_forced><![CDATA[" + str(data["str_warning_forced"]) + "]]></str_warning_forced>\n" + if "str_critical" in data: + module_xml += "\t<str_critical><![CDATA[" + str(data["str_critical"]) + "]]></str_critical>\n" + if "str_critical_forced" in data: + module_xml += "\t<str_critical_forced><![CDATA[" + str(data["str_critical_forced"]) + "]]></str_critical_forced>\n" + if "critical_inverse" in data: + module_xml += "\t<critical_inverse><![CDATA[" + str(data["critical_inverse"]) + "]]></critical_inverse>\n" + if "warning_inverse" in data: + module_xml += "\t<warning_inverse><![CDATA[" + str(data["warning_inverse"]) + "]]></warning_inverse>\n" + if "max" in data: + module_xml += "\t<max><![CDATA[" + str(data["max"]) + "]]></max>\n" + if "min" in data: + module_xml += "\t<min><![CDATA[" + str(data["min"]) + "]]></min>\n" + if "post_process" in data: + module_xml += "\t<post_process><![CDATA[" + str(data["post_process"]) + "]]></post_process>\n" + if "disabled" in data: + module_xml += "\t<disabled><![CDATA[" + str(data["disabled"]) + "]]></disabled>\n" + if "min_ff_event" in data: + module_xml += "\t<min_ff_event><![CDATA[" + str(data["min_ff_event"]) + "]]></min_ff_event>\n" + if "status" in data: + module_xml += "\t<status><![CDATA[" + str(data["status"]) + "]]></status>\n" + if "timestamp" in data: + module_xml += "\t<timestamp><![CDATA[" + str(data["timestamp"]) + "]]></timestamp>\n" + if "custom_id" in data: + module_xml += "\t<custom_id><![CDATA[" + str(data["custom_id"]) + "]]></custom_id>\n" + if "critical_instructions" in data: + module_xml += "\t<critical_instructions><![CDATA[" + str(data["critical_instructions"]) + "]]></critical_instructions>\n" + if "warning_instructions" in data: + module_xml += "\t<warning_instructions><![CDATA[" + str(data["warning_instructions"]) + "]]></warning_instructions>\n" + if "unknown_instructions" in data: + module_xml += "\t<unknown_instructions><![CDATA[" + str(data["unknown_instructions"]) + "]]></unknown_instructions>\n" + if "quiet" in data: + module_xml += "\t<quiet><![CDATA[" + str(data["quiet"]) + "]]></quiet>\n" + if "module_ff_interval" in data: + module_xml += "\t<module_ff_interval><![CDATA[" + str(data["module_ff_interval"]) + "]]></module_ff_interval>\n" + if "crontab" in data: + module_xml += "\t<crontab><![CDATA[" + str(data["crontab"]) + "]]></crontab>\n" + if "min_ff_event_normal" in data: + module_xml += "\t<min_ff_event_normal><![CDATA[" + str(data["min_ff_event_normal"]) + "]]></min_ff_event_normal>\n" + if "min_ff_event_warning" in data: + module_xml += "\t<min_ff_event_warning><![CDATA[" + str(data["min_ff_event_warning"]) + "]]></min_ff_event_warning>\n" + if "min_ff_event_critical" in data: + module_xml += "\t<min_ff_event_critical><![CDATA[" + str(data["min_ff_event_critical"]) + "]]></min_ff_event_critical>\n" + if "ff_type" in data: + module_xml += "\t<ff_type><![CDATA[" + str(data["ff_type"]) + "]]></ff_type>\n" + if "ff_timeout" in data: + module_xml += "\t<ff_timeout><![CDATA[" + str(data["ff_timeout"]) + "]]></ff_timeout>\n" + if "each_ff" in data: + module_xml += "\t<each_ff><![CDATA[" + str(data["each_ff"]) + "]]></each_ff>\n" + if "module_parent_unlink" in data: + module_xml += "\t<module_parent_unlink><![CDATA[" + str(data["parent_unlink"]) + "]]></module_parent_unlink>\n" + if "global_alerts" in data: + for alert in data["alert"]: + module_xml += "\t<alert_template><![CDATA[" + alert + "]]></alert_template>\n" + module_xml += "</module>\n" + + if print_flag: + print (module_xml) + + return (module_xml) + +######################################################################################### +# write_xml +######################################################################################### + +def write_xml(xml, agent_name, data_dir="/var/spool/pandora/data_in/"): + """Creates a agent .data file in the specified data_dir folder\n + Args: + - xml (str): XML string to be written in the file. + - agent_name (str): agent name for the xml and file name. + - data_dir (str): folder in which the file will be created.""" + Utime = datetime.now().strftime('%s') + data_file = "%s/%s.%s.data" %(str(data_dir),agent_name,str(Utime)) + try: + with open(data_file, 'x') as data: + data.write(xml) + except OSError as o: + sys.exit(f"ERROR - Could not write file: {o}, please check directory permissions") + except Exception as e: + sys.exit(f"{type(e).__name__}: {e}") + return (data_file) + +# # default agent +def clean_agent() : + global agent + agent = { + "agent_name" : "", + "agent_alias" : "", + "parent_agent_name" : "", + "description" : "", + "version" : "", + "os_name" : "", + "os_version" : "", + "timestamp" : datetime.today().strftime('%Y/%m/%d %H:%M:%S'), + #"utimestamp" : int(datetime.timestamp(datetime.today())), + "address" : "", + "group" : config["group"], + "interval" : "", + "agent_mode" : "1", + } + return agent + +# default module +def clean_module() : + global modulo + modulo = { + "name" : "", + "type" : "generic_data_string", + "desc" : "", + "value" : "", + } + return modulo + +######################################################################################### +# tentacle_xml +######################################################################################### +def tentacle_xml(file, tentacle_ops,tentacle_path='', debug=0): + """Sends file using tentacle protocol\n + - Only works with one file at time. + - file variable needs full file path. + - tentacle_opts should be a dict with tentacle options (address [password] [port]). + - tentacle_path allows to define a custom path for tentacle client in case is not in sys path). + - if debug is enabled, the data file will not be removed after being sent. + + Returns 0 for OK and 1 for errors. + """ + + if file is None : + sys.stderr.write("Tentacle error: file path is required.") + else : + data_file = file + + if tentacle_ops['address'] is None : + sys.stderr.write("Tentacle error: No address defined") + return 1 + + try : + with open(data_file, 'r') as data: + data.read() + data.close() + except Exception as e : + sys.stderr.write(f"Tentacle error: {type(e).__name__} {e}") + return 1 + + tentacle_cmd = f"{tentacle_path}tentacle_client -v -a {tentacle_ops['address']} " + if "port" in tentacle_ops: + tentacle_cmd += f"-p {tentacle_ops['port']} " + if "password" in tentacle_ops: + tentacle_cmd += f"-x {tentacle_ops['password']} " + tentacle_cmd += f"{data_file} " + + tentacle_exe=Popen(tentacle_cmd, stdout=PIPE, shell=True) + rc=tentacle_exe.wait() + + if rc != 0 : + sys.stderr.write("Tentacle error") + return 1 + elif debug == 0 : + os.remove(file) + + return 0 + +## funcion agent +def agentplugin(modules,agent,plugin_type="server",data_dir="/var/spool/pandora/data_in/",tentacle=False,tentacle_conf=None) : + if plugin_type == "server": + for modulo in modules: + print_module(modulo,1) + + elif tentacle == True and tentacle_conf is not None: + agent_file=print_agent(agent, modules,data_dir) + if agent_file[1] is not None: + tentacle_xml(agent_file[1],tentacle_conf) + print ("1") + else: + print_agent(agent, modules,data_dir) + print ("1") + +# Create agent +clean_agent() +agent.update( + agent_name = args.agent_name , + agent_alias =args.agent_name , + description ="" +) + +headers = { + 'Accept':'application/xml', + 'Content-Type': 'application/xml', +} + +req = { + "urlxml": args.url +} +try: + result = requests.get(req["urlxml"], verify=False, headers=headers, auth=HTTPBasicAuth(args.u, args.p)) + tree = ElementTree.fromstring(result.content) + +except Exception as e : + print('0') + sys.exit("\nError requesting %s, please check conectivity" %(req["urlxml"],)) + + + +dicc_gauge= {} +dicc_feature={} +dicc_counter={} +dicc_rate={} + +for element in tree: + for atributo in element: + if atributo.tag=='gauge': + dicc_gauge[atributo.tag+"_"+atributo.attrib['name']] = atributo.attrib['current'] + elif atributo.tag=='feature': + dicc_feature[atributo.tag+"_"+atributo.attrib['name']] = atributo.attrib['time_remaining'] + elif atributo.tag=='counter': + dicc_counter[atributo.tag+"_"+atributo.attrib['name']+"_reset"] = atributo.attrib['reset'] + dicc_counter[atributo.tag+"_"+atributo.attrib['name']+"_uptime"] = atributo.attrib['uptime'] + dicc_counter[atributo.tag+"_"+atributo.attrib['name']+"_lifetime"] = atributo.attrib['lifetime'] + elif atributo.tag=='rate': + dicc_rate[atributo.tag+"_"+atributo.attrib['name']+"_last_1_min"] = atributo.attrib['last_1_min'] + dicc_rate[atributo.tag+"_"+atributo.attrib['name']+"_last_5_min"] = atributo.attrib['last_5_min'] + dicc_rate[atributo.tag+"_"+atributo.attrib['name']+"_last_15_min"] = atributo.attrib['last_15_min'] + + +for key,value in dicc_gauge.items(): + clean_module() + modulo.update( + name = key, + type = "generic_data", + desc = "", + value = value + ) + modules.append(modulo) + +for key,value in dicc_feature.items(): + clean_module() + modulo.update( + name = key, + type = "generic_data", + desc = "", + value = value + ) + modules.append(modulo) + +for key,value in dicc_counter.items(): + clean_module() + modulo.update( + name = key, + type = "generic_data", + desc = "", + value = value + ) + modules.append(modulo) + +for key,value in dicc_rate.items(): + clean_module() + modulo.update( + name = key, + type = "generic_data", + desc = "", + value = value + ) + modules.append(modulo) + +if args.tentacle_address is not None: + tentacle_conf={"address":args.tentacle_address,"port":args.tentacle_port} + agentplugin(modules,agent,"agent",config["data_in"],True,tentacle_conf) +elif args.as_agent_plugin!=1: + agentplugin(modules,agent,"agent",config["data_in"]) +else: + agentplugin(modules,agent)