mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-05-11 05:40:32 +02:00
277 lines
9.8 KiB
Bash
Executable File
277 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
|
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
|
# https://securityonion.net/license; you may not use this file except in compliance with the
|
|
# Elastic License 2.0.
|
|
|
|
. /usr/sbin/so-common
|
|
|
|
SO_STATEFILE_SUCCESS=/opt/so/state/estemplates.txt
|
|
ADDON_STATEFILE_SUCCESS=/opt/so/state/addon_estemplates.txt
|
|
ELASTICSEARCH_TEMPLATES_DIR="/opt/so/conf/elasticsearch/templates"
|
|
SO_TEMPLATES_DIR="${ELASTICSEARCH_TEMPLATES_DIR}/index"
|
|
ADDON_TEMPLATES_DIR="${ELASTICSEARCH_TEMPLATES_DIR}/addon-index"
|
|
SO_LOAD_FAILURES=0
|
|
ADDON_LOAD_FAILURES=0
|
|
SO_LOAD_FAILURES_NAMES=()
|
|
ADDON_LOAD_FAILURES_NAMES=()
|
|
IS_HEAVYNODE="false"
|
|
FORCE="false"
|
|
VERBOSE="false"
|
|
SHOULD_EXIT_ON_FAILURE="true"
|
|
|
|
# If soup is running, ignore errors
|
|
pgrep soup >/dev/null && SHOULD_EXIT_ON_FAILURE="false"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--heavynode)
|
|
IS_HEAVYNODE="true"
|
|
;;
|
|
--force)
|
|
FORCE="true"
|
|
;;
|
|
--verbose)
|
|
VERBOSE="true"
|
|
;;
|
|
*)
|
|
echo "Usage: $0 [options]"
|
|
echo "Options:"
|
|
echo " --heavynode Only loads index templates specific to heavynodes"
|
|
echo " --force Force reload all templates regardless of statefiles (default: false)"
|
|
echo " --verbose Enable verbose output"
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
load_template() {
|
|
local uri="$1"
|
|
local file="$2"
|
|
|
|
echo "Loading template file $file"
|
|
if ! output=$(retry 3 3 "so-elasticsearch-query $uri -d@$file -XPUT" "{\"acknowledged\":true}"); then
|
|
echo "$output"
|
|
|
|
return 1
|
|
|
|
elif [[ "$VERBOSE" == "true" ]]; then
|
|
echo "$output"
|
|
fi
|
|
|
|
}
|
|
|
|
check_required_component_template_exists() {
|
|
local required
|
|
local missing
|
|
local file=$1
|
|
|
|
required=$(jq '[((.composed_of //[]) - (.ignore_missing_component_templates // []))[]]' "$file")
|
|
missing=$(jq -n --argjson required "$required" --argjson component_templates "$component_templates" '(($required) - ($component_templates))')
|
|
|
|
if [[ $(jq length <<<"$missing") -gt 0 ]]; then
|
|
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_heavynode_compatiable_index_template() {
|
|
# The only templates that are relevant to heavynodes are from datasets defined in elasticagent/files/elastic-agent.yml.jinja.
|
|
# Heavynodes do not have fleet server packages installed and do not support elastic agents reporting directly to them.
|
|
local -A heavynode_index_templates=(
|
|
["so-import"]=1
|
|
["so-syslog"]=1
|
|
["so-logs-soc"]=1
|
|
["so-suricata"]=1
|
|
["so-suricata.alerts"]=1
|
|
["so-zeek"]=1
|
|
["so-strelka"]=1
|
|
)
|
|
|
|
local template_name="$1"
|
|
|
|
if [[ ! -v heavynode_index_templates["$template_name"] ]]; then
|
|
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
load_component_templates() {
|
|
local printed_name="$1"
|
|
local pattern="${ELASTICSEARCH_TEMPLATES_DIR}/component/$2"
|
|
local append_mappings="${3:-"false"}"
|
|
|
|
echo -e "\nLoading $printed_name component templates...\n"
|
|
|
|
if ! compgen -G "${pattern}/*.json" > /dev/null; then
|
|
echo "No $printed_name component templates found in ${pattern}, skipping."
|
|
return
|
|
fi
|
|
|
|
for component in "$pattern"/*.json; do
|
|
tmpl_name=$(basename "${component%.json}")
|
|
|
|
if [[ "$append_mappings" == "true" ]]; then
|
|
# avoid duplicating "-mappings" if it already exists in the component template filename
|
|
tmpl_name="${tmpl_name%-mappings}-mappings"
|
|
fi
|
|
|
|
if ! load_template "_component_template/${tmpl_name}" "$component"; then
|
|
SO_LOAD_FAILURES=$((SO_LOAD_FAILURES + 1))
|
|
SO_LOAD_FAILURES_NAMES+=("$component")
|
|
fi
|
|
done
|
|
}
|
|
|
|
check_elasticsearch_responsive() {
|
|
# Cannot load templates if Elasticsearch is not responding.
|
|
# NOTE: Slightly faster exit w/ failure than previous "retry 240 1" if there is a problem with Elasticsearch the
|
|
# script should exit sooner rather than hang at the 'so-elasticsearch-templates' salt state.
|
|
retry 3 15 "so-elasticsearch-query / --output /dev/null --fail" ||
|
|
fail "Elasticsearch is not responding. Please review Elasticsearch logs /opt/so/log/elasticsearch/securityonion.log for more details. Additionally, consider running so-elasticsearch-troubleshoot."
|
|
}
|
|
|
|
index_templates_exist() {
|
|
local templates_dir="$1"
|
|
|
|
if [[ ! -d "$templates_dir" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
compgen -G "${templates_dir}/*.json" > /dev/null
|
|
}
|
|
|
|
should_load_addon_templates() {
|
|
if [[ "$IS_HEAVYNODE" == "true" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
# Skip statefile checks when forcing template load
|
|
if [[ "$FORCE" != "true" ]]; then
|
|
if [[ ! -f "$SO_STATEFILE_SUCCESS" || -f "$ADDON_STATEFILE_SUCCESS" ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
index_templates_exist "$ADDON_TEMPLATES_DIR"
|
|
}
|
|
|
|
if [[ "$FORCE" == "true" || ! -f "$SO_STATEFILE_SUCCESS" ]] && index_templates_exist "$SO_TEMPLATES_DIR"; then
|
|
check_elasticsearch_responsive
|
|
|
|
if [[ "$IS_HEAVYNODE" == "false" ]]; then
|
|
# TODO: Better way to check if fleet server is installed vs checking for Elastic Defend component template.
|
|
fleet_check="logs-endpoint.alerts@package"
|
|
if ! so-elasticsearch-query "_component_template/$fleet_check" --output /dev/null --retry 5 --retry-delay 3 --fail; then
|
|
# This check prevents so-elasticsearch-templates-load from running before so-elastic-fleet-setup has run.
|
|
echo -e "\nPackage $fleet_check not yet installed. Fleet Server may not be fully configured yet."
|
|
# Fleet Server is required because some SO index templates depend on components installed via
|
|
# specific integrations eg Elastic Defend. These are components that we do not manually create / manage
|
|
# via /opt/so/saltstack/salt/elasticsearch/templates/component/
|
|
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# load_component_templates "Name" "directory" "append '-mappings'?"
|
|
load_component_templates "ECS" "ecs" "true"
|
|
load_component_templates "Elastic Agent" "elastic-agent"
|
|
load_component_templates "Security Onion" "so"
|
|
|
|
component_templates=$(so-elasticsearch-component-templates-list)
|
|
echo -e "Loading Security Onion index templates...\n"
|
|
for so_idx_tmpl in "${SO_TEMPLATES_DIR}"/*.json; do
|
|
tmpl_name=$(basename "${so_idx_tmpl%-template.json}")
|
|
|
|
if [[ "$IS_HEAVYNODE" == "true" ]]; then
|
|
# TODO: Better way to load only heavynode specific templates
|
|
if ! check_heavynode_compatiable_index_template "$tmpl_name"; then
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
echo "Skipping over $so_idx_tmpl, template is not a heavynode specific index template."
|
|
fi
|
|
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
if check_required_component_template_exists "$so_idx_tmpl"; then
|
|
if ! load_template "_index_template/$tmpl_name" "$so_idx_tmpl"; then
|
|
SO_LOAD_FAILURES=$((SO_LOAD_FAILURES + 1))
|
|
SO_LOAD_FAILURES_NAMES+=("$so_idx_tmpl")
|
|
fi
|
|
else
|
|
echo "Skipping over $so_idx_tmpl due to missing required component template(s)."
|
|
SO_LOAD_FAILURES=$((SO_LOAD_FAILURES + 1))
|
|
SO_LOAD_FAILURES_NAMES+=("$so_idx_tmpl")
|
|
|
|
continue
|
|
fi
|
|
done
|
|
|
|
if [[ $SO_LOAD_FAILURES -eq 0 ]]; then
|
|
echo "All Security Onion core templates loaded successfully."
|
|
|
|
touch "$SO_STATEFILE_SUCCESS"
|
|
else
|
|
echo "Encountered $SO_LOAD_FAILURES failure(s) loading templates:"
|
|
for failed_template in "${SO_LOAD_FAILURES_NAMES[@]}"; do
|
|
echo " - $failed_template"
|
|
done
|
|
if [[ "$SHOULD_EXIT_ON_FAILURE" == "true" ]]; then
|
|
fail "Failed to load all Security Onion core templates successfully."
|
|
fi
|
|
fi
|
|
elif ! index_templates_exist "$SO_TEMPLATES_DIR"; then
|
|
echo "No Security Onion core index templates found in ${SO_TEMPLATES_DIR}, skipping."
|
|
elif [[ -f "$SO_STATEFILE_SUCCESS" ]]; then
|
|
echo "Security Onion core templates already loaded"
|
|
fi
|
|
|
|
# Start loading addon templates
|
|
if should_load_addon_templates; then
|
|
|
|
check_elasticsearch_responsive
|
|
|
|
echo -e "\nLoading addon integration index templates...\n"
|
|
component_templates=$(so-elasticsearch-component-templates-list)
|
|
|
|
for addon_idx_tmpl in "${ADDON_TEMPLATES_DIR}"/*.json; do
|
|
tmpl_name=$(basename "${addon_idx_tmpl%-template.json}")
|
|
|
|
if check_required_component_template_exists "$addon_idx_tmpl"; then
|
|
if ! load_template "_index_template/${tmpl_name}" "$addon_idx_tmpl"; then
|
|
ADDON_LOAD_FAILURES=$((ADDON_LOAD_FAILURES + 1))
|
|
ADDON_LOAD_FAILURES_NAMES+=("$addon_idx_tmpl")
|
|
fi
|
|
else
|
|
echo "Skipping over $addon_idx_tmpl due to missing required component template(s)."
|
|
ADDON_LOAD_FAILURES=$((ADDON_LOAD_FAILURES + 1))
|
|
ADDON_LOAD_FAILURES_NAMES+=("$addon_idx_tmpl")
|
|
|
|
continue
|
|
fi
|
|
done
|
|
|
|
if [[ $ADDON_LOAD_FAILURES -eq 0 ]]; then
|
|
echo "All addon integration templates loaded successfully."
|
|
|
|
touch "$ADDON_STATEFILE_SUCCESS"
|
|
else
|
|
echo "Encountered $ADDON_LOAD_FAILURES failure(s) loading addon integration templates:"
|
|
for failed_template in "${ADDON_LOAD_FAILURES_NAMES[@]}"; do
|
|
echo " - $failed_template"
|
|
done
|
|
if [[ "$SHOULD_EXIT_ON_FAILURE" == "true" ]]; then
|
|
fail "Failed to load all addon integration templates successfully."
|
|
fi
|
|
fi
|
|
|
|
elif [[ ! -f "$SO_STATEFILE_SUCCESS" && "$IS_HEAVYNODE" == "false" ]]; then
|
|
echo "Skipping loading addon integration templates until Security Onion core templates have been loaded."
|
|
|
|
elif [[ -f "$ADDON_STATEFILE_SUCCESS" && "$IS_HEAVYNODE" == "false" && "$FORCE" == "false" ]]; then
|
|
echo "Addon integration templates already loaded"
|
|
fi
|