From c7e7a0a871aafb9dcbdd085d264001be59379103 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Thu, 14 Aug 2025 16:36:09 -0400 Subject: [PATCH 01/62] add more detail to fail_setup output --- setup/so-functions | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/setup/so-functions b/setup/so-functions index 522446be4..75ec2019e 100755 --- a/setup/so-functions +++ b/setup/so-functions @@ -29,8 +29,46 @@ title() { } fail_setup() { - error "Setup encounted an unrecoverable failure, exiting" - touch /root/failure + local failure_reason="${1:-Unknown failure}" + + # Capture call stack information + local calling_function="${FUNCNAME[1]:-main}" + local calling_line="${BASH_LINENO[0]:-unknown}" + local calling_file="${BASH_SOURCE[1]:-unknown}" + + # Build call stack trace + local call_stack="" + local i=1 + while [[ $i -lt ${#FUNCNAME[@]} ]]; do + local func="${FUNCNAME[$i]}" + local file="${BASH_SOURCE[$i]##*/}" # Get basename only + local line="${BASH_LINENO[$((i-1))]}" + + if [[ -n "$call_stack" ]]; then + call_stack="$call_stack -> " + fi + call_stack="$call_stack$func($file:$line)" + ((i++)) + done + + # Enhanced error logging with call stack + error "FAILURE: Called from $calling_function() at line $calling_line" + error "REASON: $failure_reason" + error "STACK: $call_stack" + error "Setup encountered an unrecoverable failure: $failure_reason" + + # Create detailed failure file with enhanced information + { + echo "SETUP_FAILURE_TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M:%S UTC')" + echo "SETUP_FAILURE_REASON=$failure_reason" + echo "SETUP_CALLING_FUNCTION=$calling_function" + echo "SETUP_CALLING_LINE=$calling_line" + echo "SETUP_CALLING_FILE=${calling_file##*/}" + echo "SETUP_CALL_STACK=$call_stack" + echo "SETUP_LOG_LOCATION=$setup_log" + echo "SETUP_FAILURE_DETAILS=Check $setup_log for complete error details" + } > /root/failure + exit 1 } @@ -1194,7 +1232,7 @@ hypervisor_local_states() { info "Running libvirt states for hypervisor" logCmd "salt-call state.apply libvirt.64962 --local --file-root=../salt/ -l info" info "Setting up bridge for $MNIC" - salt-call state.apply libvirt.bridge --local --file-root=../salt/ -l info pillar="{\"host\": {\"mainint\": \"$MNIC\"}}" + salt-call state.apply libvirt.bridge --local --file-root=../salt/ -l info pillar="{\"host\": {\"mainint\": \"$MNIC\"}}" fi } From a3cc6f025e43bf8c9bf546e2882d7fffdcda8b76 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 18 Aug 2025 09:54:40 -0400 Subject: [PATCH 02/62] reports --- salt/sensoroni/config.sls | 17 ++ salt/sensoroni/enabled.sls | 1 + .../reports/custom/generic_report1.md | 39 ++++ .../templates/reports/standard/case_report.md | 133 ++++++++++++ .../reports/standard/productivity_report.md | 189 ++++++++++++++++++ salt/sensoroni/soc_sensoroni.yaml | 91 +++++++++ salt/soc/defaults.map.jinja | 1 + salt/soc/defaults.yaml | 2 + salt/soc/enabled.sls | 1 + salt/soc/soc_soc.yaml | 9 + 10 files changed, 483 insertions(+) create mode 100644 salt/sensoroni/files/templates/reports/custom/generic_report1.md create mode 100644 salt/sensoroni/files/templates/reports/standard/case_report.md create mode 100644 salt/sensoroni/files/templates/reports/standard/productivity_report.md diff --git a/salt/sensoroni/config.sls b/salt/sensoroni/config.sls index f983fce38..68b3baddc 100644 --- a/salt/sensoroni/config.sls +++ b/salt/sensoroni/config.sls @@ -43,6 +43,23 @@ analyzerscripts: - source: salt://sensoroni/files/analyzers - show_changes: False +templatesdir: + file.directory: + - name: /opt/so/conf/sensoroni/templates + - user: 939 + - group: 939 + - makedirs: True + +sensoronitemplates: + file.recurse: + - name: /opt/so/conf/sensoroni/templates + - source: salt://sensoroni/files/templates + - user: 939 + - group: 939 + - mode: 664 + - template: jinja + - show_changes: False + sensoroni_sbin: file.recurse: - name: /usr/sbin diff --git a/salt/sensoroni/enabled.sls b/salt/sensoroni/enabled.sls index 3f05568a0..fd44ea79a 100644 --- a/salt/sensoroni/enabled.sls +++ b/salt/sensoroni/enabled.sls @@ -22,6 +22,7 @@ so-sensoroni: - /nsm/pcapout:/nsm/pcapout:rw - /opt/so/conf/sensoroni/sensoroni.json:/opt/sensoroni/sensoroni.json:ro - /opt/so/conf/sensoroni/analyzers:/opt/sensoroni/analyzers:rw + - /opt/so/conf/sensoroni/templates:/opt/sensoroni/templates:r - /opt/so/log/sensoroni:/opt/sensoroni/logs:rw - /nsm/suripcap/:/nsm/suripcap:rw {% if DOCKER.containers['so-sensoroni'].custom_bind_mounts %} diff --git a/salt/sensoroni/files/templates/reports/custom/generic_report1.md b/salt/sensoroni/files/templates/reports/custom/generic_report1.md new file mode 100644 index 000000000..133480731 --- /dev/null +++ b/salt/sensoroni/files/templates/reports/custom/generic_report1.md @@ -0,0 +1,39 @@ +{{- /* query.myDocEvents.Oql = metadata.type: _doc | groupby event.module, event.dataset | sortby @timestamp desc */ -}} +{{- /* query.myDocEvents.MetricLimit = 10 */ -}} +{{- /* query.myDocEvents.EventLimit = 100 */ -}} + +SECURITY ONION SAMPLE REPORT +============================ + +{{ if .Error }} +**NOTE: This report encountered a problem extracting the relevant data and may not be complete.** + +**Error:** {{.Error}} +{{ end }} + + +Records must have been created or updated during the following time frame in order to be reflected in this report. + +**Report Start Date:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .BeginDate}} + +**Report End Date:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .EndDate}} + +## Sample Doc Events + +**Total Events:** {{ formatNumber "%d" "en" .Results.myDocEvents.TotalEvents}} + +### Event Counts By Module and Dataset + +| Count | Proportion | Module | Dataset | +| ----- | ---------- | ------ | ------- | +{{ range sortMetrics "Value" "desc" .Results.myDocEvents.Metrics.groupby_0_event_module_event_dataset -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | {{index .Keys 1}} | +{{end}} + +### Individual Events (Limited to first {{.Results.myDocEvents.Criteria.EventLimit}}) + +| Event Time | Module | Dataset | Category | +| ---------- | ------ | ------- | -------- | +{{ range .Results.myDocEvents.Events -}} +| {{.Timestamp}} | {{.Payload.event_module}} | {{.Payload.event_dataset}} | {{.Payload.event_category}} | +{{end}} diff --git a/salt/sensoroni/files/templates/reports/standard/case_report.md b/salt/sensoroni/files/templates/reports/standard/case_report.md new file mode 100644 index 000000000..ab563040a --- /dev/null +++ b/salt/sensoroni/files/templates/reports/standard/case_report.md @@ -0,0 +1,133 @@ +SECURITY ONION CASE REPORT +========================== + +## Case Details + +**Case ID:** {{.Case.Id}} + +**Title:** {{.Case.Title}} + +## Description + +{{.Case.Description}} + +## Details + +**Created:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .Case.CreateTime}} + +**Updated:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .Case.UpdateTime}} + +**Author:** {{getUserDetail "email" .Case.UserId}} + +**Status:** {{.Case.Status}} + +**TLP:** {{.Case.Tlp}} + +**PAP:** {{.Case.Pap}} + +**Severity:** {{.Case.Severity}} + +**Priority:** {{.Case.Priority}} + +**Category:** {{.Case.Category}} + +**Tags:** {{join .Case.Tags ", " }} + +**Assignee:** {{getUserDetail "email" .Case.AssigneeId}} + +**Hours Logged:** {{ formatNumber "%.2f" "en" .TotalHours}} + +## Comments + +{{ range sortComments "CreateTime" "asc" .Comments }} +**Created:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .CreateTime}} + +**Updated:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .UpdateTime}} + +**Author:** {{getUserDetail "email" .UserId}} + +**Hours Logged:** {{ formatNumber "%.2f" "en" .Hours}} + +{{.Description}} + +--- + +{{end}} + +## Detections + +{{ range sortDetections "Title" "asc" .Detections }} +**Title:** {{.Title}} + +**Description:** {{.Description}} + +**Severity:** {{.Severity}} + +**Rule Engine:** {{.Engine}} + +**Rule Set:** {{.Ruleset}} + +**Community Rule:** {{.IsCommunity}} + +**Tags:** {{.Tags}} + +{{.Content}} + +--- + +{{end}} + +## Attachments + +{{ range sortArtifacts "CreateTime" "asc" .Attachments }} +**Added:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .CreateTime}} + +**Updated:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .UpdateTime}} + +**Added By:** {{getUserDetail "email" .UserId}} + +**TLP:** {{.Tlp}} + +**Filename:** {{.Value}} + +**Size:** {{ formatNumber "%.0d" "en" .StreamLen}} bytes + +**SHA256:** {{.Sha256}} + +**SHA1:** {{.Sha1}} + +**MD5:** {{.Md5}} + +**Tags:** {{.Tags}} + +**Protected (Zipped):** {{.Protected}} + +{{.Description}} + +--- + +{{end}} + +## Observables + +| Date Added | Tlp | Type | IOC | Value | Description | +| ---------- | --- | ---- | --- | ----- | ----------- | +{{ range sortArtifacts "CreateTime" "asc" .Observables -}} +| {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .CreateTime}} | {{.Tlp}} | {{.ArtifactType}} | {{.Ioc}} | {{.Value}} | {{.Description}} | +{{end}} + +## Related Events + +| Event Time | Log ID | Source IP | Destination IP | +| ---------- | ------ | --------- | -------------- | +{{ range sortRelatedEvents "fields:soc_timestamp" "asc" .RelatedEvents -}} +| {{.Fields.soc_timestamp}} | {{.Fields.log_id_uid}} | {{.Fields.source_ip}} | {{.Fields.destination_ip}} | +{{end}} + +## Case History + +| Date | User | Object | Operation | +| ---- | ---- | ------ | --------- | +{{ range sortHistory "CreateTime" "asc" .History -}} +| {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .CreateTime}} | {{getUserDetail "email" .UserId}} | {{.Kind}} | {{.Operation}} | +{{end}} \ No newline at end of file diff --git a/salt/sensoroni/files/templates/reports/standard/productivity_report.md b/salt/sensoroni/files/templates/reports/standard/productivity_report.md new file mode 100644 index 000000000..2e99e710f --- /dev/null +++ b/salt/sensoroni/files/templates/reports/standard/productivity_report.md @@ -0,0 +1,189 @@ +SECURITY ONION PRODUCTIVITY REPORT +================================== + +{{ if .Error }} +**NOTE: This report encountered a problem extracting the relevant data and may not be complete.** + +**Error:** {{.Error}} +{{ end }} + + +Records must have been created or updated during the following time frame in order to be reflected in this report. + +**Report Start Date:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .BeginDate}} + +**Report End Date:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .EndDate}} + +## Ingested Events + +**Total Events:** {{ formatNumber "%d" "en" .TotalEvents}} + +### Events By Module + +| Count | Proportion | Module | +| ----- | ---------- | ------ | +{{ range sortMetrics "Value" "desc" .TotalEventsByModule -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Events By Module and Severity Label + +| Count | Proportion | Module | Severity | +| ----- | ---------- | ------ | -------- | +{{ range sortMetrics "Value" "desc" .TotalEventsByModuleDataset -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | {{index .Keys 1}} | +{{end}} + +## Alerts + +**Total Alerts:** {{ formatNumber "%d" "en" .TotalAlerts}} + +{{ range sortMetrics "Value" "desc" .TotalAlertsByAcknowledged -}} + {{ if index .Keys 0 | eq "true" }} +**Acknowledged Alerts:** {{ formatNumber "%.0f" "en" .Value}} ({{ formatNumber "%.1f" "en" .Percentage}}%) + {{ end }} +{{end}} + +{{ range sortMetrics "Value" "desc" .TotalAlertsByEscalated -}} + {{ if index .Keys 0 | eq "true" }} +**Escalated Alerts:** {{ formatNumber "%.0f" "en" .Value}} ({{ formatNumber "%.1f" "en" .Percentage}}%) + {{ end }} +{{end}} + +### Alerts By Severity + +| Count | Proportion | Severity | +| ----- | ---------- | -------- | +{{ range sortMetrics "Value" "desc" .TotalAlertsBySeverityLabel -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Alerts By Module + +| Count | Proportion | Module | +| ----- | ---------- | ------ | +{{ range sortMetrics "Value" "desc" .TotalAlertsByModule -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Alerts By Module and Severity Label + +| Count | Proportion | Module | Severity | +| ----- | ---------- | ------ | -------- | +{{ range sortMetrics "Value" "desc" .TotalAlertsByModuleSeverityLabel -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | {{index .Keys 1}} | +{{end}} + +### Alerts By Ruleset + +| Count | Proportion | Ruleset | +| ----- | ---------- | ------- | +{{ range sortMetrics "Value" "desc" .TotalAlertsByRuleset -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Alerts By Rule Category + +| Count | Proportion | Category | +| ----- | ---------- | -------- | +{{ range sortMetrics "Value" "desc" .TotalAlertsByCategory -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +## Cases + +**Total Cases:** {{ formatNumber "%d" "en" .TotalCases}} + +**Average Elapsed Time To Complete:** {{ formatNumber "%.1f" "en" .AverageHoursToComplete }} hours + +### Cases By Status + +| Count | Proportion | Status | +| ----- | ---------- | ------ | +{{ range sortMetrics "Value" "desc" .TotalCasesByStatus -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Cases By Assignee + +| Count | Proportion | Assignee | +| ----- | ---------- | -------- | +{{ range sortMetrics "Value" "desc" .TotalCasesByAssignee -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0 | getUserDetail "email"}} | +{{end}} + +### Cases By Status and Assignee + +| Count | Proportion | Status | Assignee | +| ----- | ---------- | ------ | -------- | +{{ range sortMetrics "Value" "desc" .TotalCasesByStatusAssignee -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | {{index .Keys 1 | getUserDetail "email"}} | +{{end}} + +### Cases By Severity + +| Count | Proportion | Severity | +| ----- | ---------- | -------- | +{{ range sortMetrics "Value" "desc" .TotalCasesBySeverity -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Cases By Priority + +| Count | Proportion | Priority | +| ----- | ---------- | -------- | +{{ range sortMetrics "Value" "desc" .TotalCasesByPriority -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Cases By Traffic Light Protocol (TLP) + +| Count | Proportion | TLP | +| ----- | ---------- | ----| +{{ range sortMetrics "Value" "desc" .TotalCasesByTlp -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Cases By Permissible Actions Protocol (PAP) + +| Count | Proportion | PAP | +| ----- | ---------- | --- | +{{ range sortMetrics "Value" "desc" .TotalCasesByPap -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Cases By Category + +| Count | Proportion | Category | +| ----- | ---------- | -------- | +{{ range sortMetrics "Value" "desc" .TotalCasesByCategory -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Cases By Tags + +| Count | Proportion | Tags | +| ----- | ---------- | ---- | +{{ range sortMetrics "Value" "desc" .TotalCasesByTags -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0}} | +{{end}} + +### Comments By User + +| Count | Proportion | User | +| ----- | ---------- | ---- | +{{ range sortMetrics "Value" "desc" .TotalCommentsByUserId -}} +| {{ formatNumber "%.0f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0 | getUserDetail "email"}} | +{{end}} + +## Time Tracking + +**Total Hours:** {{ formatNumber "%.2f" "en" .TotalHours}} + +### Hours By User + +| Hours | Proportion | User | +| ----- | ---------- | ---- | +{{ range sortMetrics "Value" "desc" .TotalHoursByUserId -}} +| {{ formatNumber "%.2f" "en" .Value}} | {{ formatNumber "%.1f" "en" .Percentage}}% | {{index .Keys 0 | getUserDetail "email"}} | +{{end}} \ No newline at end of file diff --git a/salt/sensoroni/soc_sensoroni.yaml b/salt/sensoroni/soc_sensoroni.yaml index 71a2c779b..b9ed7bec7 100644 --- a/salt/sensoroni/soc_sensoroni.yaml +++ b/salt/sensoroni/soc_sensoroni.yaml @@ -306,3 +306,94 @@ sensoroni: sensitive: False advanced: True forcedType: string + files: + templates: + reports: + standard: + case_report__md: + title: Case report Template + description: The template used when generating a case report. Supports markdown format. + file: True + global: True + syntax: md + helpLink: reports.html + productivity_report__md: + title: Productivity Report Template + description: The template used when generating a comprehensive productivity report. Supports markdown format. + file: True + global: True + syntax: md + helpLink: reports.html + custom: + generic_report1__md: + title: Custom Report 1 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report2__md: + title: Custom Report 2 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report3__md: + title: Custom Report 3 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report4__md: + title: Custom Report 4 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report5__md: + title: Custom Report 5 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report6__md: + title: Custom Report 6 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report7__md: + title: Custom Report 7 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report8__md: + title: Custom Report 8 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + generic_report9__md: + title: Custom Report 9 + description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + file: True + global: True + syntax: md + helpLink: reports.html + addl_generic_report__md: + title: Additional Custom Report + description: A duplicatable customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. This is an unsupported feature due to the inability to edit duplicated reports via the SOC app. + advanced: True + file: True + global: True + syntax: md + duplicates: True + helpLink: reports.html \ No newline at end of file diff --git a/salt/soc/defaults.map.jinja b/salt/soc/defaults.map.jinja index b52aa97d8..a2e72bcca 100644 --- a/salt/soc/defaults.map.jinja +++ b/salt/soc/defaults.map.jinja @@ -35,5 +35,6 @@ {% do SOCDEFAULTS.soc.config.server.modules.statickeyauth.update({'anonymousCidr': DOCKER.range, 'apiKey': pillar.sensoroni.config.sensoronikey}) %} {% do SOCDEFAULTS.soc.config.server.client.case.update({'analyzerNodeId': GLOBALS.hostname}) %} +{% do SOCDEFAULTS.soc.config.server.client.update({'exportNodeId': GLOBALS.hostname}) %} {% set SOCDEFAULTS = SOCDEFAULTS.soc %} diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index 66355fa24..95b569afd 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -1343,6 +1343,7 @@ soc: htmlDir: html importUploadDir: /nsm/soc/uploads forceUserOtp: false + customReportsPath: /opt/sensoroni/templates/reports/custom modules: cases: soc filedatastore: @@ -1557,6 +1558,7 @@ soc: casesEnabled: true detectionsEnabled: true inactiveTools: ['toolUnused'] + exportNodeId: tools: - name: toolKibana description: toolKibanaHelp diff --git a/salt/soc/enabled.sls b/salt/soc/enabled.sls index 09e2c16a8..df6e36dc1 100644 --- a/salt/soc/enabled.sls +++ b/salt/soc/enabled.sls @@ -48,6 +48,7 @@ so-soc: - /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_clients_roles:/opt/sensoroni/rbac/clients_roles:rw + - /opt/so/conf/sensoroni/templates:/opt/sensoroni/templates:r - /opt/so/conf/soc/queue:/opt/sensoroni/queue:rw - /opt/so/saltstack:/opt/so/saltstack:rw - /opt/so/conf/soc/migrations:/opt/so/conf/soc/migrations:rw diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index da3549039..8f3b26846 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -138,6 +138,11 @@ soc: title: Require TOTP description: Require all users to enable Time-based One Time Passwords (MFA) upon login to SOC. global: True + customReportsPath: + title: Custom Reports Path + description: Path to custom markdown templates for PDF report generation. All markdown files in this directory will be available as custom reports in the SOC Reports interface. + global: True + advanced: True subgrids: title: Subordinate Grids description: | @@ -589,6 +594,10 @@ soc: global: True advanced: True forcedType: "[]{}" + exportNodeId: + description: The node ID on which export jobs will be executed. + global: True + advanced: True hunt: &appSettings groupItemsPerPage: description: Default number of aggregations to show per page. Larger values consume more vertical area in the SOC UI. From 8d3220f94b681f29f20e9c3bf33c915303ad210a Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 18 Aug 2025 14:31:01 -0400 Subject: [PATCH 03/62] fix salt issue --- salt/sensoroni/config.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/sensoroni/config.sls b/salt/sensoroni/config.sls index 68b3baddc..590396e49 100644 --- a/salt/sensoroni/config.sls +++ b/salt/sensoroni/config.sls @@ -56,7 +56,7 @@ sensoronitemplates: - source: salt://sensoroni/files/templates - user: 939 - group: 939 - - mode: 664 + - file_mode: 664 - template: jinja - show_changes: False From 884bec746502b6c5d1216660b8f20b3eeda8ae75 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 18 Aug 2025 15:01:49 -0400 Subject: [PATCH 04/62] fix typo --- salt/soc/enabled.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/soc/enabled.sls b/salt/soc/enabled.sls index df6e36dc1..fdf4d1e6e 100644 --- a/salt/soc/enabled.sls +++ b/salt/soc/enabled.sls @@ -48,7 +48,7 @@ so-soc: - /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_clients_roles:/opt/sensoroni/rbac/clients_roles:rw - - /opt/so/conf/sensoroni/templates:/opt/sensoroni/templates:r + - /opt/so/conf/sensoroni/templates:/opt/sensoroni/templates:ro - /opt/so/conf/soc/queue:/opt/sensoroni/queue:rw - /opt/so/saltstack:/opt/so/saltstack:rw - /opt/so/conf/soc/migrations:/opt/so/conf/soc/migrations:rw From 46e1f1bc5ce16743398f0dac2bc3760ccfcba5a4 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 18 Aug 2025 16:12:34 -0400 Subject: [PATCH 05/62] fix typo --- salt/sensoroni/enabled.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/sensoroni/enabled.sls b/salt/sensoroni/enabled.sls index fd44ea79a..4b0b6b317 100644 --- a/salt/sensoroni/enabled.sls +++ b/salt/sensoroni/enabled.sls @@ -22,7 +22,7 @@ so-sensoroni: - /nsm/pcapout:/nsm/pcapout:rw - /opt/so/conf/sensoroni/sensoroni.json:/opt/sensoroni/sensoroni.json:ro - /opt/so/conf/sensoroni/analyzers:/opt/sensoroni/analyzers:rw - - /opt/so/conf/sensoroni/templates:/opt/sensoroni/templates:r + - /opt/so/conf/sensoroni/templates:/opt/sensoroni/templates:ro - /opt/so/log/sensoroni:/opt/sensoroni/logs:rw - /nsm/suripcap/:/nsm/suripcap:rw {% if DOCKER.containers['so-sensoroni'].custom_bind_mounts %} From 77273449c9ab33b5a427408b369adb9502e25b0e Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 18 Aug 2025 16:58:52 -0400 Subject: [PATCH 06/62] fix typo --- salt/sensoroni/config.sls | 1 - 1 file changed, 1 deletion(-) diff --git a/salt/sensoroni/config.sls b/salt/sensoroni/config.sls index 590396e49..8298209f1 100644 --- a/salt/sensoroni/config.sls +++ b/salt/sensoroni/config.sls @@ -57,7 +57,6 @@ sensoronitemplates: - user: 939 - group: 939 - file_mode: 664 - - template: jinja - show_changes: False sensoroni_sbin: From 751b5bd5564f7351f8c684891a68f5e6084b2438 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 19 Aug 2025 10:11:50 -0400 Subject: [PATCH 07/62] switch version for tests --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1ff799fad..09e15369f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.180 +2.4.0-delta From d77556c6729aac42d9d0cdd05cf9b632ed18365a Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 21 Aug 2025 08:25:48 -0500 Subject: [PATCH 08/62] pcap dir --- salt/pcap/init.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/pcap/init.sls b/salt/pcap/init.sls index dffeac85c..6812c2d91 100644 --- a/salt/pcap/init.sls +++ b/salt/pcap/init.sls @@ -23,7 +23,7 @@ include: # /nsm/pcap is empty until stenographer is used as pcap engine {% set pcap_id = 941 %} {% set user_list = salt['user.list_users']() %} -{% if 'stenographer' not in user_list %} +{% if GLOBALS.pcap_engine == "SURICATA" and 'stenographer' not in user_list %} {% set pcap_id = 939 %} {% endif %} pcapdir: From 424fdff934e7eff3cfcd374a5d29245a28bebc12 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Thu, 21 Aug 2025 09:43:30 -0400 Subject: [PATCH 09/62] 180 soup base --- salt/manager/tools/sbin/soup | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/salt/manager/tools/sbin/soup b/salt/manager/tools/sbin/soup index e37ccbfda..f7180c46c 100755 --- a/salt/manager/tools/sbin/soup +++ b/salt/manager/tools/sbin/soup @@ -419,6 +419,7 @@ preupgrade_changes() { [[ "$INSTALLEDVERSION" == 2.4.141 ]] && up_to_2.4.150 [[ "$INSTALLEDVERSION" == 2.4.150 ]] && up_to_2.4.160 [[ "$INSTALLEDVERSION" == 2.4.160 ]] && up_to_2.4.170 + [[ "$INSTALLEDVERSION" == 2.4.170 ]] && up_to_2.4.180 true } @@ -448,6 +449,7 @@ postupgrade_changes() { [[ "$POSTVERSION" == 2.4.141 ]] && post_to_2.4.150 [[ "$POSTVERSION" == 2.4.150 ]] && post_to_2.4.160 [[ "$POSTVERSION" == 2.4.160 ]] && post_to_2.4.170 + [[ "$POSTVERSION" == 2.4.170 ]] && post_to_2.4.180 true } @@ -599,6 +601,11 @@ post_to_2.4.170() { POSTVERSION=2.4.170 } +post_to_2.4.180() { + echo "Nothing to apply" + POSTVERSION=2.4.180 +} + repo_sync() { echo "Sync the local repo." su socore -c '/usr/sbin/so-repo-sync' || fail "Unable to complete so-repo-sync." @@ -856,6 +863,12 @@ up_to_2.4.170() { INSTALLEDVERSION=2.4.170 } +up_to_2.4.180() { + echo "Nothing to do for 2.4.180" + + INSTALLEDVERSION=2.4.180 +} + add_hydra_pillars() { mkdir -p /opt/so/saltstack/local/pillar/hydra touch /opt/so/saltstack/local/pillar/hydra/soc_hydra.sls From 7968de06b4f4c2e41d13b3d719cf8896828ef2a8 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:06:29 -0500 Subject: [PATCH 10/62] enable access to global stig pillar --- pillar/top.sls | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pillar/top.sls b/pillar/top.sls index 1fdb59deb..b15038e5e 100644 --- a/pillar/top.sls +++ b/pillar/top.sls @@ -262,6 +262,7 @@ base: - minions.adv_{{ grains.id }} - kafka.nodes - kafka.soc_kafka + - stig.soc_stig '*_import': - node_data.ips @@ -319,10 +320,12 @@ base: - elasticfleet.adv_elasticfleet - minions.{{ grains.id }} - minions.adv_{{ grains.id }} + - stig.soc_stig '*_hypervisor': - minions.{{ grains.id }} - minions.adv_{{ grains.id }} + - stig.soc_stig '*_desktop': - minions.{{ grains.id }} From c91e9ea4e0bfc156030cc0f46af740a8dfe1f0ba Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Fri, 22 Aug 2025 13:23:19 -0400 Subject: [PATCH 11/62] return to normalcy --- VERSION | 2 +- salt/sensoroni/files/sensoroni.json | 1 + .../sensoroni/files/templates/reports/custom/generic_report1.md | 2 +- salt/sensoroni/files/templates/reports/standard/case_report.md | 2 +- .../files/templates/reports/standard/productivity_report.md | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 09e15369f..1ff799fad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.0-delta +2.4.180 diff --git a/salt/sensoroni/files/sensoroni.json b/salt/sensoroni/files/sensoroni.json index 917498ba1..c7079c08c 100644 --- a/salt/sensoroni/files/sensoroni.json +++ b/salt/sensoroni/files/sensoroni.json @@ -21,6 +21,7 @@ }, {%- endif %} "importer": {}, + "export": {}, "statickeyauth": { "apiKey": "{{ GLOBALS.sensoroni_key }}" {% if GLOBALS.is_sensor %} diff --git a/salt/sensoroni/files/templates/reports/custom/generic_report1.md b/salt/sensoroni/files/templates/reports/custom/generic_report1.md index 133480731..5cabd8f88 100644 --- a/salt/sensoroni/files/templates/reports/custom/generic_report1.md +++ b/salt/sensoroni/files/templates/reports/custom/generic_report1.md @@ -2,7 +2,7 @@ {{- /* query.myDocEvents.MetricLimit = 10 */ -}} {{- /* query.myDocEvents.EventLimit = 100 */ -}} -SECURITY ONION SAMPLE REPORT +Security Onion Custom Report ============================ {{ if .Error }} diff --git a/salt/sensoroni/files/templates/reports/standard/case_report.md b/salt/sensoroni/files/templates/reports/standard/case_report.md index ab563040a..49f18e7c6 100644 --- a/salt/sensoroni/files/templates/reports/standard/case_report.md +++ b/salt/sensoroni/files/templates/reports/standard/case_report.md @@ -1,4 +1,4 @@ -SECURITY ONION CASE REPORT +Security Onion Case Report ========================== ## Case Details diff --git a/salt/sensoroni/files/templates/reports/standard/productivity_report.md b/salt/sensoroni/files/templates/reports/standard/productivity_report.md index 2e99e710f..17afd1169 100644 --- a/salt/sensoroni/files/templates/reports/standard/productivity_report.md +++ b/salt/sensoroni/files/templates/reports/standard/productivity_report.md @@ -1,4 +1,4 @@ -SECURITY ONION PRODUCTIVITY REPORT +Security Onion Productivity Report ================================== {{ if .Error }} From 1357f19e482343f3611ac2c643de984bacf938ab Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Fri, 22 Aug 2025 13:25:25 -0400 Subject: [PATCH 12/62] update wording --- salt/sensoroni/soc_sensoroni.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/salt/sensoroni/soc_sensoroni.yaml b/salt/sensoroni/soc_sensoroni.yaml index b9ed7bec7..c2978b1af 100644 --- a/salt/sensoroni/soc_sensoroni.yaml +++ b/salt/sensoroni/soc_sensoroni.yaml @@ -327,70 +327,70 @@ sensoroni: custom: generic_report1__md: title: Custom Report 1 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report2__md: title: Custom Report 2 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report3__md: title: Custom Report 3 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report4__md: title: Custom Report 4 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report5__md: title: Custom Report 5 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report6__md: title: Custom Report 6 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report7__md: title: Custom Report 7 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report8__md: title: Custom Report 8 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html generic_report9__md: title: Custom Report 9 - description: A customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. + description: A custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. file: True global: True syntax: md helpLink: reports.html addl_generic_report__md: title: Additional Custom Report - description: A duplicatable customer defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. This is an unsupported feature due to the inability to edit duplicated reports via the SOC app. + description: A duplicatable custom, user-defined report. Supports markdown format. The report title inside the file, typically near the top, will be shown in the SOC reporting UI. This is an unsupported feature due to the inability to edit duplicated reports via the SOC app. advanced: True file: True global: True From 2a6c74917e5e2aafdc5fa6f473f1a3c99574fb60 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Fri, 22 Aug 2025 13:00:17 -0600 Subject: [PATCH 13/62] Ruleset Name UiElement Add a missing UiElement so all the repo fields are represented in the UI. --- salt/soc/soc_soc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index b292d1460..fe5df6d44 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -561,6 +561,8 @@ soc: forcedType: "[]{}" syntax: json uiElements: + - field: rulesetName + label: Ruleset Name - field: repo label: Repo URL required: True From d99857002d6d17f02b4be033ad56699c5fe3b103 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Fri, 22 Aug 2025 13:18:22 -0600 Subject: [PATCH 14/62] Improved Label The underlying field is called "rulesetName" but for playbook repos we're not talking about rulesets. Improved the label for user experience. --- salt/soc/soc_soc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index fe5df6d44..174368cbb 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -562,7 +562,7 @@ soc: syntax: json uiElements: - field: rulesetName - label: Ruleset Name + label: Playbook Source Name - field: repo label: Repo URL required: True From cbdd369a1882d902f837c4e17244de3259d7a17d Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Mon, 25 Aug 2025 08:39:55 -0400 Subject: [PATCH 15/62] ensure x509 in mine --- salt/libvirt/bridge.sls | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/salt/libvirt/bridge.sls b/salt/libvirt/bridge.sls index 5ff5d670c..c9e8650a3 100644 --- a/salt/libvirt/bridge.sls +++ b/salt/libvirt/bridge.sls @@ -3,6 +3,8 @@ # https://securityonion.net/license; you may not use this file except in compliance with the # Elastic License 2.0. +# We do not import GLOBALS in this state because it is called during setup + {% from 'libvirt/map.jinja' import LIBVIRTMERGED %} {% from 'salt/map.jinja' import SYSTEMD_UNIT_FILE %} @@ -38,6 +40,10 @@ update_mine_functions: mine_functions: network.ip_addrs: - interface: br0 + {%- if role in ['so-eval','so-import','so-manager','so-managerhype','so-managersearch','so-standalone'] %} + x509.get_pem_entries: + - glob_path: '/etc/pki/ca.crt' + {% endif %} - onchanges: - cmd: wait_for_br0_ip From e10d00d114031a7b76c0d14e5d9cf3e1332a1af4 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Tue, 26 Aug 2025 14:54:37 -0400 Subject: [PATCH 16/62] support for managerhype --- salt/libvirt/bridge.sls | 7 ++++--- salt/salt/minion.sls | 2 +- setup/so-functions | 23 +++++++++++++---------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/salt/libvirt/bridge.sls b/salt/libvirt/bridge.sls index c9e8650a3..b8f720993 100644 --- a/salt/libvirt/bridge.sls +++ b/salt/libvirt/bridge.sls @@ -5,9 +5,6 @@ # We do not import GLOBALS in this state because it is called during setup -{% from 'libvirt/map.jinja' import LIBVIRTMERGED %} -{% from 'salt/map.jinja' import SYSTEMD_UNIT_FILE %} - down_original_mgmt_interface: cmd.run: - name: "nmcli con down {{ pillar.host.mainint }}" @@ -32,6 +29,8 @@ wait_for_br0_ip: - onchanges: - cmd: down_original_mgmt_interface +{% if grains.role == 'so-hypervisor' %} + update_mine_functions: file.managed: - name: /etc/salt/minion.d/mine_functions.conf @@ -53,3 +52,5 @@ restart_salt_minion_service: - enable: True - listen: - file: update_mine_functions + +{% endif %} diff --git a/salt/salt/minion.sls b/salt/salt/minion.sls index b0e078e79..b85fad1c0 100644 --- a/salt/salt/minion.sls +++ b/salt/salt/minion.sls @@ -95,7 +95,7 @@ enable_startup_states: - unless: pgrep so-setup # prior to 2.4.30 this managed file would restart the salt-minion service when updated -# since this file is currently only adding a sleep timer on service start +# since this file is currently only adding a delay service start # it is not required to restart the service salt_minion_service_unit_file: file.managed: diff --git a/setup/so-functions b/setup/so-functions index 522446be4..4b84b752b 100755 --- a/setup/so-functions +++ b/setup/so-functions @@ -1187,15 +1187,18 @@ get_minion_type() { } hypervisor_local_states() { - # these states need to run before the first highstate so that we dont deal with the salt-minion restarting - # and we need these setup prior to the highstate - info "Check if hypervisor or managerhype" - if [ $is_hypervisor ] || [ $is_managerhype ]; then - info "Running libvirt states for hypervisor" - logCmd "salt-call state.apply libvirt.64962 --local --file-root=../salt/ -l info" - info "Setting up bridge for $MNIC" - salt-call state.apply libvirt.bridge --local --file-root=../salt/ -l info pillar="{\"host\": {\"mainint\": \"$MNIC\"}}" - fi + # these states need to run before the first highstate so that we dont deal with the salt-minion restarting + # and we need these setup prior to the highstate + info "Check if hypervisor or managerhype" + if [ $is_hypervisor ] || [ $is_managerhype ]; then + info "Running libvirt states for hypervisor" + logCmd "salt-call state.apply libvirt.64962 --local --file-root=../salt/ -l info queue=True" + info "Setting up bridge for $MNIC" + salt-call state.apply libvirt.bridge --local --file-root=../salt/ -l info pillar="{\"host\": {\"mainint\": \"$MNIC\"}} queue=True" + if [ $is_managerhype ]; then + logCmd "salt-call state.apply salt.minion queue=True" + fi + fi } install_cleanup() { @@ -1642,7 +1645,7 @@ reserve_ports() { reinstall_init() { info "Putting system in state to run setup again" - if [[ $install_type =~ ^(MANAGER|EVAL|MANAGERSEARCH|STANDALONE|FLEET|IMPORT)$ ]]; then + if [[ $install_type =~ ^(MANAGER|EVAL|MANAGERSEARCH|MANAGERHYPE|STANDALONE|FLEET|IMPORT)$ ]]; then local salt_services=( "salt-master" "salt-minion" ) else local salt_services=( "salt-minion" ) From ccd79c814d01d16783283ab823ab3c49f0f30862 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Wed, 27 Aug 2025 09:53:37 -0400 Subject: [PATCH 17/62] Add script for bond0 channels --- CLAUDE.md | 104 ++++++++++++++++++++++++++++++ salt/sensor/files/so-combine-bond | 72 +++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 CLAUDE.md create mode 100644 salt/sensor/files/so-combine-bond diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..76c0577d6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,104 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +Security Onion is an open-source network security monitoring (NSM) platform that combines multiple security tools into a unified solution. It's designed for threat hunting, enterprise security monitoring, and log management. The platform integrates tools for intrusion detection, packet capture, log management, and security analytics in a comprehensive security monitoring solution. + +## Architecture + +Security Onion uses a microservice architecture with containerized components: + +- **Deployment Models**: + - Standalone: Single all-in-one instance + - Distributed: Manager/sensor architecture with multiple node types + - Manager: Central management server + - Search Nodes: Data storage and search + - Sensor Nodes: Network monitoring and data collection + - Heavy Nodes: Combined sensor/search capabilities + - IDH (Intrusion Deception Host): Honeypot services + +- **Core Components**: + - Data Collection: Zeek, Suricata, Steno (PCAP), Elastic Agents + - Data Processing: Logstash, Kafka, Strelka (file analysis) + - Data Storage: Elasticsearch, InfluxDB, Redis + - User Interface: Kibana, SOC (custom Security Onion web UI), Kratos/Hydra (auth) + - Management: Salt, Docker, Registry, Nginx + +## Development Environment + +### Prerequisites + +- Linux environment (Oracle Linux or compatible) +- Git +- Docker and Docker Compose +- SaltStack + +### Testing + +Run validation tests: +```bash +cd tests +./validation.sh +``` + +Run Python tests (requires Python 3): +```bash +./pyci.sh salt/sensoroni/files/analyzers/urlhaus +``` + +### Key Files and Directories + +- `/salt`: SaltStack states for all components +- `/setup`: Installation scripts and utilities +- `/pillar`: SaltStack pillar data (configuration) +- `/files`: Additional configuration files +- `/tests`: Test utilities and validation + +## Common Tasks + +### Testing Salt States + +To test a specific Salt state without applying it: +```bash +salt-call state.show_sls +``` + +To apply a Salt state in test mode: +```bash +salt-call state.apply test=True +``` + +### Working with Docker Containers + +View running containers: +```bash +so-status +``` + +Access container logs: +```bash +docker logs +``` + +### Development Workflow + +1. Make code changes +2. Run validation: `./tests/validation.sh` +3. Run Python tests if applicable: `./pyci.sh ` + +## Code Conventions + +- All Bash scripts should pass ShellCheck analysis +- YAML (Salt states and pillars) should be properly formatted +- Python code should pass flake8 checks (configured in pytest.ini) +- Code should match the pre-existing style of Security Onion +- All commits must be signed with a valid key + +## Important Notes + +- Security Onion uses Salt for configuration management +- Most components run as Docker containers +- The project follows a distributed architecture with different node types +- Testing should cover both code functionality and deployment scenarios \ No newline at end of file diff --git a/salt/sensor/files/so-combine-bond b/salt/sensor/files/so-combine-bond new file mode 100644 index 000000000..fdb7dfd4c --- /dev/null +++ b/salt/sensor/files/so-combine-bond @@ -0,0 +1,72 @@ +#!/bin/bash + +# Script to find all interfaces of bond0 and set channel parameters +# Compatible with Oracle Linux 9, Ubuntu, and Debian + +. /usr/sbin/so-common + +{% set NICCHANNELS = salt['pillar.get']('sensor:channels', '1') %} + +# Number of channels to set +CHANNELS={{ NICCHANNELS }} + +# Exit on any error +set -e + +# Check if running as root +if [[ $EUID -ne 0 ]]; then + exit 1 +fi + +# Check if bond0 exists +if ! ip link show bond0 &>/dev/null; then + exit 1 +fi + +# Function to get slave interfaces - works across distributions +get_bond_slaves() { + local bond_name="$1" + local slaves="" + + # Method 1: Try /sys/class/net first (most reliable) + if [ -f "/sys/class/net/$bond_name/bonding/slaves" ]; then + slaves=$(cat "/sys/class/net/$bond_name/bonding/slaves" 2>/dev/null) + fi + + # Method 2: Try /proc/net/bonding (older systems) + if [ -z "$slaves" ] && [ -f "/proc/net/bonding/$bond_name" ]; then + slaves=$(grep "Slave Interface:" "/proc/net/bonding/$bond_name" 2>/dev/null | awk '{print $3}' | tr '\n' ' ') + fi + + # Method 3: Parse ip link output (universal fallback) + if [ -z "$slaves" ]; then + slaves=$(ip -o link show | grep "master $bond_name" | awk -F': ' '{print $2}' | cut -d'@' -f1 | tr '\n' ' ') + fi + + echo "$slaves" +} + +# Get slave interfaces +SLAVES=$(get_bond_slaves bond0) + +if [ -z "$SLAVES" ]; then + exit 1 +fi + +# Process each slave interface +for interface in $SLAVES; do + # Skip if interface doesn't exist + if ! ip link show "$interface" &>/dev/null; then + continue + fi + + # Try combined mode first + if ethtool -L "$interface" combined $CHANNELS &>/dev/null; then + continue + fi + + # Fall back to separate rx/tx + ethtool -L "$interface" rx $CHANNELS tx $CHANNELS &>/dev/null || true +done + +exit 0 From 0858160be224b323f2e3b35f1d67990fb15bdff1 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Wed, 27 Aug 2025 14:51:57 -0400 Subject: [PATCH 18/62] support for modifying nic channels --- salt/manager/tools/sbin/so-minion | 1 + salt/sensor/defaults.yaml | 4 ++++ salt/sensor/init.sls | 17 +++++++++++++++++ salt/sensor/map.jinja | 7 +++++++ salt/sensor/soc_sensor.yaml | 6 ++++++ .../{files => tools/sbin_jinja}/so-combine-bond | 4 +--- 6 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 salt/sensor/defaults.yaml create mode 100644 salt/sensor/map.jinja rename salt/sensor/{files => tools/sbin_jinja}/so-combine-bond (94%) diff --git a/salt/manager/tools/sbin/so-minion b/salt/manager/tools/sbin/so-minion index 34ebdaeec..860faf445 100755 --- a/salt/manager/tools/sbin/so-minion +++ b/salt/manager/tools/sbin/so-minion @@ -454,6 +454,7 @@ function add_sensor_to_minion() { echo "sensor:" echo " interface: '$INTERFACE'" echo " mtu: 9000" + echo " channels: 1" echo "zeek:" echo " enabled: True" echo " config:" diff --git a/salt/sensor/defaults.yaml b/salt/sensor/defaults.yaml new file mode 100644 index 000000000..f071f04ba --- /dev/null +++ b/salt/sensor/defaults.yaml @@ -0,0 +1,4 @@ +sensor: + interface: bond0 + mtu: 9000 + channels: 1 diff --git a/salt/sensor/init.sls b/salt/sensor/init.sls index 9c7e52d62..1d7899b62 100644 --- a/salt/sensor/init.sls +++ b/salt/sensor/init.sls @@ -9,6 +9,8 @@ # in the software, and you may not remove or obscure any functionality in the # software that is protected by the license key." +{% from 'sensor/map.jinja' import SENSORMERGED %} + {% if 'vrt' in salt['pillar.get']('features') and salt['grains.get']('salt-cloud', {}) %} include: @@ -28,3 +30,18 @@ execute_checksum: - name: /etc/NetworkManager/dispatcher.d/pre-up.d/99-so-checksum-offload-disable - onchanges: - file: offload_script + +combine_bond_script: + file.managed: + - name: /usr/sbin/so-combine-bond + - source: salt://sensor/tools/sbin_jinja/so-combine-bond + - mode: 755 + - template: jinja + - defaults: + CHANNELS: {{ SENSORMERGED.channels }} + +execute_combine_bond: + cmd.run: + - name: /usr/sbin/so-combine-bond + - onchanges: + - file: combine_bond_script diff --git a/salt/sensor/map.jinja b/salt/sensor/map.jinja new file mode 100644 index 000000000..beabaa66e --- /dev/null +++ b/salt/sensor/map.jinja @@ -0,0 +1,7 @@ +{# 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. #} + +{% import_yaml 'sensor/defaults.yaml' as SENSORDEFAULTS %} +{% set SENSORMERGED = salt['pillar.get']('sensor', SENSORDEFAULTS.sensor, merge=True) %} diff --git a/salt/sensor/soc_sensor.yaml b/salt/sensor/soc_sensor.yaml index 9ab0c236e..f97c8d849 100644 --- a/salt/sensor/soc_sensor.yaml +++ b/salt/sensor/soc_sensor.yaml @@ -7,3 +7,9 @@ sensor: description: Maximum Transmission Unit (MTU) of the sensor monitoring interface. helpLink: network.html readonly: True + channels: + description: Set the size of the nic channels. This is rarely changed from 1 + helpLink: network.html + forcedType: int + node: True + advanced: True diff --git a/salt/sensor/files/so-combine-bond b/salt/sensor/tools/sbin_jinja/so-combine-bond similarity index 94% rename from salt/sensor/files/so-combine-bond rename to salt/sensor/tools/sbin_jinja/so-combine-bond index fdb7dfd4c..0a8a2e66a 100644 --- a/salt/sensor/files/so-combine-bond +++ b/salt/sensor/tools/sbin_jinja/so-combine-bond @@ -5,10 +5,8 @@ . /usr/sbin/so-common -{% set NICCHANNELS = salt['pillar.get']('sensor:channels', '1') %} - # Number of channels to set -CHANNELS={{ NICCHANNELS }} +CHANNELS={{ CHANNELS }} # Exit on any error set -e From 69a5e1e2f54b0556c3dff3f6ec1257e76ff22c5b Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Wed, 27 Aug 2025 15:14:15 -0400 Subject: [PATCH 19/62] remove md file --- CLAUDE.md | 104 ------------------------------------------------------ 1 file changed, 104 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 76c0577d6..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,104 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Overview - -Security Onion is an open-source network security monitoring (NSM) platform that combines multiple security tools into a unified solution. It's designed for threat hunting, enterprise security monitoring, and log management. The platform integrates tools for intrusion detection, packet capture, log management, and security analytics in a comprehensive security monitoring solution. - -## Architecture - -Security Onion uses a microservice architecture with containerized components: - -- **Deployment Models**: - - Standalone: Single all-in-one instance - - Distributed: Manager/sensor architecture with multiple node types - - Manager: Central management server - - Search Nodes: Data storage and search - - Sensor Nodes: Network monitoring and data collection - - Heavy Nodes: Combined sensor/search capabilities - - IDH (Intrusion Deception Host): Honeypot services - -- **Core Components**: - - Data Collection: Zeek, Suricata, Steno (PCAP), Elastic Agents - - Data Processing: Logstash, Kafka, Strelka (file analysis) - - Data Storage: Elasticsearch, InfluxDB, Redis - - User Interface: Kibana, SOC (custom Security Onion web UI), Kratos/Hydra (auth) - - Management: Salt, Docker, Registry, Nginx - -## Development Environment - -### Prerequisites - -- Linux environment (Oracle Linux or compatible) -- Git -- Docker and Docker Compose -- SaltStack - -### Testing - -Run validation tests: -```bash -cd tests -./validation.sh -``` - -Run Python tests (requires Python 3): -```bash -./pyci.sh salt/sensoroni/files/analyzers/urlhaus -``` - -### Key Files and Directories - -- `/salt`: SaltStack states for all components -- `/setup`: Installation scripts and utilities -- `/pillar`: SaltStack pillar data (configuration) -- `/files`: Additional configuration files -- `/tests`: Test utilities and validation - -## Common Tasks - -### Testing Salt States - -To test a specific Salt state without applying it: -```bash -salt-call state.show_sls -``` - -To apply a Salt state in test mode: -```bash -salt-call state.apply test=True -``` - -### Working with Docker Containers - -View running containers: -```bash -so-status -``` - -Access container logs: -```bash -docker logs -``` - -### Development Workflow - -1. Make code changes -2. Run validation: `./tests/validation.sh` -3. Run Python tests if applicable: `./pyci.sh ` - -## Code Conventions - -- All Bash scripts should pass ShellCheck analysis -- YAML (Salt states and pillars) should be properly formatted -- Python code should pass flake8 checks (configured in pytest.ini) -- Code should match the pre-existing style of Security Onion -- All commits must be signed with a valid key - -## Important Notes - -- Security Onion uses Salt for configuration management -- Most components run as Docker containers -- The project follows a distributed architecture with different node types -- Testing should cover both code functionality and deployment scenarios \ No newline at end of file From e5920b646525e5a393ea2dad585514b85a2eab1a Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Thu, 28 Aug 2025 09:21:20 -0400 Subject: [PATCH 20/62] add managerhype back to whiptail --- setup/so-whiptail | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup/so-whiptail b/setup/so-whiptail index 4c92f6a48..57bd10b8c 100755 --- a/setup/so-whiptail +++ b/setup/so-whiptail @@ -654,9 +654,10 @@ whiptail_install_type_dist_new() { Note: MANAGER is the recommended option for most users. MANAGERSEARCH should only be used in very specific situations. EOM - install_type=$(whiptail --title "$whiptail_title" --menu "$mngr_msg" 20 75 2 \ + install_type=$(whiptail --title "$whiptail_title" --menu "$mngr_msg" 20 75 3 \ "MANAGER" "New grid, requires separate search node(s) " \ "MANAGERSEARCH" "New grid, separate search node(s) are optional " \ + "MANAGERHYPE" "Manager with hypervisor - Security Onion Pro required " \ 3>&1 1>&2 2>&3 ) From 1ea7b3c09ff277fbca42f90f69dddef5987d50ad Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:27:56 -0500 Subject: [PATCH 21/62] es 8.18.6 --- salt/elasticsearch/defaults.yaml | 2 +- salt/kibana/defaults.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/elasticsearch/defaults.yaml b/salt/elasticsearch/defaults.yaml index e08978e0d..8224a2450 100644 --- a/salt/elasticsearch/defaults.yaml +++ b/salt/elasticsearch/defaults.yaml @@ -1,6 +1,6 @@ elasticsearch: enabled: false - version: 8.18.4 + version: 8.18.6 index_clean: true config: action: diff --git a/salt/kibana/defaults.yaml b/salt/kibana/defaults.yaml index 29d9b9bf6..645821b6c 100644 --- a/salt/kibana/defaults.yaml +++ b/salt/kibana/defaults.yaml @@ -22,7 +22,7 @@ kibana: - default - file migrations: - discardCorruptObjects: "8.18.4" + discardCorruptObjects: "8.18.6" telemetry: enabled: False security: From a5675a79fe8e5d3fdee7b5e45fd2ace015b485f3 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 28 Aug 2025 19:45:17 -0500 Subject: [PATCH 22/62] es 8.18.6 pipeline upd --- .../grid-nodes_general/import-evtx-logs.json | 2 +- ...nse.log-1.23.0 => logs-pfsense.log-1.23.1} | 20 +++++++++---------- ...icata => logs-pfsense.log-1.23.1-suricata} | 0 3 files changed, 11 insertions(+), 11 deletions(-) rename salt/elasticsearch/files/ingest/{logs-pfsense.log-1.23.0 => logs-pfsense.log-1.23.1} (95%) rename salt/elasticsearch/files/ingest/{logs-pfsense.log-1.23.0-suricata => logs-pfsense.log-1.23.1-suricata} (100%) diff --git a/salt/elasticfleet/files/integrations/grid-nodes_general/import-evtx-logs.json b/salt/elasticfleet/files/integrations/grid-nodes_general/import-evtx-logs.json index 059e4b8cc..8132f4a09 100644 --- a/salt/elasticfleet/files/integrations/grid-nodes_general/import-evtx-logs.json +++ b/salt/elasticfleet/files/integrations/grid-nodes_general/import-evtx-logs.json @@ -20,7 +20,7 @@ ], "data_stream.dataset": "import", "custom": "", - "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.3.3\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.1.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.3.3\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.3.3\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.1.0\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.5.4\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.1.2\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.5.4\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.5.4\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.1.2\n- add_fields:\n target: data_stream\n fields:\n dataset: import", "tags": [ "import" ] diff --git a/salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.0 b/salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.1 similarity index 95% rename from salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.0 rename to salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.1 index e79b91b26..d3354f363 100644 --- a/salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.0 +++ b/salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.1 @@ -107,61 +107,61 @@ }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-firewall", + "name": "logs-pfsense.log-1.23.1-firewall", "if": "ctx.event.provider == 'filterlog'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-openvpn", + "name": "logs-pfsense.log-1.23.1-openvpn", "if": "ctx.event.provider == 'openvpn'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-ipsec", + "name": "logs-pfsense.log-1.23.1-ipsec", "if": "ctx.event.provider == 'charon'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-dhcp", + "name": "logs-pfsense.log-1.23.1-dhcp", "if": "[\"dhcpd\", \"dhclient\", \"dhcp6c\"].contains(ctx.event.provider)" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-unbound", + "name": "logs-pfsense.log-1.23.1-unbound", "if": "ctx.event.provider == 'unbound'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-haproxy", + "name": "logs-pfsense.log-1.23.1-haproxy", "if": "ctx.event.provider == 'haproxy'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-php-fpm", + "name": "logs-pfsense.log-1.23.1-php-fpm", "if": "ctx.event.provider == 'php-fpm'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-squid", + "name": "logs-pfsense.log-1.23.1-squid", "if": "ctx.event.provider == 'squid'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-snort", + "name": "logs-pfsense.log-1.23.1-snort", "if": "ctx.event.provider == 'snort'" } }, { "pipeline": { - "name": "logs-pfsense.log-1.23.0-suricata", + "name": "logs-pfsense.log-1.23.1-suricata", "if": "ctx.event.provider == 'suricata'" } }, diff --git a/salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.0-suricata b/salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.1-suricata similarity index 100% rename from salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.0-suricata rename to salt/elasticsearch/files/ingest/logs-pfsense.log-1.23.1-suricata From f51cd008f27c69bcc34de92cf8f1626f2fe3fbb8 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Fri, 29 Aug 2025 10:04:56 -0400 Subject: [PATCH 23/62] only manage bond script if bond0 exists --- salt/sensor/init.sls | 2 ++ 1 file changed, 2 insertions(+) diff --git a/salt/sensor/init.sls b/salt/sensor/init.sls index 1d7899b62..ee615bf9b 100644 --- a/salt/sensor/init.sls +++ b/salt/sensor/init.sls @@ -39,6 +39,8 @@ combine_bond_script: - template: jinja - defaults: CHANNELS: {{ SENSORMERGED.channels }} + - onlyif: + - ip link show bond0 execute_combine_bond: cmd.run: From a7a81e98253258257e0d14e269b7beef3498624f Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Fri, 29 Aug 2025 11:05:42 -0400 Subject: [PATCH 24/62] always manage script, only run it if bond0 exists --- salt/sensor/init.sls | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/salt/sensor/init.sls b/salt/sensor/init.sls index ee615bf9b..7d1714c2c 100644 --- a/salt/sensor/init.sls +++ b/salt/sensor/init.sls @@ -39,11 +39,9 @@ combine_bond_script: - template: jinja - defaults: CHANNELS: {{ SENSORMERGED.channels }} - - onlyif: - - ip link show bond0 execute_combine_bond: cmd.run: - name: /usr/sbin/so-combine-bond - - onchanges: - - file: combine_bond_script + - onlyif: + - ip link show bond0 From 19362fe5e57f338e19389e21f027ac09e71d8ca1 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Fri, 29 Aug 2025 11:06:25 -0400 Subject: [PATCH 25/62] Update so-combine-bond --- salt/sensor/tools/sbin_jinja/so-combine-bond | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/sensor/tools/sbin_jinja/so-combine-bond b/salt/sensor/tools/sbin_jinja/so-combine-bond index 0a8a2e66a..ded429470 100644 --- a/salt/sensor/tools/sbin_jinja/so-combine-bond +++ b/salt/sensor/tools/sbin_jinja/so-combine-bond @@ -18,7 +18,7 @@ fi # Check if bond0 exists if ! ip link show bond0 &>/dev/null; then - exit 1 + exit 0 fi # Function to get slave interfaces - works across distributions @@ -48,7 +48,7 @@ get_bond_slaves() { SLAVES=$(get_bond_slaves bond0) if [ -z "$SLAVES" ]; then - exit 1 + exit 0 fi # Process each slave interface From 2181cddf496cd50c0add0a10d88abf488fe53dc0 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Tue, 2 Sep 2025 14:09:55 -0600 Subject: [PATCH 26/62] Move EnableReverseLookup Move EnableReverseLookup and it's annotation from ClientParams to ServerConfig. --- salt/soc/defaults.yaml | 2 +- salt/soc/soc_soc.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index 8ce5d882a..7bb2c1f03 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -1359,6 +1359,7 @@ soc: importUploadDir: /nsm/soc/uploads forceUserOtp: false customReportsPath: /opt/sensoroni/templates/reports/custom + enableReverseLookup: false modules: cases: soc filedatastore: @@ -1566,7 +1567,6 @@ soc: outputPath: /opt/sensoroni/navigator lookbackDays: 3 client: - enableReverseLookup: false docsUrl: /docs/ cheatsheetUrl: /docs/cheatsheet.pdf releaseNotesUrl: /docs/release-notes.html diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index b2f509114..f08bfd52b 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -180,6 +180,9 @@ soc: label: Subgrid Enabled forcedType: bool default: false + enableReverseLookup: + description: Set to true to enable reverse DNS lookups for IP addresses in the SOC UI. + global: True modules: elastalertengine: aiRepoUrl: @@ -577,9 +580,6 @@ soc: label: Folder airgap: *pbRepos client: - enableReverseLookup: - description: Set to true to enable reverse DNS lookups for IP addresses in the SOC UI. - global: True apiTimeoutMs: description: Duration (in milliseconds) to wait for a response from the SOC server API before giving up and showing an error on the SOC UI. global: True From df0b484b452fdc2408742ade0cc7f9dfebba9c40 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Tue, 2 Sep 2025 15:07:13 -0600 Subject: [PATCH 27/62] More Descriptive Description Include instructions for how to add local lookups and a help link. --- salt/soc/soc_soc.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index f08bfd52b..2d0eb3792 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -181,8 +181,9 @@ soc: forcedType: bool default: false enableReverseLookup: - description: Set to true to enable reverse DNS lookups for IP addresses in the SOC UI. + description: "Set to true to enable reverse DNS lookups for IP addresses in the SOC UI. To add your own local lookups, create a CSV file at /nsm/custom-mappings/ip-descriptions.csv on your Manager and populate the file with IP addresses and descriptions as follows: IP, Description. Elasticsearch will then ingest the CSV during the next high state." global: True + helpLink: soc-customization.html#reverse-dns modules: elastalertengine: aiRepoUrl: From e26310d1727807f245e2d94f3caef7689149665b Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:00:03 -0500 Subject: [PATCH 28/62] elastic agent offline alerter Signed-off-by: reyesj2 <94730068+reyesj2@users.noreply.github.com> --- .../elastic-agent-monitor.json | 48 +++++ salt/elasticsearch/defaults.yaml | 64 ++++++ .../so-elastic-agent-monitor.json | 43 ++++ salt/manager/defaults.yaml | 7 + salt/manager/init.sls | 35 ++++ salt/manager/soc_manager.yaml | 30 +++ .../tools/sbin_jinja/so-elastic-agent-monitor | 193 ++++++++++++++++++ 7 files changed, 420 insertions(+) create mode 100644 salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json create mode 100644 salt/elasticsearch/templates/component/elastic-agent/so-elastic-agent-monitor.json create mode 100644 salt/manager/tools/sbin_jinja/so-elastic-agent-monitor diff --git a/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json b/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json new file mode 100644 index 000000000..a7d425b39 --- /dev/null +++ b/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json @@ -0,0 +1,48 @@ +{ + "package": { + "name": "filestream", + "version": "" + }, + "name": "agent-monitor", + "namespace": "", + "description": "", + "policy_ids": [ + "so-grid-nodes_general" + ], + "output_id": null, + "vars": {}, + "inputs": { + "filestream-filestream": { + "enabled": true, + "streams": { + "filestream.generic": { + "enabled": true, + "vars": { + "paths": [ + "/opt/so/log/agents/agent-monitor-*.log" + ], + "data_stream.dataset": "agent-monitor", + "pipeline": "elasticagent.monitor", + "parsers": "", + "exclude_files": [ + "\\.gz$" + ], + "include_files": [], + "processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n- add_fields:\n target: event\n fields:\n module: gridmetrics", + "tags": [], + "recursive_glob": true, + "ignore_older": "72h", + "clean_inactive": -1, + "harvester_limit": 0, + "fingerprint": true, + "fingerprint_offset": 0, + "fingerprint_length": 1024, + "file_identity_native": false, + "exclude_lines": [], + "include_lines": [] + } + } + } + } + } +} diff --git a/salt/elasticsearch/defaults.yaml b/salt/elasticsearch/defaults.yaml index e08978e0d..7e3078ccf 100644 --- a/salt/elasticsearch/defaults.yaml +++ b/salt/elasticsearch/defaults.yaml @@ -1243,6 +1243,70 @@ elasticsearch: set_priority: priority: 50 min_age: 30d + so-logs-agent-monitor: + index_sorting: false + index_template: + composed_of: + - event-mappings + - so-elastic-agent-monitor + - so-fleet_integrations.ip_mappings-1 + - so-fleet_globals-1 + - so-fleet_agent_id_verification-1 + data_stream: + allow_custom_routing: false + hidden: false + ignore_missing_component_templates: + - logs-agent-monitor@custom + index_patterns: + - logs-agent-monitor-* + priority: 501 + template: + mappings: + _meta: + managed: true + managed_by: security_onion + package: + name: elastic_agent + settings: + index: + lifecycle: + name: so-logs-agent-monitor-logs + mapping: + total_fields: + limit: 5000 + number_of_replicas: 0 + sort: + field: '@timestamp' + order: desc + policy: + _meta: + managed: true + managed_by: security_onion + package: + name: elastic_agent + phases: + cold: + actions: + set_priority: + priority: 0 + min_age: 60d + delete: + actions: + delete: {} + min_age: 365d + hot: + actions: + rollover: + max_age: 30d + max_primary_shard_size: 50gb + set_priority: + priority: 100 + min_age: 0ms + warm: + actions: + set_priority: + priority: 50 + min_age: 30d so-logs-elastic_agent_x_apm_server: index_sorting: false index_template: diff --git a/salt/elasticsearch/templates/component/elastic-agent/so-elastic-agent-monitor.json b/salt/elasticsearch/templates/component/elastic-agent/so-elastic-agent-monitor.json new file mode 100644 index 000000000..50440fbed --- /dev/null +++ b/salt/elasticsearch/templates/component/elastic-agent/so-elastic-agent-monitor.json @@ -0,0 +1,43 @@ +{ + "template": { + "mappings": { + "properties": { + "agent": { + "type": "object", + "properties": { + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "last_checkin_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "last_checkin": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "offline_duration_hours": { + "type": "integer" + }, + "policy_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/salt/manager/defaults.yaml b/salt/manager/defaults.yaml index 708900af6..65247d8ff 100644 --- a/salt/manager/defaults.yaml +++ b/salt/manager/defaults.yaml @@ -5,3 +5,10 @@ manager: minute: 0 additionalCA: '' insecureSkipVerify: False + agent_monitoring: + enabled: False + config: + critical_agents: [] + offline_threshold: 5 + page_size: 250 + run_interval: 5 diff --git a/salt/manager/init.sls b/salt/manager/init.sls index 737d753f4..7daaeb8f2 100644 --- a/salt/manager/init.sls +++ b/salt/manager/init.sls @@ -34,6 +34,26 @@ agents_log_dir: - user - group +agents_conf_dir: + file.directory: + - name: /opt/so/conf/agents + - user: root + - group: root + - recurse: + - user + - group + +{% if MANAGERMERGED.agent_monitoring.config.critical_agents | length > 0 %} +critical_agents_patterns: + file.managed: + - name: /opt/so/conf/agents/critical-agents.txt + - contents: {{ MANAGERMERGED.agent_monitoring.config.critical_agents }} +{% else %} +remove_critical_agents_config: + file.absent: + - name: /opt/so/conf/agents/critical-agents.txt +{% endif %} + yara_log_dir: file.directory: - name: /opt/so/log/yarasync @@ -127,6 +147,21 @@ so_fleetagent_status: - month: '*' - dayweek: '*' +so_fleetagent_monitor: +{% if MANAGERMERGED.agent_monitoring.enabled %} + cron.present: +{% else %} + cron.absent: +{% endif %} + - name: /usr/sbin/so-elastic-agent-monitor + - identifier: so_fleetagent_monitor + - user: root + - minute: '*/{{ MANAGERMERGED.agent_monitoring.config.run_interval }}' + - hour: '*' + - daymonth: '*' + - month: '*' + - dayweek: '*' + socore_own_saltstack_default: file.directory: - name: /opt/so/saltstack/default diff --git a/salt/manager/soc_manager.yaml b/salt/manager/soc_manager.yaml index cf78658de..f69f3f42a 100644 --- a/salt/manager/soc_manager.yaml +++ b/salt/manager/soc_manager.yaml @@ -37,3 +37,33 @@ manager: forcedType: bool global: True helpLink: proxy.html + agent_monitoring: + enabled: + description: Enable monitoring elastic agents for health issues. Can be used to trigger an alert when a 'critical' agent hasn't checked in with fleet for longer than the configured offline threshold. + global: True + helpLink: elastic-fleet.html + forcedType: bool + config: + critical_agents: + description: List of 'critical' agents to log when they haven't checked in longer than the maximum allowed time. If there are no 'critical' agents specified all offline agents will be logged once they reach the offline threshold + global: True + multiline: True + helpLink: elastic-fleet.html + forcedType: "[]string" + offline_threshold: + description: The maximum allowed time in hours a 'critical' agent has been offline before being logged. + global: True + helpLink: elastic-fleet.html + forcedType: int + page_size: + description: The amount of agents that can be processed per API request to fleet. + global: True + helpLink: elastic-fleet.html + forcedType: int + advanced: True + run_interval: + description: The time in minutes between checking fleet agent statuses. + global: True + advanced: True + helpLink: elastic-fleet.html + forcedType: int diff --git a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor new file mode 100644 index 000000000..572d4de4d --- /dev/null +++ b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor @@ -0,0 +1,193 @@ +#!/bin/bash + +{% from 'manager/map.jinja' import MANAGERMERGED %} +{%- set OFFLINE_THRESHOLD_HOURS = MANAGERMERGED.agent_monitoring.config.offline_threshold %} +{%- set PAGE_SIZE = MANAGERMERGED.agent_monitoring.config.page_size %} + +set -euo pipefail + +LOG_DIR="/opt/so/log/agents" +LOG_FILE="$LOG_DIR/agent-monitor-$(date -u +"%Y%m%d").log" +CURL_CONFIG="/opt/so/conf/elasticsearch/curl.config" +FLEET_API="http://localhost:5601/api/fleet/agents" +CRITICAL_AGENTS_FILE="/opt/so/conf/agents/critical-agents.txt" + +OFFLINE_THRESHOLD_HOURS={{ OFFLINE_THRESHOLD_HOURS }} +PAGE_SIZE="{{ PAGE_SIZE }}" + +log_message() { + local level="$1" + local message="$2" + echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") [$level] $message" >&2 +} + +matches_critical_pattern() { + local hostname="$1" + local pattern_file="$2" + + # If critical agents file doesn't exist or is empty, match all + if [ ! -f "$pattern_file" ] || [ ! -s "$pattern_file" ]; then + return 0 + fi + + local hostname_lower=$(echo "$hostname" | tr '[:upper:]' '[:lower:]') + + while IFS= read -r pattern || [ -n "$pattern" ]; do + # empty lines and comments + [[ -z "$pattern" || "$pattern" =~ ^[[:space:]]*# ]] && continue + + # cut whitespace + pattern=$(echo "$pattern" | xargs) + + local pattern_lower=$(echo "$pattern" | tr '[:upper:]' '[:lower:]') + + # Replace * with bash wildcard + local bash_pattern="${pattern_lower//\*/.*}" + + # Check if hostname matches the pattern + if [[ "$hostname_lower" =~ ^${bash_pattern}$ ]]; then + return 0 + fi + done < "$pattern_file" + + return 1 +} + +calculate_offline_hours() { + local last_checkin="$1" + local current_time=$(date +%s) + local checkin_time=$(date -d "$last_checkin" +%s 2>/dev/null || echo "0") + + if [ "$checkin_time" -eq "0" ]; then + echo "0" + return + fi + + local diff=$((current_time - checkin_time)) + echo $((diff / 3600)) +} + +cleanup_old_logs() { + # Find and delete log files older than 7 days + local old_files=$(find "$LOG_DIR" -name "agent-monitor-*.log" -type f -mtime +7 2>/dev/null) + + if [ -n "$old_files" ]; then + local deleted_count=$(echo "$old_files" | wc -l) + echo "$old_files" | xargs rm -f + log_message "INFO" "Cleaned up $deleted_count old log files (>7 days)" + fi +} + +main() { + log_message "INFO" "Starting Fleet agent status check" + + # Check if critical agents file is configured + if [ -f "$CRITICAL_AGENTS_FILE" ] && [ -s "$CRITICAL_AGENTS_FILE" ]; then + log_message "INFO" "Using critical agents filter from: $CRITICAL_AGENTS_FILE" + log_message "INFO" "Patterns: $(grep -v '^#' "$CRITICAL_AGENTS_FILE" 2>/dev/null | xargs | tr ' ' ',')" + else + log_message "INFO" "No critical agents filter found, monitoring all agents" + fi + + cleanup_old_logs + + log_message "INFO" "Querying Fleet API" + + local page=1 + local total_agents=0 + local processed_agents=0 + local current_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + while true; do + log_message "INFO" "Fetching page $page (${PAGE_SIZE} agents per page)" + + if ! response_body=$(curl -K "$CURL_CONFIG" \ + -s --fail \ + "${FLEET_API}?perPage=${PAGE_SIZE}&page=${page}&showInactive=true" \ + -H 'kbn-xsrf: true' 2>/dev/null); then + log_message "ERROR" "Failed to query Fleet API (page $page)" + exit 1 + fi + + # pagination info + current_total=$(echo "$response_body" | jq -r '.total // 0') + current_page=$(echo "$response_body" | jq -r '.page // 1') + agents_in_page=$(echo "$response_body" | jq -r '.list | length') + + # Update total + if [ "$page" -eq 1 ]; then + total_agents="$current_total" + log_message "INFO" "Found $total_agents total agents across all pages" + fi + + log_message "INFO" "Processing page $current_page with $agents_in_page agents" + + # Process agents from current page + echo "$response_body" | jq -c '.list[]' | while IFS= read -r agent; do + # Grab agent details + agent_id=$(echo "$agent" | jq -r '.id // "unknown"') + agent_hostname=$(echo "$agent" | jq -r '.local_metadata.host.hostname // "unknown"') + agent_name=$(echo "$agent" | jq -r '.local_metadata.host.name // "unknown"') + agent_status=$(echo "$agent" | jq -r '.status // "unknown"') + last_checkin=$(echo "$agent" | jq -r '.last_checkin // ""') + last_checkin_status=$(echo "$agent" | jq -r '.last_checkin_status // "unknown"') + policy_id=$(echo "$agent" | jq -r '.policy_id // "unknown"') + + # Only log agents that are offline or degraded (skip inactive agents) + # Fleetserver agents can show multiple versions as 'inactive' + if [ "$agent_status" = "offline" ] || [ "$agent_status" = "degraded" ]; then + # Check if agent matches critical agent patterns (if configured) + if ! matches_critical_pattern "$agent_hostname" "$CRITICAL_AGENTS_FILE"; then + continue # Skip this agent if it doesn't match any critical agent pattern + fi + + offline_hours=$(calculate_offline_hours "$last_checkin") + + log_entry=$(jq -c \ + --arg ts "$current_timestamp" \ + --arg id "$agent_id" \ + --arg hostname "$agent_hostname" \ + --arg name "$agent_name" \ + --arg status "$agent_status" \ + --arg last_checkin "$last_checkin" \ + --arg last_checkin_status "$last_checkin_status" \ + --arg policy_id "$policy_id" \ + --arg offline_hours "$offline_hours" \ + '{ + "@timestamp": $ts, + "agent.id": $id, + "agent.hostname": $hostname, + "agent.name": $name, + "agent.status": $status, + "agent.last_checkin": $last_checkin, + "agent.last_checkin_status": $last_checkin_status, + "agent.policy_id": $policy_id, + "agent.offline_duration_hours": ($offline_hours | tonumber) + }') + + echo "$log_entry" >> "$LOG_FILE" + + log_message "INFO" "Logged offline agent: $agent_hostname (status: $agent_status, offline: ${offline_hours}h)" + fi + done + + processed_agents=$((processed_agents + agents_in_page)) + + if [ "$agents_in_page" -eq 0 ] || [ "$processed_agents" -ge "$total_agents" ]; then + log_message "INFO" "Completed processing all pages. Total processed: $processed_agents agents" + break + fi + + page=$((page + 1)) + + # Limit pagination loops incase of any issues. If agent count is high enough increase page_size in SOC manager.agent_monitoring.config.page_size + if [ "$page" -gt 100 ]; then + log_message "ERROR" "Reached maximum page limit (100). Issue with script or extremely large fleet deployment. Consider increasing page_size in SOC -> manager.agent_monitoring.config.page_size" + break + fi + done + + log_message "INFO" "Fleet agent status check completed. Processed $processed_agents out of $total_agents agents" +} + +main "$@" From 38ef4a6046c5613e968057670a9dbe0ad1ba20d0 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Thu, 4 Sep 2025 11:02:27 -0400 Subject: [PATCH 29/62] pass pillar properly --- setup/so-functions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/so-functions b/setup/so-functions index e905a51e7..dbe198958 100755 --- a/setup/so-functions +++ b/setup/so-functions @@ -1232,7 +1232,7 @@ hypervisor_local_states() { info "Running libvirt states for hypervisor" logCmd "salt-call state.apply libvirt.64962 --local --file-root=../salt/ -l info queue=True" info "Setting up bridge for $MNIC" - salt-call state.apply libvirt.bridge --local --file-root=../salt/ -l info pillar="{\"host\": {\"mainint\": \"$MNIC\"}} queue=True" + salt-call state.apply libvirt.bridge --local --file-root=../salt/ -l info pillar='{"host": {"mainint": "'$MNIC'"}}' queue=True if [ $is_managerhype ]; then logCmd "salt-call state.apply salt.minion queue=True" fi From dfec29d18e7981efd66df8e5d639bc4c6c1c7b80 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:37:28 -0500 Subject: [PATCH 30/62] custom kquery --- salt/elasticfleet/defaults.yaml | 1 + .../files/ingest/elasticagent.monitor | 36 ++++++ salt/manager/defaults.yaml | 1 + salt/manager/soc_manager.yaml | 8 +- .../tools/sbin_jinja/so-elastic-agent-monitor | 121 ++++++++++-------- 5 files changed, 113 insertions(+), 54 deletions(-) create mode 100644 salt/elasticsearch/files/ingest/elasticagent.monitor diff --git a/salt/elasticfleet/defaults.yaml b/salt/elasticfleet/defaults.yaml index d6cdd7351..0220428bf 100644 --- a/salt/elasticfleet/defaults.yaml +++ b/salt/elasticfleet/defaults.yaml @@ -38,6 +38,7 @@ elasticfleet: - elasticsearch - endpoint - fleet_server + - filestream - http_endpoint - httpjson - log diff --git a/salt/elasticsearch/files/ingest/elasticagent.monitor b/salt/elasticsearch/files/ingest/elasticagent.monitor new file mode 100644 index 000000000..09d8297c4 --- /dev/null +++ b/salt/elasticsearch/files/ingest/elasticagent.monitor @@ -0,0 +1,36 @@ +{ + "processors": [ + { + "set": { + "field": "event.dataset", + "value": "gridmetrics.agents", + "ignore_failure": true + } + }, + { + "set": { + "field": "event.module", + "value": "gridmetrics", + "ignore_failure": true + } + }, + { + "remove": { + "field": [ + "host", + "elastic_agent", + "agent" + ], + "ignore_missing": true, + "ignore_failure": true + } + }, + { + "json": { + "field": "message", + "add_to_root": true, + "ignore_failure": true + } + } + ] +} \ No newline at end of file diff --git a/salt/manager/defaults.yaml b/salt/manager/defaults.yaml index 65247d8ff..237ac2999 100644 --- a/salt/manager/defaults.yaml +++ b/salt/manager/defaults.yaml @@ -9,6 +9,7 @@ manager: enabled: False config: critical_agents: [] + custom_kquery: offline_threshold: 5 page_size: 250 run_interval: 5 diff --git a/salt/manager/soc_manager.yaml b/salt/manager/soc_manager.yaml index f69f3f42a..ac06ac2b4 100644 --- a/salt/manager/soc_manager.yaml +++ b/salt/manager/soc_manager.yaml @@ -45,11 +45,17 @@ manager: forcedType: bool config: critical_agents: - description: List of 'critical' agents to log when they haven't checked in longer than the maximum allowed time. If there are no 'critical' agents specified all offline agents will be logged once they reach the offline threshold + description: List of 'critical' agents to log when they haven't checked in longer than the maximum allowed time. If there are no 'critical' agents specified all offline agents will be logged once they reach the offline threshold. global: True multiline: True helpLink: elastic-fleet.html forcedType: "[]string" + custom_kquery: + description: For more granular control over what agents to monitor for offline|degraded status add a kquery here. It is recommended to create & test within Elastic Fleet first to ensure your agents are targeted correctly using the query. eg 'status:offline AND tags:INFRA' + global: True + helpLink: elastic-fleet.html + forcedType: string + advanced: True offline_threshold: description: The maximum allowed time in hours a 'critical' agent has been offline before being logged. global: True diff --git a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor index 572d4de4d..0f3bcac34 100644 --- a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor +++ b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor @@ -1,17 +1,21 @@ +{%- from 'manager/map.jinja' import MANAGERMERGED -%} +{%- set OFFLINE_THRESHOLD_HOURS = MANAGERMERGED.agent_monitoring.config.offline_threshold -%} +{%- set PAGE_SIZE = MANAGERMERGED.agent_monitoring.config.page_size -%} +{%- set CUSTOM_KQUERY = MANAGERMERGED.agent_monitoring.config.custom_kquery -%} #!/bin/bash -{% from 'manager/map.jinja' import MANAGERMERGED %} -{%- set OFFLINE_THRESHOLD_HOURS = MANAGERMERGED.agent_monitoring.config.offline_threshold %} -{%- set PAGE_SIZE = MANAGERMERGED.agent_monitoring.config.page_size %} - set -euo pipefail LOG_DIR="/opt/so/log/agents" LOG_FILE="$LOG_DIR/agent-monitor-$(date -u +"%Y%m%d").log" CURL_CONFIG="/opt/so/conf/elasticsearch/curl.config" FLEET_API="http://localhost:5601/api/fleet/agents" +{#- When using custom kquery ignore critical agents patterns. Since we want all the results of custom query logged #} +{%- if CUSTOM_KQUERY != None and CUSTOM_KQUERY | length > 0 %} +CRITICAL_AGENTS_FILE="/dev/null" +{%- else %} CRITICAL_AGENTS_FILE="/opt/so/conf/agents/critical-agents.txt" - +{%- endif %} OFFLINE_THRESHOLD_HOURS={{ OFFLINE_THRESHOLD_HOURS }} PAGE_SIZE="{{ PAGE_SIZE }}" @@ -80,7 +84,7 @@ cleanup_old_logs() { main() { log_message "INFO" "Starting Fleet agent status check" - + # Check if critical agents file is configured if [ -f "$CRITICAL_AGENTS_FILE" ] && [ -s "$CRITICAL_AGENTS_FILE" ]; then log_message "INFO" "Using critical agents filter from: $CRITICAL_AGENTS_FILE" @@ -98,12 +102,20 @@ main() { local processed_agents=0 local current_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + {%- if CUSTOM_KQUERY != None and CUSTOM_KQUERY | length > 0 %} + log_message "INFO" "Using custom kquery: {{ CUSTOM_KQUERY }}" + FLEET_QUERY="${FLEET_API}?kuery={{ CUSTOM_KQUERY | urlencode }}&perPage=${PAGE_SIZE}&page=${page}" + {%- else %} + log_message "INFO" "Using default query (all offline or degraded agents)" + FLEET_QUERY="${FLEET_API}?kuery=status%3Aoffline%20OR%20status%3Adegraded&perPage=${PAGE_SIZE}&page=${page}" + {%- endif %} + while true; do log_message "INFO" "Fetching page $page (${PAGE_SIZE} agents per page)" if ! response_body=$(curl -K "$CURL_CONFIG" \ -s --fail \ - "${FLEET_API}?perPage=${PAGE_SIZE}&page=${page}&showInactive=true" \ + $FLEET_QUERY \ -H 'kbn-xsrf: true' 2>/dev/null); then log_message "ERROR" "Failed to query Fleet API (page $page)" exit 1 @@ -123,52 +135,55 @@ main() { log_message "INFO" "Processing page $current_page with $agents_in_page agents" # Process agents from current page - echo "$response_body" | jq -c '.list[]' | while IFS= read -r agent; do - # Grab agent details - agent_id=$(echo "$agent" | jq -r '.id // "unknown"') - agent_hostname=$(echo "$agent" | jq -r '.local_metadata.host.hostname // "unknown"') - agent_name=$(echo "$agent" | jq -r '.local_metadata.host.name // "unknown"') - agent_status=$(echo "$agent" | jq -r '.status // "unknown"') - last_checkin=$(echo "$agent" | jq -r '.last_checkin // ""') - last_checkin_status=$(echo "$agent" | jq -r '.last_checkin_status // "unknown"') - policy_id=$(echo "$agent" | jq -r '.policy_id // "unknown"') + mapfile -t agents < <(echo "$response_body" | jq -c '.list[]') - # Only log agents that are offline or degraded (skip inactive agents) - # Fleetserver agents can show multiple versions as 'inactive' - if [ "$agent_status" = "offline" ] || [ "$agent_status" = "degraded" ]; then - # Check if agent matches critical agent patterns (if configured) - if ! matches_critical_pattern "$agent_hostname" "$CRITICAL_AGENTS_FILE"; then - continue # Skip this agent if it doesn't match any critical agent pattern + for agent in "${agents[@]}"; do + # Grab agent details + agent_id=$(echo "$agent" | jq -r '.id // "unknown"') + agent_hostname=$(echo "$agent" | jq -r '.local_metadata.host.hostname // "unknown"') + agent_name=$(echo "$agent" | jq -r '.local_metadata.host.name // "unknown"') + agent_status=$(echo "$agent" | jq -r '.status // "unknown"') + last_checkin=$(echo "$agent" | jq -r '.last_checkin // ""') + last_checkin_status=$(echo "$agent" | jq -r '.last_checkin_status // "unknown"') + policy_id=$(echo "$agent" | jq -r '.policy_id // "unknown"') + + # Only log agents that are offline or degraded (skip inactive agents) + # Fleetserver agents can show multiple versions as 'inactive' + if [ "$agent_status" = "offline" ] || [ "$agent_status" = "degraded" ]; then + # Check if agent matches critical agent patterns (if configured) + if ! matches_critical_pattern "$agent_hostname" "$CRITICAL_AGENTS_FILE"; then + log_message "WARN" "${agent_hostname^^} is ${agent_status^^}, but does not match configured critical agents patterns. Not logging ${agent_status^^} agent" + continue # Skip this agent if it doesn't match any critical agent pattern + fi + + offline_hours=$(calculate_offline_hours "$last_checkin") + + log_entry=$(echo 'null' | jq -c \ + --arg ts "$current_timestamp" \ + --arg id "$agent_id" \ + --arg hostname "$agent_hostname" \ + --arg name "$agent_name" \ + --arg status "$agent_status" \ + --arg last_checkin "$last_checkin" \ + --arg last_checkin_status "$last_checkin_status" \ + --arg policy_id "$policy_id" \ + --arg offline_hours "$offline_hours" \ + '{ + "@timestamp": $ts, + "agent.id": $id, + "agent.hostname": $hostname, + "agent.name": $name, + "agent.status": $status, + "agent.last_checkin": $last_checkin, + "agent.last_checkin_status": $last_checkin_status, + "agent.policy_id": $policy_id, + "agent.offline_duration_hours": ($offline_hours | tonumber) + }') + + echo "$log_entry" >> "$LOG_FILE" + + log_message "INFO" "Logged offline agent: $agent_hostname (status: $agent_status, offline: ${offline_hours}h)" fi - - offline_hours=$(calculate_offline_hours "$last_checkin") - - log_entry=$(jq -c \ - --arg ts "$current_timestamp" \ - --arg id "$agent_id" \ - --arg hostname "$agent_hostname" \ - --arg name "$agent_name" \ - --arg status "$agent_status" \ - --arg last_checkin "$last_checkin" \ - --arg last_checkin_status "$last_checkin_status" \ - --arg policy_id "$policy_id" \ - --arg offline_hours "$offline_hours" \ - '{ - "@timestamp": $ts, - "agent.id": $id, - "agent.hostname": $hostname, - "agent.name": $name, - "agent.status": $status, - "agent.last_checkin": $last_checkin, - "agent.last_checkin_status": $last_checkin_status, - "agent.policy_id": $policy_id, - "agent.offline_duration_hours": ($offline_hours | tonumber) - }') - - echo "$log_entry" >> "$LOG_FILE" - - log_message "INFO" "Logged offline agent: $agent_hostname (status: $agent_status, offline: ${offline_hours}h)" - fi done processed_agents=$((processed_agents + agents_in_page)) @@ -180,13 +195,13 @@ main() { page=$((page + 1)) - # Limit pagination loops incase of any issues. If agent count is high enough increase page_size in SOC manager.agent_monitoring.config.page_size + # Limit pagination loops incase of any issues. If agent count is high enough increase page_size in SOC manager.agent_monitoring.config.page_size if [ "$page" -gt 100 ]; then log_message "ERROR" "Reached maximum page limit (100). Issue with script or extremely large fleet deployment. Consider increasing page_size in SOC -> manager.agent_monitoring.config.page_size" break fi done - + log_message "INFO" "Fleet agent status check completed. Processed $processed_agents out of $total_agents agents" } From 915b9e7bd7cc8c8a6aa057a6fe084bc574aa1f91 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:22:44 -0500 Subject: [PATCH 31/62] use logrotate --- salt/logrotate/defaults.yaml | 9 +++++++++ salt/logrotate/soc_logrotate.yaml | 7 +++++++ .../tools/sbin_jinja/so-elastic-agent-monitor | 17 ++--------------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/salt/logrotate/defaults.yaml b/salt/logrotate/defaults.yaml index 2f7247ff2..479b598f5 100644 --- a/salt/logrotate/defaults.yaml +++ b/salt/logrotate/defaults.yaml @@ -268,3 +268,12 @@ logrotate: - nocompress - create - sharedscripts + /opt/so/log/agents/agent-monitor*_x_log: + - daily + - rotate 14 + - missingok + - compress + - create + - extension .log + - dateext + - dateyesterday \ No newline at end of file diff --git a/salt/logrotate/soc_logrotate.yaml b/salt/logrotate/soc_logrotate.yaml index 56f879e4f..6f0272ef0 100644 --- a/salt/logrotate/soc_logrotate.yaml +++ b/salt/logrotate/soc_logrotate.yaml @@ -175,3 +175,10 @@ logrotate: multiline: True global: True forcedType: "[]string" + "/opt/so/log/agents/agent-monitor*_x_log": + description: List of logrotate options for this file. + title: /opt/so/log/agents/agent-monitor*.log + advanced: True + multiline: True + global: True + forcedType: "[]string" diff --git a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor index 0f3bcac34..0b40925fd 100644 --- a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor +++ b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor @@ -7,7 +7,7 @@ set -euo pipefail LOG_DIR="/opt/so/log/agents" -LOG_FILE="$LOG_DIR/agent-monitor-$(date -u +"%Y%m%d").log" +LOG_FILE="$LOG_DIR/agent-monitor.log" CURL_CONFIG="/opt/so/conf/elasticsearch/curl.config" FLEET_API="http://localhost:5601/api/fleet/agents" {#- When using custom kquery ignore critical agents patterns. Since we want all the results of custom query logged #} @@ -71,17 +71,6 @@ calculate_offline_hours() { echo $((diff / 3600)) } -cleanup_old_logs() { - # Find and delete log files older than 7 days - local old_files=$(find "$LOG_DIR" -name "agent-monitor-*.log" -type f -mtime +7 2>/dev/null) - - if [ -n "$old_files" ]; then - local deleted_count=$(echo "$old_files" | wc -l) - echo "$old_files" | xargs rm -f - log_message "INFO" "Cleaned up $deleted_count old log files (>7 days)" - fi -} - main() { log_message "INFO" "Starting Fleet agent status check" @@ -92,8 +81,6 @@ main() { else log_message "INFO" "No critical agents filter found, monitoring all agents" fi - - cleanup_old_logs log_message "INFO" "Querying Fleet API" @@ -115,7 +102,7 @@ main() { if ! response_body=$(curl -K "$CURL_CONFIG" \ -s --fail \ - $FLEET_QUERY \ + "$FLEET_QUERY" \ -H 'kbn-xsrf: true' 2>/dev/null); then log_message "ERROR" "Failed to query Fleet API (page $page)" exit 1 From 348f9dcaec75bcdbbdfa47dd7d2a86f4d73316df Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Fri, 5 Sep 2025 10:01:24 -0500 Subject: [PATCH 32/62] prevent multiple script instances using file lock --- salt/manager/init.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/manager/init.sls b/salt/manager/init.sls index 7daaeb8f2..f59c33652 100644 --- a/salt/manager/init.sls +++ b/salt/manager/init.sls @@ -153,7 +153,7 @@ so_fleetagent_monitor: {% else %} cron.absent: {% endif %} - - name: /usr/sbin/so-elastic-agent-monitor + - name: /bin/flock -n /opt/so/log/agents/agent-monitor.lock /usr/sbin/so-elastic-agent-monitor - identifier: so_fleetagent_monitor - user: root - minute: '*/{{ MANAGERMERGED.agent_monitoring.config.run_interval }}' From 4afc986f484789214fd923a9b633c3f06e218f2c Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Fri, 5 Sep 2025 13:14:47 -0400 Subject: [PATCH 33/62] firewall and logstash pipeline for managerhype --- salt/firewall/defaults.yaml | 16 ++++++++++++++++ salt/firewall/map.jinja | 10 +++++----- salt/logstash/map.jinja | 4 ++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/salt/firewall/defaults.yaml b/salt/firewall/defaults.yaml index 0c43b8c0b..a11492e88 100644 --- a/salt/firewall/defaults.yaml +++ b/salt/firewall/defaults.yaml @@ -1230,6 +1230,10 @@ firewall: portgroups: - elasticsearch_node - elasticsearch_rest + managerhype: + portgroups: + - elasticsearch_node + - elasticsearch_rest standalone: portgroups: - elasticsearch_node @@ -1377,6 +1381,10 @@ firewall: portgroups: - elasticsearch_node - elasticsearch_rest + managerhype: + portgroups: + - elasticsearch_node + - elasticsearch_rest standalone: portgroups: - elasticsearch_node @@ -1579,6 +1587,9 @@ firewall: portgroups: - redis - elastic_agent_data + managerhype: + portgroups: + - elastic_agent_data self: portgroups: - redis @@ -1696,6 +1707,9 @@ firewall: managersearch: portgroups: - openssh + managerhype: + portgroups: + - openssh standalone: portgroups: - openssh @@ -1758,6 +1772,8 @@ firewall: portgroups: [] managersearch: portgroups: [] + managerhype: + portgroups: [] standalone: portgroups: [] customhostgroup0: diff --git a/salt/firewall/map.jinja b/salt/firewall/map.jinja index 4347d2b31..8bd0512ec 100644 --- a/salt/firewall/map.jinja +++ b/salt/firewall/map.jinja @@ -25,7 +25,7 @@ {% set KAFKA_EXTERNAL_ACCESS = salt['pillar.get']('kafka:config:external_access:enabled', default=False) %} {% set kafka_node_type = salt['pillar.get']('kafka:nodes:'+ GLOBALS.hostname + ':role') %} -{% if role in ['manager', 'managersearch', 'standalone'] %} +{% if role.startswith('manager') or role == 'standalone' %} {% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[role].portgroups.append('kafka_controller') %} {% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.receiver.portgroups.append('kafka_controller') %} {% endif %} @@ -38,8 +38,8 @@ {% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.receiver.portgroups.append('kafka_controller') %} {% endif %} -{% if role in ['manager', 'managersearch', 'standalone', 'receiver'] %} -{% for r in ['manager', 'managersearch', 'standalone', 'receiver', 'fleet', 'idh', 'sensor', 'searchnode','heavynode', 'elastic_agent_endpoint', 'desktop'] %} +{% if role.startswith('manager') or role in ['standalone', 'receiver'] %} +{% for r in ['manager', 'managersearch', 'managerhype', 'standalone', 'receiver', 'fleet', 'idh', 'sensor', 'searchnode','heavynode', 'elastic_agent_endpoint', 'desktop'] %} {% if FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[r] is defined %} {% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[r].portgroups.append('kafka_data') %} {% endif %} @@ -48,11 +48,11 @@ {% if KAFKA_EXTERNAL_ACCESS %} {# Kafka external access only applies for Kafka nodes with the broker role. #} -{% if role in ['manager', 'managersearch', 'standalone', 'receiver'] and 'broker' in kafka_node_type %} +{% if role.startswith('manager') or role in ['standalone', 'receiver'] and 'broker' in kafka_node_type %} {% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.external_kafka.portgroups.append('kafka_external_access') %} {% endif %} {% endif %} {% endif %} -{% set FIREWALL_MERGED = salt['pillar.get']('firewall', FIREWALL_DEFAULT.firewall, merge=True) %} \ No newline at end of file +{% set FIREWALL_MERGED = salt['pillar.get']('firewall', FIREWALL_DEFAULT.firewall, merge=True) %} diff --git a/salt/logstash/map.jinja b/salt/logstash/map.jinja index 95ec6b85d..5aad1daa9 100644 --- a/salt/logstash/map.jinja +++ b/salt/logstash/map.jinja @@ -17,7 +17,7 @@ {% for node_type, node_details in redis_node_data.items() | sort %} {% if GLOBALS.role in ['so-searchnode', 'so-standalone', 'so-managersearch', 'so-fleet'] %} -{% if node_type in ['manager', 'managersearch', 'standalone', 'receiver' ] %} +{% if node_type.startswith('manager') or node_type in ['standalone', 'receiver'] %} {% for hostname in redis_node_data[node_type].keys() %} {% do LOGSTASH_REDIS_NODES.append({hostname:node_details[hostname].ip}) %} {% endfor %} @@ -47,7 +47,7 @@ {% endif %} {# Disable logstash on manager & receiver nodes unless it has an override configured #} {% if not KAFKA_LOGSTASH %} -{% if GLOBALS.role in ['so-manager', 'so-receiver'] and GLOBALS.hostname not in KAFKA_LOGSTASH %} +{% if GLOBALS.role in ['so-manager', 'so-managerhype', 'so-receiver'] and GLOBALS.hostname not in KAFKA_LOGSTASH %} {% do LOGSTASH_MERGED.update({'enabled': False}) %} {% endif %} {% endif %} From 207572f2f94c48fd51fbdf18f7dad47830d849c3 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Fri, 5 Sep 2025 14:16:03 -0400 Subject: [PATCH 34/62] remove debug added to fail_setup --- setup/so-functions | 42 ++---------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/setup/so-functions b/setup/so-functions index dbe198958..9ab11a904 100755 --- a/setup/so-functions +++ b/setup/so-functions @@ -29,46 +29,8 @@ title() { } fail_setup() { - local failure_reason="${1:-Unknown failure}" - - # Capture call stack information - local calling_function="${FUNCNAME[1]:-main}" - local calling_line="${BASH_LINENO[0]:-unknown}" - local calling_file="${BASH_SOURCE[1]:-unknown}" - - # Build call stack trace - local call_stack="" - local i=1 - while [[ $i -lt ${#FUNCNAME[@]} ]]; do - local func="${FUNCNAME[$i]}" - local file="${BASH_SOURCE[$i]##*/}" # Get basename only - local line="${BASH_LINENO[$((i-1))]}" - - if [[ -n "$call_stack" ]]; then - call_stack="$call_stack -> " - fi - call_stack="$call_stack$func($file:$line)" - ((i++)) - done - - # Enhanced error logging with call stack - error "FAILURE: Called from $calling_function() at line $calling_line" - error "REASON: $failure_reason" - error "STACK: $call_stack" - error "Setup encountered an unrecoverable failure: $failure_reason" - - # Create detailed failure file with enhanced information - { - echo "SETUP_FAILURE_TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M:%S UTC')" - echo "SETUP_FAILURE_REASON=$failure_reason" - echo "SETUP_CALLING_FUNCTION=$calling_function" - echo "SETUP_CALLING_LINE=$calling_line" - echo "SETUP_CALLING_FILE=${calling_file##*/}" - echo "SETUP_CALL_STACK=$call_stack" - echo "SETUP_LOG_LOCATION=$setup_log" - echo "SETUP_FAILURE_DETAILS=Check $setup_log for complete error details" - } > /root/failure - + error "Setup encountered an unrecoverable failure, exiting" + touch /root/failure exit 1 } From f318a84c1806699f101bc332771bb40567535b62 Mon Sep 17 00:00:00 2001 From: Josh Brower Date: Mon, 8 Sep 2025 09:03:33 -0400 Subject: [PATCH 35/62] Update so-elastic-fleet-reset --- salt/manager/tools/sbin_jinja/so-elastic-fleet-reset | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/manager/tools/sbin_jinja/so-elastic-fleet-reset b/salt/manager/tools/sbin_jinja/so-elastic-fleet-reset index 0b116564d..1e32268da 100644 --- a/salt/manager/tools/sbin_jinja/so-elastic-fleet-reset +++ b/salt/manager/tools/sbin_jinja/so-elastic-fleet-reset @@ -15,6 +15,7 @@ require_manager echo echo "This script will remove the current Elastic Fleet install and all of its data and then rerun Elastic Fleet setup." echo "Deployed Elastic Agents will no longer be enrolled and will need to be reinstalled." +echo "Only the Elastic Fleet instance on the Manager will be reinstalled - dedicated Fleet node config will removed and will need to be reinstalled." echo "This script should only be used as a last resort to reinstall Elastic Fleet." echo echo "If you would like to proceed, then type AGREE and press ENTER." From ec27517bdd7a29f2f0f5532b1c9ba84df0d1ac45 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Fri, 11 Jul 2025 10:37:50 -0600 Subject: [PATCH 36/62] New Config Values New config values with annotations and defaults. Updated Nginx config to allow streaming requests to not be buffered on the way to the client. --- salt/nginx/etc/nginx.conf | 30 +++++++++++++++++------------- salt/soc/defaults.yaml | 4 ++++ salt/soc/soc_soc.yaml | 11 +++++++++++ 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/salt/nginx/etc/nginx.conf b/salt/nginx/etc/nginx.conf index 742f5d08d..caa05bbff 100644 --- a/salt/nginx/etc/nginx.conf +++ b/salt/nginx/etc/nginx.conf @@ -196,19 +196,23 @@ http { } location / { - auth_request /auth/sessions/whoami; - auth_request_set $userid $upstream_http_x_kratos_authenticated_identity_id; - proxy_set_header x-user-id $userid; - proxy_pass http://{{ GLOBALS.manager }}:9822/; - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Proxy ""; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header X-Forwarded-Proto $scheme; + auth_request /auth/sessions/whoami; + auth_request_set $userid $upstream_http_x_kratos_authenticated_identity_id; + proxy_set_header x-user-id $userid; + proxy_pass http://{{ GLOBALS.manager }}:9822/; + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Proxy ""; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_buffering off; + proxy_cache off; + proxy_request_buffering off; } location ~ ^/auth/.*?(login|oidc/callback) { diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index 7bb2c1f03..c86889be7 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -1491,6 +1491,10 @@ soc: - repo: file:///nsm/airgap-resources/playbooks/securityonion-resources-playbooks branch: main folder: securityonion-normalized + assistant: + apiKey: + apiUrl: https://onionai-dev.securityonion.net + model: claude-sonnet salt: queueDir: /opt/sensoroni/queue timeoutMs: 45000 diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index 2d0eb3792..b8133999f 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -580,6 +580,17 @@ soc: - field: folder label: Folder airgap: *pbRepos + assistant: + apiKey: + description: The auth token to be used when reaching out to the AI Assistant. + global: True + apiUrl: + description: The URL of the AI gateway. + advanced: True + global: True + model: + description: The model to use as the AI Assistant + global: True client: apiTimeoutMs: description: Duration (in milliseconds) to wait for a response from the SOC server API before giving up and showing an error on the SOC UI. From ba601c39b37e8d2aa3158849d903753d4af3653f Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Tue, 29 Jul 2025 11:23:28 -0600 Subject: [PATCH 37/62] Rough Go at New Mappings/Settings --- .../component/so/assistant-mappings.json | 48 +++++++++++++++++++ .../component/so/assistant-settings.json | 7 +++ 2 files changed, 55 insertions(+) create mode 100644 salt/elasticsearch/templates/component/so/assistant-mappings.json create mode 100644 salt/elasticsearch/templates/component/so/assistant-settings.json diff --git a/salt/elasticsearch/templates/component/so/assistant-mappings.json b/salt/elasticsearch/templates/component/so/assistant-mappings.json new file mode 100644 index 000000000..ad17ebd66 --- /dev/null +++ b/salt/elasticsearch/templates/component/so/assistant-mappings.json @@ -0,0 +1,48 @@ +{ + "template": { + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + }, + "so_kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "so_operation": { + "ignore_above": 1024, + "type": "keyword" + }, + "so_chat": { + "properties": { + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "content": { + "type": "text" + }, + "conversation_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "createTime": { + "type": "date" + }, + "tool_use_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "userId": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + }, + "_meta": { + "ecs_version": "1.12.2" + } +} diff --git a/salt/elasticsearch/templates/component/so/assistant-settings.json b/salt/elasticsearch/templates/component/so/assistant-settings.json new file mode 100644 index 000000000..0281fa0e1 --- /dev/null +++ b/salt/elasticsearch/templates/component/so/assistant-settings.json @@ -0,0 +1,7 @@ +{ + "template": {}, + "version": 1, + "_meta": { + "description": "default settings for common Security Onion Assistant indices" + } +} From 6323fbf46b3b27bfc6bc8bb0caf9152a04892892 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 30 Jul 2025 11:48:27 -0600 Subject: [PATCH 38/62] Content Object --- .../templates/component/so/assistant-mappings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/salt/elasticsearch/templates/component/so/assistant-mappings.json b/salt/elasticsearch/templates/component/so/assistant-mappings.json index ad17ebd66..f6f552465 100644 --- a/salt/elasticsearch/templates/component/so/assistant-mappings.json +++ b/salt/elasticsearch/templates/component/so/assistant-mappings.json @@ -20,7 +20,8 @@ "type": "keyword" }, "content": { - "type": "text" + "type": "object", + "enabled": false }, "conversation_id": { "ignore_above": 1024, From b1753f86f91b8345151f16de7eb9a06d2145fbe9 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 30 Jul 2025 13:14:09 -0600 Subject: [PATCH 39/62] New Message Structure --- .../component/so/assistant-mappings.json | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/salt/elasticsearch/templates/component/so/assistant-mappings.json b/salt/elasticsearch/templates/component/so/assistant-mappings.json index f6f552465..89a907165 100644 --- a/salt/elasticsearch/templates/component/so/assistant-mappings.json +++ b/salt/elasticsearch/templates/component/so/assistant-mappings.json @@ -23,7 +23,7 @@ "type": "object", "enabled": false }, - "conversation_id": { + "sessionId": { "ignore_above": 1024, "type": "keyword" }, @@ -37,6 +37,54 @@ "userId": { "ignore_above": 1024, "type": "keyword" + }, + "message": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "ignore_above": 1024, + "type": "keyword" + }, + "contentStr": { + "type": "text" + }, + "contentBlocks": { + "type": "nested", + "enabled": false + }, + "stopReason": { + "ignore_above": 1024, + "type": "keyword" + }, + "stopSequence": { + "ignore_above": 1024, + "type": "keyword" + }, + "usage": { + "properties": { + "input_tokens": { + "type": "long" + }, + "output_tokens": { + "type": "long" + }, + "credits": { + "type": "long" + } + } + } + } } } } From cea4eaf0819ba87e7edc2c16622d5ad8ccc891f6 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 6 Aug 2025 09:02:43 -0600 Subject: [PATCH 40/62] Updated Assistant Mapping --- .../templates/component/so/assistant-mappings.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/salt/elasticsearch/templates/component/so/assistant-mappings.json b/salt/elasticsearch/templates/component/so/assistant-mappings.json index 89a907165..3433acbd6 100644 --- a/salt/elasticsearch/templates/component/so/assistant-mappings.json +++ b/salt/elasticsearch/templates/component/so/assistant-mappings.json @@ -30,6 +30,13 @@ "createTime": { "type": "date" }, + "deletedAt": { + "type": "date" + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, "tool_use_id": { "ignore_above": 1024, "type": "keyword" From fc2d450de04c688a1ade7724aad4aa31fe637c5a Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Tue, 26 Aug 2025 09:16:04 -0600 Subject: [PATCH 41/62] Update Settings The apiKey will be built off of the license rather than a new setting. The model is hardcoded for now at the AI Gateway level. We're going to use the investigationPrompt as a trigger for the feature being visible in the UI but by default will be blank for now. --- salt/soc/defaults.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index c86889be7..f89d9e99f 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -1492,9 +1492,8 @@ soc: branch: main folder: securityonion-normalized assistant: - apiKey: apiUrl: https://onionai-dev.securityonion.net - model: claude-sonnet + investigationPrompt: salt: queueDir: /opt/sensoroni/queue timeoutMs: 45000 From 120e61e45cce0bd6796f819d4b96b5bbb2b7e9a3 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Tue, 26 Aug 2025 16:06:14 -0600 Subject: [PATCH 42/62] ClientParams Removed investigation prompt from module settings and moved to client settings, added enabledInSoc. --- salt/soc/defaults.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index f89d9e99f..d847d1d1b 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -1493,7 +1493,6 @@ soc: folder: securityonion-normalized assistant: apiUrl: https://onionai-dev.securityonion.net - investigationPrompt: salt: queueDir: /opt/sensoroni/queue timeoutMs: 45000 @@ -2544,3 +2543,6 @@ soc: - ' -priv' condition: all of selection_* level: 'high' # info | low | medium | high | critical + assistant: + enabledInSoc: false + investigationPrompt: Investigate Alert ID {socid} \ No newline at end of file From 73776f8d11ac14269bf531cc0c6cefbe548413f3 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 27 Aug 2025 12:46:19 -0600 Subject: [PATCH 43/62] Cleaning up New ES Indexes --- salt/elasticsearch/defaults.yaml | 74 +++++++++++++++++++ ...ings.json => assistant-chat-mappings.json} | 0 ...ings.json => assistant-chat-settings.json} | 0 .../so/assistant-session-mappings.json | 44 +++++++++++ .../so/assistant-session-settings.json | 7 ++ 5 files changed, 125 insertions(+) rename salt/elasticsearch/templates/component/so/{assistant-mappings.json => assistant-chat-mappings.json} (100%) rename salt/elasticsearch/templates/component/so/{assistant-settings.json => assistant-chat-settings.json} (100%) create mode 100644 salt/elasticsearch/templates/component/so/assistant-session-mappings.json create mode 100644 salt/elasticsearch/templates/component/so/assistant-session-settings.json diff --git a/salt/elasticsearch/defaults.yaml b/salt/elasticsearch/defaults.yaml index 1200701c9..b5031b9b2 100644 --- a/salt/elasticsearch/defaults.yaml +++ b/salt/elasticsearch/defaults.yaml @@ -284,6 +284,80 @@ elasticsearch: hot: actions: {} min_age: 0ms + so-assistant-chat: + index_sorting: false + index_template: + composed_of: + - assistant-chat-mappings + - assistant-chat-settings + ignore_missing_component_templates: [] + index_patterns: + - so-assistant-chat* + priority: 500 + template: + mappings: + date_detection: false + dynamic_templates: + - strings_as_keyword: + mapping: + ignore_above: 1024 + type: keyword + match_mapping_type: string + settings: + index: + lifecycle: + name: so-assistant-chat-logs + mapping: + total_fields: + limit: 1500 + number_of_replicas: 0 + number_of_shards: 1 + refresh_interval: 1s + sort: + field: '@timestamp' + order: desc + policy: + phases: + hot: + actions: {} + min_age: 0ms + so-assistant-session: + index_sorting: false + index_template: + composed_of: + - assistant-session-mappings + - assistant-session-settings + ignore_missing_component_templates: [] + index_patterns: + - so-assistant-session* + priority: 500 + template: + mappings: + date_detection: false + dynamic_templates: + - strings_as_keyword: + mapping: + ignore_above: 1024 + type: keyword + match_mapping_type: string + settings: + index: + lifecycle: + name: so-assistant-session-logs + mapping: + total_fields: + limit: 1500 + number_of_replicas: 0 + number_of_shards: 1 + refresh_interval: 1s + sort: + field: '@timestamp' + order: desc + policy: + phases: + hot: + actions: {} + min_age: 0ms so-endgame: index_sorting: false index_template: diff --git a/salt/elasticsearch/templates/component/so/assistant-mappings.json b/salt/elasticsearch/templates/component/so/assistant-chat-mappings.json similarity index 100% rename from salt/elasticsearch/templates/component/so/assistant-mappings.json rename to salt/elasticsearch/templates/component/so/assistant-chat-mappings.json diff --git a/salt/elasticsearch/templates/component/so/assistant-settings.json b/salt/elasticsearch/templates/component/so/assistant-chat-settings.json similarity index 100% rename from salt/elasticsearch/templates/component/so/assistant-settings.json rename to salt/elasticsearch/templates/component/so/assistant-chat-settings.json diff --git a/salt/elasticsearch/templates/component/so/assistant-session-mappings.json b/salt/elasticsearch/templates/component/so/assistant-session-mappings.json new file mode 100644 index 000000000..b72bbb389 --- /dev/null +++ b/salt/elasticsearch/templates/component/so/assistant-session-mappings.json @@ -0,0 +1,44 @@ +{ + "template": { + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + }, + "so_kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "so_session": { + "properties": { + "title": { + "ignore_above": 1024, + "type": "keyword" + }, + "sessionId": { + "ignore_above": 1024, + "type": "keyword" + }, + "createTime": { + "type": "date" + }, + "deleteTime": { + "type": "date" + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "userId": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + }, + "_meta": { + "ecs_version": "1.12.2" + } +} diff --git a/salt/elasticsearch/templates/component/so/assistant-session-settings.json b/salt/elasticsearch/templates/component/so/assistant-session-settings.json new file mode 100644 index 000000000..0281fa0e1 --- /dev/null +++ b/salt/elasticsearch/templates/component/so/assistant-session-settings.json @@ -0,0 +1,7 @@ +{ + "template": {}, + "version": 1, + "_meta": { + "description": "default settings for common Security Onion Assistant indices" + } +} From 834e34128d5b3f8d5e0e89176968e89d8464f0d1 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Thu, 28 Aug 2025 16:03:35 -0600 Subject: [PATCH 44/62] Non-dev URL --- salt/soc/defaults.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index d847d1d1b..0e7d8ac99 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -1492,7 +1492,7 @@ soc: branch: main folder: securityonion-normalized assistant: - apiUrl: https://onionai-dev.securityonion.net + apiUrl: https://onionai.securityonion.net salt: queueDir: /opt/sensoroni/queue timeoutMs: 45000 From 0a3ff47008029b369381abf0e4ecc5d7629a7505 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Wed, 3 Sep 2025 12:12:27 -0600 Subject: [PATCH 45/62] Cleanup Annotations Removed fields no longer need annotations. --- salt/soc/soc_soc.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index b8133999f..251a3c037 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -581,16 +581,10 @@ soc: label: Folder airgap: *pbRepos assistant: - apiKey: - description: The auth token to be used when reaching out to the AI Assistant. - global: True apiUrl: description: The URL of the AI gateway. advanced: True global: True - model: - description: The model to use as the AI Assistant - global: True client: apiTimeoutMs: description: Duration (in milliseconds) to wait for a response from the SOC server API before giving up and showing an error on the SOC UI. From 673f9cb544682efe5129727e1c490fce03f69748 Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Thu, 4 Sep 2025 09:20:50 -0600 Subject: [PATCH 46/62] Responding to Feedback --- salt/elasticsearch/defaults.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/salt/elasticsearch/defaults.yaml b/salt/elasticsearch/defaults.yaml index b5031b9b2..9eb4b4901 100644 --- a/salt/elasticsearch/defaults.yaml +++ b/salt/elasticsearch/defaults.yaml @@ -292,8 +292,8 @@ elasticsearch: - assistant-chat-settings ignore_missing_component_templates: [] index_patterns: - - so-assistant-chat* - priority: 500 + - so-assistant-chat-* + priority: 501 template: mappings: date_detection: false @@ -329,8 +329,8 @@ elasticsearch: - assistant-session-settings ignore_missing_component_templates: [] index_patterns: - - so-assistant-session* - priority: 500 + - so-assistant-session-* + priority: 501 template: mappings: date_detection: false From 855b489c4b4ead1ca5273136723ba881ed567750 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 4 Sep 2025 10:39:57 -0500 Subject: [PATCH 47/62] datastream --- salt/elasticsearch/defaults.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/salt/elasticsearch/defaults.yaml b/salt/elasticsearch/defaults.yaml index 9eb4b4901..e51f5ac4e 100644 --- a/salt/elasticsearch/defaults.yaml +++ b/salt/elasticsearch/defaults.yaml @@ -290,6 +290,9 @@ elasticsearch: composed_of: - assistant-chat-mappings - assistant-chat-settings + data_stream: + allow_custom_routing: false + hidden: false ignore_missing_component_templates: [] index_patterns: - so-assistant-chat-* @@ -327,6 +330,9 @@ elasticsearch: composed_of: - assistant-session-mappings - assistant-session-settings + data_stream: + allow_custom_routing: false + hidden: false ignore_missing_component_templates: [] index_patterns: - so-assistant-session-* From 12959d114c103de5050f33081ea94211f869baad Mon Sep 17 00:00:00 2001 From: Matthew Wright Date: Thu, 4 Sep 2025 16:36:51 -0400 Subject: [PATCH 48/62] added threshold config fields for assistant --- salt/soc/defaults.yaml | 8 +++++++- salt/soc/soc_soc.yaml | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index 0e7d8ac99..fe50fced7 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -2545,4 +2545,10 @@ soc: level: 'high' # info | low | medium | high | critical assistant: enabledInSoc: false - investigationPrompt: Investigate Alert ID {socid} \ No newline at end of file + investigationPrompt: Investigate Alert ID {socid} + contextLimitSmall: 200000 + contextLimitLarge: 1000000 + thresholdColorRatioLow: 0.5 + thresholdColorRatioMed: 0.75 + thresholdColorRatioMax: 1 + lowBalanceColorAlert: 500000 \ No newline at end of file diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index 251a3c037..cde5996ee 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -586,6 +586,36 @@ soc: advanced: True global: True client: + assistant: + enabledInSoc: + description: Set to true to enable the Onion AI assistant in SOC. + global: True + investigationPrompt: + description: Prompt given to Onion AI when beginning an investigation. + global: True + contextLimitSmall: + description: Smaller context limit for Onion AI. + global: True + advanced: True + contextLimitLarge: + description: Larger context limit for Onion AI. + global: True + advanced: True + thresholdColorRatioLow: + description: Lower visual context color change threshold. + global: True + advanced: True + thresholdColorRatioMed: + description: Middle visual context color change threshold. + global: True + advanced: True + thresholdColorRatioMax: + description: Max visual context color change threshold. + global: True + advanced: True + lowBalanceColorAlert: + description: Onion AI credit amount at which balance turns red. + advanced: True apiTimeoutMs: description: Duration (in milliseconds) to wait for a response from the SOC server API before giving up and showing an error on the SOC UI. global: True From aa43177d8c48af6c4d6170d087689b6dfdfcb24f Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Fri, 5 Sep 2025 11:31:08 -0600 Subject: [PATCH 49/62] Fix Setting Name enabledInSoc => enabled --- salt/soc/defaults.yaml | 2 +- salt/soc/soc_soc.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index fe50fced7..58b3a3827 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -2544,7 +2544,7 @@ soc: condition: all of selection_* level: 'high' # info | low | medium | high | critical assistant: - enabledInSoc: false + enabled: false investigationPrompt: Investigate Alert ID {socid} contextLimitSmall: 200000 contextLimitLarge: 1000000 diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index cde5996ee..4af20d444 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -587,7 +587,7 @@ soc: global: True client: assistant: - enabledInSoc: + enabled: description: Set to true to enable the Onion AI assistant in SOC. global: True investigationPrompt: From 9f7bcb0f7d76695f3f674144e5a56063544f10a0 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:13:11 -0500 Subject: [PATCH 50/62] add --force flag to so-kafka-fleet-output-policy & default to using fleet secret storage for client key --- .../sbin_jinja/so-kafka-fleet-output-policy | 79 +++++++++++++------ 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy b/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy index a5ea79922..2e88d00b2 100644 --- a/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy +++ b/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy @@ -5,46 +5,77 @@ # Elastic License 2.0. {% from 'vars/globals.map.jinja' import GLOBALS %} -{% if GLOBALS.role in ['so-manager', 'so-standalone', 'so-managersearch'] %} +{% if GLOBALS.role in ['so-manager', 'so-standalone', 'so-managersearch', 'so-managerhype'] %} . /usr/sbin/so-common +force=false +while [[ $# -gt 0 ]]; do + case $1 in + -f|--force) + force=true + shift + ;; + *) + echo "Unknown option $1" + echo "Usage: $0 [-f|--force]" + exit 1 + ;; + esac +done + # Check to make sure that Kibana API is up & ready RETURN_CODE=0 wait_for_web_response "http://localhost:5601/api/fleet/settings" "fleet" 300 "curl -K /opt/so/conf/elasticsearch/curl.config" RETURN_CODE=$? if [[ "$RETURN_CODE" != "0" ]]; then - printf "Kibana API not accessible, can't setup Elastic Fleet output policy for Kafka..." - exit 1 + echo -e "\nKibana API not accessible, can't setup Elastic Fleet output policy for Kafka...\n" + exit 1 fi -output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs" | jq -r .items[].id) +KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt) +KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key) +KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt) +KAFKA_OUTPUT_VERSION="2.6.0" -if ! echo "$output" | grep -q "so-manager_kafka"; then - KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt) - KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key) - KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt) - KAFKA_OUTPUT_VERSION="2.6.0" +if ! kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" --fail 2>/dev/null); then + # Create a new output policy for Kafka. Default is disabled 'is_default: false & is_default_monitoring: false' JSON_STRING=$( jq -n \ - --arg KAFKACRT "$KAFKACRT" \ - --arg KAFKAKEY "$KAFKAKEY" \ - --arg KAFKACA "$KAFKACA" \ - --arg MANAGER_IP "{{ GLOBALS.manager_ip }}:9092" \ - --arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \ - '{ "name": "grid-kafka", "id": "so-manager_kafka", "type": "kafka", "hosts": [ $MANAGER_IP ], "is_default": false, "is_default_monitoring": false, "config_yaml": "", "ssl": { "certificate_authorities": [ $KAFKACA ], "certificate": $KAFKACRT, "key": $KAFKAKEY, "verification_mode": "full" }, "proxy_id": null, "client_id": "Elastic", "version": $KAFKA_OUTPUT_VERSION, "compression": "none", "auth_type": "ssl", "partition": "round_robin", "round_robin": { "group_events": 10 }, "topics":[{"topic":"default-securityonion"}], "headers": [ { "key": "", "value": "" } ], "timeout": 30, "broker_timeout": 30, "required_acks": 1 }' - ) - curl -sK /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/outputs" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" -o /dev/null - refresh_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs" | jq -r .items[].id) - - if ! echo "$refresh_output" | grep -q "so-manager_kafka"; then - echo -e "\nFailed to setup Elastic Fleet output policy for Kafka...\n" + --arg KAFKACRT "$KAFKACRT" \ + --arg KAFKAKEY "$KAFKAKEY" \ + --arg KAFKACA "$KAFKACA" \ + --arg MANAGER_IP "{{ GLOBALS.manager_ip }}:9092" \ + --arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \ + '{"name":"grid-kafka", "id":"so-manager_kafka","type":"kafka","hosts":[ $MANAGER_IP ],"is_default":false,"is_default_monitoring":false,"config_yaml":"","ssl":{"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"proxy_id":null,"client_id":"Elastic","version": $KAFKA_OUTPUT_VERSION ,"compression":"none","auth_type":"ssl","partition":"round_robin","round_robin":{"group_events":10},"topics":[{"topic":"default-securityonion"}],"headers":[{"key":"","value":""}],"timeout":30,"broker_timeout":30,"required_acks":1,"secrets":{"ssl":{"key": $KAFKAKEY }}}' + ) + if ! response=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/outputs" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" --fail 2>/dev/null); then + echo -e "\nFailed to setup Elastic Fleet output policy for Kafka...\n" + exit 1 + else + echo -e "\nSuccessfully setup Elastic Fleet output policy for Kafka...\n" + exit 0 + fi +elif kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" --fail 2>/dev/null) && [[ "$force" == "true" ]]; then + # force an update to Kafka policy. Keep the current value of Kafka output policy (enabled/disabled). + ENABLED_DISABLED=$(echo "$kafka_output" | jq -e .item.is_default) + JSON_STRING=$( jq -n \ + --arg KAFKACRT "$KAFKACRT" \ + --arg KAFKAKEY "$KAFKAKEY" \ + --arg KAFKACA "$KAFKACA" \ + --arg MANAGER_IP "{{ GLOBALS.manager_ip }}:9092" \ + --arg ENABLED_DISABLED "$ENABLED_DISABLED"\ + --arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \ + '{"name":"grid-kafka","type":"kafka","hosts":[ $MANAGER_IP ],"is_default":$ENABLED_DISABLED,"is_default_monitoring":$ENABLED_DISABLED,"config_yaml":"","ssl":{"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"proxy_id":null,"client_id":"Elastic","version": $KAFKA_OUTPUT_VERSION ,"compression":"none","auth_type":"ssl","partition":"round_robin","round_robin":{"group_events":10},"topics":[{"topic":"default-securityonion"}],"headers":[{"key":"","value":""}],"timeout":30,"broker_timeout":30,"required_acks":1,"secrets":{"ssl":{"key": $KAFKAKEY }}}' + ) + if ! response=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" --fail 2>/dev/null); then + echo -e "\nFailed to force update to Elastic Fleet output policy for Kafka...\n" exit 1 - elif echo "$refresh_output" | grep -q "so-manager_kafka"; then - echo -e "\nSuccessfully setup Elastic Fleet output policy for Kafka...\n" + else + echo -e "\nForced update to Elastic Fleet output policy for Kafka...\n" fi -elif echo "$output" | grep -q "so-manager_kafka"; then +else echo -e "\nElastic Fleet output policy for Kafka already exists...\n" fi {% else %} From 665527641068620ed7f648ddc286f81ea551e7b1 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:13:29 -0500 Subject: [PATCH 51/62] force update to kafka-fleet-output-policy --- salt/manager/tools/sbin/soup | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/salt/manager/tools/sbin/soup b/salt/manager/tools/sbin/soup index f7180c46c..1d9f94889 100755 --- a/salt/manager/tools/sbin/soup +++ b/salt/manager/tools/sbin/soup @@ -602,7 +602,9 @@ post_to_2.4.170() { } post_to_2.4.180() { - echo "Nothing to apply" + # Force update to Kafka output policy + /usr/sbin/so-kafka-fleet-output-policy --force + POSTVERSION=2.4.180 } From 2535ae953d517d4eb2bd51b784fe9d21b327a99e Mon Sep 17 00:00:00 2001 From: Corey Ogburn Date: Tue, 9 Sep 2025 14:00:01 -0600 Subject: [PATCH 52/62] Fix Index Patterns so-assistant-chat and so-assistant-session both had templates with a trailing dash that prevented the pattern from applying to the name of the indices. --- salt/elasticsearch/defaults.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/elasticsearch/defaults.yaml b/salt/elasticsearch/defaults.yaml index e51f5ac4e..db4fc0515 100644 --- a/salt/elasticsearch/defaults.yaml +++ b/salt/elasticsearch/defaults.yaml @@ -295,7 +295,7 @@ elasticsearch: hidden: false ignore_missing_component_templates: [] index_patterns: - - so-assistant-chat-* + - so-assistant-chat* priority: 501 template: mappings: @@ -335,7 +335,7 @@ elasticsearch: hidden: false ignore_missing_component_templates: [] index_patterns: - - so-assistant-session-* + - so-assistant-session* priority: 501 template: mappings: From 8f36d2ec007877fa8b70c40f422bd621085ed169 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:38:50 -0500 Subject: [PATCH 53/62] update log file name --- .../integrations/grid-nodes_general/elastic-agent-monitor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json b/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json index a7d425b39..31b004a91 100644 --- a/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json +++ b/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json @@ -19,7 +19,7 @@ "enabled": true, "vars": { "paths": [ - "/opt/so/log/agents/agent-monitor-*.log" + "/opt/so/log/agents/agent-monitor.log" ], "data_stream.dataset": "agent-monitor", "pipeline": "elasticagent.monitor", From 29980ea95827a316e360031315594fd9ce3144b7 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:39:55 -0500 Subject: [PATCH 54/62] offline threshold check --- salt/manager/tools/sbin_jinja/so-elastic-agent-monitor | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor index 0b40925fd..3fa3221c2 100644 --- a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor +++ b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor @@ -145,6 +145,11 @@ main() { offline_hours=$(calculate_offline_hours "$last_checkin") + if [ "$offline_hours" -lt "$OFFLINE_THRESHOLD_HOURS" ]; then + log_message "INFO" "${agent_hostname^^} has been offline for ${offline_hours}h (threshold: ${OFFLINE_THRESHOLD_HOURS}h). Not logging ${agent_status^^} agent until it reaches threshold" + continue + fi + log_entry=$(echo 'null' | jq -c \ --arg ts "$current_timestamp" \ --arg id "$agent_id" \ From 4107fa006ff561b8f0d87328c960e1ecac5989ee Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Tue, 9 Sep 2025 16:51:42 -0400 Subject: [PATCH 55/62] fix repo files to remove --- salt/repo/client/map.jinja | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/repo/client/map.jinja b/salt/repo/client/map.jinja index 94228c300..2c040c3c5 100644 --- a/salt/repo/client/map.jinja +++ b/salt/repo/client/map.jinja @@ -26,9 +26,9 @@ 'rocky-devel.repo', 'rocky-extras.repo', 'rocky.repo', - 'oracle-linux-ol9', - 'uek-ol9', - 'virt-oll9' + 'oracle-linux-ol9.repo', + 'uek-ol9.repo', + 'virt-ol9.repo' ] %} {% else %} From f5ec1d4b7c4fce5f04ca18b1c807f555fee7f448 Mon Sep 17 00:00:00 2001 From: Josh Patterson Date: Wed, 10 Sep 2025 09:09:02 -0400 Subject: [PATCH 56/62] don't show sensoroni config changes --- salt/sensoroni/config.sls | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/sensoroni/config.sls b/salt/sensoroni/config.sls index 8298209f1..225d0ddb4 100644 --- a/salt/sensoroni/config.sls +++ b/salt/sensoroni/config.sls @@ -18,6 +18,7 @@ sensoroniagentconf: - group: 939 - mode: 600 - template: jinja + - show_changes: False analyzersdir: file.directory: From fbdc0c470570c6cd30a98ad6898e8c8ce4828959 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:56:09 -0500 Subject: [PATCH 57/62] add configurable realert threshold per agent --- salt/manager/defaults.yaml | 1 + salt/manager/soc_manager.yaml | 5 ++ .../tools/sbin_jinja/so-elastic-agent-monitor | 54 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/salt/manager/defaults.yaml b/salt/manager/defaults.yaml index 237ac2999..239075f74 100644 --- a/salt/manager/defaults.yaml +++ b/salt/manager/defaults.yaml @@ -11,5 +11,6 @@ manager: critical_agents: [] custom_kquery: offline_threshold: 5 + realert_threshold: 5 page_size: 250 run_interval: 5 diff --git a/salt/manager/soc_manager.yaml b/salt/manager/soc_manager.yaml index ac06ac2b4..f0d699f58 100644 --- a/salt/manager/soc_manager.yaml +++ b/salt/manager/soc_manager.yaml @@ -61,6 +61,11 @@ manager: global: True helpLink: elastic-fleet.html forcedType: int + realert_threshold: + description: The time to pass before another alert for an offline agent exceeding the offline_threshold is generated. + global: True + helpLink: elastic-fleet.html + forcedType: int page_size: description: The amount of agents that can be processed per API request to fleet. global: True diff --git a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor index 3fa3221c2..f8c3162b4 100644 --- a/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor +++ b/salt/manager/tools/sbin_jinja/so-elastic-agent-monitor @@ -2,6 +2,7 @@ {%- set OFFLINE_THRESHOLD_HOURS = MANAGERMERGED.agent_monitoring.config.offline_threshold -%} {%- set PAGE_SIZE = MANAGERMERGED.agent_monitoring.config.page_size -%} {%- set CUSTOM_KQUERY = MANAGERMERGED.agent_monitoring.config.custom_kquery -%} +{%- set REALERT_THRESHOLD = MANAGERMERGED.agent_monitoring.config.realert_threshold -%} #!/bin/bash set -euo pipefail @@ -17,6 +18,7 @@ CRITICAL_AGENTS_FILE="/dev/null" CRITICAL_AGENTS_FILE="/opt/so/conf/agents/critical-agents.txt" {%- endif %} OFFLINE_THRESHOLD_HOURS={{ OFFLINE_THRESHOLD_HOURS }} +REALERT_THRESHOLD={{ REALERT_THRESHOLD }} PAGE_SIZE="{{ PAGE_SIZE }}" log_message() { @@ -71,6 +73,52 @@ calculate_offline_hours() { echo $((diff / 3600)) } +check_recent_log_entries() { + local agent_hostname="$1" + + if [ ! -f "$LOG_FILE" ]; then + return 1 + fi + + local current_time=$(date +%s) + local threshold_seconds=$((REALERT_THRESHOLD * 3600)) + local agent_hostname_lower=$(echo "$agent_hostname" | tr '[:upper:]' '[:lower:]') + local most_recent_timestamp="" + + while IFS= read -r line; do + [ -z "$line" ] && continue + + local logged_hostname=$(echo "$line" | jq -r '.["agent.hostname"] // empty' 2>/dev/null) + local logged_timestamp=$(echo "$line" | jq -r '.["@timestamp"] // empty' 2>/dev/null) + + [ -z "$logged_hostname" ] || [ -z "$logged_timestamp" ] && continue + + local logged_hostname_lower=$(echo "$logged_hostname" | tr '[:upper:]' '[:lower:]') + + if [ "$logged_hostname_lower" = "$agent_hostname_lower" ]; then + most_recent_timestamp="$logged_timestamp" + fi + done < <(tail -n 1000 "$LOG_FILE" 2>/dev/null) + + # If there is agent entry (within last 1000), check the time difference + if [ -n "$most_recent_timestamp" ]; then + local logged_time=$(date -d "$most_recent_timestamp" +%s 2>/dev/null || echo "0") + + if [ "$logged_time" -ne "0" ]; then + local time_diff=$((current_time - logged_time)) + local hours_diff=$((time_diff / 3600)) + + # Skip if last agent timestamp was more recent than realert threshold + if ((hours_diff < REALERT_THRESHOLD)); then + return 0 + fi + fi + fi + + # Agent has not been logged within realert threshold + return 1 +} + main() { log_message "INFO" "Starting Fleet agent status check" @@ -150,6 +198,12 @@ main() { continue fi + # Check if this agent was already logged within the realert_threshold + if check_recent_log_entries "$agent_hostname"; then + log_message "INFO" "Skipping $agent_hostname (status: $agent_status) - already logged within last ${REALERT_THRESHOLD}h" + continue + fi + log_entry=$(echo 'null' | jq -c \ --arg ts "$current_timestamp" \ --arg id "$agent_id" \ From 8dc0f8d20e21ded972c3f69125b6bc8d996fb7c9 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:49:30 -0500 Subject: [PATCH 58/62] fix elastic agent ssl unpack error --- .../so-elastic-fleet-outputs-update | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/salt/elasticfleet/tools/sbin_jinja/so-elastic-fleet-outputs-update b/salt/elasticfleet/tools/sbin_jinja/so-elastic-fleet-outputs-update index b5d6e1bfe..5e2990af5 100644 --- a/salt/elasticfleet/tools/sbin_jinja/so-elastic-fleet-outputs-update +++ b/salt/elasticfleet/tools/sbin_jinja/so-elastic-fleet-outputs-update @@ -23,14 +23,20 @@ function update_logstash_outputs() { } function update_kafka_outputs() { # Make sure SSL configuration is included in policy updates for Kafka output. SSL is configured in so-elastic-fleet-setup - SSL_CONFIG=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" | jq -r '.item.ssl') - - JSON_STRING=$(jq -n \ - --arg UPDATEDLIST "$NEW_LIST_JSON" \ - --argjson SSL_CONFIG "$SSL_CONFIG" \ - '{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": $SSL_CONFIG}') - # Update Kafka outputs - curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" | jq + if kafka_policy=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" --fail 2>/dev/null); then + SSL_CONFIG=$(echo "$kafka_policy" | jq -r '.item.ssl') + SECRETS=$(echo "$kafka_policy" | jq -r '.item.secrets') + JSON_STRING=$(jq -n \ + --arg UPDATEDLIST "$NEW_LIST_JSON" \ + --argjson SSL_CONFIG "$SSL_CONFIG" \ + --argjson SECRETS "$SECRETS" \ + '{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": $SSL_CONFIG,"secrets": $SECRETS}') + # Update Kafka outputs + curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" | jq + else + printf "Failed to get current Kafka output policy..." + exit 1 + fi } {% if GLOBALS.pipeline == "KAFKA" %} From 890f76e45cfd1023f84f00263b9bb66474e79f99 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Wed, 10 Sep 2025 20:21:11 -0500 Subject: [PATCH 59/62] avoid delay in log ingest after a forced kafka output policy update --- .../tools/sbin_jinja/so-kafka-fleet-output-policy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy b/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy index 2e88d00b2..d44a5cb6c 100644 --- a/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy +++ b/salt/elasticfleet/tools/sbin_jinja/so-kafka-fleet-output-policy @@ -59,14 +59,15 @@ if ! kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://l elif kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" --fail 2>/dev/null) && [[ "$force" == "true" ]]; then # force an update to Kafka policy. Keep the current value of Kafka output policy (enabled/disabled). ENABLED_DISABLED=$(echo "$kafka_output" | jq -e .item.is_default) + HOSTS=$(echo "$kafka_output" | jq -r '.item.hosts') JSON_STRING=$( jq -n \ --arg KAFKACRT "$KAFKACRT" \ --arg KAFKAKEY "$KAFKAKEY" \ --arg KAFKACA "$KAFKACA" \ - --arg MANAGER_IP "{{ GLOBALS.manager_ip }}:9092" \ --arg ENABLED_DISABLED "$ENABLED_DISABLED"\ --arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \ - '{"name":"grid-kafka","type":"kafka","hosts":[ $MANAGER_IP ],"is_default":$ENABLED_DISABLED,"is_default_monitoring":$ENABLED_DISABLED,"config_yaml":"","ssl":{"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"proxy_id":null,"client_id":"Elastic","version": $KAFKA_OUTPUT_VERSION ,"compression":"none","auth_type":"ssl","partition":"round_robin","round_robin":{"group_events":10},"topics":[{"topic":"default-securityonion"}],"headers":[{"key":"","value":""}],"timeout":30,"broker_timeout":30,"required_acks":1,"secrets":{"ssl":{"key": $KAFKAKEY }}}' + --argjson HOSTS "$HOSTS" \ + '{"name":"grid-kafka","type":"kafka","hosts":$HOSTS,"is_default":$ENABLED_DISABLED,"is_default_monitoring":$ENABLED_DISABLED,"config_yaml":"","ssl":{"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"proxy_id":null,"client_id":"Elastic","version": $KAFKA_OUTPUT_VERSION ,"compression":"none","auth_type":"ssl","partition":"round_robin","round_robin":{"group_events":10},"topics":[{"topic":"default-securityonion"}],"headers":[{"key":"","value":""}],"timeout":30,"broker_timeout":30,"required_acks":1,"secrets":{"ssl":{"key": $KAFKAKEY }}}' ) if ! response=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" --fail 2>/dev/null); then echo -e "\nFailed to force update to Elastic Fleet output policy for Kafka...\n" From a7651b27346c71d9ef18861883d48ee108504ac9 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:30:49 -0500 Subject: [PATCH 60/62] lower filestream fingerprint length --- .../integrations/grid-nodes_general/elastic-agent-monitor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json b/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json index 31b004a91..344d7b3bc 100644 --- a/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json +++ b/salt/elasticfleet/files/integrations/grid-nodes_general/elastic-agent-monitor.json @@ -36,7 +36,7 @@ "harvester_limit": 0, "fingerprint": true, "fingerprint_offset": 0, - "fingerprint_length": 1024, + "fingerprint_length": 64, "file_identity_native": false, "exclude_lines": [], "include_lines": [] From 588a1b86d19aecc15e94b1cd5163f6a1f198dcb8 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:46:45 -0500 Subject: [PATCH 61/62] suricata metadata index rollover 1d -> 30d --- salt/elasticsearch/defaults.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/elasticsearch/defaults.yaml b/salt/elasticsearch/defaults.yaml index db4fc0515..9ad9ccd0c 100644 --- a/salt/elasticsearch/defaults.yaml +++ b/salt/elasticsearch/defaults.yaml @@ -4175,7 +4175,7 @@ elasticsearch: hot: actions: rollover: - max_age: 1d + max_age: 30d max_primary_shard_size: 50gb set_priority: priority: 100 From 0f235baa7ea0494f892735f857702309b3e919be Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:14:43 -0500 Subject: [PATCH 62/62] receiver custom fqdn --- pillar/top.sls | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pillar/top.sls b/pillar/top.sls index b15038e5e..3557b8706 100644 --- a/pillar/top.sls +++ b/pillar/top.sls @@ -263,6 +263,8 @@ base: - kafka.nodes - kafka.soc_kafka - stig.soc_stig + - elasticfleet.soc_elasticfleet + - elasticfleet.adv_elasticfleet '*_import': - node_data.ips