From 69d77382f1ac1219c92a239d734b3505e5a7a43b Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Wed, 1 Jul 2026 15:12:53 -0400 Subject: [PATCH 1/2] suricata: timestamp each line of reload log output Route the reload/verify output (ours plus so-common's retry/fail lines) through a synchronous timestamping pipeline so every line in reload.log is prefixed with a date/time, and preserve the real exit code via PIPESTATUS. --- .../tools/sbin/so-suricata-reload-rules | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/salt/suricata/tools/sbin/so-suricata-reload-rules b/salt/suricata/tools/sbin/so-suricata-reload-rules index b966e4bc0..6db519413 100644 --- a/salt/suricata/tools/sbin/so-suricata-reload-rules +++ b/salt/suricata/tools/sbin/so-suricata-reload-rules @@ -11,13 +11,12 @@ RULES_FILE="/opt/so/rules/suricata/all-rulesets.rules" SOCKET="/var/run/suricata/suricata-command.socket" SURICATASC="docker exec so-suricata /opt/suricata/bin/suricatasc" -# 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" - # 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 @@ -53,4 +52,14 @@ reload_and_verify() { return 1 } -retry 60 3 'reload_and_verify' || fail "Suricata did not load the current ruleset in time." +# 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]}" From 795aa898a30edbacb3d79769d23a0c26b6ad7729 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Wed, 1 Jul 2026 15:12:54 -0400 Subject: [PATCH 2/2] suricata: only reload rules once the ruleset file exists On a fresh install the surirulesync file.recurse creates .gitkeep before SOC has generated all-rulesets.rules. That change satisfied the surirulereload onchanges requisite, so the reload ran with no ruleset present, failed to stat the file, and reported the state (and install) as failed. Add an onlyif guard so the reload only runs when all-rulesets.rules exists. A .gitkeep-only sync now leaves the state a clean success (onlyif condition false); once SOC writes the ruleset, the reload fires normally. --- salt/suricata/enabled.sls | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/salt/suricata/enabled.sls b/salt/suricata/enabled.sls index d9d7f32ae..bb31b2c78 100644 --- a/salt/suricata/enabled.sls +++ b/salt/suricata/enabled.sls @@ -65,10 +65,11 @@ so-suricata: - file: suriclassifications surirulereload: - cmd.run: + cmd.run: - name: /usr/sbin/so-suricata-reload-rules >> /opt/so/log/suricata/reload.log 2>&1 - - onchanges: + - onchanges: - file: surirulesync + - onlyif: test -f /opt/so/rules/suricata/all-rulesets.rules - require: - docker_container: so-suricata