Compare commits

..
43 Commits
Author SHA1 Message Date
Mike ReevesandGitHub 2e141a1ad7 Merge pull request #16119 from Security-Onion-Solutions/TOoSmOotH-patch-3
Add Gemma configuration to defaults.yaml
2026-07-27 12:18:47 -04:00
Mike ReevesandGitHub 4de8f0208f Add Gemma configuration to defaults.yaml 2026-07-27 12:15:21 -04:00
Josh BrowerandGitHub 4aabf7d638 Merge pull request #16118 from Security-Onion-Solutions/playbooks-airgap
Support sigma playbooks for airgap
2026-07-27 16:33:51 +02:00
Josh Brower 812310088e Support sigma playbooks for airgap 2026-07-27 10:30:50 -04:00
Josh PattersonandGitHub 4e17390cfa Merge pull request #16117 from Security-Onion-Solutions/saltthangs
update vm state applid for pillar_push_map
2026-07-27 10:25:00 -04:00
Josh Patterson 57d629683d update vm state applid for pillar_push_map 2026-07-27 10:14:10 -04:00
Jorge ReyesandGitHub 3666b5b0de Merge pull request #16116 from Security-Onion-Solutions/reyesj2-patch-0
ignore all  TransformTask failures for so_kibana user
2026-07-27 07:41:07 -05:00
reyesj2 7f64f143d7 ignore all TransformTask failures for so_kibana user 2026-07-26 19:25:51 -05:00
Mike ReevesandGitHub 162c66a705 Merge pull request #16115 from Security-Onion-Solutions/reyesj2-patch-0
add sslverify=0 to minion repo config. Updates are pulled from the ma…
2026-07-24 19:06:28 -04:00
reyesj2 14d11cc180 add sslverify=0 to minion repo config. Updates are pulled from the manager hosted repo 2026-07-24 16:59:42 -05:00
Josh BrowerandGitHub 112fcf7804 Merge pull request #16114 from Security-Onion-Solutions/fix/sigmapipelines
Add already_running mapping
2026-07-24 23:40:45 +02:00
Josh Brower 120b426a79 Add already_running mapping 2026-07-24 17:23:00 -04:00
Jorge ReyesandGitHub 72fd754a92 Merge pull request #16113 from Security-Onion-Solutions/reyesj2-pach-1
run es-url-updae prior to upgrade
2026-07-24 14:17:17 -05:00
reyesj2 d5fddafa6a run es-url-updae prior to upgrade 2026-07-24 13:33:48 -05:00
Jorge ReyesandGitHub 0c83d4e1fe Merge pull request #16112 from Security-Onion-Solutions/revert-16052-reyesj2-patch-2
Revert "elastic fleet server persistence"
2026-07-24 13:23:10 -05:00
Jorge ReyesandGitHub baca444a7e Revert "elastic fleet server persistence" 2026-07-24 13:18:48 -05:00
Josh PattersonandGitHub 4f5af93b38 Merge pull request #16111 from Security-Onion-Solutions/saltthangs
Express the highstate schedule in minutes instead of hours
2026-07-24 12:54:25 -04:00
Josh Patterson b109ca4e9b Express the highstate schedule in minutes instead of hours
salt.schedule.highstate_interval_hours could not express a sub-hour
cadence, so an operator who disables salt.auto_apply had no way back to
the legacy 15-minute highstate. Rename the setting to
highstate_interval_minutes (default 120, behavior unchanged) and enforce
a 15-minute floor in SOC.

Non-manager splay is now a quarter of the interval clamped to [5, 30]
minutes, so a short interval no longer gets jitter larger than itself; at
the 120-minute default it stays 1800s. The so-salt-minion-check restart
threshold keeps its interval-plus-one-hour grace, now in minute math.
2026-07-24 12:34:35 -04:00
Josh PattersonandGitHub e5969a12aa Merge pull request #16109 from Security-Onion-Solutions/fixESHEAP
Add default esheap value so nodes highstate without the pillar value
2026-07-24 09:04:58 -04:00
Jorge ReyesandGitHub 4d97b562eb Merge pull request #16106 from Security-Onion-Solutions/reyesj2-patch-9
call highstate in soup with a retry if first fails vs bailing
2026-07-23 15:50:11 -05:00
reyesj2 f2d81cea3f call highstate in soup with a retry if first fails vs bailing 2026-07-23 15:06:36 -05:00
Josh Patterson 6bd3c414bb Add default esheap value so nodes highstate without the pillar value
Previously salt/vars/elasticsearch.map.jinja read the elasticsearch heap
size via a raw pillar access (INIT.PILLAR.elasticsearch.esheap). If the
esheap key was missing from a minion pillar, building GLOBALS raised
'dict object has no attribute esheap', cascading into every state that
imports GLOBALS (elasticsearch.enabled, logstash.config, telegraf.config,
etc.) and blocking highstate entirely.

