Compare commits

..

4 Commits

Author SHA1 Message Date
reyesj2 c505160480 set default DLM retention 90d 2026-06-11 15:13:28 -05:00
reyesj2 d9f6cde4e1 remove global setting from data_retention annotation 2026-06-11 15:11:29 -05:00
reyesj2 cf456dc58c reuse existing index templates 2026-06-09 23:21:43 -05:00
reyesj2 9aa9ea3255 Iniitial DLM support 2026-06-09 23:19:26 -05:00
22 changed files with 1109 additions and 641 deletions
+5
View File
@@ -142,6 +142,11 @@ check_elastic_license() {
fi fi
} }
check_elasticsearch_responsive() {
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."
}
check_salt_master_status() { check_salt_master_status() {
local count=0 local count=0
local attempts="${1:- 10}" local attempts="${1:- 10}"
@@ -9,7 +9,6 @@
{% set CORE_ESFLEET_PACKAGES = ELASTICFLEETDEFAULTS.get('elasticfleet', {}).get('packages', {}) %} {% set CORE_ESFLEET_PACKAGES = ELASTICFLEETDEFAULTS.get('elasticfleet', {}).get('packages', {}) %}
{% set ADDON_CONTENT_INTEGRATION_DEFAULTS = {} %} {% set ADDON_CONTENT_INTEGRATION_DEFAULTS = {} %}
{% set DEBUG_STUFF = {} %}
{% for pkg in ADDON_CONTENT_PACKAGE_COMPONENTS %} {% for pkg in ADDON_CONTENT_PACKAGE_COMPONENTS %}
{% if pkg.name in CORE_ESFLEET_PACKAGES %} {% if pkg.name in CORE_ESFLEET_PACKAGES %}
@@ -9,7 +9,6 @@
{% set CORE_ESFLEET_PACKAGES = ELASTICFLEETDEFAULTS.get('elasticfleet', {}).get('packages', {}) %} {% set CORE_ESFLEET_PACKAGES = ELASTICFLEETDEFAULTS.get('elasticfleet', {}).get('packages', {}) %}
{% set ADDON_INPUT_INTEGRATION_DEFAULTS = {} %} {% set ADDON_INPUT_INTEGRATION_DEFAULTS = {} %}
{% set DEBUG_STUFF = {} %}
{% for pkg in ADDON_INPUT_PACKAGE_COMPONENTS %} {% for pkg in ADDON_INPUT_PACKAGE_COMPONENTS %}
{% if pkg.name in CORE_ESFLEET_PACKAGES %} {% if pkg.name in CORE_ESFLEET_PACKAGES %}
@@ -116,7 +115,6 @@
{% do ADDON_INPUT_INTEGRATION_DEFAULTS.update({integration_key: integration_defaults}) %} {% do ADDON_INPUT_INTEGRATION_DEFAULTS.update({integration_key: integration_defaults}) %}
{% do DEBUG_STUFF.update({integration_key: "Generating defaults for "+ pkg.name })%}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -30,70 +30,6 @@ fleet_api() {
curl -sK /opt/so/conf/elasticsearch/curl.config -L "localhost:5601/api/fleet/${QUERYPATH}" "$@" --retry 3 --retry-delay 10 --fail 2>/dev/null curl -sK /opt/so/conf/elasticsearch/curl.config -L "localhost:5601/api/fleet/${QUERYPATH}" "$@" --retry 3 --retry-delay 10 --fail 2>/dev/null
} }
# Max number of concurrent Fleet write jobs (create/update). Override via env if needed.
MAX_FLEET_JOBS=${MAX_FLEET_JOBS:-10}
# Block until fewer than MAX_FLEET_JOBS background jobs are running.
elastic_fleet_throttle() {
while (( $(jobs -rp | wc -l) >= MAX_FLEET_JOBS )); do
wait -n
done
}
# Load every integration JSON in a directory into a single agent policy.
# The agent policy is fetched ONCE (not per file), and the create/update writes
# are dispatched as throttled background jobs.
# $1 AGENT_POLICY - the agent policy id/name to load integrations into
# $2 DIR - directory of integration *.json files
# $3 LABEL - human-readable label for log output
# $4 SKIP_CREATE_NAME - (optional) integration name to skip when creating (still updated if present)
# Returns 1 if any integration failed to create/update.
elastic_fleet_load_integrations_dir() {
local AGENT_POLICY=$1
local DIR=$2
local LABEL=$3
local SKIP_CREATE_NAME=$4
local POLICY_JSON FAIL_FILE INTEGRATION NAME ID
FAIL_FILE=$(mktemp)
# Fetch the agent policy a single time; we look up integration ids locally below.
POLICY_JSON=$(fleet_api "agent_policies/$AGENT_POLICY")
for INTEGRATION in "$DIR"/*.json; do
[ -e "$INTEGRATION" ] || continue
NAME=$(jq -r .name "$INTEGRATION")
ID=$(jq -r --arg n "$NAME" '.item.package_policies[]? | select(.name==$n) | .id' <<<"$POLICY_JSON")
elastic_fleet_throttle
{
if [ -n "$ID" ]; then
printf "\n\n%s - Updating integration %s\n" "$LABEL" "$NAME"
if ! elastic_fleet_integration_update "$ID" "@$INTEGRATION"; then
flock 9; echo "update ${INTEGRATION##*/}" >&9
fi
elif [ -n "$SKIP_CREATE_NAME" ] && [ "$NAME" == "$SKIP_CREATE_NAME" ]; then
printf "\n\n%s - Skipping creation of %s\n" "$LABEL" "$NAME"
else
printf "\n\n%s - Creating integration %s\n" "$LABEL" "$NAME"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
flock 9; echo "create ${INTEGRATION##*/}" >&9
fi
fi
} 9>>"$FAIL_FILE" &
done
wait
local rc=0
if [ -s "$FAIL_FILE" ]; then
printf "\n%s: failed integrations:\n" "$LABEL"
cat "$FAIL_FILE"
rc=1
fi
rm -f "$FAIL_FILE"
return $rc
}
elastic_fleet_integration_check() { elastic_fleet_integration_check() {
AGENT_POLICY=$1 AGENT_POLICY=$1
@@ -18,26 +18,93 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
# Third, configure Elastic Defend Integration seperately # Third, configure Elastic Defend Integration seperately
/usr/sbin/so-elastic-fleet-integration-policy-elastic-defend /usr/sbin/so-elastic-fleet-integration-policy-elastic-defend
# Each group fetches its agent policy once and dispatches create/update writes concurrently.
# Initial Endpoints # Initial Endpoints
elastic_fleet_load_integrations_dir "endpoints-initial" \ for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/endpoints-initial/*.json; do
/opt/so/conf/elastic-fleet/integrations/endpoints-initial "Initial Endpoints Policy" || RETURN_CODE=1 printf "\n\nInitial Endpoints Policy - Loading $INTEGRATION\n"
elastic_fleet_integration_check "endpoints-initial" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
done
# Grid Nodes - General # Grid Nodes - General
elastic_fleet_load_integrations_dir "so-grid-nodes_general" \ for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/grid-nodes_general/*.json; do
/opt/so/conf/elastic-fleet/integrations/grid-nodes_general "Grid Nodes Policy_General" || RETURN_CODE=1 printf "\n\nGrid Nodes Policy_General - Loading $INTEGRATION\n"
elastic_fleet_integration_check "so-grid-nodes_general" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
done
# Grid Nodes - Heavy # Grid Nodes - Heavy
elastic_fleet_load_integrations_dir "so-grid-nodes_heavy" \ for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/grid-nodes_heavy/*.json; do
/opt/so/conf/elastic-fleet/integrations/grid-nodes_heavy "Grid Nodes Policy_Heavy" || RETURN_CODE=1 printf "\n\nGrid Nodes Policy_Heavy - Loading $INTEGRATION\n"
elastic_fleet_integration_check "so-grid-nodes_heavy" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
done
# Fleet Server - Optional integrations (one agent policy per FleetServer_* directory) # Fleet Server - Optional integrations
for FLEET_DIR in /opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/; do for INTEGRATION in /opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/*.json; do
[ -d "$FLEET_DIR" ] || continue if ! [ "$INTEGRATION" == "/opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/*.json" ]; then
FLEET_POLICY=$(basename "$FLEET_DIR") FLEET_POLICY=`echo "$INTEGRATION"| cut -d'/' -f7`
elastic_fleet_load_integrations_dir "$FLEET_POLICY" \ printf "\n\nFleet Server Policy - Loading $INTEGRATION\n"
"${FLEET_DIR%/}" "Fleet Server Policy" "elasticsearch-logs" || RETURN_CODE=1 elastic_fleet_integration_check "$FLEET_POLICY" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
if [ "$NAME" != "elasticsearch-logs" ]; then
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
fi
fi
done done
# Only create the state file if all policies were created/updated successfully # Only create the state file if all policies were created/updated successfully
@@ -23,90 +23,73 @@ if [ $? -ne 0 ]; then
fi fi
default_packages=({% for pkg in SUPPORTED_PACKAGES %}"{{ pkg }}"{% if not loop.last %} {% endif %}{% endfor %}) default_packages=({% for pkg in SUPPORTED_PACKAGES %}"{{ pkg }}"{% if not loop.last %} {% endif %}{% endfor %})
# JSON array of the default packages, used by the jq filter below.
default_packages_json=$(printf '%s\n' "${default_packages[@]}" | jq -R . | jq -s '.')
# Output lock (serializes concurrent job output) and failure file (one marker line per
# failed integration). Mirrors the pattern used by elastic_fleet_load_integrations_dir.
OUTPUT_LOCK=$(mktemp)
FAIL_FILE=$(mktemp)
trap 'rm -f "$OUTPUT_LOCK" "$FAIL_FILE"' EXIT
# Cache of package name -> latest available version, so the same package is only looked up
# once instead of once per (policy, integration).
declare -A LATEST_VERSION_CACHE
ERROR=false
for AGENT_POLICY in $agent_policies; do for AGENT_POLICY in $agent_policies; do
# Fetch the agent policy a single time; package name/version and integration id are all if ! integrations=$(elastic_fleet_integration_policy_names "$AGENT_POLICY"); then
# extracted locally below instead of re-fetching the same policy per integration.
if ! POLICY_JSON=$(fleet_api "agent_policies/$AGENT_POLICY"); then
# this script upgrades default integration packages, exit 1 and let salt handle retrying # this script upgrades default integration packages, exit 1 and let salt handle retrying
exit 1 exit 1
fi fi
for INTEGRATION in $integrations; do
# One jq pass emits name/package.name/package.version/id for every eligible integration. if ! [[ "$INTEGRATION" == "elastic-defend-endpoints" ]] && ! [[ "$INTEGRATION" == "fleet_server-"* ]]; then
# The endpoint/fleet_server skips and the default-package gate are applied here in jq. # Get package name so we know what package to look for when checking the current and latest available version
# $defaults (not $def, a jq reserved keyword) holds the default package list. if ! PACKAGE_NAME=$(elastic_fleet_integration_policy_package_name "$AGENT_POLICY" "$INTEGRATION"); then
while IFS=$'\t' read -r INTEGRATION PACKAGE_NAME PACKAGE_VERSION INTEGRATION_ID; do
[ -n "$INTEGRATION" ] || continue
# Look up the latest available version once per package, then memoize it.
if [[ -z "${LATEST_VERSION_CACHE[$PACKAGE_NAME]+set}" ]]; then
if ! AVAILABLE_VERSION=$(elastic_fleet_package_latest_version_check "$PACKAGE_NAME"); then
echo "Error: Failed getting latest version for $PACKAGE_NAME"
exit 1 exit 1
fi fi
LATEST_VERSION_CACHE[$PACKAGE_NAME]=$AVAILABLE_VERSION {%- if not AUTO_UPGRADE_INTEGRATIONS %}
fi if [[ " ${default_packages[@]} " =~ " $PACKAGE_NAME " ]]; then
AVAILABLE_VERSION=${LATEST_VERSION_CACHE[$PACKAGE_NAME]} {%- endif %}
# Get currently installed version of package
if [[ "$PACKAGE_VERSION" != "$AVAILABLE_VERSION" ]]; then attempt=0
# Dry run, then (if clean) the actual upgrade, dispatched as a throttled background max_attempts=3
# job. Each job builds its full log into one block, then flushes it under a single while [ $attempt -lt $max_attempts ]; do
# shared lock (OUTPUT_LOCK) so concurrent jobs never interleave on stdout; a failed if PACKAGE_VERSION=$(elastic_fleet_integration_policy_package_version "$AGENT_POLICY" "$INTEGRATION") && AVAILABLE_VERSION=$(elastic_fleet_package_latest_version_check "$PACKAGE_NAME"); then
# job also appends a marker line to FAIL_FILE while holding that same lock. break
elastic_fleet_throttle
{
block=$'\n'"Current $PACKAGE_NAME package version ($PACKAGE_VERSION) is not the same as the latest available package ($AVAILABLE_VERSION)..."$'\n'
block+="Upgrading $INTEGRATION..."$'\n'"Starting dry run..."$'\n'
fail=""
if ! DRYRUN_OUTPUT=$(elastic_fleet_integration_policy_dryrun_upgrade "$INTEGRATION_ID"); then
block+="Error: Failed to complete dry run for '$INTEGRATION_ID'."$'\n'
fail="dryrun $INTEGRATION"
elif [[ "$(jq .[].hasErrors <<<"$DRYRUN_OUTPUT")" == "false" ]]; then
block+="No errors detected. Proceeding with upgrade..."$'\n'
if ! elastic_fleet_integration_policy_upgrade "$INTEGRATION_ID"; then
block+="Error: Upgrade failed for $PACKAGE_NAME with integration ID '$INTEGRATION_ID'."$'\n'
fail="upgrade $INTEGRATION"
fi fi
else attempt=$((attempt + 1))
block+="Errors detected during dry run for $PACKAGE_NAME policy upgrade..."$'\n' done
fail="dryrun-errors $INTEGRATION" if [ $attempt -eq $max_attempts ]; then
echo "Error: Failed getting $PACKAGE_VERSION or $AVAILABLE_VERSION"
exit 1
fi fi
{
flock 9 # Get integration ID
printf '%s' "$block" if ! INTEGRATION_ID=$(elastic_fleet_integration_id "$AGENT_POLICY" "$INTEGRATION"); then
[ -n "$fail" ] && printf '%s\n' "$fail" >>"$FAIL_FILE" exit 1
} 9>>"$OUTPUT_LOCK" fi
} &
if [[ "$PACKAGE_VERSION" != "$AVAILABLE_VERSION" ]]; then
# Dry run of the upgrade
echo ""
echo "Current $PACKAGE_NAME package version ($PACKAGE_VERSION) is not the same as the latest available package ($AVAILABLE_VERSION)..."
echo "Upgrading $INTEGRATION..."
echo "Starting dry run..."
if ! DRYRUN_OUTPUT=$(elastic_fleet_integration_policy_dryrun_upgrade "$INTEGRATION_ID"); then
exit 1
fi
DRYRUN_ERRORS=$(echo "$DRYRUN_OUTPUT" | jq .[].hasErrors)
# If no errors with dry run, proceed with actual upgrade
if [[ "$DRYRUN_ERRORS" == "false" ]]; then
echo "No errors detected. Proceeding with upgrade..."
if ! elastic_fleet_integration_policy_upgrade "$INTEGRATION_ID"; then
echo "Error: Upgrade failed for $PACKAGE_NAME with integration ID '$INTEGRATION_ID'."
ERROR=true
continue
fi
else
echo "Errors detected during dry run for $PACKAGE_NAME policy upgrade..."
ERROR=true
continue
fi
fi
{%- if not AUTO_UPGRADE_INTEGRATIONS %}
fi
{%- endif %}
fi fi
done < <(jq -r --argjson defaults "$default_packages_json" ' done
.item.package_policies[]
| select(.name != "elastic-defend-endpoints")
| select(.name | startswith("fleet_server-") | not)
{%- if not AUTO_UPGRADE_INTEGRATIONS %}
| select(.package.name | IN($defaults[]))
{%- endif %}
| [.name, .package.name, .package.version, .id] | @tsv
' <<<"$POLICY_JSON")
done done
if [[ "$ERROR" == "true" ]]; then
# Barrier: wait for every dispatched dry-run/upgrade job to finish.
wait
if [ -s "$FAIL_FILE" ]; then
printf '\nFailed integration upgrades:\n'
cat "$FAIL_FILE"
exit 1 exit 1
fi fi
echo echo
@@ -16,6 +16,7 @@
STATE_FILE_SUCCESS=/opt/so/state/estemplates.txt STATE_FILE_SUCCESS=/opt/so/state/estemplates.txt
INSTALLED_PACKAGE_LIST=/tmp/esfleet_installed_packages.json INSTALLED_PACKAGE_LIST=/tmp/esfleet_installed_packages.json
BULK_INSTALL_PACKAGE_LIST=/tmp/esfleet_bulk_install.json BULK_INSTALL_PACKAGE_LIST=/tmp/esfleet_bulk_install.json
BULK_INSTALL_PACKAGE_TMP=/tmp/esfleet_bulk_install_tmp.json
BULK_INSTALL_OUTPUT=/opt/so/state/esfleet_bulk_install_results.json BULK_INSTALL_OUTPUT=/opt/so/state/esfleet_bulk_install_results.json
INTEGRATION_PACKAGE_COMPONENTS=/opt/so/state/esfleet_package_components.json INTEGRATION_PACKAGE_COMPONENTS=/opt/so/state/esfleet_package_components.json
INPUT_PACKAGE_COMPONENTS=/opt/so/state/esfleet_input_package_components.json INPUT_PACKAGE_COMPONENTS=/opt/so/state/esfleet_input_package_components.json
@@ -28,6 +29,29 @@ PENDING_UPDATE=false
# Requiring some level of manual Elastic Stack configuration before installation # Requiring some level of manual Elastic Stack configuration before installation
EXCLUDED_INTEGRATIONS=('apm') EXCLUDED_INTEGRATIONS=('apm')
version_conversion(){
version=$1
echo "$version" | awk -F '.' '{ printf("%d%03d%03d\n", $1, $2, $3); }'
}
compare_versions() {
version1=$1
version2=$2
# Convert versions to numbers
num1=$(version_conversion "$version1")
num2=$(version_conversion "$version2")
# Compare using bc
if (( $(echo "$num1 < $num2" | bc -l) )); then
echo "less"
elif (( $(echo "$num1 > $num2" | bc -l) )); then
echo "greater"
else
echo "equal"
fi
}
IFS=$'\n' IFS=$'\n'
agent_policies=$(elastic_fleet_agent_policy_ids) agent_policies=$(elastic_fleet_agent_policy_ids)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@@ -39,23 +63,23 @@ default_packages=({% for pkg in SUPPORTED_PACKAGES %}"{{ pkg }}"{% if not loop.l
in_use_integrations=() in_use_integrations=()
# Fetch each agent policy once; its package_policies[] already contain both the integration name
# and the .package.name, so extract all non-default package names locally in a single jq instead
# of re-fetching the same policy per integration.
default_packages_json=$(printf '%s\n' "${default_packages[@]}" | jq -R . | jq -s '.')
for AGENT_POLICY in $agent_policies; do for AGENT_POLICY in $agent_policies; do
if ! policy_json=$(fleet_api "agent_policies/$AGENT_POLICY"); then if ! integrations=$(elastic_fleet_integration_policy_names "$AGENT_POLICY"); then
# skip the agent policy if we can't get required info, let salt retry. Integrations loaded by this script are non-default integrations. # skip the agent policy if we can't get required info, let salt retry. Integrations loaded by this script are non-default integrations.
echo "Skipping $AGENT_POLICY.. " echo "Skipping $AGENT_POLICY.. "
continue continue
fi fi
# non-default integrations that are in-use in any policy for INTEGRATION in $integrations; do
while IFS= read -r PACKAGE_NAME; do if ! PACKAGE_NAME=$(elastic_fleet_integration_policy_package_name "$AGENT_POLICY" "$INTEGRATION"); then
[ -n "$PACKAGE_NAME" ] && in_use_integrations+=("$PACKAGE_NAME") echo "Not adding $INTEGRATION, couldn't get package name"
done < <(jq -r --argjson defaults "$default_packages_json" \ continue
'.item.package_policies[].package.name | select(. as $n | ($defaults | index($n)) | not)' \ fi
<<<"$policy_json") # non-default integrations that are in-use in any policy
if ! [[ " ${default_packages[@]} " =~ " $PACKAGE_NAME " ]]; then
in_use_integrations+=("$PACKAGE_NAME")
fi
done
done done
if [[ -f $STATE_FILE_SUCCESS ]]; then if [[ -f $STATE_FILE_SUCCESS ]]; then
@@ -66,55 +90,72 @@ if [[ -f $STATE_FILE_SUCCESS ]]; then
rm -f $INSTALLED_PACKAGE_LIST rm -f $INSTALLED_PACKAGE_LIST
echo $latest_package_list | jq '{packages: [.items[] | {name: .name, latest_version: .version, installed_version: .installationInfo.version, subscription: .conditions.elastic.subscription }]}' >> $INSTALLED_PACKAGE_LIST echo $latest_package_list | jq '{packages: [.items[] | {name: .name, latest_version: .version, installed_version: .installationInfo.version, subscription: .conditions.elastic.subscription }]}' >> $INSTALLED_PACKAGE_LIST
# Build the bulk install list and the per-package status messages with two jq passes while read -r package; do
# instead of a per-package bash loop. The old loop forked ~10 processes per package # get package details
# (5 jq + awk/bc for the version compare) and re-parsed/rewrote a growing JSON file on package_name=$(echo "$package" | jq -r '.name')
# every add (O(n^2)). Selection and messages below are identical to that logic. latest_version=$(echo "$package" | jq -r '.latest_version')
SUB={% if SUB %}true{% else %}false{% endif %} installed_version=$(echo "$package" | jq -r '.installed_version')
AUTOUP={% if AUTO_UPGRADE_INTEGRATIONS %}true{% else %}false{% endif %} subscription=$(echo "$package" | jq -r '.subscription')
EXCLUDED_JSON=$(printf '%s\n' "${EXCLUDED_INTEGRATIONS[@]}" | jq -R 'select(length>0)' | jq -s '.') bulk_package=$(echo "$package" | jq '{name: .name, version: .latest_version}' )
INUSE_JSON=$(printf '%s\n' "${in_use_integrations[@]}" | jq -R 'select(length>0)' | jq -s 'unique')
# vnum replicates the previous version_conversion (%d%03d%03d of the first three dotted if [[ ! "${EXCLUDED_INTEGRATIONS[@]}" =~ "$package_name" ]]; then
# fields); needs() replicates the excluded/subscription/installed/upgrade/in-use logic. {% if not SUB %}
JQ_DECISION=' if [[ "$subscription" != "basic" && "$subscription" != "null" && -n "$subscription" ]]; then
def vnum: # pass over integrations that require non-basic elastic license
[ (split(".")|.[0:3][] | gsub("[^0-9].*";"") | (if .=="" then "0" else . end) | tonumber) ] echo "$package_name integration requires an Elastic license of $subscription or greater... skipping"
| (.[0]//0)*1000000 + (.[1]//0)*1000 + (.[2]//0); continue
def needs($sub;$autoup;$excluded;$inuse): else
.name as $n if [[ "$installed_version" == "null" || -z "$installed_version" ]]; then
| ($n | IN($excluded[]) | not) echo "$package_name is not installed... Adding to next update."
and ( $sub or (.subscription==null or .subscription=="basic" or .subscription=="") ) jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST
and ( (.installed_version==null or .installed_version=="")
or ( ((.latest_version|vnum) > (.installed_version|vnum))
and ( $autoup or ($n | IN($inuse[]) | not) ) ) );'
JQ_ARGS=(--argjson sub "$SUB" --argjson autoup "$AUTOUP" --argjson excluded "$EXCLUDED_JSON" --argjson inuse "$INUSE_JSON") PENDING_UPDATE=true
else
results=$(compare_versions "$latest_version" "$installed_version")
if [ $results == "greater" ]; then
{#- When auto_upgrade_integrations is false, skip upgrading in_use_integrations #}
{%- if not AUTO_UPGRADE_INTEGRATIONS %}
if ! [[ " ${in_use_integrations[@]} " =~ " $package_name " ]]; then
{%- endif %}
echo "$package_name is at version $installed_version latest version is $latest_version... Adding to next update."
jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST
# (a) Per-package status messages (parity with the previous echo output). PENDING_UPDATE=true
jq -r "${JQ_ARGS[@]}" "$JQ_DECISION"' {%- if not AUTO_UPGRADE_INTEGRATIONS %}
.packages[] else
| .name as $n echo "skipping available upgrade for in use integration - $package_name."
| if ($n|IN($excluded[])) then "Skipping \($n)..." fi
elif (($sub|not) and (.subscription!=null and .subscription!="basic" and .subscription!="")) then {%- endif %}
"\($n) integration requires an Elastic license of \(.subscription) or greater... skipping" fi
elif (.installed_version==null or .installed_version=="") then fi
"\($n) is not installed... Adding to next update." fi
elif ((.latest_version|vnum) > (.installed_version|vnum)) then {% else %}
(if ($autoup or ($n|IN($inuse[])|not)) if [[ "$installed_version" == "null" || -z "$installed_version" ]]; then
then "\($n) is at version \(.installed_version) latest version is \(.latest_version)... Adding to next update." echo "$package_name is not installed... Adding to next update."
else "skipping available upgrade for in use integration - \($n)." end) jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST
else empty end PENDING_UPDATE=true
' "$INSTALLED_PACKAGE_LIST" else
results=$(compare_versions "$latest_version" "$installed_version")
# (b) The bulk install list, built in a single pass. if [ $results == "greater" ]; then
jq "${JQ_ARGS[@]}" "$JQ_DECISION"' {#- When auto_upgrade_integrations is false, skip upgrading in_use_integrations #}
{packages: [ .packages[] | select(needs($sub;$autoup;$excluded;$inuse)) | {name, version: .latest_version} ]} {%- if not AUTO_UPGRADE_INTEGRATIONS %}
' "$INSTALLED_PACKAGE_LIST" > "$BULK_INSTALL_PACKAGE_LIST" if ! [[ " ${in_use_integrations[@]} " =~ " $package_name " ]]; then
{%- endif %}
if jq -e '.packages | length > 0' "$BULK_INSTALL_PACKAGE_LIST" >/dev/null; then echo "$package_name is at version $installed_version latest version is $latest_version... Adding to next update."
PENDING_UPDATE=true jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST
fi PENDING_UPDATE=true
{%- if not AUTO_UPGRADE_INTEGRATIONS %}
else
echo "skipping available upgrade for in use integration - $package_name."
fi
{%- endif %}
fi
fi
{% endif %}
else
echo "Skipping $package_name..."
fi
done <<< "$(jq -c '.packages[]' "$INSTALLED_PACKAGE_LIST")"
if [ "$PENDING_UPDATE" = true ]; then if [ "$PENDING_UPDATE" = true ]; then
# Run chunked install of packages # Run chunked install of packages
+12
View File
@@ -133,6 +133,18 @@ so-elasticsearch-templates:
- docker_container: so-elasticsearch - docker_container: so-elasticsearch
- file: elasticsearch_sbin_jinja - file: elasticsearch_sbin_jinja
so-elasticsearch-dlm-apply:
cmd.run:
- name: /usr/sbin/so-elasticsearch-dlm-apply
- cwd: /opt/so
- require:
- docker_container: so-elasticsearch
- file: elasticsearch_sbin_jinja
- cmd: so-elasticsearch-templates
- retry:
attempts: 3
interval: 10
so-elasticsearch-pipelines: so-elasticsearch-pipelines:
cmd.run: cmd.run:
- name: /usr/sbin/so-elasticsearch-pipelines {{ GLOBALS.hostname }} - name: /usr/sbin/so-elasticsearch-pipelines {{ GLOBALS.hostname }}
+138 -4
View File
@@ -2,6 +2,7 @@ elasticsearch:
enabled: false enabled: false
version: 9.3.3 version: 9.3.3
index_clean: true index_clean: true
data_retention_method: DLM
vm: vm:
max_map_count: 1048576 max_map_count: 1048576
config: config:
@@ -18,9 +19,18 @@ elasticsearch:
flood_stage: 90% flood_stage: 90%
high: 85% high: 85%
low: 80% low: 80%
# don't want to set retention here since it will make ES restart with every update +
# potentially case where we could unintentially fall back to retention 7d and cause data loss
# data_streams:
# lifecycle:
# retention:
# default: 7d
indices: indices:
id_field_data: id_field_data:
enabled: false enabled: false
# index:
# lifecycle:
# prefer_ilm: true
logger: logger:
org: org:
elasticsearch: elasticsearch:
@@ -63,6 +73,9 @@ elasticsearch:
verification_mode: none verification_mode: none
index_settings: index_settings:
global_overrides: global_overrides:
# Tie this into cluster setting for data_streams.lifecycle.retention.default
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
template: template:
settings: settings:
@@ -143,6 +156,8 @@ elasticsearch:
order: desc order: desc
so-common: so-common:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -304,6 +319,8 @@ elasticsearch:
number_of_shards: 1 number_of_shards: 1
so-assistant-chat: so-assistant-chat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: ""
index_template: index_template:
composed_of: composed_of:
- assistant-chat-mappings - assistant-chat-mappings
@@ -344,6 +361,8 @@ elasticsearch:
min_age: 0ms min_age: 0ms
so-assistant-session: so-assistant-session:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: ""
index_template: index_template:
composed_of: composed_of:
- assistant-session-mappings - assistant-session-mappings
@@ -497,6 +516,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-idh: so-idh:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -605,6 +626,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-import: so-import:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -787,6 +810,8 @@ elasticsearch:
min_age: 0ms min_age: 0ms
so-kismet: so-kismet:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- kismet-mappings - kismet-mappings
@@ -836,6 +861,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-kratos: so-kratos:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -904,6 +931,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-hydra: so-hydra:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -1049,6 +1078,8 @@ elasticsearch:
min_age: 0ms min_age: 0ms
so-logs: so-logs:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- so-data-streams-mappings - so-data-streams-mappings
@@ -1129,6 +1160,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-detections_x_alerts: so-logs-detections_x_alerts:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- so-data-streams-mappings - so-data-streams-mappings
@@ -1192,6 +1225,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent: so-logs-elastic_agent:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -1307,6 +1342,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-elastic-agent-monitor: so-elastic-agent-monitor:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -1369,6 +1406,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_apm_server: so-logs-elastic_agent_x_apm_server:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-elastic_agent.apm_server@package - logs-elastic_agent.apm_server@package
@@ -1433,6 +1472,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_auditbeat: so-logs-elastic_agent_x_auditbeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-elastic_agent.auditbeat@package - logs-elastic_agent.auditbeat@package
@@ -1497,6 +1538,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_cloudbeat: so-logs-elastic_agent_x_cloudbeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-elastic_agent.cloudbeat@package - logs-elastic_agent.cloudbeat@package
@@ -1561,6 +1604,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_endpoint_security: so-logs-elastic_agent_x_endpoint_security:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -1620,6 +1665,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_filebeat: so-logs-elastic_agent_x_filebeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -1679,6 +1726,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_fleet_server: so-logs-elastic_agent_x_fleet_server:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -1735,6 +1784,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_heartbeat: so-logs-elastic_agent_x_heartbeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-elastic_agent.heartbeat@package - logs-elastic_agent.heartbeat@package
@@ -1799,6 +1850,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_metricbeat: so-logs-elastic_agent_x_metricbeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -1858,6 +1911,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_osquerybeat: so-logs-elastic_agent_x_osquerybeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -1917,6 +1972,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elastic_agent_x_packetbeat: so-logs-elastic_agent_x_packetbeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-elastic_agent.packetbeat@package - logs-elastic_agent.packetbeat@package
@@ -1981,6 +2038,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-elasticsearch_x_server: so-logs-elasticsearch_x_server:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-elasticsearch.server@package - logs-elasticsearch.server@package
@@ -2045,6 +2104,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_actions: so-logs-endpoint_x_actions:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- .logs-endpoint.actions@package - .logs-endpoint.actions@package
@@ -2104,6 +2165,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_action_x_responses: so-logs-endpoint_x_action_x_responses:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- .logs-endpoint.action.responses@package - .logs-endpoint.action.responses@package
@@ -2163,6 +2226,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_alerts: so-logs-endpoint_x_alerts:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.alerts@package - logs-endpoint.alerts@package
@@ -2222,6 +2287,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_diagnostic_x_collection: so-logs-endpoint_x_diagnostic_x_collection:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- .logs-endpoint.diagnostic.collection@package - .logs-endpoint.diagnostic.collection@package
@@ -2297,6 +2364,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_events_x_api: so-logs-endpoint_x_events_x_api:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.events.api@package - logs-endpoint.events.api@package
@@ -2356,6 +2425,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_events_x_file: so-logs-endpoint_x_events_x_file:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.events.file@package - logs-endpoint.events.file@package
@@ -2415,6 +2486,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_events_x_library: so-logs-endpoint_x_events_x_library:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.events.library@package - logs-endpoint.events.library@package
@@ -2474,6 +2547,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_events_x_network: so-logs-endpoint_x_events_x_network:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.events.network@package - logs-endpoint.events.network@package
@@ -2533,6 +2608,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_events_x_process: so-logs-endpoint_x_events_x_process:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.events.process@package - logs-endpoint.events.process@package
@@ -2592,6 +2669,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_events_x_registry: so-logs-endpoint_x_events_x_registry:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.events.registry@package - logs-endpoint.events.registry@package
@@ -2651,6 +2730,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_events_x_security: so-logs-endpoint_x_events_x_security:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-endpoint.events.security@package - logs-endpoint.events.security@package
@@ -2710,6 +2791,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-endpoint_x_heartbeat: so-logs-endpoint_x_heartbeat:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- .logs-endpoint.heartbeat@package - .logs-endpoint.heartbeat@package
@@ -2769,6 +2852,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-http_endpoint_x_generic: so-logs-http_endpoint_x_generic:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-http_endpoint.generic@package - logs-http_endpoint.generic@package
@@ -2817,6 +2902,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-httpjson_x_generic: so-logs-httpjson_x_generic:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-httpjson.generic@package - logs-httpjson.generic@package
@@ -2882,6 +2969,8 @@ elasticsearch:
number_of_replicas: 0 number_of_replicas: 0
so-logs-osquery-manager_x_action_x_responses: so-logs-osquery-manager_x_action_x_responses:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
_meta: _meta:
managed: true managed: true
@@ -2953,6 +3042,8 @@ elasticsearch:
number_of_replicas: 0 number_of_replicas: 0
so-logs-osquery-manager_x_result: so-logs-osquery-manager_x_result:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
_meta: _meta:
managed: true managed: true
@@ -3005,6 +3096,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-soc: so-logs-soc:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -3113,6 +3206,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-system_x_application: so-logs-system_x_application:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -3162,6 +3257,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-system_x_auth: so-logs-system_x_auth:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -3211,6 +3308,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-system_x_security: so-logs-system_x_security:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -3260,6 +3359,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-system_x_syslog: so-logs-system_x_syslog:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -3309,6 +3410,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-system_x_system: so-logs-system_x_system:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- event-mappings - event-mappings
@@ -3358,6 +3461,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-windows_x_forwarded: so-logs-windows_x_forwarded:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-windows.forwarded@package - logs-windows.forwarded@package
@@ -3405,6 +3510,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-windows_x_powershell: so-logs-windows_x_powershell:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-windows.powershell@package - logs-windows.powershell@package
@@ -3452,6 +3559,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-windows_x_powershell_operational: so-logs-windows_x_powershell_operational:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-windows.powershell_operational@package - logs-windows.powershell_operational@package
@@ -3499,6 +3608,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-windows_x_sysmon_operational: so-logs-windows_x_sysmon_operational:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-windows.sysmon_operational@package - logs-windows.sysmon_operational@package
@@ -3546,6 +3657,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logs-winlog_x_winlog: so-logs-winlog_x_winlog:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- logs-winlog.winlog@package - logs-winlog.winlog@package
@@ -3594,6 +3707,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-logstash: so-logstash:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -3709,6 +3824,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-metrics-endpoint_x_metadata: so-metrics-endpoint_x_metadata:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- metrics-endpoint.metadata@package - metrics-endpoint.metadata@package
@@ -3756,6 +3873,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-metrics-endpoint_x_metrics: so-metrics-endpoint_x_metrics:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- metrics-endpoint.metrics@package - metrics-endpoint.metrics@package
@@ -3803,6 +3922,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-metrics-endpoint_x_policy: so-metrics-endpoint_x_policy:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- metrics-endpoint.policy@package - metrics-endpoint.policy@package
@@ -3850,6 +3971,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-metrics-fleet_server_x_agent_status: so-metrics-fleet_server_x_agent_status:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- metrics@tsdb-settings - metrics@tsdb-settings
@@ -3874,6 +3997,8 @@ elasticsearch:
number_of_replicas: 0 number_of_replicas: 0
so-metrics-fleet_server_x_agent_versions: so-metrics-fleet_server_x_agent_versions:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- metrics@tsdb-settings - metrics@tsdb-settings
@@ -3898,6 +4023,8 @@ elasticsearch:
number_of_replicas: 0 number_of_replicas: 0
so-redis: so-redis:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -3958,13 +4085,10 @@ elasticsearch:
- vulnerability-mappings - vulnerability-mappings
- common-settings - common-settings
- common-dynamic-mappings - common-dynamic-mappings
- logs-redis.log@package
- logs-redis.log@custom
data_stream: data_stream:
allow_custom_routing: false allow_custom_routing: false
hidden: false hidden: false
ignore_missing_component_templates: ignore_missing_component_templates: []
- logs-redis.log@custom
index_patterns: index_patterns:
- logs-redis.log* - logs-redis.log*
priority: 501 priority: 501
@@ -4016,6 +4140,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-strelka: so-strelka:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -4133,6 +4259,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-suricata: so-suricata:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -4249,6 +4377,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-suricata_x_alerts: so-suricata_x_alerts:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -4365,6 +4495,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-syslog: so-syslog:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
@@ -4481,6 +4613,8 @@ elasticsearch:
min_age: 30d min_age: 30d
so-zeek: so-zeek:
index_sorting: false index_sorting: false
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
composed_of: composed_of:
- agent-mappings - agent-mappings
+390 -61
View File
@@ -4,6 +4,13 @@ elasticsearch:
forcedType: bool forcedType: bool
advanced: True advanced: True
helpLink: elasticsearch helpLink: elasticsearch
data_retention_method:
description: Method for data retention. Options are ILM or DLM. For single node deployments and most distributed grid users, DLM will be the recommended option for simplified management. Those with more complex use cases may prefer ILM. The latter allows for more granular control, but requires more management overhead.
options:
- ILM
- DLM
forcedType: string
global: True
version: version:
description: "This specifies the version of the following containers: so-elastic-fleet-package-registry, so-elastic-agent, so-elastic-fleet, so-kibana, so-logstash and so-elasticsearch. Modifying this value in the Elasticsearch defaults.yaml will result in catastrophic grid failure." description: "This specifies the version of the following containers: so-elastic-fleet-package-registry, so-elastic-agent, so-elastic-fleet, so-kibana, so-logstash and so-elasticsearch. Modifying this value in the Elasticsearch defaults.yaml will result in catastrophic grid failure."
readonly: True readonly: True
@@ -13,7 +20,7 @@ elasticsearch:
description: Specify the memory heap size in (m)egabytes for Elasticsearch. description: Specify the memory heap size in (m)egabytes for Elasticsearch.
helpLink: elasticsearch helpLink: elasticsearch
index_clean: index_clean:
description: Determines if indices should be considered for deletion by available disk space in the cluster. Otherwise, indices will only be deleted by the age defined in the ILM settings. This setting only applies to EVAL, STANDALONE, and HEAVY NODE installations. Other installations can only use ILM settings. description: Determines if indices should be considered for deletion by available disk space in the cluster. Otherwise, data is retained by the configured lifecycle settings. This setting only applies to EVAL, STANDALONE, and HEAVY NODE installations. Other installations use lifecycle settings only.
forcedType: bool forcedType: bool
helpLink: elasticsearch helpLink: elasticsearch
vm: vm:
@@ -139,6 +146,21 @@ elasticsearch:
custom010: *pipelines custom010: *pipelines
index_settings: index_settings:
global_overrides: global_overrides:
data_stream_lifecycle:
data_retention:
description: |
The retention period for all data streams. Retention does not define the period that the data will be removed, but the minimum time period they will be kept.
Use a number followed by a time unit, such as 7d. Leave blank for indefinite retention where supported.
Configured retention period also affects the frequency of rolling over data streams.
- If retention is less than or equal to 1 day, max_age will be 1 hour
- If retention is less than or equal to 14 days, max_age will be 1 day
- If retention is less than or equal to 90 days, max_age will be 7 days
- If retention is greater than 90 days, max_age will be 30 days
forcedType: string
regex: ^$|^[0-9]{1,5}(?:d|h|m|s)$
regexFailureMessage: Must be blank or a number followed by d, h, m, or s, such as 7d.
index_template: index_template:
template: template:
settings: settings:
@@ -311,13 +333,28 @@ elasticsearch:
forcedType: string forcedType: string
global: True global: True
helpLink: elasticsearch helpLink: elasticsearch
so-logs: &indexSettings so-logs: &dataStreamSettings
index_sorting: index_sorting:
description: Sorts the index by event time, at the cost of additional processing resource consumption. description: Sorts the index by event time, at the cost of additional processing resource consumption.
forcedType: bool forcedType: bool
global: True global: True
advanced: True advanced: True
helpLink: elasticsearch helpLink: elasticsearch
data_stream_lifecycle:
data_retention:
description: |
The retention period for this data stream. Retention does not define the period that the data will be removed, but the minimum time period it will be kept.
Use a number followed by a time unit, such as 7d. Leave blank for indefinite retention where supported.
Configured retention period also affects the frequency of rolling over this data stream.
- If retention is less than or equal to 1 day, max_age will be 1 hour
- If retention is less than or equal to 14 days, max_age will be 1 day
- If retention is less than or equal to 90 days, max_age will be 7 days
- If retention is greater than 90 days, max_age will be 30 days
forcedType: string
regex: ^$|^[0-9]{1,5}(?:d|h|m|s)$
regexFailureMessage: Must be blank or a number followed by d, h, m, or s, such as 7d.
index_template: index_template:
index_patterns: index_patterns:
description: Patterns for matching multiple indices or tables. description: Patterns for matching multiple indices or tables.
@@ -335,6 +372,14 @@ elasticsearch:
global: True global: True
advanced: True advanced: True
helpLink: elasticsearch helpLink: elasticsearch
auto_expand_replicas:
description: Automatically expand the number of replicas based on the number of data nodes in the cluster. This can help ensure high availability as the cluster scales up or down.
forcedType: string
regex: "^(0-[1-9]|1-[2-9]|2-[3-9]|3-[4-9]|4-[5-9]|5-[6-9]|6-[7-9]|7-[89]|8-9|[0-9]-all|false)$"
regexFailureMessage: Must be in the format of "x-y" where x is minimum number of replicas and y is maximum number of replicas, or "0-all" to specify a minimum of 0 and no maximum, or "false" to disable automatic replica expansion.
global: True
advanced: True
helpLink: elasticsearch
mapping: mapping:
total_fields: total_fields:
limit: limit:
@@ -596,65 +641,349 @@ elasticsearch:
global: True global: True
advanced: True advanced: True
helpLink: elasticsearch helpLink: elasticsearch
so-logs-system_x_auth: *indexSettings so-logs-system_x_auth: *dataStreamSettings
so-logs-system_x_syslog: *indexSettings so-logs-system_x_syslog: *dataStreamSettings
so-logs-system_x_system: *indexSettings so-logs-system_x_system: *dataStreamSettings
so-logs-system_x_application: *indexSettings so-logs-system_x_application: *dataStreamSettings
so-logs-system_x_security: *indexSettings so-logs-system_x_security: *dataStreamSettings
so-logs-windows_x_forwarded: *indexSettings so-logs-windows_x_forwarded: *dataStreamSettings
so-logs-windows_x_powershell: *indexSettings so-logs-windows_x_powershell: *dataStreamSettings
so-logs-windows_x_powershell_operational: *indexSettings so-logs-windows_x_powershell_operational: *dataStreamSettings
so-logs-windows_x_sysmon_operational: *indexSettings so-logs-windows_x_sysmon_operational: *dataStreamSettings
so-logs-winlog_x_winlog: *indexSettings so-logs-winlog_x_winlog: *dataStreamSettings
so-logs-detections_x_alerts: *indexSettings so-logs-detections_x_alerts: *dataStreamSettings
so-logs-http_endpoint_x_generic: *indexSettings so-logs-http_endpoint_x_generic: *dataStreamSettings
so-logs-httpjson_x_generic: *indexSettings so-logs-httpjson_x_generic: *dataStreamSettings
so-logs-osquery-manager-actions: *indexSettings so-logs-osquery-manager-actions: *dataStreamSettings
so-logs-osquery-manager-action_x_responses: *indexSettings so-logs-osquery-manager-action_x_responses: *dataStreamSettings
so-logs-osquery-manager_x_action_x_responses: *indexSettings so-logs-osquery-manager_x_action_x_responses: *dataStreamSettings
so-logs-osquery-manager_x_result: *indexSettings so-logs-osquery-manager_x_result: *dataStreamSettings
so-logs-elastic_agent_x_apm_server: *indexSettings so-logs-elastic_agent_x_apm_server: *dataStreamSettings
so-logs-elastic_agent_x_auditbeat: *indexSettings so-logs-elastic_agent_x_auditbeat: *dataStreamSettings
so-logs-elastic_agent_x_cloudbeat: *indexSettings so-logs-elastic_agent_x_cloudbeat: *dataStreamSettings
so-logs-elastic_agent_x_endpoint_security: *indexSettings so-logs-elastic_agent_x_endpoint_security: *dataStreamSettings
so-logs-endpoint_x_alerts: *indexSettings so-logs-endpoint_x_alerts: *dataStreamSettings
so-logs-endpoint_x_events_x_api: *indexSettings so-logs-endpoint_x_events_x_api: *dataStreamSettings
so-logs-endpoint_x_events_x_file: *indexSettings so-logs-endpoint_x_events_x_file: *dataStreamSettings
so-logs-endpoint_x_events_x_library: *indexSettings so-logs-endpoint_x_events_x_library: *dataStreamSettings
so-logs-endpoint_x_events_x_network: *indexSettings so-logs-endpoint_x_events_x_network: *dataStreamSettings
so-logs-endpoint_x_events_x_process: *indexSettings so-logs-endpoint_x_events_x_process: *dataStreamSettings
so-logs-endpoint_x_events_x_registry: *indexSettings so-logs-endpoint_x_events_x_registry: *dataStreamSettings
so-logs-endpoint_x_events_x_security: *indexSettings so-logs-endpoint_x_events_x_security: *dataStreamSettings
so-logs-elastic_agent_x_filebeat: *indexSettings so-logs-elastic_agent_x_filebeat: *dataStreamSettings
so-logs-elastic_agent_x_fleet_server: *indexSettings so-logs-elastic_agent_x_fleet_server: *dataStreamSettings
so-logs-elastic_agent_x_heartbeat: *indexSettings so-logs-elastic_agent_x_heartbeat: *dataStreamSettings
so-logs-elastic_agent: *indexSettings so-logs-elastic_agent: *dataStreamSettings
so-logs-elastic_agent_x_metricbeat: *indexSettings so-logs-elastic_agent_x_metricbeat: *dataStreamSettings
so-logs-elastic_agent_x_osquerybeat: *indexSettings so-logs-elastic_agent_x_osquerybeat: *dataStreamSettings
so-logs-elastic_agent_x_packetbeat: *indexSettings so-logs-elastic_agent_x_packetbeat: *dataStreamSettings
so-logs-elasticsearch_x_server: *indexSettings so-logs-elasticsearch_x_server: *dataStreamSettings
so-metrics-endpoint_x_metadata: *indexSettings so-metrics-endpoint_x_metadata: *dataStreamSettings
so-metrics-endpoint_x_metrics: *indexSettings so-metrics-endpoint_x_metrics: *dataStreamSettings
so-metrics-endpoint_x_policy: *indexSettings so-metrics-endpoint_x_policy: *dataStreamSettings
so-metrics-nginx_x_stubstatus: *indexSettings so-metrics-nginx_x_stubstatus: *dataStreamSettings
so-metrics-vsphere_x_datastore: *indexSettings so-metrics-vsphere_x_datastore: *dataStreamSettings
so-metrics-vsphere_x_host: *indexSettings so-metrics-vsphere_x_host: *dataStreamSettings
so-metrics-vsphere_x_virtualmachine: *indexSettings so-metrics-vsphere_x_virtualmachine: *dataStreamSettings
so-case: *indexSettings so-common: *dataStreamSettings
so-common: *indexSettings so-endgame: *dataStreamSettings
so-endgame: *indexSettings so-idh: *dataStreamSettings
so-idh: *indexSettings so-suricata: *dataStreamSettings
so-suricata: *indexSettings so-suricata_x_alerts: *dataStreamSettings
so-suricata_x_alerts: *indexSettings so-import: *dataStreamSettings
so-import: *indexSettings so-kratos: *dataStreamSettings
so-kratos: *indexSettings so-hydra: *dataStreamSettings
so-hydra: *indexSettings so-kismet: *dataStreamSettings
so-kismet: *indexSettings so-logstash: *dataStreamSettings
so-logstash: *indexSettings so-redis: *dataStreamSettings
so-redis: *indexSettings so-strelka: *dataStreamSettings
so-strelka: *indexSettings so-syslog: *dataStreamSettings
so-syslog: *indexSettings so-zeek: *dataStreamSettings
so-zeek: *indexSettings # Managed SOC integration annotations are inserted below this line. Referencing '*dataStreamSettings'
so-case: &indexSettings
index_sorting:
description: Sorts the index by event time, at the cost of additional processing resource consumption.
forcedType: bool
global: True
advanced: True
helpLink: elasticsearch
index_template:
index_patterns:
description: Patterns for matching multiple indices or tables.
forcedType: "[]string"
multiline: True
global: True
advanced: True
helpLink: elasticsearch
template:
settings:
index:
number_of_replicas:
description: Number of replicas required for this index. Multiple replicas protects against data loss, but also increases storage costs.
forcedType: int
global: True
advanced: True
helpLink: elasticsearch
auto_expand_replicas:
description: Automatically expand the number of replicas based on the number of data nodes in the cluster. This can help ensure high availability as the cluster scales up or down.
forcedType: string
regex: "^(0-[1-9]|1-[2-9]|2-[3-9]|3-[4-9]|4-[5-9]|5-[6-9]|6-[7-9]|7-[89]|8-9|[0-9]-all|false)$"
regexFailureMessage: Must be in the format of "x-y" where x is minimum number of replicas and y is maximum number of replicas, or "0-all" to specify a minimum of 0 and no maximum, or "false" to disable automatic replica expansion.
global: True
advanced: True
helpLink: elasticsearch
mapping:
total_fields:
limit:
description: Max number of fields that can exist on a single index. Larger values will consume more resources.
global: True
advanced: True
helpLink: elasticsearch
refresh_interval:
description: Seconds between index refreshes. Shorter intervals can cause query performance to suffer since this is a synchronous and resource-intensive operation.
global: True
advanced: True
helpLink: elasticsearch
number_of_shards:
description: Number of shards required for this index. Using multiple shards increases fault tolerance, but also increases storage and network costs.
global: True
advanced: True
helpLink: elasticsearch
sort:
field:
description: The field to sort by. Must set index_sorting to True.
global: True
advanced: True
helpLink: elasticsearch
order:
description: The order to sort by. Must set index_sorting to True.
global: True
advanced: True
helpLink: elasticsearch
mappings:
_meta:
package:
name:
description: Meta settings for the mapping.
global: True
advanced: True
helpLink: elasticsearch
managed_by:
description: Meta settings for the mapping.
global: True
advanced: True
helpLink: elasticsearch
managed:
description: Meta settings for the mapping.
forcedType: bool
global: True
advanced: True
helpLink: elasticsearch
composed_of:
description: The index template is composed of these component templates.
forcedType: "[]string"
global: True
advanced: True
helpLink: elasticsearch
priority:
description: The priority of the index template.
forcedType: int
global: True
advanced: True
helpLink: elasticsearch
policy:
phases:
hot:
min_age:
description: Minimum age of index. This determines when the index should be moved to the hot tier.
global: True
advanced: True
helpLink: elasticsearch
actions:
set_priority:
priority:
description: Priority of index. This is used for recovery after a node restart. Indices with higher priorities are recovered before indices with lower priorities.
forcedType: int
global: True
advanced: True
helpLink: elasticsearch
rollover:
max_age:
description: Maximum age of index. Once an index reaches this limit, it will be rolled over into a new index.
global: True
advanced: True
helpLink: elasticsearch
max_primary_shard_size:
description: Maximum primary shard size. Once an index reaches this limit, it will be rolled over into a new index.
global: True
advanced: True
helpLink: elasticsearch
shrink:
method:
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
options:
- COUNT
- SIZE
global: True
advanced: True
forcedType: string
number_of_shards:
title: shard count
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
global: True
forcedType: int
advanced: True
max_primary_shard_size:
title: max shard size
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
regex: ^[0-9]+(?:gb|tb|pb)$
global: True
forcedType: string
advanced: True
allow_write_after_shrink:
description: Allow writes after shrink.
global: True
forcedType: bool
default: False
advanced: True
forcemerge:
max_num_segments:
description: Reduce the number of segments in each index shard and clean up deleted documents.
global: True
forcedType: int
advanced: True
index_codec:
title: compression
description: Use higher compression for stored fields at the cost of slower performance.
forcedType: bool
global: True
default: False
advanced: True
warm:
min_age:
description: Minimum age of index. ex. 30d - This determines when the index should be moved to the warm tier. Nodes in the warm tier generally dont need to be as fast as those in the hot tier. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and warm min_age set to 30 then there will be 30 days from index creation to rollover and then an additional 30 days before moving to warm tier.
regex: ^[0-9]{1,5}d$
forcedType: string
global: True
advanced: True
helpLink: elasticsearch
actions:
set_priority:
priority:
description: Priority of index. This is used for recovery after a node restart. Indices with higher priorities are recovered before indices with lower priorities.
forcedType: int
global: True
advanced: True
helpLink: elasticsearch
rollover:
max_age:
description: Maximum age of index. Once an index reaches this limit, it will be rolled over into a new index.
global: True
advanced: True
helpLink: elasticsearch
max_primary_shard_size:
description: Maximum primary shard size. Once an index reaches this limit, it will be rolled over into a new index.
global: True
advanced: True
helpLink: elasticsearch
shrink:
method:
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
options:
- COUNT
- SIZE
global: True
advanced: True
number_of_shards:
title: shard count
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
global: True
forcedType: int
advanced: True
max_primary_shard_size:
title: max shard size
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
regex: ^[0-9]+(?:gb|tb|pb)$
global: True
forcedType: string
advanced: True
allow_write_after_shrink:
description: Allow writes after shrink.
global: True
forcedType: bool
default: False
advanced: True
forcemerge:
max_num_segments:
description: Reduce the number of segments in each index shard and clean up deleted documents.
global: True
forcedType: int
advanced: True
index_codec:
title: compression
description: Use higher compression for stored fields at the cost of slower performance.
forcedType: bool
global: True
default: False
advanced: True
allocate:
number_of_replicas:
description: Set the number of replicas. Remains the same as the previous phase by default.
forcedType: int
global: True
advanced: True
cold:
min_age:
description: Minimum age of index. ex. 60d - This determines when the index should be moved to the cold tier. While still searchable, this tier is typically optimized for lower storage costs rather than search speed. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and cold min_age set to 60 then there will be 30 days from index creation to rollover and then an additional 60 days before moving to cold tier.
regex: ^[0-9]{1,5}d$
forcedType: string
global: True
advanced: True
helpLink: elasticsearch
actions:
set_priority:
priority:
description: Used for index recovery after a node restart. Indices with higher priorities are recovered before indices with lower priorities.
forcedType: int
global: True
advanced: True
helpLink: elasticsearch
allocate:
number_of_replicas:
description: Set the number of replicas. Remains the same as the previous phase by default.
forcedType: int
global: True
advanced: True
delete:
min_age:
description: Minimum age of index. ex. 90d - This determines when the index should be deleted. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and delete min_age set to 90 then there will be 30 days from index creation to rollover and then an additional 90 days before deletion.
regex: ^[0-9]{1,5}d$
forcedType: string
global: True
advanced: True
helpLink: elasticsearch
_meta:
package:
name:
description: Meta settings for the mapping.
global: True
advanced: True
helpLink: elasticsearch
managed_by:
description: Meta settings for the mapping.
global: True
advanced: True
helpLink: elasticsearch
managed:
description: Meta settings for the mapping.
forcedType: bool
global: True
advanced: True
helpLink: elasticsearch
sos-backup: *indexSettings
so-detection: *indexSettings
so-assistant-chat: *indexSettings
so-assistant-session: *indexSettings
so-metrics-fleet_server_x_agent_status: &fleetMetricsSettings so-metrics-fleet_server_x_agent_status: &fleetMetricsSettings
index_sorting: index_sorting:
description: Sorts the index by event time, at the cost of additional processing resource consumption. description: Sorts the index by event time, at the cost of additional processing resource consumption.
+12
View File
@@ -5,6 +5,7 @@
{% import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS %} {% import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS %}
{% set DEFAULT_GLOBAL_OVERRIDES = ELASTICSEARCHDEFAULTS.elasticsearch.index_settings.pop('global_overrides') %} {% set DEFAULT_GLOBAL_OVERRIDES = ELASTICSEARCHDEFAULTS.elasticsearch.index_settings.pop('global_overrides') %}
{% set DATA_RETENTION_METHOD = salt['pillar.get']('elasticsearch:data_retention_method', ELASTICSEARCHDEFAULTS.elasticsearch.get('data_retention_method', 'ILM')) %}
{% set PILLAR_GLOBAL_OVERRIDES = {} %} {% set PILLAR_GLOBAL_OVERRIDES = {} %}
{% set ES_INDEX_PILLAR = salt['pillar.get']('elasticsearch:index_settings', {}) %} {% set ES_INDEX_PILLAR = salt['pillar.get']('elasticsearch:index_settings', {}) %}
@@ -105,6 +106,17 @@
{% if not settings.get('index_sorting', False) | to_bool and settings.index_template.template.settings.index.sort is defined %} {% if not settings.get('index_sorting', False) | to_bool and settings.index_template.template.settings.index.sort is defined %}
{% do settings.index_template.template.settings.index.pop('sort') %} {% do settings.index_template.template.settings.index.pop('sort') %}
{% endif %} {% endif %}
{% if DATA_RETENTION_METHOD == 'DLM' and settings.index_template.data_stream is defined and settings.data_stream_lifecycle is defined %}
{% if settings.data_stream_lifecycle.data_retention is defined and settings.data_stream_lifecycle.data_retention %}
{% do settings.index_template.template.update({'lifecycle': {'data_retention': settings.data_stream_lifecycle.data_retention}}) %}
{% else %}
{% do settings.index_template.template.update({'lifecycle': {}}) %}
{% endif %}
{% if settings.index_template.template.settings.index.lifecycle is not defined %}
{% do settings.index_template.template.settings.index.update({'lifecycle': {}}) %}
{% endif %}
{% do settings.index_template.template.settings.index.lifecycle.update({'prefer_ilm': false}) %}
{% endif %}
{% endif %} {% endif %}
{# advanced ilm actions #} {# advanced ilm actions #}
@@ -11,8 +11,10 @@ ADDON_STATEFILE_SUCCESS=/opt/so/state/addon_estemplates.txt
ELASTICSEARCH_TEMPLATES_DIR="/opt/so/conf/elasticsearch/templates" ELASTICSEARCH_TEMPLATES_DIR="/opt/so/conf/elasticsearch/templates"
SO_TEMPLATES_DIR="${ELASTICSEARCH_TEMPLATES_DIR}/index" SO_TEMPLATES_DIR="${ELASTICSEARCH_TEMPLATES_DIR}/index"
ADDON_TEMPLATES_DIR="${ELASTICSEARCH_TEMPLATES_DIR}/addon-index" ADDON_TEMPLATES_DIR="${ELASTICSEARCH_TEMPLATES_DIR}/addon-index"
FAILED_NAMES=() SO_LOAD_FAILURES=0
FAILED_COUNT=0 ADDON_LOAD_FAILURES=0
SO_LOAD_FAILURES_NAMES=()
ADDON_LOAD_FAILURES_NAMES=()
IS_HEAVYNODE="false" IS_HEAVYNODE="false"
FORCE="false" FORCE="false"
VERBOSE="false" VERBOSE="false"
@@ -44,86 +46,20 @@ while [[ $# -gt 0 ]]; do
shift shift
done done
# Max number of concurrent template PUT jobs. Override via env if needed.
MAX_TEMPLATE_JOBS=${MAX_TEMPLATE_JOBS:-10}
# Block until fewer than MAX_TEMPLATE_JOBS background jobs are running.
template_throttle() {
while (( $(jobs -rp | wc -l) >= MAX_TEMPLATE_JOBS )); do
wait -n
done
}
# Per-job failure markers and an output lock for serializing parallel job output.
# Each failed load drops one file (named after the template) into FAIL_DIR; the
# output of each job is flushed as a single block under flock so concurrent jobs
# never interleave their (chatty) retry output.
FAIL_DIR=$(mktemp -d)
OUTPUT_LOCK="${FAIL_DIR}/.output.lock"
: > "$OUTPUT_LOCK"
trap 'rm -rf "$FAIL_DIR"' EXIT
# Record a failure: $1 = the template name/path to report later. Slashes are
# encoded so the path becomes a safe single filename.
record_failure() {
local marker="${1//\//__}"
: > "${FAIL_DIR}/fail.${marker}"
}
# Populate FAILED_NAMES and FAILED_COUNT from the current phase's markers.
# Must run in the current shell (not a command substitution) so the array sticks.
collect_failures() {
FAILED_NAMES=()
FAILED_COUNT=0
local f name
shopt -s nullglob
for f in "${FAIL_DIR}"/fail.*; do
name="${f##*/fail.}"
name="${name//__//}"
FAILED_NAMES+=("$name")
FAILED_COUNT=$((FAILED_COUNT + 1))
done
shopt -u nullglob
}
# Clear markers and names between phases so SO and addon counts stay independent.
reset_failures() {
shopt -s nullglob
rm -f "${FAIL_DIR}"/fail.*
shopt -u nullglob
FAILED_NAMES=()
FAILED_COUNT=0
}
# Print a block of text atomically (under the shared output lock) so the output
# of concurrent background jobs is not interleaved.
locked_echo() {
{ flock 9; printf '%s\n' "$1"; } 9>>"$OUTPUT_LOCK"
}
# Loads one template file via PUT. Intended to be dispatched as a background job.
# $1 uri - e.g. _component_template/foo or _index_template/foo
# $2 file - path to the template JSON
# $3 report_name - name/path to record if this load fails
load_template() { load_template() {
local uri="$1" local uri="$1"
local file="$2" local file="$2"
local report_name="$3"
local out rc=0 block
# Capture everything (including retry's diagnostic chatter) into one block so echo "Loading template file $file"
# concurrent jobs never interleave; the whole block is flushed under one flock. if ! output=$(retry 3 3 "so-elasticsearch-query $uri -d@$file -XPUT" "{\"acknowledged\":true}"); then
block="Loading template file $file"$'\n' echo "$output"
if ! out=$(retry 3 3 "so-elasticsearch-query $uri -d@$file -XPUT" "{\"acknowledged\":true}" 2>&1); then
block+="$out"$'\n' return 1
rc=1
elif [[ "$VERBOSE" == "true" ]]; then elif [[ "$VERBOSE" == "true" ]]; then
block+="$out"$'\n' echo "$output"
fi fi
{ flock 9; printf '%s' "$block"; } 9>>"$OUTPUT_LOCK"
(( rc != 0 )) && record_failure "$report_name"
} }
check_required_component_template_exists() { check_required_component_template_exists() {
@@ -174,9 +110,6 @@ load_component_templates() {
return return
fi fi
# Dispatch loads as throttled background jobs. The barrier (wait) happens in
# the caller after all component groups have been dispatched, since index
# templates must not load until every component template is in place.
for component in "$pattern"/*.json; do for component in "$pattern"/*.json; do
tmpl_name=$(basename "${component%.json}") tmpl_name=$(basename "${component%.json}")
@@ -185,19 +118,13 @@ load_component_templates() {
tmpl_name="${tmpl_name%-mappings}-mappings" tmpl_name="${tmpl_name%-mappings}-mappings"
fi fi
template_throttle if ! load_template "_component_template/${tmpl_name}" "$component"; then
load_template "_component_template/${tmpl_name}" "$component" "$component" & SO_LOAD_FAILURES=$((SO_LOAD_FAILURES + 1))
SO_LOAD_FAILURES_NAMES+=("$component")
fi
done 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() { index_templates_exist() {
local templates_dir="$1" local templates_dir="$1"
@@ -245,9 +172,6 @@ if [[ "$FORCE" == "true" || ! -f "$SO_STATEFILE_SUCCESS" ]] && index_templates_e
load_component_templates "Elastic Agent" "elastic-agent" load_component_templates "Elastic Agent" "elastic-agent"
load_component_templates "Security Onion" "so" load_component_templates "Security Onion" "so"
# Barrier: every component template PUT must complete before we snapshot the
# component template list and start loading index templates that depend on them.
wait
component_templates=$(so-elasticsearch-component-templates-list) component_templates=$(so-elasticsearch-component-templates-list)
echo -e "Loading Security Onion index templates...\n" echo -e "Loading Security Onion index templates...\n"
for so_idx_tmpl in "${SO_TEMPLATES_DIR}"/*.json; do for so_idx_tmpl in "${SO_TEMPLATES_DIR}"/*.json; do
@@ -257,7 +181,7 @@ if [[ "$FORCE" == "true" || ! -f "$SO_STATEFILE_SUCCESS" ]] && index_templates_e
# TODO: Better way to load only heavynode specific templates # TODO: Better way to load only heavynode specific templates
if ! check_heavynode_compatiable_index_template "$tmpl_name"; then if ! check_heavynode_compatiable_index_template "$tmpl_name"; then
if [[ "$VERBOSE" == "true" ]]; then if [[ "$VERBOSE" == "true" ]]; then
locked_echo "Skipping over $so_idx_tmpl, template is not a heavynode specific index template." echo "Skipping over $so_idx_tmpl, template is not a heavynode specific index template."
fi fi
continue continue
@@ -265,34 +189,32 @@ if [[ "$FORCE" == "true" || ! -f "$SO_STATEFILE_SUCCESS" ]] && index_templates_e
fi fi
if check_required_component_template_exists "$so_idx_tmpl"; then if check_required_component_template_exists "$so_idx_tmpl"; then
template_throttle if ! load_template "_index_template/$tmpl_name" "$so_idx_tmpl"; then
load_template "_index_template/$tmpl_name" "$so_idx_tmpl" "$so_idx_tmpl" & SO_LOAD_FAILURES=$((SO_LOAD_FAILURES + 1))
SO_LOAD_FAILURES_NAMES+=("$so_idx_tmpl")
fi
else else
locked_echo "Skipping over $so_idx_tmpl due to missing required component template(s)." echo "Skipping over $so_idx_tmpl due to missing required component template(s)."
record_failure "$so_idx_tmpl" SO_LOAD_FAILURES=$((SO_LOAD_FAILURES + 1))
SO_LOAD_FAILURES_NAMES+=("$so_idx_tmpl")
continue continue
fi fi
done done
# Barrier: all SO index template PUTs must finish before tallying failures. if [[ $SO_LOAD_FAILURES -eq 0 ]]; then
wait
collect_failures
if [[ $FAILED_COUNT -eq 0 ]]; then
echo "All Security Onion core templates loaded successfully." echo "All Security Onion core templates loaded successfully."
touch "$SO_STATEFILE_SUCCESS" touch "$SO_STATEFILE_SUCCESS"
else else
echo "Encountered $FAILED_COUNT failure(s) loading templates:" echo "Encountered $SO_LOAD_FAILURES failure(s) loading templates:"
for failed_template in "${FAILED_NAMES[@]}"; do for failed_template in "${SO_LOAD_FAILURES_NAMES[@]}"; do
echo " - $failed_template" echo " - $failed_template"
done done
if [[ "$SHOULD_EXIT_ON_FAILURE" == "true" ]]; then if [[ "$SHOULD_EXIT_ON_FAILURE" == "true" ]]; then
fail "Failed to load all Security Onion core templates successfully." fail "Failed to load all Security Onion core templates successfully."
fi fi
fi fi
reset_failures
elif ! index_templates_exist "$SO_TEMPLATES_DIR"; then elif ! index_templates_exist "$SO_TEMPLATES_DIR"; then
echo "No Security Onion core index templates found in ${SO_TEMPLATES_DIR}, skipping." echo "No Security Onion core index templates found in ${SO_TEMPLATES_DIR}, skipping."
elif [[ -f "$SO_STATEFILE_SUCCESS" ]]; then elif [[ -f "$SO_STATEFILE_SUCCESS" ]]; then
@@ -311,27 +233,26 @@ if should_load_addon_templates; then
tmpl_name=$(basename "${addon_idx_tmpl%-template.json}") tmpl_name=$(basename "${addon_idx_tmpl%-template.json}")
if check_required_component_template_exists "$addon_idx_tmpl"; then if check_required_component_template_exists "$addon_idx_tmpl"; then
template_throttle if ! load_template "_index_template/${tmpl_name}" "$addon_idx_tmpl"; then
load_template "_index_template/${tmpl_name}" "$addon_idx_tmpl" "$addon_idx_tmpl" & ADDON_LOAD_FAILURES=$((ADDON_LOAD_FAILURES + 1))
ADDON_LOAD_FAILURES_NAMES+=("$addon_idx_tmpl")
fi
else else
locked_echo "Skipping over $addon_idx_tmpl due to missing required component template(s)." echo "Skipping over $addon_idx_tmpl due to missing required component template(s)."
record_failure "$addon_idx_tmpl" ADDON_LOAD_FAILURES=$((ADDON_LOAD_FAILURES + 1))
ADDON_LOAD_FAILURES_NAMES+=("$addon_idx_tmpl")
continue continue
fi fi
done done
# Barrier: all addon index template PUTs must finish before tallying failures. if [[ $ADDON_LOAD_FAILURES -eq 0 ]]; then
wait
collect_failures
if [[ $FAILED_COUNT -eq 0 ]]; then
echo "All addon integration templates loaded successfully." echo "All addon integration templates loaded successfully."
touch "$ADDON_STATEFILE_SUCCESS" touch "$ADDON_STATEFILE_SUCCESS"
else else
echo "Encountered $FAILED_COUNT failure(s) loading addon integration templates:" echo "Encountered $ADDON_LOAD_FAILURES failure(s) loading addon integration templates:"
for failed_template in "${FAILED_NAMES[@]}"; do for failed_template in "${ADDON_LOAD_FAILURES_NAMES[@]}"; do
echo " - $failed_template" echo " - $failed_template"
done done
if [[ "$SHOULD_EXIT_ON_FAILURE" == "true" ]]; then if [[ "$SHOULD_EXIT_ON_FAILURE" == "true" ]]; then
@@ -0,0 +1,178 @@
#!/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
{%- import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS %}
{%- set DATA_RETENTION_METHOD = salt['pillar.get']('elasticsearch:data_retention_method', ELASTICSEARCHDEFAULTS.elasticsearch.get('data_retention_method', 'ILM')) %}
ELASTICSEARCH_TEMPLATES_DIR="${ELASTICSEARCH_TEMPLATES_DIR:-/opt/so/conf/elasticsearch/templates}"
TEMPLATE_DIRS=(
"${ELASTICSEARCH_TEMPLATES_DIR}/index"
"${ELASTICSEARCH_TEMPLATES_DIR}/addon-index"
)
DATA_RETENTION_METHOD=$(cat <<'EOF'
{{ DATA_RETENTION_METHOD }}
EOF
)
DLM_FAILURES=0
DLM_FAILURE_NAMES=()
if [[ "$DATA_RETENTION_METHOD" != "DLM" && "$DATA_RETENTION_METHOD" != "ILM" ]]; then
echo "Unsupported data retention method $DATA_RETENTION_METHOD. Expected DLM or ILM."
exit 1
fi
validate_template_file() {
local template_file="$1"
if ! jq -e 'type == "object" and (.data_stream == null or (.data_stream | type == "object")) and (.template.lifecycle == null or (.template.lifecycle | type == "object")) and (.template.lifecycle.data_retention == null or (.template.lifecycle.data_retention | type == "string"))' >/dev/null 2>&1 "$template_file"; then
echo "Invalid index template JSON: $template_file"
return 1
fi
}
is_data_stream_template() {
jq -e '.data_stream | type == "object"' >/dev/null 2>&1 "$1"
}
has_data_stream_lifecycle() {
jq -e '.template.lifecycle | type == "object"' >/dev/null 2>&1 "$1"
}
get_data_retention() {
jq -r '.template.lifecycle.data_retention // ""' "$1"
}
find_template_file() {
local template="$1"
local template_dir
local template_file
for template_dir in "${TEMPLATE_DIRS[@]}"; do
template_file="${template_dir}/${template}-template.json"
if [[ -f "$template_file" ]]; then
echo "$template_file"
return 0
fi
done
return 1
}
set_data_stream_lifecycle() {
local data_stream="$1"
local data_retention="$2"
local body
local output
if [[ -n "$data_retention" ]]; then
if jq -e --arg data_stream "$data_stream" --arg data_retention "$data_retention" '.data_streams[]? | select(.name == $data_stream and .lifecycle.enabled == true and .lifecycle.data_retention == $data_retention)' >/dev/null 2>&1 <<< "$data_streams"; then
echo "DLM lifecycle already set for $data_stream with data_retention $data_retention, skipping."
return 0
fi
elif jq -e --arg data_stream "$data_stream" '.data_streams[]? | select(.name == $data_stream and .lifecycle.enabled == true and (.lifecycle.data_retention == null))' >/dev/null 2>&1 <<< "$data_streams"; then
echo "DLM lifecycle already set for $data_stream with indefinite retention, skipping."
return 0
fi
if [[ -n "$data_retention" ]]; then
body=$(jq -cn --arg data_retention "$data_retention" '{data_retention: $data_retention}')
else
# Setting indefinite retention
body='{}'
fi
if ! output=$(so-elasticsearch-query "_data_stream/${data_stream}/_lifecycle" -XPUT -d "$body" --retry 3 --retry-delay 5 --fail); then
echo "Failed to set data stream lifecycle for $data_stream."
echo "$output"
return 1
fi
if [[ -n "$data_retention" ]]; then
echo "Set DLM lifecycle for $data_stream with data_retention $data_retention."
else
echo "Set DLM lifecycle for $data_stream with indefinite retention."
fi
}
disable_data_stream_lifecycle() {
local data_stream="$1"
local body='{"enabled":false}'
local output
if ! jq -e --arg data_stream "$data_stream" '.data_streams[]? | select(.name == $data_stream and .lifecycle != null and .lifecycle.enabled != false)' >/dev/null 2>&1 <<< "$data_streams"; then
# No action needed
return 0
fi
if ! output=$(so-elasticsearch-query "_data_stream/${data_stream}/_lifecycle" -XPUT -d "$body" --retry 3 --retry-delay 5 --fail); then
echo "Failed to disable data stream lifecycle for $data_stream."
echo "$output"
return 1
fi
echo "Disabled DLM lifecycle for $data_stream."
}
process_data_stream() {
local data_stream="$1"
local data_retention="$2"
if [[ "$DATA_RETENTION_METHOD" == "DLM" ]]; then
set_data_stream_lifecycle "$data_stream" "$data_retention"
else
disable_data_stream_lifecycle "$data_stream"
fi
}
check_elasticsearch_responsive
if ! data_streams=$(so-elasticsearch-query "_data_stream?format=json" --retry 3 --retry-delay 5 --fail); then
echo "Failed to retrieve data streams."
exit 1
fi
while read -r data_stream_config; do
data_stream=$(jq -r '.name' <<< "$data_stream_config")
template=$(jq -r '.template' <<< "$data_stream_config")
if ! template_file=$(find_template_file "$template"); then
echo "Skipping $data_stream: index template file not found for $template."
continue
fi
validate_template_file "$template_file" || exit 1
if ! is_data_stream_template "$template_file"; then
echo "Skipping $data_stream: $template_file is not a data stream template."
continue
fi
if [[ "$DATA_RETENTION_METHOD" == "DLM" ]] && ! has_data_stream_lifecycle "$template_file"; then
echo "Skipping $data_stream: $template_file does not define data stream lifecycle."
continue
fi
data_retention=$(get_data_retention "$template_file")
if ! process_data_stream "$data_stream" "$data_retention"; then
DLM_FAILURES=$((DLM_FAILURES + 1))
DLM_FAILURE_NAMES+=("$data_stream")
fi
done < <(jq -c '.data_streams[]' <<< "$data_streams")
if [[ $DLM_FAILURES -eq 0 ]]; then
echo "Data stream lifecycle updates completed successfully."
else
echo "Encountered $DLM_FAILURES failure(s) updating data stream lifecycle:"
for failed_data_stream in "${DLM_FAILURE_NAMES[@]}"; do
echo " - $failed_data_stream"
done
exit 1
fi
@@ -6,37 +6,6 @@
. /usr/sbin/so-common . /usr/sbin/so-common
MAX_JOBS=10
# Lock used to serialize block writes so concurrent jobs never interleave their output.
ILM_OUTPUT_LOCK=$(mktemp)
trap 'rm -f "$ILM_OUTPUT_LOCK"' EXIT
# Policies are loaded concurrently (up to MAX_JOBS at a time) for speed. Each policy's block is
# printed the moment its curl returns, so output appears in COMPLETION ORDER, not the order
# policies are defined in configuration.
echo "Loading ILM policies concurrently; output below appears in completion order, not configuration order."
echo
put_policy() {
local desc="$1" policyname="$2" data="$3" result
result=$(curl -K /opt/so/conf/elasticsearch/curl.config -s -k -L \
-X PUT "https://localhost:9200/_ilm/policy/${policyname}" \
-H 'Content-Type: application/json' -d"${data}")
# curl above ran in parallel; serialize just this block write so concurrent jobs never interleave.
{
flock 200
printf 'Setting up %s policy...\n%s\n\n' "${desc}" "${result}"
} 200>>"${ILM_OUTPUT_LOCK}"
}
# Block until fewer than MAX_JOBS background curls are running.
throttle() {
while (( $(jobs -rp | wc -l) >= MAX_JOBS )); do
wait -n
done
}
{%- from 'elasticsearch/template.map.jinja' import ES_INDEX_SETTINGS %} {%- from 'elasticsearch/template.map.jinja' import ES_INDEX_SETTINGS %}
{%- if GLOBALS.role != "so-heavynode" %} {%- if GLOBALS.role != "so-heavynode" %}
{%- from 'elasticsearch/template.map.jinja' import ALL_ADDON_SETTINGS %} {%- from 'elasticsearch/template.map.jinja' import ALL_ADDON_SETTINGS %}
@@ -45,26 +14,35 @@ throttle() {
{%- for index, settings in ES_INDEX_SETTINGS.items() %} {%- for index, settings in ES_INDEX_SETTINGS.items() %}
{%- if settings.policy is defined %} {%- if settings.policy is defined %}
{%- if index == 'so-logs-detections.alerts' %} {%- if index == 'so-logs-detections.alerts' %}
throttle echo
put_policy "so-logs-detections.alerts-so" "{{ index }}-so" '{ "policy": {{ settings.policy | tojson(true) }} }' & echo "Setting up so-logs-detections.alerts-so policy..."
curl -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -s -k -L -X PUT "https://localhost:9200/_ilm/policy/{{ index }}-so" -H 'Content-Type: application/json' -d'{ "policy": {{ settings.policy | tojson(true) }} }'
echo
{%- elif index == 'so-logs-soc' %} {%- elif index == 'so-logs-soc' %}
throttle echo
put_policy "so-soc-logs" "so-soc-logs" '{ "policy": {{ settings.policy | tojson(true) }} }' & echo "Setting up so-soc-logs policy..."
throttle curl -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -s -k -L -X PUT "https://localhost:9200/_ilm/policy/so-soc-logs" -H 'Content-Type: application/json' -d'{ "policy": {{ settings.policy | tojson(true) }} }'
put_policy "{{ index }}-logs" "{{ index }}-logs" '{ "policy": {{ settings.policy | tojson(true) }} }' & echo
echo
echo "Setting up {{ index }}-logs policy..."
curl -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -s -k -L -X PUT "https://localhost:9200/_ilm/policy/{{ index }}-logs" -H 'Content-Type: application/json' -d'{ "policy": {{ settings.policy | tojson(true) }} }'
echo
{%- else %} {%- else %}
throttle echo
put_policy "{{ index }}-logs" "{{ index }}-logs" '{ "policy": {{ settings.policy | tojson(true) }} }' & echo "Setting up {{ index }}-logs policy..."
curl -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -s -k -L -X PUT "https://localhost:9200/_ilm/policy/{{ index }}-logs" -H 'Content-Type: application/json' -d'{ "policy": {{ settings.policy | tojson(true) }} }'
echo
{%- endif %} {%- endif %}
{%- endif %} {%- endif %}
{%- endfor %} {%- endfor %}
echo
{%- if GLOBALS.role != "so-heavynode" %} {%- if GLOBALS.role != "so-heavynode" %}
{%- for index, settings in ALL_ADDON_SETTINGS.items() %} {%- for index, settings in ALL_ADDON_SETTINGS.items() %}
{%- if settings.policy is defined %} {%- if settings.policy is defined %}
throttle echo
put_policy "{{ index }}-logs" "{{ index }}-logs" '{ "policy": {{ settings.policy | tojson(true) }} }' & echo "Setting up {{ index }}-logs policy..."
curl -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -s -k -L -X PUT "https://localhost:9200/_ilm/policy/{{ index }}-logs" -H 'Content-Type: application/json' -d'{ "policy": {{ settings.policy | tojson(true) }} }'
echo
{%- endif %} {%- endif %}
{%- endfor %} {%- endfor %}
{%- endif %} {%- endif %}
wait
+27 -32
View File
@@ -16,40 +16,35 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
{% set soc_annotation_lines = [] %}
{% set defaults_lines = [] %}
{% for k in matched_integration_names %}
{% do soc_annotation_lines.append(' ' ~ k ~ ': *dataStreamSettings') %}
{% do defaults_lines.append(' ' ~ k ~ ':') %}
{% set defaults_yaml = salt['slsutil.serialize']('yaml', ADDON_INTEGRATION_DEFAULTS[k], default_flow_style=False).strip() %}
{% for line in defaults_yaml.splitlines() %}
{% do defaults_lines.append(' ' ~ line) %}
{% endfor %}
{% endfor %}
{% set es_soc_annotations = '/opt/so/saltstack/default/salt/elasticsearch/soc_elasticsearch.yaml' %} {% set es_soc_annotations = '/opt/so/saltstack/default/salt/elasticsearch/soc_elasticsearch.yaml' %}
{{ es_soc_annotations }}: manage_soc_annotations:
file.serialize: file.blockreplace:
- dataset: - name: {{ es_soc_annotations }}
{% set data = salt['file.read'](es_soc_annotations) | load_yaml %} - marker_start: ' # START managed SOC integration annotations'
{% set es = data.get('elasticsearch', {}) %} - marker_end: ' # END managed SOC integration annotations'
{% set index_settings = es.get('index_settings', {}) %} - content: {{ soc_annotation_lines | join('\n') | tojson }}
{% set input = index_settings.get('so-logs', {}) %} - insert_after_match: '^ # Managed SOC integration annotations are inserted below this line\.'
{% for k in matched_integration_names %} - append_if_not_found: False
{% do index_settings.update({k: input}) %} - show_changes: True
{% endfor %}
{% for k in addon_integration_keys %}
{% if k not in matched_integration_names and k in index_settings %}
{% do index_settings.pop(k) %}
{% endif %}
{% endfor %}
{{ data }}
{# Managed elasticsearch/defaults.yaml file for enabling 'Revert to default' via SOC UI for newly added config items #} {# Managed elasticsearch/defaults.yaml file for enabling 'Revert to default' via SOC UI for newly added config items #}
{% set es_defaults = '/opt/so/saltstack/default/salt/elasticsearch/defaults.yaml' %} {% set es_defaults = '/opt/so/saltstack/default/salt/elasticsearch/defaults.yaml' %}
{{ es_defaults }}: {{ es_defaults }}:
file.serialize: file.blockreplace:
- dataset: - marker_start: ' # START managed SOC integration defaults'
{% set data = salt['file.read'](es_defaults) | load_yaml %} - marker_end: ' # END managed SOC integration defaults'
{% set es = data.get('elasticsearch', {}) %} - content: {{ defaults_lines | join('\n') | tojson }}
{% set index_settings = es.get('index_settings', {}) %} - insert_after_match: '^ index_settings:$'
{% for k in matched_integration_names %} - append_if_not_found: False
{% set input = ADDON_INTEGRATION_DEFAULTS[k] %} - show_changes: True
{% do index_settings.update({k: input})%} {% endif %}
{% endfor %}
{% for k in addon_integration_keys %}
{% if k not in matched_integration_names and k in index_settings %}
{% do index_settings.pop(k) %}
{% endif %}
{% endfor %}
{{ data }}
{% endif %}
-117
View File
@@ -1,117 +0,0 @@
#!/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.
# Runs once per boot on managers (via so-boot-mine-update.service), before
# so-boot-highstate.service. Waits for the responsive minion set to settle, pushes
# mine.update, waits until every up minion has actually reported to the mine, then
# warms the master's per-minion pillar cache so the mine-backed node pillars (node
# IPs, ES/Redis/Logstash/hypervisor discovery -- some glob- and some pillar/grain-
# targeted) are complete before the boot highstate renders them. Otherwise a node
# that is up but not yet fully reported gets dropped from those pillars and torn
# out of the configs they build (e.g. so-elasticsearch ExtraHosts -> container recreate).
MAX_WAIT=${MINE_UPDATE_MAX_WAIT:-180} # hard backstop only
INTERVAL=10
STABLE_CHECKS=3 # up-count must hold steady this many polls
elapsed=0
prev=-1
stable=0
up=0
# Wait for the *reachable* minion set to settle rather than for every accepted
# key to report up: an operator may accept a minion's key and then intentionally
# power off that host, so requiring up >= accepted would never be satisfied and
# we'd always burn the full MAX_WAIT. Once the responsive count stops growing we
# stop waiting and run mine.update against whoever is up.
while [ "$elapsed" -lt "$MAX_WAIT" ]; do
up=$(/usr/bin/salt-run manage.up --out=json 2>/dev/null \
| python3 -c 'import sys,json; print(len(json.load(sys.stdin)))' 2>/dev/null)
up=${up:-0}
if [ "$up" -gt 0 ] && [ "$up" -eq "$prev" ]; then
stable=$((stable + 1))
[ "$stable" -ge "$STABLE_CHECKS" ] && break
else
stable=0
fi
prev=$up
sleep "$INTERVAL"
elapsed=$((elapsed + INTERVAL))
done
echo "so-boot-mine-update: ${up} minions up (settled after ${elapsed}s); running mine.update"
/usr/bin/salt '*' mine.update --out=txt
# A node that is up but has not yet re-reported network.ip_addrs to the mine is
# silently dropped from mine-backed pillars (elasticsearch:nodes, node_data, ...)
# when highstate recompiles them -- which e.g. removes it from so-elasticsearch
# ExtraHosts and forces a container recreate. After the broad mine.update above,
# wait until every up minion actually has network.ip_addrs in the mine, re-pushing
# mine.update to stragglers, before releasing the boot highstate. Bounded by the
# same MAX_WAIT backstop so a slow/down node never blocks boot indefinitely.
missing=""
while [ "$elapsed" -lt "$MAX_WAIT" ]; do
up_json=$(/usr/bin/salt-run manage.up --out=json 2>/dev/null)
mine_json=$(/usr/bin/salt-run mine.get '*' network.ip_addrs tgt_type=glob --out=json 2>/dev/null)
missing=$(printf '%s' "$up_json" | python3 -c '
import sys, json
up = set(json.load(sys.stdin) or [])
mine = {k for k, v in (json.loads(sys.argv[1]) or {}).items() if v}
print("\n".join(sorted(up - mine)))
' "$mine_json" 2>/dev/null)
if [ -z "$missing" ]; then
echo "so-boot-mine-update: mine complete for all up minions after ${elapsed}s"
break
fi
echo "so-boot-mine-update: mine missing up minion(s): $(echo $missing); re-running mine.update"
for m in $missing; do /usr/bin/salt "$m" mine.update --out=txt; done
sleep "$INTERVAL"
elapsed=$((elapsed + INTERVAL))
done
[ -n "$missing" ] && echo "so-boot-mine-update: WARNING ${MAX_WAIT}s backstop hit; up minion(s) still absent from mine: $(echo $missing); highstate may drop them from configs"
# The pillar/compound-targeted node pillars (elasticsearch:nodes, redis:nodes,
# logstash:nodes, hypervisor:nodes) resolve their target against the master's
# per-minion data cache (grains+pillar in .../minions/<id>/data.p), populated only
# when a minion's pillar is (re)compiled -- separately from the mine. A freshly
# booted node can be in the mine (glob/node_data sees it) yet absent from that
# cache, so it is dropped from those pillars and from the configs they build (e.g.
# so-elasticsearch ExtraHosts). Force a synchronous pillar refresh so the master
# caches every up node's pillar; refresh_pillar wait=True returns only once the
# pillar is recompiled (and thus cached for matching). Retry stragglers <= MAX_WAIT.
echo "so-boot-mine-update: warming master pillar cache for pillar/grain-targeted node pillars"
/usr/bin/salt '*' saltutil.refresh_pillar wait=True --out=txt
missing=""
while [ "$elapsed" -lt "$MAX_WAIT" ]; do
up_json=$(/usr/bin/salt-run manage.up --out=json 2>/dev/null)
cached_json=$(/usr/bin/salt-run cache.pillar tgt='*' --out=json 2>/dev/null)
missing=$(printf '%s' "$up_json" | python3 -c '
import sys, json
up = set(json.load(sys.stdin) or [])
cached = {k for k, v in (json.loads(sys.argv[1]) or {}).items() if v}
print("\n".join(sorted(up - cached)))
' "$cached_json" 2>/dev/null)
if [ -z "$missing" ]; then
echo "so-boot-mine-update: pillar cache warm for all up minions after ${elapsed}s"
break
fi
echo "so-boot-mine-update: pillar not yet cached for: $(echo $missing); refreshing"
for m in $missing; do /usr/bin/salt "$m" saltutil.refresh_pillar wait=True --out=txt; done
sleep "$INTERVAL"
elapsed=$((elapsed + INTERVAL))
done
[ -n "$missing" ] && echo "so-boot-mine-update: WARNING ${MAX_WAIT}s backstop hit; pillar not cached for: $(echo $missing); pillar-targeted pillars may drop them"
# Log what the mine-backed pillars render so the boot-time state is inspectable.
/usr/bin/salt-call saltutil.refresh_pillar >/dev/null 2>&1
sleep 2
for key in node_data elasticsearch:nodes; do
rendered=$(/usr/bin/salt-call --out=json pillar.get "$key" 2>/dev/null \
| python3 -c 'import sys,json; print(json.dumps(json.load(sys.stdin).get("local"), indent=2, sort_keys=True))' 2>/dev/null)
echo "so-boot-mine-update: ${key} rendered as:"
echo "${rendered:-null}"
done
exit 0
+54 -5
View File
@@ -343,11 +343,10 @@ highstate() {
masterlock() { masterlock() {
echo "Locking Salt Master" echo "Locking Salt Master"
mv -v $TOPFILE $BACKUPTOPFILE mv -v $TOPFILE $BACKUPTOPFILE
# Render the real top file only for the host running soup; every other echo "base:" > $TOPFILE
# minion gets an empty top (no states) while the master is upgrading. echo " $MINIONID:" >> $TOPFILE
echo "{% if grains['id'] == '$MINIONID' %}" > $TOPFILE echo " - ca" >> $TOPFILE
cat $BACKUPTOPFILE >> $TOPFILE echo " - elasticsearch" >> $TOPFILE
echo "{% endif %}" >> $TOPFILE
} }
masterunlock() { masterunlock() {
@@ -762,9 +761,57 @@ bootstrap_so_soc_database() {
echo "so_soc bootstrap complete." echo "so_soc bootstrap complete."
} }
# Existing grids should keep ILM unless an admin explicitly opts in to DLM.
pin_elasticsearch_data_retention_method() {
local elasticsearch_file=/opt/so/saltstack/local/pillar/elasticsearch/soc_elasticsearch.sls
mkdir -p "$(dirname "$elasticsearch_file")"
[[ -f "$elasticsearch_file" ]] || touch "$elasticsearch_file"
if so-yaml.py get -r "$elasticsearch_file" elasticsearch.data_retention_method >/dev/null 2>&1; then
echo "elasticsearch.data_retention_method already set; leaving as-is."
return 0
fi
echo "Pinning existing grid to ILM data retention."
so-yaml.py add "$elasticsearch_file" elasticsearch.data_retention_method ILM
chown socore:socore "$elasticsearch_file"
}
# Addes auto_expand_replicas setting to .kibana_streams index template
#
# In Kibana 9.3.3 the auto_expand_replicas setting was not added to the .kibana_streams index template. Causing single node deployments to be stuck in yellow state (unable to assign replica). Here we update the template in place using the so_kibana system user (system managed index template) to include the auto_expand_replicas setting
#
# Reference: https://github.com/elastic/kibana/issues/263048
kibana_backport_streams_index_template() {
local current_template updated_template
current_template=$(so-elasticsearch-query "_index_template/.kibana_streams" --retry 3 --retry-delay 5 --fail)
if [[ -z "$current_template" ]]; then
echo "Unable to retrieve current .kibana_streams index template, skipping backport."
return 0
fi
updated_template=$(jq '.index_templates[0].index_template | .template.settings += {"index.auto_expand_replicas": "0-1"} | del(.created_date_millis, .modified_date_millis)' <<< "$current_template")
if ! kibana_user_pass=$(/usr/sbin/so-yaml.py get -r /opt/so/saltstack/local/pillar/elasticsearch/auth.sls elasticsearch.auth.users.so_kibana_user.pass); then
echo "Unable to retrieve so_kibana_user password, skipping .kibana_streams index template backport."
return 0
fi
if ! so-elasticsearch-query "_index_template/.kibana_streams" -XPUT -d "$updated_template" -u "so_kibana:$kibana_user_pass" --retry 3 --retry-delay 5 --fail; then
echo "Unable to automatically update .kibana_streams index template"
return 0
fi
## NOTE: Should really add a check here for existing .kibana_streams index and then update its config in place
}
up_to_3.2.0() { up_to_3.2.0() {
fix_logstash_0013_lumberjack_pipeline_name fix_logstash_0013_lumberjack_pipeline_name
pin_elasticsearch_data_retention_method
INSTALLEDVERSION=3.2.0 INSTALLEDVERSION=3.2.0
} }
@@ -775,6 +822,8 @@ post_to_3.2.0() {
echo "Regenerating Elastic Agent Installers" echo "Regenerating Elastic Agent Installers"
/sbin/so-elastic-agent-gen-installers /sbin/so-elastic-agent-gen-installers
kibana_backport_streams_index_template
POSTVERSION=3.2.0 POSTVERSION=3.2.0
} }
-1
View File
@@ -14,7 +14,6 @@
include: include:
- salt.minion - salt.minion
- salt.master.boot_mine_update
{% if 'vrt' in salt['pillar.get']('features', []) %} {% if 'vrt' in salt['pillar.get']('features', []) %}
- salt.cloud - salt.cloud
- salt.cloud.reactor_config_hypervisor - salt.cloud.reactor_config_hypervisor
-29
View File
@@ -1,29 +0,0 @@
# 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.
# Manages /etc/systemd/system/so-boot-mine-update.service, a manager-only
# Type=oneshot unit that pushes `salt '*' mine.update` once per boot, ordered
# before so-boot-highstate.service so mine-backed pillars (node IPs, ES/Redis/
# Logstash discovery) are fresh before the boot highstate renders them.
include:
- systemd.reload
so_boot_mine_update_unit_file:
file.managed:
- name: /etc/systemd/system/so-boot-mine-update.service
- source: salt://salt/service/so-boot-mine-update.service
- onchanges_in:
- module: systemd_reload
# Only enable once setup is complete. Until then the gate file is missing and
# the unit's own ConditionPathExists would no-op it anyway.
so_boot_mine_update_service:
service.enabled:
- name: so-boot-mine-update.service
- onlyif: test -e /opt/so/state/setup-complete
- require:
- file: so_boot_mine_update_unit_file
- module: systemd_reload
@@ -1,15 +0,0 @@
[Unit]
Description=Security Onion boot-time grid mine.update (managers, runs once per boot before highstate)
After=salt-master.service salt-minion.service network-online.target
Wants=network-online.target
Requires=salt-master.service salt-minion.service
Before=so-boot-highstate.service
ConditionPathExists=/opt/so/state/setup-complete
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/so-boot-mine-update
[Install]
WantedBy=multi-user.target
-2
View File
@@ -980,8 +980,6 @@ docker_seed_registry() {
docker_seed_update_percent=25 docker_seed_update_percent=25
update_docker_containers 'netinstall' '' 'docker_seed_update' '/dev/stdout' 2>&1 | tee -a "$setup_log" update_docker_containers 'netinstall' '' 'docker_seed_update' '/dev/stdout' 2>&1 | tee -a "$setup_log"
# Use pipe exit status of 'update_docker_containers' for return code
return ${PIPESTATUS[0]}
fi fi
} }
+1 -6
View File
@@ -223,8 +223,6 @@ if [ -n "$test_profile" ]; then
WEBPASSWD1=0n10nus3r WEBPASSWD1=0n10nus3r
WEBPASSWD2=0n10nus3r WEBPASSWD2=0n10nus3r
NODE_DESCRIPTION="${HOSTNAME} - ${install_type} - ${MSRVIP_OFFSET}" NODE_DESCRIPTION="${HOSTNAME} - ${install_type} - ${MSRVIP_OFFSET}"
# opt out of telemetry for automated testing
telemetry=1
update_sudoers_for_testing update_sudoers_for_testing
fi fi
@@ -769,10 +767,7 @@ if ! [[ -f $install_opt_file ]]; then
title "Applying the registry state" title "Applying the registry state"
logCmd "salt-call state.apply -l info registry" logCmd "salt-call state.apply -l info registry"
title "Seeding the docker registry" title "Seeding the docker registry"
if ! docker_seed_registry; then docker_seed_registry
error "Failed to seed the docker registry"
fail_setup
fi
title "Applying the manager state" title "Applying the manager state"
logCmd "salt-call state.apply -l info manager" logCmd "salt-call state.apply -l info manager"
logCmd "salt-call state.apply influxdb -l info" logCmd "salt-call state.apply influxdb -l info"