Compare commits

..

1 Commits

Author SHA1 Message Date
Mike Reeves 213afe4875 oracle 10 testing 2026-06-03 14:11:30 -04:00
83 changed files with 794 additions and 2548 deletions
View File
-1
View File
@@ -11,7 +11,6 @@ body:
- -
- 3.0.0 - 3.0.0
- 3.1.0 - 3.1.0
- 3.2.0
- Other (please provide detail below) - Other (please provide detail below)
validations: validations:
required: true required: true
+1 -1
View File
@@ -1 +1 @@
20260528
+1 -1
View File
@@ -1 +1 @@
3.2.0 3.1.0
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# This script adds sensors/nodes/etc to the nodes tab
default_salt_dir=/opt/so/saltstack/default
local_salt_dir=/opt/so/saltstack/local
TYPE=$1
NAME=$2
IPADDRESS=$3
CPUS=$4
GUID=$5
MANINT=$6
ROOTFS=$7
NSM=$8
MONINT=$9
#NODETYPE=$10
#HOTNAME=$11
echo "Seeing if this host is already in here. If so delete it"
if grep -q $NAME "$local_salt_dir/pillar/data/$TYPE.sls"; then
echo "Node Already Present - Let's re-add it"
awk -v blah=" $NAME:" 'BEGIN{ print_flag=1 }
{
if( $0 ~ blah )
{
print_flag=0;
next
}
if( $0 ~ /^ [a-zA-Z0-9]+:$/ )
{
print_flag=1;
}
if ( print_flag == 1 )
print $0
} ' $local_salt_dir/pillar/data/$TYPE.sls > $local_salt_dir/pillar/data/tmp.$TYPE.sls
mv $local_salt_dir/pillar/data/tmp.$TYPE.sls $local_salt_dir/pillar/data/$TYPE.sls
echo "Deleted $NAME from the tab. Now adding it in again with updated info"
fi
echo " $NAME:" >> $local_salt_dir/pillar/data/$TYPE.sls
echo " ip: $IPADDRESS" >> $local_salt_dir/pillar/data/$TYPE.sls
echo " manint: $MANINT" >> $local_salt_dir/pillar/data/$TYPE.sls
echo " totalcpus: $CPUS" >> $local_salt_dir/pillar/data/$TYPE.sls
echo " guid: $GUID" >> $local_salt_dir/pillar/data/$TYPE.sls
echo " rootfs: $ROOTFS" >> $local_salt_dir/pillar/data/$TYPE.sls
echo " nsmfs: $NSM" >> $local_salt_dir/pillar/data/$TYPE.sls
if [ $TYPE == 'sensorstab' ]; then
echo " monint: bond0" >> $local_salt_dir/pillar/data/$TYPE.sls
fi
if [ $TYPE == 'evaltab' ] || [ $TYPE == 'standalonetab' ]; then
echo " monint: bond0" >> $local_salt_dir/pillar/data/$TYPE.sls
if [ ! $10 ]; then
salt-call state.apply utility queue=True
fi
fi
if [ $TYPE == 'nodestab' ]; then
salt-call state.apply elasticsearch queue=True
# echo " nodetype: $NODETYPE" >> $local_salt_dir/pillar/data/$TYPE.sls
# echo " hotname: $HOTNAME" >> $local_salt_dir/pillar/data/$TYPE.sls
fi
+2 -1
View File
@@ -37,7 +37,8 @@
'elasticfleet', 'elasticfleet',
'elasticfleet.manager', 'elasticfleet.manager',
'elasticsearch.cluster', 'elasticsearch.cluster',
'elastic-fleet-package-registry' 'elastic-fleet-package-registry',
'utility'
] %} ] %}
{% set sensor_states = [ {% set sensor_states = [
@@ -25,11 +25,9 @@ if [ ! -f $BACKUPFILE ]; then
# Create empty backup file # Create empty backup file
tar -cf $BACKUPFILE -T /dev/null tar -cf $BACKUPFILE -T /dev/null
# Loop through all paths defined in global.sls, and append them to backup file if they exist # Loop through all paths defined in global.sls, and append them to backup file
{%- for LOCATION in BACKUPLOCATIONS %} {%- for LOCATION in BACKUPLOCATIONS %}
if [[ -d {{ LOCATION }} || -f {{ LOCATION }} ]]; then tar -rf $BACKUPFILE "${EXCLUSIONS[@]}" {{ LOCATION }}
tar -rf $BACKUPFILE "${EXCLUSIONS[@]}" {{ LOCATION }}
fi
{%- endfor %} {%- endfor %}
fi fi
-11
View File
@@ -130,17 +130,6 @@ common_sbin:
- so-pcap-import - so-pcap-import
{% endif %} {% endif %}
# Pin physical NIC names by MAC (run-once) so a kernel upgrade can't renumber the
# interfaces SO binds by name. The marker keeps it a one-time setup; an admin can
# pre-create the marker to opt out.
pin_nic_names:
cmd.run:
- name: /usr/sbin/so-nic-pin
- unless: 'test -e /opt/so/state/nic_names_pinned'
- require:
- file: common_sbin
- file: statedir
common_sbin_jinja: common_sbin_jinja:
file.recurse: file.recurse:
- name: /usr/sbin - name: /usr/sbin
+20 -1
View File
@@ -26,14 +26,33 @@ commonpkgs:
- net-tools - net-tools
- nmap-ncat - nmap-ncat
- procps-ng - procps-ng
{# OL10 test path: python3-docker / python3-m2crypto are not packaged in EPEL 10 and are not
referenced by SO code (salt uses its bundled docker module from salt/python_modules.sls).
python3-rich is also unavailable on EL10 (its pygments dep is not packaged), so it is
installed via pip below. Gate on the grain because GLOBALS/pillars are not available this
early (see header note). #}
{% if grains['osmajorrelease']|int < 10 %}
- python3-docker - python3-docker
- python3-m2crypto - python3-m2crypto
- python3-rich
{% else %}
- python3-pip
{% endif %}
- python3-packaging - python3-packaging
- python3-pyyaml - python3-pyyaml
- python3-rich
- rsync - rsync
- sqlite - sqlite
- tcpdump - tcpdump
- unzip - unzip
- wget - wget
- yum-utils - yum-utils
{% if grains['osmajorrelease']|int >= 10 %}
# OL10 test path: rich is not packaged for EL10; install it into the system python3 for so-status.
commonpkgs_pip_rich:
cmd.run:
- name: python3 -m pip install rich
- unless: python3 -c "import rich"
- require:
- pkg: commonpkgs
{% endif %}
+8 -22
View File
@@ -142,11 +142,6 @@ 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}"
@@ -291,20 +286,6 @@ download_and_verify() {
fi fi
} }
# check if container with name is running and optionally stop it
docker_check_running() {
# show running containers, only names
if docker ps --format '{{.Names}}' | grep -q "^so-${1}$"; then
if [[ "$2" == "--stop" ]]; then
docker stop "so-${1}"
fi
return 0
else
return 1
fi
}
elastic_license() { elastic_license() {
read -r -d '' message <<- EOM read -r -d '' message <<- EOM
@@ -373,7 +354,12 @@ gpg_rpm_import() {
else else
local RPMKEYSLOC="$UPDATE_DIR/salt/repo/client/files/$OS/keys" local RPMKEYSLOC="$UPDATE_DIR/salt/repo/client/files/$OS/keys"
fi fi
RPMKEYS=('RPM-GPG-KEY-oracle' 'RPM-GPG-KEY-EPEL-9' 'SALT-PROJECT-GPG-PUBKEY-2023.pub' 'docker.pub' 'securityonion.pub') if [[ "$OSVER" == "10" ]]; then
# OL10 test path uses public repos; the public oracle-epel-release and docker repos provide their own keys
RPMKEYS=('RPM-GPG-KEY-oracle' 'SALT-PROJECT-GPG-PUBKEY-2023.pub')
else
RPMKEYS=('RPM-GPG-KEY-oracle' 'RPM-GPG-KEY-EPEL-9' 'SALT-PROJECT-GPG-PUBKEY-2023.pub' 'docker.pub' 'securityonion.pub')
fi
for RPMKEY in "${RPMKEYS[@]}"; do for RPMKEY in "${RPMKEYS[@]}"; do
rpm --import $RPMKEYSLOC/$RPMKEY rpm --import $RPMKEYSLOC/$RPMKEY
echo "Imported $RPMKEY" echo "Imported $RPMKEY"
@@ -645,9 +631,9 @@ salt_minion_count() {
} }
set_os() { set_os() {
if [ -f /etc/redhat-release ] && grep -q "Red Hat Enterprise Linux release 9" /etc/redhat-release && [ -f /etc/oracle-release ]; then if [ -f /etc/oracle-release ] && grep -qE "release (9|10)\b" /etc/oracle-release; then
OS=oracle OS=oracle
OSVER=9 OSVER=$(grep -oE "release [0-9]+" /etc/oracle-release | grep -oE "[0-9]+")
is_oracle=true is_oracle=true
is_rpm=true is_rpm=true
fi fi
+17 -2
View File
@@ -112,8 +112,23 @@ update_docker_containers() {
# does not include so-elastic-fleet since that container uses so-elastic-agent image # does not include so-elastic-fleet since that container uses so-elastic-agent image
local IMAGES_USING_ES_VERSION=("so-elasticsearch") local IMAGES_USING_ES_VERSION=("so-elasticsearch")
rm -rf $SIGNPATH >> "$LOG_FILE" 2>&1 rm -rf $SIGNPATH >> "$LOG_FILE" 2>&1
mkdir -p $SIGNPATH >> "$LOG_FILE" 2>&1 mkdir -p $SIGNPATH >> "$LOG_FILE" 2>&1
# OL10 test path: GnuPG 2.4 enables the keybox daemon (keyboxd) by default, which deadlocks
# under the rapid sequential gpg --verify calls below ("waiting for lock ... keydb_search
# failed: Connection timed out ... No public key"). Editing the default homedir's common.conf
# is unreliable (gpg re-adds use-keyboxd when it re-initializes the homedir), so run all the
# image-signature gpg ops in a dedicated homedir whose pre-written common.conf leaves keyboxd
# off, forcing the classic keybox. Isolated from the system keyring and deterministic.
if [ "$OSVER" = "10" ]; then
export GNUPGHOME="$SIGNPATH/gnupg"
rm -rf "$GNUPGHOME" >> "$LOG_FILE" 2>&1
mkdir -p "$GNUPGHOME" >> "$LOG_FILE" 2>&1
chmod 700 "$GNUPGHOME"
echo "# keyboxd disabled for SO image signature verification on EL10" > "$GNUPGHOME/common.conf"
gpgconf --kill keyboxd gpg-agent >> "$LOG_FILE" 2>&1 || true
fi
# Let's make sure we have the public key # Let's make sure we have the public key
run_check_net_err \ run_check_net_err \
-57
View File
@@ -1,57 +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.
#
# so-kernel-upgrade — switch the boot default to the installed UEK8 (6.x) kernel.
#
# Security Onion is moving off the EL9 stock kernel / UEK7 (5.x) onto UEK8 (6.x).
# Installing the kernel-uek-core package adds a UEK8 boot entry but does NOT make it the
# default: kernel-install/grubby only auto-promote a new kernel within the running
# kernel's flavor lineage, and we're crossing from a 5.x kernel to the new 6.x UEK flavor.
# So even with UPDATEDEFAULT=yes and DEFAULTKERNEL=kernel-uek-core the box keeps booting
# the old kernel. This tool finds the newest installed 6.x UEK kernel and makes it the
# GRUB default via grubby so the next boot comes up on UEK8.
#
# Idempotent: if the UEK8 kernel is already the default it does nothing. It only sets the
# boot default; it does NOT reboot — the admin reboots the node on their own schedule.
log() { echo "[so-kernel-upgrade] $*"; }
[ "$(id -u)" -eq 0 ] || { log "must run as root"; exit 1; }
command -v grubby >/dev/null 2>&1 || { log "grubby not found"; exit 1; }
# Newest installed UEK8 (6.x) kernel known to the bootloader. UEK8 vmlinuz paths look like
# /boot/vmlinuz-6.12.0-203.76.7.5.el9uek.x86_64; the 5.x UEK7 and 5.14 RHCK won't match.
target="$(grubby --info=ALL 2>/dev/null \
| sed -n 's/^kernel="\(.*\)"$/\1/p' \
| grep -E '/vmlinuz-6\.[0-9]+.*uek' \
| sort -V | tail -1)"
if [ -z "$target" ]; then
log "no installed 6.x UEK (UEK8) kernel found — confirm the kernel repo is assigned and"
log "'dnf update' has installed kernel-uek-core. Nothing to do."
exit 0
fi
current="$(grubby --default-kernel 2>/dev/null)"
if [ "$current" = "$target" ]; then
log "UEK8 kernel is already the boot default: $target"
exit 0
fi
log "current default kernel: ${current:-unknown}"
log "switching boot default to UEK8 kernel: $target"
grubby --set-default="$target" || { log "ERROR: grubby --set-default failed for $target"; exit 1; }
# Verify the change actually took before claiming success.
now="$(grubby --default-kernel 2>/dev/null)"
if [ "$now" != "$target" ]; then
log "ERROR: default kernel is still '${now:-unknown}' after set-default"
exit 1
fi
log "boot default is now $target"
log "REBOOT REQUIRED to start using the UEK8 kernel (currently running $(uname -r))."
-76
View File
@@ -1,76 +0,0 @@
#!/bin/bash
#
# so-nic-pin — pin physical NIC names by permanent MAC via classic by-MAC udev
# rules, so a kernel upgrade can't renumber them.
#
# Security Onion binds its management and monitor interfaces BY NAME in pillar
# (host:mainint, sensor:mainint, and bond0 is built on a specific physical NIC).
# A kernel upgrade can change the kernel/systemd-udevd predictable-naming output
# and renumber those NICs (e.g. enp1s0 -> enp2s0), which breaks the grid: the
# pillar references a name that no longer exists and bond/bridge bring-up fails.
#
# This writes /etc/udev/rules.d/70-persistent-net.rules pinning each PHYSICAL NIC
# to its CURRENT name by its PERMANENT MAC, freezing the names across future kernel
# changes. It only writes the rules file; it does NOT live-trigger a rename (the
# rules apply on the next boot/kernel, and a live rename would be disruptive).
#
# Run-once: gated by the drop file /opt/so/state/nic_names_pinned. If the marker is
# present the script does nothing, so an admin can pre-create it to opt out. Invoked
# from the common state on every highstate; the marker keeps it a one-time setup.
NET_RULES_FILE="/etc/udev/rules.d/70-persistent-net.rules"
MARKER="/opt/so/state/nic_names_pinned"
log() { echo -e "[so-nic-pin] $*"; }
# Echo "<name> <permanent-mac>" for every PHYSICAL NIC. A physical NIC is backed by a
# real device (has device/driver), which excludes bond0/sobridge/docker0/veth*/lo whose
# MACs are dynamic and must never be pinned. The PERMANENT MAC is used (ethtool -P, with
# fallbacks), not the current one: an enslaved bond member's current MAC is rewritten to
# the bond's, so matching on it would be wrong/ambiguous.
physical_nics() {
local path n mac
for path in /sys/class/net/*; do
n="${path##*/}"
[ "$n" = "lo" ] && continue
[ -e "${path}/device/driver" ] || continue # real device only
mac="$(ethtool -P "$n" 2>/dev/null | awk '/Permanent address/{print $NF}')"
case "$mac" in ""|00:00:00:00:00:00) mac="$(cat "${path}/bonding_slave/perm_hwaddr" 2>/dev/null)" ;; esac
case "$mac" in ""|00:00:00:00:00:00) mac="$(cat "${path}/address" 2>/dev/null)" ;; esac
case "$mac" in ""|00:00:00:00:00:00) continue ;; esac
echo "$n $mac"
done
}
# Turn "<name> <mac>" lines on stdin into classic by-MAC persistent-net udev rules.
render_net_rules() {
echo "# Generated by so-nic-pin: pin NIC names by MAC so kernel upgrades can't renumber them."
echo "# Security Onion binds its management/monitor interfaces by name; do not hand-edit."
local n mac
while read -r n mac; do
[ -n "$n" ] || continue
printf 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", NAME="%s"\n' \
"$mac" "$n"
done
}
[ "$(id -u)" -eq 0 ] || exit 0 # salt runs us as root; bail quietly otherwise
[ -e "${MARKER}" ] && exit 0 # run-once guard (mirrors the state's unless)
nics="$(physical_nics)"
if [ -z "${nics}" ]; then
log "no physical NICs detected — nothing to pin (will retry on next highstate)"
exit 0 # do NOT drop the marker; let it retry later
fi
log "pinning physical NICs by permanent MAC:"
echo "${nics}" | sed 's/^/ /'
[ -f "${NET_RULES_FILE}" ] && cp -f "${NET_RULES_FILE}" "${NET_RULES_FILE}.bak"
echo "${nics}" | render_net_rules > "${NET_RULES_FILE}" || {
log "ERROR: failed to write ${NET_RULES_FILE}"
exit 1
}
mkdir -p "$(dirname "${MARKER}")" && touch "${MARKER}"
log "wrote ${NET_RULES_FILE} ($(grep -c '^SUBSYSTEM' "${NET_RULES_FILE}") NIC(s) pinned); dropped ${MARKER}"
+19 -33
View File
@@ -5,41 +5,27 @@
# https://securityonion.net/license; you may not use this file except in compliance with the # https://securityonion.net/license; you may not use this file except in compliance with the
# Elastic License 2.0. # Elastic License 2.0.
# Usage: so-restart kibana | playbook
. /usr/sbin/so-common . /usr/sbin/so-common
usage() { if [ $# -ge 1 ]; then
echo "Usage: $0 <component> [args]"
echo ""
echo "Supported args:"
echo " --force | -f Force stop all Salt jobs before starting component."
echo ""
echo "Examples:"
echo " $0 kibana Restart Kibana"
echo " $0 kibana --force Force stop all Salt jobs before restarting Kibana"
exit 1
}
if [[ $# -lt 1 ]]; then echo $banner
usage printf "Restarting $1...\n\nThis could take a while if another Salt job is running. \nRun this command with --force to stop all Salt jobs before proceeding.\n"
fi echo $banner
#shellcheck disable=SC2154 if [ "$2" = "--force" ]; then
echo "$banner" printf "\nForce-stopping all Salt jobs before proceeding\n\n"
printf "Restarting %s...\n\nThis could take a while if another Salt job is running. \nRun this command with --force to stop all Salt jobs before proceeding.\n" "$1" salt-call saltutil.kill_all_jobs
echo "$banner" fi
if [[ "$2" = "--force" ]] || [[ "$2" = "-f" ]]; then
printf "\nForce-stopping all Salt jobs before proceeding\n\n" case $1 in
salt-call saltutil.kill_all_jobs "elastic-fleet") docker stop so-elastic-fleet && docker rm so-elastic-fleet && salt-call state.apply elasticfleet queue=True;;
*) docker stop so-$1 ; docker rm so-$1 ; salt-call state.apply $1 queue=True;;
esac
else
echo -e "\nPlease provide an argument by running like so-restart $component, or by using the component-specific script.\nEx. so-restart logstash, or so-logstash-restart\n"
fi fi
case $1 in
"elastic-fleet"|"elasticfleet")
docker_check_running "elastic-fleet" "--stop"
docker rm "so-elastic-fleet" 2> /dev/null
salt-call state.apply elasticfleet queue=True
;;
*)
docker_check_running "$1" "--stop"
docker rm "so-${1}" 2> /dev/null
salt-call state.apply "$1" queue=True
;;
esac
+20 -47
View File
@@ -5,54 +5,27 @@
# https://securityonion.net/license; you may not use this file except in compliance with the # https://securityonion.net/license; you may not use this file except in compliance with the
# Elastic License 2.0. # Elastic License 2.0.
# shellcheck disable=SC1091
# Usage: so-start all | kibana | playbook
. /usr/sbin/so-common . /usr/sbin/so-common
usage() { if [ $# -ge 1 ]; then
echo "Usage: $0 <component> [args]" echo $banner
echo "" printf "Starting $1...\n\nThis could take a while if another Salt job is running. \nRun this command with --force to stop all Salt jobs before proceeding.\n"
echo "Supported args:" echo $banner
echo " --force | -f Force stop all Salt jobs before starting component."
echo ""
echo "Examples:"
echo " $0 kibana Start Kibana"
echo " $0 kibana --force Force stop all Salt jobs before starting Kibana"
exit 1
}
if [[ $# -lt 1 ]]; then if [ "$2" = "--force" ]; then
usage printf "\nForce-stopping all Salt jobs before proceeding\n\n"
salt-call saltutil.kill_all_jobs
fi
case $1 in
"all") salt-call state.highstate queue=True;;
"elastic-fleet") if docker ps | grep -q so-$1; then printf "\n$1 is already running!\n\n"; else docker rm so-$1 >/dev/null 2>&1 ; salt-call state.apply elasticfleet queue=True; fi ;;
*) if docker ps | grep -E -q '^so-$1$'; then printf "\n$1 is already running\n\n"; else docker rm so-$1 >/dev/null 2>&1 ; salt-call state.apply $1 queue=True; fi ;;
esac
else
echo -e "\nPlease provide an argument by running like so-start $component, or by using the component-specific script.\nEx. so-start logstash, or so-logstash-start\n"
fi fi
#shellcheck disable=SC2154
echo "$banner"
printf "Starting %s...\n\nThis could take a while if another Salt job is running. \nRun this command with --force to stop all Salt jobs before proceeding.\n" "$1"
echo "$banner"
if [[ "$2" = "--force" ]] || [[ "$2" == "-f" ]]; then
printf "\nForce-stopping all Salt jobs before proceeding\n\n"
salt-call saltutil.kill_all_jobs
fi
case "$1" in
"all")
salt-call state.highstate queue=True
;;
"elastic-fleet"|"elasticfleet")
if docker_check_running "elastic-fleet"; then
printf "\nso-%s is already running!\n\n" "elastic-fleet"
/usr/sbin/so-status
else
docker rm "so-elastic-fleet" 2> /dev/null
salt-call state.apply elasticfleet queue=True
fi
;;
*)
if docker_check_running "$1"; then
printf "\nso-%s is already running\n\n" "$1"
/usr/sbin/so-status
else
docker rm "so-${1}" 2> /dev/null
salt-call state.apply "$1" queue=True
fi
;;
esac
+13 -25
View File
@@ -5,33 +5,21 @@
# https://securityonion.net/license; you may not use this file except in compliance with the # https://securityonion.net/license; you may not use this file except in compliance with the
# Elastic License 2.0. # Elastic License 2.0.
# shellcheck disable=SC1091
# Usage: so-stop kibana | playbook | thehive
. /usr/sbin/so-common . /usr/sbin/so-common
usage() { if [ $# -ge 1 ]; then
echo "Usage: $0 <component>" echo $banner
echo "" printf "Stopping $1...\n"
echo "Examples:" echo $banner
echo " $0 kibana Stop Kibana"
exit 1
}
if [[ $# -lt 1 ]]; then case $1 in
usage *) docker stop so-$1 ; docker rm so-$1 ;;
esac
else
echo -e "\nPlease provide an argument by running like so-stop $component, or by using the component-specific script.\nEx. so-stop logstash, or so-logstash-stop\n"
fi fi
#shellcheck disable=SC2154
echo "$banner"
printf "Stopping %s...\n" "$1"
echo "$banner"
case $1 in
"elasticfleet"|"elastic-fleet")
docker_check_running "elastic-fleet" "--stop"
docker rm "so-elastic-fleet" 2> /dev/null
;;
*)
docker_check_running "$1" "--stop"
docker rm "so-${1}" 2> /dev/null
;;
esac
+1 -2
View File
@@ -63,8 +63,7 @@ function status {
function pcapinfo() { function pcapinfo() {
PCAP=$1 PCAP=$1
ARGS=$2 ARGS=$2
docker run --rm -v "$PCAP:/input.pcap" --entrypoint capinfos {{ MANAGER }}:5000/{{ IMAGEREPO }}/so-pcaptools:{{ VERSION }} /input.pcap -ae $ARGS |\ docker run --rm -v "$PCAP:/input.pcap" --entrypoint capinfos {{ MANAGER }}:5000/{{ IMAGEREPO }}/so-pcaptools:{{ VERSION }} /input.pcap -ae $ARGS
sed 's/First packet/Earliest packet/g' | sed 's/Last packet/Latest packet/g'
} }
function pcapfix() { function pcapfix() {
+8
View File
@@ -18,10 +18,18 @@ dockergroup:
dockerheldpackages: dockerheldpackages:
pkg.installed: pkg.installed:
- pkgs: - pkgs:
{% if GLOBALS.os_version|int >= 10 %}
# OL10 test path: install latest Docker CE from the public repo (no .el9 builds available)
- containerd.io
- docker-ce
- docker-ce-cli
- docker-ce-rootless-extras
{% else %}
- containerd.io: 2.2.1-1.el9 - containerd.io: 2.2.1-1.el9
- docker-ce: 3:29.2.1-1.el9 - docker-ce: 3:29.2.1-1.el9
- docker-ce-cli: 1:29.2.1-1.el9 - docker-ce-cli: 1:29.2.1-1.el9
- docker-ce-rootless-extras: 29.2.1-1.el9 - docker-ce-rootless-extras: 29.2.1-1.el9
{% endif %}
- hold: True - hold: True
- update_holds: True - update_holds: True
+1 -1
View File
@@ -173,7 +173,7 @@ eaoptionalintegrationsdir:
{% for minion in node_data %} {% for minion in node_data %}
{% set role = node_data[minion]["role"] %} {% set role = node_data[minion]["role"] %}
{% if role in [ "eval","fleet","import","manager", "managerhype", "managersearch","standalone" ] %} {% if role in [ "eval","fleet","heavynode","import","manager", "managerhype", "managersearch","standalone" ] %}
{% set optional_integrations = ELASTICFLEETMERGED.optional_integrations %} {% set optional_integrations = ELASTICFLEETMERGED.optional_integrations %}
{% set integration_keys = optional_integrations.keys() %} {% set integration_keys = optional_integrations.keys() %}
fleet_server_integrations_{{ minion }}: fleet_server_integrations_{{ minion }}:
@@ -9,6 +9,7 @@
{% 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 %}
-11
View File
@@ -101,17 +101,6 @@ so-elastic-fleet:
- file: trusttheca - file: trusttheca
- x509: etc_elasticfleet_key - x509: etc_elasticfleet_key
- x509: etc_elasticfleet_crt - x509: etc_elasticfleet_crt
wait_for_so-elastic-fleet:
http.wait_for_successful_query:
- name: "https://localhost:8220/api/status"
- ssl: True
- verify_ssl: False
- status: 200
- wait_for: 300
- request_interval: 15
- require:
- docker_container: so-elastic-fleet
{% endif %} {% endif %}
delete_so-elastic-fleet_so-status.disabled: delete_so-elastic-fleet_so-status.disabled:
@@ -9,6 +9,7 @@
{% 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 %}
@@ -115,6 +116,7 @@
{% 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 %}
+5 -26
View File
@@ -9,20 +9,16 @@
include: include:
- elasticfleet.config - elasticfleet.config
- kibana.enabled
# If enabled, automatically update Fleet Logstash Outputs # If enabled, automatically update Fleet Logstash Outputs
{% if ELASTICFLEETMERGED.config.server.enable_auto_configuration %} {% if ELASTICFLEETMERGED.config.server.enable_auto_configuration and grains.role not in ['so-import', 'so-eval'] %}
{% if grains.role not in ['so-import', 'so-eval']%}
so-elastic-fleet-auto-configure-logstash-outputs: so-elastic-fleet-auto-configure-logstash-outputs:
cmd.run: cmd.run:
- name: /usr/sbin/so-elastic-fleet-outputs-update - name: /usr/sbin/so-elastic-fleet-outputs-update
- retry: - retry:
attempts: 4 attempts: 4
interval: 30 interval: 30
- require: {% endif %}
- http: wait_for_so-kibana
{% endif %}
# If enabled, automatically update Fleet Server URLs & ES Connection # If enabled, automatically update Fleet Server URLs & ES Connection
so-elastic-fleet-auto-configure-server-urls: so-elastic-fleet-auto-configure-server-urls:
@@ -31,9 +27,6 @@ so-elastic-fleet-auto-configure-server-urls:
- retry: - retry:
attempts: 4 attempts: 4
interval: 30 interval: 30
- require:
- http: wait_for_so-kibana
{% endif %}
# Automatically update Fleet Server Elasticsearch URLs & Agent Artifact URLs # Automatically update Fleet Server Elasticsearch URLs & Agent Artifact URLs
so-elastic-fleet-auto-configure-elasticsearch-urls: so-elastic-fleet-auto-configure-elasticsearch-urls:
@@ -42,8 +35,6 @@ so-elastic-fleet-auto-configure-elasticsearch-urls:
- retry: - retry:
attempts: 4 attempts: 4
interval: 30 interval: 30
- require:
- http: wait_for_so-kibana
so-elastic-fleet-auto-configure-artifact-urls: so-elastic-fleet-auto-configure-artifact-urls:
cmd.run: cmd.run:
@@ -51,8 +42,6 @@ so-elastic-fleet-auto-configure-artifact-urls:
- retry: - retry:
attempts: 4 attempts: 4
interval: 30 interval: 30
- require:
- http: wait_for_so-kibana
so-elastic-fleet-package-statefile: so-elastic-fleet-package-statefile:
file.managed: file.managed:
@@ -64,9 +53,9 @@ so-elastic-fleet-package-upgrade:
- name: /usr/sbin/so-elastic-fleet-package-upgrade - name: /usr/sbin/so-elastic-fleet-package-upgrade
- retry: - retry:
attempts: 3 attempts: 3
interval: 30 interval: 10
- require: - onchanges:
- http: wait_for_so-kibana - file: /opt/so/state/elastic_fleet_packages.txt
so-elastic-fleet-integrations: so-elastic-fleet-integrations:
cmd.run: cmd.run:
@@ -74,8 +63,6 @@ so-elastic-fleet-integrations:
- retry: - retry:
attempts: 3 attempts: 3
interval: 10 interval: 10
- require:
- http: wait_for_so-kibana
so-elastic-agent-grid-upgrade: so-elastic-agent-grid-upgrade:
cmd.run: cmd.run:
@@ -83,8 +70,6 @@ so-elastic-agent-grid-upgrade:
- retry: - retry:
attempts: 12 attempts: 12
interval: 5 interval: 5
- require:
- http: wait_for_so-kibana
so-elastic-fleet-integration-upgrade: so-elastic-fleet-integration-upgrade:
cmd.run: cmd.run:
@@ -92,22 +77,16 @@ so-elastic-fleet-integration-upgrade:
- retry: - retry:
attempts: 3 attempts: 3
interval: 10 interval: 10
- require:
- http: wait_for_so-kibana
{# Optional integrations script doesn't need the retries like so-elastic-fleet-integration-upgrade which loads the default integrations #} {# Optional integrations script doesn't need the retries like so-elastic-fleet-integration-upgrade which loads the default integrations #}
so-elastic-fleet-addon-integrations: so-elastic-fleet-addon-integrations:
cmd.run: cmd.run:
- name: /usr/sbin/so-elastic-fleet-optional-integrations-load - name: /usr/sbin/so-elastic-fleet-optional-integrations-load
- require:
- http: wait_for_so-kibana
{% if ELASTICFLEETMERGED.config.defend_filters.enable_auto_configuration %} {% if ELASTICFLEETMERGED.config.defend_filters.enable_auto_configuration %}
so-elastic-defend-manage-filters-file-watch: so-elastic-defend-manage-filters-file-watch:
cmd.run: cmd.run:
- name: python3 /sbin/so-elastic-defend-manage-filters.py -c /opt/so/conf/elasticsearch/curl.config -d /opt/so/conf/elastic-fleet/defend-exclusions/disabled-filters.yaml -i /nsm/securityonion-resources/event_filters/ -i /opt/so/conf/elastic-fleet/defend-exclusions/rulesets/custom-filters/ &>> /opt/so/log/elasticfleet/elastic-defend-manage-filters.log - name: python3 /sbin/so-elastic-defend-manage-filters.py -c /opt/so/conf/elasticsearch/curl.config -d /opt/so/conf/elastic-fleet/defend-exclusions/disabled-filters.yaml -i /nsm/securityonion-resources/event_filters/ -i /opt/so/conf/elastic-fleet/defend-exclusions/rulesets/custom-filters/ &>> /opt/so/log/elasticfleet/elastic-defend-manage-filters.log
- require:
- http: wait_for_so-kibana
- onchanges: - onchanges:
- file: elasticdefendcustom - file: elasticdefendcustom
- file: elasticdefenddisabled - file: elasticdefenddisabled
@@ -30,94 +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 || true
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 the policy cannot be fetched or 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 OUT_DIR INTEGRATION NAME ID i
FAIL_FILE=$(mktemp)
# Each job buffers its full output (header + API response) into its own file so the
# parent can print them grouped and in submission order after concurrent writes finish.
OUT_DIR=$(mktemp -d)
i=0
# Fetch the agent policy a single time; we look up integration ids locally below.
if ! POLICY_JSON=$(fleet_api "agent_policies/$AGENT_POLICY"); then
echo "Error: Failed to retrieve agent policy '$AGENT_POLICY'."
rm -f "$FAIL_FILE"
rm -rf "$OUT_DIR"
return 1
fi
if ! jq -e '.item.package_policies' <<<"$POLICY_JSON" >/dev/null 2>&1; then
echo "Error: Invalid agent policy response for '$AGENT_POLICY'."
rm -f "$FAIL_FILE"
rm -rf "$OUT_DIR"
return 1
fi
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
{
local RESP
if [ -n "$ID" ]; then
printf "\n\n%s - Updating integration %s\n" "$LABEL" "$NAME"
if ! RESP=$(elastic_fleet_integration_update "$ID" "@$INTEGRATION"); then
flock 9; echo "update ${INTEGRATION##*/}" >&9
fi
printf '%s\n' "$RESP"
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 ! RESP=$(elastic_fleet_integration_create "@$INTEGRATION"); then
flock 9; echo "create ${INTEGRATION##*/}" >&9
fi
printf '%s\n' "$RESP"
fi
} >"$OUT_DIR/$(printf '%03d' "$i")" 9>>"$FAIL_FILE" &
i=$((i+1))
done
wait || true
# Emit per-integration output grouped and in submission order (glob sorts numerically).
cat "$OUT_DIR"/* 2>/dev/null
rm -rf "$OUT_DIR"
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
@@ -134,9 +46,7 @@ elastic_fleet_integration_create() {
JSON_STRING=$1 JSON_STRING=$1
# --retry-all-errors so transient 409 conflicts (concurrent writes to the same agent if ! fleet_api "package_policies" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -XPOST -d "$JSON_STRING"; then
# policy) are retried; curl --retry alone does not retry 409.
if ! fleet_api "package_policies" --retry-all-errors -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -XPOST -d "$JSON_STRING"; then
return 1 return 1
fi fi
} }
@@ -167,9 +77,7 @@ elastic_fleet_integration_update() {
JSON_STRING=$2 JSON_STRING=$2
# --retry-all-errors so transient 409 conflicts (concurrent writes to the same agent if ! fleet_api "package_policies/$UPDATE_ID" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -XPUT -d "$JSON_STRING"; then
# policy) are retried; curl --retry alone does not retry 409.
if ! fleet_api "package_policies/$UPDATE_ID" --retry-all-errors -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -XPUT -d "$JSON_STRING"; then
return 1 return 1
fi fi
} }
@@ -9,45 +9,108 @@
RETURN_CODE=0 RETURN_CODE=0
if [ ! -f /opt/so/state/eaintegrations.txt ]; then if [ ! -f /opt/so/state/eaintegrations.txt ]; then
# First, check for any package upgrades
/usr/sbin/so-elastic-fleet-package-upgrade
# update Fleet Server policies # Second, update Fleet Server policies
/usr/sbin/so-elastic-fleet-integration-policy-elastic-fleet-server /usr/sbin/so-elastic-fleet-integration-policy-elastic-fleet-server
# configure Elastic Defend Integration separately # 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 (adds integration configuration to a given FleetServer_ policy) # 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
INTEGRATIONS=("${FLEET_DIR%/}"/*.json) FLEET_POLICY=`echo "$INTEGRATION"| cut -d'/' -f7`
[ -e "${INTEGRATIONS[0]}" ] || continue printf "\n\nFleet Server Policy - Loading $INTEGRATION\n"
elastic_fleet_integration_check "$FLEET_POLICY" "$INTEGRATION"
FLEET_POLICY=$(basename "$FLEET_DIR") if [ -n "$INTEGRATION_ID" ]; then
elastic_fleet_load_integrations_dir "$FLEET_POLICY" \ printf "\n\nIntegration $NAME exists - Updating integration\n"
"${FLEET_DIR%/}" "Fleet Server Policy" "elasticsearch-logs" || RETURN_CODE=1 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
if [[ $RETURN_CODE -eq 0 ]]; then if [[ "$RETURN_CODE" != "1" ]]; then
touch /opt/so/state/eaintegrations.txt touch /opt/so/state/eaintegrations.txt
else
exit 1
fi fi
else else
echo "Fleet integration policies already loaded." exit $RETURN_CODE
exit 0
fi fi
@@ -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
@@ -8,35 +8,18 @@
. /usr/sbin/so-elastic-fleet-common . /usr/sbin/so-elastic-fleet-common
PKG_LOAD_FAILURES=0
PKG_LOAD_FAILURES_NAMES=()
{%- for PACKAGE in SUPPORTED_PACKAGES %} {%- for PACKAGE in SUPPORTED_PACKAGES %}
if INSTALLED_VERSION=$(elastic_fleet_package_version_check "{{ PACKAGE }}") && LATEST_VERSION=$(elastic_fleet_package_latest_version_check "{{ PACKAGE }}"); then echo "Upgrading {{ PACKAGE }} package..."
if VERSION=$(elastic_fleet_package_latest_version_check "{{ PACKAGE }}"); then
if [ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]; then if ! elastic_fleet_package_install "{{ PACKAGE }}" "$VERSION"; then
echo "{{ PACKAGE }} integration version $INSTALLED_VERSION is already at the reported latest version $LATEST_VERSION, skipping upgrade." # exit 1 on failure to upgrade a default package, allow salt to handle retries
else echo -e "\nERROR: Failed to upgrade $PACKAGE to version: $VERSION"
echo "Upgrading {{ PACKAGE }} package to version $LATEST_VERSION..." exit 1
if ! elastic_fleet_package_install "{{ PACKAGE }}" "$LATEST_VERSION"; then
PKG_LOAD_FAILURES=$((PKG_LOAD_FAILURES + 1))
PKG_LOAD_FAILURES_NAMES+=("{{ PACKAGE }}")
fi
fi fi
else else
echo "ERROR: Failed to get version information for integration {{ PACKAGE }}" echo -e "\nERROR: Failed to get version information for integration $PACKAGE"
PKG_LOAD_FAILURES=$((PKG_LOAD_FAILURES + 1))
PKG_LOAD_FAILURES_NAMES+=("{{ PACKAGE }}")
fi fi
echo
{%- endfor %} {%- endfor %}
echo
if [ $PKG_LOAD_FAILURES -gt 0 ]; then /usr/sbin/so-elasticsearch-templates-load
echo "ERROR: Failed to upgrade $PKG_LOAD_FAILURES package(s):"
for PKG in "${PKG_LOAD_FAILURES_NAMES[@]}"; do
echo " - $PKG"
done
# exit 1 on failure to upgrade a default package, allow salt to handle retries
exit 1
else
echo "Successfully upgraded all packages."
fi
@@ -181,9 +181,6 @@ if ! elastic_fleet_policy_create "so-grid-nodes_heavy" "SO Grid Nodes - Heavy No
exit 1 exit 1
fi fi
# Check for package upgrades
so-elastic-fleet-package-upgrade
# Load Integrations for default policies # Load Integrations for default policies
so-elastic-fleet-integration-policy-load so-elastic-fleet-integration-policy-load
+2 -32
View File
@@ -9,12 +9,9 @@
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCHMERGED %} {% from 'elasticsearch/config.map.jinja' import ELASTICSEARCHMERGED %}
{% from 'elasticsearch/template.map.jinja' import ES_INDEX_SETTINGS, SO_MANAGED_INDICES %} {% from 'elasticsearch/template.map.jinja' import ES_INDEX_SETTINGS, SO_MANAGED_INDICES %}
{% if GLOBALS.role != 'so-heavynode' %} {% if GLOBALS.role != 'so-heavynode' %}
{% from 'elasticsearch/template.map.jinja' import ALL_ADDON_SETTINGS, ADDON_INDICES %} {% from 'elasticsearch/template.map.jinja' import ALL_ADDON_SETTINGS %}
{% endif %} {% endif %}
include:
- elasticsearch.enabled
escomponenttemplates: escomponenttemplates:
file.recurse: file.recurse:
- name: /opt/so/conf/elasticsearch/templates/component - name: /opt/so/conf/elasticsearch/templates/component
@@ -38,20 +35,6 @@ so_index_template_dir:
{%- endfor %} {%- endfor %}
{%- endif %} {%- endif %}
{% if GLOBALS.role != "so-heavynode" %}
# Clean up legacy and non-SO managed templates from the elasticsearch/templates/addon-index/ directory
addon_index_template_dir:
file.directory:
- name: /opt/so/conf/elasticsearch/templates/addon-index
- clean: True
{%- if ADDON_INDICES %}
- require:
{%- for index in ADDON_INDICES %}
- file: addon_index_template_{{index}}
{%- endfor %}
{%- endif %}
{% endif %}
# Auto-generate index templates for SO managed indices (directly defined in elasticsearch/defaults.yaml) # Auto-generate index templates for SO managed indices (directly defined in elasticsearch/defaults.yaml)
# These index templates are for the core SO datasets and are always required # These index templates are for the core SO datasets and are always required
{% for index, settings in ES_INDEX_SETTINGS.items() %} {% for index, settings in ES_INDEX_SETTINGS.items() %}
@@ -133,18 +116,6 @@ 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 }}
@@ -165,8 +136,7 @@ so-elasticsearch-roles-load:
{% set ap = "absent" %} {% set ap = "absent" %}
{% endif %} {% endif %}
{% if grains.role in ['so-eval', 'so-standalone', 'so-heavynode'] %} {% if grains.role in ['so-eval', 'so-standalone', 'so-heavynode'] %}
{# Remove so-elasticsearch-indices-delete script when using DLM #} {% if ELASTICSEARCHMERGED.index_clean %}
{% if ELASTICSEARCHMERGED.index_clean and ELASTICSEARCHMERGED.data_retention_method == "ILM" %}
{% set ap = "present" %} {% set ap = "present" %}
{% else %} {% else %}
{% set ap = "absent" %} {% set ap = "absent" %}
+7 -135
View File
@@ -2,7 +2,6 @@ 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:
@@ -64,8 +63,6 @@ elasticsearch:
verification_mode: none verification_mode: none
index_settings: index_settings:
global_overrides: global_overrides:
data_stream_lifecycle:
data_retention: 90d
index_template: index_template:
template: template:
settings: settings:
@@ -146,8 +143,6 @@ 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
@@ -309,8 +304,6 @@ 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
@@ -351,8 +344,6 @@ 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
@@ -506,8 +497,6 @@ 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
@@ -616,8 +605,6 @@ 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
@@ -800,8 +787,6 @@ 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
@@ -851,8 +836,6 @@ 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
@@ -921,8 +904,6 @@ 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
@@ -1068,8 +1049,6 @@ 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
@@ -1150,8 +1129,6 @@ 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
@@ -1215,8 +1192,6 @@ 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
@@ -1332,8 +1307,6 @@ 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
@@ -1396,8 +1369,6 @@ 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
@@ -1462,8 +1433,6 @@ 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
@@ -1528,8 +1497,6 @@ 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
@@ -1594,8 +1561,6 @@ 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
@@ -1655,8 +1620,6 @@ 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
@@ -1716,8 +1679,6 @@ 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
@@ -1774,8 +1735,6 @@ 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
@@ -1840,8 +1799,6 @@ 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
@@ -1901,8 +1858,6 @@ 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
@@ -1962,8 +1917,6 @@ 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
@@ -2028,8 +1981,6 @@ 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
@@ -2094,13 +2045,10 @@ 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
- .logs-endpoint.actions@custom - .logs-endpoint.actions@custom
- endpoint@custom
- event-mappings - event-mappings
- so-fleet_integrations.ip_mappings-1 - so-fleet_integrations.ip_mappings-1
- so-fleet_globals-1 - so-fleet_globals-1
@@ -2110,9 +2058,8 @@ elasticsearch:
hidden: false hidden: false
ignore_missing_component_templates: ignore_missing_component_templates:
- .logs-endpoint.actions@custom - .logs-endpoint.actions@custom
- endpoint@custom
index_patterns: index_patterns:
- .logs-endpoint.actions-* - logs-endpoint.actions-*
priority: 501 priority: 501
template: template:
settings: settings:
@@ -2157,13 +2104,10 @@ 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
- .logs-endpoint.action.responses@custom - .logs-endpoint.action.responses@custom
- endpoint@custom
- event-mappings - event-mappings
- so-fleet_integrations.ip_mappings-1 - so-fleet_integrations.ip_mappings-1
- so-fleet_globals-1 - so-fleet_globals-1
@@ -2173,15 +2117,14 @@ elasticsearch:
hidden: false hidden: false
ignore_missing_component_templates: ignore_missing_component_templates:
- .logs-endpoint.action.responses@custom - .logs-endpoint.action.responses@custom
- endpoint@custom
index_patterns: index_patterns:
- .logs-endpoint.action.responses-* - logs-endpoint.action.responses-*
priority: 501 priority: 501
template: template:
settings: settings:
index: index:
lifecycle: lifecycle:
name: so-logs-endpoint.action.responses-logs name: so-logs-endpoint.actions-logs
mapping: mapping:
total_fields: total_fields:
limit: 5000 limit: 5000
@@ -2220,8 +2163,6 @@ 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
@@ -2281,8 +2222,6 @@ 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
@@ -2358,8 +2297,6 @@ 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
@@ -2419,8 +2356,6 @@ 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
@@ -2480,8 +2415,6 @@ 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
@@ -2541,8 +2474,6 @@ 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
@@ -2602,8 +2533,6 @@ 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
@@ -2663,8 +2592,6 @@ 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
@@ -2724,8 +2651,6 @@ 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
@@ -2785,8 +2710,6 @@ 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
@@ -2846,8 +2769,6 @@ 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
@@ -2896,8 +2817,6 @@ 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
@@ -2963,8 +2882,6 @@ 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
@@ -3036,8 +2953,6 @@ 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
@@ -3090,8 +3005,6 @@ 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
@@ -3200,8 +3113,6 @@ 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
@@ -3251,8 +3162,6 @@ 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
@@ -3302,8 +3211,6 @@ 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
@@ -3353,8 +3260,6 @@ 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
@@ -3404,8 +3309,6 @@ 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
@@ -3455,8 +3358,6 @@ 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
@@ -3504,8 +3405,6 @@ 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
@@ -3553,8 +3452,6 @@ 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
@@ -3602,8 +3499,6 @@ 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
@@ -3651,8 +3546,6 @@ 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
@@ -3701,8 +3594,6 @@ 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
@@ -3818,8 +3709,6 @@ 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
@@ -3867,8 +3756,6 @@ 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
@@ -3916,8 +3803,6 @@ 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
@@ -3965,8 +3850,6 @@ 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
@@ -3991,8 +3874,6 @@ 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
@@ -4017,8 +3898,6 @@ 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
@@ -4079,10 +3958,13 @@ 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
@@ -4134,8 +4016,6 @@ 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
@@ -4253,8 +4133,6 @@ 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
@@ -4371,8 +4249,6 @@ 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
@@ -4489,8 +4365,6 @@ 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
@@ -4607,8 +4481,6 @@ 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
-1
View File
@@ -5,7 +5,6 @@
{ "remove": { "field": ["host"], "ignore_failure": true } }, { "remove": { "field": ["host"], "ignore_failure": true } },
{ "json": { "field": "message", "target_field": "message2", "ignore_failure": true } }, { "json": { "field": "message", "target_field": "message2", "ignore_failure": true } },
{ "rename": { "field": "message2.version", "target_field": "ssl.version", "ignore_missing": true } }, { "rename": { "field": "message2.version", "target_field": "ssl.version", "ignore_missing": true } },
{ "set": { "description": "Set transport for the community_id processor", "if": "ctx.ssl?.version == null || !ctx.ssl.version.startsWith('DTLS')", "field": "network.transport", "value": "tcp", "ignore_failure": true } },
{ "rename": { "field": "message2.cipher", "target_field": "ssl.cipher", "ignore_missing": true } }, { "rename": { "field": "message2.cipher", "target_field": "ssl.cipher", "ignore_missing": true } },
{ "rename": { "field": "message2.curve", "target_field": "ssl.curve", "ignore_missing": true } }, { "rename": { "field": "message2.curve", "target_field": "ssl.curve", "ignore_missing": true } },
{ "rename": { "field": "message2.server_name", "target_field": "ssl.server_name", "ignore_missing": true } }, { "rename": { "field": "message2.server_name", "target_field": "ssl.server_name", "ignore_missing": true } },
+61 -394
View File
@@ -4,13 +4,6 @@ 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
@@ -20,7 +13,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, 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. 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.
forcedType: bool forcedType: bool
helpLink: elasticsearch helpLink: elasticsearch
vm: vm:
@@ -146,23 +139,6 @@ 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
allowedNodeTypes:
- heavynode
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:
@@ -335,30 +311,13 @@ elasticsearch:
forcedType: string forcedType: string
global: True global: True
helpLink: elasticsearch helpLink: elasticsearch
so-logs: &dataStreamSettings so-logs: &indexSettings
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
allowedNodeTypes:
- heavynode
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.
@@ -376,14 +335,6 @@ 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:
@@ -645,349 +596,65 @@ elasticsearch:
global: True global: True
advanced: True advanced: True
helpLink: elasticsearch helpLink: elasticsearch
so-logs-system_x_auth: *dataStreamSettings so-logs-system_x_auth: *indexSettings
so-logs-system_x_syslog: *dataStreamSettings so-logs-system_x_syslog: *indexSettings
so-logs-system_x_system: *dataStreamSettings so-logs-system_x_system: *indexSettings
so-logs-system_x_application: *dataStreamSettings so-logs-system_x_application: *indexSettings
so-logs-system_x_security: *dataStreamSettings so-logs-system_x_security: *indexSettings
so-logs-windows_x_forwarded: *dataStreamSettings so-logs-windows_x_forwarded: *indexSettings
so-logs-windows_x_powershell: *dataStreamSettings so-logs-windows_x_powershell: *indexSettings
so-logs-windows_x_powershell_operational: *dataStreamSettings so-logs-windows_x_powershell_operational: *indexSettings
so-logs-windows_x_sysmon_operational: *dataStreamSettings so-logs-windows_x_sysmon_operational: *indexSettings
so-logs-winlog_x_winlog: *dataStreamSettings so-logs-winlog_x_winlog: *indexSettings
so-logs-detections_x_alerts: *dataStreamSettings so-logs-detections_x_alerts: *indexSettings
so-logs-http_endpoint_x_generic: *dataStreamSettings so-logs-http_endpoint_x_generic: *indexSettings
so-logs-httpjson_x_generic: *dataStreamSettings so-logs-httpjson_x_generic: *indexSettings
so-logs-osquery-manager-actions: *dataStreamSettings so-logs-osquery-manager-actions: *indexSettings
so-logs-osquery-manager-action_x_responses: *dataStreamSettings so-logs-osquery-manager-action_x_responses: *indexSettings
so-logs-osquery-manager_x_action_x_responses: *dataStreamSettings so-logs-osquery-manager_x_action_x_responses: *indexSettings
so-logs-osquery-manager_x_result: *dataStreamSettings so-logs-osquery-manager_x_result: *indexSettings
so-logs-elastic_agent_x_apm_server: *dataStreamSettings so-logs-elastic_agent_x_apm_server: *indexSettings
so-logs-elastic_agent_x_auditbeat: *dataStreamSettings so-logs-elastic_agent_x_auditbeat: *indexSettings
so-logs-elastic_agent_x_cloudbeat: *dataStreamSettings so-logs-elastic_agent_x_cloudbeat: *indexSettings
so-logs-elastic_agent_x_endpoint_security: *dataStreamSettings so-logs-elastic_agent_x_endpoint_security: *indexSettings
so-logs-endpoint_x_alerts: *dataStreamSettings so-logs-endpoint_x_alerts: *indexSettings
so-logs-endpoint_x_events_x_api: *dataStreamSettings so-logs-endpoint_x_events_x_api: *indexSettings
so-logs-endpoint_x_events_x_file: *dataStreamSettings so-logs-endpoint_x_events_x_file: *indexSettings
so-logs-endpoint_x_events_x_library: *dataStreamSettings so-logs-endpoint_x_events_x_library: *indexSettings
so-logs-endpoint_x_events_x_network: *dataStreamSettings so-logs-endpoint_x_events_x_network: *indexSettings
so-logs-endpoint_x_events_x_process: *dataStreamSettings so-logs-endpoint_x_events_x_process: *indexSettings
so-logs-endpoint_x_events_x_registry: *dataStreamSettings so-logs-endpoint_x_events_x_registry: *indexSettings
so-logs-endpoint_x_events_x_security: *dataStreamSettings so-logs-endpoint_x_events_x_security: *indexSettings
so-logs-elastic_agent_x_filebeat: *dataStreamSettings so-logs-elastic_agent_x_filebeat: *indexSettings
so-logs-elastic_agent_x_fleet_server: *dataStreamSettings so-logs-elastic_agent_x_fleet_server: *indexSettings
so-logs-elastic_agent_x_heartbeat: *dataStreamSettings so-logs-elastic_agent_x_heartbeat: *indexSettings
so-logs-elastic_agent: *dataStreamSettings so-logs-elastic_agent: *indexSettings
so-logs-elastic_agent_x_metricbeat: *dataStreamSettings so-logs-elastic_agent_x_metricbeat: *indexSettings
so-logs-elastic_agent_x_osquerybeat: *dataStreamSettings so-logs-elastic_agent_x_osquerybeat: *indexSettings
so-logs-elastic_agent_x_packetbeat: *dataStreamSettings so-logs-elastic_agent_x_packetbeat: *indexSettings
so-logs-elasticsearch_x_server: *dataStreamSettings so-logs-elasticsearch_x_server: *indexSettings
so-metrics-endpoint_x_metadata: *dataStreamSettings so-metrics-endpoint_x_metadata: *indexSettings
so-metrics-endpoint_x_metrics: *dataStreamSettings so-metrics-endpoint_x_metrics: *indexSettings
so-metrics-endpoint_x_policy: *dataStreamSettings so-metrics-endpoint_x_policy: *indexSettings
so-metrics-nginx_x_stubstatus: *dataStreamSettings so-metrics-nginx_x_stubstatus: *indexSettings
so-metrics-vsphere_x_datastore: *dataStreamSettings so-metrics-vsphere_x_datastore: *indexSettings
so-metrics-vsphere_x_host: *dataStreamSettings so-metrics-vsphere_x_host: *indexSettings
so-metrics-vsphere_x_virtualmachine: *dataStreamSettings so-metrics-vsphere_x_virtualmachine: *indexSettings
so-common: *dataStreamSettings so-case: *indexSettings
so-endgame: *dataStreamSettings so-common: *indexSettings
so-idh: *dataStreamSettings so-endgame: *indexSettings
so-suricata: *dataStreamSettings so-idh: *indexSettings
so-suricata_x_alerts: *dataStreamSettings so-suricata: *indexSettings
so-import: *dataStreamSettings so-suricata_x_alerts: *indexSettings
so-kratos: *dataStreamSettings so-import: *indexSettings
so-hydra: *dataStreamSettings so-kratos: *indexSettings
so-kismet: *dataStreamSettings so-hydra: *indexSettings
so-logstash: *dataStreamSettings so-kismet: *indexSettings
so-redis: *dataStreamSettings so-logstash: *indexSettings
so-strelka: *dataStreamSettings so-redis: *indexSettings
so-syslog: *dataStreamSettings so-strelka: *indexSettings
so-zeek: *dataStreamSettings so-syslog: *indexSettings
# Managed SOC integration annotations are inserted below this line. Referencing '*dataStreamSettings' so-zeek: *indexSettings
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.
+4 -38
View File
@@ -4,11 +4,7 @@
Elastic License 2.0. #} Elastic License 2.0. #}
{% import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS %} {% import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS %}
{# ELASTICSEARCHMERGED only used here to collect data_retention_method. This file intentionally works with ELASTICSEARCHDEFAULTS #}
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCHMERGED %}
{% 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 = ELASTICSEARCHMERGED.data_retention_method %}
{% 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', {}) %}
@@ -65,25 +61,15 @@
{% if ALL_ADDON_SETTINGS_ORIG.keys() | length > 0 %} {% if ALL_ADDON_SETTINGS_ORIG.keys() | length > 0 %}
{% for index in ALL_ADDON_SETTINGS_ORIG.keys() %} {% for index in ALL_ADDON_SETTINGS_ORIG.keys() %}
{% do ALL_ADDON_SETTINGS_GLOBAL_OVERRIDES.update({index: salt['defaults.merge'](ALL_ADDON_SETTINGS_ORIG[index], PILLAR_GLOBAL_OVERRIDES, in_place=False)}) %} {% do ALL_ADDON_SETTINGS_GLOBAL_OVERRIDES.update({index: salt['defaults.merge'](ALL_ADDON_SETTINGS_ORIG[index], PILLAR_GLOBAL_OVERRIDES, in_place=False)}) %}
{# Explicitly excluding addon indices from ES_INDEX_SETTINGS_ORIG
When manager.soc_managed_annotations runs, new entries are added to the salt/elasticsearch/defaults.yaml file to support 'revert to default' functionality.
Subsequent map renders will then incorrectly include 'integration X' in 'ES_INDEX_SETTINGS_ORIG' due to being in the defaults.yaml file. #}
{% if index in ES_INDEX_SETTINGS_ORIG.keys() %}
{% do ES_INDEX_SETTINGS_ORIG.pop(index) %}
{% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% set ES_INDEX_SETTINGS = {} %} {% set ES_INDEX_SETTINGS = {} %}
{% macro create_final_index_template(DEFINED_SETTINGS, GLOBAL_OVERRIDES, FINAL_INDEX_SETTINGS, EXCLUDE_INDICES=[]) %} {% macro create_final_index_template(DEFINED_SETTINGS, GLOBAL_OVERRIDES, FINAL_INDEX_SETTINGS) %}
{% do GLOBAL_OVERRIDES.update(salt['defaults.merge'](GLOBAL_OVERRIDES, ES_INDEX_PILLAR, in_place=False)) %} {% do GLOBAL_OVERRIDES.update(salt['defaults.merge'](GLOBAL_OVERRIDES, ES_INDEX_PILLAR, in_place=False)) %}
{% for index, settings in GLOBAL_OVERRIDES.items() %} {% for index, settings in GLOBAL_OVERRIDES.items() %}
{% if index in EXCLUDE_INDICES %}
{% continue %}
{% endif %}
{# prevent this action from being performed on custom defined indices. #} {# prevent this action from being performed on custom defined indices. #}
{# the custom defined index is not present in either of the dictionaries and fails to reder. #} {# the custom defined index is not present in either of the dictionaries and fails to reder. #}
{% if index in DEFINED_SETTINGS and index in GLOBAL_OVERRIDES %} {% if index in DEFINED_SETTINGS and index in GLOBAL_OVERRIDES %}
@@ -109,17 +95,6 @@
{% 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 #}
@@ -175,19 +150,10 @@
{% endfor %} {% endfor %}
{% endmacro %} {% endmacro %}
{# Exclude addon integrations from final ES_INDEX_SETTINGS #} {{ create_final_index_template(ES_INDEX_SETTINGS_ORIG, ES_INDEX_SETTINGS_GLOBAL_OVERRIDES, ES_INDEX_SETTINGS) }}
{{ create_final_index_template(ES_INDEX_SETTINGS_ORIG, ES_INDEX_SETTINGS_GLOBAL_OVERRIDES, ES_INDEX_SETTINGS, ALL_ADDON_SETTINGS_ORIG.keys() | list ) }} {{ create_final_index_template(ALL_ADDON_SETTINGS_ORIG, ALL_ADDON_SETTINGS_GLOBAL_OVERRIDES, ALL_ADDON_SETTINGS) }}
{# Exclude SO managed indices, otherwise ALL_ADDON_SETTINGS will include pillar values
of core integrations without merging defaults, resulting in an overlapping, but bad index template being generated. #}
{{ create_final_index_template(ALL_ADDON_SETTINGS_ORIG, ALL_ADDON_SETTINGS_GLOBAL_OVERRIDES, ALL_ADDON_SETTINGS, ES_INDEX_SETTINGS_ORIG.keys() | list ) }}
{% set SO_MANAGED_INDICES = [] %} {% set SO_MANAGED_INDICES = [] %}
{% for index, settings in ES_INDEX_SETTINGS.items() %} {% for index, settings in ES_INDEX_SETTINGS.items() %}
{% do SO_MANAGED_INDICES.append(index) %} {% do SO_MANAGED_INDICES.append(index) %}
{% endfor %} {% endfor %}
{% set ADDON_INDICES = [] %}
{% for index, settings in ALL_ADDON_SETTINGS.items() %}
{% do ADDON_INDICES.append(index) %}
{% endfor %}
@@ -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,11 +118,21 @@ 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"
@@ -237,9 +180,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
@@ -249,7 +189,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
@@ -257,34 +197,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
@@ -303,27 +241,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
@@ -1,175 +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.
. /usr/sbin/so-common
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCHMERGED %}
{%- set DATA_RETENTION_METHOD = ELASTICSEARCHMERGED.data_retention_method %}
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."
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."
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,48 +6,6 @@
. /usr/sbin/so-common . /usr/sbin/so-common
MAX_JOBS=${MAX_ILM_JOBS:-10}
# Lock used to serialize block writes so concurrent jobs never interleave their output.
ILM_OUTPUT_LOCK=$(mktemp)
ILM_FAIL_FILE=$(mktemp)
trap 'rm -f "$ILM_OUTPUT_LOCK" "$ILM_FAIL_FILE"' 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 rc=0
if ! result=$(curl -K /opt/so/conf/elasticsearch/curl.config -s -k -L --fail \
-X PUT "https://localhost:9200/_ilm/policy/${policyname}" \
-H 'Content-Type: application/json' -d"${data}" 2>&1); then
rc=1
elif ! jq -e '.acknowledged == true' <<<"$result" >/dev/null 2>&1; then
rc=1
fi
# 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}"
if (( rc != 0 )); then
printf '%s\n' "${policyname}" >>"$ILM_FAIL_FILE"
fi
} 200>>"${ILM_OUTPUT_LOCK}"
return "$rc"
}
# Block until fewer than MAX_JOBS background curls are running.
throttle() {
while (( $(jobs -rp | wc -l) >= MAX_JOBS )); do
wait -n || true
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 %}
@@ -56,36 +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 || true
if [[ -s "$ILM_FAIL_FILE" ]]; then
echo "ERROR: Failed to load ILM policy(s):"
while read -r POLICY; do
echo " - $POLICY"
done < "$ILM_FAIL_FILE"
exit 1
else
echo "Successfully loaded all ILM policies."
fi
+1 -1
View File
@@ -32,7 +32,7 @@ so-kafka:
- networks: - networks:
- sobridge: - sobridge:
- ipv4_address: {{ DOCKERMERGED.containers['so-kafka'].ip }} - ipv4_address: {{ DOCKERMERGED.containers['so-kafka'].ip }}
- user: "960" - user: kafka
- environment: - environment:
KAFKA_HEAP_OPTS: -Xmx2G -Xms1G KAFKA_HEAP_OPTS: -Xmx2G -Xms1G
KAFKA_OPTS: "-javaagent:/opt/jolokia/agents/jolokia-agent-jvm-javaagent.jar=port=8778,host={{ DOCKERMERGED.containers['so-kafka'].ip }},policyLocation=file:/opt/jolokia/jolokia.xml {%- if KAFKA_EXTERNAL_ACCESS %} -Djava.security.auth.login.config=/opt/kafka/config/kafka_server_jaas.conf {% endif -%}" KAFKA_OPTS: "-javaagent:/opt/jolokia/agents/jolokia-agent-jvm-javaagent.jar=port=8778,host={{ DOCKERMERGED.containers['so-kafka'].ip }},policyLocation=file:/opt/jolokia/jolokia.xml {%- if KAFKA_EXTERNAL_ACCESS %} -Djava.security.auth.login.config=/opt/kafka/config/kafka_server_jaas.conf {% endif -%}"
+1 -15
View File
@@ -6,7 +6,6 @@
{% from 'allowed_states.map.jinja' import allowed_states %} {% from 'allowed_states.map.jinja' import allowed_states %}
{% if sls.split('.')[0] in allowed_states %} {% if sls.split('.')[0] in allowed_states %}
{% from 'docker/docker.map.jinja' import DOCKERMERGED %} {% from 'docker/docker.map.jinja' import DOCKERMERGED %}
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCHMERGED %}
{% from 'vars/globals.map.jinja' import GLOBALS %} {% from 'vars/globals.map.jinja' import GLOBALS %}
include: include:
@@ -18,7 +17,7 @@ so-kibana:
docker_container.running: docker_container.running:
- image: {{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-kibana:{{ GLOBALS.so_version }} - image: {{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-kibana:{{ GLOBALS.so_version }}
- hostname: kibana - hostname: kibana
- user: "932:0" - user: kibana
- networks: - networks:
- sobridge: - sobridge:
- ipv4_address: {{ DOCKERMERGED.containers['so-kibana'].ip }} - ipv4_address: {{ DOCKERMERGED.containers['so-kibana'].ip }}
@@ -61,19 +60,6 @@ so-kibana:
- watch: - watch:
- file: kibanaconfig - file: kibanaconfig
wait_for_so-kibana:
http.wait_for_successful_query:
- name: "http://localhost:5601/api/status"
- username: 'so_elastic'
- password: '{{ ELASTICSEARCHMERGED.auth.users.so_elastic_user.pass }}'
- ssl: True
- verify_ssl: False
- status: 200
- wait_for: 600
- request_interval: 15
- require:
- docker_container: so-kibana
delete_so-kibana_so-status.disabled: delete_so-kibana_so-status.disabled:
file.uncomment: file.uncomment:
- name: /opt/so/conf/so-status/so-status.conf - name: /opt/so/conf/so-status/so-status.conf
+1 -1
View File
@@ -103,7 +103,7 @@ kratos:
config: config:
session: session:
lifespan: lifespan:
description: Defines the length of a login session before it will timeout, and require a new login. description: Defines the length of a login session.
global: True global: True
helpLink: kratos helpLink: kratos
whoami: whoami:
+1 -1
View File
@@ -33,7 +33,7 @@ so-logstash:
- networks: - networks:
- sobridge: - sobridge:
- ipv4_address: {{ DOCKERMERGED.containers['so-logstash'].ip }} - ipv4_address: {{ DOCKERMERGED.containers['so-logstash'].ip }}
- user: "931:0" - user: logstash
- extra_hosts: - extra_hosts:
{% for node in LOGSTASH_NODES %} {% for node in LOGSTASH_NODES %}
{% for hostname, ip in node.items() %} {% for hostname, ip in node.items() %}
-2
View File
@@ -1,2 +0,0 @@
https://repo.securityonion.net/file/so-repo/prod/3/oracle/9-uek8
https://repo-alt.securityonion.net/prod/3/oracle/9-uek8
+1 -6
View File
@@ -10,9 +10,4 @@ keepcache=0
name=Security Onion Repo repo name=Security Onion Repo repo
mirrorlist=file:///opt/so/conf/reposync/mirror.txt mirrorlist=file:///opt/so/conf/reposync/mirror.txt
enabled=1 enabled=1
gpgcheck=1 gpgcheck=1
[securityonionkernelsync]
name=Security Onion Kernel Repo repo
mirrorlist=file:///opt/so/conf/reposync/mirror-kernel.txt
enabled=1
gpgcheck=1
-29
View File
@@ -86,28 +86,6 @@ repo_dir:
- group - group
- show_changes: False - show_changes: False
kernelrepo_dir:
file.directory:
- name: /nsm/kernelrepo
- user: socore
- group: socore
- recurse:
- user
- group
- show_changes: False
# Ensure /nsm/kernelrepo is always a valid (if empty) repo before it is ever assigned to
# a client. Without repodata/repomd.xml an enabled file:///nsm/kernelrepo repo makes every
# dnf operation fail; so-repo-sync only populates it after the highstate, so seed an empty
# repo here. Only runs when repodata is missing, so it won't clobber a synced repo.
kernelrepo_init_empty:
cmd.run:
- name: createrepo /nsm/kernelrepo
- unless: 'test -e /nsm/kernelrepo/repodata/repomd.xml'
- require:
- file: kernelrepo_dir
- pkg: install_createrepo
manager_sbin: manager_sbin:
file.recurse: file.recurse:
- name: /usr/sbin - name: /usr/sbin
@@ -144,13 +122,6 @@ so-repo-mirrorlist:
- user: socore - user: socore
- group: socore - group: socore
so-repo-kernel-mirrorlist:
file.managed:
- name: /opt/so/conf/reposync/mirror-kernel.txt
- source: salt://manager/files/mirror-kernel.txt
- user: socore
- group: socore
so-repo-sync: so-repo-sync:
{% if MANAGERMERGED.reposync.enabled %} {% if MANAGERMERGED.reposync.enabled %}
cron.present: cron.present:
+32 -27
View File
@@ -16,35 +16,40 @@
{% 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' %}
manage_soc_annotations: {{ es_soc_annotations }}:
file.blockreplace: file.serialize:
- name: {{ es_soc_annotations }} - dataset:
- marker_start: ' # START managed SOC integration annotations' {% set data = salt['file.read'](es_soc_annotations) | load_yaml %}
- marker_end: ' # END managed SOC integration annotations' {% set es = data.get('elasticsearch', {}) %}
- content: {{ soc_annotation_lines | join('\n') | tojson }} {% set index_settings = es.get('index_settings', {}) %}
- insert_after_match: '^ # Managed SOC integration annotations are inserted below this line\.' {% set input = index_settings.get('so-logs', {}) %}
- append_if_not_found: False {% for k in matched_integration_names %}
- show_changes: True {% do index_settings.update({k: input}) %}
{% 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.blockreplace: file.serialize:
- marker_start: ' # START managed SOC integration defaults' - dataset:
- marker_end: ' # END managed SOC integration defaults' {% set data = salt['file.read'](es_defaults) | load_yaml %}
- content: {{ defaults_lines | join('\n') | tojson }} {% set es = data.get('elasticsearch', {}) %}
- insert_after_match: '^ index_settings:$' {% set index_settings = es.get('index_settings', {}) %}
- append_if_not_found: False {% for k in matched_integration_names %}
- show_changes: True {% set input = ADDON_INTEGRATION_DEFAULTS[k] %}
{% endif %} {% do index_settings.update({k: input})%}
{% 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 %}
+3 -5
View File
@@ -31,13 +31,11 @@ sync_es_users:
- http: wait_for_kratos - http: wait_for_kratos
- file: so-user.lock # require so-user.lock file to be missing - file: so-user.lock # require so-user.lock file to be missing
# we dont want this added too early in setup, so the onlyif gates on the # we dont want this added too early in setup, so we add the onlyif to verify 'startup_states: highstate'
# /opt/so/state/setup-complete marker. The marker is written by # is in the minion config. That line is added before the final highstate during setup
# mark_setup_complete in setup/so-functions just before the final setup
# highstate (and by an upgrade-path state for systems set up under the old gate).
so-user_sync: so-user_sync:
cron.present: cron.present:
- user: root - user: root
- name: 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin /usr/sbin/so-user sync &>> /opt/so/log/soc/sync.log' - name: 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin /usr/sbin/so-user sync &>> /opt/so/log/soc/sync.log'
- identifier: so-user_sync - identifier: so-user_sync
- onlyif: "test -e /opt/so/state/setup-complete" - onlyif: "grep -x 'startup_states: highstate' /etc/salt/minion"
-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
-11
View File
@@ -10,16 +10,5 @@ NOROOT=1
set -e set -e
curl --retry 5 --retry-delay 60 -A "reposync/$(sync_options)" https://sigs.securityonion.net/checkup --output /tmp/checkup curl --retry 5 --retry-delay 60 -A "reposync/$(sync_options)" https://sigs.securityonion.net/checkup --output /tmp/checkup
dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionsync --download-metadata -p /nsm/repo/ dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionsync --download-metadata -p /nsm/repo/
createrepo /nsm/repo createrepo /nsm/repo
# The kernel repo section is deployed to repodownload.conf by the manager highstate, which
# runs AFTER this script during soup. On the first upgrade to a kernel-aware version the
# on-disk config still predates the section, so guard on its presence to avoid dnf's
# "Unknown repo: 'securityonionkernelsync'" aborting the sync (set -e). The next sync after the
# highstate deploys the section will pick it up.
if grep -q '^\[securityonionkernelsync\]' /opt/so/conf/reposync/repodownload.conf; then
dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionkernelsync --download-metadata -p /nsm/kernelrepo/
createrepo /nsm/kernelrepo
fi
+31 -203
View File
@@ -16,7 +16,6 @@ POSTVERSION=$INSTALLEDVERSION
INSTALLEDSALTVERSION=$(salt --versions-report | grep Salt: | awk '{print $2}') INSTALLEDSALTVERSION=$(salt --versions-report | grep Salt: | awk '{print $2}')
BATCHSIZE=5 BATCHSIZE=5
SOUP_LOG=/root/soup.log SOUP_LOG=/root/soup.log
SOUP_DEBUG_LOG=/root/soup-debug.log
WHATWOULDYOUSAYYAHDOHERE=soup WHATWOULDYOUSAYYAHDOHERE=soup
whiptail_title='Security Onion UPdater' whiptail_title='Security Onion UPdater'
NOTIFYCUSTOMELASTICCONFIG=false NOTIFYCUSTOMELASTICCONFIG=false
@@ -35,7 +34,6 @@ if [[ -f /etc/salt/cloud.profiles.d/socloud.conf ]]; then
fi fi
# used to display messages to the user at the end of soup # used to display messages to the user at the end of soup
declare -a FINAL_MESSAGE_QUEUE=() declare -a FINAL_MESSAGE_QUEUE=()
SOUP_ERR_CONTEXT=
check_err() { check_err() {
@@ -116,52 +114,11 @@ check_err() {
echo "$err_msg" echo "$err_msg"
fi fi
if [[ -n $SOUP_ERR_CONTEXT ]]; then
echo ""
printf '%s\n' "$SOUP_ERR_CONTEXT"
fi
echo "SOUP XTRACE debug log (if enabled) at $SOUP_DEBUG_LOG. Re-run soup with SOUP_DEBUG=1 to create $SOUP_DEBUG_LOG"
exit $exit_code exit $exit_code
fi fi
} }
# Collect bash error context before passing off to check_err()
on_err() {
local exit_code=$?
# Ignore failures in blocks that explicitly disabled errexit with `set +e`.
[[ $- == *e* ]] || return $exit_code
# turn off xtrace to prevent added noise in debug log
set +x 2>/dev/null || true
# Use first error context, multiple errors can happen with command substitutions or nested functions. We just need context from the initial error.
[[ -n $SOUP_ERR_CONTEXT ]] && return $exit_code
local cmd=$BASH_COMMAND
local line=${BASH_LINENO[0]}
local function=${FUNCNAME[1]:-main}
local source=${BASH_SOURCE[1]##*/}
local -a err_lines=(
"ERROR on: ${cmd}"
" source: ${source}:${line} in ${function}()"
)
local i caller_line caller_src caller_func
for ((i=2; i<${#FUNCNAME[@]}-1; i++)); do
caller_line=${BASH_LINENO[$((i-1))]}
[[ -n $caller_line && $caller_line -gt 0 ]] || continue
caller_src=${BASH_SOURCE[$i]##*/}
caller_func=${FUNCNAME[$i]:-main}
err_lines+=(" called by: ${caller_src}:${caller_line} in ${caller_func}()")
done
SOUP_ERR_CONTEXT=$(printf '%s\n' "${err_lines[@]}")
return $exit_code
}
airgap_mounted() { airgap_mounted() {
# Let's see if the ISO is already mounted. # Let's see if the ISO is already mounted.
if [[ -f /tmp/soagupdate/SecurityOnion/VERSION ]]; then if [[ -f /tmp/soagupdate/SecurityOnion/VERSION ]]; then
@@ -231,6 +188,13 @@ airgap_update_dockers() {
fi fi
} }
backup_old_states_pillars() {
tar czf /nsm/backup/$(echo $INSTALLEDVERSION)_$(date +%Y%m%d-%H%M%S)_soup_default_states_pillars.tar.gz /opt/so/saltstack/default/
tar czf /nsm/backup/$(echo $INSTALLEDVERSION)_$(date +%Y%m%d-%H%M%S)_soup_local_states_pillars.tar.gz /opt/so/saltstack/local/
}
update_registry() { update_registry() {
docker stop so-dockerregistry docker stop so-dockerregistry
docker rm so-dockerregistry docker rm so-dockerregistry
@@ -245,7 +209,6 @@ check_airgap() {
UPDATE_DIR=/tmp/soagupdate/SecurityOnion UPDATE_DIR=/tmp/soagupdate/SecurityOnion
AGDOCKER=/tmp/soagupdate/docker AGDOCKER=/tmp/soagupdate/docker
AGREPO=/tmp/soagupdate/minimal/Packages AGREPO=/tmp/soagupdate/minimal/Packages
AGUEKREPO=/tmp/soagupdate/uek/Packages
else else
is_airgap=1 is_airgap=1
fi fi
@@ -387,11 +350,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() {
@@ -408,9 +370,8 @@ preupgrade_changes() {
# This function is to add any new pillar items if needed. # This function is to add any new pillar items if needed.
echo "Checking to see if changes are needed." echo "Checking to see if changes are needed."
[[ "$INSTALLEDVERSION" =~ ^2\.4\.21[0-9]+$ ]] && up_to_3.0.0 [[ "$INSTALLEDVERSION" =~ ^2\.4\.21[0-9]+$ ]] && up_to_3.0.0
[[ "$INSTALLEDVERSION" == "3.0.0" ]] && up_to_3.1.0 [[ "$INSTALLEDVERSION" == "3.0.0" ]] && up_to_3.1.0
[[ "$INSTALLEDVERSION" == "3.1.0" ]] && up_to_3.2.0
true true
} }
@@ -420,7 +381,6 @@ postupgrade_changes() {
[[ "$POSTVERSION" =~ ^2\.4\.21[0-9]+$ ]] && post_to_3.0.0 [[ "$POSTVERSION" =~ ^2\.4\.21[0-9]+$ ]] && post_to_3.0.0
[[ "$POSTVERSION" == "3.0.0" ]] && post_to_3.1.0 [[ "$POSTVERSION" == "3.0.0" ]] && post_to_3.1.0
[[ "$POSTVERSION" == "3.1.0" ]] && post_to_3.2.0
true true
} }
@@ -782,121 +742,6 @@ post_to_3.1.0() {
### 3.1.0 End ### ### 3.1.0 End ###
### 3.2.0 Scripts ###
bootstrap_so_soc_database() {
# init-db.sh is mounted into so-postgres at /docker-entrypoint-initdb.d/init-db.sh
# and runs automatically only on a fresh data directory. Hosts upgrading from
# 3.1.0 already have /nsm/postgres populated, so the so_soc bootstrap block
# added in 3.2 never fires. Re-run the script explicitly; it's idempotent.
echo "Bootstrapping so_soc database via init-db.sh."
# The postgres image has no USER directive, so `docker exec` defaults to
# root, and the container env intentionally omits POSTGRES_USER (the upstream
# entrypoint defaults it transiently during first-init only). Recreate both
# so psql inside init-db.sh resolves the connect user correctly.
local exec_cmd="docker exec -u postgres -e POSTGRES_USER=postgres so-postgres bash /docker-entrypoint-initdb.d/init-db.sh"
if ! /usr/sbin/so-postgres-wait; then
FINAL_MESSAGE_QUEUE+=("WARNING: so-postgres was not ready during the 3.2.0 upgrade; the so_soc database may not have been bootstrapped. Re-run manually: $exec_cmd")
return 0
fi
if ! $exec_cmd; then
FINAL_MESSAGE_QUEUE+=("WARNING: init-db.sh failed inside so-postgres during the 3.2.0 upgrade; the so_soc database may not have been bootstrapped. Re-run manually: $exec_cmd")
return 0
fi
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
set +e
if ! current_template=$(so-elasticsearch-query "_index_template/.kibana_streams" --retry 3 --retry-delay 5 --fail); then
echo "Index template .kibana_streams does not exist, skipping backport."
return 0
fi
set -e
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
}
# Runs kafka-features.sh upgrade --release-version $1
# Upgrades Kafka KRaft cluster metadata
update_kafka_metadata() {
metadata_version="$1"
global_pillar="/opt/so/saltstack/local/pillar/global/soc_global.sls"
if PIPELINE=$(so-yaml.py get -r "$global_pillar" global.pipeline 2> /dev/null) && [[ "$PIPELINE" == "KAFKA" ]]; then
kafka_nodes_raw=$(salt-call pillar.get kafka:nodes --out=json)
if kafka_nodes=$(jq -er '.local | select(type == "object" and length > 0)' <<< "$kafka_nodes_raw"); then
bootstrap_servers=$(jq -r '[to_entries[] | select(.value.role | contains("broker")) | "\(.value.ip):9092"] | join(",")' <<< "$kafka_nodes")
echo "Upgrading Kafka KRaft cluster version"
so-kafka-cli kafka-features.sh --bootstrap-server "$bootstrap_servers" --command-config /opt/kafka/config/kraft/client.properties upgrade --release-version "$metadata_version" 2>/dev/null || true
return 0
else
FINAL_MESSAGE_QUEUE+=("WARNING: Unable to automatically perform Kafka KRaft cluster metadata update. This step can be performed manually using the following command (replacing \$BROKER_IP with the ip of atleast 1 available Kafka broker):")
FINAL_MESSAGE_QUEUE+=(" - so-kafka-cli kafka-features.sh --bootstrap-server \$BROKER_IP:9092 --command-config /opt/kafka/config/kraft/client.properties upgrade --release-version $metadata_version")
fi
else
echo "Nothing to do!"
fi
}
up_to_3.2.0() {
fix_logstash_0013_lumberjack_pipeline_name
pin_elasticsearch_data_retention_method
INSTALLEDVERSION=3.2.0
}
post_to_3.2.0() {
bootstrap_so_soc_database
# Including agent regen script here since it was missed in post_to_3.1.0
echo "Regenerating Elastic Agent Installers"
/sbin/so-elastic-agent-gen-installers
kibana_backport_streams_index_template
update_kafka_metadata "4.3"
POSTVERSION=3.2.0
}
### 3.2.0 End ###
repo_sync() { repo_sync() {
echo "Sync the local repo." echo "Sync the local repo."
@@ -1005,19 +850,13 @@ update_airgap_rules() {
rsync -a $UPDATE_DIR/agrules/securityonion-resources/* /nsm/securityonion-resources/ rsync -a $UPDATE_DIR/agrules/securityonion-resources/* /nsm/securityonion-resources/
} }
update_airgap_repos() { update_airgap_repo() {
# Update the files in the repo # Update the files in the repo
echo "Syncing new updates to /nsm/repo & /nsm/kernelrepo" echo "Syncing new updates to /nsm/repo"
# Airgap soup copies new files into the local repo, but doesn't remove old packages. Retaining the ability to rollback package updates rsync -a $AGREPO/* /nsm/repo/
rsync -a "$AGREPO"/ /nsm/repo/ echo "Creating repo"
rsync -a "$AGUEKREPO"/ /nsm/kernelrepo/
dnf -y install yum-utils createrepo_c dnf -y install yum-utils createrepo_c
echo "Running createrepo for /nsm/repo"
createrepo /nsm/repo createrepo /nsm/repo
echo "Running createrepo for /nsm/kernelrepo"
createrepo /nsm/kernelrepo
} }
update_salt_mine() { update_salt_mine() {
@@ -1689,7 +1528,13 @@ EOF
# Keeping this block in case we need to do a hotfix that requires salt update # Keeping this block in case we need to do a hotfix that requires salt update
apply_hotfix() { apply_hotfix() {
echo "No actions required. ($INSTALLEDVERSION/$HOTFIXVERSION)" if [[ "$INSTALLEDVERSION" == "3.1.0" ]] ; then
# Do not remove this fix_logstash_0013_lumberjack_pipeline_name in future hotfixes without first validating older
# installs referencing "so/0013_input_lumberjack_fleet.conf" via pillar are upgradable
fix_logstash_0013_lumberjack_pipeline_name
else
echo "No actions required. ($INSTALLEDVERSION/$HOTFIXVERSION)"
fi
} }
failed_soup_restore_items() { failed_soup_restore_items() {
@@ -1761,19 +1606,19 @@ main() {
echo "Verifying we have the latest soup script." echo "Verifying we have the latest soup script."
verify_latest_update_script verify_latest_update_script
echo "Verifying Elasticsearch version compatibility across the grid before upgrading."
verify_es_version_compatibility
echo "Let's see if we need to update Security Onion." echo "Let's see if we need to update Security Onion."
upgrade_check upgrade_check
upgrade_space upgrade_space
echo "Verifying Elasticsearch version compatibility across the grid before upgrading."
verify_es_version_compatibility
echo "Checking for Salt Master and Minion updates." echo "Checking for Salt Master and Minion updates."
upgrade_check_salt upgrade_check_salt
set -e set -e
if [[ $is_airgap -eq 0 ]]; then if [[ $is_airgap -eq 0 ]]; then
update_airgap_repos update_airgap_repo
dnf clean all dnf clean all
check_os_updates check_os_updates
elif [[ $OS == 'oracle' ]]; then elif [[ $OS == 'oracle' ]]; then
@@ -1787,8 +1632,7 @@ main() {
echo "Applying $HOTFIXVERSION hotfix" echo "Applying $HOTFIXVERSION hotfix"
# since we don't run the backup.config_backup state on import we wont snapshot previous version states and pillars # since we don't run the backup.config_backup state on import we wont snapshot previous version states and pillars
if [[ ! "$MINION_ROLE" == "import" ]]; then if [[ ! "$MINION_ROLE" == "import" ]]; then
echo "Running so-config-backup script." backup_old_states_pillars
/sbin/so-config-backup
fi fi
copy_new_files copy_new_files
create_local_directories "/opt/so/saltstack/default" create_local_directories "/opt/so/saltstack/default"
@@ -1844,8 +1688,8 @@ main() {
# since we don't run the backup.config_backup state on import we wont snapshot previous version states and pillars # since we don't run the backup.config_backup state on import we wont snapshot previous version states and pillars
if [[ ! "$MINION_ROLE" == "import" ]]; then if [[ ! "$MINION_ROLE" == "import" ]]; then
echo "" echo ""
echo "Running so-config-backup script." echo "Creating snapshots of default and local Salt states and pillars and saving to /nsm/backup/"
/sbin/so-config-backup backup_old_states_pillars
fi fi
echo "" echo ""
@@ -2057,20 +1901,4 @@ EOF
read -r input read -r input
fi fi
set -o errtrace main "$@" | tee -a $SOUP_LOG
trap on_err ERR
if [[ $SOUP_DEBUG == 1 ]]; then
if [ -f $SOUP_DEBUG_LOG ]; then
current_time=$(date +%Y%m%d.%H%M%S)
mv $SOUP_DEBUG_LOG $SOUP_DEBUG_LOG.$INSTALLEDVERSION.$current_time
fi
exec {SOUP_XTRACE_FD}>>"$SOUP_DEBUG_LOG"
export SOUP_XTRACE_FD
BASH_XTRACEFD=$SOUP_XTRACE_FD
PS4='+ [${BASH_SOURCE##*/}:${LINENO} ${FUNCNAME[0]:-main}()] | '
set -x
export SOUP_DEBUG
fi
main "$@" 2>&1 | tee -a $SOUP_LOG
-1
View File
@@ -59,7 +59,6 @@ so-nginx:
- /opt/so/conf/navigator/layers/:/opt/socore/html/navigator/assets/so:ro - /opt/so/conf/navigator/layers/:/opt/socore/html/navigator/assets/so:ro
- /opt/so/conf/navigator/config.json:/opt/socore/html/navigator/assets/config.json:ro - /opt/so/conf/navigator/config.json:/opt/socore/html/navigator/assets/config.json:ro
- /nsm/repo:/opt/socore/html/repo:ro - /nsm/repo:/opt/socore/html/repo:ro
- /nsm/kernelrepo:/opt/socore/html/kernelrepo:ro
- /nsm/rules:/nsm/rules:ro - /nsm/rules:/nsm/rules:ro
{% if NGINXMERGED.external_suricata %} {% if NGINXMERGED.external_suricata %}
- /opt/so/rules/nids/suri:/surirules:ro - /opt/so/rules/nids/suri:/surirules:ro
-10
View File
@@ -323,16 +323,6 @@ http {
autoindex_localtime on; autoindex_localtime on;
} }
location /kernelrepo/ {
allow all;
sendfile on;
sendfile_max_chunk 1m;
autoindex on;
autoindex_exact_size off;
autoindex_format html;
autoindex_localtime on;
}
location /influxdb/ { location /influxdb/ {
auth_request /auth/sessions/whoami; auth_request /auth/sessions/whoami;
rewrite /influxdb/api/(.*) /api/$1 break; rewrite /influxdb/api/(.*) /api/$1 break;
+1 -2
View File
@@ -17,7 +17,6 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E
END IF; END IF;
END END
\$\$; \$\$;
GRANT ALL ON SCHEMA public TO "$SO_POSTGRES_USER";
GRANT ALL PRIVILEGES ON DATABASE "$POSTGRES_DB" TO "$SO_POSTGRES_USER"; GRANT ALL PRIVILEGES ON DATABASE "$POSTGRES_DB" TO "$SO_POSTGRES_USER";
-- Lock the SOC database down at the connect layer; PUBLIC gets CONNECT -- Lock the SOC database down at the connect layer; PUBLIC gets CONNECT
-- by default, which would let per-minion telegraf roles open sessions -- by default, which would let per-minion telegraf roles open sessions
@@ -32,4 +31,4 @@ EOSQL
# only ensures the shared database exists on first initialization. # only ensures the shared database exists on first initialization.
if ! psql -U "$POSTGRES_USER" -tAc "SELECT 1 FROM pg_database WHERE datname='so_telegraf'" | grep -q 1; then if ! psql -U "$POSTGRES_USER" -tAc "SELECT 1 FROM pg_database WHERE datname='so_telegraf'" | grep -q 1; then
psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -c "CREATE DATABASE so_telegraf" psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -c "CREATE DATABASE so_telegraf"
fi fi
+16 -2
View File
@@ -18,12 +18,26 @@ include:
{% set TG_OUT = TELEGRAFMERGED.output | upper %} {% set TG_OUT = TELEGRAFMERGED.output | upper %}
{% if TG_OUT in ['POSTGRES', 'BOTH'] %} {% if TG_OUT in ['POSTGRES', 'BOTH'] %}
# docker_container.running returns as soon as the container starts, but on
# first-init docker-entrypoint.sh starts a temporary postgres with
# `listen_addresses=''` to run /docker-entrypoint-initdb.d scripts, then
# shuts it down before exec'ing the real CMD. A default pg_isready check
# (Unix socket) passes during that ephemeral phase and races the shutdown
# with "the database system is shutting down". Checking TCP readiness on
# 127.0.0.1 only succeeds after the final postgres binds the port.
postgres_wait_ready: postgres_wait_ready:
cmd.run: cmd.run:
- name: /usr/sbin/so-postgres-wait - name: |
for i in $(seq 1 60); do
if docker exec so-postgres pg_isready -h 127.0.0.1 -U postgres -q 2>/dev/null; then
exit 0
fi
sleep 2
done
echo "so-postgres did not accept TCP connections within 120s" >&2
exit 1
- require: - require:
- docker_container: so-postgres - docker_container: so-postgres
- file: postgres_sbin
# Ensure the shared Telegraf database exists. init-db.sh only runs on a # Ensure the shared Telegraf database exists. init-db.sh only runs on a
# fresh data dir, so hosts upgraded onto an existing /nsm/postgres volume # fresh data dir, so hosts upgraded onto an existing /nsm/postgres volume
-32
View File
@@ -1,32 +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.
# Wait for the so-postgres container to accept TCP connections.
#
# docker_container.running returns as soon as the container starts, but on
# first-init docker-entrypoint.sh starts a temporary postgres with
# `listen_addresses=''` to run /docker-entrypoint-initdb.d scripts, then
# shuts it down before exec'ing the real CMD. A default pg_isready check
# (Unix socket) passes during that ephemeral phase and races the shutdown
# with "the database system is shutting down". Checking TCP readiness on
# 127.0.0.1 only succeeds after the final postgres binds the port.
#
# Usage: so-postgres-wait [iterations] [sleep_seconds]
# Default: 60 iterations, 2s sleep (~120s total).
ITERATIONS=${1:-60}
SLEEP_SECONDS=${2:-2}
for i in $(seq 1 "$ITERATIONS"); do
if docker exec so-postgres pg_isready -h 127.0.0.1 -U postgres -q 2>/dev/null; then
exit 0
fi
sleep "$SLEEP_SECONDS"
done
echo "so-postgres did not accept TCP connections within $((ITERATIONS * SLEEP_SECONDS))s" >&2
exit 1
+1 -1
View File
@@ -16,7 +16,7 @@ include:
# Install the registry container # Install the registry container
so-dockerregistry: so-dockerregistry:
docker_container.running: docker_container.running:
- image: ghcr.io/security-onion-solutions/registry:3.1.1 - image: ghcr.io/security-onion-solutions/registry:3.0.0
- hostname: so-registry - hostname: so-registry
- networks: - networks:
- sobridge: - sobridge:
+2 -1
View File
@@ -1,5 +1,6 @@
{% from 'vars/globals.map.jinja' import GLOBALS %} {% from 'vars/globals.map.jinja' import GLOBALS %}
{% if GLOBALS.os == 'OEL' %} {# OL10 test path uses public repos; skip the SO repo state (which removes public repos and points at /nsm/repo) #}
{% if GLOBALS.os == 'OEL' and GLOBALS.os_version|int == 9 %}
include: include:
- repo.client.oracle - repo.client.oracle
{% endif %} {% endif %}
-30
View File
@@ -6,10 +6,6 @@
{% from 'repo/client/map.jinja' import REPOPATH with context %} {% from 'repo/client/map.jinja' import REPOPATH with context %}
{% from 'vars/globals.map.jinja' import GLOBALS %} {% from 'vars/globals.map.jinja' import GLOBALS %}
{% import_yaml 'salt/minion.defaults.yaml' as saltversion %}
{% set saltversion = saltversion.salt.minion.version %}
{% set INSTALLEDSALTVERSION = grains.saltversion %}
{% set role = grains.id.split('_') | last %} {% set role = grains.id.split('_') | last %}
{% set MANAGER = salt['grains.get']('master') %} {% set MANAGER = salt['grains.get']('master') %}
{% if grains['os'] == 'OEL' %} {% if grains['os'] == 'OEL' %}
@@ -61,32 +57,6 @@ so_repo:
- enabled: 1 - enabled: 1
- gpgcheck: 1 - gpgcheck: 1
# Only assign the kernel repo once this node's running salt matches the version this
# SO release ships. During a soup the grid is mid-salt-upgrade; gating here keeps the
# UEK8 kernel repo (and the kernel update it enables) from activating until the node is
# fully on the target salt, the same way other states defer across the upgrade window.
{% if saltversion | string == INSTALLEDSALTVERSION | string %}
so_kernel_repo:
pkgrepo.managed:
- name: securityonionkernel
- humanname: Security Onion Kernel Repo
{% if GLOBALS.is_manager %}
- baseurl: file:///nsm/kernelrepo/
{% else %}
- baseurl: https://{{ GLOBALS.repo_host }}/kernelrepo
{% endif %}
- enabled: 1
- gpgcheck: 1
# Supplementary kernel repo: tolerate it being empty/unreachable (e.g. before the
# manager has populated /nsm/kernelrepo) so a missing repomd.xml can't make every
# dnf/pkg operation on the grid fail.
- skip_if_unavailable: 1
# Only assign the kernel repo once physical NIC names are pinned by MAC, so the
# UEK8 kernel update can't renumber interfaces SO binds by name (see pin_nic_names
# in salt/common/init.sls, which drops this marker via /usr/sbin/so-nic-pin).
- onlyif: 'test -e /opt/so/state/nic_names_pinned'
{% endif %}
{% endif %} {% endif %}
# TODO: Add a pillar entry for custom repos # TODO: Add a pillar entry for custom repos
-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
-31
View File
@@ -1,31 +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-highstate.service, a Type=oneshot
# RemainAfterExit=yes unit that runs `salt-call state.highstate` exactly once
# per system boot. Replaces the legacy `startup_states: highstate` minion
# config, which fired on every salt-minion service restart (causing a redundant
# highstate whenever a highstate itself restarted salt-minion).
include:
- systemd.reload
so_boot_highstate_unit_file:
file.managed:
- name: /etc/systemd/system/so-boot-highstate.service
- source: salt://salt/service/so-boot-highstate.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 -- this just keeps
# `systemctl is-enabled` honest for the sync_es_users gate.
so_boot_highstate_service:
service.enabled:
- name: so-boot-highstate.service
- onlyif: test -e /opt/so/state/setup-complete
- require:
- file: so_boot_highstate_unit_file
- module: systemd_reload
+4 -27
View File
@@ -17,7 +17,6 @@ include:
- repo.client - repo.client
- salt.mine_functions - salt.mine_functions
- salt.minion.service_file - salt.minion.service_file
- salt.minion.boot_highstate
{% if GLOBALS.is_manager %} {% if GLOBALS.is_manager %}
- ca.signing_policy - ca.signing_policy
{% endif %} {% endif %}
@@ -81,33 +80,11 @@ set_log_levels:
- "log_level: info" - "log_level: info"
- "log_level_logfile: info" - "log_level_logfile: info"
# startup_states: highstate caused a full highstate to run on every enable_startup_states:
# salt-minion service start, including the restart triggered when a highstate file.uncomment:
# itself modified the minion config (beacons, mine, unit file). Replaced by
# so-boot-highstate.service (managed in salt.minion.boot_highstate), which
# runs once per system boot only. Strip the line from /etc/salt/minion on
# upgrade; both the commented and uncommented forms historically existed.
remove_startup_states:
file.line:
- name: /etc/salt/minion - name: /etc/salt/minion
- match: 'startup_states: highstate' - regex: '^startup_states: highstate$'
- mode: delete - unless: pgrep so-setup
# Upgrade-path bridge: systems that already passed setup under the old gate
# (`grep -x 'startup_states: highstate' /etc/salt/minion`) get a /opt/so/state/setup-complete
# marker so so-boot-highstate.service can be enabled and the so-user_sync cron
# in sync_es_users.sls keeps installing. Setup-in-progress systems instead get
# the marker from `mark_setup_complete` in setup/so-functions at the right
# moment. `replace: false` means we never overwrite a marker once written.
mark_setup_complete_for_upgrades:
file.managed:
- name: /opt/so/state/setup-complete
- replace: false
- makedirs: True
- onlyif: "grep -qx 'startup_states: highstate' /etc/salt/minion"
- require_in:
- file: remove_startup_states
- service: so_boot_highstate_service
{% endif %} {% endif %}
@@ -1,14 +0,0 @@
[Unit]
Description=Security Onion boot-time highstate (runs once per boot)
After=salt-minion.service network-online.target docker.service
Wants=network-online.target docker.service
Requires=salt-minion.service
ConditionPathExists=/opt/so/state/setup-complete
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/salt-call state.highstate -l info queue=True
[Install]
WantedBy=multi-user.target
@@ -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
+5
View File
@@ -8,6 +8,11 @@ set_role_grain:
- name: role - name: role
- value: so-{{ grains.id.split("_") | last }} - value: so-{{ grains.id.split("_") | last }}
set_highstate:
file.append:
- name: /etc/salt/minion
- text: 'startup_states: highstate'
enable_salt_minion: enable_salt_minion:
service.enabled: service.enabled:
- name: salt-minion - name: salt-minion
-24
View File
@@ -134,30 +134,6 @@ socsigmasopipeline:
- group: 939 - group: 939
- mode: 600 - mode: 600
socsigmaplaybookpipeline:
file.managed:
- name: /opt/so/conf/soc/sigma_playbook_pipeline.yaml
- source: salt://soc/files/soc/sigma_playbook_pipeline.yaml
- user: 939
- group: 939
- mode: 600
socplaybookplaceholdermap:
file.managed:
- name: /opt/so/conf/soc/playbook_placeholder_map.yaml
- source: salt://soc/files/soc/playbook_placeholder_map.yaml
- user: 939
- group: 939
- mode: 600
socplaybookplaceholdermapcustom:
file.managed:
- name: /opt/so/conf/soc/playbook_placeholder_map_custom.yaml
- source: salt://soc/files/soc/playbook_placeholder_map_custom.yaml
- user: 939
- group: 939
- mode: 600
socbanner: socbanner:
file.managed: file.managed:
- name: /opt/so/conf/soc/banner.md - name: /opt/so/conf/soc/banner.md
+4 -18
View File
@@ -1464,7 +1464,6 @@ soc:
sigmaRulePackages: sigmaRulePackages:
- core - core
- emerging_threats_addon - emerging_threats_addon
useEsql: false
elastic: elastic:
hostUrl: hostUrl:
remoteHostUrls: [] remoteHostUrls: []
@@ -1502,9 +1501,6 @@ soc:
- repo: https://github.com/Security-Onion-Solutions/securityonion-resources-playbooks - repo: https://github.com/Security-Onion-Solutions/securityonion-resources-playbooks
branch: main branch: main
folder: securityonion-normalized folder: securityonion-normalized
- repo: https://github.com/Security-Onion-Solutions/securityonion-resources-playbooks
branch: published
folder: sigma
airgap: airgap:
- repo: file:///nsm/airgap-resources/playbooks/securityonion-resources-playbooks - repo: file:///nsm/airgap-resources/playbooks/securityonion-resources-playbooks
branch: main branch: main
@@ -1523,16 +1519,6 @@ soc:
serviceAccountJSON: "" serviceAccountJSON: ""
serviceAccountLocation: "" serviceAccountLocation: ""
healthTimeoutSeconds: 5 healthTimeoutSeconds: 5
onionconfig:
saltstackDir: /opt/so/saltstack
bypassEnabled: false
postgres:
host: ""
port: 5432
sslMode: "allow"
database: securityonion
user: ""
password: ""
salt: salt:
queueDir: /opt/sensoroni/queue queueDir: /opt/sensoroni/queue
timeoutMs: 45000 timeoutMs: 45000
@@ -1774,13 +1760,13 @@ soc:
enabled: true enabled: true
queries: queries:
- name: Default Query - name: Default Query
description: Show all events grouped by module and dataset
query: '* | groupby event.module* event.dataset'
showSubtitle: true
- name: Observer
description: Show all events grouped by the observer host description: Show all events grouped by the observer host
query: '* | groupby observer.name' query: '* | groupby observer.name'
showSubtitle: true showSubtitle: true
- name: Log Type
description: Show all events grouped by module and dataset
query: '* | groupby event.module* event.dataset'
showSubtitle: true
- name: SOC - Auth - name: SOC - Auth
description: Users authenticated to SOC grouped by IP address and identity description: Users authenticated to SOC grouped by IP address and identity
query: 'event.dataset:kratos.audit AND msg:*authenticated* | groupby http.request.headers.x-real-ip user.name' query: 'event.dataset:kratos.audit AND msg:*authenticated* | groupby http.request.headers.x-real-ip user.name'
+1 -6
View File
@@ -45,10 +45,7 @@ so-soc:
- /opt/so/conf/soc/motd.md:/opt/sensoroni/html/motd.md:ro - /opt/so/conf/soc/motd.md:/opt/sensoroni/html/motd.md:ro
- /opt/so/conf/soc/banner.md:/opt/sensoroni/html/login/banner.md:ro - /opt/so/conf/soc/banner.md:/opt/sensoroni/html/login/banner.md:ro
- /opt/so/conf/soc/sigma_so_pipeline.yaml:/opt/sensoroni/sigma_so_pipeline.yaml:ro - /opt/so/conf/soc/sigma_so_pipeline.yaml:/opt/sensoroni/sigma_so_pipeline.yaml:ro
- /opt/so/conf/soc/sigma_playbook_pipeline.yaml:/opt/sensoroni/sigma_playbook_pipeline.yaml:ro - /opt/so/conf/soc/sigma_final_pipeline.yaml:/opt/sensoroni/sigma_final_pipeline.yaml:rw
- /opt/so/conf/soc/sigma_final_pipeline.yaml:/opt/sensoroni/sigma_final_pipeline.yaml:ro
- /opt/so/conf/soc/playbook_placeholder_map.yaml:/opt/sensoroni/playbook_placeholder_map.yaml:ro
- /opt/so/conf/soc/playbook_placeholder_map_custom.yaml:/opt/sensoroni/playbook_placeholder_map_custom.yaml:ro
- /opt/so/conf/soc/custom.js:/opt/sensoroni/html/js/custom.js:ro - /opt/so/conf/soc/custom.js:/opt/sensoroni/html/js/custom.js:ro
- /opt/so/conf/soc/custom_roles:/opt/sensoroni/rbac/custom_roles:ro - /opt/so/conf/soc/custom_roles:/opt/sensoroni/rbac/custom_roles:ro
- /opt/so/conf/soc/soc_users_roles:/opt/sensoroni/rbac/users_roles:rw - /opt/so/conf/soc/soc_users_roles:/opt/sensoroni/rbac/users_roles:rw
@@ -102,8 +99,6 @@ so-soc:
- file: soccustomroles - file: soccustomroles
- file: socusersroles - file: socusersroles
- file: socclientsroles - file: socclientsroles
- file: socplaybookplaceholdermap
- file: socplaybookplaceholdermapcustom
delete_so-soc_so-status.disabled: delete_so-soc_so-status.disabled:
file.uncomment: file.uncomment:
@@ -1,49 +0,0 @@
# Global Playbook placeholder map: %token% -> event field path.
#
# Loaded by the SOC Playbook module and used to resolve `field|expand:%placeholder%` values
# from an alert when converting playbook questions to OQL.
# Left: the %token% used in a question
# Right: the event field its value is read from (event_data.-nested or bare; the module
# tries both).
#
# Example: with `src_ip: source.ip` (below), a question that writes
# `source.ip|expand: '%src_ip%'` resolves %src_ip% to the alert's source.ip at convert time.
#
# This is the global base layer. To add or override tokens edit playbook_placeholder_map_custom.yaml.
# those entries overlay this map and win on conflict.
CommandLine: process.command_line
CurrentDirectory: process.working_directory
Image: process.executable
ImageLoaded: dll.name
ParentImage: process.parent.executable
ParentName: process.parent.name
ParentProcessGuid: process.parent.entity_id
ProcessGuid: process.entity_id
TargetFilename: file.name
TargetObject: registry.path
TargetUserName: user.target.name
User: user.name
community_id: network.community_id
dns_resolved_ip: dns.resolved_ip
document_id: soc_id
dst_ip: destination.ip
dst_port: destination.port
event_data_source_ip: source.ip
file_path: file.path
file_dirs: process.file_dirs
file_name: process.name
file_paths: process.file_paths
hostname: host.name
private_ip: network.private_ip
public_ip: network.public_ip
related_hosts: related.hosts
related_ip: related.ip
src_ip: source.ip
dns_query_name: dns.query_name
flow_id: log.id.uid
payload: network.data.decoded
rule_category: rule.category
rule_name: rule.name
rule_uuid: rule.uuid
src_port: source.port
@@ -1,14 +0,0 @@
# Custom Playbook placeholder map: %token% -> event field path.
#
#
# Left: the %token% used in a playbook question.
# Right: the event field its value is read from (event_data.-nested or bare; the module tries
# both). Note: a token that is simply named after a flat event field resolves automatically
# without an entry here - only add a mapping when the token name differs from the field name.
#
# Example:
#
# account_id: cloudflare.account_id
#
# A question that writes
# `account_id|expand: '%account_id%'` resolves %account_id% from the alert at convert time.
@@ -1,12 +0,0 @@
name: Security Onion - Playbook Pipeline
priority: 97
transformations:
# Route string fields to their lowercase-normalized .caseless subfield so wildcard
# matches are case-insensitive.
- id: case_insensitive_string_fields
type: field_name_mapping
mapping:
process.executable: process.executable.caseless
process.parent.executable: process.parent.executable.caseless
process.command_line: process.command_line.caseless
process.parent.command_line: process.parent.command_line.caseless
-51
View File
@@ -63,14 +63,6 @@ transformations:
rule_conditions: rule_conditions:
- type: logsource - type: logsource
category: antivirus category: antivirus
# OS-agnostic process_creation scoping for product-less (NIDS/host-pivot) rules.
- id: process_creation_os_agnostic
type: add_condition
conditions:
event.category: process
rule_conditions:
- type: logsource
category: process_creation
# Transforms the `Hashes` field to ECS fields # Transforms the `Hashes` field to ECS fields
# ECS fields are used by the hash fields emitted by Elastic Defend # ECS fields are used by the hash fields emitted by Elastic Defend
# If shipped with Elastic Agent, sysmon logs will also have hashes mapped to ECS fields # If shipped with Elastic Agent, sysmon logs will also have hashes mapped to ECS fields
@@ -116,40 +108,6 @@ transformations:
- type: logsource - type: logsource
product: windows product: windows
category: driver_load category: driver_load
- id: ecs_fix_process_creation
type: field_name_mapping
mapping:
# bare `Hashes` (the combined-string case is broken out above)
winlog.event_data.Hashes: process.hash.sha256
winlog.event_data.IntegrityLevel: process.Ext.token.integrity_level_name
winlog.event_data.ParentName: process.parent.name
rule_conditions:
- type: logsource
product: windows
category: process_creation
- id: ecs_fix_registry_set
type: field_name_mapping
mapping:
winlog.event_data.Details: registry.data.strings
# field rename only; EventType values (SetValue/CreateKey) still differ from
# event.action values (modification/creation)
winlog.event_data.EventType: event.action
rule_conditions:
- type: logsource
product: windows
category: registry_set
- id: ecs_fix_image_load
type: field_name_mapping
mapping:
file.path: dll.path
file.code_signature.signed: dll.code_signature.exists
winlog.event_data.Signature: dll.code_signature.subject_name
file.code_signature.status: dll.code_signature.status
winlog.event_data.Hashes: dll.hash.sha256
rule_conditions:
- type: logsource
product: windows
category: image_load
- id: linux_security_add-fields - id: linux_security_add-fields
type: add_condition type: add_condition
conditions: conditions:
@@ -323,15 +281,6 @@ transformations:
rule_conditions: rule_conditions:
- type: logsource - type: logsource
category: file_event category: file_event
# Scope image_load rules to Elastic Endpoint library events (event.category:library, dll.*
# populated).
- id: endpoint_image_load_add-fields
type: add_condition
conditions:
event.category: 'library'
rule_conditions:
- type: logsource
category: image_load
# Maps network rules to all network logs # Maps network rules to all network logs
# This targets all network logs, all services, generated from endpoints and network # This targets all network logs, all services, generated from endpoints and network
- id: network_add-fields - id: network_add-fields
-8
View File
@@ -16,14 +16,6 @@
{% do SOCMERGED.config.server.update({'additionalCA': MANAGERMERGED.additionalCA}) %} {% do SOCMERGED.config.server.update({'additionalCA': MANAGERMERGED.additionalCA}) %}
{% do SOCMERGED.config.server.update({'insecureSkipVerify': MANAGERMERGED.insecureSkipVerify}) %} {% do SOCMERGED.config.server.update({'insecureSkipVerify': MANAGERMERGED.insecureSkipVerify}) %}
{% if not SOCMERGED.config.server.modules.postgres.host %}
{% do SOCMERGED.config.server.modules.postgres.update({'host': GLOBALS.manager}) %}
{% endif %}
{% if not SOCMERGED.config.server.modules.postgres.password %}
{% do SOCMERGED.config.server.modules.postgres.update({'password': salt['pillar.get']('postgres:auth:users:so_postgres_user:pass', '')}) %}
{% do SOCMERGED.config.server.modules.postgres.update({'user': salt['pillar.get']('postgres:auth:users:so_postgres_user:user', 'so_postgres')}) %}
{% endif %}
{# if SOCMERGED.config.server.modules.cases == httpcase details come from the soc pillar #} {# if SOCMERGED.config.server.modules.cases == httpcase details come from the soc pillar #}
{% if SOCMERGED.config.server.modules.cases != 'soc' %} {% if SOCMERGED.config.server.modules.cases != 'soc' %}
{% do SOCMERGED.config.server.modules.elastic.update({'casesEnabled': false}) %} {% do SOCMERGED.config.server.modules.elastic.update({'casesEnabled': false}) %}
+1 -51
View File
@@ -46,15 +46,7 @@ soc:
syntax: yaml syntax: yaml
file: True file: True
global: True global: True
advanced: False advanced: True
helpLink: security-onion-console-customization
playbook_placeholder_map_custom__yaml:
title: Playbook Placeholder Map
description: Custom mappings of Playbook %placeholder% tokens to event fields.
syntax: yaml
file: True
global: True
advanced: False
helpLink: security-onion-console-customization helpLink: security-onion-console-customization
config: config:
licenseKey: licenseKey:
@@ -391,11 +383,6 @@ soc:
global: True global: True
advanced: False advanced: False
helpLink: sigma helpLink: sigma
useEsql:
description: "(Pre-release) Use Elasticsearch Piped Query Language (ES|QL) instead of EQL (Elastic Query Language) for Elasticsearch queries. The Sigma converter will output ES|QL instead of EQL, allowing support for correlations."
global: True
advanced: True
forcedType: bool
elastic: elastic:
index: index:
description: Comma-separated list of indices or index patterns (wildcard "*" supported) that SOC will search for records. description: Comma-separated list of indices or index patterns (wildcard "*" supported) that SOC will search for records.
@@ -466,42 +453,6 @@ soc:
description: Duration (in milliseconds) that must elapse after a grid node fails to check-in before the node will be marked offline (fault). description: Duration (in milliseconds) that must elapse after a grid node fails to check-in before the node will be marked offline (fault).
global: True global: True
advanced: True advanced: True
onionconfig:
saltstackDir:
description: Root directory containing the SaltStack tree that SOC reads and writes configuration from. Should not be changed under normal circumstances.
global: True
advanced: True
bypassEnabled:
description: When enabled, errors encountered while reading the SaltStack pillar tree (missing files, unreadable directories, etc.) are logged but do not prevent SOC from starting or serving settings. Intended for advanced troubleshooting and recovery scenarios when the pillar tree is partially unreadable.
global: True
advanced: True
forcedType: bool
postgres:
host:
description: Hostname or IP address of the PostgreSQL server used by SOC. Defaults to the manager hostname.
global: True
advanced: True
port:
description: Port of the PostgreSQL server used by SOC.
global: True
advanced: True
sslMode:
description: "Use encrypted connections to the PostgreSQL server. Must be one of the following values: disable, allow, prefer, require, verify-ca, verify-full. Defaults to allow."
global: True
advanced: True
database:
description: Database used by SOC to authenticate to the PostgreSQL server.
global: True
advanced: True
user:
description: Username used by SOC to authenticate to the PostgreSQL server.
global: True
advanced: True
password:
description: Password used by SOC to authenticate to the PostgreSQL server.
global: True
sensitive: True
advanced: True
salt: salt:
longRelayTimeoutMs: longRelayTimeoutMs:
description: Duration (in milliseconds) to wait for a response from the Salt API when executing tasks known for being long running before giving up and showing an error on the SOC UI. description: Duration (in milliseconds) to wait for a response from the Salt API when executing tasks known for being long running before giving up and showing an error on the SOC UI.
@@ -867,7 +818,6 @@ soc:
description: List of available external tools visible in the SOC UI. Each tool is defined in JSON object notation, and must include the "name" key and "link" key, where the link is the tool's URL. description: List of available external tools visible in the SOC UI. Each tool is defined in JSON object notation, and must include the "name" key and "link" key, where the link is the tool's URL.
global: True global: True
advanced: True advanced: True
multiline: True
forcedType: "[]{}" forcedType: "[]{}"
exportNodeId: exportNodeId:
description: The node ID on which export jobs will be executed. description: The node ID on which export jobs will be executed.
+2 -3
View File
@@ -65,11 +65,10 @@ so-suricata:
- file: suriclassifications - file: suriclassifications
surirulereload: surirulereload:
cmd.run: cmd.run:
- name: /usr/sbin/so-suricata-reload-rules >> /opt/so/log/suricata/reload.log 2>&1 - name: /usr/sbin/so-suricata-reload-rules >> /opt/so/log/suricata/reload.log 2>&1
- onchanges: - onchanges:
- file: surirulesync - file: surirulesync
- onlyif: test -f /opt/so/rules/suricata/all-rulesets.rules
- require: - require:
- docker_container: so-suricata - docker_container: so-suricata
@@ -7,59 +7,5 @@
. /usr/sbin/so-common . /usr/sbin/so-common
RULES_FILE="/opt/so/rules/suricata/all-rulesets.rules" retry 60 3 'docker exec so-suricata /opt/suricata/bin/suricatasc -c reload-rules /var/run/suricata/suricata-command.socket' '{"message":"done","return":"OK"}' || fail "The Suricata container was not ready in time."
SOCKET="/var/run/suricata/suricata-command.socket" retry 60 3 'docker exec so-suricata /opt/suricata/bin/suricatasc -c ruleset-reload-nonblocking /var/run/suricata/suricata-command.socket' '{"message":"done","return":"OK"}' || fail "The Suricata container was not ready in time."
SURICATASC="docker exec so-suricata /opt/suricata/bin/suricatasc"
# Format an epoch as a human-readable local timestamp for log messages.
fmt_time() { date -d "@$1" '+%Y-%m-%d %H:%M:%S %Z' 2>/dev/null; }
# Prefix each input line with the current timestamp.
timestamp_lines() { while IFS= read -r line; do printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S %Z')" "$line"; done; }
# Epoch of Suricata's last *completed* ruleset reload; non-zero return on failure.
suricata_reload_epoch() {
local out ts
out=$($SURICATASC -c ruleset-reload-time "$SOCKET" 2>/dev/null)
ts=$(echo "$out" | jq -r '.message[0].last_reload // empty' 2>/dev/null)
[ -n "$ts" ] || return 1
date -d "$ts" +%s 2>/dev/null
}
# Trigger a fresh reload and confirm Suricata is running a ruleset at least as new
# as the rules file. Returns 0 only when both hold, so retry keeps going until an
# in-progress reload clears and our own reload completes.
reload_and_verify() {
local out reload_epoch
out=$($SURICATASC -c reload-rules "$SOCKET")
echo "reload-rules: $out"
if [[ "$out" =~ "Reload already in progress" ]]; then
echo "A reload is already in progress; waiting for it to clear so a fresh reload can load the current ruleset."
return 1
fi
if [[ ! "$out" =~ '{"message":"done","return":"OK"}' ]]; then
echo "Suricata not ready or unexpected reload output; will retry."
return 1
fi
reload_epoch=$(suricata_reload_epoch) || { echo "Could not read ruleset-reload-time; will retry."; return 1; }
if [ "$reload_epoch" -ge "$target_mtime" ]; then
echo "Loaded ruleset is current: last reload ($(fmt_time "$reload_epoch")) is newer than rules file ($(fmt_time "$target_mtime"))."
return 0
fi
echo "Loaded ruleset is stale: last reload ($(fmt_time "$reload_epoch")) is older than rules file ($(fmt_time "$target_mtime")); retrying."
return 1
}
# Run the reload/verify, timestamping every line of output (ours and the
# retry/fail helpers') so reload.log shows when each step ran. The pipeline is
# synchronous, so the log is fully flushed and ordered before we exit; the
# script's real exit code is preserved via PIPESTATUS.
{
# Epoch mtime of the ruleset we need Suricata to have loaded. Captured once so
# a file update mid-reload does not move the goalpost.
target_mtime=$(stat -c %Y "$RULES_FILE") || fail "Could not stat the Suricata rules file: $RULES_FILE"
retry 60 3 'reload_and_verify' || fail "Suricata did not load the current ruleset in time."
} 2>&1 | timestamp_lines
exit "${PIPESTATUS[0]}"
-1
View File
@@ -244,7 +244,6 @@
username = "{{ ES_USER }}" username = "{{ ES_USER }}"
password = "{{ ES_PASS }}" password = "{{ ES_PASS }}"
insecure_skip_verify = true insecure_skip_verify = true
cluster_health = true
{%- elif grains['role'] in ['so-searchnode'] %} {%- elif grains['role'] in ['so-searchnode'] %}
[[inputs.elasticsearch]] [[inputs.elasticsearch]]
servers = ["https://{{ NODEIP }}:9200"] servers = ["https://{{ NODEIP }}:9200"]
+6
View File
@@ -83,6 +83,7 @@ base:
- zeek - zeek
- strelka - strelka
- elastalert - elastalert
- utility
- elasticfleet - elasticfleet
- pcap.cleanup - pcap.cleanup
@@ -112,6 +113,7 @@ base:
- zeek - zeek
- strelka - strelka
- elastalert - elastalert
- utility
- elasticfleet - elasticfleet
- stig - stig
- kafka - kafka
@@ -139,6 +141,7 @@ base:
- elastic-fleet-package-registry - elastic-fleet-package-registry
- kibana - kibana
- elastalert - elastalert
- utility
- elasticfleet - elasticfleet
- stig - stig
- kafka - kafka
@@ -165,6 +168,7 @@ base:
- elastic-fleet-package-registry - elastic-fleet-package-registry
- kibana - kibana
- elastalert - elastalert
- utility
- elasticfleet - elasticfleet
- kafka - kafka
@@ -194,6 +198,7 @@ base:
- elastic-fleet-package-registry - elastic-fleet-package-registry
- kibana - kibana
- elastalert - elastalert
- utility
- elasticfleet - elasticfleet
- stig - stig
- kafka - kafka
@@ -217,6 +222,7 @@ base:
- elasticsearch - elasticsearch
- elastic-fleet-package-registry - elastic-fleet-package-registry
- kibana - kibana
- utility
- suricata - suricata
- zeek - zeek
- elasticfleet - elasticfleet
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# Wait for ElasticSearch to come up, so that we can query for version infromation
echo -n "Waiting for ElasticSearch..."
COUNT=0
ELASTICSEARCH_CONNECTED="no"
while [[ "$COUNT" -le 30 ]]; do
curl -K /opt/so/conf/elasticsearch/curl.config -k --output /dev/null --silent --head --fail -L https://{{ GLOBALS.manager_ip }}:9200
if [ $? -eq 0 ]; then
ELASTICSEARCH_CONNECTED="yes"
echo "connected!"
break
else
((COUNT+=1))
sleep 1
echo -n "."
fi
done
if [ "$ELASTICSEARCH_CONNECTED" == "no" ]; then
echo
echo -e "Connection attempt timed out. Unable to connect to ElasticSearch. \nPlease try: \n -checking log(s) in /var/log/elasticsearch/\n -running 'docker ps' \n -running 'sudo so-elastic-restart'"
echo
exit
fi
echo "Applying cross cluster search config..."
curl -K /opt/so/conf/elasticsearch/curl.config -s -k -XPUT -L https://{{ GLOBALS.manager_ip }}:9200/_cluster/settings \
-H 'Content-Type: application/json' \
-d "{\"persistent\": {\"search\": {\"remote\": {\"{{ grains.host }}\": {\"seeds\": [\"127.0.0.1:9300\"]}}}}}"
+22
View File
@@ -0,0 +1,22 @@
{% from 'allowed_states.map.jinja' import allowed_states %}
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% if sls in allowed_states %}
{% if grains['role'] in ['so-eval', 'so-import'] %}
fixsearch:
cmd.script:
- shell: /bin/bash
- cwd: /opt/so
- source: salt://utility/bin/eval
- template: jinja
- defaults:
GLOBALS: {{ GLOBALS }}
{% endif %}
{% else %}
{{sls}}_state_not_allowed:
test.fail_without_changes:
- name: {{sls}}_state_not_allowed
{% endif %}
+1
View File
@@ -31,6 +31,7 @@
'so_model': INIT.GRAINS.get('sosmodel',''), 'so_model': INIT.GRAINS.get('sosmodel',''),
'sensoroni_key': INIT.PILLAR.sensoroni.config.sensoronikey, 'sensoroni_key': INIT.PILLAR.sensoroni.config.sensoronikey,
'os': INIT.GRAINS.os, 'os': INIT.GRAINS.os,
'os_version': INIT.GRAINS.osmajorrelease,
'os_family': INIT.GRAINS.os_family, 'os_family': INIT.GRAINS.os_family,
'application_urls': {}, 'application_urls': {},
'manager_roles': [ 'manager_roles': [
+39 -76
View File
@@ -29,12 +29,8 @@ title() {
} }
fail_setup() { fail_setup() {
local err_msg=$1
if [[ -n "$err_msg" ]]; then
error "$err_msg"
fi
error "Setup encountered an unrecoverable failure, exiting" error "Setup encountered an unrecoverable failure, exiting"
echo "setup incomplete: $err_msg" > /root/failure touch /root/failure
exit 1 exit 1
} }
@@ -543,19 +539,16 @@ configure_minion() {
" x509_v2: true"\ " x509_v2: true"\
"log_level: info"\ "log_level: info"\
"log_level_logfile: info"\ "log_level_logfile: info"\
"log_file: /opt/so/log/salt/minion" >> "$minion_config" "log_file: /opt/so/log/salt/minion"\
"#startup_states: highstate" >> "$minion_config"
} }
mark_setup_complete() { checkin_at_boot() {
# Writes the setup-complete marker. Salt's so-boot-highstate.service local minion_config=/etc/salt/minion
# (boot-time oneshot) and the so-user_sync cron gate in
# salt/manager/sync_es_users.sls both key off this file.
local marker=/opt/so/state/setup-complete
info "Marking setup as complete" info "Enabling checkin at boot"
mkdir -p "$(dirname "$marker")" sed -i 's/#startup_states: highstate/startup_states: highstate/' "$minion_config"
touch "$marker"
} }
check_requirements() { check_requirements() {
@@ -701,7 +694,7 @@ compare_main_nic_ip() {
EOM EOM
[[ -n $TESTING ]] || whiptail --title "$whiptail_title" --msgbox "$message" 11 75 [[ -n $TESTING ]] || whiptail --title "$whiptail_title" --msgbox "$message" 11 75
kill -SIGINT "$(ps --pid $$ -oppid=)"; fail_setup "Main IP mismatch" kill -SIGINT "$(ps --pid $$ -oppid=)"; fail_setup
fi fi
else else
# Setup uses MAINIP, but since we ignore the equality condition when using a VPN # Setup uses MAINIP, but since we ignore the equality condition when using a VPN
@@ -759,7 +752,8 @@ configure_management_bond() {
info "Setting up $bond_name management interface with mode $bond_mode" info "Setting up $bond_name management interface with mode $bond_mode"
if [[ ${#MBNICS[@]} -eq 0 ]]; then if [[ ${#MBNICS[@]} -eq 0 ]]; then
fail_setup "No management bond NICs selected" error "[ERROR] No management bond NICs were selected."
fail_setup
fi fi
nmcli -t -f NAME con show | grep -Fxq "$bond_name" nmcli -t -f NAME con show | grep -Fxq "$bond_name"
@@ -889,7 +883,6 @@ create_repo() {
title "Create the repo directory" title "Create the repo directory"
logCmd "dnf -y install yum-utils createrepo_c" logCmd "dnf -y install yum-utils createrepo_c"
logCmd "createrepo /nsm/repo" logCmd "createrepo /nsm/repo"
logCmd "createrepo /nsm/kernelrepo"
} }
@@ -910,14 +903,15 @@ detect_cloud() {
detect_os() { detect_os() {
title "Detecting Base OS" title "Detecting Base OS"
if [ -f /etc/redhat-release ] && grep -q "Red Hat Enterprise Linux release 9" /etc/redhat-release && [ -f /etc/oracle-release ]; then if [ -f /etc/oracle-release ] && grep -qE "release (9|10)\b" /etc/oracle-release; then
OS=oracle OS=oracle
OSVER=9 OSVER=$(grep -oE "release [0-9]+" /etc/oracle-release | grep -oE "[0-9]+")
is_oracle=true is_oracle=true
is_rpm=true is_rpm=true
is_supported=true is_supported=true
else else
fail_setup "This OS is not supported. Security Onion requires Oracle Linux 9." info "This OS is not supported. Security Onion requires Oracle Linux 9 or 10."
fail_setup
fi fi
info "Found OS: $OS $OSVER" info "Found OS: $OS $OSVER"
@@ -925,7 +919,7 @@ detect_os() {
download_elastic_agent_artifacts() { download_elastic_agent_artifacts() {
if ! update_elastic_agent 2>&1 | tee -a "$setup_log"; then if ! update_elastic_agent 2>&1 | tee -a "$setup_log"; then
fail_setup "Failed to update Elastic Agent" fail_setup
fi fi
} }
@@ -983,8 +977,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
} }
@@ -1569,7 +1561,7 @@ proxy_validate() {
error "Received error: $proxy_test_err" error "Received error: $proxy_test_err"
if [[ -n $TESTING ]]; then if [[ -n $TESTING ]]; then
error "Exiting setup" error "Exiting setup"
kill -SIGINT "$(ps --pid $$ -oppid=)"; fail_setup "Proxy validation failed" kill -SIGINT "$(ps --pid $$ -oppid=)"; fail_setup
fi fi
fi fi
return $ret return $ret
@@ -1776,7 +1768,8 @@ ensure_pyyaml() {
local result=$? local result=$?
set +o pipefail set +o pipefail
if [[ $result -ne 0 ]] || ! rpm -q python3-pyyaml >/dev/null 2>&1; then if [[ $result -ne 0 ]] || ! rpm -q python3-pyyaml >/dev/null 2>&1; then
fail_setup "Failed to install python3-pyyaml (exit=$result)" error "Failed to install python3-pyyaml (exit=$result)"
fail_setup
fi fi
info "python3-pyyaml installed successfully" info "python3-pyyaml installed successfully"
} }
@@ -1790,6 +1783,15 @@ ensure_pyyaml() {
# - securityonion/salt/salt/minion.defaults.yaml # - securityonion/salt/salt/minion.defaults.yaml
securityonion_repo() { securityonion_repo() {
if [[ "$OSVER" == "10" ]]; then
# TEST PATH: Oracle Linux 10 uses the public OL10 + EPEL + Docker CE repos.
# Keep the stock /etc/yum.repos.d/* in place, skip the SO mirror and local reposync.
gpg_rpm_import
logCmd "dnf -y install oracle-epel-release-el10"
logCmd "dnf -y config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo"
logCmd "dnf repolist"
return
fi
# Remove all the current repos # Remove all the current repos
logCmd "dnf -v clean all" logCmd "dnf -v clean all"
logCmd "mkdir -vp /root/oldrepos" logCmd "mkdir -vp /root/oldrepos"
@@ -1814,16 +1816,6 @@ securityonion_repo() {
echo "mirrorlist=file:///etc/yum/mirror.txt" >> /etc/yum.repos.d/securityonion.repo echo "mirrorlist=file:///etc/yum/mirror.txt" >> /etc/yum.repos.d/securityonion.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "https://repo.securityonion.net/file/so-repo/prod/3/oracle/9-uek8" > /etc/yum/mirror-kernel.txt
echo "https://so-repo-east.s3.us-east-005.backblazeb2.com/prod/3/oracle/9-uek8" >> /etc/yum/mirror-kernel.txt
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "mirrorlist=file:///etc/yum/mirror-kernel.txt" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
# Supplementary kernel repo: tolerate it being empty/unreachable so a missing
# repomd.xml can't make every dnf operation fail before the repo is populated.
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
logCmd "dnf repolist" logCmd "dnf repolist"
else else
echo "[securityonion]" > /etc/yum.repos.d/securityonion.repo echo "[securityonion]" > /etc/yum.repos.d/securityonion.repo
@@ -1832,13 +1824,6 @@ securityonion_repo() {
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonion.repo echo "sslverify=0" >> /etc/yum.repos.d/securityonion.repo
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "baseurl=https://$MSRV/kernelrepo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonionkernel.repo
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
logCmd "dnf repolist" logCmd "dnf repolist"
fi fi
elif [[ ! $waitforstate ]]; then elif [[ ! $waitforstate ]]; then
@@ -1848,25 +1833,12 @@ securityonion_repo() {
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonion.repo echo "sslverify=0" >> /etc/yum.repos.d/securityonion.repo
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "baseurl=https://$MSRV/kernelrepo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonionkernel.repo
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
elif [[ $waitforstate ]]; then elif [[ $waitforstate ]]; then
echo "[securityonion]" > /etc/yum.repos.d/securityonion.repo echo "[securityonion]" > /etc/yum.repos.d/securityonion.repo
echo "name=Security Onion Repo" >> /etc/yum.repos.d/securityonion.repo echo "name=Security Onion Repo" >> /etc/yum.repos.d/securityonion.repo
echo "baseurl=file:///nsm/repo/" >> /etc/yum.repos.d/securityonion.repo echo "baseurl=file:///nsm/repo/" >> /etc/yum.repos.d/securityonion.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "baseurl=file:///nsm/kernelrepo/" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
fi fi
logCmd "dnf repolist all" logCmd "dnf repolist all"
if [[ $waitforstate ]]; then if [[ $waitforstate ]]; then
@@ -1882,12 +1854,9 @@ repo_sync_local() {
# Sync the repo from the SO repo locally. # Sync the repo from the SO repo locally.
info "Adding Repo Download Configuration" info "Adding Repo Download Configuration"
mkdir -p /nsm/repo mkdir -p /nsm/repo
mkdir -p /nsm/kernelrepo
mkdir -p /opt/so/conf/reposync/cache mkdir -p /opt/so/conf/reposync/cache
echo "https://repo.securityonion.net/file/so-repo/prod/3/oracle/9" > /opt/so/conf/reposync/mirror.txt echo "https://repo.securityonion.net/file/so-repo/prod/3/oracle/9" > /opt/so/conf/reposync/mirror.txt
echo "https://repo-alt.securityonion.net/prod/3/oracle/9" >> /opt/so/conf/reposync/mirror.txt echo "https://repo-alt.securityonion.net/prod/3/oracle/9" >> /opt/so/conf/reposync/mirror.txt
echo "https://repo.securityonion.net/file/so-repo/prod/3/oracle/9-uek8" > /opt/so/conf/reposync/mirror-kernel.txt
echo "https://repo-alt.securityonion.net/prod/3/oracle/9-uek8" >> /opt/so/conf/reposync/mirror-kernel.txt
echo "[main]" > /opt/so/conf/reposync/repodownload.conf echo "[main]" > /opt/so/conf/reposync/repodownload.conf
echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf
echo "installonly_limit=3" >> /opt/so/conf/reposync/repodownload.conf echo "installonly_limit=3" >> /opt/so/conf/reposync/repodownload.conf
@@ -1901,18 +1870,12 @@ repo_sync_local() {
echo "mirrorlist=file:///opt/so/conf/reposync/mirror.txt" >> /opt/so/conf/reposync/repodownload.conf echo "mirrorlist=file:///opt/so/conf/reposync/mirror.txt" >> /opt/so/conf/reposync/repodownload.conf
echo "enabled=1" >> /opt/so/conf/reposync/repodownload.conf echo "enabled=1" >> /opt/so/conf/reposync/repodownload.conf
echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf
echo "[securityonionkernel]" >> /opt/so/conf/reposync/repodownload.conf
echo "name=Security Onion Kernel Repo repo" >> /opt/so/conf/reposync/repodownload.conf
echo "mirrorlist=file:///opt/so/conf/reposync/mirror-kernel.txt" >> /opt/so/conf/reposync/repodownload.conf
echo "enabled=1" >> /opt/so/conf/reposync/repodownload.conf
echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf
logCmd "dnf repolist" logCmd "dnf repolist"
if [[ ! $is_airgap ]]; then if [[ ! $is_airgap ]]; then
curl --retry 5 --retry-delay 60 -A "netinstall/$SOVERSION/$OS/$(uname -r)/1" https://sigs.securityonion.net/checkup --output /tmp/install curl --retry 5 --retry-delay 60 -A "netinstall/$SOVERSION/$OS/$(uname -r)/1" https://sigs.securityonion.net/checkup --output /tmp/install
retry 5 60 "dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionsync --download-metadata -p /nsm/repo/" >> "$setup_log" 2>&1 || fail_setup "Failed to sync repos" retry 5 60 "dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionsync --download-metadata -p /nsm/repo/" >> "$setup_log" 2>&1 || fail_setup
retry 5 60 "dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionkernel --download-metadata -p /nsm/kernelrepo/" >> "$setup_log" 2>&1 || fail_setup "Failed to sync kernel repos"
# After the download is complete run createrepo # After the download is complete run createrepo
create_repo create_repo
fi fi
@@ -1923,12 +1886,19 @@ saltify() {
info "Installing Salt $SALTVERSION" info "Installing Salt $SALTVERSION"
chmod u+x ../salt/salt/scripts/bootstrap-salt.sh chmod u+x ../salt/salt/scripts/bootstrap-salt.sh
# Normally Salt packages come from the SO mirror, so -r disables the bootstrap's own repo setup.
# On the OL10 test path there is no SO mirror, so let bootstrap configure the public Salt repo.
local saltrepoflag="-r"
if [[ "$OSVER" == "10" ]]; then
saltrepoflag=""
fi
if [[ $waitforstate ]]; then if [[ $waitforstate ]]; then
# install all for a manager # install all for a manager
retry 30 10 "bash ../salt/salt/scripts/bootstrap-salt.sh -r -M -X stable $SALTVERSION" || fail_setup "Failed to install salt master" retry 30 10 "bash ../salt/salt/scripts/bootstrap-salt.sh $saltrepoflag -M -X stable $SALTVERSION" || fail_setup
else else
# just a minion # just a minion
retry 30 10 "bash ../salt/salt/scripts/bootstrap-salt.sh -r -X stable $SALTVERSION" || fail_setup "Failed to install salt minion" retry 30 10 "bash ../salt/salt/scripts/bootstrap-salt.sh $saltrepoflag -X stable $SALTVERSION" || fail_setup
fi fi
salt_install_module_deps salt_install_module_deps
@@ -2000,7 +1970,7 @@ set_main_ip() {
info "MAINIP=$MAINIP" info "MAINIP=$MAINIP"
info "MNIC_IP=$MNIC_IP" info "MNIC_IP=$MNIC_IP"
whiptail_error_message "The management IP could not be determined. Please check the log at /root/sosetup.log and verify the network configuration. Select OK to exit." whiptail_error_message "The management IP could not be determined. Please check the log at /root/sosetup.log and verify the network configuration. Select OK to exit."
fail_setup "Could not determine MAINIP or MNIC_IP" fail_setup
fi fi
sleep 1 sleep 1
done done
@@ -2204,7 +2174,7 @@ set_initial_firewall_access() {
set_management_interface() { set_management_interface() {
title "Setting up the main interface" title "Setting up the main interface"
if [[ $MNIC == "bond1" ]]; then if [[ $MNIC == "bond1" ]]; then
configure_management_bond || fail_setup "Failed to configure management bond" configure_management_bond || fail_setup
fi fi
if [ "$address_type" = 'DHCP' ]; then if [ "$address_type" = 'DHCP' ]; then
@@ -2269,13 +2239,6 @@ update_sudoers_for_testing() {
} }
update_packages() { update_packages() {
# Pin physical NIC names by MAC BEFORE pulling packages, so the UEK8 kernel that
# the update below installs can't renumber the interfaces SO binds by name. Doing
# it here (instead of waiting for the common highstate) also drops the
# /opt/so/state/nic_names_pinned marker that gates the kernel repo, so the kernel
# repo is assigned on the very first highstate and the kernel isn't downgraded and
# then re-upgraded. Run-once: so-nic-pin no-ops if the marker already exists.
logCmd "bash ../salt/common/tools/sbin/so-nic-pin"
logCmd "dnf repolist" logCmd "dnf repolist"
logCmd "dnf -y update --allowerasing --exclude=salt*,docker*,containerd*" logCmd "dnf -y update --allowerasing --exclude=salt*,docker*,containerd*"
RMREPOFILES=("oracle-linux-ol9.repo" "uek-ol9.repo" "virt-ol9.repo") RMREPOFILES=("oracle-linux-ol9.repo" "uek-ol9.repo" "virt-ol9.repo")
+12 -16
View File
@@ -9,17 +9,14 @@
# Make sure you are root before doing anything # Make sure you are root before doing anything
uid="$(id -u)" uid="$(id -u)"
if [ "$uid" -ne 0 ]; then if [ "$uid" -ne 0 ]; then
echo "This script must be run using sudo!" >&2 echo "This script must be run using sudo!"
exit 1 fail_setup
fi fi
# Save the original argument array since we modify it # Save the original argument array since we modify it
original_args=("$@") original_args=("$@")
cd "$(dirname "$0")" || { cd "$(dirname "$0")" || fail_setup
echo "Unable to change to setup directory" >&2
exit 1
}
echo "Getting started..." echo "Getting started..."
@@ -90,7 +87,8 @@ if [[ "$setup_type" == 'iso' ]]; then
if [[ $is_rpm ]]; then if [[ $is_rpm ]]; then
is_iso=true is_iso=true
else else
fail_setup "Only use 'so-setup iso' for an ISO install on Security Onion ISO images. Please run 'so-setup network' instead." echo "Only use 'so-setup iso' for an ISO install on Security Onion ISO images. Please run 'so-setup network' instead."
fail_setup
fi fi
fi fi
@@ -129,7 +127,7 @@ catch() {
info "Fatal error occurred at $1 in so-setup, failing setup." info "Fatal error occurred at $1 in so-setup, failing setup."
grep --color=never "ERROR" "$setup_log" > "$error_log" grep --color=never "ERROR" "$setup_log" > "$error_log"
whiptail_setup_failed whiptail_setup_failed
fail_setup "Fatal error occurred at $1 in so-setup" fail_setup
} }
# Add the progress function for manager node type installs # Add the progress function for manager node type installs
@@ -225,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
@@ -237,7 +233,8 @@ case "$setup_type" in
info "Beginning Security Onion $setup_type install" info "Beginning Security Onion $setup_type install"
;; ;;
*) *)
fail_setup "Invalid install type, must be 'iso', 'network' or 'desktop'." error "Invalid install type, must be 'iso', 'network' or 'desktop'."
fail_setup
;; ;;
esac esac
@@ -770,9 +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
fail_setup "Failed to seed the docker registry"
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"
@@ -794,9 +789,10 @@ if ! [[ -f $install_opt_file ]]; then
title "Setting up Elastic Fleet" title "Setting up Elastic Fleet"
logCmd "salt-call state.apply elasticfleet.config" logCmd "salt-call state.apply elasticfleet.config"
if ! logCmd so-elastic-fleet-setup; then if ! logCmd so-elastic-fleet-setup; then
fail_setup "Failed to run so-elastic-fleet-setup" error "Failed to run so-elastic-fleet-setup"
fail_setup
fi fi
mark_setup_complete checkin_at_boot
set_initial_firewall_access set_initial_firewall_access
initialize_elasticsearch_indices "so-case so-casehistory so-assistant-session so-assistant-chat" initialize_elasticsearch_indices "so-case so-casehistory so-assistant-session so-assistant-chat"
# run a final highstate before enabling scheduled highstates. # run a final highstate before enabling scheduled highstates.
+3 -3
View File
@@ -143,15 +143,15 @@ main() {
cat $error_log cat $error_log
echo "--------------------------" echo "--------------------------"
exit_code=1 exit_code=1
echo "Found setup errors. Check $error_log for details" > /root/failure touch /root/failure
elif using_iso && cron_error_in_mail_spool; then elif using_iso && cron_error_in_mail_spool; then
echo "WARNING: Unexpected cron job output in mail spool" echo "WARNING: Unexpected cron job output in mail spool"
exit_code=1 exit_code=1
echo "Unexpected cron job output found in /var/spool/mail/" > /root/failure touch /root/failure
elif is_manager_node && status_failed; then elif is_manager_node && status_failed; then
echo "WARNING: Containers are not in a healthy state" echo "WARNING: Containers are not in a healthy state"
exit_code=1 exit_code=1
echo "Containers are not in a healthy state. Check so-status for details" > /root/failure touch /root/failure
else else
echo "Successfully completed setup!" echo "Successfully completed setup!"
touch /root/success touch /root/success