Provision users and organisations¶
Introduction¶
- UUID: 078ac11a-d0d9-4beb-aec7-067735ce6bed
- Started from issue 43
- State: Published : demo version with output
- Purpose: This playbook creates users and organisations with PyMISP. It also shows how to reset a password and delete or disable users. It includes an example how to get the user logs and how to create large number of users at once. For this playbook your MISP API key needs to be associated with a user that is a site admin.
- Tags: [ "users", "organisations", "training", "provisioning" ]
- External resources: Mattermost
- Target audience: CTI
- Graphical workflow
Playbook¶
- Playbook title
- Introduction
- Preparation
- PR:1 Initialise environment
- PR:2 Load helper variables
- Organisations
- OR:1 List the MISP organisations
- OR:2 Add a new MISP organisation
- OR:3 Delete a MISP organisation
- OR:4 Edit a MISP organisation
- Roles
- RO:1 List MISP user roles
- Users
- US:1 List MISP users
- US:2 Add MISP users
- US:3 Delete a MISP user
- US:4 Reset password of a user
- US:5 Disable a user
- US:6 Search for a user by IP
- US:7 Dump user logs
- Users for training instances
- TR:1 Create training users
- TR:2 Delete training users
- Summary
- EN:1 Create the summary of the playbook
- EN:2 Send a summary to Mattermost
- EN:3 End of the playbook
- External references
- Technical details
Preparation¶
PR:1 Initialise environment¶
This section initialises the playbook environment and loads the required Python libraries.
The credentials for MISP (API key) are loaded from the file keys.py
in the directory vault. For this playbook your MISP API key needs to be associated with a user that is a site admin.
A PyMISP object is created to interact with MISP and the active MISP server is displayed. By printing out the server name you know that it's possible to connect to MISP. In case of a problem PyMISP will indicate the error with PyMISPError: Unable to connect to MISP
.
The contents of the keys.py
file should contain at least :
misp_url="<MISP URL>" # The URL to our MISP server
misp_key="<MISP API KEY>" # The MISP API key
misp_verifycert=<True or False> # Indicate if PyMISP should attempt to verify the certificate or ignore errors
# Initialise Python environment
import urllib3
import sys
import json
from pyfaup.faup import Faup
from prettytable import PrettyTable, MARKDOWN
from IPython.display import Image, display, display_markdown, HTML
from datetime import date
import requests
import uuid
from uuid import uuid4
from pymisp import *
from pymisp.tools import GenericObjectGenerator
import csv
from io import StringIO
import secrets, string
# Load the credentials
sys.path.insert(0, "../vault/")
from keys import *
if misp_verifycert is False:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
print("The \033[92mPython libraries\033[90m are loaded and the \033[92mcredentials\033[90m are read from the keys file.")
# Create the PyMISP object
misp = PyMISP(misp_url, misp_key, misp_verifycert)
print("I will use the MISP server \033[92m{}\033[90m for this playbook.\n\n".format(misp_url))
The version of PyMISP recommended by the MISP instance (2.4.176) is newer than the one you're using now (2.4.173). Please upgrade PyMISP.
The Python libraries are loaded and the credentials are read from the keys file. I will use the MISP server https://misp.demo.cudeso.be/ for this playbook.
PR:2 Set helper variables¶
This cell contains helper variables that are used in this playbook.
# MISP roles
misp_roles = {}
# List of created users
add_users = []
# Char list for generating the password
charlist = string.ascii_letters + string.digits + string.punctuation
charlist = charlist.replace('|', '') # Remove the pipe
# Password length when generating passwords
password_length = 16
# Organisation ID
org_id = False
# Summary
summary_org = ""
summary_users = ""
summary_users_training = ""
Organisations¶
In MISP, users belongs to an organisation.
As a reminder, local organisations are those local on your MISP server, remote organisations are automatically created when you sync with external servers. You can change an organisation from remote to local by editing its properties.
OR:1 List the MISP organisations¶
The next cell queries MISP for all local (set with scope
) organisations and prints out their ID (org_id
) and UUID.
organisation_list = misp.organisations(scope="local", pythonify=True)
table = PrettyTable()
table.field_names = ["ID", "Organisation", "UUID"]
table.align["ID"] = "r"
table.align["Organisation"] = "l"
for org in organisation_list:
table.add_row([org.id, org.name, org.uuid])
print(table.get_string(sortby="Organisation"))
+----+--------------+--------------------------------------+ | ID | Organisation | UUID | +----+--------------+--------------------------------------+ | 1 | DEMO | 5c1eb4f8-bae5-45f5-a772-06d4a3f64c3e | | 5 | LEA demo | bc081572-a389-49c7-bd2a-5e405834b181 | +----+--------------+--------------------------------------+
OR:2 Add a new MISP organisation¶
You can add a new MISP organisation in the next cells. Later on, when you add users you can add them to this newly created organisation or you can use an existing organisation from the list above. When you create an organisation you can specify its UUID. If you don't provide one, then MISP will generate it for you.
# Organisation details
organisation = MISPOrganisation()
organisation.name = "Training botvrij.eu"
# organisation.uuid = ""
organisation.type = "MISP Organisation - Training"
organisation.nationality = "Belgium"
organisation.sector = "IT"
organisation.local = 1
# Only execute if you want to create a new organisation
org = misp.add_organisation(organisation, pythonify=True)
if not "errors" in org:
print("Organisation \033[92m{}\033[90m created.\nUse org_id \033[92m{}\033[90m or \033[92m{}\033[90m to add users.\n\n".format(org.name, org.id, org.uuid))
org_id = org.id
summary_org += "### New MISP organisation\n"
summary_org += "\n - Name: **{}**".format(organisation.name)
summary_org += "\n - ID: {}".format(org.id)
summary_org += "\n - UUID: {}".format(org.uuid)
summary_org += "\n"
else:
print("Unable to create organisation \033[91m{}\033[90m \n{}\n\n".format(org_name, org))
org_id = False
Organisation Training botvrij.eu created. Use org_id 112 or dcd381ca-8f5d-4c71-a50b-182b3e0509cd to add users.
OR:3 Delete a MISP organisation¶
The playbook also allows you to delete an existing organisation. The code in the next cell is set to raw
, so it's not automatically executed. Generally organisations should never be deleted. If you are certain you would like to remove an organisation and are aware of the impact, make sure that there are no users or events tied to the organisation before deleting it.
OR:4 Edit a MISP organisation¶
Next to adding or deleting organisations, you can edit existing organisations. One of the common actions is moving a local organisation to a remote organisation. In the next cell we query for the organisation you created earlier and change it to a remote organisation, afterwards we switch it back to its original state.
org = misp.get_organisation(org_id, pythonify=True)
if not "errors" in org:
current_state = org.local
org.local = False
print("Setting organisation {} to \033[92mremote\033[90m.".format(org.name))
print(misp.update_organisation(org), "\n\n")
org.local = current_state
print("Setting organisation {} to \033[92moriginal state\033[90m.".format(org.name))
print(misp.update_organisation(org))
else:
print("Organisation not found \033[91m{}\033[90m.\n\n".format(org))
Setting organisation Training botvrij.eu to remote. {'Organisation': {'id': '112', 'name': 'Training botvrij.eu', 'date_created': '2023-10-18 15:56:28', 'date_modified': '2023-10-18 15:56:32', 'description': None, 'type': 'MISP Organisation - Training', 'nationality': 'Belgium', 'sector': 'IT', 'created_by': '1', 'uuid': 'dcd381ca-8f5d-4c71-a50b-182b3e0509cd', 'contacts': None, 'local': False, 'restricted_to_domain': [], 'landingpage': None}} Setting organisation Training botvrij.eu to original state. {'Organisation': {'id': '112', 'name': 'Training botvrij.eu', 'date_created': '2023-10-18 15:56:28', 'date_modified': '2023-10-18 15:56:32', 'description': None, 'type': 'MISP Organisation - Training', 'nationality': 'Belgium', 'sector': 'IT', 'created_by': '1', 'uuid': 'dcd381ca-8f5d-4c71-a50b-182b3e0509cd', 'contacts': None, 'local': True, 'restricted_to_domain': [], 'landingpage': None}}
Roles¶
Privileges are assigned to users by assigning them to role groups. Role groups use one of four options (Read Only, Manage My Own Events, Manage Organisation Events, Manage & Publish Organisation Events) determining what users can do with events as well as four additional privilege elevation settings.
RO:1 List MISP user roles¶
MISP has a build-in list of user roles but you can also add your own roles. When you add a new user you have to specify its role (with role_id
). If you do not specify a role in this playbook the newly created users are assigned the role of User.
roles = misp.roles(pythonify=True)
table = PrettyTable()
table.field_names = ["ID", "Role name"]
table.align["ID"] = "r"
table.align["Role name"] = "l"
for role in roles:
misp_roles[role.id] = role.name
table.add_row([role.id, role.name])
print(table.get_string(sortby="ID"))
+----+-------------+ | ID | Role name | +----+-------------+ | 1 | admin | | 2 | Org Admin | | 3 | User | | 4 | Publisher | | 5 | Sync user | | 6 | Read Only | | 7 | BigCruncher | +----+-------------+
# Set this to an organisation ID if you want to see users from a specific organisation
filter_organisation = False
users_list = misp.users(organisation=filter_organisation, pythonify=True)
table = PrettyTable()
table.field_names = ["ID", "Username", "org_id", "role_id"]
table.align["ID"] = "r"
table.align["Username"] = "l"
for user in users_list:
table.add_row([user.id, user.email, user.org_id, user.role_id])
print(table.get_string(sortby="Username"))
+-----+------------------+--------+---------+ | ID | Username | org_id | role_id | +-----+------------------+--------+---------+ | 1 | admin@admin.test | 1 | 1 | | 199 | sync@admin.test | 1 | 5 | +-----+------------------+--------+---------+
US:2 Add MISP users¶
The playbook adds users from a CSV that you can paste in the next cell. There is only one mandatory field: username. The other fields are optional. The format of the CSV is: username,role_id,password,org_id
- If role_id is empty then the user (
default_role_id
) role is used; - If password is empty then a random generated password is used;
- If org_id is empty the organisation specified earlier via
org_id
is used.
After the creation of the user the playbook prints a summary. Set summary_print_password
to True to print the newly generated password if you have emailing disabled in MISP. There are additional settings of importance, such as requiring a user to change the password after first login (change_pw
).
# CSV data
csv_data = """username,role_id,password,org_id
user1@training.botvrij.eu,,,
orgadmin1@training.botvrij.eu,2,,
admin1@training.botvrij.eu,1,,
"""
# Password change required
change_pw = 1
# Disable autoalert and contactalert
autoalert = 0
contactalert = 0
# Is the user initially disabled?
disabled = 0
# Default use role
default_role_id = 3 # User
# Print the password after creation
summary_print_password = True
print("Default organisation (if none is provided in the CSV): \033[92m{}\033[90m".format(org_id))
print("Default role (if none is provided in the CSV): \033[92m{}\033[90m".format(default_role_id))
print("\n\nContinue with the next cell to create the users.\n\n")
Default organisation (if none is provided in the CSV): 112 Default role (if none is provided in the CSV): 3 Continue with the next cell to create the users.
csv_file = StringIO(csv_data)
csv_reader = csv.reader(csv_file)
next(csv_reader) # Skip headers
count_user = 0
print("Start creating users ...")
for row in csv_reader:
username, role_id, password, user_org_id = row
# Generate a random password
if len(password.strip()) < 1:
password = ""
for i in range(password_length):
password += "".join(secrets.choice(charlist))
# Use the default organisation
if len(user_org_id.strip()) < 1:
user_org_id = org_id
organisation = misp.get_organisation(user_org_id, pythonify=True)
if "errors" in organisation:
user_org_id = 0
else:
organisation_name = organisation.name
# Use the default user role
if len(role_id.strip()) < 1:
role_id = default_role_id
# Create the user
user = MISPUser()
user.email = username
user.password = password
user.change_pw = change_pw
user.org_id = org_id
user.role_id = role_id
user.user_invited_by = "MISP playbook"
user.autoalert = autoalert
user.contactalert = contactalert
user.disabled = disabled
new_user = misp.add_user(user, pythonify=True)
if not "errors" in new_user:
user.id = new_user.id
user.authkey = new_user.authkey
user.custom_delete = False
user.custom_disabled = False
user["custom_organisation_name"] = organisation_name
add_users.append(user)
count_user += 1
print(" New user \033[92m{}\033[90m created with ID {}.".format(new_user.email, new_user.id))
else:
print(" Unable to add \033[91m{}\033[90m.{}.".format(user.email, new_user))
print("Created \033[92m{}\033[90m users.".format(count_user))
Start creating users ... New user user1@training.botvrij.eu created with ID 241. New user orgadmin1@training.botvrij.eu created with ID 242. New user admin1@training.botvrij.eu created with ID 243. Created 3 users.
Summary¶
The next cell prints out the list of users created. Be warned that depending on the setting of summary_print_password
it can print out passwords. At the end of this playbook the summary is sent to Mattermost.
table = PrettyTable()
table.field_names = ["ID", "Username", "Organisation", "Role", "Password"]
table.align["ID"] = "r"
table.align["Username"] = "l"
table.align["Organisation"] = "l"
table.align["Role"] = "l"
table.align["Password"] = "l"
for user in add_users:
password = user.password if summary_print_password else "******"
table.add_row([user.id, user.email, "{} - {}".format(user.org_id, user.custom_organisation_name), "{} - {}".format(user.role_id, misp_roles[str(user.role_id)]), password])
print(table.get_string(sortby="Username"))
+-----+-------------------------------+---------------------------+---------------+------------------+ | ID | Username | Organisation | Role | Password | +-----+-------------------------------+---------------------------+---------------+------------------+ | 243 | admin1@training.botvrij.eu | 112 - Training botvrij.eu | 1 - admin | la>4wI`&\j!'ICBq | | 242 | orgadmin1@training.botvrij.eu | 112 - Training botvrij.eu | 2 - Org Admin | ]ebqZv=4'wq6Rx4- | | 241 | user1@training.botvrij.eu | 112 - Training botvrij.eu | 3 - User | x$4qC,eY}.\jj;zF | +-----+-------------------------------+---------------------------+---------------+------------------+
US:3 Delete a MISP user¶
Just as easy it is to create a user, it's also possible to delete them. Note that the delete_user
function of PyMISP expects a user ID, not a user email. In the cell below we loop through all users until there is a match with the searched for email.
delete_user = "orgadmin1@training.botvrij.eu"
users_list = misp.users(pythonify=True)
result = False
for user in users_list:
if delete_user.strip() == user.email:
result = misp.delete_user(user.id) # Delete user can only by ID
if not "errors" in result:
print("User \033[92m{}\033[90m. deleted.\n\n".format(delete_user))
for el in add_users:
if int(el.id) == int(user.id):
el.custom_delete = True
else:
print("Unable to delete user \033[91m{}\033[90m {}.\n\n".format(delete_user, result))
if not result:
print("User \033[91m{}\033[90m not found and/or not deleted.\n\n".format(delete_user))
User orgadmin1@training.botvrij.eu. deleted.
US:4 Reset password of a user¶
MISP allows you to reset the password of a user (via a POST request to https://misp/users/initiatePasswordReset/{$user_id}). This relies on MISP being able to send out emails. In some environments emailing has been disabled. In these cases you have to manually set a temporary password and request the user to change the password on first login.
Note that the reset relies on the user ID. Previous cells have shown how to match the ID with an e-mail address.
user_id = 243
user = misp.get_user(user_id, expanded=False, pythonify=True)
if not "errors" in user:
password = ""
for i in range(password_length):
password += "".join(secrets.choice(charlist))
data = {"enable_password": 1, "change_pw": 1, "password": password}
request = misp._prepare_request("POST", "{}/admin/users/edit/{}".format(misp_url, user_id), data=data)
print("Attempt to reset the password for user \033[92m{}\033[90m with \033[92m{}\033[90m\n\n".format(user.email, password))
for el in add_users:
if int(el.id) == int(user.id):
el.password = password
print(request.text)
else:
print("User \033[91m{}\033[90m not found.\n\n".format(user_id))
Attempt to reset the password for user admin1@training.botvrij.eu with '18-d27z_`iY7s-W {"User":{"id":"243","password":"******","org_id":"112","server_id":"0","email":"admin1@training.botvrij.eu","autoalert":false,"invited_by":"1","gpgkey":"","certif_public":"","nids_sid":"9238533","termsaccepted":false,"newsread":"0","role_id":"1","change_pw":true,"contactalert":false,"disabled":false,"expiration":null,"current_login":"0","last_login":"0","force_logout":false,"date_created":"1697644601","date_modified":"1697644629","sub":null,"external_auth_required":false,"external_auth_key":"","last_api_access":"0","notification_daily":false,"notification_weekly":false,"notification_monthly":false,"totp":null,"hotp_counter":null}}
US:5 Disable a user¶
You can use the same approach to disable a user.
user_id = 241
user = misp.get_user(user_id, expanded=False, pythonify=True)
if not "errors" in user:
data = {"disabled": 1}
request = misp._prepare_request("POST", "{}/admin/users/edit/{}".format(misp_url, user_id), data=data)
print("Attempt to disable user \033[92m{}\033[90m.\n\n".format(user.email))
for el in add_users:
if int(el.id) == int(user.id):
el.custom_disabled = True
print(request.text)
else:
print("User \033[91m{}\033[90m not found.\n\n".format(user_id))
Attempt to disable user user1@training.botvrij.eu. {"User":{"id":"241","password":"******","org_id":"112","server_id":"0","email":"user1@training.botvrij.eu","autoalert":false,"invited_by":"1","gpgkey":"","certif_public":"","nids_sid":"9816788","termsaccepted":false,"newsread":"0","role_id":"3","change_pw":true,"contactalert":false,"disabled":true,"expiration":null,"current_login":"0","last_login":"0","force_logout":false,"date_created":"1697644601","date_modified":"1697644640","sub":null,"external_auth_required":false,"external_auth_key":"","last_api_access":"0","notification_daily":false,"notification_weekly":false,"notification_monthly":false,"totp":null,"hotp_counter":null}}
US:6 Search for a user by IP¶
MISP allows you to search for the user account that accessed MISP from a specific IP. Note that if multiple users use the same IP, only the last user ID will be returned.
Note that it's also possible to list the IPs used by a specific user account, but this feature is only accessible via the console interface with cake user user_ips <userid>
.
search_ip = "127.0.0.1"
data = {"ip": search_ip}
request = misp._prepare_request("POST", "{}/servers/ipUser".format(misp_url), data=data)
print(request.text)
{"127.0.0.1":{"id":"1","email":"admin@admin.test"}}
US:7 Dump user logs¶
And finally, you can also get the last log entries for a specific user. This can be used to alert on specific activity.
user_id = 1
request = misp._prepare_request("GET", "{}/admin/audit_logs/index/user:{}".format(misp_url, user_id))
request_json = json.loads(request.text)
table = PrettyTable()
table.field_names = ["ID", "Created", "Action", "Model", "Event ID", "Change"]
table.align["Change"] = "l"
table._max_width = {"Change": 30}
for row in request_json:
if "AuditLog" in row:
auditlog = row["AuditLog"]
table.add_row([auditlog["id"], auditlog["created"], auditlog["action"], auditlog["model"], auditlog["event_id"], auditlog["change"]])
print(table.get_string(sortby="Created", reversesort=True))
Users for training instances¶
In specific cases, such as when you setup a training environment, it's useful to automatically create a large number of users, with default credentials and then afterwards delete these users.
TR:1 Create training users¶
The next cell creates a number (max_user
) of user accounts (both a regular user and a site admin user) and links these users to an organisation (org_id
).
# How many users (Site Admin and User) do you want to add?
max_users = 5
# What's the organisation ID?
training_org_id = 1
if 'org_id' in vars() and org_id is not None:
training_org_id = org_id
# Naming and credentials for the training accounts
default_password = "Password-User-{user}"
default_siteadmin = "training-admin-{user}@training.botvrij.eu"
default_user = "training-user-{user}@training.botvrij.eu"
current_user = 1
print("Creating training users ...")
summary_users_training += "### Training users\n\n"
summary_users_training += "| User | Role | Password |\n"
summary_users_training += "|:------|:--------------|:-----------------|\n"
while current_user <= max_users:
user = MISPUser()
user.email = default_siteadmin.format(user=current_user)
user.password = default_password.format(user=current_user)
user.change_pw = 0
user.org_id = training_org_id
user.role_id = 1 # Site admin
add_user = misp.add_user(user, pythonify=True)
print(" Add Site Admin %s" % user.email)
summary_users_training += "| %s | %s | %s |\n" % (user.email, "admin", user.password)
user = MISPUser()
user.email = default_user.format(user=current_user)
user.password = default_password.format(user=current_user)
user.change_pw = 0
user.org_id = training_org_id
user.role_id = 3 # User
add_user = misp.add_user(user, pythonify=True)
print(" Add User %s" % user.email)
summary_users_training += "| %s | %s | %s |\n" % (user.email, "user", user.password)
current_user += 1
print("Finished.\n\n")
Creating training users ... Add Site Admin training-admin-1@training.botvrij.eu Add User training-user-1@training.botvrij.eu Add Site Admin training-admin-2@training.botvrij.eu Add User training-user-2@training.botvrij.eu Add Site Admin training-admin-3@training.botvrij.eu Add User training-user-3@training.botvrij.eu Add Site Admin training-admin-4@training.botvrij.eu Add User training-user-4@training.botvrij.eu Add Site Admin training-admin-5@training.botvrij.eu Add User training-user-5@training.botvrij.eu Finished.
TR:2 Delete training users¶
After the training, this snippet can be used to remove these training users.
If you execute this step, then it's worth setting summary_users_training
to blank, otherwise the message to Mattermost contains wrong information.
users_list = misp.users(pythonify=True)
for user in users_list:
if ("training-user-" in user.email) or ("training-admin-" in user.email):
print(misp.delete_user(user.id))
{'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/244', 'id': '244'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/245', 'id': '245'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/246', 'id': '246'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/247', 'id': '247'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/248', 'id': '248'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/249', 'id': '249'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/250', 'id': '250'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/251', 'id': '251'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/252', 'id': '252'} {'saved': True, 'success': True, 'name': 'User deleted.', 'message': 'User deleted.', 'url': '/admin/users/delete/253', 'id': '253'}
Closure¶
In this closure or end step we create a summary of the actions that were performed by the playbook. The summary is printed in the playbook and can also be send to a chat channel.
EN:1 Create the summary of the playbook¶
The next section creates a summary and stores the output in the variable summary
in Markdown format. It also stores an intro text in the variable intro
. These variables can later be used when sending information to Mattermost or TheHive.
# Create a new user table, we might have deleted, disabled or changed passwords
table = PrettyTable()
table.field_names = ["ID", "Username", "Organisation", "Role", "Password", "Deleted", "Disabled"]
table.align["ID"] = "r"
table.align["Username"] = "l"
table.align["Organisation"] = "l"
table.align["Role"] = "l"
table.align["Password"] = "l"
for user in add_users:
password = user.password if summary_print_password else "******"
table.add_row([user.id, user.email, "{} - {}".format(user.org_id, user.custom_organisation_name), "{} - {}".format(user.role_id, misp_roles[str(user.role_id)]), password, user.custom_delete, user.custom_disabled])
summary_users = "### Add users\n"
table.set_style(MARKDOWN)
summary_users += table.get_string()
summary = "## MISP Playbook summary\nProvision users and organisations. \n\n"
intro = summary
summary += summary_org
summary += "\n\n"
summary += summary_users
summary += "\n\n"
summary += summary_users_training
summary += "\n\n"
print("The \033[92msummary\033[90m of the playbook is available.\n")
The summary of the playbook is available.
EN:2 Send a summary to Mattermost¶
Now you can send the summary to Mattermost. You can send the summary in two ways by selecting one of the options for the variable send_to_mattermost_option
in the next cell.
- The default option where the entire summary is in the chat, or
- a short intro and the summary in a card
For this playbook we rely on a webhook in Mattermost. You can add a webhook by choosing the gear icon in Mattermost, then choose Integrations and then Incoming Webhooks. Set a channel for the webhook and lock the webhook to this channel with "Lock to this channel".
send_to_mattermost_option = "via a chat message"
#send_to_mattermost_option = "via a chat message with card"
message = False
if send_to_mattermost_option == "via a chat message":
message = {"username": mattermost_playbook_user, "text": summary}
elif send_to_mattermost_option == "via a chat message with card":
message = {"username": mattermost_playbook_user, "text": intro, "props": {"card": summary}}
if message:
r = requests.post(mattermost_hook, data=json.dumps(message))
r.raise_for_status()
if message and r.status_code == 200:
print("Summary is \033[92msent to Mattermost.\n")
else:
print("\033[91mFailed to sent summary\033[90m to Mattermost.\n")
Summary is sent to Mattermost.
EN:3 End of the playbook¶
print("\033[92m End of the playbook")
End of the playbook
External references ¶
Technical details¶
Documentation¶
This playbook requires these Python libraries to exist in the environment where the playbook is executed. You can install them with pip install <library>
.
pyfaup
chardet
PrettyTable
ipywidgets
You need to have network access to
- your MISP server (HTTP or HTTPS)
You need
- an API key with MISP
- Under Global Actions, My Profile. Add an extra authentication key.
- Add the API key (
misp_key
) and the MISP URL (misp_url
) tokeys.py
- Add the API key (
- If you use a self-signed certificate set
misp_verifycert
to False
- If you use a self-signed certificate set
Helper functions¶
Colour codes¶
The output from Python displays some text in different colours. These are the colour codes
Red = '\033[91m'
Green = '\033[92m'
Blue = '\033[94m'
Cyan = '\033[96m'
White = '\033[97m'
Yellow = '\033[93m'
Magenta = '\033[95m'
Grey = '\033[90m'
Black = '\033[90m'
Default = '\033[99m'