You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2379 lines
91 KiB
2379 lines
91 KiB
#!/usr/bin/env python
|
|
# coding=utf-8
|
|
|
|
__name__ = "trackme_rest_handler_configuration.py"
|
|
__author__ = "TrackMe Limited"
|
|
__copyright__ = "Copyright 2022-2026, TrackMe Limited, U.K."
|
|
__credits__ = "TrackMe Limited, U.K."
|
|
__license__ = "TrackMe Limited, all rights reserved"
|
|
__version__ = "0.1.0"
|
|
__maintainer__ = "TrackMe Limited, U.K."
|
|
__email__ = "support@trackme-solutions.com"
|
|
__status__ = "PRODUCTION"
|
|
|
|
# Built-in libraries
|
|
import json
|
|
import os
|
|
import sys
|
|
import re
|
|
from collections import OrderedDict
|
|
|
|
# Third-party libraries
|
|
import requests
|
|
|
|
# splunk home
|
|
splunkhome = os.environ["SPLUNK_HOME"]
|
|
|
|
# append current directory
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
# import libs
|
|
import import_declare_test
|
|
|
|
# set logging
|
|
from trackme_libs_logging import setup_logger
|
|
|
|
logger = setup_logger(
|
|
"trackme.rest.configuration", "trackme_rest_api_configuration.log"
|
|
)
|
|
# Redirect global logging to use the same handler
|
|
import logging
|
|
logging.getLogger().handlers = logger.handlers
|
|
logging.getLogger().setLevel(logger.level)
|
|
|
|
|
|
# import test handler
|
|
import trackme_rest_handler
|
|
|
|
# import trackme libs
|
|
from trackme_libs import (
|
|
trackme_getloglevel,
|
|
trackme_reqinfo,
|
|
trackme_test_remote_account,
|
|
trackme_test_remote_connectivity,
|
|
trackme_get_remote_account,
|
|
trackme_get_emails_account,
|
|
run_splunk_search,
|
|
trackme_get_report,
|
|
TrackMeRemoteConnectionError,
|
|
trackme_get_version,
|
|
)
|
|
|
|
# import trackme libs schema
|
|
from trackme_libs_schema import trackme_schema_format_version
|
|
|
|
# import trackme libs utils
|
|
from trackme_libs_utils import remove_leading_spaces
|
|
|
|
# import Splunk libs
|
|
import splunklib.client as client
|
|
|
|
|
|
class TrackMeHandlerConfigurationRead_v2(trackme_rest_handler.RESTHandler):
|
|
def __init__(self, command_line, command_arg):
|
|
super(TrackMeHandlerConfigurationRead_v2, self).__init__(
|
|
command_line, command_arg, logger
|
|
)
|
|
|
|
def get_resource_group_desc_configuration(self, request_info, **kwargs):
|
|
response = {
|
|
"resource_group_name": "configuration",
|
|
"resource_group_desc": "These endpoints provide various application-level configuration capabilities. They are used internally by the user interface and can be customized according to your needs.",
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Return request info
|
|
def get_request_info(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=get url=\"/services/trackme/v2/configuration/request_info\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint returns request information such as splunkd_uri and other useful technical details. It requires a GET call with no options.",
|
|
"resource_desc": "Return request information",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/request_info"',
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# conf
|
|
conf_file = "trackme_settings"
|
|
confs = service.confs[str(conf_file)]
|
|
|
|
# Initialize the trackme_conf dictionary
|
|
trackme_conf = {}
|
|
|
|
# TrackMe version
|
|
trackme_version = trackme_get_version(service)
|
|
|
|
# Get schema_version_required
|
|
schema_version_required = trackme_schema_format_version(trackme_version)
|
|
|
|
# Get conf
|
|
for stanza in confs:
|
|
logger.debug(f'get_trackme_conf, Processing stanza.name="{stanza.name}"')
|
|
|
|
# Create a sub-dictionary for the current stanza name if it doesn't exist
|
|
if stanza.name not in trackme_conf:
|
|
trackme_conf[stanza.name] = {}
|
|
|
|
# Store key-value pairs from the stanza content in the corresponding sub-dictionary
|
|
for stanzakey, stanzavalue in stanza.content.items():
|
|
logger.debug(
|
|
f'get_trackme_conf, Processing stanzakey="{stanzakey}", stanzavalue="{stanzavalue}"'
|
|
)
|
|
if stanzavalue:
|
|
trackme_conf[stanza.name][stanzakey] = stanzavalue
|
|
else:
|
|
trackme_conf[stanza.name][stanzakey] = ""
|
|
|
|
|
|
# set logger.level
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# gen record
|
|
record = {
|
|
"user": request_info.user,
|
|
"server_rest_uri": request_info.server_rest_uri,
|
|
"server_rest_host": request_info.server_rest_host,
|
|
"server_rest_port": request_info.server_rest_port,
|
|
"server_hostname": request_info.server_hostname,
|
|
"server_servername": request_info.server_servername,
|
|
"connection_src_ip": request_info.connection_src_ip,
|
|
"connection_listening_port": request_info.connection_listening_port,
|
|
"logging_level": trackme_conf["logging"]["loglevel"],
|
|
"trackme_version": trackme_version,
|
|
"schema_version_required": schema_version_required,
|
|
"trackme_conf": trackme_conf,
|
|
}
|
|
|
|
return {"payload": record, "status": 200}
|
|
|
|
# This endpoint verifies the level of privileges of the user currently connected
|
|
def get_trackme_check_privileges_level(self, request_info, **kwargs):
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint verifies the privilege level of the currently connected user. It requires a GET call with no options.",
|
|
"resource_desc": "Check current user's privilege level",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/trackme_check_privileges_level"',
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Define an header for requests authenticated communications with splunkd
|
|
header = {
|
|
"Authorization": "Splunk %s" % request_info.session_key,
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
# final_response
|
|
final_response = {}
|
|
|
|
# TrackMe reqinfo
|
|
reqinfo_trackme = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
trackmeconf = reqinfo_trackme["trackme_conf"]
|
|
logger.info(f'trackmeconf="{json.dumps(trackmeconf, indent=2)}"')
|
|
|
|
# init
|
|
is_admin = False
|
|
is_power = False
|
|
|
|
# check allow_admin_ops
|
|
|
|
# check admin
|
|
record_url = "%s/services/trackme/v2/vtenants/admin/add_tenant" % (
|
|
request_info.server_rest_uri
|
|
)
|
|
|
|
try:
|
|
response = requests.post(
|
|
record_url,
|
|
headers=header,
|
|
data=json.dumps({"describe": "True"}),
|
|
verify=False,
|
|
timeout=600,
|
|
)
|
|
if response.status_code == 200:
|
|
is_admin = True
|
|
else:
|
|
is_admin = False
|
|
except Exception as e:
|
|
return {
|
|
"payload": {
|
|
"response": "An exception was encountered",
|
|
"exception": str(e),
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
# check power
|
|
record_url = "%s/services/trackme/v2/ack/write/ack_manage" % (
|
|
request_info.server_rest_uri
|
|
)
|
|
|
|
try:
|
|
response = requests.post(
|
|
record_url,
|
|
headers=header,
|
|
data=json.dumps({"describe": "True"}),
|
|
verify=False,
|
|
timeout=600,
|
|
)
|
|
if response.status_code == 200:
|
|
is_power = True
|
|
else:
|
|
is_power = False
|
|
except Exception as e:
|
|
return {
|
|
"payload": {
|
|
"response": "An exception was encountered",
|
|
"exception": str(e),
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
# prepare the response
|
|
final_response = {
|
|
"username": request_info.user,
|
|
"user_level": "admin",
|
|
}
|
|
|
|
if is_admin:
|
|
final_response["user_level"] = "admin"
|
|
elif is_power:
|
|
final_response["user_level"] = "power"
|
|
else:
|
|
final_response["user_level"] = "user"
|
|
|
|
# add trackme_conf
|
|
final_response["trackme_conf"] = trackmeconf
|
|
|
|
# UI defaults configuration
|
|
ui_defaults_conf = trackmeconf.get("trackme_ui_defaults", {})
|
|
ui_default_theme = ui_defaults_conf.get("default_theme", "dark")
|
|
ui_auto_refresh = ui_defaults_conf.get("auto_refresh", "1")
|
|
ui_vtenants_card_detail_level = ui_defaults_conf.get("vtenants_card_detail_level", "0")
|
|
|
|
# Add ui_default_theme to response (system-level setting)
|
|
user_prefs_dict = {
|
|
"ui_default_theme": ui_default_theme,
|
|
"ui_auto_refresh": ui_auto_refresh,
|
|
"ui_vtenants_card_detail_level": ui_vtenants_card_detail_level,
|
|
}
|
|
|
|
# add user_prefs_dict to response
|
|
final_response["user_prefs"] = user_prefs_dict
|
|
|
|
# return
|
|
return {"payload": final_response, "status": 200}
|
|
|
|
# This endpoint verifies that the local instance meets TrackMe requirements
|
|
def get_trackme_check_dependencies(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=get url=\"/services/trackme/v2/configuration/trackme_check_dependencies\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint verifies that the local instance meets TrackMe dependencies requirements. It requires a GET call with no options.",
|
|
"resource_desc": "Check TrackMe dependencies requirements",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/trackme_check_dependencies"',
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# TrackMe reqinfo
|
|
trackmeconf = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
|
|
# proceed
|
|
try:
|
|
apps = []
|
|
for app in service.apps:
|
|
apps.append(app.name)
|
|
|
|
missing_apps = []
|
|
checked_apps = []
|
|
|
|
app_check = "Splunk_ML_Toolkit"
|
|
if not app_check in apps:
|
|
missing_apps.append(
|
|
{
|
|
"application_name": app_check,
|
|
"splunkbase_link": "https://splunkbase.splunk.com/app/2890",
|
|
}
|
|
)
|
|
else:
|
|
checked_apps.append(app_check)
|
|
|
|
# Then, within your try block where you check for apps, add the following:
|
|
app_prefix = "Splunk_SA_Scientific_Python_"
|
|
# This will create a pattern that matches any app name starting with the app_prefix
|
|
pattern = re.compile(re.escape(app_prefix) + r".*")
|
|
scientific_python_app_found = any(
|
|
pattern.match(app.name) for app in service.apps
|
|
)
|
|
|
|
if not scientific_python_app_found:
|
|
missing_apps.append(
|
|
{
|
|
"application_name": "Splunk_SA_Scientific_Python_<architecture>",
|
|
"splunkbase_link": "https://splunkbase.splunk.com/app/2882",
|
|
}
|
|
)
|
|
else:
|
|
checked_apps.append("Splunk_SA_Scientific_Python_<architecture>")
|
|
|
|
if len(missing_apps) > 0:
|
|
response = {
|
|
"action": "failure",
|
|
"response": "Applications dependencies requirements are not met",
|
|
"missing_apps": missing_apps,
|
|
}
|
|
logger.error(json.dumps(response, indent=2))
|
|
return {"payload": response, "status": 200}
|
|
|
|
else:
|
|
response = {
|
|
"action": "success",
|
|
"response": "All applications dependencies are met",
|
|
"checked_apps": checked_apps,
|
|
"trackme_conf": trackmeconf,
|
|
}
|
|
logger.debug(json.dumps(response, indent=2))
|
|
return {"payload": response, "status": 200}
|
|
|
|
except Exception as e:
|
|
response = {
|
|
"action": "failure",
|
|
"response": "An exception was encountered",
|
|
"exception": str(e),
|
|
}
|
|
logger.error(json.dumps(response, indent=2))
|
|
return {"payload": response, "status": 500}
|
|
|
|
# Retrieve tenants according to RBAC
|
|
def get_vtenants_all(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=get url=\"/services/trackme/v2/configuration/vtenants_all\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves all the tenants the user profiles allows access to, it requires a GET call with no options",
|
|
"resource_desc": "Get the list of TrackMe tenants according to RBAC policies for the user currently connected",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/vtenants_all"',
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# get current user
|
|
username = request_info.user
|
|
|
|
# get user info
|
|
users = service.users
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# Get roles for the current user
|
|
username_roles = []
|
|
for user in users:
|
|
if user.name == username:
|
|
username_roles = user.roles
|
|
logger.info(f'username="{username}", roles="{username_roles}"')
|
|
|
|
try:
|
|
# Data collection
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
collection = service.kvstore[collection_name]
|
|
|
|
records = collection.data.query()
|
|
filtered_records = []
|
|
|
|
for record in records:
|
|
logger.info(
|
|
f'tenant_id="{record["tenant_id"]}", tenant_roles_admin="{record["tenant_roles_admin"]}", tenant_roles_power="{record["tenant_roles_power"]}", tenant_roles_user="{record["tenant_roles_user"]}"'
|
|
)
|
|
|
|
# handle all other cases and use RBAC accordingly to the tenant
|
|
|
|
# log
|
|
logger.info(
|
|
f'checking permissions of user="{username}" with roles="{username_roles}" for tenant_id="{record["tenant_id"]}"'
|
|
)
|
|
|
|
# default, no access
|
|
user_has_access = False
|
|
|
|
# per tenant admin and user roles
|
|
if isinstance(record["tenant_roles_admin"], list):
|
|
a = set(record["tenant_roles_admin"])
|
|
else:
|
|
a = set(record["tenant_roles_admin"].split(","))
|
|
|
|
if isinstance(record["tenant_roles_power"], list):
|
|
b = set(record["tenant_roles_power"])
|
|
else:
|
|
b = set(record["tenant_roles_power"].split(","))
|
|
|
|
if isinstance(record["tenant_roles_user"], list):
|
|
c = set(record["tenant_roles_user"])
|
|
else:
|
|
c = set(record["tenant_roles_user"].split(","))
|
|
|
|
# any member of these has access to any tenant
|
|
d = ["admin", "trackme_admin", "sc_admin"]
|
|
|
|
# loop
|
|
for username_role in username_roles:
|
|
logger.debug(
|
|
f'check if username_role="{username_role}" is in {a} or {b} or {c}'
|
|
)
|
|
if username_role in a or username_role in b or username_role in c:
|
|
logger.debug(
|
|
f'user="{username}" has access to tenant_id="{record["tenant_id"]}"'
|
|
)
|
|
user_has_access = True
|
|
break
|
|
elif username_role in d:
|
|
logger.debug(
|
|
f'user="{username}" has access to tenant_id="{record["tenant_id"]}"'
|
|
)
|
|
user_has_access = True
|
|
break
|
|
|
|
if user_has_access:
|
|
filtered_records.append(record)
|
|
|
|
return {"payload": filtered_records, "status": 200}
|
|
|
|
except Exception as e:
|
|
logger.error(f'Warn: exception encountered="{str(e)}"')
|
|
return {"payload": f'Warn: exception encountered="{str(e)}"'}
|
|
|
|
# Retrieve tenants RBAC configuration
|
|
def post_show_vtenants_rbac(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/show_vtenants_rbac\" body=\"{'tenant_id': 'mytenant'}\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
if not describe:
|
|
tenant_id = resp_dict["tenant_id"]
|
|
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint shows the Virtual Tenants and their RBAC current configuration, it requires a POST call with the following options",
|
|
"resource_desc": "Shows Virtual Tenants RBAC current configuration",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/show_vtenants_rbac\" body=\"{'tenant_id': 'mytenant'}\"",
|
|
"options": [
|
|
{
|
|
"tenant_id": "tenant identifier, use a wildcard to get RBAC configuration for all existing tenants",
|
|
}
|
|
],
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
try:
|
|
# Data collection
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
collection = service.kvstore[collection_name]
|
|
|
|
# Define the KV query
|
|
if tenant_id != "*":
|
|
query_string = {
|
|
"tenant_id": tenant_id,
|
|
}
|
|
else:
|
|
query_string = {}
|
|
|
|
records = collection.data.query(query=json.dumps(query_string))
|
|
filtered_records = []
|
|
|
|
for record in records:
|
|
logger.info(
|
|
f'tenant_id="{record["tenant_id"]}", tenant_owner="{record["tenant_owner"]}", tenant_roles_admin="{record["tenant_roles_admin"]}", tenant_roles_user="{record["tenant_roles_user"]}"'
|
|
)
|
|
|
|
# get, turn into a list and sort
|
|
tenant_roles_admin_orig = record["tenant_roles_admin"].split(",")
|
|
tenant_roles_admin = sorted(tenant_roles_admin_orig)
|
|
|
|
# get, turn into a list and sort
|
|
tenant_roles_power_orig = record["tenant_roles_power"].split(",")
|
|
tenant_roles_power = sorted(tenant_roles_power_orig)
|
|
|
|
# get, turn into a list and sort
|
|
tenant_roles_user_orig = record["tenant_roles_user"].split(",")
|
|
tenant_roles_user = sorted(tenant_roles_user_orig)
|
|
|
|
filtered_records.append(
|
|
{
|
|
"tenant_id": record["tenant_id"],
|
|
"tenant_owner": record["tenant_owner"],
|
|
"tenant_roles_admin": tenant_roles_admin,
|
|
"tenant_roles_power": tenant_roles_power,
|
|
"tenant_roles_user": tenant_roles_user,
|
|
}
|
|
)
|
|
|
|
return {"payload": filtered_records, "status": 200}
|
|
|
|
except Exception as e:
|
|
logger.error(f'Warn: exception encountered="{str(e)}"')
|
|
return {"payload": f'Warn: exception encountered="{str(e)}"'}
|
|
|
|
# List all accounts
|
|
def get_list_accounts(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/list_accounts\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint lists all available accounts. It requires a GET call with no options.",
|
|
"resource_desc": "Lists all configured accounts",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/list_accounts"',
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# get all acounts
|
|
accounts = ["local"]
|
|
try:
|
|
conf_file = "trackme_account"
|
|
confs = service.confs[str(conf_file)]
|
|
for stanza in confs:
|
|
# get all accounts
|
|
for name in stanza.name:
|
|
accounts.append(stanza.name)
|
|
break
|
|
except Exception as e:
|
|
accounts = ["local"]
|
|
|
|
return {"payload": {"accounts": accounts}, "status": 200}
|
|
|
|
# List local users with a least privileges approach
|
|
def get_list_local_users(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=get url=\"/services/trackme/v2/configuration/list_local_users\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves local Splunk users with a least privileges approach, it requires a GET call with no options",
|
|
"resource_desc": "List local Splunk users",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/list_local_users"',
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# get user info
|
|
users = service.users
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# TrackMe reqinfo
|
|
trackmeconf = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
trackme_owner_default = trackmeconf["trackme_conf"]["trackme_general"][
|
|
"trackme_owner_default"
|
|
]
|
|
|
|
# users_lister
|
|
users_list = []
|
|
users_list.append(trackme_owner_default)
|
|
for user in users:
|
|
if user.name not in users_list:
|
|
users_list.append(user.name)
|
|
|
|
return {"payload": {"users": users_list}, "status": 200}
|
|
|
|
# Test remote account connectivity
|
|
def post_test_remote_account(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/test_remote_account\" body=\"{'account': 'lab'}\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
account = resp_dict["account"]
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint performs a connectivity check for a Splunk remote account. It requires a POST call with the following options:",
|
|
"resource_desc": "Run connectivity checks for a Splunk remote account. This validates the configuration, network connectivity and authentication.",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/test_remote_account\" body=\"{'account': 'lab'}\"",
|
|
"options": [
|
|
{
|
|
"account": "The account configuration identifier",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# get all acounts
|
|
try:
|
|
accounts = []
|
|
conf_file = "trackme_account"
|
|
confs = service.confs[str(conf_file)]
|
|
for stanza in confs:
|
|
# get all accounts
|
|
for name in stanza.name:
|
|
accounts.append(stanza.name)
|
|
break
|
|
|
|
except Exception as e:
|
|
error_msg = "There are no remote Splunk account configured yet"
|
|
return {
|
|
"payload": {
|
|
"status": "failure",
|
|
"message": error_msg,
|
|
"account": account,
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
else:
|
|
try:
|
|
response = trackme_test_remote_account(request_info, account)
|
|
return {"payload": response, "status": 200}
|
|
|
|
except TrackMeRemoteConnectionError as e:
|
|
return {"payload": e.error_info, "status": 500}
|
|
except Exception as e:
|
|
return {"payload": str(e), "status": 500}
|
|
|
|
# Test remote connectivity prior to the creation of a remote account
|
|
def post_test_remote_connectivity(self, request_info, **kwargs):
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
target_endpoints = resp_dict["target_endpoints"]
|
|
bearer_token = resp_dict["bearer_token"]
|
|
app_namespace = resp_dict.get("app_namespace", "search")
|
|
try:
|
|
timeout_connect_check = int(
|
|
resp_dict.get("timeout_connect_check", 15)
|
|
)
|
|
except Exception as e:
|
|
return {
|
|
"payload": {
|
|
"action": "failure",
|
|
"response": "timeout_connect_check should be an integer",
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
try:
|
|
timeout_search_check = int(
|
|
resp_dict.get("timeout_search_check", 300)
|
|
)
|
|
except Exception as e:
|
|
return {
|
|
"payload": {
|
|
"action": "failure",
|
|
"response": "timeout_search_check should be an integer",
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint performs a connectivity check for Splunk remote search capabilities prior to the formal creation of a remote account. It requires a POST call with the following options:",
|
|
"resource_desc": "Run connectivity checks for remote search capabilities prior to the creation of an account. This validates the configuration, network connectivity and authentication.",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/test_remote_connectivity\" body=\"{'target_endpoints': 'https://myendpoint1:8089,https://myendpoint2:8089,https://myendpoint3:8089', 'bearer_token': 'xxx', 'app_namespace': 'search'}\"",
|
|
"options": [
|
|
{
|
|
"target_endpoints": "One or more splunkd API endpoints in the form: https://<url>:<port>",
|
|
"app_namespace": "The remote application namespace. If not provided, defaults to search",
|
|
"bearer_token": "The Splunk bearer token to be used",
|
|
"timeout_connect_check": "Optional: The timeout in seconds for the connect health check. Defaults to 15 seconds (integer)",
|
|
"timeout_search_check": "Optional: The timeout in seconds for the search connection. Defaults to 300 seconds (integer)",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
try:
|
|
connection_info = {
|
|
"target_endpoints": target_endpoints,
|
|
"app_namespace": app_namespace,
|
|
"bearer_token": bearer_token,
|
|
"timeout_connect_check": timeout_connect_check,
|
|
"timeout_search_check": timeout_search_check,
|
|
}
|
|
response = trackme_test_remote_connectivity(connection_info)
|
|
return {"payload": response, "status": 200}
|
|
|
|
# note: the exception is returned as a JSON object
|
|
except Exception as e:
|
|
return {"payload": str(e), "status": 500}
|
|
|
|
# Get remote account credentials with a least privileges approach
|
|
def post_get_remote_account(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/get_remote_account\" body=\"{'account': 'lab'}\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
account = resp_dict["account"]
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint provides connection details for a Splunk remote account to be used in a programmatic manner with a least privileges approach, it requires a POST call with the following options:",
|
|
"resource_desc": "Return a remote account credential details for programmatic access with a least privileges approach",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/get_remote_account\" body=\"{'account': 'lab'}\"",
|
|
"options": [
|
|
{
|
|
"account": "The account configuration identifier",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# get all acounts
|
|
try:
|
|
accounts = []
|
|
conf_file = "trackme_account"
|
|
confs = service.confs[str(conf_file)]
|
|
for stanza in confs:
|
|
# get all accounts
|
|
for name in stanza.name:
|
|
accounts.append(stanza.name)
|
|
break
|
|
|
|
except Exception as e:
|
|
error_msg = "There are no remote Splunk account configured yet"
|
|
return {
|
|
"payload": {
|
|
"status": "failure",
|
|
"message": error_msg,
|
|
"account": account,
|
|
},
|
|
"status": 500,
|
|
}
|
|
|
|
else:
|
|
try:
|
|
response = trackme_get_remote_account(request_info, account)
|
|
return {"payload": response, "status": 200}
|
|
|
|
# note: the exception is returned as a JSON object
|
|
except Exception as e:
|
|
return {"payload": str(e), "status": 500}
|
|
|
|
# Get components
|
|
def post_components(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/components\" body=\"{'tenant_id': 'mytenant'}\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
tenant_id = resp_dict["tenant_id"]
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the components status for a specific tenant id, it requires a POST call with the following options:",
|
|
"resource_desc": "Get the status of the TrackMe components for a given tenant",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/components\" body=\"{'tenant_id': 'mytenant'}\"",
|
|
"options": [
|
|
{
|
|
"tenant_id": "The tenant identifier",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# TrackMe reqinfo
|
|
reqinfo_trackme = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
trackmeconf = reqinfo_trackme["trackme_conf"]
|
|
|
|
# conf
|
|
conf_file = "trackme_settings"
|
|
confs = service.confs[str(conf_file)]
|
|
|
|
# get vtenant account
|
|
conf_file = "trackme_vtenants"
|
|
|
|
# if there are no account, raise an exception, otherwise what we would do here?
|
|
try:
|
|
confs = service.confs[str(conf_file)]
|
|
except Exception as e:
|
|
error_msg = "there are no tenants configured yet"
|
|
raise Exception(error_msg)
|
|
|
|
# init
|
|
trackme_vtenant_conf = {}
|
|
trackme_vtenant_conf[tenant_id] = {}
|
|
|
|
# get account
|
|
for stanza in confs:
|
|
if stanza.name == str(tenant_id):
|
|
# Store key-value pairs from the stanza content in the corresponding sub-dictionary
|
|
for stanzakey, stanzavalue in stanza.content.items():
|
|
logger.debug(
|
|
f'get virtual tenant account, Processing stanzakey="{stanzakey}", stanzavalue="{stanzavalue}"'
|
|
)
|
|
trackme_vtenant_conf[stanza.name][stanzakey] = stanzavalue
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# TrackMe version
|
|
trackme_version = trackme_get_version(service)
|
|
|
|
# Data collection
|
|
collection_name = "kv_trackme_virtual_tenants"
|
|
collection = service.kvstore[collection_name]
|
|
|
|
# Define the KV query search string
|
|
query_string = {
|
|
"tenant_id": tenant_id,
|
|
}
|
|
|
|
# Get the record
|
|
try:
|
|
kvrecord = collection.data.query(query=json.dumps(query_string))[0]
|
|
key = kvrecord.get("_key")
|
|
|
|
except Exception as e:
|
|
key = None
|
|
|
|
# proceed
|
|
if key:
|
|
# debug
|
|
logger.debug(
|
|
f"tenant_id={tenant_id} record={json.dumps(kvrecord, indent=1)}"
|
|
)
|
|
|
|
# schema version: detect the current schema_version and if the upgrade is in progress
|
|
schema_version_raw = kvrecord.get("schema_version")
|
|
schema_version_required = trackme_schema_format_version(trackme_version)
|
|
|
|
# If schema_version_required is 0 (version retrieval failed), treat as graceful degradation
|
|
# and don't block operations - consistent with other handlers
|
|
if schema_version_required == 0:
|
|
schema_version = int(schema_version_raw) if schema_version_raw is not None else None
|
|
schema_version_upgrade_in_progress = False
|
|
elif schema_version_raw is None:
|
|
# If schema_version is missing (e.g., tenant was created when version retrieval failed),
|
|
# treat it as needing an upgrade
|
|
schema_version = None
|
|
schema_version_upgrade_in_progress = True
|
|
else:
|
|
schema_version = int(schema_version_raw)
|
|
schema_version_upgrade_in_progress = False
|
|
if not schema_version or schema_version < schema_version_required:
|
|
schema_version_upgrade_in_progress = True
|
|
|
|
# retrieve the components configuration
|
|
try:
|
|
component_splk_dhm = int(kvrecord.get("tenant_dhm_enabled"))
|
|
except Exception as e:
|
|
component_splk_dhm = 0
|
|
|
|
try:
|
|
component_splk_dsm = int(kvrecord.get("tenant_dsm_enabled"))
|
|
except Exception as e:
|
|
component_splk_dsm = 0
|
|
|
|
try:
|
|
component_splk_mhm = int(kvrecord.get("tenant_mhm_enabled"))
|
|
except Exception as e:
|
|
component_splk_mhm = 0
|
|
|
|
try:
|
|
component_splk_flx = int(kvrecord.get("tenant_flx_enabled"))
|
|
except Exception as e:
|
|
component_splk_flx = 0
|
|
|
|
try:
|
|
component_splk_fqm = int(kvrecord.get("tenant_fqm_enabled"))
|
|
except Exception as e:
|
|
component_splk_fqm = 0
|
|
|
|
try:
|
|
component_splk_wlk = int(kvrecord.get("tenant_wlk_enabled"))
|
|
except Exception as e:
|
|
component_splk_wlk = 0
|
|
|
|
try:
|
|
ui_default_timerange = str(
|
|
trackme_vtenant_conf[tenant_id]["ui_default_timerange"]
|
|
)
|
|
except Exception as e:
|
|
ui_default_timerange = "24h"
|
|
|
|
try:
|
|
ui_min_object_width = int(
|
|
trackme_vtenant_conf[tenant_id]["ui_min_object_width"]
|
|
)
|
|
except Exception as e:
|
|
ui_min_object_width = 300
|
|
|
|
try:
|
|
ui_expand_metrics = int(
|
|
trackme_vtenant_conf[tenant_id]["ui_expand_metrics"]
|
|
)
|
|
except Exception as e:
|
|
ui_expand_metrics = 0
|
|
|
|
try:
|
|
ui_home_tabs_order = str(
|
|
trackme_vtenant_conf[tenant_id]["ui_home_tabs_order"]
|
|
)
|
|
except Exception as e:
|
|
ui_home_tabs_order = "dsm,flx,dhm,mhm,wlk,fqm,flip,audit,alerts"
|
|
|
|
try:
|
|
sampling = int(trackme_vtenant_conf[tenant_id]["sampling"])
|
|
except Exception as e:
|
|
sampling = 1
|
|
|
|
try:
|
|
mloutliers = int(trackme_vtenant_conf[tenant_id]["mloutliers"])
|
|
except Exception as e:
|
|
mloutliers = 1
|
|
|
|
try:
|
|
mloutliers_allowlist = str(
|
|
trackme_vtenant_conf[tenant_id]["mloutliers_allowlist"]
|
|
)
|
|
except Exception as e:
|
|
mloutliers_allowlist = "dsm,dhm,flx,wlk,fqm"
|
|
|
|
try:
|
|
adaptive_delay = int(trackme_vtenant_conf[tenant_id]["adaptive_delay"])
|
|
except Exception as e:
|
|
adaptive_delay = 1
|
|
|
|
try:
|
|
indexed_constraint = str(
|
|
trackme_vtenant_conf[tenant_id]["indexed_constraint"]
|
|
)
|
|
except Exception as e:
|
|
indexed_constraint = ""
|
|
|
|
try:
|
|
splk_feeds_delayed_inspector_24hours_range_min_sec = int(
|
|
trackme_vtenant_conf[tenant_id][
|
|
"splk_feeds_delayed_inspector_24hours_range_min_sec"
|
|
]
|
|
)
|
|
except Exception as e:
|
|
splk_feeds_delayed_inspector_24hours_range_min_sec = int(
|
|
trackmeconf["splk_general"][
|
|
"splk_general_feeds_delayed_inspector_24hours_range_min_sec"
|
|
]
|
|
)
|
|
|
|
try:
|
|
splk_feeds_delayed_inspector_7days_range_min_sec = int(
|
|
trackme_vtenant_conf[tenant_id][
|
|
"splk_feeds_delayed_inspector_7days_range_min_sec"
|
|
]
|
|
)
|
|
except Exception as e:
|
|
splk_feeds_delayed_inspector_7days_range_min_sec = int(
|
|
trackmeconf["splk_general"][
|
|
"splk_general_feeds_delayed_inspector_7days_range_min_sec"
|
|
]
|
|
)
|
|
|
|
try:
|
|
splk_feeds_delayed_inspector_until_disabled_range_min_sec = int(
|
|
trackme_vtenant_conf[tenant_id][
|
|
"splk_feeds_delayed_inspector_until_disabled_range_min_sec"
|
|
]
|
|
)
|
|
except Exception as e:
|
|
splk_feeds_delayed_inspector_until_disabled_range_min_sec = int(
|
|
trackmeconf["splk_general"][
|
|
"splk_general_feeds_delayed_inspector_until_disabled_range_min_sec"
|
|
]
|
|
)
|
|
|
|
try:
|
|
splk_feeds_auto_disablement_period = str(
|
|
trackme_vtenant_conf[tenant_id][
|
|
"splk_feeds_auto_disablement_period"
|
|
]
|
|
)
|
|
except Exception as e:
|
|
splk_feeds_auto_disablement_period = trackmeconf["splk_general"][
|
|
"splk_general_feeds_auto_disablement_period"
|
|
]
|
|
|
|
try:
|
|
cmdb_lookup = int(trackme_vtenant_conf[tenant_id]["cmdb_lookup"])
|
|
except Exception as e:
|
|
cmdb_lookup = 1
|
|
|
|
try:
|
|
data_sampling_obfuscation = int(
|
|
trackme_vtenant_conf[tenant_id]["data_sampling_obfuscation"]
|
|
)
|
|
except Exception as e:
|
|
data_sampling_obfuscation = 0
|
|
|
|
try:
|
|
pagination_mode = str(
|
|
trackme_vtenant_conf[tenant_id]["pagination_mode"]
|
|
)
|
|
except Exception as e:
|
|
pagination_mode = trackmeconf["trackme_general"]["pagination_mode"]
|
|
|
|
try:
|
|
pagination_size = int(
|
|
trackme_vtenant_conf[tenant_id]["pagination_size"]
|
|
)
|
|
except Exception as e:
|
|
pagination_size = int(trackmeconf["trackme_general"]["pagination_size"])
|
|
|
|
try:
|
|
splk_dsm_tabulator_groupby = trackme_vtenant_conf[tenant_id][
|
|
"splk_dsm_tabulator_groupby"
|
|
]
|
|
except Exception as e:
|
|
splk_dsm_tabulator_groupby = "data_index"
|
|
|
|
try:
|
|
splk_dhm_tabulator_groupby = trackme_vtenant_conf[tenant_id][
|
|
"splk_dhm_tabulator_groupby"
|
|
]
|
|
except Exception as e:
|
|
splk_dhm_tabulator_groupby = "tenant_id"
|
|
|
|
try:
|
|
splk_mhm_tabulator_groupby = trackme_vtenant_conf[tenant_id][
|
|
"splk_mhm_tabulator_groupby"
|
|
]
|
|
except Exception as e:
|
|
splk_mhm_tabulator_groupby = "tenant_id"
|
|
|
|
try:
|
|
splk_flx_tabulator_groupby = trackme_vtenant_conf[tenant_id][
|
|
"splk_flx_tabulator_groupby"
|
|
]
|
|
except Exception as e:
|
|
splk_flx_tabulator_groupby = "group"
|
|
|
|
try:
|
|
splk_fqm_tabulator_groupby = trackme_vtenant_conf[tenant_id][
|
|
"splk_fqm_tabulator_groupby"
|
|
]
|
|
except Exception as e:
|
|
splk_fqm_tabulator_groupby = "group"
|
|
|
|
try:
|
|
splk_wlk_tabulator_groupby = trackme_vtenant_conf[tenant_id][
|
|
"splk_wlk_tabulator_groupby"
|
|
]
|
|
except Exception as e:
|
|
splk_wlk_tabulator_groupby = "overgroup"
|
|
|
|
try:
|
|
default_disruption_min_time_sec = int(
|
|
trackme_vtenant_conf[tenant_id]["default_disruption_min_time_sec"]
|
|
)
|
|
except Exception as e:
|
|
default_disruption_min_time_sec = 0
|
|
|
|
component_owner = str(kvrecord.get("tenant_owner"))
|
|
|
|
#
|
|
# mloutliers:
|
|
# - loop troough each component in mloutliers_allowlist,
|
|
# for each define a new key as mloutliers_<component> which gets 0 if mloutliers is disabled, 0 if enabled and not in the list, 1 if enabled and in the list
|
|
|
|
# Define the components
|
|
outliers_components = ["dsm", "dhm", "flx", "wlk", "fqm"]
|
|
|
|
# Convert the allowlist to a set for faster lookups
|
|
mloutliers_set = set(mloutliers_allowlist.split(","))
|
|
|
|
# Create a dictionary dynamically
|
|
mloutliers_dict = {
|
|
f"mloutliers_{comp}": (
|
|
1 if comp in mloutliers_set and mloutliers == 1 else 0
|
|
)
|
|
for comp in outliers_components
|
|
}
|
|
|
|
# If you need separate variables, you can unpack the dictionary
|
|
mloutliers_dsm = mloutliers_dict["mloutliers_dsm"]
|
|
mloutliers_dhm = mloutliers_dict["mloutliers_dhm"]
|
|
mloutliers_flx = mloutliers_dict["mloutliers_flx"]
|
|
mloutliers_fqm = mloutliers_dict["mloutliers_fqm"]
|
|
mloutliers_wlk = mloutliers_dict["mloutliers_wlk"]
|
|
|
|
response = {
|
|
"schema_version": str(schema_version),
|
|
"schema_version_upgrade_in_progress": int(
|
|
schema_version_upgrade_in_progress
|
|
),
|
|
"component_splk_dsm": int(component_splk_dsm),
|
|
"component_splk_dhm": int(component_splk_dhm),
|
|
"component_splk_mhm": int(component_splk_mhm),
|
|
"component_splk_flx": int(component_splk_flx),
|
|
"component_splk_fqm": int(component_splk_fqm),
|
|
"component_splk_wlk": int(component_splk_wlk),
|
|
"component_owner": str(component_owner),
|
|
"ui_default_timerange": str(ui_default_timerange),
|
|
"ui_min_object_width": int(ui_min_object_width),
|
|
"ui_expand_metrics": int(ui_expand_metrics),
|
|
"ui_home_tabs_order": str(ui_home_tabs_order),
|
|
"sampling": int(sampling),
|
|
"mloutliers": int(mloutliers),
|
|
"mloutliers_allowlist": str(mloutliers_allowlist),
|
|
"mloutliers_dsm": int(mloutliers_dsm),
|
|
"mloutliers_dhm": int(mloutliers_dhm),
|
|
"mloutliers_flx": int(mloutliers_flx),
|
|
"mloutliers_fqm": int(mloutliers_fqm),
|
|
"mloutliers_wlk": int(mloutliers_wlk),
|
|
"adaptive_delay": int(adaptive_delay),
|
|
"cmdb_lookup": int(cmdb_lookup),
|
|
"data_sampling_obfuscation": int(data_sampling_obfuscation),
|
|
"indexed_constraint": str(indexed_constraint),
|
|
"splk_feeds_delayed_inspector_24hours_range_min_sec": int(
|
|
splk_feeds_delayed_inspector_24hours_range_min_sec
|
|
),
|
|
"splk_feeds_delayed_inspector_7days_range_min_sec": int(
|
|
splk_feeds_delayed_inspector_7days_range_min_sec
|
|
),
|
|
"splk_feeds_delayed_inspector_until_disabled_range_min_sec": int(
|
|
splk_feeds_delayed_inspector_until_disabled_range_min_sec
|
|
),
|
|
"splk_feeds_auto_disablement_period": str(
|
|
splk_feeds_auto_disablement_period
|
|
),
|
|
"pagination_mode": str(pagination_mode),
|
|
"pagination_size": int(pagination_size),
|
|
"splk_dsm_tabulator_groupby": str(splk_dsm_tabulator_groupby),
|
|
"splk_dhm_tabulator_groupby": str(splk_dhm_tabulator_groupby),
|
|
"splk_mhm_tabulator_groupby": str(splk_mhm_tabulator_groupby),
|
|
"splk_flx_tabulator_groupby": str(splk_flx_tabulator_groupby),
|
|
"splk_fqm_tabulator_groupby": str(splk_fqm_tabulator_groupby),
|
|
"splk_wlk_tabulator_groupby": str(splk_wlk_tabulator_groupby),
|
|
"default_disruption_min_time_sec": int(default_disruption_min_time_sec),
|
|
}
|
|
|
|
logger.debug(
|
|
f"tenant_id={tenant_id} components={json.dumps(response, indent=1)}"
|
|
)
|
|
|
|
# add trackme_conf
|
|
response["trackme_conf"] = trackmeconf
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
else:
|
|
logger.debug(f"could not find a record for tenant={tenant_id}")
|
|
return {"payload": "Tenant was not found", "status": 404}
|
|
|
|
# Shows knowledge objects per tenant
|
|
def post_get_tenant_knowledge_objects(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/get_tenant_knowledge_objects\" body=\"{'tenant_id':'mytenant'}\"
|
|
"""
|
|
|
|
describe = False
|
|
tenant_id = None
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
if not describe:
|
|
tenant_id = resp_dict["tenant_id"]
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the tenant knowledge objects, it requires a POST call with the following options:",
|
|
"resource_desc": "Get all knowledge objects for a given TrackMe tenant",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/get_tenant_knowledge_objects\" body=\"{'tenant_id':'mytenant'}\"",
|
|
"options": [
|
|
{
|
|
"tenant_id": "tenant identifier",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# header
|
|
header = {
|
|
"Authorization": "Splunk %s" % request_info.session_key,
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# Define the SPL query
|
|
kwargs_search = {
|
|
"app": "trackme",
|
|
"earliest_time": "-5m",
|
|
"latest_time": "now",
|
|
"output_mode": "json",
|
|
"count": 0,
|
|
}
|
|
searchquery = f"| `get_tenants_reports({tenant_id})`"
|
|
|
|
# specific to alerts
|
|
alerts_static_fields = [
|
|
"alert_type",
|
|
"alert.severity",
|
|
"alert.suppress",
|
|
"alert.suppress.fields",
|
|
"alert.suppress.period",
|
|
"alert.track",
|
|
"alert_comparator",
|
|
"alert_threshold",
|
|
"alert.digest_mode",
|
|
]
|
|
|
|
query_results = []
|
|
try:
|
|
# spawn the search and get the results
|
|
reader = run_splunk_search(
|
|
service,
|
|
searchquery,
|
|
kwargs_search,
|
|
24,
|
|
5,
|
|
)
|
|
|
|
with requests.Session() as session:
|
|
session.headers.update(header)
|
|
|
|
for item in reader:
|
|
if isinstance(item, dict):
|
|
|
|
# extract the values
|
|
tenant_id_value = item.get("tenant_id")
|
|
component_value = item.get("component")
|
|
title_value = item.get("title")
|
|
type_value = item.get("type")
|
|
properties_value = {}
|
|
|
|
# init object_dict
|
|
object_dict = {
|
|
"tenant_id": tenant_id_value,
|
|
"component": component_value,
|
|
"type": type_value,
|
|
"title": title_value,
|
|
"properties": properties_value,
|
|
}
|
|
|
|
if type_value in ("savedsearches", "alerts"):
|
|
|
|
# get the object
|
|
savedsearch_object = service.saved_searches[
|
|
item.get("title")
|
|
]
|
|
|
|
acl_link = savedsearch_object.links["alternate"]
|
|
acl_url = f"{request_info.server_rest_uri}/{acl_link}/acl/list?output_mode=json"
|
|
|
|
try:
|
|
acl_response = session.get(acl_url, verify=False)
|
|
acl_properties = json.loads(acl_response.text).get(
|
|
"entry"
|
|
)[0]["acl"]
|
|
|
|
# get perms['read'] as perms_read and turn from list to csv
|
|
perms_read = ",".join(acl_properties["perms"]["read"])
|
|
# get perms['write'] as perms_write and turn from list to csv
|
|
perms_write = ",".join(acl_properties["perms"]["write"])
|
|
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": acl_properties.get("owner"),
|
|
"eai:acl.perms.read": perms_read,
|
|
"eai:acl.perms.write": perms_write,
|
|
"eai:acl.sharing": acl_properties.get("sharing"),
|
|
}
|
|
|
|
# check if we have a value for dispatch.sample_ratio and if it differs from 1, if so add it to the properties
|
|
try:
|
|
if savedsearch_object.content.get("dispatch.sample_ratio") != "1":
|
|
object_dict["properties"]["dispatch.sample_ratio"] = savedsearch_object.content.get("dispatch.sample_ratio")
|
|
except Exception as e:
|
|
pass
|
|
|
|
except Exception as e:
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": "nobody",
|
|
"eai:acl.perms.read": "trackme_user,trackmer_power",
|
|
"eai:acl.perms.write": "trackme_user,trackme_power,trackmer_admin",
|
|
"eai:acl.sharing": "app",
|
|
}
|
|
logger.error(
|
|
f'failed to retrieve the ACL properties for object="{title_value}" with exception="{str(e)}"'
|
|
)
|
|
|
|
# get the search definition
|
|
definition = savedsearch_object.content["search"]
|
|
object_dict["definition"] = definition
|
|
|
|
# get description as description
|
|
description = savedsearch_object.content.get("description")
|
|
object_dict["properties"]["description"] = description
|
|
|
|
# get schedule_window as schedule_window
|
|
schedule_window = savedsearch_object.content.get(
|
|
"schedule_window"
|
|
)
|
|
object_dict["properties"][
|
|
"schedule_window"
|
|
] = schedule_window
|
|
|
|
# get is_scheduled as is_scheduled
|
|
is_scheduled = savedsearch_object.content.get(
|
|
"is_scheduled"
|
|
)
|
|
object_dict["properties"]["is_scheduled"] = int(
|
|
is_scheduled
|
|
)
|
|
|
|
# get cron_schedule as cron_schedule, only if it's not None
|
|
cron_schedule = savedsearch_object.content.get(
|
|
"cron_schedule"
|
|
)
|
|
if cron_schedule and cron_schedule not in (None, "None", "null"):
|
|
object_dict["properties"]["cron_schedule"] = cron_schedule
|
|
|
|
# get dispath.earliest_time as earliest_time
|
|
earliest_time = savedsearch_object.content.get(
|
|
"dispatch.earliest_time"
|
|
)
|
|
object_dict["properties"]["earliest_time"] = earliest_time
|
|
|
|
# get dispath.latest_time as latest_time
|
|
latest_time = savedsearch_object.content.get(
|
|
"dispatch.latest_time"
|
|
)
|
|
object_dict["properties"]["latest_time"] = latest_time
|
|
|
|
# only for alerts
|
|
if type_value == "alerts":
|
|
|
|
# store in alert_properties
|
|
alert_properties = {}
|
|
|
|
# Process the predefined fields
|
|
for field in alerts_static_fields:
|
|
alert_properties[field] = (
|
|
savedsearch_object.content.get(field)
|
|
)
|
|
|
|
# other use cases
|
|
for (
|
|
key,
|
|
value,
|
|
) in savedsearch_object.content.items():
|
|
|
|
# support trackme actions
|
|
if (
|
|
(key.startswith("action.trackme_"))
|
|
and value is not None
|
|
and ".param." in key
|
|
):
|
|
alert_properties[key] = value
|
|
|
|
# support trackme actions enablement
|
|
elif key in (
|
|
"action.trackme_auto_ack",
|
|
"action.trackme_notable",
|
|
"action.trackme_smart_status",
|
|
"action.trackme_stateful_alert",
|
|
):
|
|
alert_properties[key] = value
|
|
|
|
# support email actions
|
|
if (
|
|
key.startswith("action.email")
|
|
and value is not None
|
|
):
|
|
alert_properties[key] = value
|
|
|
|
# add to object_dict
|
|
object_dict["alert_properties"] = alert_properties
|
|
|
|
elif type_value == "macros":
|
|
|
|
# get the object
|
|
macro_object = service.confs["macros"][item.get("title")]
|
|
|
|
acl_link = macro_object.links["alternate"]
|
|
acl_url = f"{request_info.server_rest_uri}/{acl_link}/acl/list?output_mode=json"
|
|
|
|
try:
|
|
acl_response = session.get(acl_url, verify=False)
|
|
acl_properties = json.loads(acl_response.text).get(
|
|
"entry"
|
|
)[0]["acl"]
|
|
|
|
# get perms['read'] as perms_read and turn from list to csv
|
|
perms_read = ",".join(acl_properties["perms"]["read"])
|
|
# get perms['write'] as perms_write and turn from list to csv
|
|
perms_write = ",".join(acl_properties["perms"]["write"])
|
|
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": acl_properties.get("owner"),
|
|
"eai:acl.perms.read": perms_read,
|
|
"eai:acl.perms.write": perms_write,
|
|
"eai:acl.sharing": acl_properties.get("sharing"),
|
|
}
|
|
|
|
except Exception as e:
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": "nobody",
|
|
"eai:acl.perms.read": "trackme_user,trackmer_power",
|
|
"eai:acl.perms.write": "trackme_user,trackme_power,trackmer_admin",
|
|
"eai:acl.sharing": "app",
|
|
}
|
|
logger.error(
|
|
f'failed to retrieve the ACL properties for object="{title_value}" with exception="{str(e)}"'
|
|
)
|
|
|
|
definition = macro_object.content["definition"]
|
|
object_dict["definition"] = definition
|
|
|
|
elif type_value == "lookup_definitions":
|
|
|
|
# get the object
|
|
lookup_object = service.confs["transforms"][
|
|
item.get("title")
|
|
]
|
|
|
|
acl_link = lookup_object.links["alternate"]
|
|
acl_url = f"{request_info.server_rest_uri}/{acl_link}/acl/list?output_mode=json"
|
|
|
|
try:
|
|
acl_response = session.get(acl_url, verify=False)
|
|
acl_properties = json.loads(acl_response.text).get(
|
|
"entry"
|
|
)[0]["acl"]
|
|
|
|
# get perms['read'] as perms_read and turn from list to csv
|
|
perms_read = ",".join(acl_properties["perms"]["read"])
|
|
# get perms['write'] as perms_write and turn from list to csv
|
|
perms_write = ",".join(acl_properties["perms"]["write"])
|
|
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": acl_properties.get("owner"),
|
|
"eai:acl.perms.read": perms_read,
|
|
"eai:acl.perms.write": perms_write,
|
|
"eai:acl.sharing": acl_properties.get("sharing"),
|
|
}
|
|
|
|
except Exception as e:
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": "nobody",
|
|
"eai:acl.perms.read": "trackme_user,trackmer_power",
|
|
"eai:acl.perms.write": "trackme_user,trackme_power,trackmer_admin",
|
|
"eai:acl.sharing": "app",
|
|
}
|
|
logger.error(
|
|
f'failed to retrieve the ACL properties for object="{title_value}" with exception="{str(e)}"'
|
|
)
|
|
|
|
collection_value = lookup_object.content["collection"]
|
|
field_list_value = lookup_object.content["fields_list"]
|
|
object_dict["collection"] = collection_value
|
|
object_dict["fields_list"] = field_list_value
|
|
|
|
elif type_value == "kvstore_collections":
|
|
|
|
acl_link = f"/servicesNS/nobody/trackme/storage/collections/config/{title_value}"
|
|
acl_url = f"{request_info.server_rest_uri}/{acl_link}/acl/list?output_mode=json"
|
|
|
|
try:
|
|
acl_response = session.get(acl_url, verify=False)
|
|
acl_properties = json.loads(acl_response.text).get(
|
|
"entry"
|
|
)[0]["acl"]
|
|
|
|
# get perms['read'] as perms_read and turn from list to csv
|
|
perms_read = ",".join(acl_properties["perms"]["read"])
|
|
# get perms['write'] as perms_write and turn from list to csv
|
|
perms_write = ",".join(acl_properties["perms"]["write"])
|
|
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": acl_properties.get("owner"),
|
|
"eai:acl.perms.read": perms_read,
|
|
"eai:acl.perms.write": perms_write,
|
|
"eai:acl.sharing": acl_properties.get("sharing"),
|
|
}
|
|
|
|
except Exception as e:
|
|
object_dict["properties"] = {
|
|
"eai:acl.owner": "nobody",
|
|
"eai:acl.perms.read": "trackme_user,trackmer_power",
|
|
"eai:acl.perms.write": "trackme_user,trackme_power,trackmer_admin",
|
|
"eai:acl.sharing": "app",
|
|
}
|
|
logger.error(
|
|
f'failed to retrieve the ACL properties for object="{title_value}" with exception="{str(e)}"'
|
|
)
|
|
|
|
# create the result
|
|
query_results.append(object_dict)
|
|
|
|
return {"payload": query_results, "status": 200}
|
|
|
|
except Exception as e:
|
|
response = {
|
|
"action": "failure",
|
|
"response": f'an exception was encountered, exception="{str(e)}"',
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
# Shows tenants operational status
|
|
def post_get_tenant_ops_status(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/get_tenant_ops_status\" body=\"{'tenant_id':'mytenant'}\"
|
|
"""
|
|
|
|
describe = False
|
|
mode = "pretty"
|
|
tenant_id = "*"
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
|
|
if not describe:
|
|
try:
|
|
tenant_id = resp_dict["tenant_id"]
|
|
except Exception as e:
|
|
tenant_id = "*"
|
|
|
|
try:
|
|
mode = resp_dict["mode"]
|
|
if mode in ("pretty", "raw"):
|
|
mode = mode
|
|
else:
|
|
mode = "pretty"
|
|
except Exception as e:
|
|
mode = "pretty"
|
|
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the tenant operational status, it requires a POST call with optional data:",
|
|
"resource_desc": "Get operational status for a TrackMe tenant",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/get_tenant_ops_status\" body=\"{'tenant_id':'mytenant'}\"",
|
|
"options": [
|
|
{
|
|
"tenant_id": "Tenant identifier, optional and defaults to all tenants if not specified",
|
|
"mode": "rendering mode, valid options are: pretty | raw (defaults to pretty if not specified)",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service - Attention this must run as the user!
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.session_key,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# Define the SPL query
|
|
kwargs_search = {
|
|
"app": "trackme",
|
|
"earliest_time": "-5m",
|
|
"latest_time": "now",
|
|
"output_mode": "json",
|
|
"count": 0,
|
|
}
|
|
if mode == "pretty":
|
|
searchquery = "| `per_tenant_ops_statusv2(" + str(tenant_id) + ")`"
|
|
elif mode == "raw":
|
|
searchquery = "| `per_tenant_ops_status_raw(" + str(tenant_id) + ")`"
|
|
logger.debug(f'searchquery="{searchquery}"')
|
|
|
|
query_results = []
|
|
try:
|
|
# spawn the search and get the results
|
|
reader = run_splunk_search(
|
|
service,
|
|
searchquery,
|
|
kwargs_search,
|
|
24,
|
|
5,
|
|
)
|
|
|
|
for item in reader:
|
|
if isinstance(item, dict):
|
|
query_results.append(item)
|
|
return {"payload": query_results, "status": 200}
|
|
|
|
except Exception as e:
|
|
response = {
|
|
"action": "failure",
|
|
"response": f'an exception was encountered, exception="{str(e)}"',
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
# Shows tenants scheduler status
|
|
def get_get_tenant_scheduler_status(self, request_info, **kwargs):
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
else:
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint retrieves the tenant scheduler status, it requires a GET call:",
|
|
"resource_desc": "Get scheduler status for a TrackMe tenant",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/get_tenant_scheduler_status"',
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service - Attention this must run as the user!
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.session_key,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# Define the SPL query
|
|
kwargs_search = {
|
|
"app": "trackme",
|
|
"earliest_time": "-24h",
|
|
"latest_time": "now",
|
|
"output_mode": "json",
|
|
"count": 0,
|
|
}
|
|
searchquery = remove_leading_spaces(
|
|
f"""
|
|
search (index=_internal sourcetype=scheduler app="trackme")
|
|
| rex field=savedsearch_name "_tenant_(?<tenant_id>.*)$"
|
|
| lookup trackme_virtual_tenants tenant_id OUTPUT tenant_id as found | where isnotnull(found) | fields - found
|
|
| eval alert_actions=if((isnull(alert_actions) OR (alert_actions == "")),"none",alert_actions)
|
|
| eval status=case(((status == "success") OR (status == "completed")),"completed",(status == "skipped"),"skipped",(status == "continued"),"deferred")
|
|
| search (status="completed" OR status="deferred" OR status="skipped")
|
|
| stats count(eval(status=="completed")) as count_completed, count(eval(status=="skipped")) as count_skipped, count by tenant_id, savedsearch_name
|
|
| eval "pct_completed"=round(((count_completed / count) * 100),2)
|
|
| eval status=if('pct_completed'==100, "completed", "skipped")
|
|
| eval "pct_completed_icon"=if('pct_completed'==100, "✅", "❌")
|
|
| rename savedsearch_name as report
|
|
| sort 0 tenant_id, report
|
|
"""
|
|
)
|
|
|
|
logger.debug(f'searchquery="{searchquery}"')
|
|
|
|
query_results = []
|
|
try:
|
|
# spawn the search and get the results
|
|
reader = run_splunk_search(
|
|
service,
|
|
searchquery,
|
|
kwargs_search,
|
|
24,
|
|
5,
|
|
)
|
|
|
|
for item in reader:
|
|
if isinstance(item, dict):
|
|
query_results.append(item)
|
|
return {"payload": query_results, "status": 200}
|
|
|
|
except Exception as e:
|
|
response = {
|
|
"action": "failure",
|
|
"response": f'an exception was encountered, exception="{str(e)}"',
|
|
}
|
|
logger.error(json.dumps(response))
|
|
return {"payload": response, "status": 500}
|
|
|
|
# Retrieve a report definition
|
|
def post_get_report(self, request_info, **kwargs):
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
tenant_id = resp_dict["tenant_id"]
|
|
report_name = resp_dict["report_name"]
|
|
else:
|
|
describe = True
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint allows retrieving a Splunk report/alert definition. It requires a POST call with the following options:",
|
|
"resource_desc": "Retrieve a TrackMe report definition",
|
|
"resource_spl_example": '| trackme mode=post url="/services/trackme/v2/configuration/get_report" body="{\\"tenant_id\\": \\"<tenant_id>\\", \\"report_name\\": \\"<report_name>\\"}"',
|
|
}
|
|
|
|
return {"payload": response, "status": 200}
|
|
|
|
# create the transform
|
|
try:
|
|
report_definition = trackme_get_report(
|
|
request_info.system_authtoken,
|
|
request_info.server_rest_uri,
|
|
tenant_id,
|
|
report_name,
|
|
)
|
|
return {"payload": report_definition, "status": 200}
|
|
|
|
except Exception as e:
|
|
error_msg = f'tenant_id="{tenant_id}", failed to retrieve the report definition, report="{report_name}", exception="{str(e)}"'
|
|
logger.error(error_msg)
|
|
return {"payload": error_msg, "status": 500}
|
|
|
|
# List emails delivery accounts with a least privileges approach
|
|
def get_get_emails_delivery_accounts(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/get_emails_delivery_accounts\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint provides the list of configured emails delivery accounts, it requires a GET call:",
|
|
"resource_desc": "Return the list of emails delivery accounts, if none are configured it will return localhost for the local MTA",
|
|
"resource_spl_example": '| trackme mode=get url="/services/trackme/v2/configuration/get_emails_delivery_accounts"',
|
|
"options": [],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# get all acounts
|
|
try:
|
|
accounts = []
|
|
conf_file = "trackme_emails"
|
|
confs = service.confs[str(conf_file)]
|
|
for stanza in confs:
|
|
# get all accounts
|
|
for name in stanza.name:
|
|
accounts.append(stanza.name)
|
|
break
|
|
|
|
# If no accounts found, return localhost as default
|
|
if not accounts:
|
|
accounts = ["localhost"]
|
|
|
|
return {"payload": {"accounts": accounts}, "status": 200}
|
|
|
|
except Exception as e:
|
|
return {"payload": {"accounts": ["localhost"]}, "status": 200}
|
|
|
|
# Get emails delivery accounts with a least privileges approach
|
|
def post_get_emails_delivery_account(self, request_info, **kwargs):
|
|
"""
|
|
| trackme mode=post url=\"/services/trackme/v2/configuration/get_emails_delivery_account\" body=\"{'account': 'lab'}\"
|
|
"""
|
|
|
|
describe = False
|
|
|
|
# Retrieve from data
|
|
try:
|
|
resp_dict = json.loads(str(request_info.raw_args["payload"]))
|
|
except Exception as e:
|
|
resp_dict = None
|
|
|
|
if resp_dict is not None:
|
|
try:
|
|
describe = resp_dict["describe"]
|
|
if describe in ("true", "True"):
|
|
describe = True
|
|
except Exception as e:
|
|
describe = False
|
|
account = resp_dict["account"]
|
|
else:
|
|
# body is not required in this endpoint, if not submitted do not describe the usage
|
|
describe = False
|
|
|
|
# if describe is requested, show the usage
|
|
if describe:
|
|
response = {
|
|
"describe": "This endpoint provides connection details for a Splunk remote account to be used in a programmatic manner with a least privileges approach, it requires a POST call with the following options:",
|
|
"resource_desc": "Return a emails delivery account details for programmatic access with a least privileges approach",
|
|
"resource_spl_example": "| trackme mode=post url=\"/services/trackme/v2/configuration/get_emails_delivery_account\" body=\"{'account': 'lab'}\"",
|
|
"options": [
|
|
{
|
|
"account": "The account configuration identifier",
|
|
}
|
|
],
|
|
}
|
|
return {"payload": response, "status": 200}
|
|
|
|
# Get splunkd port
|
|
splunkd_port = request_info.server_rest_port
|
|
|
|
# Get service
|
|
service = client.connect(
|
|
owner="nobody",
|
|
app="trackme",
|
|
port=splunkd_port,
|
|
token=request_info.system_authtoken,
|
|
timeout=600,
|
|
)
|
|
|
|
# set loglevel
|
|
loglevel = trackme_getloglevel(
|
|
request_info.system_authtoken, request_info.server_rest_port
|
|
)
|
|
logger.setLevel(loglevel)
|
|
|
|
# TrackMe reqinfo
|
|
trackmeconf = trackme_reqinfo(
|
|
request_info.system_authtoken, request_info.server_rest_uri
|
|
)
|
|
|
|
# get all acounts
|
|
try:
|
|
accounts = []
|
|
conf_file = "trackme_emails"
|
|
confs = service.confs[str(conf_file)]
|
|
for stanza in confs:
|
|
# get all accounts
|
|
for name in stanza.name:
|
|
accounts.append(stanza.name)
|
|
break
|
|
|
|
except Exception as e:
|
|
accounts = []
|
|
|
|
if not accounts:
|
|
return {
|
|
"payload": {
|
|
"account": "localhost",
|
|
"allowed_email_domains": None,
|
|
"email_footer": trackmeconf["trackme_conf"]["trackme_general"][
|
|
"email_footer"
|
|
],
|
|
"email_format": trackmeconf["trackme_conf"]["trackme_general"][
|
|
"email_format"
|
|
],
|
|
"email_password": None,
|
|
"email_security": None,
|
|
"email_server": "localhost:25",
|
|
"email_username": None,
|
|
"sender_email": trackmeconf["trackme_conf"]["trackme_general"][
|
|
"sender_email"
|
|
],
|
|
},
|
|
"status": 200,
|
|
}
|
|
|
|
else:
|
|
try:
|
|
response = trackme_get_emails_account(request_info, account)
|
|
return {"payload": response, "status": 200}
|
|
|
|
# note: the exception is returned as a JSON object
|
|
except Exception as e:
|
|
return {"payload": str(e), "status": 500}
|