From 9d2133187f806ff50f2345506c96b9d7ccee773f Mon Sep 17 00:00:00 2001 From: rafael Date: Fri, 22 Jan 2021 14:59:42 +0100 Subject: [PATCH] messaging integrations --- .../.vscode/settings.json | 3 + .../message_app_connectors/discord/.gitignore | 1 + .../discord/pandora_discord_cli.py | 173 +++++++++++++++++ .../discord/requirements.txt | 6 + .../discord/test-exec.txt | 19 ++ .../gchat/pandora-gchat-cli.py | 111 +++++++++++ .../gchat/requirements.txt | 5 + .../gchat/test-exec.txt | 14 ++ .../ms-teams/pandora-msteams-cli.py | 83 ++++++++ .../ms-teams/pandora-teams-cli.py | 81 ++++++++ .../ms-teams/requirements.txt | 6 + .../ms-teams/test-exec.txt | 15 ++ .../message_app_connectors/slack/.gitignore | 1 + .../slack/pandora-slack-cli.py | 177 ++++++++++++++++++ .../slack/requirements.txt | 6 + .../slack/test_exec.txt | 14 ++ .../telegram-bot-cli/pandora-telegram-cli.py | 27 +++ .../telegram-bot-cli/requirements.txt | 5 + .../telegram-bot-cli/test_exec.txt | 6 + 19 files changed, 753 insertions(+) create mode 100644 pandora_plugins/message_app_connectors/.vscode/settings.json create mode 100644 pandora_plugins/message_app_connectors/discord/.gitignore create mode 100644 pandora_plugins/message_app_connectors/discord/pandora_discord_cli.py create mode 100644 pandora_plugins/message_app_connectors/discord/requirements.txt create mode 100644 pandora_plugins/message_app_connectors/discord/test-exec.txt create mode 100644 pandora_plugins/message_app_connectors/gchat/pandora-gchat-cli.py create mode 100644 pandora_plugins/message_app_connectors/gchat/requirements.txt create mode 100644 pandora_plugins/message_app_connectors/gchat/test-exec.txt create mode 100755 pandora_plugins/message_app_connectors/ms-teams/pandora-msteams-cli.py create mode 100755 pandora_plugins/message_app_connectors/ms-teams/pandora-teams-cli.py create mode 100644 pandora_plugins/message_app_connectors/ms-teams/requirements.txt create mode 100644 pandora_plugins/message_app_connectors/ms-teams/test-exec.txt create mode 100644 pandora_plugins/message_app_connectors/slack/.gitignore create mode 100644 pandora_plugins/message_app_connectors/slack/pandora-slack-cli.py create mode 100644 pandora_plugins/message_app_connectors/slack/requirements.txt create mode 100644 pandora_plugins/message_app_connectors/slack/test_exec.txt create mode 100644 pandora_plugins/message_app_connectors/telegram-bot-cli/pandora-telegram-cli.py create mode 100644 pandora_plugins/message_app_connectors/telegram-bot-cli/requirements.txt create mode 100644 pandora_plugins/message_app_connectors/telegram-bot-cli/test_exec.txt diff --git a/pandora_plugins/message_app_connectors/.vscode/settings.json b/pandora_plugins/message_app_connectors/.vscode/settings.json new file mode 100644 index 0000000000..dd432a8cda --- /dev/null +++ b/pandora_plugins/message_app_connectors/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "slack/venv/bin/python" +} \ No newline at end of file diff --git a/pandora_plugins/message_app_connectors/discord/.gitignore b/pandora_plugins/message_app_connectors/discord/.gitignore new file mode 100644 index 0000000000..5ceb3864c2 --- /dev/null +++ b/pandora_plugins/message_app_connectors/discord/.gitignore @@ -0,0 +1 @@ +venv diff --git a/pandora_plugins/message_app_connectors/discord/pandora_discord_cli.py b/pandora_plugins/message_app_connectors/discord/pandora_discord_cli.py new file mode 100644 index 0000000000..5a278a0e6b --- /dev/null +++ b/pandora_plugins/message_app_connectors/discord/pandora_discord_cli.py @@ -0,0 +1,173 @@ +import requests, argparse, sys, os +from datetime import datetime +from re import search +from base64 import b64decode +from discord_webhook import DiscordWebhook, DiscordEmbed + + +### Variables and arg parser ### +parser = argparse.ArgumentParser(description='Test parser dic') +parser.add_argument('-d', '--data', help='Data in coma separate keypairs. Ex: test=5,house=2', required=True) +parser.add_argument('-u', '--url', help='Discord webhook URL', required=True) +parser.add_argument('-t', '--alert_tittle', help='Alert tittle', default='PandoraFMS alert fired') +parser.add_argument('-D', '--alert_desc', help='Alert description', default='alert') +parser.add_argument('-m', '--message', help='Discord message', default='') +parser.add_argument('-T','--tittle_color', help='Alert tittle descripcion in HEX EX: 53e514', default="53e514") +parser.add_argument('-A','--author', help='Alert custom author', default='PandoraFMS') +parser.add_argument('-F','--footer', help='Custom footer', default='') +parser.add_argument('--avatar_url', help='Custom avatar URL for the user which send the alert', default='') +parser.add_argument('--author_url', help='Alert custom url author', default='') +parser.add_argument('--author_icon_url', help='Alert custom author icon url ', default='') +parser.add_argument('--thumb', help='Custom thumbnail url', default='') +parser.add_argument('--api_conf', help='Api configuration parameters in coma separate keypairs. EX "user=admin,pass=pandora,api_pass=1234,api_url=http://test.artica.es/pandora_console/include/api.php"') +parser.add_argument('--module_graph', help='Uses pandora API to generate a module graph and attach it to the alert needs module_id and interval parameters in coma separate keypairs. EX "module_id=55,interval=3600"') +parser.add_argument('--tmp_dir', help='Temporary path to store grph images', default='/tmp') + + +args = parser.parse_args() + +### Functions: +def parse_dic(cValues): + """convert coma separate keypairs into a dic. EX "test=5,house=8,market=2" wil return "{'test': '5', 'casa': '8', 'mercado': '2'}" """ + data={} + try : + for kv in cValues.split(","): + k,v = kv.strip().split("=") + data[k.strip()]=v.strip() + except Exception as e : + print(f"Warning, error parsing keypairs values: {e}") + return data + +def add_embed_itmes(data): + """iterate dictionary and set webhook fields, one for eacj keypair""" + for k, v in data.items() : + embed.add_embed_field(name=k, value=v) + +def parse_api_conf(cConf): + if args.api_conf : + # Parse Api config + print ("Api config enable") + apid = parse_dic(cConf) + + if apid.get("user") is None: + print ("Error no user defined in api_conf keypairs, skiping graph generation.") + return + + if apid.get("pass") is None: + print ("Error no password defined in api_conf keypairs, skiping graph generation.") + return + + if apid.get("api_pass") is None: + print ("Error no Api pass defined in api_conf keypairs, skiping graph generation.") + return + + if apid.get("api_url") is None: + apid['api_url'] = "http://127.0.0.1/pandora_console/include/api.php" + #print(f"api_url: {apid['api_url']}") + + return apid + else: + return None + +def parse_graph_conf(cGraph): + if not args.api_conf: + print ("To get graph data api conf shoul be provided please set an api config") + return + + if cGraph : + # Parse Api config + graphd = parse_dic(cGraph) + if graphd.get("module_id") is None: + print ("error no module_id defined in module_graph keypairs, skiping graph generation.") + return + + if graphd.get("interval") is None: + graphd["interval"] = 3600 + + return graphd + else: + return None + +def get_graph_by_moduleid (baseUrl,pUser, pPass, apiPass, moduleId, graphInterval) : + sep="url_encode_separator_%7C" + try: + url = f"{baseUrl}?op=get&op2=module_graph&id={moduleId}&other={graphInterval}%7C1&other_mode={sep}C&apipass={apiPass}&api=1&user={pUser}&pass={pPass}" + graph = requests.get(url) + if graph.status_code != 200: + print (f"Error requested api url, status code: {graph.status_code}. Skiping graph generation") + return None + if graph.text == "auth error": + print (f"Error requested Pandora api url, status code: {graph.text}. Skiping graph generation") + return None + if graph.text == "Id does not exist in database.": + print (f"Error requested Pandora api url, status code: {graph.text}. Skiping graph generation") + return None + except: + print("Error requested api url. Skiping graph generation") + return None + + return graph + +## Main +## Basic message +webhook = DiscordWebhook(url=args.url, content=args.message, avatar_url=args.avatar_url) +# create embed object for webhook +embed = DiscordEmbed(title=args.alert_tittle, description=args.alert_desc, color=int(args.tittle_color, 16)) +# set author +embed.set_author(name=args.author, url=args.author_url, icon_url=args.author_icon_url) +# set thumbnail +if args.thumb: embed.set_thumbnail(url=args.thumb) +# set footer +if args.footer : embed.set_footer(text=args.footer) +# set timestamp (default is now) +embed.set_timestamp() +# Parse data keys +data = parse_dic(args.data) +# add fields to embed +add_embed_itmes(data) + +# Parse api config +api = parse_api_conf(args.api_conf) +# Parse graph config +graph_cfg = parse_graph_conf(args.module_graph) + +## Generate module graph + +if graph_cfg is not None and api is not None: + graph = get_graph_by_moduleid (api["api_url"],api["user"], api["pass"], api["api_pass"], graph_cfg["module_id"], graph_cfg["interval"]) + + if graph is not None: + try: + namef = f"graph_{graph_cfg['module_id']}.{datetime.now().strftime('%s')}.png" + filename = f"{args.tmp_dir}/{namef}" + with open(filename, "wb") as f: + f.write(b64decode(graph.text)) + f.close + print (f"Graph generated on temporary file {filename}") + except Exception as e : + print(f"Error, cant generate graph file: {e}") + filename = None + + try: + with open(filename, "rb") as F: + webhook.add_file(file=F.read(), filename=namef) + f.close + embed.set_image(url=f'attachment://{namef}') + except Exception as e : + print(f"Error, cant add graph file: {e}") + filename = None + +# add embed object to webhook +webhook.add_embed(embed) + +# Execute webhook send +response = webhook.execute() + +# clean temp file if exist +try: + os.remove(filename) +except: + pass + +# print response +print (f"Message sent. status code: {response[0].status_code}") if response[0].status_code == 200 else print (f"Error status code: {response[0].status_code}") diff --git a/pandora_plugins/message_app_connectors/discord/requirements.txt b/pandora_plugins/message_app_connectors/discord/requirements.txt new file mode 100644 index 0000000000..edbda16382 --- /dev/null +++ b/pandora_plugins/message_app_connectors/discord/requirements.txt @@ -0,0 +1,6 @@ +certifi==2020.11.8 +chardet==3.0.4 +discord-webhook==0.11.0 +idna==2.10 +requests==2.24.0 +urllib3==1.25.11 diff --git a/pandora_plugins/message_app_connectors/discord/test-exec.txt b/pandora_plugins/message_app_connectors/discord/test-exec.txt new file mode 100644 index 0000000000..f63e7f5084 --- /dev/null +++ b/pandora_plugins/message_app_connectors/discord/test-exec.txt @@ -0,0 +1,19 @@ +#exetution example + +python3 pandora_discord_cli.py -d "Agent=Server22,Module=test_module,Group=Servers,State=Critical,Data=22,Timestamp=2020-11-04 11:14:00" \ +-u https://discord.com/api/webhooks/702868786843353179/YI1LOUzC64EcYcpPVB_ \ +--tittle_color ed2512 \ +--footer "PandoraFMS Alert" \ +-A "Sauron Systems" \ +--author_icon_url "https://pandorafms.com/wp-content/uploads/2019/04/software-de-monitorizacion-pandorafms-logo.png" \ +-m "We have bad news for you. Something is on CRITICAL status 2" \ +--author_url https://pandorafms.com/ \ +-D "Module test is going to critical" \ +--thumb https://pandorafms.com/images/alerta_roja.png \ +--avatar_url https://pandorafms.com/images/alerta_roja.png \ +--api_conf "user=admin,pass=pandora,api_pass=pandora,api_url=http://192.168.80.222/pandora_console/include/api.php" \ +--module_graph "module_id=6266, interval=3600" \ +--tmp_dir /tmp + +# Pandora FMS command definition example +python3 /usr/share/pandora_server/util/pandora-discord/pandora_discord_cli.py -u "_field1_" -d "_field2_" -D "_field3_" --tittle_color _field4_ --thumb _field5_ --api_conf "_field6_" --module_graph "_field7_" -A "Pandora FMS Alert system" --footer "PandoraFMS" --author_icon_url "https://pandorafms.com/wp-content/uploads/2019/04/software-de-monitorizacion-pandorafms-logo.png" --author_url https://pandorafms.com/ --tmp_dir /tmp \ No newline at end of file diff --git a/pandora_plugins/message_app_connectors/gchat/pandora-gchat-cli.py b/pandora_plugins/message_app_connectors/gchat/pandora-gchat-cli.py new file mode 100644 index 0000000000..5e2a47b3f8 --- /dev/null +++ b/pandora_plugins/message_app_connectors/gchat/pandora-gchat-cli.py @@ -0,0 +1,111 @@ +import requests +import argparse +import sys +import os +import json + +### Variables and arg parser ### +parser = argparse.ArgumentParser(description='Google chat webhook conector') +parser.add_argument( + '-d', '--data', help='Data in coma separate keypairs. Ex: test=5,house=2', required=True) +parser.add_argument( + '-u', '--url', help='Google chat webhook URL', required=True) +parser.add_argument('-t', '--alert_title', help='Alert title', + default='PandoraFMS alert system') +parser.add_argument('-D', '--alert_desc', + help='Alert description', default='Alert Fired') +parser.add_argument('--thumb', help='Custom thumbnail url', + default="https://pandorafms.com/images/alerta_roja.png") +parser.add_argument('--btn_desc', help='button description', default=None) +parser.add_argument('--btn_url', help='button url', + default="https://pandorafms.com/") + + +args = parser.parse_args() + +# classes + + +class Message(): + def __init__(self, title, subtitle, imageurl='https://goo.gl/aeDtrS'): + """ Initialize message object, setting header options""" + self.dic = { + 'cards': [] + } + + header = {'header': {'title': title, + 'subtitle': subtitle, 'imageUrl': imageurl}} + self.dic['cards'].append(header) + + sections = {'sections': []} + self.dic['cards'].append(sections) + + def add_header(self, title, subtitle, imageurl='https://goo.gl/aeDtrS'): + """Add header to message object""" + header = {'header': {'title': title, + 'subtitle': subtitle, 'imageUrl': imageurl}} + self.dic['cards'].append(header) + + def add_value(self, keyval): + """Add key value pairs data to message object, keyval should be a dictionary""" + m = '' + arr = [] + for k, v in keyval.items(): + m += f"{k}: {v} \n" + + arr.append({'textParagraph': {'text': m}}) + + widgets = {'widgets': arr} + self.dic['cards'][1]['sections'].append(widgets) + + def add_buttom(self, desc, url): + """Add button to message object""" + btn = [{"textButton": {"text": desc, "onClick": {"openLink": {'url': url}}}}] + arr = [({'buttons': btn})] + + widgets = {'widgets': arr} + self.dic['cards'][1]['sections'].append(widgets) + +# functions + + +def parse_dic(cValues): + """convert coma separate keypairs into a dic. EX "test=5,house=8,market=2" wil return "{'test': '5', 'casa': '8', 'mercado': '2'}" """ + data = {} + try: + for kv in cValues.split(","): + k, v = kv.strip().split("=") + data[k.strip()] = v.strip() + except Exception as e: + print(f"Warning, error parsing keypairs values: {e}") + return data + + +def sendMessage(url, message): + """sends google chat message""" + message = json.dumps(message) + try: + header = {'Content-Type': 'application/json; charset: UTF-8'} + response = requests.post(url, headers=header, data=message) + + print(f"Mesage sent succefuly: {response.status_code}") + except: + print("Error requested api url. Skiping graph generation") + return None + return response + + +if __name__ == '__main__': + + # Initializaate message object + a = Message(args.alert_title, args.alert_desc, args.thumb) + # Parse data values + data = parse_dic(args.data) + # Add datavalues into message object + a.add_value(data) + # Chek button parameters and add it to the message object + if args.btn_desc != None: + a.add_buttom(args.btn_desc, args.btn_url) + + # Send message + sendMessage(args.url, a.dic) diff --git a/pandora_plugins/message_app_connectors/gchat/requirements.txt b/pandora_plugins/message_app_connectors/gchat/requirements.txt new file mode 100644 index 0000000000..2d0a3be144 --- /dev/null +++ b/pandora_plugins/message_app_connectors/gchat/requirements.txt @@ -0,0 +1,5 @@ +certifi==2020.12.5 +chardet==3.0.4 +idna==2.10 +requests==2.25.0 +urllib3==1.26.2 diff --git a/pandora_plugins/message_app_connectors/gchat/test-exec.txt b/pandora_plugins/message_app_connectors/gchat/test-exec.txt new file mode 100644 index 0000000000..d2703d9e9a --- /dev/null +++ b/pandora_plugins/message_app_connectors/gchat/test-exec.txt @@ -0,0 +1,14 @@ +#exetution example + +python3 pandora-gchat-cli.py \ +-d 'Agent=Server22,Module=test_module,Group=Servers,State=Critical,Data=22,Timestamp=2020-11-04 11:14:00' \ +-u https://chat.googleapis.com/v1/spaces/AAAA6-AOZQ8/messages\?key\=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI\&token\=_ZGwKN4lue8ZiDKGVMLfMay3hLRrYjmgYr2fXPqPy0c%3D \ +-t 'PandoraFMS Alert' \ +-D 'Alert Fired' \ +--thumb https://pandorafms.com/images/alerta_roja.png \ +--btn_desc pandorafms.com \ +--btn_url https://pandorafms.com/ + + +# Pandora FMS command definition example +python3 /usr/share/pandora_server/util/pandora-gchat/pandora-gchat-cli.py -d '_field1_' -u '_field2_' -t '_field3_' -D '_field4_' --thumb '_field5_' --btn_desc pandorafms.com --btn_url https://pandorafms.com/ \ No newline at end of file diff --git a/pandora_plugins/message_app_connectors/ms-teams/pandora-msteams-cli.py b/pandora_plugins/message_app_connectors/ms-teams/pandora-msteams-cli.py new file mode 100755 index 0000000000..f0d4ac89a8 --- /dev/null +++ b/pandora_plugins/message_app_connectors/ms-teams/pandora-msteams-cli.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse, pymsteams + +parser = argparse.ArgumentParser(description='MS Teams connector') +parser.add_argument('-d', '--data', help='Data in coma separate keypairs. Ex: test=5,house=2', required=True) +parser.add_argument('-u', '--url', help='Teams webhook URL', required=True) +parser.add_argument('-t', '--alert_tittle', help='Alert tittle', default='PandoraFMS alert fired') +parser.add_argument('-D', '--alert_desc', help='Alert description', default='Alert Fired') +parser.add_argument('-m', '--message', help='Alert message', default='') +parser.add_argument('-T','--tittle_color', help='Alert tittle descripcion in HEX EX: 53e514', default="ff0000") +parser.add_argument('--sub_desc', help='Alert sub description', default='Alert Fired') +parser.add_argument('--thumb', help='Custom thumbnail url', default="https://pandorafms.com/images/alerta_roja.png") +parser.add_argument('--button', help='Pandora button Url', default='https://pandorafms.com') +parser.add_argument('--button_desc', help='Pandora button description', default='Open web console') + +args = parser.parse_args() + +### Functions: +def parse_dic(cValues): + """convert coma separate keypairs into a dic. EX "test=5,house=8,market=2" wil return "{'test': '5', 'casa': '8', 'mercado': '2'}" """ + data={} + try : + for kv in cValues.split(","): + k,v = kv.strip().split("=") + data[k.strip()]=v.strip() + except Exception as e : + print(f"Warning, error parsing keypairs values: {e}") + return data + +def add_embed_itmes(data): + """iterate dictionary and set webhook fields, one for eacj keypair""" + for k, v in data.items() : + myMessageSection.addFact(f"{k}:", v) + +##Main + +# You must create the connectorcard object with the Microsoft Webhook URL +myTeamsMessage = pymsteams.connectorcard(args.url) + +# Set Summary +myTeamsMessage.summary('Pandora FMS') + +# Set Alert tittle +myTeamsMessage.title(args.alert_tittle) + +# Set link buttom +myTeamsMessage.addLinkButton(args.button_desc, args.button) + +# Set message color +myTeamsMessage.color(args.tittle_color) + +# create the section +myMessageSection = pymsteams.cardsection() + +# Section Title +myMessageSection.title(args.message) + +# Activity Elements +myMessageSection.activityTitle(args.alert_desc) +myMessageSection.activitySubtitle(args.sub_desc) +myMessageSection.activityImage(args.thumb) + +# Facts are key value pairs displayed in a list. +data = parse_dic(args.data) +add_embed_itmes(data) + +# Section Text +# myMessageSection.text("This is my section text") + +# Section Images +# myMessageSection.addImage("http://i.imgur.com/c4jt321l.png", ititle="This Is Fine") + +# Add your section to the connector card object before sending +myTeamsMessage.addSection(myMessageSection) + +# Then send the card +try: + myTeamsMessage.send() +except Exception as e : + exit(f"Error sending to message: {e}") + +print (f"Mesage sent succefuly: {myTeamsMessage.last_http_status}") \ No newline at end of file diff --git a/pandora_plugins/message_app_connectors/ms-teams/pandora-teams-cli.py b/pandora_plugins/message_app_connectors/ms-teams/pandora-teams-cli.py new file mode 100755 index 0000000000..4bd997b38f --- /dev/null +++ b/pandora_plugins/message_app_connectors/ms-teams/pandora-teams-cli.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse, pymsteams + +parser = argparse.ArgumentParser(description='MsTeams connector') +parser.add_argument('-d', '--data', help='Data in coma separate keypairs. Ex: test=5,house=2', required=True) +parser.add_argument('-u', '--url', help='Teams webhook URL', required=True) +parser.add_argument('-t', '--alert_tittle', help='Alert tittle', default='PandoraFMS alert fired') +parser.add_argument('-D', '--alert_desc', help='Alert description', default='Alert Fired') +parser.add_argument('-m', '--message', help='Alert message', default='') +parser.add_argument('-T','--tittle_color', help='Alert tittle descripcion in HEX EX: 53e514', default="53e514") +parser.add_argument('--sub_desc', help='Alert sub description', default='Alert Fired') +parser.add_argument('--thumb', help='Custom thumbnail url', default="https://pandorafms.com/images/alerta_roja.png") +parser.add_argument('--button', help='Pandora button Url', default='https://pandorafms.com') +parser.add_argument('--button_desc', help='Pandora button description', default='Open web console') + +args = parser.parse_args() + +### Functions: +def parse_dic(cValues): + """convert coma separate keypairs into a dic. EX "test=5,house=8,market=2" wil return "{'test': '5', 'casa': '8', 'mercado': '2'}" """ + data={} + try : + for kv in cValues.split(","): + k,v = kv.strip().split("=") + data[k.strip()]=v.strip() + except Exception as e : + print(f"Warning, error parsing keypairs values: {e}") + return data + +def add_embed_itmes(data): + """iterate dictionary and set webhook fields, one for eacj keypair""" + for k, v in data.items() : + myMessageSection.addFact(f"{k}:", v) + +##Main + +# You must create the connectorcard object with the Microsoft Webhook URL +myTeamsMessage = pymsteams.connectorcard(args.url) + +# Set Summary +myTeamsMessage.summary(args.message) + +# Set Alert tittle +myTeamsMessage.title(args.alert_tittle) + +# Set link buttom +myTeamsMessage.addLinkButton(args.button_desc, args.button) + +# Set message color +myTeamsMessage.color(args.tittle_color) + +# create the section +myMessageSection = pymsteams.cardsection() + +# Section Title +myMessageSection.title(args.message) + +# Activity Elements +myMessageSection.activityTitle(args.alert_desc) +myMessageSection.activitySubtitle(args.sub_desc) +myMessageSection.activityImage(args.thumb) + +# Facts are key value pairs displayed in a list. +data = parse_dic(args.data) +add_embed_itmes(data) + +# Section Text +# myMessageSection.text("This is my section text") + +# Section Images +# myMessageSection.addImage("http://i.imgur.com/c4jt321l.png", ititle="This Is Fine") + +# Add your section to the connector card object before sending +myTeamsMessage.addSection(myMessageSection) + +# Then send the card +try: + myTeamsMessage.send() +except Exception as e : + print(f"Error sending to message: {e}") \ No newline at end of file diff --git a/pandora_plugins/message_app_connectors/ms-teams/requirements.txt b/pandora_plugins/message_app_connectors/ms-teams/requirements.txt new file mode 100644 index 0000000000..da3bc60eee --- /dev/null +++ b/pandora_plugins/message_app_connectors/ms-teams/requirements.txt @@ -0,0 +1,6 @@ +certifi==2020.11.8 +chardet==3.0.4 +idna==2.10 +pymsteams==0.1.14 +requests==2.25.0 +urllib3==1.26.2 diff --git a/pandora_plugins/message_app_connectors/ms-teams/test-exec.txt b/pandora_plugins/message_app_connectors/ms-teams/test-exec.txt new file mode 100644 index 0000000000..f92f6ae2c9 --- /dev/null +++ b/pandora_plugins/message_app_connectors/ms-teams/test-exec.txt @@ -0,0 +1,15 @@ +#exetution example + +python3 pandora-teams-cli.py -d "Agent=Server22,Module=test_module,Group=Servers,State=Critical,Data=22,Timestamp=2020-11-04 11:14:00" \ +-u https://outlook.office.com/webhook/6f819e54-9c3f-4f87-94f4-90159496ef12@b3b55021-a812-46af-a5ef-127cc662d5b7/IncomingWebhook/634dafb7fe6549c9a214dd7fa9b97416/76c50d52-8678-49c2-9279-9f7bb3bb5a07 \ +-t "Alert Tittle" \ +-D "Alert description" \ +-m "Pandora FMS Alert message" \ +-T 53e514 \ +--sub_desc "Alert subdescription" \ +--thumb "https://pandorafms.com/images/alerta_roja.png" \ +--button https://pandorafms.com \ +--button_desc "Open PandoraFMS" + +# Pandora FMS command definition example +python3 /usr/share/pandora_server/util/pandora-teams/pandora-msteams-cli.py -d "_field1_" -u "_field2_" -t "_field3_" -D "_field4_" -m "_field5_" -T _field6_ --sub_desc "_field7_" --thumb "_field8_" --button http://newfork.artica.es/pandora_console/ --button_desc "Open PandoraFMS Console" diff --git a/pandora_plugins/message_app_connectors/slack/.gitignore b/pandora_plugins/message_app_connectors/slack/.gitignore new file mode 100644 index 0000000000..5ceb3864c2 --- /dev/null +++ b/pandora_plugins/message_app_connectors/slack/.gitignore @@ -0,0 +1 @@ +venv diff --git a/pandora_plugins/message_app_connectors/slack/pandora-slack-cli.py b/pandora_plugins/message_app_connectors/slack/pandora-slack-cli.py new file mode 100644 index 0000000000..31cfa97811 --- /dev/null +++ b/pandora_plugins/message_app_connectors/slack/pandora-slack-cli.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import requests, argparse, sys, os +from slack_sdk import WebClient +from slack_sdk.errors import SlackApiError +from datetime import datetime +from re import search +from base64 import b64decode + + + +### Variables and arg parser ### +parser = argparse.ArgumentParser(description='Slack BOT APP conector') +parser.add_argument('-d', '--data', help='Data in coma separate keypairs. Ex: test=5,house=2', required=True) +parser.add_argument('-t', '--token', help='BOT Token', required=True) +parser.add_argument('-c', '--channel', help='Slack channel id/name', required=True) +parser.add_argument('-e', '--emoji', help='Slack emoji for tittle, default: :red_circle:', default=':red_circle:') +parser.add_argument('-T', '--tittle', help='Alert tittle, default: PandoraFMS alert', default='PandoraFMS alert') +parser.add_argument('-D', '--desc', help='Slack description message', default='') +parser.add_argument('-F','--footer', help='Custom footer, default: PandoraFMS', default='PandoraFMS') +parser.add_argument('--api_conf', help='Api configuration parameters in coma separate keypairs. EX "user=admin,pass=pandora,api_pass=1234,api_url=http://test.artica.es/pandora_console/include/api.php"') +parser.add_argument('--module_graph', help='Uses pandora API to generate a module graph and attach it to the alert needs module_id and interval parameters in coma separate keypairs. EX "module_id=55,interval=3600"') +parser.add_argument('--tmp_dir', help='Temporary path to store grph images', default='/tmp') + +args = parser.parse_args() +filename = None + +#Functions + +def parse_dic(cValues): + """convert coma separate keypairs into a dic. EX "test=5,house=8,market=2" wil return "{'test': '5', 'casa': '8', 'mercado': '2'}" """ + data={} + try : + for kv in cValues.split(","): + k,v = kv.strip().split("=") + data[k.strip()]=v.strip() + except Exception as e : + print(f"Warning, error parsing keypairs values: {e}") + return data + +def compose_message (values, tittle, emoji, message, footer): + """Format Text""" + values = parse_dic(values) + m = f"{emoji} *{tittle}*\n_{message}_\n\n" + + for k, v in values.items() : + m += f"*{k}* : {v}\n" + return m + +def parse_api_conf(cConf): + """Check apiconfiguration parameters """ + if args.api_conf : + # Parse Api config + print ("Api config enable") + apid = parse_dic(cConf) + + if apid.get("user") is None: + print ("Warning. no user defined in api_conf keypairs, skiping graph generation.") + return None + + if apid.get("pass") is None: + print ("Warning. no password defined in api_conf keypairs, skiping graph generation.") + return None + + if apid.get("api_pass") is None: + print ("Warning. no api pass defined in api_conf keypairs, skiping graph generation.") + return None + + if apid.get("api_url") is None: + apid['api_url'] = "http://127.0.0.1/pandora_console/include/api.php" + #print(f"api_url: {apid['api_url']}") + + return apid + else: + return None + +def parse_graph_conf(cGraph): + """Check module graph parameters """ + if cGraph : + # Parse Api config + graphd = parse_dic(cGraph) + if graphd.get("module_id") is None: + print ("Warning. no module_id defined in module_graph keypairs, skiping graph generation.") + return + + if graphd.get("interval") is None: + graphd["interval"] = 3600 + + return graphd + else: + print("Warning. no module_graph keypairs defined, skiping graph generation") + return None + +def get_graph_by_moduleid (baseUrl,pUser, pPass, apiPass, moduleId, graphInterval, sep="url_encode_separator_%7C") : + """Call Pandorafms api to get graph""" + try: + url = f"{baseUrl}?op=get&op2=module_graph&id={moduleId}&other={graphInterval}%7C1&other_mode={sep}C&apipass={apiPass}&api=1&user={pUser}&pass={pPass}" + graph = requests.get(url) + if graph.status_code != 200: + print (f"Error requested api url, status code: {graph.status_code}. Skiping graph generation") + return None + if graph.text == "auth error": + print (f"Error requested Pandora api url, status code: {graph.text}. Skiping graph generation") + return None + if graph.text == "Id does not exist in database.": + print (f"Error requested Pandora api url, status code: {graph.text}. Skiping graph generation") + return None + + except: + print("Error requested api url. Skiping graph generation") + return None + return graph + +def send_message(message, channel, client, feddback=None): + """Send text message as slack bot""" + try: + response = client.chat_postMessage(channel=channel, text=message) + assert response["message"]["text"] == message + if feddback is not None: print(feddback) + except SlackApiError as e: + # You will get a SlackApiError if "ok" is False + assert e.response["ok"] is False + assert e.response["error"] # str like 'invalid_auth', 'channel_not_found' + print(f"Got an Slack auth error: {e.response['error']}") + exit() + +def send_image(imagepath, channel, client) : + """Send file as slack bot""" + try: + response = client.files_upload(channels=channel, file=imagepath) + assert response["file"] # the uploaded file + except SlackApiError as e: + # You will get a SlackApiError if "ok" is False + assert e.response["ok"] is False + assert e.response["error"] # str like 'invalid_auth', 'channel_not_found' + print(f"File Got an error: {e.response['error']}") + +# Main +# Intance the client object +client = WebClient(token=args.token) +# Compose message +messageString = compose_message(args.data, args.tittle, args.emoji, args.desc, args.footer) + +# Parse api config +if args.api_conf : + api = parse_api_conf(args.api_conf) + # Parse graph config + if api is not None: + graph_cfg = parse_graph_conf(args.module_graph) + + ## Generate graph + if graph_cfg is not None : + graph = get_graph_by_moduleid (api["api_url"],api["user"], api["pass"], api["api_pass"], graph_cfg["module_id"], graph_cfg["interval"]) + + if graph is not None: + try: + filename = f"{args.tmp_dir}/graph_{graph_cfg['module_id']}.{datetime.now().strftime('%s')}.png" + with open(filename, "wb") as f: + f.write(b64decode(graph.text)) + f.close + print (f"Graph generated on temporary file {filename}") + except Exception as e : + print(f"Error, cant generate graph file: {e}") + filename = None + else: filename = None + +# Send message +send_message(messageString, args.channel, client, "> Mesage sent succefuly") +if filename is not None: + if os.path.isfile(filename): send_image(filename, args.channel, client) +if args.footer: send_message(args.footer, args.channel, client) + +try: + os.remove(filename) +except: + exit() \ No newline at end of file diff --git a/pandora_plugins/message_app_connectors/slack/requirements.txt b/pandora_plugins/message_app_connectors/slack/requirements.txt new file mode 100644 index 0000000000..cdb6237a13 --- /dev/null +++ b/pandora_plugins/message_app_connectors/slack/requirements.txt @@ -0,0 +1,6 @@ +certifi==2020.11.8 +chardet==3.0.4 +idna==2.10 +requests==2.24.0 +slack-sdk==3.0.0 +urllib3==1.25.11 diff --git a/pandora_plugins/message_app_connectors/slack/test_exec.txt b/pandora_plugins/message_app_connectors/slack/test_exec.txt new file mode 100644 index 0000000000..f4639dd282 --- /dev/null +++ b/pandora_plugins/message_app_connectors/slack/test_exec.txt @@ -0,0 +1,14 @@ +#exetution example + +python3 pandora-slack-cli.py -d "Agent=Server22,Module=test_module,Group=Servers,State=Critical,Data=22,Timestamp=2020-11-04 11:14:00" \ +-t xoxb-1506287138481-1518221486533-V3QVzyBbS6lQnTKdfrdwCqYI \ +-c "varios" \ +-e ":red_circle:" \ +-T "PandoraFMS alert" \ +-D "El agente x esta en estado critico" \ +-F "Pandora FMS" \ +--api_conf "user=admin,pass=pandora,api_pass=pandora,api_url=http://192.168.80.43/pandora_console/include/api.php" \ +--module_graph "module_id=62, interval=3600" --tmp_dir /tmp + +# Pandora FMS command definition example +python3 /usr/share/pandora_server/util/pandora-slack/pandora-slack-cli.py -d "_field1_" -t _field2_ -c "_field3_" -e "_field4_" -T "_field5_" -D "_field6_" -F "Pandora FMS" --api_conf "_field7_" --module_graph "_field8_" --tmp_dir /tmp \ No newline at end of file diff --git a/pandora_plugins/message_app_connectors/telegram-bot-cli/pandora-telegram-cli.py b/pandora_plugins/message_app_connectors/telegram-bot-cli/pandora-telegram-cli.py new file mode 100644 index 0000000000..bcba8b65b0 --- /dev/null +++ b/pandora_plugins/message_app_connectors/telegram-bot-cli/pandora-telegram-cli.py @@ -0,0 +1,27 @@ +import requests, argparse, json, sys, os + +### Variables and arg parser ### +parser = argparse.ArgumentParser(description='Bot telegram cli') +parser.add_argument('-m', '--message', help='Message to be send', required=True) +parser.add_argument('-t', '--token', help='Bot token', required=True) +parser.add_argument('-c', '--chat_id', help='chat id to send messages', required=True) + + +args = parser.parse_args() + + +def send(mssg, chatId, token): + url = f"https://api.telegram.org/bot{token}/sendMessage" + headers = {'content-type': 'application/json'} + + data = { + "chat_id": chatId, + "text": mssg + } + + response = requests.get(url, data=json.dumps(data), headers=headers) + + r = response.json() + print(r) + +send(mssg=args.message, chatId=args.chat_id, token=args.token) diff --git a/pandora_plugins/message_app_connectors/telegram-bot-cli/requirements.txt b/pandora_plugins/message_app_connectors/telegram-bot-cli/requirements.txt new file mode 100644 index 0000000000..d8b263c7fb --- /dev/null +++ b/pandora_plugins/message_app_connectors/telegram-bot-cli/requirements.txt @@ -0,0 +1,5 @@ +certifi==2020.11.8 +chardet==3.0.4 +idna==2.10 +requests==2.24.0 +urllib3==1.25.11 diff --git a/pandora_plugins/message_app_connectors/telegram-bot-cli/test_exec.txt b/pandora_plugins/message_app_connectors/telegram-bot-cli/test_exec.txt new file mode 100644 index 0000000000..86844d04c2 --- /dev/null +++ b/pandora_plugins/message_app_connectors/telegram-bot-cli/test_exec.txt @@ -0,0 +1,6 @@ +#exetution example + +python3 pandora-telegram-cli.py -t 1412764845:AAG-OxOKISOXwhITLFFNm6oq5YD2KI72fTQ -c -432610056 -m "Testing pandora telegram cli" + +# Pandora FMS command definition example +python3 pandora-telegram-cli.py -t _field1_ -c _field2_ -m" _field3_"