diff --git a/salt/manager/tools/sbin/soup b/salt/manager/tools/sbin/soup index b6be60709..94fb1305f 100755 --- a/salt/manager/tools/sbin/soup +++ b/salt/manager/tools/sbin/soup @@ -344,6 +344,16 @@ check_cluster_health() { check_fleet_server() { echo "Checking that Elastic Fleet Server is responding." + # Before checking fleet health, check for and fix known issue with elastic-agent container and fs.protected_symlinks + local protected_symlinks=$(sysctl -b fs.protected_symlinks) + if [[ "$protected_symlinks" == "1" ]]; then + # disable fs.protected_symlinks and restart elasticfleet + sysctl -w fs.protected_symlinks=0 + docker stop so-elastic-fleet; docker rm -f so-elastic-fleet + printf "\nUpdated sysctl fs.protected_symlinks. Restarting fleet before running health check and continuing with soup.\n" + salt-call state.apply elasticfleet queue=True + fi + # 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 @@ -1036,8 +1046,20 @@ post_to_3.2.0() { } ### 3.2.0 End ### -### 3.2.0 Scripts ### +### 3.3.0 Scripts ### + + # Sets fs.protected_symlinks=0 + # + # Elastic Agent docker image chowns its directory to the running UID + # but does not chown the elastic-agent launcher symlink. + # Preventing non-root users from following that launcher symlink. +disable_sysctl_fs_protected_symlink() { + salt -C 'I@stig:enabled' state.single sysctl.present name=fs.protected_symlinks value=0 config=/etc/sysctl.conf || true +} + up_to_3.3.0() { + disable_sysctl_fs_protected_symlink + INSTALLEDVERSION=3.3.0 } diff --git a/salt/salt/minion/boot_highstate.sls b/salt/salt/minion/boot_highstate.sls index eb2596dad..b97972c9f 100644 --- a/salt/salt/minion/boot_highstate.sls +++ b/salt/salt/minion/boot_highstate.sls @@ -3,6 +3,8 @@ # https://securityonion.net/license; you may not use this file except in compliance with the # Elastic License 2.0. +{% from 'vars/globals.map.jinja' import GLOBALS %} + # Manages /etc/systemd/system/so-boot-highstate.service, a Type=oneshot # RemainAfterExit=yes unit that runs `salt-call state.highstate` exactly once # per system boot. Replaces the legacy `startup_states: highstate` minion @@ -19,9 +21,25 @@ so_boot_highstate_unit_file: - onchanges_in: - module: systemd_reload +# Non-managers never apply salt.minion during setup, so reaching this state means +# setup is finished and the marker is safe to write unconditionally. This also +# heals nodes installed before this fix, which have no marker and no legacy +# startup_states line to grep for. Managers do highstate mid-setup, so they only +# get the marker from the legacy upgrade signal; fresh installs get it from +# mark_setup_complete in setup/so-functions. +mark_setup_complete: + file.managed: + - name: /opt/so/state/setup-complete + - replace: false + - makedirs: True +{% if GLOBALS.is_manager %} + - onlyif: "grep -qx 'startup_states: highstate' /etc/salt/minion" +{% endif %} + - require_in: + - service: so_boot_highstate_service + # Only enable once setup is complete. Until then the gate file is missing and -# the unit's own ConditionPathExists would no-op it anyway -- this just keeps -# `systemctl is-enabled` honest for the sync_es_users gate. +# the unit's own ConditionPathExists would no-op it anyway. so_boot_highstate_service: service.enabled: - name: so-boot-highstate.service diff --git a/salt/salt/minion/init.sls b/salt/salt/minion/init.sls index fa94ec7be..a608788c2 100644 --- a/salt/salt/minion/init.sls +++ b/salt/salt/minion/init.sls @@ -87,27 +87,15 @@ set_log_levels: # so-boot-highstate.service (managed in salt.minion.boot_highstate), which # runs once per system boot only. Strip the line from /etc/salt/minion on # upgrade; both the commented and uncommented forms historically existed. +# Ordered after mark_setup_complete (salt.minion.boot_highstate); the manager +# gate there greps for this line, so it must run before we delete it. remove_startup_states: file.line: - name: /etc/salt/minion - match: 'startup_states: highstate' - mode: delete - -# Upgrade-path bridge: systems that already passed setup under the old gate -# (`grep -x 'startup_states: highstate' /etc/salt/minion`) get a /opt/so/state/setup-complete -# marker so so-boot-highstate.service can be enabled and the so-user_sync cron -# in sync_es_users.sls keeps installing. Setup-in-progress systems instead get -# the marker from `mark_setup_complete` in setup/so-functions at the right -# moment. `replace: false` means we never overwrite a marker once written. -mark_setup_complete_for_upgrades: - file.managed: - - name: /opt/so/state/setup-complete - - replace: false - - makedirs: True - - onlyif: "grep -qx 'startup_states: highstate' /etc/salt/minion" - - require_in: - - file: remove_startup_states - - service: so_boot_highstate_service + - require: + - file: mark_setup_complete {% endif %} diff --git a/salt/setup/virt/setSalt.sls b/salt/setup/virt/setSalt.sls index 59ab9e1e3..41496fea5 100644 --- a/salt/setup/virt/setSalt.sls +++ b/salt/setup/virt/setSalt.sls @@ -8,6 +8,15 @@ set_role_grain: - name: role - value: so-{{ grains.id.split("_") | last }} +# salt-cloud guests never run so-setup, so nothing else marks them setup-complete. +# Replaces the 'startup_states: highstate' line this state used to append. No +# GLOBALS import -- this runs before the guest's pillars exist. +mark_setup_complete_vm_guest: + file.managed: + - name: /opt/so/state/setup-complete + - replace: false + - makedirs: True + enable_salt_minion: service.enabled: - name: salt-minion diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index 4bb1b0f8b..c5e14d31b 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -2671,7 +2671,7 @@ soc: # The id (UUIDv4) is pregenerated and can safely be used. # Click "Convert" to convert the Sigma rule to use Security Onion field mappings within an EQL query # - # Rule Creation Guide: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide + # Rule Creation Guide: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-High%E2%80%90Level-Guide # Logsources: https://sigmahq.io/docs/basics/log-sources.html title: 'A Short Capitalized Title With Less Than 50 Characters' @@ -2683,7 +2683,7 @@ soc: references: - 'https://local.invalid' author: '@SecurityOnion' - date: 'YYYY/MM/DD' + date: '[today]' tags: - detection.threat_hunting - attack.technique_id diff --git a/salt/stig/enabled.sls b/salt/stig/enabled.sls index 91aae7069..b53256e04 100644 --- a/salt/stig/enabled.sls +++ b/salt/stig/enabled.sls @@ -65,6 +65,15 @@ run_remediate: - success_retcodes: - 2 +# Elastic Agent docker image chowns its directory to the running UID but does not +# chown the elastic-agent launcher symlink. fs.protected_symlinks=1 then prevents +# non-root users from following that launcher symlink. +{# OSCAP rule id: xccdf_org.ssgproject.content_rule_sysctl_fs_protected_symlinks #} +fs.protected_symlinks: + sysctl.present: + - value: 0 + - config: /etc/sysctl.conf + {# OSCAP rule id: xccdf_org.ssgproject.content_rule_disable_ctrlaltdel_burstaction #} disable_ctrl_alt_del_action: file.replace: diff --git a/salt/stig/files/sos-oscap.xml b/salt/stig/files/sos-oscap.xml index aa5b2ed31..9fc8dacf8 100644 --- a/salt/stig/files/sos-oscap.xml +++ b/salt/stig/files/sos-oscap.xml @@ -1601,7 +1601,7 @@ DISA STIG for Oracle Linux 9 V1R3. - + @@ -2202,7 +2202,7 @@ standard DISA STIG for Oracle Linux 9 profile. - + diff --git a/setup/so-setup b/setup/so-setup index 896505ba5..79fcabfeb 100755 --- a/setup/so-setup +++ b/setup/so-setup @@ -833,6 +833,7 @@ if ! [[ -f $install_opt_file ]]; then check_sos_appliance drop_install_options hypervisor_local_states + mark_setup_complete verify_setup fi