Add an esheap default ('600m', matching the es_heapsize() floor) to
elasticsearch/defaults.yaml and fall back to it via dict .get() so a
missing pillar value degrades gracefully. The per-node pillar value still
wins when present.
2026-07-23 15:16:26 -04:00
Josh BrowerandGitHub 16f958dac0 Merge pull request #16103 from Security-Onion-Solutions/defend-mapping
Map av to both defender and defend
2026-07-23 20:45:53 +02:00
Mike ReevesandGitHub a57ff5f89e Merge pull request #16104 from Security-Onion-Solutions/TOoSmOotH-patch-2
Remove icsnpp-modbus from defaults.yaml
2026-07-23 14:44:14 -04:00
Mike ReevesandGitHub e2513daddc Remove icsnpp-modbus from defaults.yaml
Removed 'icsnpp-modbus' from the list of modules.
2026-07-23 14:39:19 -04:00
Josh Brower 01a873b2d9 Map av to both defender and defend 2026-07-23 13:39:19 -04:00
Jorge ReyesandGitHub 19957d9530 Merge pull request #16099 from Security-Onion-Solutions/reyesj2-patch-7
template updates
2026-07-22 17:40:25 -05:00
Josh PattersonandGitHub 4871098278 Merge pull request #16097 from Security-Onion-Solutions/saltthangs
apply salt.master state instead of just salt.minion during soup
2026-07-22 12:13:59 -04:00
Mike ReevesandGitHub 7b32c73da8 Merge pull request #16096 from Security-Onion-Solutions/rename/detectionengineer-key
Rename Detection Engineer agent key to DetectionEngineer
2026-07-22 12:04:17 -04:00
Mike Reeves 334978ad92 Rename Detection Engineer agent key to DetectionEngineer
The agent name is also the agentMapping config key and a SOC setting id
segment, so the space made it awkward to target. Matches the rename in
securityonion-soc.
2026-07-22 11:55:12 -04:00
Josh Patterson a21186ccce Merge remote-tracking branch 'origin/3/dev' into saltthangs 2026-07-21 16:29:37 -04:00
Josh Patterson f77fad8087 apply salt.master state instead of just salt.minion 2026-07-21 16:29:24 -04:00
Josh BrowerandGitHub 963e475d1a Merge pull request #16087 from Security-Onion-Solutions/sigma-pipeline
Broader support for SigmaHQ rules + Elastic Defend logs
2026-07-21 22:05:19 +02:00
Josh Patterson ac46636196 ensure salt-master service restarted last 2026-07-21 16:05:18 -04:00
Mike ReevesandGitHub f3d8bae13d Merge pull request #16093 from Security-Onion-Solutions/fix/agentic-adapter-resolution
Map default agents to model displayName, not id
2026-07-21 15:13:00 -04:00
Mike Reeves 894d323323 Map default agents to model displayName, not id
agentMapping values are model displayNames (the canonical selector); the
stock config used the model id, which only resolved via the legacy
id@adapter fallback. Use the Claude Sonnet displayName so agent-to-model
resolution matches the documented contract.
2026-07-21 15:08:24 -04:00
Jason ErtelandGitHub 26eb8c3c18 Merge pull request #16089 from Security-Onion-Solutions/jertel/wip
postgress updates
2026-07-21 12:01:41 -04:00
Jason Ertel 4e1935f8a0 postgress updates 2026-07-21 11:58:11 -04:00
Josh Brower c4c1464b2a Better support SigmaHQ rules 2026-07-21 10:07:33 -04:00
Mike ReevesandGitHub 89f4950521 Merge pull request #16084 from Security-Onion-Solutions/postgres-log-password-scrub
PostgreSQL: prevent password log leak + self-heal SOC database bootstrap
2026-07-20 15:33:05 -04:00
Mike Reeves 387781c629 Trim verbose comments in postgres provisioning changes 2026-07-20 15:19:02 -04:00
Mike Reeves 011749ad09 Self-heal PostgreSQL SOC database bootstrap
init-db.sh only runs on a fresh PGDATA (docker-entrypoint-initdb.d), so a
cluster left partially initialized -- e.g. the container restarted mid first-init
by a watch trigger -- never recovered: the so_postgres role existed but its
schema grants and the so_telegraf database were missing, breaking SOC with
"permission denied for schema public".

- init-db.sh: make the role upsert race-safe. Try CREATE ROLE and fall back to
  ALTER on duplicate_object/unique_violation instead of IF NOT EXISTS, so a
  concurrent creator no longer aborts the script (set -e + ON_ERROR_STOP=1)
  before the grants and so_telegraf creation run. The whole script is now
  idempotent and safe to re-run.
- postgres/enabled.sls: move postgres_wait_ready here (was telegraf-gated in
  telegraf_users.sls) and add postgres_bootstrap_soc_db, which re-runs init-db.sh
  every highstate so a partially-initialized cluster self-heals.
- telegraf_users.sls: drop its now-duplicate postgres_wait_ready definition; it
  resolves from postgres.enabled via the existing include.
- soup: remove bootstrap_so_soc_database and its post_to_3.2.0 call. The
  highstates soup runs before postupgrade now reconcile the SOC DB, making the
  one-shot bootstrap redundant.
2026-07-20 15:04:58 -04:00
Mike Reeves ad78e84ccd Prevent PostgreSQL from leaking plaintext passwords to postgres.log
PostgreSQL's log_min_error_statement defaults to 'error', so whenever a
CREATE/ALTER ROLE ... PASSWORD statement errored, the full statement text --
including the plaintext password -- was written to /opt/so/log/postgres/postgres.log.
The role-provisioning paths (init-db.sh for so_postgres, so-telegraf-postgres
for per-minion telegraf roles) both dispatch such DDL, the latter on every
state.apply.

- init-db.sh / so-telegraf-postgres: SET log_min_error_statement = panic before
  the password-bearing DDL so an error no longer emits the STATEMENT line. The
  ERROR message itself (no password) still logs, preserving debuggability.
- logrotate: add a postgres stanza (daily, keep 14, copytruncate, compress) so
  postgres.log is rotated like every other service and leaked content can't
  persist indefinitely. copytruncate is required because the container holds the
  log open via redirected stderr.
- soup: scrub any already-logged PASSWORD lines from postgres.log during
  post_to_3.2.0, rewriting in place to preserve the inode postgres is writing to.
