diff --git a/salt/sensoroni/config.sls b/salt/sensoroni/config.sls index f983fce38..8298209f1 100644 --- a/salt/sensoroni/config.sls +++ b/salt/sensoroni/config.sls @@ -43,6 +43,22 @@ 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 + - file_mode: 664 + - show_changes: False + sensoroni_sbin: file.recurse: - name: /usr/sbin diff --git a/salt/sensoroni/enabled.sls b/salt/sensoroni/enabled.sls index 3f05568a0..4b0b6b317 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:ro - /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/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 new file mode 100644 index 000000000..5cabd8f88 --- /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 Custom 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..49f18e7c6 --- /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..17afd1169 --- /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..c2978b1af 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 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 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 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 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 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 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 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 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 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 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 + 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 0e3e50240..8ce5d882a 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -1358,6 +1358,7 @@ soc: htmlDir: html importUploadDir: /nsm/soc/uploads forceUserOtp: false + customReportsPath: /opt/sensoroni/templates/reports/custom modules: cases: soc filedatastore: @@ -1576,6 +1577,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..fdf4d1e6e 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: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 diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index 174368cbb..b2f509114 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: | @@ -608,6 +613,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.