mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-07-15 21:34:46 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52885e28c5 | ||
|
|
9a71f64a35 | ||
|
|
40c02b3149 | ||
|
|
5fd5df54b4 |
@@ -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
|
||||||
@@ -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 = [
|
||||||
|
|||||||
@@ -291,20 +291,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
|
||||||
|
|||||||
@@ -5,53 +5,239 @@
|
|||||||
# 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.
|
||||||
#
|
#
|
||||||
# so-kernel-upgrade — switch the boot default to the installed UEK8 (6.x) kernel.
|
# so-kernel-upgrade — install the UEK8 (6.x) kernel and make it the boot default.
|
||||||
#
|
#
|
||||||
# Security Onion is moving off the EL9 stock kernel / UEK7 (5.x) onto UEK8 (6.x).
|
# Security Onion is moving off the EL9 stock kernel (RHCK, 5.14) and UEK7 (5.15) onto UEK8
|
||||||
# Installing the kernel-uek-core package adds a UEK8 boot entry but does NOT make it the
|
# (6.x). Three things have to happen, and the tool has to drive each one:
|
||||||
# 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
|
# 1. Populate. The manager mirrors the UEK8 packages into /nsm/kernelrepo via so-repo-sync,
|
||||||
# boot default; it does NOT reboot — the admin reboots the node on their own schedule.
|
# and serves them to the grid over https://<manager>/kernelrepo. Until that sync runs the
|
||||||
|
# repo is valid but EMPTY -- dnf resolves it happily and installs nothing, with no error.
|
||||||
|
# 2. Install. A node on RHCK has no kernel-uek* package at all, so there is nothing for
|
||||||
|
# 'dnf update' to upgrade. A node on UEK7 does have kernel-uek installed, so
|
||||||
|
# 'dnf install kernel-uek' reports "Nothing to do" and exits 0 without installing 6.x.
|
||||||
|
# Both cases need an explicit install of the UEK8 NEVRA.
|
||||||
|
# 3. Boot it. Whether a newly installed UEK8 kernel becomes the boot default depends on the
|
||||||
|
# RUNNING kernel's flavor. kernel-install/grubby (with UPDATEDEFAULT=yes) only auto-promote
|
||||||
|
# within the running kernel's flavor lineage:
|
||||||
|
# - From UEK7 (5.x, kernel-uek) the install stays in the kernel-uek lineage and IS
|
||||||
|
# auto-promoted, so no grubby change is needed -- just make sure the repo is populated
|
||||||
|
# and install UEK8.
|
||||||
|
# - From the stock EL9 kernel (RHCK, 5.14, no UEK) it is a flavor CROSS that is NOT
|
||||||
|
# auto-promoted, so the box keeps booting RHCK until grubby is told otherwise.
|
||||||
|
# This tool inspects the running kernel and only runs 'grubby --set-default' for RHCK.
|
||||||
|
#
|
||||||
|
# Every one of those failure modes is silent by default. This tool handles each case and fails
|
||||||
|
# loudly when it cannot, rather than reporting success while changing nothing.
|
||||||
|
#
|
||||||
|
# Manager vs minion: only the manager owns /nsm/kernelrepo, so only the manager can populate
|
||||||
|
# it. If the repo is empty here, a manager runs so-repo-sync itself; a minion has no way to
|
||||||
|
# fix it and exits non-zero telling the admin to sync the manager first.
|
||||||
|
#
|
||||||
|
# Idempotent: an already-installed, already-default UEK8 kernel is left alone. It only sets
|
||||||
|
# the boot default; it does NOT reboot -- the admin reboots the node on their own schedule.
|
||||||
|
|
||||||
|
. /usr/sbin/so-common
|
||||||
|
|
||||||
|
# Client-side repo id (what dnf enables on this node, from repo/client/oracle.sls) vs the
|
||||||
|
# reposync-side section in repodownload.conf that the manager mirrors from (mirrors the
|
||||||
|
# securityonion/securityonionsync split for the main repo).
|
||||||
|
KERNEL_REPO="securityonionkernel"
|
||||||
|
KERNEL_REPO_SYNC="securityonionkernelsync"
|
||||||
|
KERNEL_PKG="kernel-uek"
|
||||||
|
KERNEL_REPO_DIR="/nsm/kernelrepo"
|
||||||
|
REPOSYNC_CONF="/opt/so/conf/reposync/repodownload.conf"
|
||||||
|
GLOBAL_PILLAR="/opt/so/saltstack/local/pillar/global/soc_global.sls"
|
||||||
|
|
||||||
log() { echo "[so-kernel-upgrade] $*"; }
|
log() { echo "[so-kernel-upgrade] $*"; }
|
||||||
|
die() { echo "[so-kernel-upgrade] ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
[ "$(id -u)" -eq 0 ] || { log "must run as root"; exit 1; }
|
command -v grubby >/dev/null 2>&1 || die "grubby not found"
|
||||||
command -v grubby >/dev/null 2>&1 || { log "grubby not found"; exit 1; }
|
command -v dnf >/dev/null 2>&1 || die "dnf not found"
|
||||||
|
|
||||||
|
ARCH="$(rpm -E '%{_arch}')"
|
||||||
|
|
||||||
|
is_airgap() {
|
||||||
|
[ -f "$GLOBAL_PILLAR" ] && grep -q 'airgap: *[Tt]rue' "$GLOBAL_PILLAR"
|
||||||
|
}
|
||||||
|
|
||||||
# Newest installed UEK8 (6.x) kernel known to the bootloader. UEK8 vmlinuz paths look like
|
# 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.
|
# /boot/vmlinuz-6.12.0-204.92.4.2.el9uek.x86_64; UEK7 (5.15) and RHCK (5.14) won't match.
|
||||||
target="$(grubby --info=ALL 2>/dev/null \
|
find_uek8() {
|
||||||
| sed -n 's/^kernel="\(.*\)"$/\1/p' \
|
grubby --info=ALL 2>/dev/null \
|
||||||
| grep -E '/vmlinuz-6\.[0-9]+.*uek' \
|
| sed -n 's/^kernel="\(.*\)"$/\1/p' \
|
||||||
| sort -V | tail -1)"
|
| grep -E '/vmlinuz-6\.[0-9]+.*uek' \
|
||||||
|
| sort -V | tail -1
|
||||||
|
}
|
||||||
|
|
||||||
if [ -z "$target" ]; then
|
# Classify the RUNNING kernel (uname -r) -- this, not what's installed, is what decides whether
|
||||||
log "no installed 6.x UEK (UEK8) kernel found — confirm the kernel repo is assigned and"
|
# a UEK8 install auto-promotes to the boot default:
|
||||||
log "'dnf update' has installed kernel-uek-core. Nothing to do."
|
# uek8 6.x UEK already on the target line; nothing to do
|
||||||
|
# uek7 5.x UEK a UEK8 install stays in the kernel-uek lineage and auto-promotes (no grubby)
|
||||||
|
# rhck 5.14 EL9 crossing into the UEK flavor does NOT auto-promote (needs grubby --set-default)
|
||||||
|
running_flavor() {
|
||||||
|
case "$(uname -r)" in
|
||||||
|
6.*uek*) echo uek8 ;;
|
||||||
|
*uek*) echo uek7 ;;
|
||||||
|
*) echo rhck ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Newest UEK8 kernel-uek NEVRA offered by the kernel repo, empty if the repo has none.
|
||||||
|
# Restricted to the kernel repo so a UEK7 kernel-uek in the main repo can't be picked up,
|
||||||
|
# and filtered to 6.x so we never "succeed" by reinstalling the 5.15 we already have.
|
||||||
|
uek8_available() {
|
||||||
|
dnf -q repoquery --disablerepo='*' --enablerepo="$KERNEL_REPO" \
|
||||||
|
--arch="$ARCH" --latest-limit=1 \
|
||||||
|
--qf '%{name}-%{evr}.%{arch}\n' "$KERNEL_PKG" 2>/dev/null \
|
||||||
|
| grep -E "^${KERNEL_PKG}-6\." | tail -1
|
||||||
|
}
|
||||||
|
|
||||||
|
kernelrepo_rpm_count() {
|
||||||
|
find "$KERNEL_REPO_DIR" -maxdepth 1 -name '*.rpm' 2>/dev/null | wc -l
|
||||||
|
}
|
||||||
|
|
||||||
|
# The kernel repo starts life as valid-but-empty (kernelrepo_init_empty in
|
||||||
|
# salt/manager/init.sls) and is filled by so-repo-sync. During a soup, so-repo-sync runs
|
||||||
|
# BEFORE the highstate deploys the [securityonionkernelsync] section into repodownload.conf, so
|
||||||
|
# the first kernel-aware soup leaves the repo empty until the next nightly sync.
|
||||||
|
sync_kernel_repo() {
|
||||||
|
if is_airgap; then
|
||||||
|
log "airgap install: $KERNEL_REPO_DIR is populated from the airgap ISO, not by so-repo-sync."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! grep -q "^\[${KERNEL_REPO_SYNC}\]" "$REPOSYNC_CONF" 2>/dev/null; then
|
||||||
|
log "$REPOSYNC_CONF has no [${KERNEL_REPO_SYNC}] section -- run a highstate to deploy it."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "populating $KERNEL_REPO_DIR with so-repo-sync (mirrors upstream; can take several minutes)"
|
||||||
|
su socore -c '/usr/sbin/so-repo-sync' || { log "so-repo-sync failed"; return 1; }
|
||||||
|
|
||||||
|
dnf -q clean expire-cache >/dev/null 2>&1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make the kernel repo actually able to serve a UEK8 package, or fail trying.
|
||||||
|
ensure_kernel_repo() {
|
||||||
|
# The repo is assigned by the repo.client highstate, and only once NICs are pinned by MAC
|
||||||
|
# (/opt/so/state/nic_names_pinned) so the kernel swap can't renumber interfaces SO binds
|
||||||
|
# by name. skip_if_unavailable=1 means a broken repo is silently ignored, so check first.
|
||||||
|
if ! dnf -q repolist --enabled 2>/dev/null | awk '{print $1}' | grep -qx "$KERNEL_REPO"; then
|
||||||
|
log "repo '$KERNEL_REPO' is not enabled on this node."
|
||||||
|
log "Run a highstate first; the repo is skipped until /opt/so/state/nic_names_pinned"
|
||||||
|
log "exists (run so-nic-pin) and this node's salt matches the version this release ships."
|
||||||
|
die "kernel repo unavailable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "$(uek8_available)" ] && return 0
|
||||||
|
|
||||||
|
log "repo '$KERNEL_REPO' is enabled but offers no UEK8 $KERNEL_PKG package"
|
||||||
|
|
||||||
|
if ! is_manager_node; then
|
||||||
|
log "This is a minion; it consumes the kernel repo from the manager and cannot populate it."
|
||||||
|
log "On the manager, run: su socore -c /usr/sbin/so-repo-sync"
|
||||||
|
log "then re-run this script here."
|
||||||
|
die "manager's kernel repo is empty"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "this is a manager and $KERNEL_REPO_DIR holds $(kernelrepo_rpm_count) rpm(s)"
|
||||||
|
sync_kernel_repo || die "could not populate $KERNEL_REPO_DIR"
|
||||||
|
|
||||||
|
[ -n "$(uek8_available)" ] \
|
||||||
|
|| die "so-repo-sync completed but $KERNEL_REPO still offers no UEK8 $KERNEL_PKG"
|
||||||
|
}
|
||||||
|
|
||||||
|
reboot_notice() {
|
||||||
|
[ "$(uname -r)" = "$(basename "$1" | sed 's/^vmlinuz-//')" ] \
|
||||||
|
|| log "REBOOT REQUIRED to start using the UEK8 kernel (currently running $(uname -r))."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep future kernel updates on the UEK line rather than falling back to RHCK. Oracle ships
|
||||||
|
# /etc/sysconfig/kernel; only rewrite it when it's actually pointing somewhere else.
|
||||||
|
set_default_kernel_conf() {
|
||||||
|
if [ -f /etc/sysconfig/kernel ] && ! grep -q '^DEFAULTKERNEL=kernel-uek-core$' /etc/sysconfig/kernel; then
|
||||||
|
log "setting DEFAULTKERNEL=kernel-uek-core in /etc/sysconfig/kernel"
|
||||||
|
sed -i 's/^DEFAULTKERNEL=.*/DEFAULTKERNEL=kernel-uek-core/' /etc/sysconfig/kernel
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure a UEK8 kernel is installed, leaving its boot entry in INSTALLED_UEK8. If one is
|
||||||
|
# already present we leave the repo alone -- it may be disabled or empty and we don't need it
|
||||||
|
# just to flip the boot default. Otherwise install the explicit NEVRA, not the bare package
|
||||||
|
# name: on a UEK7 node 'dnf install kernel-uek' sees 5.15 already present, prints "Nothing to
|
||||||
|
# do" and exits 0 without installing 6.x.
|
||||||
|
ensure_uek8_installed() {
|
||||||
|
INSTALLED_UEK8="$(find_uek8)"
|
||||||
|
if [ -n "$INSTALLED_UEK8" ]; then
|
||||||
|
log "UEK8 kernel already installed: $INSTALLED_UEK8"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_kernel_repo
|
||||||
|
local nevra; nevra="$(uek8_available)"
|
||||||
|
log "installing $nevra from $KERNEL_REPO"
|
||||||
|
dnf -y install "$nevra" || die "failed to install $nevra"
|
||||||
|
|
||||||
|
INSTALLED_UEK8="$(find_uek8)"
|
||||||
|
[ -n "$INSTALLED_UEK8" ] || die "$nevra installed but no 6.x UEK boot entry appeared -- check 'grubby --info=ALL'"
|
||||||
|
log "installed UEK8 kernel: $INSTALLED_UEK8"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$(running_flavor)" in
|
||||||
|
uek8)
|
||||||
|
# Already on the 6.x UEK line. A plain 'dnf update' keeps this node current within the
|
||||||
|
# lineage and auto-promotes newer builds, so there is nothing for this tool to do.
|
||||||
|
log "already running a UEK8 kernel ($(uname -r)); nothing to do."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
;;
|
||||||
|
|
||||||
current="$(grubby --default-kernel 2>/dev/null)"
|
uek7)
|
||||||
if [ "$current" = "$target" ]; then
|
# On a 5.x UEK kernel. Installing UEK8 stays inside the kernel-uek lineage, so dnf/grubby
|
||||||
log "UEK8 kernel is already the boot default: $target"
|
# (UPDATEDEFAULT=yes) auto-promote it and we do NOT touch grubby. A node still on UEK7
|
||||||
exit 0
|
# usually means the kernel repo was empty when it last updated, so populate it and install.
|
||||||
fi
|
log "running UEK7 kernel ($(uname -r)); the kernel repo was likely not yet populated when"
|
||||||
|
log "this node last updated. Populating it and installing UEK8 -- the update stays on the"
|
||||||
|
log "kernel-uek line, so it becomes the boot default automatically (no grubby change needed)."
|
||||||
|
set_default_kernel_conf
|
||||||
|
ensure_uek8_installed
|
||||||
|
|
||||||
log "current default kernel: ${current:-unknown}"
|
now="$(grubby --default-kernel 2>/dev/null)"
|
||||||
log "switching boot default to UEK8 kernel: $target"
|
if [ "$now" = "$INSTALLED_UEK8" ]; then
|
||||||
grubby --set-default="$target" || { log "ERROR: grubby --set-default failed for $target"; exit 1; }
|
log "boot default auto-promoted to UEK8 kernel: $INSTALLED_UEK8"
|
||||||
|
else
|
||||||
|
log "WARNING: expected the UEK8 kernel to auto-promote but the default is still"
|
||||||
|
log "'${now:-unknown}'. Run 'grubby --set-default=$INSTALLED_UEK8' to force it."
|
||||||
|
fi
|
||||||
|
reboot_notice "$INSTALLED_UEK8"
|
||||||
|
;;
|
||||||
|
|
||||||
# Verify the change actually took before claiming success.
|
rhck)
|
||||||
now="$(grubby --default-kernel 2>/dev/null)"
|
# On the stock EL9 kernel (5.14, no UEK installed). Crossing from RHCK into the UEK flavor
|
||||||
if [ "$now" != "$target" ]; then
|
# does NOT auto-promote -- kernel-install/grubby only auto-promote within the running
|
||||||
log "ERROR: default kernel is still '${now:-unknown}' after set-default"
|
# kernel's flavor lineage -- so after installing we must set the boot default explicitly.
|
||||||
exit 1
|
log "running stock EL9 (RHCK) kernel ($(uname -r)); installing UEK8 and setting it as the"
|
||||||
fi
|
log "boot default explicitly (a RHCK->UEK flavor change does not auto-promote)."
|
||||||
|
set_default_kernel_conf
|
||||||
|
ensure_uek8_installed
|
||||||
|
target="$INSTALLED_UEK8"
|
||||||
|
|
||||||
log "boot default is now $target"
|
current="$(grubby --default-kernel 2>/dev/null)"
|
||||||
log "REBOOT REQUIRED to start using the UEK8 kernel (currently running $(uname -r))."
|
if [ "$current" = "$target" ]; then
|
||||||
|
log "UEK8 kernel is already the boot default: $target"
|
||||||
|
reboot_notice "$target"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "current default kernel: ${current:-unknown}"
|
||||||
|
log "switching boot default to UEK8 kernel: $target"
|
||||||
|
grubby --set-default="$target" || die "grubby --set-default failed for $target"
|
||||||
|
|
||||||
|
# Verify the change actually took before claiming success.
|
||||||
|
now="$(grubby --default-kernel 2>/dev/null)"
|
||||||
|
[ "$now" = "$target" ] || die "default kernel is still '${now:-unknown}' after set-default"
|
||||||
|
|
||||||
|
log "boot default is now $target"
|
||||||
|
reboot_notice "$target"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|||||||
@@ -5,44 +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"
|
||||||
|
echo $banner
|
||||||
|
|
||||||
|
if [ "$2" = "--force" ]; then
|
||||||
|
printf "\nForce-stopping all Salt jobs before proceeding\n\n"
|
||||||
|
salt-call saltutil.kill_all_jobs
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
"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
|
||||||
|
|
||||||
#shellcheck disable=SC2154
|
|
||||||
echo "$banner"
|
|
||||||
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"
|
|
||||||
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
|
|
||||||
"elastic-fleet"|"elasticfleet")
|
|
||||||
docker_check_running "elastic-fleet" "--stop"
|
|
||||||
docker rm "so-elastic-fleet" 2> /dev/null
|
|
||||||
# Removing the elastic fleet state directory, so that the next startup re-enrolls with a fresh policy
|
|
||||||
rm -rf /opt/so/conf/elastic-fleet/state
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -5,35 +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
|
|
||||||
# Removing the elastic fleet state directory, so that the next startup re-enrolls with a fresh policy
|
|
||||||
rm -rf /opt/so/conf/elastic-fleet/state
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
docker_check_running "$1" "--stop"
|
|
||||||
docker rm "so-${1}" 2> /dev/null
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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 }}:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
elasticfleet:
|
elasticfleet:
|
||||||
enabled: False
|
enabled: False
|
||||||
|
patch_version: 9.3.3+build202604082258 # Elastic Agent specific patch release.
|
||||||
enable_manager_output: True
|
enable_manager_output: True
|
||||||
config:
|
config:
|
||||||
server:
|
server:
|
||||||
|
|||||||
@@ -11,10 +11,6 @@
|
|||||||
|
|
||||||
{# This value is generated during node install and stored in minion pillar #}
|
{# This value is generated during node install and stored in minion pillar #}
|
||||||
{% set SERVICETOKEN = salt['pillar.get']('elasticfleet:config:server:es_token','') %}
|
{% set SERVICETOKEN = salt['pillar.get']('elasticfleet:config:server:es_token','') %}
|
||||||
{# Prevent Elastic Agent from re-enrolling with a new agent.id everytime the container starts up.
|
|
||||||
- if a fresh enrollment is needed use 'so-stop elasticfleet'
|
|
||||||
#}
|
|
||||||
{% set ENROLLED = salt['file.file_exists']('/opt/so/conf/elastic-fleet/state/fleet.enc') %}
|
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- ca
|
- ca
|
||||||
@@ -69,7 +65,6 @@ so-elastic-fleet:
|
|||||||
- /etc/pki/elasticfleet-server.crt:/etc/pki/elasticfleet-server.crt:ro
|
- /etc/pki/elasticfleet-server.crt:/etc/pki/elasticfleet-server.crt:ro
|
||||||
- /etc/pki/elasticfleet-server.key:/etc/pki/elasticfleet-server.key:ro
|
- /etc/pki/elasticfleet-server.key:/etc/pki/elasticfleet-server.key:ro
|
||||||
- /etc/pki/tls/certs/intca.crt:/etc/pki/tls/certs/intca.crt:ro
|
- /etc/pki/tls/certs/intca.crt:/etc/pki/tls/certs/intca.crt:ro
|
||||||
- /opt/so/conf/elastic-fleet/state:/usr/share/elastic-agent/state
|
|
||||||
- /opt/so/log/elasticfleet:/usr/share/elastic-agent/logs
|
- /opt/so/log/elasticfleet:/usr/share/elastic-agent/logs
|
||||||
{% if DOCKERMERGED.containers['so-elastic-fleet'].custom_bind_mounts %}
|
{% if DOCKERMERGED.containers['so-elastic-fleet'].custom_bind_mounts %}
|
||||||
{% for BIND in DOCKERMERGED.containers['so-elastic-fleet'].custom_bind_mounts %}
|
{% for BIND in DOCKERMERGED.containers['so-elastic-fleet'].custom_bind_mounts %}
|
||||||
@@ -77,7 +72,6 @@ so-elastic-fleet:
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
- environment:
|
- environment:
|
||||||
{% if not ENROLLED %}
|
|
||||||
- FLEET_SERVER_ENABLE=true
|
- FLEET_SERVER_ENABLE=true
|
||||||
- FLEET_URL=https://{{ GLOBALS.hostname }}:8220
|
- FLEET_URL=https://{{ GLOBALS.hostname }}:8220
|
||||||
- FLEET_SERVER_ELASTICSEARCH_HOST=https://{{ GLOBALS.manager }}:9200
|
- FLEET_SERVER_ELASTICSEARCH_HOST=https://{{ GLOBALS.manager }}:9200
|
||||||
@@ -87,9 +81,6 @@ so-elastic-fleet:
|
|||||||
- FLEET_SERVER_CERT_KEY=/etc/pki/elasticfleet-server.key
|
- FLEET_SERVER_CERT_KEY=/etc/pki/elasticfleet-server.key
|
||||||
- FLEET_CA=/etc/pki/tls/certs/intca.crt
|
- FLEET_CA=/etc/pki/tls/certs/intca.crt
|
||||||
- FLEET_SERVER_ELASTICSEARCH_CA=/etc/pki/tls/certs/intca.crt
|
- FLEET_SERVER_ELASTICSEARCH_CA=/etc/pki/tls/certs/intca.crt
|
||||||
{% endif %}
|
|
||||||
- STATE_PATH=/usr/share/elastic-agent/state
|
|
||||||
- CONFIG_PATH=/usr/share/elastic-agent/state
|
|
||||||
- LOGS_PATH=logs
|
- LOGS_PATH=logs
|
||||||
{% if DOCKERMERGED.containers['so-elastic-fleet'].extra_env %}
|
{% if DOCKERMERGED.containers['so-elastic-fleet'].extra_env %}
|
||||||
{% for XTRAENV in DOCKERMERGED.containers['so-elastic-fleet'].extra_env %}
|
{% for XTRAENV in DOCKERMERGED.containers['so-elastic-fleet'].extra_env %}
|
||||||
@@ -108,7 +99,6 @@ so-elastic-fleet:
|
|||||||
- x509: etc_elasticfleet_crt
|
- x509: etc_elasticfleet_crt
|
||||||
- require:
|
- require:
|
||||||
- file: trusttheca
|
- file: trusttheca
|
||||||
- file: eastatedir
|
|
||||||
- x509: etc_elasticfleet_key
|
- x509: etc_elasticfleet_key
|
||||||
- x509: etc_elasticfleet_crt
|
- x509: etc_elasticfleet_crt
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"package": {
|
"package": {
|
||||||
"name": "endpoint",
|
"name": "endpoint",
|
||||||
"title": "Elastic Defend",
|
"title": "Elastic Defend",
|
||||||
"version": "9.3.1",
|
"version": "9.3.0",
|
||||||
"requires_root": true
|
"requires_root": true
|
||||||
},
|
},
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"\\.gz$"
|
"\\.gz$"
|
||||||
],
|
],
|
||||||
"include_files": [],
|
"include_files": [],
|
||||||
"processors": "- dissect:\n tokenizer: \"/nsm/import/%{import.id}/evtx/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"\n- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n- drop_fields:\n fields: [\"host\"]\n ignore_missing: true\n- add_fields:\n target: data_stream\n fields:\n type: logs\n dataset: system.security\n- add_fields:\n target: event\n fields:\n dataset: system.security\n module: system\n imported: true\n- add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.security-2.20.0\n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-Sysmon/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.sysmon_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.sysmon_operational\n module: windows\n imported: true\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.sysmon_operational-3.8.3\n- if:\n equals:\n winlog.channel: 'Application'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.application\n - add_fields:\n target: event\n fields:\n dataset: system.application\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.application-2.20.0\n- if:\n equals:\n winlog.channel: 'System'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.system\n - add_fields:\n target: event\n fields:\n dataset: system.system\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.system-2.20.0\n \n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-PowerShell/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.powershell_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.powershell_operational\n module: windows\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.powershell_operational-3.8.3\n- add_fields:\n target: data_stream\n fields:\n dataset: import",
|
"processors": "- dissect:\n tokenizer: \"/nsm/import/%{import.id}/evtx/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"\n- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n- drop_fields:\n fields: [\"host\"]\n ignore_missing: true\n- add_fields:\n target: data_stream\n fields:\n type: logs\n dataset: system.security\n- add_fields:\n target: event\n fields:\n dataset: system.security\n module: system\n imported: true\n- add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.security-2.15.0\n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-Sysmon/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.sysmon_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.sysmon_operational\n module: windows\n imported: true\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.sysmon_operational-3.8.0\n- if:\n equals:\n winlog.channel: 'Application'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.application\n - add_fields:\n target: event\n fields:\n dataset: system.application\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.application-2.15.0\n- if:\n equals:\n winlog.channel: 'System'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.system\n - add_fields:\n target: event\n fields:\n dataset: system.system\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.system-2.15.0\n \n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-PowerShell/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.powershell_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.powershell_operational\n module: windows\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.powershell_operational-3.8.0\n- add_fields:\n target: data_stream\n fields:\n dataset: import",
|
||||||
"tags": [
|
"tags": [
|
||||||
"import"
|
"import"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,15 +10,6 @@
|
|||||||
{% set AGENT_STATUS = salt['service.available']('elastic-agent') %}
|
{% set AGENT_STATUS = salt['service.available']('elastic-agent') %}
|
||||||
{% set AGENT_EXISTS = salt['file.file_exists']('/opt/Elastic/Agent/elastic-agent') %}
|
{% set AGENT_EXISTS = salt['file.file_exists']('/opt/Elastic/Agent/elastic-agent') %}
|
||||||
|
|
||||||
so-elastic-agent-install:
|
|
||||||
file.managed:
|
|
||||||
- name: /usr/sbin/so-elastic-agent-install
|
|
||||||
- source: salt://elasticfleet/tools/sbin/so-elastic-agent-install
|
|
||||||
- user: 947
|
|
||||||
- group: 939
|
|
||||||
- mode: 755
|
|
||||||
- show_changes: False
|
|
||||||
|
|
||||||
{% if not AGENT_STATUS or not AGENT_EXISTS %}
|
{% if not AGENT_STATUS or not AGENT_EXISTS %}
|
||||||
|
|
||||||
pull_agent_installer:
|
pull_agent_installer:
|
||||||
@@ -30,9 +21,11 @@ pull_agent_installer:
|
|||||||
|
|
||||||
run_installer:
|
run_installer:
|
||||||
cmd.run:
|
cmd.run:
|
||||||
- name: /usr/sbin/so-elastic-agent-install "{{ GRIDNODETOKEN }}"
|
- name: ./so-elastic-agent_linux_amd64 -token={{ GRIDNODETOKEN }} -force
|
||||||
- require:
|
- cwd: /opt/so
|
||||||
- file: pull_agent_installer
|
- retry:
|
||||||
|
attempts: 3
|
||||||
|
interval: 20
|
||||||
|
|
||||||
cleanup_agent_installer:
|
cleanup_agent_installer:
|
||||||
file.absent:
|
file.absent:
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ so-elastic-fleet-package-upgrade:
|
|||||||
interval: 30
|
interval: 30
|
||||||
- require:
|
- require:
|
||||||
- http: wait_for_so-kibana
|
- http: wait_for_so-kibana
|
||||||
|
- onchanges:
|
||||||
|
- file: /opt/so/state/elastic_fleet_packages.txt
|
||||||
|
|
||||||
so-elastic-fleet-integrations:
|
so-elastic-fleet-integrations:
|
||||||
cmd.run:
|
cmd.run:
|
||||||
|
|||||||
@@ -1,100 +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-elastic-fleet-common
|
|
||||||
|
|
||||||
|
|
||||||
# passed in as arg from elasticfleet/install_agent_grid.sls, else pulled from pillar later
|
|
||||||
GRIDNODETOKEN="$1"
|
|
||||||
LOGFILE="/opt/so/SO-Elastic-Agent_Installer_Health.log"
|
|
||||||
|
|
||||||
check_agent_health() {
|
|
||||||
timeout=300
|
|
||||||
interval=10
|
|
||||||
start=$SECONDS
|
|
||||||
|
|
||||||
while (( SECONDS - start < timeout )); do
|
|
||||||
agent_status=$(elastic-agent status 2>&1)
|
|
||||||
echo -e "\n$(date)\n$agent_status\n" >> "$LOGFILE"
|
|
||||||
if echo "$agent_status" | grep -A1 'elastic-agent$' | grep -q 'status: (HEALTHY)'; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
echo "The Elastic Agent is not yet healthy. Waiting for ${interval} seconds before checking again..."
|
|
||||||
sleep "$interval"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "The Elastic Agent did not become healthy within ${timeout} seconds"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
uninstall_agent() {
|
|
||||||
if command -v elastic-agent >/dev/null 2>&1; then
|
|
||||||
elastic-agent uninstall -f
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ -z "$GRIDNODETOKEN" ]]; then
|
|
||||||
noderole=$(so-yaml.py get -r /etc/salt/grains role)
|
|
||||||
if [[ "$noderole" == "so-heavynode" ]]; then
|
|
||||||
GRIDNODETOKEN=$(salt-call pillar.get global:fleet_grid_enrollment_token_heavy --out=newline_values_only)
|
|
||||||
else
|
|
||||||
GRIDNODETOKEN=$(salt-call pillar.get global:fleet_grid_enrollment_token_general --out=newline_values_only)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$GRIDNODETOKEN" ]]; then
|
|
||||||
echo "Unable to determine Elastic Fleet enrollment token. Exiting."
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -x /opt/so/so-elastic-agent_linux_amd64 ]]; then
|
|
||||||
echo "Downloading so-elastic-agent installer... This could take a while if another Salt job is running."
|
|
||||||
|
|
||||||
# When running outside of elasticfleet/install_agent_grid.sls we need to download the installer independently.
|
|
||||||
# PYTHONWARNINGS="ignore" to avoid messages like the following when running salt-call:
|
|
||||||
# '/opt/saltstack/salt/lib/python3.10/site-packages/salt/transport/base.py:129: TransportWarning: Unclosed transport! <salt.transport.zeromq.RequestClient object at 0x7fc5f0ee7a30>
|
|
||||||
# File "/bin/salt-call", line 12, in <module>
|
|
||||||
# sys.exit(salt_call())'
|
|
||||||
|
|
||||||
PYTHONWARNINGS="ignore" salt-call state.single file.managed name=/opt/so/so-elastic-agent_linux_amd64 source=salt://elasticfleet/files/so_agent-installers/so-elastic-agent_linux_amd64 mode=755 makedirs=True queue=True
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -x /opt/so/so-elastic-agent_linux_amd64 ]]; then
|
|
||||||
attempts=0
|
|
||||||
cd /opt/so/ || exit 1
|
|
||||||
|
|
||||||
truncate -s 0 "$LOGFILE"
|
|
||||||
|
|
||||||
uninstall_agent
|
|
||||||
|
|
||||||
while [[ $attempts -lt 3 ]]; do
|
|
||||||
if ./so-elastic-agent_linux_amd64 -token="$GRIDNODETOKEN" -force && echo "Verifying Elastic Agent health..." && check_agent_health; then
|
|
||||||
rm -f /opt/so/so-elastic-agent_linux_amd64
|
|
||||||
elastic-agent status
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
attempts=$((attempts + 1))
|
|
||||||
|
|
||||||
if [[ $attempts -lt 3 ]]; then
|
|
||||||
echo "Unable to verify Elastic Agent health... Retrying in 20 seconds..."
|
|
||||||
sleep 20
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
uninstall_agent
|
|
||||||
rm -f /opt/so/so-elastic-agent_linux_amd64
|
|
||||||
echo "The so-elastic-agent installer failed after 3 attempts. Exiting."
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Unable to locate so-elastic-agent installer. Exiting."
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -9,11 +9,13 @@
|
|||||||
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.
|
# Each group fetches its agent policy once and dispatches create/update writes concurrently.
|
||||||
@@ -30,12 +32,9 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
|
|||||||
elastic_fleet_load_integrations_dir "so-grid-nodes_heavy" \
|
elastic_fleet_load_integrations_dir "so-grid-nodes_heavy" \
|
||||||
/opt/so/conf/elastic-fleet/integrations/grid-nodes_heavy "Grid Nodes Policy_Heavy" || RETURN_CODE=1
|
/opt/so/conf/elastic-fleet/integrations/grid-nodes_heavy "Grid Nodes Policy_Heavy" || RETURN_CODE=1
|
||||||
|
|
||||||
# Fleet Server - Optional integrations (adds integration configuration to a given FleetServer_ policy)
|
# Fleet Server - Optional integrations (one agent policy per FleetServer_* directory)
|
||||||
for FLEET_DIR in /opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/; do
|
for FLEET_DIR in /opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/; do
|
||||||
[ -d "$FLEET_DIR" ] || continue
|
[ -d "$FLEET_DIR" ] || continue
|
||||||
INTEGRATIONS=("${FLEET_DIR%/}"/*.json)
|
|
||||||
[ -e "${INTEGRATIONS[0]}" ] || continue
|
|
||||||
|
|
||||||
FLEET_POLICY=$(basename "$FLEET_DIR")
|
FLEET_POLICY=$(basename "$FLEET_DIR")
|
||||||
elastic_fleet_load_integrations_dir "$FLEET_POLICY" \
|
elastic_fleet_load_integrations_dir "$FLEET_POLICY" \
|
||||||
"${FLEET_DIR%/}" "Fleet Server Policy" "elasticsearch-logs" || RETURN_CODE=1
|
"${FLEET_DIR%/}" "Fleet Server Policy" "elasticsearch-logs" || RETURN_CODE=1
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ done
|
|||||||
if [[ -z $FLEETHOST ]] || [[ -z $ENROLLMENTOKEN ]]; then
|
if [[ -z $FLEETHOST ]] || [[ -z $ENROLLMENTOKEN ]]; then
|
||||||
printf "\nFleet Host URL, Enrollment Token or Elastic Version empty - exiting..."
|
printf "\nFleet Host URL, Enrollment Token or Elastic Version empty - exiting..."
|
||||||
printf "\nFleet Host: $FLEETHOST, Enrollment Token: $ENROLLMENTOKEN\n"
|
printf "\nFleet Host: $FLEETHOST, Enrollment Token: $ENROLLMENTOKEN\n"
|
||||||
exit 1
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
OSARCH=( "linux-x86_64" "windows-x86_64" "darwin-x86_64" "darwin-aarch64" )
|
OSARCH=( "linux-x86_64" "windows-x86_64" "darwin-x86_64" "darwin-aarch64" )
|
||||||
@@ -62,54 +62,31 @@ do
|
|||||||
done
|
done
|
||||||
|
|
||||||
GOTARGETOS=( "linux" "windows" "darwin" "darwin/arm64" )
|
GOTARGETOS=( "linux" "windows" "darwin" "darwin/arm64" )
|
||||||
|
GOARCH="amd64"
|
||||||
printf "\n### Generating OS packages using the cleaned up tarballs"
|
printf "\n### Generating OS packages using the cleaned up tarballs"
|
||||||
for GOOS in "${GOTARGETOS[@]}"; do
|
for GOOS in "${GOTARGETOS[@]}"
|
||||||
GOARCH="amd64"
|
do
|
||||||
if [[ $GOOS == 'darwin/arm64' ]]; then GOOS="darwin" && GOARCH="arm64"; fi
|
if [[ $GOOS == 'darwin/arm64' ]]; then GOOS="darwin" && GOARCH="arm64"; fi
|
||||||
printf "\n\n### Generating $GOOS/$GOARCH Installer...\n"
|
printf "\n\n### Generating $GOOS/$GOARCH Installer...\n"
|
||||||
docker run -e CGO_ENABLED=0 -e GOOS=$GOOS -e GOARCH=$GOARCH \
|
docker run -e CGO_ENABLED=0 -e GOOS=$GOOS -e GOARCH=$GOARCH \
|
||||||
--mount type=bind,source=/etc/pki/tls/certs/,target=/workspace/files/cert/ \
|
--mount type=bind,source=/etc/pki/tls/certs/,target=/workspace/files/cert/ \
|
||||||
--mount type=bind,source=/nsm/elastic-agent-workspace/,target=/workspace/files/elastic-agent/ \
|
--mount type=bind,source=/nsm/elastic-agent-workspace/,target=/workspace/files/elastic-agent/ \
|
||||||
--mount type=bind,source=/opt/so/saltstack/local/salt/elasticfleet/files/,target=/output/ \
|
--mount type=bind,source=/opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/,target=/output/ \
|
||||||
{{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-elastic-agent-builder:{{ GLOBALS.so_version }} go build -ldflags "-X main.fleetHostURLsList=$FLEETHOST -X main.enrollmentToken=$ENROLLMENTOKEN" -o /output/so-elastic-agent_${GOOS}_${GOARCH}
|
{{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-elastic-agent-builder:{{ GLOBALS.so_version }} go build -ldflags "-X main.fleetHostURLsList=$FLEETHOST -X main.enrollmentToken=$ENROLLMENTOKEN" -o /output/so-elastic-agent_${GOOS}_${GOARCH}
|
||||||
printf "\n### $GOOS/$GOARCH Installer Generated...\n"
|
printf "\n### $GOOS/$GOARCH Installer Generated...\n"
|
||||||
done
|
done
|
||||||
|
|
||||||
printf "\n\n### Generating MSI...\n"
|
printf "\n\n### Generating MSI...\n"
|
||||||
cp /opt/so/saltstack/local/salt/elasticfleet/files/so-elastic-agent_windows_amd64 /opt/so/saltstack/local/salt/elasticfleet/files/so-elastic-agent_windows_amd64.exe
|
cp /opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/so-elastic-agent_windows_amd64 /opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/so-elastic-agent_windows_amd64.exe
|
||||||
docker run \
|
docker run \
|
||||||
--mount type=bind,source=/opt/so/saltstack/local/salt/elasticfleet/files/,target=/output/ -w /output \
|
--mount type=bind,source=/opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/,target=/output/ -w /output \
|
||||||
{{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-elastic-agent-builder:{{ GLOBALS.so_version }} wixl -o so-elastic-agent_windows_amd64_msi --arch x64 /workspace/so-elastic-agent.wxs
|
{{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-elastic-agent-builder:{{ GLOBALS.so_version }} wixl -o so-elastic-agent_windows_amd64_msi --arch x64 /workspace/so-elastic-agent.wxs
|
||||||
printf "\n### MSI Generated...\n"
|
printf "\n### MSI Generated...\n"
|
||||||
|
|
||||||
# Verify installers were created
|
|
||||||
for GOOS in "${GOTARGETOS[@]}"; do
|
|
||||||
GOARCH="amd64"
|
|
||||||
if [[ $GOOS == 'darwin/arm64' ]]; then GOOS="darwin"; GOARCH="arm64"; fi
|
|
||||||
if [[ ! -f /opt/so/saltstack/local/salt/elasticfleet/files/so-elastic-agent_${GOOS}_${GOARCH} ]]; then
|
|
||||||
printf "\n### ERROR: Installer for %s/%s was not generated. Exiting...\n" "$GOOS" "$GOARCH"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# After verifying new installer was generated, move it to so_agent-installers directory
|
|
||||||
mv /opt/so/saltstack/local/salt/elasticfleet/files/so-elastic-agent_${GOOS}_${GOARCH} /opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/
|
|
||||||
done
|
|
||||||
|
|
||||||
# Verify MSI installer
|
|
||||||
if [[ ! -f /opt/so/saltstack/local/salt/elasticfleet/files/so-elastic-agent_windows_amd64_msi ]]; then
|
|
||||||
printf "\n### ERROR: Installer MSI was not generated. Exiting...\n"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
# After verifying new installer MSI was generated, move it to so_agent-installers directory
|
|
||||||
mv /opt/so/saltstack/local/salt/elasticfleet/files/so-elastic-agent_windows_amd64_msi /opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "\n### Cleaning up temp files \n"
|
printf "\n### Cleaning up temp files \n"
|
||||||
rm -rf /nsm/elastic-agent-workspace
|
rm -rf /nsm/elastic-agent-workspace
|
||||||
rm -rf /opt/so/saltstack/local/salt/elasticfleet/files/so-elastic-agent_windows_amd64.exe
|
rm -rf /opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/so-elastic-agent_windows_amd64.exe
|
||||||
|
|
||||||
printf "\n### Copying so_agent-installers to /nsm/elastic-fleet/ for nginx.\n"
|
printf "\n### Copying so_agent-installers to /nsm/elastic-fleet/ for nginx.\n"
|
||||||
\cp -vr /opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/ /nsm/elastic-fleet/
|
\cp -vr /opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers/ /nsm/elastic-fleet/
|
||||||
chmod 644 /nsm/elastic-fleet/so_agent-installers/*
|
chmod 644 /nsm/elastic-fleet/so_agent-installers/*
|
||||||
|
|
||||||
# if we got here all installers have been generated successfully
|
|
||||||
exit 0
|
|
||||||
|
|||||||
@@ -12,22 +12,17 @@ PKG_LOAD_FAILURES=0
|
|||||||
PKG_LOAD_FAILURES_NAMES=()
|
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."
|
PKG_LOAD_FAILURES=$((PKG_LOAD_FAILURES + 1))
|
||||||
else
|
PKG_LOAD_FAILURES_NAMES+=("{{ PACKAGE }}")
|
||||||
echo "Upgrading {{ PACKAGE }} package to version $LATEST_VERSION..."
|
|
||||||
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 }}"
|
|
||||||
PKG_LOAD_FAILURES=$((PKG_LOAD_FAILURES + 1))
|
PKG_LOAD_FAILURES=$((PKG_LOAD_FAILURES + 1))
|
||||||
PKG_LOAD_FAILURES_NAMES+=("{{ PACKAGE }}")
|
PKG_LOAD_FAILURES_NAMES+=("{{ PACKAGE }}")
|
||||||
fi
|
fi
|
||||||
|
echo
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
if [ $PKG_LOAD_FAILURES -gt 0 ]; then
|
if [ $PKG_LOAD_FAILURES -gt 0 ]; then
|
||||||
@@ -40,3 +35,6 @@ if [ $PKG_LOAD_FAILURES -gt 0 ]; then
|
|||||||
else
|
else
|
||||||
echo "Successfully upgraded all packages."
|
echo "Successfully upgraded all packages."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
/usr/sbin/so-elasticsearch-templates-load
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -244,37 +241,11 @@ printf '%s\n'\
|
|||||||
"" >> "$global_pillar_file"
|
"" >> "$global_pillar_file"
|
||||||
|
|
||||||
# Call Elastic-Fleet Salt State
|
# Call Elastic-Fleet Salt State
|
||||||
printf "\nApplying elasticfleet state\n"
|
printf "\nApplying elasticfleet state"
|
||||||
for state_attempt in {1..3}; do
|
salt-call state.apply elasticfleet queue=True
|
||||||
if salt-call state.apply elasticfleet queue=True; then
|
|
||||||
break
|
|
||||||
elif [[ $state_attempt -lt 3 ]]; then
|
|
||||||
printf "\nElasticfleet state did not complete successfully... Attempt (%s/3). Retrying...\n" "$state_attempt"
|
|
||||||
sleep 10
|
|
||||||
else
|
|
||||||
printf "\nFailure(s) in elasticfleet state... Exiting...\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
printf "\nRunning so-elastic-agent-gen-installers\n"
|
|
||||||
# Generate installers & install Elastic Agent on the node
|
# Generate installers & install Elastic Agent on the node
|
||||||
for agent_gen_attempt in {1..3}; do
|
so-elastic-agent-gen-installers
|
||||||
if so-elastic-agent-gen-installers; then
|
printf "\nApplying elasticfleet.install_agent_grid state"
|
||||||
break
|
salt-call state.apply elasticfleet.install_agent_grid queue=True
|
||||||
elif [[ $agent_gen_attempt -lt 3 ]]; then
|
exit 0
|
||||||
printf "\nUnable to generate Elastic Agent installers... Attempt (%s/3). Retrying...\n" "$agent_gen_attempt"
|
|
||||||
sleep 10
|
|
||||||
else
|
|
||||||
printf "\nFailed to generate Elastic Agent installers after 3 attempts. Exiting...\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
printf "\nApplying elasticfleet.install_agent_grid state\n"
|
|
||||||
if ! salt-call state.apply elasticfleet.install_agent_grid queue=True; then
|
|
||||||
printf "\nFailure(s) in elasticfleet.install_agent_grid state... Exiting...\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "\nElastic Fleet setup completed successfully\n"
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
elasticsearch:
|
elasticsearch:
|
||||||
enabled: false
|
enabled: false
|
||||||
version: 9.3.7
|
version: 9.3.3
|
||||||
index_clean: true
|
index_clean: true
|
||||||
data_retention_method: DLM
|
data_retention_method: DLM
|
||||||
vm:
|
vm:
|
||||||
|
|||||||
+10
-10
@@ -118,70 +118,70 @@
|
|||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_e16851a7",
|
"tag": "pipeline_e16851a7",
|
||||||
"name": "logs-pfsense.log-1.25.4-firewall",
|
"name": "logs-pfsense.log-1.25.2-firewall",
|
||||||
"if": "ctx.event.provider == 'filterlog'"
|
"if": "ctx.event.provider == 'filterlog'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_828590b5",
|
"tag": "pipeline_828590b5",
|
||||||
"name": "logs-pfsense.log-1.25.4-openvpn",
|
"name": "logs-pfsense.log-1.25.2-openvpn",
|
||||||
"if": "ctx.event.provider == 'openvpn'"
|
"if": "ctx.event.provider == 'openvpn'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_9d37039c",
|
"tag": "pipeline_9d37039c",
|
||||||
"name": "logs-pfsense.log-1.25.4-ipsec",
|
"name": "logs-pfsense.log-1.25.2-ipsec",
|
||||||
"if": "ctx.event.provider == 'charon'"
|
"if": "ctx.event.provider == 'charon'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_ad56bbca",
|
"tag": "pipeline_ad56bbca",
|
||||||
"name": "logs-pfsense.log-1.25.4-dhcp",
|
"name": "logs-pfsense.log-1.25.2-dhcp",
|
||||||
"if": "[\"dhcpd\", \"dhclient\", \"dhcp6c\", \"dnsmasq-dhcp\"].contains(ctx.event.provider)"
|
"if": "[\"dhcpd\", \"dhclient\", \"dhcp6c\", \"dnsmasq-dhcp\"].contains(ctx.event.provider)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_dd85553d",
|
"tag": "pipeline_dd85553d",
|
||||||
"name": "logs-pfsense.log-1.25.4-unbound",
|
"name": "logs-pfsense.log-1.25.2-unbound",
|
||||||
"if": "ctx.event.provider == 'unbound'"
|
"if": "ctx.event.provider == 'unbound'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_720ed255",
|
"tag": "pipeline_720ed255",
|
||||||
"name": "logs-pfsense.log-1.25.4-haproxy",
|
"name": "logs-pfsense.log-1.25.2-haproxy",
|
||||||
"if": "ctx.event.provider == 'haproxy'"
|
"if": "ctx.event.provider == 'haproxy'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_456beba5",
|
"tag": "pipeline_456beba5",
|
||||||
"name": "logs-pfsense.log-1.25.4-php-fpm",
|
"name": "logs-pfsense.log-1.25.2-php-fpm",
|
||||||
"if": "ctx.event.provider == 'php-fpm'"
|
"if": "ctx.event.provider == 'php-fpm'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_a0d89375",
|
"tag": "pipeline_a0d89375",
|
||||||
"name": "logs-pfsense.log-1.25.4-squid",
|
"name": "logs-pfsense.log-1.25.2-squid",
|
||||||
"if": "ctx.event.provider == 'squid'"
|
"if": "ctx.event.provider == 'squid'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag": "pipeline_c2f1ed55",
|
"tag": "pipeline_c2f1ed55",
|
||||||
"name": "logs-pfsense.log-1.25.4-snort",
|
"name": "logs-pfsense.log-1.25.2-snort",
|
||||||
"if": "ctx.event.provider == 'snort'"
|
"if": "ctx.event.provider == 'snort'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"tag":"pipeline_33db1c9e",
|
"tag":"pipeline_33db1c9e",
|
||||||
"name": "logs-pfsense.log-1.25.4-suricata",
|
"name": "logs-pfsense.log-1.25.2-suricata",
|
||||||
"if": "ctx.event.provider == 'suricata'"
|
"if": "ctx.event.provider == 'suricata'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -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 } },
|
||||||
|
|||||||
@@ -645,7 +645,6 @@ elasticsearch:
|
|||||||
global: True
|
global: True
|
||||||
advanced: True
|
advanced: True
|
||||||
helpLink: elasticsearch
|
helpLink: elasticsearch
|
||||||
so-logs-soc: *dataStreamSettings
|
|
||||||
so-logs-system_x_auth: *dataStreamSettings
|
so-logs-system_x_auth: *dataStreamSettings
|
||||||
so-logs-system_x_syslog: *dataStreamSettings
|
so-logs-system_x_syslog: *dataStreamSettings
|
||||||
so-logs-system_x_system: *dataStreamSettings
|
so-logs-system_x_system: *dataStreamSettings
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ kibana:
|
|||||||
- default
|
- default
|
||||||
- file
|
- file
|
||||||
migrations:
|
migrations:
|
||||||
discardCorruptObjects: "9.3.7"
|
discardCorruptObjects: "9.3.3"
|
||||||
telemetry:
|
telemetry:
|
||||||
enabled: False
|
enabled: False
|
||||||
xpack:
|
xpack:
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ wait_for_so-kibana:
|
|||||||
- ssl: True
|
- ssl: True
|
||||||
- verify_ssl: False
|
- verify_ssl: False
|
||||||
- status: 200
|
- status: 200
|
||||||
- wait_for: 600
|
- wait_for: 300
|
||||||
- request_interval: 15
|
- request_interval: 15
|
||||||
- require:
|
- require:
|
||||||
- docker_container: so-kibana
|
- docker_container: so-kibana
|
||||||
|
|||||||
+16
-174
@@ -12,17 +12,7 @@
|
|||||||
UPDATE_DIR=/tmp/sogh/securityonion
|
UPDATE_DIR=/tmp/sogh/securityonion
|
||||||
DEFAULT_SALT_DIR=/opt/so/saltstack/default
|
DEFAULT_SALT_DIR=/opt/so/saltstack/default
|
||||||
INSTALLEDVERSION=$(cat /etc/soversion)
|
INSTALLEDVERSION=$(cat /etc/soversion)
|
||||||
# /etc/sopostversion is a soup-owned marker (no salt state manages it) tracking how
|
POSTVERSION=$INSTALLEDVERSION
|
||||||
# far the post-upgrade walk has progressed. Its presence means a prior upgrade did
|
|
||||||
# not finish its post-upgrade steps; its contents are the resume point. It is read
|
|
||||||
# here before preupgrade_changes mutates INSTALLEDVERSION and before any highstate
|
|
||||||
# stamps /etc/soversion from the pillar.
|
|
||||||
POSTVERSION_FILE=/etc/sopostversion
|
|
||||||
if [ -f "$POSTVERSION_FILE" ]; then
|
|
||||||
POSTVERSION=$(cat "$POSTVERSION_FILE")
|
|
||||||
else
|
|
||||||
POSTVERSION=$INSTALLEDVERSION
|
|
||||||
fi
|
|
||||||
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
|
||||||
@@ -33,10 +23,6 @@ NOTIFYCUSTOMELASTICCONFIG=false
|
|||||||
TOPFILE=/opt/so/saltstack/default/salt/top.sls
|
TOPFILE=/opt/so/saltstack/default/salt/top.sls
|
||||||
BACKUPTOPFILE=/opt/so/saltstack/default/salt/top.sls.backup
|
BACKUPTOPFILE=/opt/so/saltstack/default/salt/top.sls.backup
|
||||||
SALTUPGRADED=false
|
SALTUPGRADED=false
|
||||||
# Set true once soup begins modifying the system (past the pre-flight checks), so the
|
|
||||||
# EXIT trap can tell the user the update did not finish and must be re-run. Only the
|
|
||||||
# pre-flight gates (ES compatibility, disk, network) fail before this is set.
|
|
||||||
SOUP_UPGRADE_STARTED=false
|
|
||||||
SALT_CLOUD_INSTALLED=false
|
SALT_CLOUD_INSTALLED=false
|
||||||
SALT_CLOUD_CONFIGURED=false
|
SALT_CLOUD_CONFIGURED=false
|
||||||
# Check if salt-cloud is installed
|
# Check if salt-cloud is installed
|
||||||
@@ -137,28 +123,6 @@ check_err() {
|
|||||||
|
|
||||||
echo "SOUP XTRACE debug log (if enabled) at $SOUP_DEBUG_LOG. Re-run soup with SOUP_DEBUG=1 to create $SOUP_DEBUG_LOG"
|
echo "SOUP XTRACE debug log (if enabled) at $SOUP_DEBUG_LOG. Re-run soup with SOUP_DEBUG=1 to create $SOUP_DEBUG_LOG"
|
||||||
|
|
||||||
# If soup had already started modifying the system, make it unmistakable that the
|
|
||||||
# update is incomplete and must be re-run. soup is resumable: a version upgrade
|
|
||||||
# picks up from the /etc/sopostversion marker, and a hotfix re-applies because
|
|
||||||
# /etc/sohotfix is only advanced after a successful highstate.
|
|
||||||
if [[ "$SOUP_UPGRADE_STARTED" == "true" ]]; then
|
|
||||||
echo ""
|
|
||||||
echo "=============================================================================="
|
|
||||||
echo " UPGRADE INCOMPLETE"
|
|
||||||
echo "=============================================================================="
|
|
||||||
echo " This soup run did NOT finish. Your Security Onion installation may be in a"
|
|
||||||
echo " partially-updated state and is not yet fully upgraded."
|
|
||||||
echo ""
|
|
||||||
echo " Review the error above and $SOUP_LOG, resolve the underlying problem, then"
|
|
||||||
echo " run soup again to resume and complete the update:"
|
|
||||||
echo ""
|
|
||||||
echo " sudo soup"
|
|
||||||
echo ""
|
|
||||||
echo " soup is resumable -- re-running it continues from where this run stopped."
|
|
||||||
echo "=============================================================================="
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit $exit_code
|
exit $exit_code
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -281,7 +245,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
|
||||||
@@ -327,30 +290,6 @@ check_pillar_items() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_cluster_health() {
|
|
||||||
echo "Checking Elasticsearch cluster health."
|
|
||||||
# Require a 'green' cluster before upgrading; anything less (yellow, red, or
|
|
||||||
# unreachable) blocks. Modeled on the wait used in so-elasticsearch-roles-load.
|
|
||||||
if so-elasticsearch-query "_cluster/health?wait_for_status=green&timeout=120s" --fail > /dev/null 2>&1; then
|
|
||||||
printf "\nThe Elasticsearch cluster is healthy (green). We can proceed with SOUP.\n\n"
|
|
||||||
else
|
|
||||||
printf "\nThe Elasticsearch cluster is not green. Please resolve the cluster health issue so the cluster is green before running SOUP again.\n\n"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_fleet_server() {
|
|
||||||
echo "Checking that Elastic Fleet Server is responding."
|
|
||||||
# Modeled on the wait_for_so-elastic-fleet state check in elasticfleet/enabled.sls,
|
|
||||||
# which waits for HTTP 200 from the Fleet Server status API.
|
|
||||||
if curl -sk --fail --retry 3 --retry-delay 10 --max-time 30 "https://localhost:8220/api/status" > /dev/null 2>&1; then
|
|
||||||
printf "\nElastic Fleet Server is responding. We can proceed with SOUP.\n\n"
|
|
||||||
else
|
|
||||||
printf "\nElastic Fleet Server is not responding at https://localhost:8220/api/status. Please ensure Elastic Fleet is healthy before running SOUP again.\n\n"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_saltmaster_status() {
|
check_saltmaster_status() {
|
||||||
set +e
|
set +e
|
||||||
echo "Waiting on the Salt Master service to be ready."
|
echo "Waiting on the Salt Master service to be ready."
|
||||||
@@ -474,13 +413,6 @@ preupgrade_changes() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
set_postversion() {
|
|
||||||
# Persist post-upgrade walk progress so an interrupted upgrade can resume the
|
|
||||||
# remaining steps on the next soup run (see /etc/sopostversion handling).
|
|
||||||
POSTVERSION="$1"
|
|
||||||
echo "$POSTVERSION" > "$POSTVERSION_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
postupgrade_changes() {
|
postupgrade_changes() {
|
||||||
# This function is to add any new pillar items if needed.
|
# This function is to add any new pillar items if needed.
|
||||||
echo "Running post upgrade processes."
|
echo "Running post upgrade processes."
|
||||||
@@ -488,8 +420,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
|
[[ "$POSTVERSION" == "3.1.0" ]] && post_to_3.2.0
|
||||||
# All applicable post-upgrade steps completed; clear the resume marker.
|
|
||||||
rm -f "$POSTVERSION_FILE"
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,7 +512,7 @@ post_to_3.0.0() {
|
|||||||
# convert yes/no in suricata pillars to true/false
|
# convert yes/no in suricata pillars to true/false
|
||||||
convert_suricata_yes_no
|
convert_suricata_yes_no
|
||||||
|
|
||||||
set_postversion 3.0.0
|
POSTVERSION=3.0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
### 3.0.0 End ###
|
### 3.0.0 End ###
|
||||||
@@ -809,6 +739,7 @@ fix_logstash_0013_lumberjack_pipeline_name() {
|
|||||||
up_to_3.1.0() {
|
up_to_3.1.0() {
|
||||||
ensure_postgres_local_pillar
|
ensure_postgres_local_pillar
|
||||||
ensure_postgres_secret
|
ensure_postgres_secret
|
||||||
|
determine_elastic_agent_upgrade
|
||||||
elasticsearch_backup_index_templates
|
elasticsearch_backup_index_templates
|
||||||
# Clear existing component template state file.
|
# Clear existing component template state file.
|
||||||
rm -f /opt/so/state/esfleet_component_templates.json
|
rm -f /opt/so/state/esfleet_component_templates.json
|
||||||
@@ -845,30 +776,19 @@ post_to_3.1.0() {
|
|||||||
# Check for unhealthy / unauthorized integration transform jobs and attempt reauthorizations
|
# Check for unhealthy / unauthorized integration transform jobs and attempt reauthorizations
|
||||||
check_transform_health_and_reauthorize || true
|
check_transform_health_and_reauthorize || true
|
||||||
|
|
||||||
set_postversion 3.1.0
|
POSTVERSION=3.1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
### 3.1.0 End ###
|
### 3.1.0 End ###
|
||||||
|
|
||||||
### 3.2.0 Scripts ###
|
### 3.2.0 Scripts ###
|
||||||
|
|
||||||
recollate_postgres() {
|
|
||||||
echo ""
|
|
||||||
echo "Recollating PostgreSQL databases. The following output may contain warnings about a version mismatch, followed by a note indicating that the collation version has been changed."
|
|
||||||
for db in postgres securityonion so_telegraf; do
|
|
||||||
docker exec so-postgres psql -U postgres $db -c "reindex database $db"
|
|
||||||
docker exec so-postgres psql -U postgres $db -c "alter database $db refresh collation version"
|
|
||||||
done
|
|
||||||
echo "Recollating PostgreSQL databases complete."
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
bootstrap_so_soc_database() {
|
bootstrap_so_soc_database() {
|
||||||
# init-db.sh is mounted into so-postgres at /docker-entrypoint-initdb.d/init-db.sh
|
# 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
|
# 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
|
# 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.
|
# added in 3.2 never fires. Re-run the script explicitly; it's idempotent.
|
||||||
echo "Bootstrapping database via init-db.sh."
|
echo "Bootstrapping so_soc database via init-db.sh."
|
||||||
# The postgres image has no USER directive, so `docker exec` defaults to
|
# The postgres image has no USER directive, so `docker exec` defaults to
|
||||||
# root, and the container env intentionally omits POSTGRES_USER (the upstream
|
# root, and the container env intentionally omits POSTGRES_USER (the upstream
|
||||||
# entrypoint defaults it transiently during first-init only). Recreate both
|
# entrypoint defaults it transiently during first-init only). Recreate both
|
||||||
@@ -879,13 +799,10 @@ bootstrap_so_soc_database() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if ! $exec_cmd; then
|
if ! $exec_cmd; then
|
||||||
FINAL_MESSAGE_QUEUE+=("WARNING: init-db.sh failed inside so-postgres during the 3.2.0 upgrade; the database may not have been bootstrapped. Re-run manually: $exec_cmd")
|
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
|
return 0
|
||||||
fi
|
fi
|
||||||
echo "Database bootstrap complete."
|
echo "so_soc bootstrap complete."
|
||||||
|
|
||||||
echo "Restarting so-soc container to pick up database changes"
|
|
||||||
docker restart so-soc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Existing grids should keep ILM unless an admin explicitly opts in to DLM.
|
# Existing grids should keep ILM unless an admin explicitly opts in to DLM.
|
||||||
@@ -933,32 +850,7 @@ kibana_backport_streams_index_template() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 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() {
|
up_to_3.2.0() {
|
||||||
# download 9.3.7 elastic agent packages
|
|
||||||
determine_elastic_agent_upgrade
|
|
||||||
|
|
||||||
fix_logstash_0013_lumberjack_pipeline_name
|
fix_logstash_0013_lumberjack_pipeline_name
|
||||||
|
|
||||||
pin_elasticsearch_data_retention_method
|
pin_elasticsearch_data_retention_method
|
||||||
@@ -967,20 +859,15 @@ up_to_3.2.0() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
post_to_3.2.0() {
|
post_to_3.2.0() {
|
||||||
# Recollate due to image OS rebase
|
|
||||||
recollate_postgres
|
|
||||||
|
|
||||||
bootstrap_so_soc_database
|
bootstrap_so_soc_database
|
||||||
|
|
||||||
# Generate 9.3.7 elastic agent installers
|
# Including agent regen script here since it was missed in post_to_3.1.0
|
||||||
echo "Regenerating Elastic Agent Installers"
|
echo "Regenerating Elastic Agent Installers"
|
||||||
/sbin/so-elastic-agent-gen-installers
|
/sbin/so-elastic-agent-gen-installers
|
||||||
|
|
||||||
kibana_backport_streams_index_template
|
kibana_backport_streams_index_template
|
||||||
|
|
||||||
update_kafka_metadata "4.3"
|
POSTVERSION=3.2.0
|
||||||
|
|
||||||
set_postversion 3.2.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
### 3.2.0 End ###
|
### 3.2.0 End ###
|
||||||
@@ -1093,19 +980,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() {
|
||||||
@@ -1132,20 +1013,8 @@ upgrade_check() {
|
|||||||
fi
|
fi
|
||||||
[[ -f /etc/sohotfix ]] && CURRENTHOTFIX=$(cat /etc/sohotfix)
|
[[ -f /etc/sohotfix ]] && CURRENTHOTFIX=$(cat /etc/sohotfix)
|
||||||
if [ "$INSTALLEDVERSION" == "$NEWVERSION" ]; then
|
if [ "$INSTALLEDVERSION" == "$NEWVERSION" ]; then
|
||||||
# A leftover post-version marker means a previous upgrade to this version
|
|
||||||
# advanced /etc/soversion (the highstate stamps it from the pillar) but did not
|
|
||||||
# finish its post-upgrade steps. Resume the upgrade instead of reporting "latest".
|
|
||||||
if [ -f "$POSTVERSION_FILE" ] && [ "$(cat "$POSTVERSION_FILE")" != "$NEWVERSION" ]; then
|
|
||||||
echo "A previous upgrade to $NEWVERSION did not complete its post-upgrade steps; resuming."
|
|
||||||
is_hotfix=false
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
echo "Checking to see if there are hotfixes needed"
|
echo "Checking to see if there are hotfixes needed"
|
||||||
if [ "$HOTFIXVERSION" == "$CURRENTHOTFIX" ]; then
|
if [ "$HOTFIXVERSION" == "$CURRENTHOTFIX" ]; then
|
||||||
# Reaching here means we are at the target version and NOT resuming (the resume
|
|
||||||
# check above returned otherwise). Clear any stale resume marker so a completed
|
|
||||||
# upgrade is never mistaken for a partial one and re-run on a later invocation.
|
|
||||||
rm -f "$POSTVERSION_FILE"
|
|
||||||
echo "You are already running the latest version of Security Onion."
|
echo "You are already running the latest version of Security Onion."
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
@@ -1257,8 +1126,7 @@ verify_es_version_compatibility() {
|
|||||||
["8.18.4"]="8.18.6 8.18.8 9.0.8"
|
["8.18.4"]="8.18.6 8.18.8 9.0.8"
|
||||||
["8.18.6"]="8.18.8 9.0.8"
|
["8.18.6"]="8.18.8 9.0.8"
|
||||||
["8.18.8"]="9.0.8"
|
["8.18.8"]="9.0.8"
|
||||||
["9.0.8"]="9.3.3 9.3.7"
|
["9.0.8"]="9.3.3"
|
||||||
["9.3.3"]="9.3.7"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Elasticsearch MUST upgrade through these versions
|
# Elasticsearch MUST upgrade through these versions
|
||||||
@@ -1841,15 +1709,6 @@ main() {
|
|||||||
set_minionid
|
set_minionid
|
||||||
MINION_ROLE=$(lookup_role)
|
MINION_ROLE=$(lookup_role)
|
||||||
echo "Found that Security Onion $INSTALLEDVERSION is currently installed."
|
echo "Found that Security Onion $INSTALLEDVERSION is currently installed."
|
||||||
# /etc/soversion is stamped to the target version before the upgrade fully
|
|
||||||
# completes, so a lingering resume marker means this grid is only partially
|
|
||||||
# upgraded even though the line above shows the target version. Make that explicit
|
|
||||||
# so it is not mistaken for a finished upgrade.
|
|
||||||
if [ -f "$POSTVERSION_FILE" ] && [ "$(cat "$POSTVERSION_FILE")" != "$INSTALLEDVERSION" ]; then
|
|
||||||
echo ""
|
|
||||||
echo "NOTE: A previous upgrade to $INSTALLEDVERSION did not finish. This grid is"
|
|
||||||
echo " partially upgraded and this soup run will resume and complete it."
|
|
||||||
fi
|
|
||||||
echo ""
|
echo ""
|
||||||
check_minimum_version
|
check_minimum_version
|
||||||
|
|
||||||
@@ -1878,18 +1737,12 @@ main() {
|
|||||||
echo "Verifying Elasticsearch version compatibility across the grid before upgrading."
|
echo "Verifying Elasticsearch version compatibility across the grid before upgrading."
|
||||||
verify_es_version_compatibility
|
verify_es_version_compatibility
|
||||||
|
|
||||||
# Pre-flight health checks: confirm the grid is in a good state before we change
|
|
||||||
# anything. These run before any modifications, so a failure exits cleanly and the
|
|
||||||
# operator can fix the issue and re-run soup.
|
|
||||||
check_cluster_health
|
|
||||||
check_fleet_server
|
|
||||||
|
|
||||||
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
|
||||||
@@ -1900,7 +1753,6 @@ main() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$is_hotfix" == "true" ]; then
|
if [ "$is_hotfix" == "true" ]; then
|
||||||
SOUP_UPGRADE_STARTED=true
|
|
||||||
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
|
||||||
@@ -1911,16 +1763,10 @@ main() {
|
|||||||
create_local_directories "/opt/so/saltstack/default"
|
create_local_directories "/opt/so/saltstack/default"
|
||||||
apply_hotfix
|
apply_hotfix
|
||||||
echo "Hotfix applied"
|
echo "Hotfix applied"
|
||||||
|
update_version
|
||||||
enable_highstate
|
enable_highstate
|
||||||
highstate
|
highstate
|
||||||
# Record the hotfix only after the highstate succeeds. /etc/sohotfix is written
|
|
||||||
# solely by soup (no salt state manages it), so deferring the write means a failed
|
|
||||||
# hotfix highstate leaves the old hotfix value and re-running soup re-applies it,
|
|
||||||
# rather than reporting "already latest". The soversion/pillar writes in
|
|
||||||
# update_version are no-ops here since the version is unchanged for a hotfix.
|
|
||||||
update_version
|
|
||||||
else
|
else
|
||||||
SOUP_UPGRADE_STARTED=true
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Performing upgrade from Security Onion $INSTALLEDVERSION to Security Onion $NEWVERSION."
|
echo "Performing upgrade from Security Onion $INSTALLEDVERSION to Security Onion $NEWVERSION."
|
||||||
echo ""
|
echo ""
|
||||||
@@ -1976,10 +1822,6 @@ main() {
|
|||||||
copy_new_files
|
copy_new_files
|
||||||
echo ""
|
echo ""
|
||||||
create_local_directories "/opt/so/saltstack/default"
|
create_local_directories "/opt/so/saltstack/default"
|
||||||
# Seed the resume marker before the highstate stamps /etc/soversion to the new
|
|
||||||
# version, so an interrupted upgrade is detectable as "not finished" on re-run.
|
|
||||||
# POSTVERSION still holds the pre-upgrade (or prior resume) version here.
|
|
||||||
[ -f "$POSTVERSION_FILE" ] || echo "$POSTVERSION" > "$POSTVERSION_FILE"
|
|
||||||
update_version
|
update_version
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ http {
|
|||||||
error_page 429 = @error429;
|
error_page 429 = @error429;
|
||||||
|
|
||||||
location @error401 {
|
location @error401 {
|
||||||
if ($request_uri ~* (^.*/api/.*|^/connect/.*|^/oauth2/.*|^/.*\.map$)) {
|
if ($request_uri ~* (^/api/.*|^/connect/.*|^/oauth2/.*|^/.*\.map$)) {
|
||||||
return 401;
|
return 401;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
{% from 'docker/docker.map.jinja' import DOCKERMERGED -%}
|
{% from 'docker/docker.map.jinja' import DOCKERMERGED -%}
|
||||||
{% set INFLUXDB_TOKEN = salt['pillar.get']('influxdb:token') %}
|
{% set INFLUXDB_TOKEN = salt['pillar.get']('influxdb:token') %}
|
||||||
{% import_text 'influxdb/metrics_link.txt' as METRICS_LINK %}
|
{% import_text 'influxdb/metrics_link.txt' as METRICS_LINK %}
|
||||||
{% from 'telegraf/map.jinja' import TELEGRAFMERGED %}
|
|
||||||
|
|
||||||
{% for module, application_url in GLOBALS.application_urls.items() %}
|
{% for module, application_url in GLOBALS.application_urls.items() %}
|
||||||
{% do SOCDEFAULTS.soc.config.server.modules[module].update({'hostUrl': application_url}) %}
|
{% do SOCDEFAULTS.soc.config.server.modules[module].update({'hostUrl': application_url}) %}
|
||||||
@@ -25,22 +24,13 @@
|
|||||||
|
|
||||||
{% do SOCDEFAULTS.soc.config.server.modules.elastic.update({'username': GLOBALS.elasticsearch.auth.users.so_elastic_user.user, 'password': GLOBALS.elasticsearch.auth.users.so_elastic_user.pass}) %}
|
{% do SOCDEFAULTS.soc.config.server.modules.elastic.update({'username': GLOBALS.elasticsearch.auth.users.so_elastic_user.user, 'password': GLOBALS.elasticsearch.auth.users.so_elastic_user.pass}) %}
|
||||||
|
|
||||||
{% if TELEGRAFMERGED.output == 'POSTGRES' %}
|
{% do SOCDEFAULTS.soc.config.server.modules.influxdb.update({'hostUrl': 'https://' ~ GLOBALS.influxdb_host ~ ':8086'}) %}
|
||||||
{% for tool in SOCDEFAULTS.soc.config.server.client.tools %}
|
{% do SOCDEFAULTS.soc.config.server.modules.influxdb.update({'token': INFLUXDB_TOKEN}) %}
|
||||||
{% if tool.name == "toolInfluxDb" %}
|
{% for tool in SOCDEFAULTS.soc.config.server.client.tools %}
|
||||||
{% do SOCDEFAULTS.soc.config.server.client.tools.remove(tool) %}
|
{% if tool.name == "toolInfluxDb" and METRICS_LINK | length > 0 %}
|
||||||
{% endif %}
|
{% do tool.update({'link': METRICS_LINK}) %}
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
{% else %}
|
|
||||||
{% do SOCDEFAULTS.soc.config.server.modules.influxdb.update({'hostUrl': 'https://' ~ GLOBALS.influxdb_host ~ ':8086'}) %}
|
|
||||||
{% do SOCDEFAULTS.soc.config.server.modules.influxdb.update({'token': INFLUXDB_TOKEN}) %}
|
|
||||||
{% for tool in SOCDEFAULTS.soc.config.server.client.tools %}
|
|
||||||
{% if tool.name == "toolInfluxDb" and METRICS_LINK | length > 0 %}
|
|
||||||
{% do tool.update({'link': METRICS_LINK}) %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% do SOCDEFAULTS.soc.config.server.modules.statickeyauth.update({'anonymousCidr': DOCKERMERGED.range, 'apiKey': pillar.sensoroni.config.sensoronikey}) %}
|
{% do SOCDEFAULTS.soc.config.server.modules.statickeyauth.update({'anonymousCidr': DOCKERMERGED.range, 'apiKey': pillar.sensoroni.config.sensoronikey}) %}
|
||||||
|
|
||||||
|
|||||||
+4
-18
@@ -1500,23 +1500,15 @@ soc:
|
|||||||
playbookRepos:
|
playbookRepos:
|
||||||
default:
|
default:
|
||||||
- repo: https://github.com/Security-Onion-Solutions/securityonion-resources-playbooks
|
- repo: https://github.com/Security-Onion-Solutions/securityonion-resources-playbooks
|
||||||
rulesetName: sos-playbook-resources
|
|
||||||
branch: main
|
branch: main
|
||||||
folder: securityonion-normalized
|
folder: securityonion-normalized
|
||||||
- repo: https://github.com/Security-Onion-Solutions/securityonion-resources-playbooks
|
|
||||||
rulesetName: sos-published
|
|
||||||
branch: published
|
|
||||||
folder: sigma
|
|
||||||
airgap:
|
airgap:
|
||||||
- repo: file:///nsm/airgap-resources/playbooks/securityonion-resources-playbooks
|
- repo: file:///nsm/airgap-resources/playbooks/securityonion-resources-playbooks
|
||||||
rulesetName: sos-resources-ag
|
|
||||||
branch: main
|
branch: main
|
||||||
folder: securityonion-normalized
|
folder: securityonion-normalized
|
||||||
assistant:
|
assistant:
|
||||||
systemPromptAddendum: ""
|
systemPromptAddendum: ""
|
||||||
systemPromptAddendumMaxLength: 50000
|
systemPromptAddendumMaxLength: 50000
|
||||||
maxSubSessionTokens: 0
|
|
||||||
maxDelegationDepth: 5
|
|
||||||
adapters:
|
adapters:
|
||||||
- name: SOAI
|
- name: SOAI
|
||||||
protocol: securityonion_ai_cloud
|
protocol: securityonion_ai_cloud
|
||||||
@@ -1528,10 +1520,6 @@ soc:
|
|||||||
serviceAccountJSON: ""
|
serviceAccountJSON: ""
|
||||||
serviceAccountLocation: ""
|
serviceAccountLocation: ""
|
||||||
healthTimeoutSeconds: 5
|
healthTimeoutSeconds: 5
|
||||||
agentic: false
|
|
||||||
agentMapping:
|
|
||||||
Orchestrator: sonnet
|
|
||||||
Hunter: sonnet
|
|
||||||
onionconfig:
|
onionconfig:
|
||||||
saltstackDir: /opt/so/saltstack
|
saltstackDir: /opt/so/saltstack
|
||||||
bypassEnabled: false
|
bypassEnabled: false
|
||||||
@@ -1783,13 +1771,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'
|
||||||
@@ -2701,8 +2689,6 @@ soc:
|
|||||||
thresholdColorRatioLow: 0.5
|
thresholdColorRatioLow: 0.5
|
||||||
thresholdColorRatioMed: 0.75
|
thresholdColorRatioMed: 0.75
|
||||||
thresholdColorRatioMax: 1
|
thresholdColorRatioMax: 1
|
||||||
toolBusyMaxRetries: 30
|
|
||||||
toolBusyRetryDelayMs: 1000
|
|
||||||
availableModels:
|
availableModels:
|
||||||
- id: sonnet
|
- id: sonnet
|
||||||
displayName: Claude Sonnet
|
displayName: Claude Sonnet
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -7,11 +7,6 @@
|
|||||||
{% from 'soc/defaults.map.jinja' import SOCDEFAULTS with context %}
|
{% from 'soc/defaults.map.jinja' import SOCDEFAULTS with context %}
|
||||||
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCH_NODES %}
|
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCH_NODES %}
|
||||||
{% from 'manager/map.jinja' import MANAGERMERGED %}
|
{% from 'manager/map.jinja' import MANAGERMERGED %}
|
||||||
{% from 'telegraf/map.jinja' import TELEGRAFMERGED %}
|
|
||||||
{%- set PG_ENTRY = salt['pillar.get']('telegraf:postgres_creds:' ~ grains.id, {}) %}
|
|
||||||
{%- set PG_USER = PG_ENTRY.get('user', '') %}
|
|
||||||
{%- set PG_PASS = PG_ENTRY.get('pass', '') %}
|
|
||||||
|
|
||||||
{% set DOCKER_EXTRA_HOSTS = ELASTICSEARCH_NODES %}
|
{% set DOCKER_EXTRA_HOSTS = ELASTICSEARCH_NODES %}
|
||||||
{% do DOCKER_EXTRA_HOSTS.append({GLOBALS.influxdb_host:pillar.node_data[GLOBALS.influxdb_host].ip}) %}
|
{% do DOCKER_EXTRA_HOSTS.append({GLOBALS.influxdb_host:pillar.node_data[GLOBALS.influxdb_host].ip}) %}
|
||||||
|
|
||||||
@@ -80,20 +75,6 @@
|
|||||||
{% do SOCMERGED.config.server.update({'airgapEnabled': false}) %}
|
{% do SOCMERGED.config.server.update({'airgapEnabled': false}) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Define the postgresmetrics module if telegraf is setup to only use Postgres #}
|
|
||||||
{% if TELEGRAFMERGED.output != 'INFLUXDB' and PG_USER and PG_PASS %}
|
|
||||||
{% do SOCMERGED.config.server.modules.update({
|
|
||||||
'postgresmetrics': {
|
|
||||||
'database': 'so_telegraf',
|
|
||||||
'host': GLOBALS.manager_ip,
|
|
||||||
'password': PG_PASS,
|
|
||||||
'port': 5432,
|
|
||||||
'sslMode': 'allow',
|
|
||||||
'user': PG_USER,
|
|
||||||
}
|
|
||||||
}) %}
|
|
||||||
{% do SOCMERGED.config.server.modules.pop('influxdb') %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Define the Detections custom ruleset that should always be present #}
|
{# Define the Detections custom ruleset that should always be present #}
|
||||||
{% set CUSTOM_RULESET = {
|
{% set CUSTOM_RULESET = {
|
||||||
|
|||||||
+1
-36
@@ -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:
|
||||||
@@ -727,16 +719,6 @@ soc:
|
|||||||
description: Maximum length of the system prompt addendum. Longer prompts will be truncated.
|
description: Maximum length of the system prompt addendum. Longer prompts will be truncated.
|
||||||
global: True
|
global: True
|
||||||
advanced: True
|
advanced: True
|
||||||
maxSubSessionTokens:
|
|
||||||
description: Maximum number of output tokens a delegated sub-session may generate across all of its turns. When the budget is reached, the sub-agent is halted and its result is returned to the parent agent. Set to 0 to disable the limit.
|
|
||||||
global: True
|
|
||||||
advanced: True
|
|
||||||
forcedType: int
|
|
||||||
maxDelegationDepth:
|
|
||||||
description: Maximum delegation nesting depth for sub-agents. For example, a value of 2 lets the main agent delegate to a sub-agent that may itself delegate one level deeper. Any deeper delegation is refused and the requesting agent continues without it. Set to 0 to disable the limit.
|
|
||||||
global: True
|
|
||||||
advanced: True
|
|
||||||
forcedType: int
|
|
||||||
adapters:
|
adapters:
|
||||||
description: Configuration for AI adapters used by the Onion AI assistant. Please see documentation for help on which fields are required for which protocols.
|
description: Configuration for AI adapters used by the Onion AI assistant. Please see documentation for help on which fields are required for which protocols.
|
||||||
global: True
|
global: True
|
||||||
@@ -775,29 +757,12 @@ soc:
|
|||||||
label: Health Timeout Seconds
|
label: Health Timeout Seconds
|
||||||
required: False
|
required: False
|
||||||
forcedType: int
|
forcedType: int
|
||||||
agentic:
|
|
||||||
description: Indicates if the Assistant Module should operate in agentic mode or not. If true, agents can work together to solve tasks.
|
|
||||||
global: True
|
|
||||||
forcedType: bool
|
|
||||||
agentMapping:
|
|
||||||
Orchestrator:
|
|
||||||
description: The initial agent in most agentic conversations. This agent will delegate requests to specialized agents.
|
|
||||||
global: True
|
|
||||||
Hunter:
|
|
||||||
description: This agent is specialized in querying events.
|
|
||||||
global: True
|
|
||||||
client:
|
client:
|
||||||
assistant:
|
assistant:
|
||||||
enabled:
|
enabled:
|
||||||
description: Set to true to enable the Onion AI assistant in SOC.
|
description: Set to true to enable the Onion AI assistant in SOC.
|
||||||
global: True
|
global: True
|
||||||
forcedType: bool
|
forcedType: bool
|
||||||
toolBusyMaxRetries:
|
|
||||||
description: How many times to retry auto approving a tool while a tool is already running.
|
|
||||||
global: True
|
|
||||||
toolBusyRetryDelayMs:
|
|
||||||
description: How long in milliseconds to wait between each retry when auto approving a tool.
|
|
||||||
global: True
|
|
||||||
investigationPrompt:
|
investigationPrompt:
|
||||||
description: Prompt given to Onion AI when beginning an investigation.
|
description: Prompt given to Onion AI when beginning an investigation.
|
||||||
global: True
|
global: True
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ surirulereload:
|
|||||||
- 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]}"
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ telegraf:
|
|||||||
advanced: True
|
advanced: True
|
||||||
helpLink: influxdb
|
helpLink: influxdb
|
||||||
output:
|
output:
|
||||||
description: Selects the backend(s) Telegraf writes metrics to. INFLUXDB keeps the current behavior; POSTGRES writes to the grid's Postgres instance; BOTH dual-writes for migration validation. When set to BOTH, the grid screen's metrics are pulled from Postgres, and the InfluxDB tool link remains visible. When set to POSTGRES, the InfluxDB tool link is removed.
|
description: Selects the backend(s) Telegraf writes metrics to. INFLUXDB keeps the current behavior; POSTGRES writes to the grid's Postgres instance; BOTH dual-writes for migration validation.
|
||||||
options:
|
options:
|
||||||
- INFLUXDB
|
- INFLUXDB
|
||||||
- POSTGRES
|
- POSTGRES
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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\"]}}}}}"
|
||||||
@@ -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 %}
|
||||||
+16
-17
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,7 +697,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 +755,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"
|
||||||
@@ -917,7 +914,8 @@ detect_os() {
|
|||||||
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."
|
||||||
|
fail_setup
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Found OS: $OS $OSVER"
|
info "Found OS: $OS $OSVER"
|
||||||
@@ -925,7 +923,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1569,7 +1567,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 +1774,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"
|
||||||
}
|
}
|
||||||
@@ -1911,8 +1910,8 @@ repo_sync_local() {
|
|||||||
|
|
||||||
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"
|
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
|
||||||
# After the download is complete run createrepo
|
# After the download is complete run createrepo
|
||||||
create_repo
|
create_repo
|
||||||
fi
|
fi
|
||||||
@@ -1925,10 +1924,10 @@ saltify() {
|
|||||||
|
|
||||||
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 -r -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 -r -X stable $SALTVERSION" || fail_setup
|
||||||
fi
|
fi
|
||||||
|
|
||||||
salt_install_module_deps
|
salt_install_module_deps
|
||||||
@@ -2000,7 +1999,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 +2203,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
|
||||||
|
|||||||
+13
-12
@@ -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
|
||||||
@@ -237,7 +235,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
|
||||||
|
|
||||||
@@ -771,7 +770,8 @@ if ! [[ -f $install_opt_file ]]; then
|
|||||||
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
|
if ! docker_seed_registry; then
|
||||||
fail_setup "Failed to seed the docker registry"
|
error "Failed to seed the docker registry"
|
||||||
|
fail_setup
|
||||||
fi
|
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"
|
||||||
@@ -793,8 +793,9 @@ if ! [[ -f $install_opt_file ]]; then
|
|||||||
logCmd "so-soc-restart"
|
logCmd "so-soc-restart"
|
||||||
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 ! 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
|
mark_setup_complete
|
||||||
set_initial_firewall_access
|
set_initial_firewall_access
|
||||||
|
|||||||
+3
-3
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user