2026-07-20 12:41:14 -04:00
27 changed files with 511 additions and 91 deletions
+2 -1
View File
@@ -133,6 +133,7 @@ if [[ $EXCLUDE_STARTUP_ERRORS == 'Y' ]]; then
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Cancelling deferred write event maybeFenceReplicas because the event queue is now closed" # Kafka controller log during shutdown/restart EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Cancelling deferred write event maybeFenceReplicas because the event queue is now closed" # Kafka controller log during shutdown/restart
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Redis may have been restarted" # Redis likely restarted by salt EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Redis may have been restarted" # Redis likely restarted by salt
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|file already closed" # Go logging race condition during container restart EXCLUDED_ERRORS="$EXCLUDED_ERRORS|file already closed" # Go logging race condition during container restart
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|relation \"audit_settings\" does not exist" # salt checking for changes before SOC starts
fi fi
if [[ $EXCLUDE_FALSE_POSITIVE_ERRORS == 'Y' ]]; then if [[ $EXCLUDE_FALSE_POSITIVE_ERRORS == 'Y' ]]; then
@@ -230,7 +231,7 @@ if [[ $EXCLUDE_KNOWN_ERRORS == 'Y' ]]; then
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|from NIC checksum offloading" # zeek reporter.log EXCLUDED_ERRORS="$EXCLUDED_ERRORS|from NIC checksum offloading" # zeek reporter.log
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|marked for removal" # docker container getting recycled EXCLUDED_ERRORS="$EXCLUDED_ERRORS|marked for removal" # docker container getting recycled
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|tcp 127.0.0.1:6791: bind: address already in use" # so-elastic-fleet agent restarting. Seen starting w/ 8.18.8 https://github.com/elastic/kibana/issues/201459 EXCLUDED_ERRORS="$EXCLUDED_ERRORS|tcp 127.0.0.1:6791: bind: address already in use" # so-elastic-fleet agent restarting. Seen starting w/ 8.18.8 https://github.com/elastic/kibana/issues/201459
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|TransformTask\] \[logs-(tychon|aws_billing|microsoft_defender_endpoint|armis|o365_metrics|microsoft_sentinel|snyk|cyera|island_browser).*user so_kibana lacks the required permissions \[(logs|metrics)-\1" # Known issue with integrations starting transform jobs that are explicitly not allowed to start as a system user. This error should not be seen on fresh ES 9.3.3 installs or after SO 3.1.0 with soups addition of check_transform_health_and_reauthorize() EXCLUDED_ERRORS="$EXCLUDED_ERRORS|TransformTask\] \[logs-.*user so_kibana lacks the required permissions" # Known issue with integrations starting transform jobs that are explicitly not allowed to start as a system user
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|manifest unknown" # appears in so-dockerregistry log for so-tcpreplay following docker upgrade to 29.2.1-1 EXCLUDED_ERRORS="$EXCLUDED_ERRORS|manifest unknown" # appears in so-dockerregistry log for so-tcpreplay following docker upgrade to 29.2.1-1
fi fi
-3
View File
@@ -35,9 +35,6 @@ case $1 in
"elastic-fleet"|"elasticfleet") "elastic-fleet"|"elasticfleet")
docker_check_running "elastic-fleet" "--stop" docker_check_running "elastic-fleet" "--stop"
docker rm "so-elastic-fleet" 2> /dev/null 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 salt-call state.apply elasticfleet queue=True
;; ;;
*) *)
-2
View File
@@ -29,8 +29,6 @@ case $1 in
"elasticfleet"|"elastic-fleet") "elasticfleet"|"elastic-fleet")
docker_check_running "elastic-fleet" "--stop" docker_check_running "elastic-fleet" "--stop"
docker rm "so-elastic-fleet" 2> /dev/null 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_check_running "$1" "--stop"
@@ -25,7 +25,7 @@ LAST_HIGHSTATE_END=$([ -e "/opt/so/log/salt/lasthighstate" ] && date -r /opt/so/
LAST_HEALTHCHECK_STATE_APPLY=$([ -e "/opt/so/log/salt/state-apply-test" ] && date -r /opt/so/log/salt/state-apply-test +%s || echo 0) LAST_HEALTHCHECK_STATE_APPLY=$([ -e "/opt/so/log/salt/state-apply-test" ] && date -r /opt/so/log/salt/state-apply-test +%s || echo 0)
# SETTING THRESHOLD TO ANYTHING UNDER 600 seconds may cause a lot of salt-minion restarts since the job to touch the file occurs every 5-8 minutes by default # SETTING THRESHOLD TO ANYTHING UNDER 600 seconds may cause a lot of salt-minion restarts since the job to touch the file occurs every 5-8 minutes by default
# THRESHOLD is derived from the salt schedule highstate interval + 1 hour, so the minion-check grace period tracks the schedule automatically. # THRESHOLD is derived from the salt schedule highstate interval + 1 hour, so the minion-check grace period tracks the schedule automatically.
THRESHOLD=$(( ({{ SCHEDULEMERGED.highstate_interval_hours }} + 1) * 3600 )) #within how many seconds the file /opt/so/log/salt/state-apply-test must have been touched/modified before the salt minion is restarted THRESHOLD=$(( ({{ SCHEDULEMERGED.highstate_interval_minutes }} + 60) * 60 )) #within how many seconds the file /opt/so/log/salt/state-apply-test must have been touched/modified before the salt minion is restarted
THRESHOLD_DATE=$((LAST_HEALTHCHECK_STATE_APPLY+THRESHOLD)) THRESHOLD_DATE=$((LAST_HEALTHCHECK_STATE_APPLY+THRESHOLD))
logCmd() { logCmd() {
-10
View File
@@ -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
@@ -70,7 +66,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 %}
@@ -78,7 +73,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
@@ -88,9 +82,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 %}
@@ -109,7 +100,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
+1
View File
@@ -1,5 +1,6 @@
elasticsearch: elasticsearch:
enabled: false enabled: false
esheap: '600m'
version: 9.3.7 version: 9.3.7
index_clean: true index_clean: true
data_retention_method: DLM data_retention_method: DLM
+10
View File
@@ -150,6 +150,16 @@ logrotate:
- extension .log - extension .log
- dateext - dateext
- dateyesterday - dateyesterday
/opt/so/log/postgres/*_x_log:
- daily
- rotate 14
- missingok
- copytruncate
- compress
- create
- extension .log
- dateext
- dateyesterday
/opt/so/log/telegraf/*_x_log: /opt/so/log/telegraf/*_x_log:
- daily - daily
- rotate 14 - rotate 14
+7
View File
@@ -91,6 +91,13 @@ logrotate:
multiline: True multiline: True
global: True global: True
forcedType: "[]string" forcedType: "[]string"
"/opt/so/log/postgres/*_x_log":
description: List of logrotate options for this file.
title: /opt/so/log/postgres/*.log
advanced: True
multiline: True
global: True
forcedType: "[]string"
"/opt/so/log/telegraf/*_x_log": "/opt/so/log/telegraf/*_x_log":
description: List of logrotate options for this file. description: List of logrotate options for this file.
title: /opt/so/log/telegraf/*.log title: /opt/so/log/telegraf/*.log
+29 -26
View File
@@ -442,7 +442,13 @@ get_soup_script_hashes() {
} }
highstate() { highstate() {
# Run a highstate. # Run a highstate with a retry attempt.
if salt-call state.highstate -l info queue=True; then
return 0
fi
echo "Initial highstate attempt had a problem; retrying in 30 seconds."
sleep 30
salt-call state.highstate -l info queue=True salt-call state.highstate -l info queue=True
} }
@@ -880,29 +886,21 @@ recollate_postgres() {
echo "" echo ""
} }
bootstrap_so_soc_database() { scrub_postgres_log_passwords() {
# init-db.sh is mounted into so-postgres at /docker-entrypoint-initdb.d/init-db.sh # Purge plaintext passwords a pre-3.2 postgres could log on DDL errors.
# and runs automatically only on a fresh data directory. Hosts upgrading from local log=/opt/so/log/postgres/postgres.log
# 3.1.0 already have /nsm/postgres populated, so the so_soc bootstrap block [[ -f "$log" ]] || return 0
# added in 3.2 never fires. Re-run the script explicitly; it's idempotent. if ! grep -qai "PASSWORD '" "$log" 2>/dev/null; then
echo "Bootstrapping database via init-db.sh." echo "No leaked passwords found in $log."
# The postgres image has no USER directive, so `docker exec` defaults to
# root, and the container env intentionally omits POSTGRES_USER (the upstream
# entrypoint defaults it transiently during first-init only). Recreate both
# so psql inside init-db.sh resolves the connect user correctly.
local exec_cmd="docker exec -u postgres -e POSTGRES_USER=postgres so-postgres bash /docker-entrypoint-initdb.d/init-db.sh"
if ! /usr/sbin/so-postgres-wait; then
FINAL_MESSAGE_QUEUE+=("WARNING: so-postgres was not ready during the 3.2.0 upgrade; the so_soc database may not have been bootstrapped. Re-run manually: $exec_cmd")
return 0 return 0
fi fi
if ! $exec_cmd; then echo "Removing leaked password statements from $log."
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") local tmp
return 0 tmp=$(mktemp)
fi # Rewrite in place (cat >) to keep the inode postgres is writing to.
echo "Database bootstrap complete." grep -avi "PASSWORD '" "$log" > "$tmp" 2>/dev/null || true
cat "$tmp" > "$log"
echo "Restarting so-soc container to pick up database changes" rm -f "$tmp"
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.
@@ -982,6 +980,10 @@ up_to_3.2.0() {
pin_elasticsearch_data_retention_method pin_elasticsearch_data_retention_method
# Run so-elastic-fleet-es-url update with --force to ensure eval/import have
# configured so-manager_elasicsearch as the default output for both monitoring and logs
/usr/sbin/so-elastic-fleet-es-url-update --force
INSTALLEDVERSION=3.2.0 INSTALLEDVERSION=3.2.0
} }
@@ -989,7 +991,8 @@ post_to_3.2.0() {
# Recollate due to image OS rebase # Recollate due to image OS rebase
recollate_postgres recollate_postgres
bootstrap_so_soc_database # SOC database bootstrap is handled by the postgres.enabled highstate.
scrub_postgres_log_passwords
# Generate 9.3.7 elastic agent installers # Generate 9.3.7 elastic agent installers
echo "Regenerating Elastic Agent Installers" echo "Regenerating Elastic Agent Installers"
@@ -2005,11 +2008,11 @@ main() {
# Testing that salt-master is up by checking that is it connected to itself # Testing that salt-master is up by checking that is it connected to itself
check_saltmaster_status check_saltmaster_status
# update the salt-minion configs here and start the minion # update the salt-master and salt-minion configs here and start the minion
# since highstate are disabled above, minion start should not trigger a highstate # since highstate are disabled above, minion start should not trigger a highstate
echo "" echo ""
echo "Ensuring salt-minion configs are up-to-date." echo "Ensuring salt-master and salt-minion configs are up-to-date."
salt-call state.apply salt.minion -l info queue=True salt-call state.apply salt.master -l info queue=True
echo "" echo ""
# ensure the mine is updated and populated before highstates run, following the salt-master restart # ensure the mine is updated and populated before highstates run, following the salt-master restart
+17
View File
@@ -85,6 +85,23 @@ so-postgres:
- x509: postgres_crt - x509: postgres_crt
- x509: postgres_key - x509: postgres_key
postgres_wait_ready:
cmd.run:
- name: /usr/sbin/so-postgres-wait
- require:
- docker_container: so-postgres
- file: postgres_sbin
# Reconcile the SOC database (role, grants, so_telegraf) every highstate so a
# partially-initialized cluster self-heals. POSTGRES_USER is injected because
# the container env omits it.
postgres_bootstrap_soc_db:
cmd.run:
- name: docker exec -u postgres -e POSTGRES_USER=postgres so-postgres bash /docker-entrypoint-initdb.d/init-db.sh
- require:
- cmd: postgres_wait_ready
- file: postgresinitdb
delete_so-postgres_so-status.disabled: delete_so-postgres_so-status.disabled:
file.uncomment: file.uncomment:
- name: /opt/so/conf/so-status/so-status.conf - name: /opt/so/conf/so-status/so-status.conf
+8 -4
View File
@@ -8,13 +8,17 @@ if [ -z "${SO_POSTGRES_PASS:-}" ] && [ -n "${SO_POSTGRES_PASS_FILE:-}" ] && [ -r
SO_POSTGRES_PASS="$(< "$SO_POSTGRES_PASS_FILE")" SO_POSTGRES_PASS="$(< "$SO_POSTGRES_PASS_FILE")"
fi fi
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
-- Keep the password out of postgres.log if this DDL errors.
SET log_min_error_statement = panic;
-- Idempotent, race-safe upsert: CREATE, falling back to ALTER if the role
-- already exists or is created concurrently.
DO \$\$ DO \$\$
BEGIN BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '${SO_POSTGRES_USER}') THEN BEGIN
EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', '${SO_POSTGRES_USER}', '${SO_POSTGRES_PASS}'); EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', '${SO_POSTGRES_USER}', '${SO_POSTGRES_PASS}');
ELSE EXCEPTION WHEN duplicate_object OR unique_violation THEN
EXECUTE format('ALTER ROLE %I WITH PASSWORD %L', '${SO_POSTGRES_USER}', '${SO_POSTGRES_PASS}'); EXECUTE format('ALTER ROLE %I WITH LOGIN PASSWORD %L', '${SO_POSTGRES_USER}', '${SO_POSTGRES_PASS}');
END IF; END;
END END
\$\$; \$\$;
GRANT ALL ON SCHEMA public TO "$SO_POSTGRES_USER"; GRANT ALL ON SCHEMA public TO "$SO_POSTGRES_USER";
+2 -11
View File
@@ -8,23 +8,14 @@
{% from 'vars/globals.map.jinja' import GLOBALS %} {% from 'vars/globals.map.jinja' import GLOBALS %}
{% from 'telegraf/map.jinja' import TELEGRAFMERGED %} {% from 'telegraf/map.jinja' import TELEGRAFMERGED %}
{# postgres_wait_ready below requires `docker_container: so-postgres`, which is {# postgres.enabled declares the so-postgres container and postgres_wait_ready
declared in postgres.enabled. Include it here so state.apply postgres.telegraf_users that the requires below reference. Salt de-duplicates the circular include. #}
on its own (e.g. from orch.deploy_newnode) still has that ID in scope. Salt
de-duplicates the circular include. #}
include: include:
- postgres.enabled - postgres.enabled
{% set TG_OUT = TELEGRAFMERGED.output | upper %} {% set TG_OUT = TELEGRAFMERGED.output | upper %}
{% if TG_OUT in ['POSTGRES', 'BOTH'] %} {% if TG_OUT in ['POSTGRES', 'BOTH'] %}
postgres_wait_ready:
cmd.run:
- name: /usr/sbin/so-postgres-wait
- require:
- docker_container: so-postgres
- file: postgres_sbin
# Ensure the shared Telegraf database exists. init-db.sh only runs on a # Ensure the shared Telegraf database exists. init-db.sh only runs on a
# fresh data dir, so hosts upgraded onto an existing /nsm/postgres volume # fresh data dir, so hosts upgraded onto an existing /nsm/postgres volume
# would otherwise never get so_telegraf. # would otherwise never get so_telegraf.
@@ -71,6 +71,8 @@ EOSQL
-v role_user="$ROLE_USER" \ -v role_user="$ROLE_USER" \
-v role_pass="$ROLE_PASS" \ -v role_pass="$ROLE_PASS" \
-U postgres -d so_telegraf <<'EOSQL' -U postgres -d so_telegraf <<'EOSQL'
-- Keep the password out of postgres.log if this DDL errors.
SET log_min_error_statement = panic;
SELECT format( SELECT format(
CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = :'role_user') CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = :'role_user')
THEN 'ALTER ROLE %I WITH LOGIN PASSWORD %L' THEN 'ALTER ROLE %I WITH LOGIN PASSWORD %L'
+1 -1
View File
@@ -242,7 +242,7 @@ versionlock:
# grain (compound supports nested grain matching via G@<key>:<subkey>:<value>). # grain (compound supports nested grain matching via G@<key>:<subkey>:<value>).
# pillar/vm/soc_vm.sls write path is referenced at salt/_runners/setup_hypervisor.py:856. # pillar/vm/soc_vm.sls write path is referenced at salt/_runners/setup_hypervisor.py:856.
vm: vm:
- state: vm - state: vm.user
tgt: 'G@salt-cloud:driver:libvirt' tgt: 'G@salt-cloud:driver:libvirt'
# zeek: sensor_roles + so-import (5 roles). # zeek: sensor_roles + so-import (5 roles).
+6
View File
@@ -60,6 +60,9 @@ so_repo:
{% endif %} {% endif %}
- enabled: 1 - enabled: 1
- gpgcheck: 1 - gpgcheck: 1
{% if not GLOBALS.is_manager %}
- sslverify: 0
{% endif %}
# Only assign the kernel repo once this node's running salt matches the version this # Only assign the kernel repo once this node's running salt matches the version this
# SO release ships. During a soup the grid is mid-salt-upgrade; gating here keeps the # SO release ships. During a soup the grid is mid-salt-upgrade; gating here keeps the
@@ -77,6 +80,9 @@ so_kernel_repo:
{% endif %} {% endif %}
- enabled: 1 - enabled: 1
- gpgcheck: 1 - gpgcheck: 1
{% if not GLOBALS.is_manager %}
- sslverify: 0
{% endif %}
# Supplementary kernel repo: tolerate it being empty/unreachable (e.g. before the # Supplementary kernel repo: tolerate it being empty/unreachable (e.g. before the
# manager has populated /nsm/kernelrepo) so a missing repomd.xml can't make every # manager has populated /nsm/kernelrepo) so a missing repomd.xml can't make every
# dnf/pkg operation on the grid fail. # dnf/pkg operation on the grid fail.
+1 -1
View File
@@ -6,4 +6,4 @@ salt:
batch: '25%' batch: '25%'
batch_wait: 15 batch_wait: 15
schedule: schedule:
highstate_interval_hours: 2 highstate_interval_minutes: 120
+6 -2
View File
@@ -1,11 +1,15 @@
{% from 'vars/globals.map.jinja' import GLOBALS %} {% from 'vars/globals.map.jinja' import GLOBALS %}
{% from 'salt/schedule.map.jinja' import SCHEDULEMERGED %} {% from 'salt/schedule.map.jinja' import SCHEDULEMERGED %}
{# splay a quarter of the interval, clamped to [5 min, 30 min], so short intervals
don't get jitter larger than the interval itself #}
{% set SPLAY = [[(SCHEDULEMERGED.highstate_interval_minutes * 60 // 4) | int, 300] | max, 1800] | min %}
highstate_schedule: highstate_schedule:
schedule.present: schedule.present:
- function: state.highstate - function: state.highstate
- hours: {{ SCHEDULEMERGED.highstate_interval_hours }} - minutes: {{ SCHEDULEMERGED.highstate_interval_minutes }}
- maxrunning: 1 - maxrunning: 1
{% if not GLOBALS.is_manager %} {% if not GLOBALS.is_manager %}
- splay: 1800 - splay: {{ SPLAY }}
{% endif %} {% endif %}
+3 -1
View File
@@ -94,7 +94,9 @@ salt_master_service:
- file: checkmine_engine - file: checkmine_engine
- file: pillarWatch_engine - file: pillarWatch_engine
- file: engines_config - file: engines_config
- order: 9002 - require:
- cmd: wait_for_salt_minion_ready
- order: last
{% else %} {% else %}
+4 -2
View File
@@ -31,9 +31,11 @@ salt:
global: True global: True
advanced: True advanced: True
schedule: schedule:
highstate_interval_hours: highstate_interval_minutes:
description: How often every minion in the grid runs a scheduled state.highstate, in hours. Lower values keep minions closer in sync at the cost of more load; higher values reduce load but increase worst-case latency for non-pushed changes. The salt-minion health check restarts a minion if its last highstate is older than this value plus one hour. description: How often every minion in the grid runs a scheduled state.highstate, in minutes. Minimum 15 minutes. Lower values keep minions closer in sync at the cost of more load; higher values reduce load but increase worst-case latency for non-pushed changes. If Auto Apply is disabled, set this to the 15-minute minimum so changes are still picked up promptly. The salt-minion health check restarts a minion if its last state apply is older than this value plus one hour.
forcedType: int forcedType: int
helpLink: push helpLink: push
global: True global: True
advanced: True advanced: True
regex: '^(1[5-9]|[2-9][0-9]|[1-9][0-9]{2,4})$'
regexFailureMessage: The value must be an integer of at least 15 minutes (maximum 99999).
+18 -4
View File
@@ -1512,6 +1512,10 @@ soc:
rulesetName: sos-resources-ag rulesetName: sos-resources-ag
branch: main branch: main
folder: securityonion-normalized folder: securityonion-normalized
- repo: file:///nsm/airgap-resources/playbooks/securityonion-resources-playbooks
rulesetName: sos-published-ag
branch: published
folder: sigma
assistant: assistant:
systemPromptAddendum: "" systemPromptAddendum: ""
systemPromptAddendumMaxLength: 50000 systemPromptAddendumMaxLength: 50000
@@ -1530,16 +1534,16 @@ soc:
healthTimeoutSeconds: 5 healthTimeoutSeconds: 5
agentic: false agentic: false
agentMapping: agentMapping:
Orchestrator: sonnet Orchestrator: Claude Sonnet
Investigator: sonnet Investigator: Claude Sonnet
Detection Engineer: sonnet DetectionEngineer: Claude Sonnet
onionconfig: onionconfig:
saltstackDir: /opt/so/saltstack saltstackDir: /opt/so/saltstack
bypassEnabled: false bypassEnabled: false
postgres: postgres:
host: "" host: ""
port: 5432 port: 5432
sslMode: "allow" sslMode: "require"
database: securityonion database: securityonion
user: "" user: ""
password: "" password: ""
@@ -2714,4 +2718,14 @@ soc:
enabled: true enabled: true
adapter: SOAI adapter: SOAI
charsPerTokenEstimate: 4 charsPerTokenEstimate: 4
- id: gemma
displayName: Gemma
origin: USA
contextLimitSmall: 256000
contextLimitLarge: 256000
lowBalanceColorAlert: 500000
enabled: true
adapter: SOAI
charsPerTokenEstimate: 4
@@ -14,18 +14,22 @@
CommandLine: process.command_line CommandLine: process.command_line
CurrentDirectory: process.working_directory CurrentDirectory: process.working_directory
Details: registry.data.strings
Image: process.executable Image: process.executable
ImageLoaded: dll.name ImageLoaded: dll.path
ParentImage: process.parent.executable ParentImage: process.parent.executable
ParentName: process.parent.name ParentName: process.parent.name
ParentProcessGuid: process.parent.entity_id ParentProcessGuid: process.parent.entity_id
ProcessGuid: process.entity_id ProcessGuid: process.entity_id
TargetFilename: file.name TargetFilename: file.path
TargetObject: registry.path TargetObject: registry.path
TargetUserName: user.target.name TargetUserName: user.target.name
User: user.name User: user.name
community_id: network.community_id community_id: network.community_id
dns_resolved_ip: dns.resolved_ip dns_resolved_ip: dns.resolved_ip
QueryName: dns.question.name
dll_basename: dll.name
file_basename: file.name
document_id: soc_id document_id: soc_id
dst_ip: destination.ip dst_ip: destination.ip
dst_port: destination.port dst_port: destination.port
@@ -40,10 +44,15 @@ public_ip: network.public_ip
related_hosts: related.hosts related_hosts: related.hosts
related_ip: related.ip related_ip: related.ip
src_ip: source.ip src_ip: source.ip
dns_query_name: dns.query_name dns_query_name: dns.question.name
flow_id: log.id.uid flow_id: log.id.uid
payload: network.data.decoded payload: network.data.decoded
rule_category: rule.category rule_category: rule.category
rule_name: rule.name rule_name: rule.name
rule_uuid: rule.uuid rule_uuid: rule.uuid
src_port: source.port src_port: source.port
# PowerShell channel (ps_script EID 4104 / classic EID 400)
ScriptBlockText: powershell.file.script_block_text
script_block_id: powershell.file.script_block_id
script_block_hash: powershell.file.script_block_hash
EngineVersion: powershell.engine.version
+145 -2
View File
@@ -1,8 +1,9 @@
name: Security Onion - Playbook Pipeline name: Security Onion - Playbook Pipeline
priority: 97 priority: 97
transformations: transformations:
# Route string fields to their lowercase-normalized .caseless subfield so wildcard # Route to lowercase-normalized .caseless subfields for case-insensitive matching.
# matches are case-insensitive. # file.path.caseless exists on Defend only (Sysmon file events lack it);
# registry.path / dll.path / file.name have no .caseless on any source.
- id: case_insensitive_string_fields - id: case_insensitive_string_fields
type: field_name_mapping type: field_name_mapping
mapping: mapping:
@@ -10,3 +11,145 @@ transformations:
process.parent.executable: process.parent.executable.caseless process.parent.executable: process.parent.executable.caseless
process.command_line: process.command_line.caseless process.command_line: process.command_line.caseless
process.parent.command_line: process.parent.command_line.caseless process.parent.command_line: process.parent.command_line.caseless
file.path: file.path.caseless
# entity_id pivots must also match processes that were already running when the
# agent started: Defend emits already_running (event.type:info), not start.
# Kept out of Playbook sigma query because Sysmon has no equivalent concept.
# contains_field is exact, so child pivots (process.parent.entity_id) stay
# start-only. Drop must precede add;
- id: playbook_process_lifecycle_drop_start_scope
type: drop_detection_item
field_name_conditions:
- type: include_fields
fields: ['event.type']
rule_conditions:
- type: logsource
category: process_creation
- type: contains_field
field: process.entity_id
- id: playbook_process_lifecycle_add-fields
type: add_condition
conditions:
event.type: ['start', 'info']
rule_conditions:
- type: logsource
category: process_creation
- type: contains_field
field: process.entity_id
# file_activity: playbook-only pseudo-category spanning all file operations.
- id: playbook_file_activity_add-fields
type: add_condition
conditions:
event.category: 'file'
rule_conditions:
- type: logsource
category: file_activity
# EventType -> event.action is safe as a projection only — the values differ
# (Sysmon DeleteFile/SetValue vs ECS deletion/modification).
- id: playbook_file_activity_event_type
type: field_name_mapping
mapping:
winlog.event_data.EventType: event.action
rule_conditions:
- type: logsource
category: file_activity
- id: playbook_file_event_event_type
type: field_name_mapping
mapping:
winlog.event_data.EventType: event.action
rule_conditions:
- type: logsource
category: file_event
- id: playbook_file_delete_event_type
type: field_name_mapping
mapping:
winlog.event_data.EventType: event.action
rule_conditions:
- type: logsource
category: file_delete
- id: playbook_file_rename_event_type
type: field_name_mapping
mapping:
winlog.event_data.EventType: event.action
rule_conditions:
- type: logsource
category: file_rename
- id: playbook_registry_set_event_type
type: field_name_mapping
mapping:
winlog.event_data.EventType: event.action
rule_conditions:
- type: logsource
category: registry_set
# WMI-Activity events carry no event.category; event.dataset is source-specific
- id: playbook_wmi_activity_add-fields
type: add_condition
conditions:
event.dataset: 'wmi.operational'
rule_conditions:
- type: logsource
category: wmi
- id: playbook_wmi_event_activity_add-fields
type: add_condition
conditions:
event.dataset: 'wmi.operational'
rule_conditions:
- type: logsource
category: wmi_event
- id: playbook_ps_script_field-mapping
type: field_name_mapping
mapping:
ScriptBlockText: powershell.file.script_block_text
Path: file.path
rule_conditions:
- type: logsource
category: ps_script
- id: playbook_ps_script_add-fields
type: add_condition
conditions:
event.dataset: 'windows.powershell_operational'
event.code: '4104'
rule_conditions:
- type: logsource
category: ps_script
# Data (raw EID 400/600 message blob) -> process.command_line: the winlog
# integration parses HostApplication into it; `message` is analyzed text and
# wildcard matches on it silently return zero. No .caseless on this dataset.
- id: playbook_ps_classic_start_field-mapping
type: field_name_mapping
mapping:
Data: process.command_line
rule_conditions:
- type: logsource
category: ps_classic_start
- id: playbook_ps_classic_start_add-fields
type: add_condition
conditions:
event.dataset: 'windows.powershell'
event.code: '400'
rule_conditions:
- type: logsource
category: ps_classic_start
- id: playbook_ps_classic_provider_start_field-mapping
type: field_name_mapping
mapping:
Data: process.command_line
rule_conditions:
- type: logsource
category: ps_classic_provider_start
- id: playbook_ps_classic_provider_start_add-fields
type: add_condition
conditions:
event.dataset: 'windows.powershell'
event.code: '600'
rule_conditions:
- type: logsource
category: ps_classic_provider_start
# Alert docs nest the host under event_data.host.name; no top-level host.name.
- id: playbook_alert_host_field
type: field_name_mapping
mapping:
host.name: event_data.host.name
rule_conditions:
- type: logsource
category: alert
+229 -10
View File
@@ -46,31 +46,47 @@ transformations:
- type: logsource - type: logsource
product: opencanary product: opencanary
# Maps "antivirus" category to Windows Defender logs shipped by Elastic Agent Winlog Integration # Maps "antivirus" category to Windows Defender logs shipped by Elastic Agent Winlog Integration
# and to Elastic Defend malware alerts
# winlog.event_data.threat_name has to be renamed prior to ingestion, it is originally winlog.event_data.Threat Name # winlog.event_data.threat_name has to be renamed prior to ingestion, it is originally winlog.event_data.Threat Name
- id: antivirus_field-mappings_windows-defender - id: antivirus_field-mappings
type: field_name_mapping type: field_name_mapping
mapping: mapping:
Signature: winlog.event_data.threat_name Signature:
- winlog.event_data.threat_name
- rule.name
rule_conditions: rule_conditions:
- type: logsource - type: logsource
category: antivirus category: antivirus
- id: antivirus_add-fields_windows-defender - id: antivirus_add-fields
type: add_condition type: add_condition
conditions: conditions:
winlog.channel: 'Microsoft-Windows-Windows Defender/Operational' event.code:
winlog.provider_name: 'Microsoft-Windows-Windows Defender' - "1116"
event.code: "1116" - "malicious_file"
- "memory_signature"
rule_conditions: rule_conditions:
- type: logsource - type: logsource
category: antivirus category: antivirus
# OS-agnostic process_creation scoping for product-less (NIDS/host-pivot) rules. # OS-agnostic process_creation scoping for product-less (NIDS/host-pivot) rules.
# pySigma: rule_cond_expr requires rule_conditions as a mapping, not a list.
- id: process_creation_os_agnostic - id: process_creation_os_agnostic
type: add_condition type: add_condition
conditions: conditions:
event.category: process event.category: process
rule_conditions: rule_conditions:
- type: logsource pc_cat:
type: logsource
category: process_creation category: process_creation
pc_win:
type: logsource
product: windows
pc_mac:
type: logsource
product: macos
pc_lin:
type: logsource
product: linux
rule_cond_expr: "pc_cat and not (pc_win or pc_mac or pc_lin)"
# 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
@@ -131,13 +147,22 @@ transformations:
type: field_name_mapping type: field_name_mapping
mapping: mapping:
winlog.event_data.Details: registry.data.strings 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: rule_conditions:
- type: logsource - type: logsource
product: windows product: windows
category: registry_set category: registry_set
# ecs_windows renames Initiated -> network.direction without translating the value,
# so rules compile to network.direction:"true" and never match (field holds
# ingress/egress). network.initiated carries the boolean on both Defend and Sysmon.
# Keyed on network.direction because ecs_windows has already renamed by this layer.
- id: ecs_fix_network_connection_initiated
type: field_name_mapping
mapping:
network.direction: network.initiated
rule_conditions:
- type: logsource
product: windows
category: network_connection
- id: ecs_fix_image_load - id: ecs_fix_image_load
type: field_name_mapping type: field_name_mapping
mapping: mapping:
@@ -150,6 +175,30 @@ transformations:
- type: logsource - type: logsource
product: windows product: windows
category: image_load category: image_load
# Defend reports the driver image in dll.path (file.path is null on driver events) —
# same renames as image_load.
- id: ecs_fix_driver_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: driver_load
- id: ecs_fix_file_rename
type: field_name_mapping
mapping:
# Defend records the pre-rename path in file.Ext.original.path; Sysmon's
# SourceFilename has no ECS equivalent.
winlog.event_data.SourceFilename: file.Ext.original.path
rule_conditions:
- type: logsource
product: windows
category: file_rename
- id: linux_security_add-fields - id: linux_security_add-fields
type: add_condition type: add_condition
conditions: conditions:
@@ -323,6 +372,33 @@ transformations:
rule_conditions: rule_conditions:
- type: logsource - type: logsource
category: file_event category: file_event
# Without event.type scoping, file_delete rules also match creations.
- id: endpoint_file_delete_add-fields
type: add_condition
conditions:
event.category: 'file'
event.type: 'deletion'
rule_conditions:
- type: logsource
category: file_delete
# Defend reports renames as event.action:rename with event.type:change.
- id: endpoint_file_rename_add-fields
type: add_condition
conditions:
event.category: 'file'
event.action: 'rename'
rule_conditions:
- type: logsource
category: file_rename
# event.type:change selects writes and excludes Defend's read-only registry events.
- id: endpoint_registry_set_add-fields
type: add_condition
conditions:
event.category: 'registry'
event.type: 'change'
rule_conditions:
- type: logsource
category: registry_set
# Scope image_load rules to Elastic Endpoint library events (event.category:library, dll.* # Scope image_load rules to Elastic Endpoint library events (event.category:library, dll.*
# populated). # populated).
- id: endpoint_image_load_add-fields - id: endpoint_image_load_add-fields
@@ -351,6 +427,149 @@ transformations:
rule_conditions: rule_conditions:
- type: logsource - type: logsource
category: network_connection category: network_connection
# Scope on lookup actions, not network.protocol:dns (also matches Zeek port-53).
- id: endpoint_dns_query_add-fields
type: add_condition
conditions:
event.category: 'network'
event.action:
- 'lookup_requested'
- 'lookup_result'
rule_conditions:
- type: logsource
category: dns_query
# OS gates: without them a `product: windows` rule in these categories also matches
# Linux/macOS. Separate conditions (not `product:` on the mappings above) so the
# mappings keep scoping product-less rules.
- id: endpoint_file_create_windows_os-gate
type: add_condition
conditions:
host.os.type: 'windows'
rule_conditions:
- type: logsource
category: file_event
product: windows
- id: endpoint_file_create_linux_os-gate
type: add_condition
conditions:
host.os.type: 'linux'
rule_conditions:
- type: logsource
category: file_event
product: linux
- id: endpoint_file_create_macos_os-gate
type: add_condition
conditions:
host.os.type: 'macos'
rule_conditions:
- type: logsource
category: file_event
product: macos
- id: endpoint_file_delete_windows_os-gate
type: add_condition
conditions:
host.os.type: 'windows'
rule_conditions:
- type: logsource
category: file_delete
product: windows
- id: endpoint_file_delete_linux_os-gate
type: add_condition
conditions:
host.os.type: 'linux'
rule_conditions:
- type: logsource
category: file_delete
product: linux
- id: endpoint_file_delete_macos_os-gate
type: add_condition
conditions:
host.os.type: 'macos'
rule_conditions:
- type: logsource
category: file_delete
product: macos
- id: endpoint_file_rename_windows_os-gate
type: add_condition
conditions:
host.os.type: 'windows'
rule_conditions:
- type: logsource
category: file_rename
product: windows
- id: endpoint_file_rename_linux_os-gate
type: add_condition
conditions:
host.os.type: 'linux'
rule_conditions:
- type: logsource
category: file_rename
product: linux
- id: endpoint_file_rename_macos_os-gate
type: add_condition
conditions:
host.os.type: 'macos'
rule_conditions:
- type: logsource
category: file_rename
product: macos
- id: endpoint_network_connection_windows_os-gate
type: add_condition
conditions:
host.os.type: 'windows'
rule_conditions:
- type: logsource
category: network_connection
product: windows
- id: endpoint_network_connection_linux_os-gate
type: add_condition
conditions:
host.os.type: 'linux'
rule_conditions:
- type: logsource
category: network_connection
product: linux
- id: endpoint_network_connection_macos_os-gate
type: add_condition
conditions:
host.os.type: 'macos'
rule_conditions:
- type: logsource
category: network_connection
product: macos
- id: endpoint_dns_query_windows_os-gate
type: add_condition
conditions:
host.os.type: 'windows'
rule_conditions:
- type: logsource
category: dns_query
product: windows
- id: endpoint_dns_query_linux_os-gate
type: add_condition
conditions:
host.os.type: 'linux'
rule_conditions:
- type: logsource
category: dns_query
product: linux
- id: endpoint_dns_query_macos_os-gate
type: add_condition
conditions:
host.os.type: 'macos'
rule_conditions:
- type: logsource
category: dns_query
product: macos
# registry_set / image_load / driver_load are not gated — windows-only telemetry.
# wmi / wmi_event are not scoped here: their only handle is event.dataset
- id: endpoint_driver_load_add-fields
type: add_condition
conditions:
event.category: 'driver'
rule_conditions:
- type: logsource
category: driver_load
# Maps "alert" category to SO Alert events # Maps "alert" category to SO Alert events
- id: alert_so_add-fields - id: alert_so_add-fields
type: add_condition type: add_condition
+1 -1
View File
@@ -88,7 +88,7 @@
'host': GLOBALS.manager_ip, 'host': GLOBALS.manager_ip,
'password': PG_PASS, 'password': PG_PASS,
'port': 5432, 'port': 5432,
'sslMode': 'allow', 'sslMode': 'require',
'user': PG_USER, 'user': PG_USER,
} }
}) %} }) %}
+1 -1
View File
@@ -486,7 +486,7 @@ soc:
global: True global: True
advanced: True advanced: True
sslMode: sslMode:
description: "Use encrypted connections to the PostgreSQL server. Must be one of the following values: disable, allow, prefer, require, verify-ca, verify-full. Defaults to allow." description: "Use encrypted connections to the PostgreSQL server. Must be one of the following values: disable, allow, prefer, require, verify-ca, verify-full."
global: True global: True
advanced: True advanced: True
database: database:
+2 -1
View File
@@ -1,10 +1,11 @@
{% import 'vars/init.map.jinja' as INIT %} {% import 'vars/init.map.jinja' as INIT %}
{% import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS %}
{% {%
set ELASTICSEARCH_GLOBALS = { set ELASTICSEARCH_GLOBALS = {
'elasticsearch': { 'elasticsearch': {
'es_cluster_name': INIT.PILLAR.elasticsearch.config.cluster.name, 'es_cluster_name': INIT.PILLAR.elasticsearch.config.cluster.name,
'es_heap': INIT.PILLAR.elasticsearch.esheap 'es_heap': INIT.PILLAR.elasticsearch.get('esheap', ELASTICSEARCHDEFAULTS.elasticsearch.esheap)
} }
} }
%} %}
-1
View File
@@ -62,7 +62,6 @@ zeek:
- securityonion/file-extraction - securityonion/file-extraction
- securityonion/community-id-extended - securityonion/community-id-extended
- oui-logging - oui-logging
- icsnpp-modbus
- icsnpp-dnp3 - icsnpp-dnp3
- icsnpp-bacnet - icsnpp-bacnet
- icsnpp-ethercat - icsnpp-ethercat