elkserver/docker/redelk-base/redelkinstalldata/scripts/modules/email/module.py (89 lines of code) (raw):
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Part of RedELK
This connector sends RedELK alerts via email
Authors:
- Outflank B.V. / Mark Bergman (@xychix)
- Lorenzo Bernardi (@fastlorenzo)
"""
import os
import logging
import smtplib
import base64
from email.header import Header
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr, formatdate
from json2html import json2html
from config import notifications, project_name
from modules.helpers import get_value, pprint
info = {
"version": 0.1,
"name": "email connector",
"description": "This connector sends RedELK alerts via email",
"type": "redelk_connector",
"submodule": "email",
}
FILEDIR = os.path.abspath(os.path.dirname(__file__))
class Module:
"""email connector module"""
def __init__(self):
self.logger = logging.getLogger(info["submodule"])
def send_mail(
self,
to_addresses,
mail,
subject,
from_address=notifications["email"]["from"],
attachment=None,
smtp_host=notifications["email"]["smtp"]["host"],
smtp_port=notifications["email"]["smtp"]["port"],
smtp_user=notifications["email"]["smtp"]["login"],
smtp_pass=notifications["email"]["smtp"]["pass"],
): # pylint: disable=too-many-arguments
"""Sends the email"""
message = MIMEMultipart()
# Read html File
html = mail
message["Subject"] = f"[{project_name}] {subject}"
message["From"] = formataddr((str(Header(from_address, "utf-8")), from_address))
message["To"] = ", ".join(to_addresses)
message["Date"] = formatdate()
# DONE PREPARATION, BUILD MAIL
message.attach(MIMEText(html, "html"))
if attachment is not None:
message = self.attach_file(message, attachment)
# Sending the stuff
connection = smtplib.SMTP(smtp_host, int(smtp_port))
connection.starttls()
connection.login(smtp_user, smtp_pass)
resp = connection.sendmail(from_address, to_addresses, message.as_string())
self.logger.debug("smtpd response: %s", resp)
connection.close()
def attach_file(self, message, filename): # pylint: disable=no-self-use
"""Attaches the HTML version of the email to the message"""
with open(filename, "rb") as file:
part = MIMEApplication(file.read(), Name=filename)
part["Content-Disposition"] = f'attachment; filename="{filename}"'
message.attach(part)
return message
def send_alarm(self, alarm):
"""Send the alarm"""
# Read the RedELK logo from file and base64 encode it
with open(f"{FILEDIR}/redelk_white.png", "rb") as logo_file:
redelk_logo_b64 = base64.b64encode(logo_file.read()).decode("utf-8")
mail = f"""
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Alarm from RedELK</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style type="text/css">
#normal {{
font-family: Tahoma, Geneva, sans-serif;
font-size: 16px;
line-height: 24px;
}}
</style>
</head>
<body style="margin: 0; padding: 0;">
<table align="center" cellpadding="0" cellspacing="0" width="800" style="border-collapse: collapse;" style="max-width:800px;">
<tr>
<td bgcolor="#212121" rowspan=2 width="120px" style="padding: 30px 30px 30px 30px; text-align:center;">
<img height="60px" src="data:image/png;base64,{redelk_logo_b64}" alt="img" />
</td>
<td bgcolor="#212121" height="30px" style="color: #FAFAFA; font-family: Arial, sans-serif; font-size: 24px; padding: 30px 30px 0px 10px;">
RedELK alarm: <em>{alarm["info"]["name"]}</em>
</td>
</tr>
<tr>
<td bgcolor="#212121" height="20px" style="color: #FAFAFA; font-family: Arial, sans-serif; font-size: 16px; line-height: 20px; padding: 20px 30px 30px 10px;">
Project: <em>{project_name}</em><br/>Total hits: <em>{alarm["hits"]["total"]}</em>
</td>
</tr>
<tr>
<td colspan=2 style="color: #153643; font-family: Arial, sans-serif; font-size: 16px; line-height: 20px; padding: 0px 30px 0px 10px;">
<p>{alarm["info"]["description"]}</p>
</td>
</tr>
"""
subject = f'Alarm from {alarm["info"]["name"]} [{alarm["hits"]["total"]} hits]'
if len(alarm["groupby"]) > 0:
mail += f"""
<tr>
<td colspan=2 style="color: #153643; font-family: Arial, sans-serif; font-size: 12px; line-height: 16px; padding: 0px 15px 0px 15px;">
<p>Please note that the items below have been grouped by: {pprint(alarm["groupby"])}</p>
</td>
</tr>
"""
try:
for hit in alarm["hits"]["hits"]:
index = 0
title = hit["_id"]
while index < len(alarm["groupby"]):
val = get_value(f'_source.{alarm["groupby"][index]}', hit)
if index == 0:
title = val
else:
title = f"{title} / {val}"
index += 1
mail += f"""
<tr>
<td bgcolor="#323232" colspan=2 style="color: #FAFAFA; font-family: Arial, sans-serif; font-size: 16px; line-height: 20px; padding: 10px 10px 10px 10px; text-align:center;">
<b>{title}</b>
</td>
</tr>
"""
row = 0
for field in alarm["fields"]:
bgcolor = "#FAFAFA" if row % 2 == 0 else "#F1F1F1"
val = get_value(f"_source.{field}", hit)
value = json2html.convert(json=val)
mail += f"""
<tr bgcolor="{bgcolor}" style="color: #153643; font-family: Arial, sans-serif; font-size: 12px; line-height: 16px;">
<td style="padding: 10px 10px 10px 10px;"><b>{field}</b></td>
<td style="padding: 10px 10px 10px 10px; white-space:pre-wrap; word-wrap:break-word">{value}</td>
</tr>
"""
row += 1
mail += '<tr><td colspan=2 style="padding: 15px;"> </td></tr>'
mail += "</table>\n</body>\n</html>"
except Exception as error: # pylint: disable=broad-except
self.logger.error("Error sending email: %s", error)
self.logger.exception(error)
mail += "</body></html>\n"
self.send_mail(notifications["email"]["to"], mail, subject)