mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-01-12 03:03:09 +01:00
Compare commits
115 Commits
822c411e83
...
2.4/dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c99dd4e44f | ||
|
|
541b8b288d | ||
|
|
db168a0452 | ||
|
|
aa96cf44d4 | ||
|
|
0d59c35d2a | ||
|
|
8463bde90d | ||
|
|
150c31009e | ||
|
|
ee66d6c7d1 | ||
|
|
3effd30f7e | ||
|
|
c075b5a1a7 | ||
|
|
cb1e59fa49 | ||
|
|
588aa435ec | ||
|
|
752c764066 | ||
|
|
af604c2ea8 | ||
|
|
605797c86a | ||
|
|
1ee5b1611a | ||
|
|
5028729e4c | ||
|
|
ab00fa8809 | ||
|
|
2d705e7caa | ||
|
|
3b349b9803 | ||
|
|
f2b7ffe0eb | ||
|
|
3a410eed1a | ||
|
|
a53619f10f | ||
|
|
893aaafa1b | ||
|
|
33c34cdeca | ||
|
|
9b411867df | ||
|
|
fd1596b3a0 | ||
|
|
b05de22f58 | ||
|
|
f666ad600f | ||
|
|
9345718967 | ||
|
|
6c879cbd13 | ||
|
|
089b5aaf44 | ||
|
|
b61885add5 | ||
|
|
5cb1e284af | ||
|
|
e3a4f0873e | ||
|
|
7977a020ac | ||
|
|
1d63269883 | ||
|
|
dd8027480b | ||
|
|
c45bd77e44 | ||
|
|
032e0abd61 | ||
|
|
8509d1e454 | ||
|
|
ddd6935e50 | ||
|
|
5588a56b24 | ||
|
|
12aed6e280 | ||
|
|
b2a469e08c | ||
|
|
285b0e4af9 | ||
|
|
f9edfd6391 | ||
|
|
f6301bc3e5 | ||
|
|
6c5c176b7d | ||
|
|
c6d52b5eb1 | ||
|
|
7cac528389 | ||
|
|
d518f75468 | ||
|
|
6fe817ca4a | ||
|
|
cb9a6fac25 | ||
|
|
a945768251 | ||
|
|
c6646e3821 | ||
|
|
99dc72cece | ||
|
|
04d6cca204 | ||
|
|
5ab6bda639 | ||
|
|
f433de7e12 | ||
|
|
8ef6c2f91d | ||
|
|
7575218697 | ||
|
|
dc945dad00 | ||
|
|
ddcd74ffd2 | ||
|
|
e105bd12e6 | ||
|
|
f5688175b6 | ||
|
|
72a4ba405f | ||
|
|
94694d394e | ||
|
|
03dd746601 | ||
|
|
eec3373ae7 | ||
|
|
db45ce07ed | ||
|
|
ba49765312 | ||
|
|
72c8c2371e | ||
|
|
80411ab6cf | ||
|
|
0ff8fa57e7 | ||
|
|
411f28a049 | ||
|
|
0f42233092 | ||
|
|
2dd49f6d9b | ||
|
|
271f545f4f | ||
|
|
c4a70b540e | ||
|
|
bef85772e3 | ||
|
|
a6b19c4a6c | ||
|
|
44f5e6659b | ||
|
|
3f9a9b7019 | ||
|
|
b7ad985c7a | ||
|
|
dba087ae25 | ||
|
|
bbc4b1b502 | ||
|
|
9304513ce8 | ||
|
|
0b127582cb | ||
|
|
6e9b8791c8 | ||
|
|
ef87ad77c3 | ||
|
|
8477420911 | ||
|
|
f5741e318f | ||
|
|
e010b5680a | ||
|
|
8620d3987e | ||
|
|
30487a54c1 | ||
|
|
f15a39c153 | ||
|
|
aed27fa111 | ||
|
|
8d2701e143 | ||
|
|
877444ac29 | ||
|
|
b0d9426f1b | ||
|
|
18accae47e | ||
|
|
45a8c0acd1 | ||
|
|
cc8fb96047 | ||
|
|
3339b50daf | ||
|
|
415ea07a4f | ||
|
|
b80ec95fa8 | ||
|
|
99cb51482f | ||
|
|
90638f7a43 | ||
|
|
1fb00c8eb6 | ||
|
|
4490ea7635 | ||
|
|
bce7a20d8b | ||
|
|
b52dd53e29 | ||
|
|
a155f45036 | ||
|
|
de4424fab0 |
1
.github/DISCUSSION_TEMPLATE/2-4.yml
vendored
1
.github/DISCUSSION_TEMPLATE/2-4.yml
vendored
@@ -33,6 +33,7 @@ body:
|
||||
- 2.4.180
|
||||
- 2.4.190
|
||||
- 2.4.200
|
||||
- 2.4.210
|
||||
- Other (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
### 2.4.190-20251024 ISO image released on 2025/10/24
|
||||
### 2.4.200-20251216 ISO image released on 2025/12/16
|
||||
|
||||
|
||||
### Download and Verify
|
||||
|
||||
2.4.190-20251024 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.4.190-20251024.iso
|
||||
2.4.200-20251216 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.4.200-20251216.iso
|
||||
|
||||
MD5: 25358481FB876226499C011FC0710358
|
||||
SHA1: 0B26173C0CE136F2CA40A15046D1DFB78BCA1165
|
||||
SHA256: 4FD9F62EDA672408828B3C0C446FE5EA9FF3C4EE8488A7AB1101544A3C487872
|
||||
MD5: 07B38499952D1F2FD7B5AF10096D0043
|
||||
SHA1: 7F3A26839CA3CAEC2D90BB73D229D55E04C7D370
|
||||
SHA256: 8D3AC735873A2EA8527E16A6A08C34BD5018CBC0925AC4096E15A0C99F591D5F
|
||||
|
||||
Signature for ISO image:
|
||||
https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.190-20251024.iso.sig
|
||||
https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.200-20251216.iso.sig
|
||||
|
||||
Signing key:
|
||||
https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.4/main/KEYS
|
||||
@@ -25,22 +25,22 @@ wget https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.
|
||||
|
||||
Download the signature file for the ISO:
|
||||
```
|
||||
wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.190-20251024.iso.sig
|
||||
wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.200-20251216.iso.sig
|
||||
```
|
||||
|
||||
Download the ISO image:
|
||||
```
|
||||
wget https://download.securityonion.net/file/securityonion/securityonion-2.4.190-20251024.iso
|
||||
wget https://download.securityonion.net/file/securityonion/securityonion-2.4.200-20251216.iso
|
||||
```
|
||||
|
||||
Verify the downloaded ISO image using the signature file:
|
||||
```
|
||||
gpg --verify securityonion-2.4.190-20251024.iso.sig securityonion-2.4.190-20251024.iso
|
||||
gpg --verify securityonion-2.4.200-20251216.iso.sig securityonion-2.4.200-20251216.iso
|
||||
```
|
||||
|
||||
The output should show "Good signature" and the Primary key fingerprint should match what's shown below:
|
||||
```
|
||||
gpg: Signature made Thu 23 Oct 2025 07:21:46 AM EDT using RSA key ID FE507013
|
||||
gpg: Signature made Mon 15 Dec 2025 05:24:11 PM EST using RSA key ID FE507013
|
||||
gpg: Good signature from "Security Onion Solutions, LLC <info@securityonionsolutions.com>"
|
||||
gpg: WARNING: This key is not certified with a trusted signature!
|
||||
gpg: There is no indication that the signature belongs to the owner.
|
||||
|
||||
@@ -129,6 +129,7 @@ if [[ $EXCLUDE_STARTUP_ERRORS == 'Y' ]]; then
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|responded with status-code 503" # telegraf getting 503 from ES during startup
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|process_cluster_event_timeout_exception" # logstash waiting for elasticsearch to start
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|not configured for GeoIP" # SO does not bundle the maxminddb with Zeek
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|HTTP 404: Not Found" # Salt loops until Kratos returns 200, during startup Kratos may not be ready
|
||||
fi
|
||||
|
||||
if [[ $EXCLUDE_FALSE_POSITIVE_ERRORS == 'Y' ]]; then
|
||||
|
||||
@@ -85,7 +85,7 @@ function suricata() {
|
||||
docker run --rm \
|
||||
-v /opt/so/conf/suricata/suricata.yaml:/etc/suricata/suricata.yaml:ro \
|
||||
-v /opt/so/conf/suricata/threshold.conf:/etc/suricata/threshold.conf:ro \
|
||||
-v /opt/so/conf/suricata/rules:/etc/suricata/rules:ro \
|
||||
-v /opt/so/rules/suricata/:/etc/suricata/rules:ro \
|
||||
-v ${LOG_PATH}:/var/log/suricata/:rw \
|
||||
-v ${NSM_PATH}/:/nsm/:rw \
|
||||
-v "$PCAP:/input.pcap:ro" \
|
||||
|
||||
@@ -60,7 +60,7 @@ so-elastalert:
|
||||
- watch:
|
||||
- file: elastaconf
|
||||
- onlyif:
|
||||
- "so-elasticsearch-query / | jq -r '.version.number[0:1]' | grep -q 8" {# only run this state if elasticsearch is version 8 #}
|
||||
- "so-elasticsearch-query / | jq -r '.version.number[0:1]' | grep -q 9" {# only run this state if elasticsearch is version 9 #}
|
||||
|
||||
delete_so-elastalert_so-status.disabled:
|
||||
file.uncomment:
|
||||
|
||||
34
salt/elasticfleet/config.map.jinja
Normal file
34
salt/elasticfleet/config.map.jinja
Normal file
@@ -0,0 +1,34 @@
|
||||
{# 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. #}
|
||||
|
||||
{% from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
|
||||
|
||||
{# advanced config_yaml options for elasticfleet logstash output #}
|
||||
{% set ADV_OUTPUT_LOGSTASH_RAW = ELASTICFLEETMERGED.config.outputs.logstash %}
|
||||
{% set ADV_OUTPUT_LOGSTASH = {} %}
|
||||
{% for k, v in ADV_OUTPUT_LOGSTASH_RAW.items() %}
|
||||
{% if v != "" and v is not none %}
|
||||
{% if k == 'queue_mem_events' %}
|
||||
{# rename queue_mem_events queue.mem.events #}
|
||||
{% do ADV_OUTPUT_LOGSTASH.update({'queue.mem.events':v}) %}
|
||||
{% elif k == 'loadbalance' %}
|
||||
{% if v %}
|
||||
{# only include loadbalance config when its True #}
|
||||
{% do ADV_OUTPUT_LOGSTASH.update({k:v}) %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% do ADV_OUTPUT_LOGSTASH.update({k:v}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set LOGSTASH_CONFIG_YAML_RAW = [] %}
|
||||
{% if ADV_OUTPUT_LOGSTASH %}
|
||||
{% for k, v in ADV_OUTPUT_LOGSTASH.items() %}
|
||||
{% do LOGSTASH_CONFIG_YAML_RAW.append(k ~ ': ' ~ v) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% set LOGSTASH_CONFIG_YAML = LOGSTASH_CONFIG_YAML_RAW | join('\\n') if LOGSTASH_CONFIG_YAML_RAW else '' %}
|
||||
@@ -10,6 +10,14 @@ elasticfleet:
|
||||
grid_enrollment: ''
|
||||
defend_filters:
|
||||
enable_auto_configuration: False
|
||||
outputs:
|
||||
logstash:
|
||||
bulk_max_size: ''
|
||||
worker: ''
|
||||
queue_mem_events: ''
|
||||
timeout: ''
|
||||
loadbalance: False
|
||||
compression_level: ''
|
||||
subscription_integrations: False
|
||||
auto_upgrade_integrations: False
|
||||
logging:
|
||||
|
||||
@@ -36,12 +36,13 @@ so-elastic-fleet-auto-configure-logstash-outputs:
|
||||
{# Separate from above in order to catch elasticfleet-logstash.crt changes and force update to fleet output policy #}
|
||||
so-elastic-fleet-auto-configure-logstash-outputs-force:
|
||||
cmd.run:
|
||||
- name: /usr/sbin/so-elastic-fleet-outputs-update --force --certs
|
||||
- name: /usr/sbin/so-elastic-fleet-outputs-update --certs
|
||||
- retry:
|
||||
attempts: 4
|
||||
interval: 30
|
||||
- onchanges:
|
||||
- x509: etc_elasticfleet_logstash_crt
|
||||
- x509: elasticfleet_kafka_crt
|
||||
{% endif %}
|
||||
|
||||
# If enabled, automatically update Fleet Server URLs & ES Connection
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"package": {
|
||||
"name": "endpoint",
|
||||
"title": "Elastic Defend",
|
||||
"version": "8.18.1",
|
||||
"version": "9.0.2",
|
||||
"requires_root": true
|
||||
},
|
||||
"enabled": true,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
'azure_application_insights.app_state': 'azure.app_state',
|
||||
'azure_billing.billing': 'azure.billing',
|
||||
'azure_functions.metrics': 'azure.function',
|
||||
'azure_ai_foundry.metrics': 'azure.ai_foundry',
|
||||
'azure_metrics.compute_vm_scaleset': 'azure.compute_vm_scaleset',
|
||||
'azure_metrics.compute_vm': 'azure.compute_vm',
|
||||
'azure_metrics.container_instance': 'azure.container_instance',
|
||||
@@ -121,6 +122,9 @@
|
||||
"phases": {
|
||||
"cold": {
|
||||
"actions": {
|
||||
"allocate":{
|
||||
"number_of_replicas": ""
|
||||
},
|
||||
"set_priority": {"priority": 0}
|
||||
},
|
||||
"min_age": "60d"
|
||||
@@ -137,12 +141,31 @@
|
||||
"max_age": "30d",
|
||||
"max_primary_shard_size": "50gb"
|
||||
},
|
||||
"forcemerge":{
|
||||
"max_num_segments": ""
|
||||
},
|
||||
"shrink":{
|
||||
"max_primary_shard_size": "",
|
||||
"method": "COUNT",
|
||||
"number_of_shards": ""
|
||||
},
|
||||
"set_priority": {"priority": 100}
|
||||
},
|
||||
"min_age": "0ms"
|
||||
},
|
||||
"warm": {
|
||||
"actions": {
|
||||
"allocate": {
|
||||
"number_of_replicas": ""
|
||||
},
|
||||
"forcemerge": {
|
||||
"max_num_segments": ""
|
||||
},
|
||||
"shrink":{
|
||||
"max_primary_shard_size": "",
|
||||
"method": "COUNT",
|
||||
"number_of_shards": ""
|
||||
},
|
||||
"set_priority": {"priority": 50}
|
||||
},
|
||||
"min_age": "30d"
|
||||
|
||||
@@ -50,6 +50,46 @@ elasticfleet:
|
||||
global: True
|
||||
forcedType: bool
|
||||
helpLink: elastic-fleet.html
|
||||
outputs:
|
||||
logstash:
|
||||
bulk_max_size:
|
||||
description: The maximum number of events to bulk in a single Logstash request.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
helpLink: elastic-fleet.html
|
||||
worker:
|
||||
description: The number of workers per configured host publishing events.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: true
|
||||
helpLink: elastic-fleet.html
|
||||
queue_mem_events:
|
||||
title: queued events
|
||||
description: The number of events the queue can store. This value should be evenly divisible by the smaller of 'bulk_max_size' to avoid sending partial batches to the output.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
helpLink: elastic-fleet.html
|
||||
timeout:
|
||||
description: The number of seconds to wait for responses from the Logstash server before timing out. Eg 30s
|
||||
regex: ^[0-9]+s$
|
||||
advanced: True
|
||||
global: True
|
||||
helpLink: elastic-fleet.html
|
||||
loadbalance:
|
||||
description: If true and multiple Logstash hosts are configured, the output plugin load balances published events onto all Logstash hosts. If false, the output plugin sends all events to one host (determined at random) and switches to another host if the selected one becomes unresponsive.
|
||||
forcedType: bool
|
||||
advanced: True
|
||||
global: True
|
||||
helpLink: elastic-fleet.html
|
||||
compression_level:
|
||||
description: The gzip compression level. The compression level must be in the range of 1 (best speed) to 9 (best compression).
|
||||
regex: ^[1-9]$
|
||||
forcedType: int
|
||||
advanced: True
|
||||
global: True
|
||||
helpLink: elastic-fleet.html
|
||||
server:
|
||||
custom_fqdn:
|
||||
description: Custom FQDN for Agents to connect to. One per line.
|
||||
|
||||
@@ -86,7 +86,7 @@ if [[ -f $STATE_FILE_SUCCESS ]]; then
|
||||
latest_package_list=$(/usr/sbin/so-elastic-fleet-package-list)
|
||||
echo '{ "packages" : []}' > $BULK_INSTALL_PACKAGE_LIST
|
||||
rm -f $INSTALLED_PACKAGE_LIST
|
||||
echo $latest_package_list | jq '{packages: [.items[] | {name: .name, latest_version: .version, installed_version: .savedObject.attributes.install_version, subscription: .conditions.elastic.subscription }]}' >> $INSTALLED_PACKAGE_LIST
|
||||
echo $latest_package_list | jq '{packages: [.items[] | {name: .name, latest_version: .version, installed_version: .installationInfo.version, subscription: .conditions.elastic.subscription }]}' >> $INSTALLED_PACKAGE_LIST
|
||||
|
||||
while read -r package; do
|
||||
# get package details
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
# 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; you may not use
|
||||
# this file except in compliance with the Elastic License 2.0.
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
|
||||
{%- from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{%- from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
|
||||
{%- from 'elasticfleet/config.map.jinja' import LOGSTASH_CONFIG_YAML %}
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
FORCE_UPDATE=false
|
||||
UPDATE_CERTS=false
|
||||
LOGSTASH_PILLAR_CONFIG_YAML="{{ LOGSTASH_CONFIG_YAML }}"
|
||||
LOGSTASH_PILLAR_STATE_FILE="/opt/so/state/esfleet_logstash_config_pillar"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
@@ -19,6 +22,7 @@ while [[ $# -gt 0 ]]; do
|
||||
;;
|
||||
-c| --certs)
|
||||
UPDATE_CERTS=true
|
||||
FORCE_UPDATE=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
@@ -41,38 +45,45 @@ function update_logstash_outputs() {
|
||||
LOGSTASHKEY=$(openssl rsa -in /etc/pki/elasticfleet-logstash.key)
|
||||
LOGSTASHCRT=$(openssl x509 -in /etc/pki/elasticfleet-logstash.crt)
|
||||
LOGSTASHCA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
|
||||
# Revert escaped \\n to \n for jq
|
||||
LOGSTASH_PILLAR_CONFIG_YAML=$(printf '%b' "$LOGSTASH_PILLAR_CONFIG_YAML")
|
||||
|
||||
if SECRETS=$(echo "$logstash_policy" | jq -er '.item.secrets' 2>/dev/null); then
|
||||
if [[ "$UPDATE_CERTS" != "true" ]]; then
|
||||
# Reuse existing secret
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
|
||||
--argjson SECRETS "$SECRETS" \
|
||||
--argjson SSL_CONFIG "$SSL_CONFIG" \
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":"","ssl": $SSL_CONFIG,"secrets": $SECRETS}')
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": $SSL_CONFIG,"secrets": $SECRETS}')
|
||||
else
|
||||
# Update certs, creating new secret
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
|
||||
--arg LOGSTASHKEY "$LOGSTASHKEY" \
|
||||
--arg LOGSTASHCRT "$LOGSTASHCRT" \
|
||||
--arg LOGSTASHCA "$LOGSTASHCA" \
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":"","ssl": {"certificate": $LOGSTASHCRT,"certificate_authorities":[ $LOGSTASHCA ]},"secrets": {"ssl":{"key": $LOGSTASHKEY }}}')
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": {"certificate": $LOGSTASHCRT,"certificate_authorities":[ $LOGSTASHCA ]},"secrets": {"ssl":{"key": $LOGSTASHKEY }}}')
|
||||
fi
|
||||
else
|
||||
if [[ "$UPDATE_CERTS" != "true" ]]; then
|
||||
# Reuse existing ssl config
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
|
||||
--argjson SSL_CONFIG "$SSL_CONFIG" \
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":"","ssl": $SSL_CONFIG}')
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": $SSL_CONFIG}')
|
||||
else
|
||||
# Update ssl config
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
|
||||
--arg LOGSTASHKEY "$LOGSTASHKEY" \
|
||||
--arg LOGSTASHCRT "$LOGSTASHCRT" \
|
||||
--arg LOGSTASHCA "$LOGSTASHCA" \
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":"","ssl": {"certificate": $LOGSTASHCRT,"key": $LOGSTASHKEY,"certificate_authorities":[ $LOGSTASHCA ]}}')
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": {"certificate": $LOGSTASHCRT,"key": $LOGSTASHKEY,"certificate_authorities":[ $LOGSTASHCA ]}}')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -84,19 +95,42 @@ function update_kafka_outputs() {
|
||||
# Make sure SSL configuration is included in policy updates for Kafka output. SSL is configured in so-elastic-fleet-setup
|
||||
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')
|
||||
KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key)
|
||||
KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt)
|
||||
KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
|
||||
if SECRETS=$(echo "$kafka_policy" | jq -er '.item.secrets' 2>/dev/null); then
|
||||
# Update policy when fleet has secrets enabled
|
||||
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}')
|
||||
if [[ "$UPDATE_CERTS" != "true" ]]; then
|
||||
# Update policy when fleet has secrets enabled
|
||||
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}')
|
||||
else
|
||||
# Update certs, creating new secret
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--arg KAFKAKEY "$KAFKAKEY" \
|
||||
--arg KAFKACRT "$KAFKACRT" \
|
||||
--arg KAFKACA "$KAFKACA" \
|
||||
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": {"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"secrets": {"ssl":{"key": $KAFKAKEY }}}')
|
||||
fi
|
||||
else
|
||||
# Update policy when fleet has secrets disabled or policy hasn't been force updated
|
||||
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}')
|
||||
if [[ "$UPDATE_CERTS" != "true" ]]; then
|
||||
# Update policy when fleet has secrets disabled or policy hasn't been force updated
|
||||
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}')
|
||||
else
|
||||
# Update ssl config
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--arg KAFKAKEY "$KAFKAKEY" \
|
||||
--arg KAFKACRT "$KAFKACRT" \
|
||||
--arg KAFKACA "$KAFKACA" \
|
||||
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": { "certificate_authorities": [ $KAFKACA ], "certificate": $KAFKACRT, "key": $KAFKAKEY, "verification_mode": "full" }}')
|
||||
fi
|
||||
fi
|
||||
# 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
|
||||
@@ -119,7 +153,7 @@ function update_kafka_outputs() {
|
||||
|
||||
# Get the current list of kafka outputs & hash them
|
||||
CURRENT_LIST=$(jq -c -r '.item.hosts' <<< "$RAW_JSON")
|
||||
CURRENT_HASH=$(sha1sum <<< "$CURRENT_LIST" | awk '{print $1}')
|
||||
CURRENT_HASH=$(sha256sum <<< "$CURRENT_LIST" | awk '{print $1}')
|
||||
|
||||
declare -a NEW_LIST=()
|
||||
|
||||
@@ -142,10 +176,19 @@ function update_kafka_outputs() {
|
||||
printf "Failed to query for current Logstash Outputs..."
|
||||
exit 1
|
||||
fi
|
||||
# logstash adv config - compare pillar to last state file value
|
||||
if [[ -f "$LOGSTASH_PILLAR_STATE_FILE" ]]; then
|
||||
PREVIOUS_LOGSTASH_PILLAR_CONFIG_YAML=$(cat "$LOGSTASH_PILLAR_STATE_FILE")
|
||||
if [[ "$LOGSTASH_PILLAR_CONFIG_YAML" != "$PREVIOUS_LOGSTASH_PILLAR_CONFIG_YAML" ]]; then
|
||||
echo "Logstash pillar config has changed - forcing update"
|
||||
FORCE_UPDATE=true
|
||||
fi
|
||||
echo "$LOGSTASH_PILLAR_CONFIG_YAML" > "$LOGSTASH_PILLAR_STATE_FILE"
|
||||
fi
|
||||
|
||||
# Get the current list of Logstash outputs & hash them
|
||||
CURRENT_LIST=$(jq -c -r '.item.hosts' <<< "$RAW_JSON")
|
||||
CURRENT_HASH=$(sha1sum <<< "$CURRENT_LIST" | awk '{print $1}')
|
||||
CURRENT_HASH=$(sha256sum <<< "$CURRENT_LIST" | awk '{print $1}')
|
||||
|
||||
declare -a NEW_LIST=()
|
||||
|
||||
@@ -194,7 +237,7 @@ function update_kafka_outputs() {
|
||||
|
||||
# Sort & hash the new list of Logstash Outputs
|
||||
NEW_LIST_JSON=$(jq --compact-output --null-input '$ARGS.positional' --args -- "${NEW_LIST[@]}")
|
||||
NEW_HASH=$(sha1sum <<< "$NEW_LIST_JSON" | awk '{print $1}')
|
||||
NEW_HASH=$(sha256sum <<< "$NEW_LIST_JSON" | awk '{print $1}')
|
||||
|
||||
# Compare the current & new list of outputs - if different, update the Logstash outputs
|
||||
if [[ "$NEW_HASH" = "$CURRENT_HASH" ]] && [[ "$FORCE_UPDATE" != "true" ]]; then
|
||||
|
||||
@@ -47,7 +47,7 @@ if ! kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://l
|
||||
--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 }}}'
|
||||
'{"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},"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"
|
||||
@@ -67,7 +67,7 @@ elif kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://l
|
||||
--arg ENABLED_DISABLED "$ENABLED_DISABLED"\
|
||||
--arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \
|
||||
--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 }}}'
|
||||
'{"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},"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"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
elasticsearch:
|
||||
enabled: false
|
||||
version: 8.18.8
|
||||
version: 9.0.8
|
||||
index_clean: true
|
||||
config:
|
||||
action:
|
||||
@@ -72,6 +72,8 @@ elasticsearch:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 0
|
||||
allocate:
|
||||
number_of_replicas: ""
|
||||
min_age: 60d
|
||||
delete:
|
||||
actions:
|
||||
@@ -84,11 +86,25 @@ elasticsearch:
|
||||
max_primary_shard_size: 50gb
|
||||
set_priority:
|
||||
priority: 100
|
||||
forcemerge:
|
||||
max_num_segments: ""
|
||||
shrink:
|
||||
max_primary_shard_size: ""
|
||||
method: COUNT
|
||||
number_of_shards: ""
|
||||
min_age: 0ms
|
||||
warm:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 50
|
||||
forcemerge:
|
||||
max_num_segments: ""
|
||||
shrink:
|
||||
max_primary_shard_size: ""
|
||||
method: COUNT
|
||||
number_of_shards: ""
|
||||
allocate:
|
||||
number_of_replicas: ""
|
||||
min_age: 30d
|
||||
so-case:
|
||||
index_sorting: false
|
||||
@@ -245,7 +261,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-detection:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -284,6 +299,19 @@ elasticsearch:
|
||||
hot:
|
||||
actions: {}
|
||||
min_age: 0ms
|
||||
sos-backup:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
composed_of: []
|
||||
ignore_missing_component_templates: []
|
||||
index_patterns:
|
||||
- sos-backup-*
|
||||
priority: 501
|
||||
template:
|
||||
settings:
|
||||
index:
|
||||
number_of_replicas: 0
|
||||
number_of_shards: 1
|
||||
so-assistant-chat:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -584,7 +612,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-import:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -830,53 +857,11 @@ elasticsearch:
|
||||
composed_of:
|
||||
- agent-mappings
|
||||
- dtc-agent-mappings
|
||||
- base-mappings
|
||||
- dtc-base-mappings
|
||||
- client-mappings
|
||||
- dtc-client-mappings
|
||||
- container-mappings
|
||||
- destination-mappings
|
||||
- dtc-destination-mappings
|
||||
- pb-override-destination-mappings
|
||||
- dll-mappings
|
||||
- dns-mappings
|
||||
- dtc-dns-mappings
|
||||
- ecs-mappings
|
||||
- dtc-ecs-mappings
|
||||
- error-mappings
|
||||
- event-mappings
|
||||
- dtc-event-mappings
|
||||
- file-mappings
|
||||
- dtc-file-mappings
|
||||
- group-mappings
|
||||
- host-mappings
|
||||
- dtc-host-mappings
|
||||
- http-mappings
|
||||
- dtc-http-mappings
|
||||
- log-mappings
|
||||
- metadata-mappings
|
||||
- network-mappings
|
||||
- dtc-network-mappings
|
||||
- observer-mappings
|
||||
- dtc-observer-mappings
|
||||
- organization-mappings
|
||||
- package-mappings
|
||||
- process-mappings
|
||||
- dtc-process-mappings
|
||||
- related-mappings
|
||||
- rule-mappings
|
||||
- dtc-rule-mappings
|
||||
- server-mappings
|
||||
- service-mappings
|
||||
- dtc-service-mappings
|
||||
- source-mappings
|
||||
- dtc-source-mappings
|
||||
- pb-override-source-mappings
|
||||
- threat-mappings
|
||||
- tls-mappings
|
||||
- url-mappings
|
||||
- user_agent-mappings
|
||||
- dtc-user_agent-mappings
|
||||
- common-settings
|
||||
- common-dynamic-mappings
|
||||
data_stream:
|
||||
@@ -932,7 +917,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-hydra:
|
||||
close: 30
|
||||
delete: 365
|
||||
@@ -1043,7 +1027,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-lists:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -1127,6 +1110,8 @@ elasticsearch:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 0
|
||||
allocate:
|
||||
number_of_replicas: ""
|
||||
min_age: 60d
|
||||
delete:
|
||||
actions:
|
||||
@@ -1139,11 +1124,25 @@ elasticsearch:
|
||||
max_primary_shard_size: 50gb
|
||||
set_priority:
|
||||
priority: 100
|
||||
forcemerge:
|
||||
max_num_segments: ""
|
||||
shrink:
|
||||
max_primary_shard_size: ""
|
||||
method: COUNT
|
||||
number_of_shards: ""
|
||||
min_age: 0ms
|
||||
warm:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 50
|
||||
allocate:
|
||||
number_of_replicas: ""
|
||||
forcemerge:
|
||||
max_num_segments: ""
|
||||
shrink:
|
||||
max_primary_shard_size: ""
|
||||
method: COUNT
|
||||
number_of_shards: ""
|
||||
min_age: 30d
|
||||
so-logs-detections_x_alerts:
|
||||
index_sorting: false
|
||||
@@ -3123,7 +3122,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-logs-system_x_application:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
|
||||
@@ -1,9 +1,90 @@
|
||||
{
|
||||
"description" : "kratos",
|
||||
"processors" : [
|
||||
{"set":{"field":"audience","value":"access","override":false,"ignore_failure":true}},
|
||||
{"set":{"field":"event.dataset","ignore_empty_value":true,"ignore_failure":true,"value":"kratos.{{{audience}}}","media_type":"text/plain"}},
|
||||
{"set":{"field":"event.action","ignore_failure":true,"copy_from":"msg" }},
|
||||
{ "pipeline": { "name": "common" } }
|
||||
]
|
||||
"description": "kratos",
|
||||
"processors": [
|
||||
{
|
||||
"set": {
|
||||
"field": "audience",
|
||||
"value": "access",
|
||||
"override": false,
|
||||
"ignore_failure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"field": "event.dataset",
|
||||
"ignore_empty_value": true,
|
||||
"ignore_failure": true,
|
||||
"value": "kratos.{{{audience}}}",
|
||||
"media_type": "text/plain"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"field": "event.action",
|
||||
"ignore_failure": true,
|
||||
"copy_from": "msg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"field": "http_request",
|
||||
"target_field": "http.request",
|
||||
"ignore_failure": true,
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"field": "http_response",
|
||||
"target_field": "http.response",
|
||||
"ignore_failure": true,
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"field": "http.request.path",
|
||||
"target_field": "http.uri",
|
||||
"ignore_failure": true,
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"field": "http.request.method",
|
||||
"target_field": "http.method",
|
||||
"ignore_failure": true,
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"field": "http.request.method",
|
||||
"target_field": "http.method",
|
||||
"ignore_failure": true,
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"field": "http.request.query",
|
||||
"target_field": "http.query",
|
||||
"ignore_failure": true,
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"field": "http.request.headers.user-agent",
|
||||
"target_field": "http.useragent",
|
||||
"ignore_failure": true,
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"pipeline": {
|
||||
"name": "common"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -131,6 +131,47 @@ elasticsearch:
|
||||
description: Maximum primary shard size. Once an index reaches this limit, it will be rolled over into a new index.
|
||||
global: True
|
||||
helpLink: elasticsearch.html
|
||||
shrink:
|
||||
method:
|
||||
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
|
||||
options:
|
||||
- COUNT
|
||||
- SIZE
|
||||
global: True
|
||||
advanced: True
|
||||
forcedType: string
|
||||
number_of_shards:
|
||||
title: shard count
|
||||
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
max_primary_shard_size:
|
||||
title: max shard size
|
||||
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
|
||||
regex: ^[0-9]+(?:gb|tb|pb)$
|
||||
global: True
|
||||
forcedType: string
|
||||
advanced: True
|
||||
allow_write_after_shrink:
|
||||
description: Allow writes after shrink.
|
||||
global: True
|
||||
forcedType: bool
|
||||
default: False
|
||||
advanced: True
|
||||
forcemerge:
|
||||
max_num_segments:
|
||||
description: Reduce the number of segments in each index shard and clean up deleted documents.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
index_codec:
|
||||
title: compression
|
||||
description: Use higher compression for stored fields at the cost of slower performance.
|
||||
forcedType: bool
|
||||
global: True
|
||||
default: False
|
||||
advanced: True
|
||||
cold:
|
||||
min_age:
|
||||
description: Minimum age of index. ex. 60d - This determines when the index should be moved to the cold tier. While still searchable, this tier is typically optimized for lower storage costs rather than search speed. It’s important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and cold min_age set to 60 then there will be 30 days from index creation to rollover and then an additional 60 days before moving to cold tier.
|
||||
@@ -144,6 +185,12 @@ elasticsearch:
|
||||
description: Used for index recovery after a node restart. Indices with higher priorities are recovered before indices with lower priorities.
|
||||
global: True
|
||||
helpLink: elasticsearch.html
|
||||
allocate:
|
||||
number_of_replicas:
|
||||
description: Set the number of replicas. Remains the same as the previous phase by default.
|
||||
forcedType: int
|
||||
global: True
|
||||
advanced: True
|
||||
warm:
|
||||
min_age:
|
||||
description: Minimum age of index. ex. 30d - This determines when the index should be moved to the warm tier. Nodes in the warm tier generally don’t need to be as fast as those in the hot tier. It’s important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and warm min_age set to 30 then there will be 30 days from index creation to rollover and then an additional 30 days before moving to warm tier.
|
||||
@@ -158,6 +205,52 @@ elasticsearch:
|
||||
forcedType: int
|
||||
global: True
|
||||
helpLink: elasticsearch.html
|
||||
shrink:
|
||||
method:
|
||||
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
|
||||
options:
|
||||
- COUNT
|
||||
- SIZE
|
||||
global: True
|
||||
advanced: True
|
||||
number_of_shards:
|
||||
title: shard count
|
||||
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
max_primary_shard_size:
|
||||
title: max shard size
|
||||
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
|
||||
regex: ^[0-9]+(?:gb|tb|pb)$
|
||||
global: True
|
||||
forcedType: string
|
||||
advanced: True
|
||||
allow_write_after_shrink:
|
||||
description: Allow writes after shrink.
|
||||
global: True
|
||||
forcedType: bool
|
||||
default: False
|
||||
advanced: True
|
||||
forcemerge:
|
||||
max_num_segments:
|
||||
description: Reduce the number of segments in each index shard and clean up deleted documents.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
index_codec:
|
||||
title: compression
|
||||
description: Use higher compression for stored fields at the cost of slower performance.
|
||||
forcedType: bool
|
||||
global: True
|
||||
default: False
|
||||
advanced: True
|
||||
allocate:
|
||||
number_of_replicas:
|
||||
description: Set the number of replicas. Remains the same as the previous phase by default.
|
||||
forcedType: int
|
||||
global: True
|
||||
advanced: True
|
||||
delete:
|
||||
min_age:
|
||||
description: Minimum age of index. ex. 90d - This determines when the index should be deleted. It’s important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and delete min_age set to 90 then there will be 30 days from index creation to rollover and then an additional 90 days before deletion.
|
||||
@@ -287,6 +380,47 @@ elasticsearch:
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: elasticsearch.html
|
||||
shrink:
|
||||
method:
|
||||
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
|
||||
options:
|
||||
- COUNT
|
||||
- SIZE
|
||||
global: True
|
||||
advanced: True
|
||||
forcedType: string
|
||||
number_of_shards:
|
||||
title: shard count
|
||||
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
max_primary_shard_size:
|
||||
title: max shard size
|
||||
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
|
||||
regex: ^[0-9]+(?:gb|tb|pb)$
|
||||
global: True
|
||||
forcedType: string
|
||||
advanced: True
|
||||
allow_write_after_shrink:
|
||||
description: Allow writes after shrink.
|
||||
global: True
|
||||
forcedType: bool
|
||||
default: False
|
||||
advanced: True
|
||||
forcemerge:
|
||||
max_num_segments:
|
||||
description: Reduce the number of segments in each index shard and clean up deleted documents.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
index_codec:
|
||||
title: compression
|
||||
description: Use higher compression for stored fields at the cost of slower performance.
|
||||
forcedType: bool
|
||||
global: True
|
||||
default: False
|
||||
advanced: True
|
||||
warm:
|
||||
min_age:
|
||||
description: Minimum age of index. ex. 30d - This determines when the index should be moved to the warm tier. Nodes in the warm tier generally don’t need to be as fast as those in the hot tier. It’s important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and warm min_age set to 30 then there will be 30 days from index creation to rollover and then an additional 30 days before moving to warm tier.
|
||||
@@ -314,6 +448,52 @@ elasticsearch:
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: elasticsearch.html
|
||||
shrink:
|
||||
method:
|
||||
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
|
||||
options:
|
||||
- COUNT
|
||||
- SIZE
|
||||
global: True
|
||||
advanced: True
|
||||
number_of_shards:
|
||||
title: shard count
|
||||
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
max_primary_shard_size:
|
||||
title: max shard size
|
||||
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
|
||||
regex: ^[0-9]+(?:gb|tb|pb)$
|
||||
global: True
|
||||
forcedType: string
|
||||
advanced: True
|
||||
allow_write_after_shrink:
|
||||
description: Allow writes after shrink.
|
||||
global: True
|
||||
forcedType: bool
|
||||
default: False
|
||||
advanced: True
|
||||
forcemerge:
|
||||
max_num_segments:
|
||||
description: Reduce the number of segments in each index shard and clean up deleted documents.
|
||||
global: True
|
||||
forcedType: int
|
||||
advanced: True
|
||||
index_codec:
|
||||
title: compression
|
||||
description: Use higher compression for stored fields at the cost of slower performance.
|
||||
forcedType: bool
|
||||
global: True
|
||||
default: False
|
||||
advanced: True
|
||||
allocate:
|
||||
number_of_replicas:
|
||||
description: Set the number of replicas. Remains the same as the previous phase by default.
|
||||
forcedType: int
|
||||
global: True
|
||||
advanced: True
|
||||
cold:
|
||||
min_age:
|
||||
description: Minimum age of index. ex. 60d - This determines when the index should be moved to the cold tier. While still searchable, this tier is typically optimized for lower storage costs rather than search speed. It’s important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and cold min_age set to 60 then there will be 30 days from index creation to rollover and then an additional 60 days before moving to cold tier.
|
||||
@@ -330,6 +510,12 @@ elasticsearch:
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: elasticsearch.html
|
||||
allocate:
|
||||
number_of_replicas:
|
||||
description: Set the number of replicas. Remains the same as the previous phase by default.
|
||||
forcedType: int
|
||||
global: True
|
||||
advanced: True
|
||||
delete:
|
||||
min_age:
|
||||
description: Minimum age of index. ex. 90d - This determines when the index should be deleted. It’s important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and delete min_age set to 90 then there will be 30 days from index creation to rollover and then an additional 90 days before deletion.
|
||||
|
||||
@@ -61,5 +61,55 @@
|
||||
{% do settings.index_template.template.settings.index.pop('sort') %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{# advanced ilm actions #}
|
||||
{% if settings.policy is defined and settings.policy.phases is defined %}
|
||||
{% set PHASE_NAMES = ["hot", "warm", "cold"] %}
|
||||
{% for P in PHASE_NAMES %}
|
||||
{% if settings.policy.phases[P] is defined and settings.policy.phases[P].actions is defined %}
|
||||
{% set PHASE = settings.policy.phases[P].actions %}
|
||||
{# remove allocate action if number_of_replicas isn't configured #}
|
||||
{% if PHASE.allocate is defined %}
|
||||
{% if PHASE.allocate.number_of_replicas is not defined or PHASE.allocate.number_of_replicas == "" %}
|
||||
{% do PHASE.pop('allocate', none) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{# start shrink action #}
|
||||
{% if PHASE.shrink is defined %}
|
||||
{% if PHASE.shrink.method is defined %}
|
||||
{% if PHASE.shrink.method == 'COUNT' and PHASE.shrink.number_of_shards is defined and PHASE.shrink.number_of_shards %}
|
||||
{# remove max_primary_shard_size value when doing shrink operation by count vs size #}
|
||||
{% do PHASE.shrink.pop('max_primary_shard_size', none) %}
|
||||
{% elif PHASE.shrink.method == 'SIZE' and PHASE.shrink.max_primary_shard_size is defined and PHASE.shrink.max_primary_shard_size %}
|
||||
{# remove number_of_shards value when doing shrink operation by size vs count #}
|
||||
{% do PHASE.shrink.pop('number_of_shards', none) %}
|
||||
{% else %}
|
||||
{# method isn't defined or missing a required config number_of_shards/max_primary_shard_size #}
|
||||
{% do PHASE.pop('shrink', none) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{# always remove shrink method since its only used for SOC config, not in the actual ilm policy #}
|
||||
{% if PHASE.shrink is defined %}
|
||||
{% do PHASE.shrink.pop('method', none) %}
|
||||
{% endif %}
|
||||
{# end shrink action #}
|
||||
{# start force merge #}
|
||||
{% if PHASE.forcemerge is defined %}
|
||||
{% if PHASE.forcemerge.index_codec is defined and PHASE.forcemerge.index_codec %}
|
||||
{% do PHASE.forcemerge.update({'index_codec': 'best_compression'}) %}
|
||||
{% else %}
|
||||
{% do PHASE.forcemerge.pop('index_codec', none) %}
|
||||
{% endif %}
|
||||
{% if PHASE.forcemerge.max_num_segments is not defined or not PHASE.forcemerge.max_num_segments %}
|
||||
{# max_num_segments is empty, drop it #}
|
||||
{% do PHASE.pop('forcemerge', none) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{# end force merge #}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% do ES_INDEX_SETTINGS.update({index | replace("_x_", "."): ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index]}) %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -15,7 +15,7 @@ set -e
|
||||
if [ ! -f /opt/so/saltstack/local/salt/elasticsearch/cacerts ]; then
|
||||
docker run -v /etc/pki/ca.crt:/etc/ssl/ca.crt --name so-elasticsearchca --user root --entrypoint jdk/bin/keytool {{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-elasticsearch:$ELASTIC_AGENT_TARBALL_VERSION -keystore /usr/share/elasticsearch/jdk/lib/security/cacerts -alias SOSCA -import -file /etc/ssl/ca.crt -storepass changeit -noprompt
|
||||
docker cp so-elasticsearchca:/usr/share/elasticsearch/jdk/lib/security/cacerts /opt/so/saltstack/local/salt/elasticsearch/cacerts
|
||||
docker cp so-elasticsearchca:/etc/ssl/certs/ca-certificates.crt /opt/so/saltstack/local/salt/elasticsearch/tls-ca-bundle.pem
|
||||
docker cp so-elasticsearchca:/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /opt/so/saltstack/local/salt/elasticsearch/tls-ca-bundle.pem
|
||||
docker rm so-elasticsearchca
|
||||
echo "" >> /opt/so/saltstack/local/salt/elasticsearch/tls-ca-bundle.pem
|
||||
echo "sosca" >> /opt/so/saltstack/local/salt/elasticsearch/tls-ca-bundle.pem
|
||||
|
||||
@@ -75,6 +75,7 @@ kratosconfig:
|
||||
- group: 928
|
||||
- mode: 600
|
||||
- template: jinja
|
||||
- show_changes: False
|
||||
- defaults:
|
||||
KRATOSMERGED: {{ KRATOSMERGED }}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ kratos:
|
||||
ui_url: https://URL_BASE/
|
||||
login:
|
||||
ui_url: https://URL_BASE/login/
|
||||
lifespan: 60m
|
||||
error:
|
||||
ui_url: https://URL_BASE/login/
|
||||
registration:
|
||||
|
||||
@@ -182,6 +182,10 @@ kratos:
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: kratos.html
|
||||
lifespan:
|
||||
description: Defines the duration that a login form will remain valid.
|
||||
global: True
|
||||
helpLink: kratos.html
|
||||
error:
|
||||
ui_url:
|
||||
description: User accessible URL containing the Security Onion login page. Leave as default to ensure proper operation.
|
||||
|
||||
@@ -63,7 +63,7 @@ logstash:
|
||||
settings:
|
||||
lsheap: 500m
|
||||
config:
|
||||
http_x_host: 0.0.0.0
|
||||
api_x_http_x_host: 0.0.0.0
|
||||
path_x_logs: /var/log/logstash
|
||||
pipeline_x_workers: 1
|
||||
pipeline_x_batch_x_size: 125
|
||||
|
||||
@@ -5,10 +5,10 @@ input {
|
||||
codec => es_bulk
|
||||
request_headers_target_field => client_headers
|
||||
remote_host_target_field => client_host
|
||||
ssl => true
|
||||
ssl_enabled => true
|
||||
ssl_certificate_authorities => ["/usr/share/filebeat/ca.crt"]
|
||||
ssl_certificate => "/usr/share/logstash/filebeat.crt"
|
||||
ssl_key => "/usr/share/logstash/filebeat.key"
|
||||
ssl_verify_mode => "peer"
|
||||
ssl_client_authentication => "required"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ input {
|
||||
elastic_agent {
|
||||
port => 5055
|
||||
tags => [ "elastic-agent", "input-{{ GLOBALS.hostname }}" ]
|
||||
ssl => true
|
||||
ssl_enabled => true
|
||||
ssl_certificate_authorities => ["/usr/share/filebeat/ca.crt"]
|
||||
ssl_certificate => "/usr/share/logstash/elasticfleet-logstash.crt"
|
||||
ssl_key => "/usr/share/logstash/elasticfleet-logstash.key"
|
||||
ssl_verify_mode => "force_peer"
|
||||
ssl_client_authentication => "required"
|
||||
ecs_compatibility => v8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ input {
|
||||
elastic_agent {
|
||||
port => 5056
|
||||
tags => [ "elastic-agent", "fleet-lumberjack-input" ]
|
||||
ssl => true
|
||||
ssl_enabled => true
|
||||
ssl_certificate => "/usr/share/logstash/elasticfleet-lumberjack.crt"
|
||||
ssl_key => "/usr/share/logstash/elasticfleet-lumberjack.key"
|
||||
ecs_compatibility => v8
|
||||
|
||||
@@ -8,8 +8,8 @@ output {
|
||||
document_id => "%{[metadata][_id]}"
|
||||
index => "so-ip-mappings"
|
||||
silence_errors_in_log => ["version_conflict_engine_exception"]
|
||||
ssl => true
|
||||
ssl_certificate_verification => false
|
||||
ssl_enabled => true
|
||||
ssl_verification_mode => "none"
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -25,8 +25,8 @@ output {
|
||||
document_id => "%{[metadata][_id]}"
|
||||
pipeline => "%{[metadata][pipeline]}"
|
||||
silence_errors_in_log => ["version_conflict_engine_exception"]
|
||||
ssl => true
|
||||
ssl_certificate_verification => false
|
||||
ssl_enabled => true
|
||||
ssl_verification_mode => "none"
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -37,8 +37,8 @@ output {
|
||||
user => "{{ ES_USER }}"
|
||||
password => "{{ ES_PASS }}"
|
||||
pipeline => "%{[metadata][pipeline]}"
|
||||
ssl => true
|
||||
ssl_certificate_verification => false
|
||||
ssl_enabled => true
|
||||
ssl_verification_mode => "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,8 @@ output {
|
||||
data_stream => true
|
||||
user => "{{ ES_USER }}"
|
||||
password => "{{ ES_PASS }}"
|
||||
ssl => true
|
||||
ssl_certificate_verification => false
|
||||
ssl_enabled => true
|
||||
ssl_verification_mode=> "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ output {
|
||||
user => "{{ ES_USER }}"
|
||||
password => "{{ ES_PASS }}"
|
||||
index => "endgame-%{+YYYY.MM.dd}"
|
||||
ssl => true
|
||||
ssl_certificate_verification => false
|
||||
ssl_enabled => true
|
||||
ssl_verification_mode => "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ logstash:
|
||||
helpLink: logstash.html
|
||||
global: False
|
||||
config:
|
||||
http_x_host:
|
||||
api_x_http_x_host:
|
||||
description: Host interface to listen to connections.
|
||||
helpLink: logstash.html
|
||||
readonly: True
|
||||
|
||||
@@ -214,7 +214,7 @@ git_config_set_safe_dirs:
|
||||
|
||||
surinsmrulesdir:
|
||||
file.directory:
|
||||
- name: /nsm/rules/suricata
|
||||
- name: /nsm/rules/suricata/etopen
|
||||
- user: 939
|
||||
- group: 939
|
||||
- makedirs: True
|
||||
|
||||
@@ -25,13 +25,11 @@
|
||||
{% set index_settings = es.get('index_settings', {}) %}
|
||||
{% set input = index_settings.get('so-logs', {}) %}
|
||||
{% for k in matched_integration_names %}
|
||||
{% if k not in index_settings %}
|
||||
{% set _ = index_settings.update({k: input}) %}
|
||||
{% endif %}
|
||||
{% do index_settings.update({k: input}) %}
|
||||
{% endfor %}
|
||||
{% for k in addon_integration_keys %}
|
||||
{% if k not in matched_integration_names and k in index_settings %}
|
||||
{% set _ = index_settings.pop(k) %}
|
||||
{% do index_settings.pop(k) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ data }}
|
||||
@@ -45,14 +43,12 @@
|
||||
{% set es = data.get('elasticsearch', {}) %}
|
||||
{% set index_settings = es.get('index_settings', {}) %}
|
||||
{% for k in matched_integration_names %}
|
||||
{% if k not in index_settings %}
|
||||
{% set input = ADDON_INTEGRATION_DEFAULTS[k] %}
|
||||
{% set _ = index_settings.update({k: input})%}
|
||||
{% endif %}
|
||||
{% set input = ADDON_INTEGRATION_DEFAULTS[k] %}
|
||||
{% do index_settings.update({k: input})%}
|
||||
{% endfor %}
|
||||
{% for k in addon_integration_keys %}
|
||||
{% if k not in matched_integration_names and k in index_settings %}
|
||||
{% set _ = index_settings.pop(k) %}
|
||||
{% do index_settings.pop(k) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ data }}
|
||||
|
||||
@@ -133,7 +133,7 @@ function getinstallinfo() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
source <(echo $INSTALLVARS)
|
||||
export $(echo "$INSTALLVARS" | xargs)
|
||||
if [ $? -ne 0 ]; then
|
||||
log "ERROR" "Failed to source install variables"
|
||||
return 1
|
||||
|
||||
@@ -87,6 +87,12 @@ check_err() {
|
||||
113)
|
||||
echo 'No route to host'
|
||||
;;
|
||||
160)
|
||||
echo 'Incompatiable Elasticsearch upgrade'
|
||||
;;
|
||||
161)
|
||||
echo 'Required intermediate Elasticsearch upgrade not complete'
|
||||
;;
|
||||
*)
|
||||
echo 'Unhandled error'
|
||||
echo "$err_msg"
|
||||
@@ -427,6 +433,7 @@ preupgrade_changes() {
|
||||
[[ "$INSTALLEDVERSION" == 2.4.170 ]] && up_to_2.4.180
|
||||
[[ "$INSTALLEDVERSION" == 2.4.180 ]] && up_to_2.4.190
|
||||
[[ "$INSTALLEDVERSION" == 2.4.190 ]] && up_to_2.4.200
|
||||
[[ "$INSTALLEDVERSION" == 2.4.200 ]] && up_to_2.4.210
|
||||
true
|
||||
}
|
||||
|
||||
@@ -459,6 +466,7 @@ postupgrade_changes() {
|
||||
[[ "$POSTVERSION" == 2.4.170 ]] && post_to_2.4.180
|
||||
[[ "$POSTVERSION" == 2.4.180 ]] && post_to_2.4.190
|
||||
[[ "$POSTVERSION" == 2.4.190 ]] && post_to_2.4.200
|
||||
[[ "$POSTVERSION" == 2.4.200 ]] && post_to_2.4.210
|
||||
true
|
||||
}
|
||||
|
||||
@@ -615,9 +623,6 @@ post_to_2.4.180() {
|
||||
}
|
||||
|
||||
post_to_2.4.190() {
|
||||
echo "Regenerating Elastic Agent Installers"
|
||||
/sbin/so-elastic-agent-gen-installers
|
||||
|
||||
# Only need to update import / eval nodes
|
||||
if [[ "$MINION_ROLE" == "import" ]] || [[ "$MINION_ROLE" == "eval" ]]; then
|
||||
update_import_fleet_output
|
||||
@@ -645,6 +650,17 @@ post_to_2.4.200() {
|
||||
POSTVERSION=2.4.200
|
||||
}
|
||||
|
||||
post_to_2.4.210() {
|
||||
echo "Rolling over Kratos index to apply new index template"
|
||||
|
||||
rollover_index "logs-kratos-so"
|
||||
|
||||
echo "Regenerating Elastic Agent Installers"
|
||||
/sbin/so-elastic-agent-gen-installers
|
||||
|
||||
POSTVERSION=2.4.210
|
||||
}
|
||||
|
||||
repo_sync() {
|
||||
echo "Sync the local repo."
|
||||
su socore -c '/usr/sbin/so-repo-sync' || fail "Unable to complete so-repo-sync."
|
||||
@@ -906,9 +922,7 @@ up_to_2.4.180() {
|
||||
}
|
||||
|
||||
up_to_2.4.190() {
|
||||
# Elastic Update for this release, so download Elastic Agent files
|
||||
determine_elastic_agent_upgrade
|
||||
|
||||
echo "Nothing to do for 2.4.190"
|
||||
INSTALLEDVERSION=2.4.190
|
||||
}
|
||||
|
||||
@@ -916,9 +930,18 @@ up_to_2.4.200() {
|
||||
echo "Backing up idstools config..."
|
||||
suricata_idstools_removal_pre
|
||||
|
||||
touch /opt/so/state/esfleet_logstash_config_pillar
|
||||
|
||||
INSTALLEDVERSION=2.4.200
|
||||
}
|
||||
|
||||
up_to_2.4.210() {
|
||||
# Elastic Update for this release, so download Elastic Agent files
|
||||
determine_elastic_agent_upgrade
|
||||
|
||||
INSTALLEDVERSION=2.4.210
|
||||
}
|
||||
|
||||
add_hydra_pillars() {
|
||||
mkdir -p /opt/so/saltstack/local/pillar/hydra
|
||||
touch /opt/so/saltstack/local/pillar/hydra/soc_hydra.sls
|
||||
@@ -1111,47 +1134,47 @@ suricata_idstools_removal_pre() {
|
||||
install -d -o 939 -g 939 -m 755 /opt/so/conf/soc/fingerprints
|
||||
install -o 939 -g 939 -m 644 /dev/null /opt/so/conf/soc/fingerprints/suricataengine.syncBlock
|
||||
cat > /opt/so/conf/soc/fingerprints/suricataengine.syncBlock << EOF
|
||||
Suricata ruleset sync is blocked until this file is removed. Make sure that you have manually added any custom Suricata rulesets via SOC config - review the documentation for more details: securityonion.net/docs
|
||||
Suricata ruleset sync is blocked until this file is removed. **CRITICAL** Make sure that you have manually added any custom Suricata rulesets via SOC config before removing this file - review the documentation for more details: https://docs.securityonion.net/en/2.4/nids.html#sync-block
|
||||
EOF
|
||||
|
||||
# Remove possible symlink & create salt local rules dir
|
||||
[ -L /opt/so/saltstack/local/salt/suricata/rules ] && rm -f /opt/so/saltstack/local/salt/suricata/rules
|
||||
install -d -o 939 -g 939 /opt/so/saltstack/local/salt/suricata/rules/ || echo "Failed to create Suricata local rules directory"
|
||||
|
||||
# Backup custom rules & overrides
|
||||
mkdir -p /nsm/backup/detections-migration/2-4-200
|
||||
cp /usr/sbin/so-rule-update /nsm/backup/detections-migration/2-4-200
|
||||
cp /opt/so/conf/idstools/etc/rulecat.conf /nsm/backup/detections-migration/2-4-200
|
||||
|
||||
if [[ -f /opt/so/conf/soc/so-detections-backup.py ]]; then
|
||||
python3 /opt/so/conf/soc/so-detections-backup.py
|
||||
# Backup so-detection index via reindex
|
||||
echo "Creating sos-backup index template..."
|
||||
template_result=$(/sbin/so-elasticsearch-query '_index_template/sos-backup' -X PUT \
|
||||
--retry 5 --retry-delay 15 --retry-all-errors \
|
||||
-d '{"index_patterns":["sos-backup-*"],"priority":501,"template":{"settings":{"index":{"number_of_replicas":0,"number_of_shards":1}}}}')
|
||||
|
||||
# Verify backup by comparing counts
|
||||
echo "Verifying detection overrides backup..."
|
||||
es_override_count=$(/sbin/so-elasticsearch-query 'so-detection/_count' \
|
||||
-d '{"query": {"bool": {"must": [{"exists": {"field": "so_detection.overrides"}}]}}}' | jq -r '.count') || {
|
||||
echo " Error: Failed to query Elasticsearch for override count"
|
||||
exit 1
|
||||
}
|
||||
if [[ -z "$template_result" ]] || ! echo "$template_result" | jq -e '.acknowledged == true' > /dev/null 2>&1; then
|
||||
echo "Error: Failed to create sos-backup index template"
|
||||
echo "$template_result"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$es_override_count" =~ ^[0-9]+$ ]]; then
|
||||
echo " Error: Invalid override count from Elasticsearch: '$es_override_count'"
|
||||
exit 1
|
||||
fi
|
||||
BACKUP_INDEX="sos-backup-detection-$(date +%Y%m%d-%H%M%S)"
|
||||
echo "Backing up so-detection index to $BACKUP_INDEX..."
|
||||
reindex_result=$(/sbin/so-elasticsearch-query '_reindex?wait_for_completion=true' \
|
||||
--retry 5 --retry-delay 15 --retry-all-errors \
|
||||
-X POST -d "{\"source\": {\"index\": \"so-detection\"}, \"dest\": {\"index\": \"$BACKUP_INDEX\"}}")
|
||||
|
||||
backup_override_count=$(find /nsm/backup/detections/repo/*/overrides -type f 2>/dev/null | wc -l)
|
||||
|
||||
echo " Elasticsearch overrides: $es_override_count"
|
||||
echo " Backed up overrides: $backup_override_count"
|
||||
|
||||
if [[ "$es_override_count" -gt 0 ]]; then
|
||||
if [[ "$backup_override_count" -gt 0 ]]; then
|
||||
echo " Override backup verified successfully"
|
||||
else
|
||||
echo " Error: Elasticsearch has $es_override_count overrides but backup has 0 files"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo " No overrides to backup"
|
||||
fi
|
||||
if [[ -z "$reindex_result" ]]; then
|
||||
echo "Error: Backup of detections failed - no response from Elasticsearch"
|
||||
exit 1
|
||||
elif echo "$reindex_result" | jq -e '.created >= 0' > /dev/null 2>&1; then
|
||||
echo "Backup complete: $(echo "$reindex_result" | jq -r '.created') documents copied"
|
||||
elif echo "$reindex_result" | grep -q "index_not_found_exception"; then
|
||||
echo "so-detection index does not exist, skipping backup"
|
||||
else
|
||||
echo "SOC Detections backup script not found, skipping detection backup"
|
||||
echo "Error: Backup of detections failed"
|
||||
echo "$reindex_result"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
}
|
||||
@@ -1172,21 +1195,47 @@ hash_normalized_file() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
sed -E \
|
||||
# Ensure trailing newline for consistent hashing regardless of source file
|
||||
{ sed -E \
|
||||
-e 's/^[[:space:]]+//; s/[[:space:]]+$//' \
|
||||
-e '/^$/d' \
|
||||
-e 's|--url=http://[^:]+:7788|--url=http://MANAGER:7788|' \
|
||||
"$file" | sha256sum | awk '{print $1}'
|
||||
"$file"; echo; } | sed '/^$/d' | sha256sum | awk '{print $1}'
|
||||
}
|
||||
|
||||
# Known-default hashes
|
||||
# Known-default hashes for so-rule-update (ETOPEN ruleset)
|
||||
KNOWN_SO_RULE_UPDATE_HASHES=(
|
||||
"8f1fe1cb65c08aab78830315b952785c7ccdcc108c5c0474f427e29d4e39ee5f" # non-Airgap
|
||||
"d23ac5a962c709dcb888103effb71444df72b46009b6c426e280dbfbc7d74d40" # Airgap
|
||||
# 2.4.100+ (suricata 7.0.3, non-airgap)
|
||||
"5fbd067ced86c8ec72ffb7e1798aa624123b536fb9d78f4b3ad8d3b45db1eae7" # 2.4.100-2.4.190 non-Airgap
|
||||
# 2.4.90+ airgap (same for 2.4.90 and 2.4.100+)
|
||||
"61f632c55791338c438c071040f1490066769bcce808b595b5cc7974a90e653a" # 2.4.90+ Airgap
|
||||
# 2.4.90 (suricata 6.0, non-airgap, comment inside proxy block)
|
||||
"0380ec52a05933244ab0f0bc506576e1d838483647b40612d5fe4b378e47aedd" # 2.4.90 non-Airgap
|
||||
# 2.4.10-2.4.80 (suricata 6.0, non-airgap, comment outside proxy block)
|
||||
"b6e4d1b5a78d57880ad038a9cd2cc6978aeb2dd27d48ea1a44dd866a2aee7ff4" # 2.4.10-2.4.80 non-Airgap
|
||||
# 2.4.10-2.4.80 airgap
|
||||
"b20146526ace2b142fde4664f1386a9a1defa319b3a1d113600ad33a1b037dad" # 2.4.10-2.4.80 Airgap
|
||||
# 2.4.5 and earlier (no pidof check, non-airgap)
|
||||
"d04f5e4015c348133d28a7840839e82d60009781eaaa1c66f7f67747703590dc" # 2.4.5 non-Airgap
|
||||
)
|
||||
|
||||
# Known-default hashes for rulecat.conf
|
||||
KNOWN_RULECAT_CONF_HASHES=(
|
||||
"17fc663a83b30d4ba43ac6643666b0c96343c5ea6ea833fe6a8362fe415b666b" # default
|
||||
# 2.4.100+ (suricata 7.0.3)
|
||||
"302e75dca9110807f09ade2eec3be1fcfc8b2bf6cf2252b0269bb72efeefe67e" # 2.4.100-2.4.190 without SURICATA md_engine
|
||||
"8029b7718c324a9afa06a5cf180afde703da1277af4bdd30310a6cfa3d6398cb" # 2.4.100-2.4.190 with SURICATA md_engine
|
||||
# 2.4.80-2.4.90 (suricata 6.0, with --suricata-version and --output)
|
||||
"4d8b318e6950a6f60b02f307cf27c929efd39652990c1bd0c8820aa8a307e1e7" # 2.4.80-2.4.90 without SURICATA md_engine
|
||||
"a1ddf264c86c4e91c81c5a317f745a19466d4311e4533ec3a3c91fed04c11678" # 2.4.80-2.4.90 with SURICATA md_engine
|
||||
# 2.4.50-2.4.70 (/suri/ path, no --suricata-version)
|
||||
"86e3afb8d0f00c62337195602636864c98580a13ca9cc85029661a539deae6ae" # 2.4.50-2.4.70 without SURICATA md_engine
|
||||
"5a97604ca5b820a10273a2d6546bb5e00c5122ca5a7dfe0ba0bfbce5fc026f4b" # 2.4.50-2.4.70 with SURICATA md_engine
|
||||
# 2.4.20-2.4.40 (/nids/ path without /suri/)
|
||||
"d098ea9ecd94b5cca35bf33543f8ea8f48066a0785221fabda7fef43d2462c29" # 2.4.20-2.4.40 without SURICATA md_engine
|
||||
"9dbc60df22ae20d65738ba42e620392577857038ba92278e23ec182081d191cd" # 2.4.20-2.4.40 with SURICATA md_engine
|
||||
# 2.4.5-2.4.10 (/sorules/ path for extraction/filters)
|
||||
"490f6843d9fca759ee74db3ada9c702e2440b8393f2cfaf07bbe41aaa6d955c3" # 2.4.5-2.4.10 with SURICATA md_engine
|
||||
# Note: 2.4.5-2.4.10 without SURICATA md_engine has same hash as 2.4.20-2.4.40 without SURICATA md_engine
|
||||
)
|
||||
|
||||
# Check a config file against known hashes
|
||||
@@ -1247,6 +1296,13 @@ custom_found=0
|
||||
check_config_file "$SO_RULE_UPDATE" "KNOWN_SO_RULE_UPDATE_HASHES" || custom_found=1
|
||||
check_config_file "$RULECAT_CONF" "KNOWN_RULECAT_CONF_HASHES" || custom_found=1
|
||||
|
||||
# Check for ETPRO rules on airgap systems
|
||||
if [[ $is_airgap -eq 0 ]] && grep -q 'ETPRO ' /nsm/rules/suricata/emerging-all.rules 2>/dev/null; then
|
||||
echo "ETPRO rules detected on airgap system - custom configuration"
|
||||
echo "ETPRO rules detected on Airgap in /nsm/rules/suricata/emerging-all.rules" >> /opt/so/conf/soc/fingerprints/suricataengine.syncBlock
|
||||
custom_found=1
|
||||
fi
|
||||
|
||||
# If no custom configs found, remove syncBlock
|
||||
if [[ $custom_found -eq 0 ]]; then
|
||||
echo "idstools migration completed successfully - removing Suricata engine syncBlock"
|
||||
@@ -1270,12 +1326,14 @@ if [ -n "$(docker ps -q -f name=^so-idstools$)" ]; then
|
||||
fi
|
||||
|
||||
echo "Removing idstools symlink and scripts..."
|
||||
rm /opt/so/saltstack/local/salt/suricata/rules
|
||||
rm -rf /usr/sbin/so-idstools*
|
||||
sed -i '/^#\?so-idstools$/d' /opt/so/conf/so-status/so-status.conf
|
||||
crontab -l | grep -v 'so-rule-update' | crontab -
|
||||
|
||||
# Backup the salt master config before editing it
|
||||
cp /etc/salt/master /nsm/backup/detections-migration/2-4-200
|
||||
# Backup the salt master config & manager pillar before editing it
|
||||
cp /opt/so/saltstack/local/pillar/minions/$MINIONID.sls /nsm/backup/detections-migration/2-4-200/
|
||||
cp /etc/salt/master /nsm/backup/detections-migration/2-4-200/
|
||||
so-yaml.py remove /opt/so/saltstack/local/pillar/minions/$MINIONID.sls idstools
|
||||
so-yaml.py removelistitem /etc/salt/master file_roots.base /opt/so/rules/nids
|
||||
|
||||
}
|
||||
@@ -1326,7 +1384,7 @@ unmount_update() {
|
||||
|
||||
update_airgap_rules() {
|
||||
# Copy the rules over to update them for airgap.
|
||||
rsync -a $UPDATE_DIR/agrules/suricata/* /nsm/rules/suricata/
|
||||
rsync -a --delete $UPDATE_DIR/agrules/suricata/ /nsm/rules/suricata/etopen/
|
||||
rsync -a $UPDATE_DIR/agrules/detect-sigma/* /nsm/rules/detect-sigma/
|
||||
rsync -a $UPDATE_DIR/agrules/detect-yara/* /nsm/rules/detect-yara/
|
||||
# Copy the securityonion-resorces repo over for SOC Detection Summaries and checkout the published summaries branch
|
||||
@@ -1575,6 +1633,243 @@ verify_latest_update_script() {
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
verify_es_version_compatibility() {
|
||||
|
||||
local es_required_version_statefile="/opt/so/state/so_es_required_upgrade_version.txt"
|
||||
local es_verification_script="/tmp/so_intermediate_upgrade_verification.sh"
|
||||
# supported upgrade paths for SO-ES versions
|
||||
declare -A es_upgrade_map=(
|
||||
["8.14.3"]="8.17.3 8.18.4 8.18.6 8.18.8"
|
||||
["8.17.3"]="8.18.4 8.18.6 8.18.8"
|
||||
["8.18.4"]="8.18.6 8.18.8 9.0.8"
|
||||
["8.18.6"]="8.18.8 9.0.8"
|
||||
["8.18.8"]="9.0.8"
|
||||
)
|
||||
|
||||
# Elasticsearch MUST upgrade through these versions
|
||||
declare -A es_to_so_version=(
|
||||
["8.18.8"]="2.4.190-20251024"
|
||||
)
|
||||
|
||||
# Get current Elasticsearch version
|
||||
if es_version_raw=$(so-elasticsearch-query / --fail --retry 5 --retry-delay 10); then
|
||||
es_version=$(echo "$es_version_raw" | jq -r '.version.number' )
|
||||
else
|
||||
echo "Could not determine current Elasticsearch version to validate compatibility with post soup Elasticsearch version."
|
||||
exit 160
|
||||
fi
|
||||
|
||||
if ! target_es_version=$(so-yaml.py get $UPDATE_DIR/salt/elasticsearch/defaults.yaml elasticsearch.version | sed -n '1p'); then
|
||||
# so-yaml.py failed to get the ES version from upgrade versions elasticsearch/defaults.yaml file. Likely they are upgrading to an SO version older than 2.4.110 prior to the ES version pinning and should be OKAY to continue with the upgrade.
|
||||
|
||||
# if so-yaml.py failed to get the ES version AND the version we are upgrading to is newer than 2.4.110 then we should bail
|
||||
if [[ $(cat $UPDATE_DIR/VERSION | cut -d'.' -f3) > 110 ]]; then
|
||||
echo "Couldn't determine the target Elasticsearch version (post soup version) to ensure compatibility with current Elasticsearch version. Exiting"
|
||||
exit 160
|
||||
fi
|
||||
|
||||
# allow upgrade to version < 2.4.110 without checking ES version compatibility
|
||||
return 0
|
||||
|
||||
fi
|
||||
|
||||
# if this statefile exists then we have done an intermediate upgrade and we need to ensure that ALL ES nodes have been upgraded to the version in the statefile before allowing soup to continue
|
||||
if [[ -f "$es_required_version_statefile" ]]; then
|
||||
# required so verification script should have already been created
|
||||
if [[ ! -f "$es_verification_script" ]]; then
|
||||
create_intermediate_upgrade_verification_script $es_verification_script
|
||||
fi
|
||||
|
||||
local es_required_version_statefile_value=$(cat $es_required_version_statefile)
|
||||
echo -e "\n##############################################################################################################################\n"
|
||||
echo "A previously required intermediate Elasticsearch upgrade was detected. Verifying that all Searchnodes/Heavynodes have successfully upgraded Elasticsearch to $es_required_version_statefile_value before proceeding with soup to avoid potential data loss!"
|
||||
# create script using version in statefile
|
||||
timeout --foreground 4000 bash "$es_verification_script" "$es_required_version_statefile_value" "$es_required_version_statefile"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
|
||||
echo "A previous required intermediate Elasticsearch upgrade to $es_required_version_statefile_value has yet to successfully complete across the grid. Please allow time for all Searchnodes/Heavynodes to have upgraded Elasticsearch to $es_required_version_statefile_value before running soup again to avoid potential data loss!"
|
||||
|
||||
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
exit 161
|
||||
fi
|
||||
echo -e "\n##############################################################################################################################\n"
|
||||
fi
|
||||
|
||||
if [[ " ${es_upgrade_map[$es_version]} " =~ " $target_es_version " || "$es_version" == "$target_es_version" ]]; then
|
||||
# supported upgrade
|
||||
return 0
|
||||
else
|
||||
compatible_versions=${es_upgrade_map[$es_version]}
|
||||
next_step_so_version=${es_to_so_version[${compatible_versions##* }]}
|
||||
echo -e "\n##############################################################################################################################\n"
|
||||
echo -e "You are currently running Security Onion $INSTALLEDVERSION. You will need to update to version $next_step_so_version before updating to $(cat $UPDATE_DIR/VERSION).\n"
|
||||
|
||||
echo "${compatible_versions##* }" > "$es_required_version_statefile"
|
||||
|
||||
# We expect to upgrade to the latest compatiable minor version of ES
|
||||
create_intermediate_upgrade_verification_script $es_verification_script
|
||||
|
||||
if [[ $is_airgap -eq 0 ]]; then
|
||||
echo "You can download the $next_step_so_version ISO image from https://download.securityonion.net/file/securityonion/securityonion-$next_step_so_version.iso"
|
||||
echo "*** Once you have updated to $next_step_so_version, you can then run soup again to update to $(cat $UPDATE_DIR/VERSION). ***"
|
||||
echo -e "\n##############################################################################################################################\n"
|
||||
exit 160
|
||||
else
|
||||
# preserve BRANCH value if set originally
|
||||
if [[ -n "$BRANCH" ]]; then
|
||||
local originally_requested_so_version="$BRANCH"
|
||||
else
|
||||
local originally_requested_so_version="2.4/main"
|
||||
fi
|
||||
|
||||
echo "Starting automated intermediate upgrade to $next_step_so_version."
|
||||
echo "After completion, the system will automatically attempt to upgrade to the latest version."
|
||||
echo -e "\n##############################################################################################################################\n"
|
||||
exec bash -c "BRANCH=$next_step_so_version soup -y && BRANCH=$next_step_so_version soup -y && \
|
||||
echo -e \"\n##############################################################################################################################\n\" && \
|
||||
echo -e \"Verifying Elasticsearch was successfully upgraded to ${compatible_versions##* } across the grid. This part can take a while as Searchnodes/Heavynodes sync up with the Manager! \n\nOnce verification completes the next soup will begin automatically. If verification takes longer than 1 hour it will stop waiting and your grid will remain at $next_step_so_version. Allowing for all Searchnodes/Heavynodes to upgrade Elasticsearch to the required version on their own time.\n\" \
|
||||
&& timeout --foreground 4000 bash /tmp/so_intermediate_upgrade_verification.sh ${compatible_versions##* } $es_required_version_statefile && \
|
||||
echo -e \"\n##############################################################################################################################\n\" \
|
||||
&& BRANCH=$originally_requested_so_version soup -y && BRANCH=$originally_requested_so_version soup -y"
|
||||
fi
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
create_intermediate_upgrade_verification_script() {
|
||||
# After an intermediate upgrade, verify that ALL nodes running Elasticsearch are at the expected version BEFORE proceeding to the next upgrade step. This is a CRITICAL step
|
||||
local verification_script="$1"
|
||||
|
||||
cat << 'EOF' > "$verification_script"
|
||||
#!/bin/bash
|
||||
|
||||
SOUP_INTERMEDIATE_UPGRADE_FAILURES_LOG_FILE="/root/so_intermediate_upgrade_verification_failures.log"
|
||||
CURRENT_TIME=$(date +%Y%m%d.%H%M%S)
|
||||
EXPECTED_ES_VERSION="$1"
|
||||
|
||||
if [[ -z "$EXPECTED_ES_VERSION" ]]; then
|
||||
echo -e "\nExpected Elasticsearch version not provided. Usage: $0 <expected_es_version>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "$SOUP_INTERMEDIATE_UPGRADE_FAILURES_LOG_FILE" ]]; then
|
||||
mv "$SOUP_INTERMEDIATE_UPGRADE_FAILURES_LOG_FILE" "$SOUP_INTERMEDIATE_UPGRADE_FAILURES_LOG_FILE.$CURRENT_TIME"
|
||||
fi
|
||||
|
||||
check_heavynodes_es_version() {
|
||||
# Check if heavynodes are in this grid
|
||||
if ! salt-key -l accepted | grep -q 'heavynode$'; then
|
||||
|
||||
# No heavynodes, skip version check
|
||||
echo "No heavynodes detected in this Security Onion deployment. Skipping heavynode Elasticsearch version verification."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -e "\nOne or more heavynodes detected. Verifying their Elasticsearch versions."
|
||||
|
||||
local retries=20
|
||||
local retry_count=0
|
||||
local delay=180
|
||||
|
||||
while [[ $retry_count -lt $retries ]]; do
|
||||
# keep stderr with variable for logging
|
||||
heavynode_versions=$(salt -C 'G@role:so-heavynode' cmd.run 'so-elasticsearch-query / --retry 3 --retry-delay 10 | jq ".version.number"' shell=/bin/bash --out=json 2> /dev/null)
|
||||
local exit_status=$?
|
||||
|
||||
# Check that all heavynodes returned good data
|
||||
if [[ $exit_status -ne 0 ]]; then
|
||||
echo "Failed to retrieve Elasticsearch version from one or more heavynodes... Retrying in $delay seconds. Attempt $((retry_count + 1)) of $retries."
|
||||
((retry_count++))
|
||||
sleep $delay
|
||||
|
||||
continue
|
||||
else
|
||||
if echo "$heavynode_versions" | jq -s --arg expected "\"$EXPECTED_ES_VERSION\"" --exit-status 'all(.[]; . | to_entries | all(.[]; .value == $expected))' > /dev/null; then
|
||||
echo -e "\nAll heavynodes are at the expected Elasticsearch version $EXPECTED_ES_VERSION."
|
||||
|
||||
return 0
|
||||
else
|
||||
echo "One or more heavynodes are not at the expected Elasticsearch version $EXPECTED_ES_VERSION. Rechecking in $delay seconds. Attempt $((retry_count + 1)) of $retries."
|
||||
((retry_count++))
|
||||
sleep $delay
|
||||
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
echo "One or more heavynodes is not at the expected Elasticsearch version $EXPECTED_ES_VERSION."
|
||||
echo "Current versions:"
|
||||
echo "$heavynode_versions" | jq -s 'add'
|
||||
echo "$heavynode_versions" | jq -s 'add' >> "$SOUP_INTERMEDIATE_UPGRADE_FAILURES_LOG_FILE"
|
||||
echo -e "\n Stopping automatic upgrade to latest Security Onion version. Heavynodes must ALL be at Elasticsearch version $EXPECTED_ES_VERSION before proceeding with the next upgrade step to avoid potential data loss!"
|
||||
echo -e "\n Heavynodes will upgrade themselves to Elasticsearch $EXPECTED_ES_VERSION on their own, but this process can take a long time depending on network link between Manager and Heavynodes."
|
||||
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
check_searchnodes_es_version() {
|
||||
local retries=20
|
||||
local retry_count=0
|
||||
local delay=180
|
||||
|
||||
while [[ $retry_count -lt $retries ]]; do
|
||||
# keep stderr with variable for logging
|
||||
cluster_versions=$(so-elasticsearch-query _nodes/_all/version --retry 5 --retry-delay 10 --fail 2>&1)
|
||||
local exit_status=$?
|
||||
|
||||
if [[ $exit_status -ne 0 ]]; then
|
||||
echo "Failed to retrieve Elasticsearch versions from searchnodes... Retrying in $delay seconds. Attempt $((retry_count + 1)) of $retries."
|
||||
((retry_count++))
|
||||
sleep $delay
|
||||
|
||||
continue
|
||||
else
|
||||
if echo "$cluster_versions" | jq --arg expected "$EXPECTED_ES_VERSION" --exit-status '.nodes | to_entries | all(.[].value.version; . == $expected)' > /dev/null; then
|
||||
echo "All Searchnodes are at the expected Elasticsearch version $EXPECTED_ES_VERSION."
|
||||
|
||||
return 0
|
||||
else
|
||||
echo "One or more Searchnodes is not at the expected Elasticsearch version $EXPECTED_ES_VERSION. Rechecking in $delay seconds. Attempt $((retry_count + 1)) of $retries."
|
||||
((retry_count++))
|
||||
sleep $delay
|
||||
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
echo "One or more Searchnodes is not at the expected Elasticsearch version $EXPECTED_ES_VERSION."
|
||||
echo "Current versions:"
|
||||
echo "$cluster_versions" | jq '.nodes | to_entries | map({(.value.name): .value.version}) | sort | add'
|
||||
echo "$cluster_versions" >> "$SOUP_INTERMEDIATE_UPGRADE_FAILURES_LOG_FILE"
|
||||
echo -e "\nStopping automatic upgrade to latest version. Searchnodes must ALL be at Elasticsearch version $EXPECTED_ES_VERSION before proceeding with the next upgrade step to avoid potential data loss!"
|
||||
echo -e "\nSearchnodes will upgrade themselves to Elasticsearch $EXPECTED_ES_VERSION on their own, but this process can take a while depending on cluster size / network link between Manager and Searchnodes."
|
||||
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
|
||||
echo "$cluster_versions" > "$SOUP_INTERMEDIATE_UPGRADE_FAILURES_LOG_FILE"
|
||||
|
||||
return 1
|
||||
|
||||
}
|
||||
|
||||
# Need to add a check for heavynodes and ensure all heavynodes get their own "cluster" upgraded before moving on to final upgrade.
|
||||
check_searchnodes_es_version || exit 1
|
||||
check_heavynodes_es_version || exit 1
|
||||
|
||||
# Remove required version state file after successful verification
|
||||
rm -f "$2"
|
||||
|
||||
exit 0
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Keeping this block in case we need to do a hotfix that requires salt update
|
||||
apply_hotfix() {
|
||||
if [[ "$INSTALLEDVERSION" == "2.4.20" ]] ; then
|
||||
@@ -1671,6 +1966,8 @@ main() {
|
||||
echo "Verifying we have the latest soup script."
|
||||
verify_latest_update_script
|
||||
|
||||
verify_es_version_compatibility
|
||||
|
||||
echo "Let's see if we need to update Security Onion."
|
||||
upgrade_check
|
||||
upgrade_space
|
||||
@@ -1836,7 +2133,7 @@ main() {
|
||||
if [[ $is_airgap -eq 0 ]]; then
|
||||
echo ""
|
||||
echo "Cleaning repos on remote Security Onion nodes."
|
||||
salt -C 'not *_eval and not *_manager and not *_managersearch and not *_standalone and G@os:CentOS' cmd.run "yum clean all"
|
||||
salt -C 'not *_eval and not *_manager* and not *_standalone and G@os:OEL' cmd.run "dnf clean all"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
Onion AI Session Report
|
||||
==========================
|
||||
|
||||
## Session Details
|
||||
|
||||
**Session ID:** {{.Session.SessionId}}
|
||||
|
||||
**Title:** {{.Session.Title}}
|
||||
|
||||
**Created:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .Session.CreateTime}}
|
||||
|
||||
**Updated:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .Session.UpdateTime}}
|
||||
|
||||
{{ if .Session.DeleteTime }}
|
||||
**Deleted:** {{ formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .Session.DeleteTime}}
|
||||
{{ end }}
|
||||
|
||||
**User ID:** {{getUserDetail "email" .Session.UserId}}
|
||||
|
||||
## Session Usage
|
||||
|
||||
**Total Input Tokens** {{.Session.Usage.TotalInputTokens}}
|
||||
|
||||
**Total Output Tokens** {{.Session.Usage.TotalOutputTokens}}
|
||||
|
||||
**Total Credits:** {{.Session.Usage.TotalCredits}}
|
||||
|
||||
**Total Messages:** {{.Session.Usage.TotalMessages}}
|
||||
|
||||
## Messages
|
||||
|
||||
{{ range $index, $msg := sortAssistantMessages "CreateTime" "asc" .History }}
|
||||
#### Message {{ add $index 1 }}
|
||||
|
||||
**Created:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" $msg.CreateTime}}
|
||||
|
||||
**User ID:** {{getUserDetail "email" $msg.UserId}}
|
||||
|
||||
**Role:** {{$msg.Message.Role}}
|
||||
|
||||
{{ range $i, $block := $msg.Message.ContentBlocks }}
|
||||
|
||||
---
|
||||
|
||||
{{ if eq $block.Type "text" }}
|
||||
**Text:** {{ stripEmoji $block.Text }}
|
||||
{{ else if eq $block.Type "tool_use" }}
|
||||
**Tool:** {{ $block.Name }}
|
||||
{{ if $block.Input }}
|
||||
**Parameters:**
|
||||
{{ range $key, $value := parseJSON $block.Input }}
|
||||
{{ if eq $key "limit" }}- {{ $key }}: {{ $value }}
|
||||
{{ else }}- {{ $key }}: "{{ $value }}"
|
||||
{{ end }}{{ end }}{{ end }}
|
||||
{{ else if $block.ToolResult }}
|
||||
**Tool Result:**
|
||||
{{ if $block.ToolResult.Content }}
|
||||
{{ range $j, $contentBlock := $block.ToolResult.Content }}
|
||||
{{ if gt $j 0 }}
|
||||
|
||||
---
|
||||
|
||||
{{ end }}
|
||||
{{ if $contentBlock.Text }}
|
||||
{{ if $block.ToolResult.IsError }}
|
||||
**Error:** {{ $contentBlock.Text }}
|
||||
{{ else }}
|
||||
{{ $contentBlock.Text }}
|
||||
{{ end }}
|
||||
{{ else if $contentBlock.Json }}
|
||||
```json
|
||||
{{ toJSON $contentBlock.Json }}
|
||||
```
|
||||
{{ end }}{{ end }}
|
||||
{{ end }}{{ end }}{{ end }}
|
||||
|
||||
{{ if eq $msg.Message.Role "assistant" }}{{ if $msg.Message.Usage }}
|
||||
|
||||
---
|
||||
|
||||
**Message Usage:**
|
||||
|
||||
- Input Tokens: {{$msg.Message.Usage.InputTokens}}
|
||||
- Output Tokens: {{$msg.Message.Usage.OutputTokens}}
|
||||
- Credits: {{$msg.Message.Usage.Credits}}
|
||||
|
||||
{{end}}{{end}}
|
||||
|
||||
---
|
||||
|
||||
{{end}}
|
||||
@@ -130,4 +130,42 @@ Security Onion Case Report
|
||||
| ---- | ---- | ------ | --------- |
|
||||
{{ range sortHistory "CreateTime" "asc" .History -}}
|
||||
| {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" .CreateTime}} | {{getUserDetail "email" .UserId}} | {{.Kind}} | {{.Operation}} |
|
||||
{{end}}
|
||||
|
||||
## Attached Onion AI Sessions
|
||||
|
||||
{{ range $idx, $session := sortAssistantSessionDetails "CreateTime" "desc" .AssistantSessions }}
|
||||
|
||||
#### Session {{ add $idx 1 }}
|
||||
|
||||
**Session ID:** {{$session.Session.SessionId}}
|
||||
|
||||
**Title:** {{$session.Session.Title}}
|
||||
|
||||
**User ID:** {{getUserDetail "email" $session.Session.UserId}}
|
||||
|
||||
**Created:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" $session.Session.CreateTime}}
|
||||
|
||||
**Updated:** {{formatDateTime "Mon Jan 02 15:04:05 -0700 2006" $session.Session.UpdateTime}}
|
||||
|
||||
{{ if $session.Session.DeleteTime }}
|
||||
**Deleted:** {{ formatDateTime "Mon Jan 02 15:04:05 -0700 2006" $session.Session.DeleteTime}}
|
||||
{{ end }}
|
||||
|
||||
#### Messages
|
||||
|
||||
{{ range $index, $msg := sortAssistantMessages "CreateTime" "asc" $session.History }}
|
||||
{{ range $i, $block := $msg.Message.ContentBlocks }}
|
||||
|
||||
{{ if eq $block.Type "text" }}
|
||||
|
||||
**Role:** {{$msg.Message.Role}}
|
||||
|
||||
{{ stripEmoji $block.Text }}
|
||||
|
||||
---
|
||||
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -357,7 +357,7 @@ sensoroni:
|
||||
reports:
|
||||
standard:
|
||||
case_report__md:
|
||||
title: Case report Template
|
||||
title: Case Report Template
|
||||
description: The template used when generating a case report. Supports markdown format.
|
||||
file: True
|
||||
global: True
|
||||
@@ -370,6 +370,13 @@ sensoroni:
|
||||
global: True
|
||||
syntax: md
|
||||
helpLink: reports.html
|
||||
assistant_session_report__md:
|
||||
title: Assistant Session Report Template
|
||||
description: The template used when generating an assistant session report. Supports markdown format.
|
||||
file: True
|
||||
global: True
|
||||
syntax: md
|
||||
helplink: reports.html
|
||||
custom:
|
||||
generic_report1__md:
|
||||
title: Custom Report 1
|
||||
|
||||
@@ -115,16 +115,16 @@ soc:
|
||||
':kratos:':
|
||||
- soc_timestamp
|
||||
- event.dataset
|
||||
- http_request.headers.x-real-ip
|
||||
- http.request.headers.x-real-ip
|
||||
- user.name
|
||||
- http_request.headers.user-agent
|
||||
- http.useragent
|
||||
- msg
|
||||
':hydra:':
|
||||
- soc_timestamp
|
||||
- event.dataset
|
||||
- http_request.headers.x-real-ip
|
||||
- http.request.headers.x-real-ip
|
||||
- user.name
|
||||
- http_request.headers.user-agent
|
||||
- http.useragent
|
||||
- msg
|
||||
'::conn':
|
||||
- soc_timestamp
|
||||
@@ -1622,12 +1622,11 @@ soc:
|
||||
sourceType: directory
|
||||
airgap:
|
||||
- name: Emerging-Threats
|
||||
description: "Emerging Threats ruleset - To enable ET Pro, enter your license key below. Leave empty for ET Open (free) rules."
|
||||
description: "Emerging Threats ruleset - To enable ET Pro on Airgap, review the documentation at https://docs.securityonion.net/suricata"
|
||||
licenseKey: ""
|
||||
enabled: true
|
||||
sourceType: url
|
||||
sourcePath: 'https://rules.emergingthreats.net/open/suricata/emerging.rules.tar.gz'
|
||||
urlHash: "https://rules.emergingthreats.net/open/suricata/emerging.rules.tar.gz.md5"
|
||||
sourceType: directory
|
||||
sourcePath: /nsm/rules/suricata/etopen/
|
||||
license: "BSD"
|
||||
excludeFiles:
|
||||
- "*deleted*"
|
||||
@@ -1748,7 +1747,7 @@ soc:
|
||||
showSubtitle: true
|
||||
- name: SOC - Auth
|
||||
description: Users authenticated to SOC grouped by IP address and identity
|
||||
query: 'event.dataset:kratos.audit AND msg:*authenticated* | groupby http_request.headers.x-real-ip user.name'
|
||||
query: 'event.dataset:kratos.audit AND msg:*authenticated* | groupby http.request.headers.x-real-ip user.name'
|
||||
showSubtitle: true
|
||||
- name: SOC - App
|
||||
description: Logs generated by the Security Onion Console (SOC) server and modules
|
||||
@@ -2028,10 +2027,10 @@ soc:
|
||||
query: '* | groupby event.category | groupby -sankey event.category event.module | groupby event.module | groupby -sankey event.module event.dataset | groupby event.dataset | groupby observer.name | groupby host.name | groupby source.ip | groupby destination.ip | groupby destination.port'
|
||||
- name: SOC Logins
|
||||
description: SOC (Security Onion Console) logins
|
||||
query: 'event.dataset:kratos.audit AND msg:*authenticated* | groupby http_request.headers.x-real-ip | groupby -sankey http_request.headers.x-real-ip user.name | groupby user.name | groupby http_request.headers.user-agent'
|
||||
query: 'event.dataset:kratos.audit AND msg:*authenticated* | groupby http.request.headers.x-real-ip | groupby -sankey http.request.headers.x-real-ip user.name | groupby user.name | groupby http.useragent'
|
||||
- name: SOC Login Failures
|
||||
description: SOC (Security Onion Console) login failures
|
||||
query: 'event.dataset:kratos.audit AND msg:*Encountered*self-service*login*error* | groupby user.name | groupby http_request.headers.x-real-ip | groupby -sankey http_request.headers.x-real-ip http_request.headers.user-agent | groupby http_request.headers.user-agent'
|
||||
query: 'event.dataset:kratos.audit AND msg:*Encountered*self-service*login*error* | groupby user.name | groupby http.request.headers.x-real-ip | groupby -sankey http.request.headers.x-real-ip http.useragent | groupby http.useragent'
|
||||
- name: Alerts
|
||||
description: Overview of all alerts
|
||||
query: 'tags:alert | groupby event.module* | groupby source.ip | groupby -sankey source.ip destination.ip | groupby destination.ip | groupby destination.port | groupby rule.name | groupby event.severity | groupby destination.as.organization.name'
|
||||
@@ -2653,26 +2652,16 @@ soc:
|
||||
thresholdColorRatioMed: 0.75
|
||||
thresholdColorRatioMax: 1
|
||||
availableModels:
|
||||
- id: sonnet-4
|
||||
displayName: Claude Sonnet 4
|
||||
contextLimitSmall: 200000
|
||||
contextLimitLarge: 1000000
|
||||
lowBalanceColorAlert: 500000
|
||||
enabled: true
|
||||
- id: sonnet-4.5
|
||||
displayName: Claude Sonnet 4.5
|
||||
displayName: Claude Sonnet 4.5 ($$$)
|
||||
origin: USA
|
||||
contextLimitSmall: 200000
|
||||
contextLimitLarge: 1000000
|
||||
lowBalanceColorAlert: 500000
|
||||
enabled: true
|
||||
- id: gptoss-120b
|
||||
displayName: GPT-OSS 120B
|
||||
contextLimitSmall: 128000
|
||||
contextLimitLarge: 128000
|
||||
lowBalanceColorAlert: 500000
|
||||
enabled: true
|
||||
- id: qwen-235b
|
||||
displayName: QWEN 235B
|
||||
displayName: QWEN 235B ($)
|
||||
origin: China
|
||||
contextLimitSmall: 256000
|
||||
contextLimitLarge: 256000
|
||||
lowBalanceColorAlert: 500000
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# This script queries Elasticsearch for Custom Detections and all Overrides,
|
||||
# and git commits them to disk at $OUTPUT_DIR
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
@@ -18,10 +19,10 @@ from datetime import datetime
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
# Constants
|
||||
ES_URL = "https://localhost:9200/so-detection/_search"
|
||||
DEFAULT_INDEX = "so-detection"
|
||||
DEFAULT_OUTPUT_DIR = "/nsm/backup/detections/repo"
|
||||
QUERY_DETECTIONS = '{"query": {"bool": {"must": [{"match_all": {}}, {"term": {"so_detection.ruleset": "__custom__"}}]}},"size": 10000}'
|
||||
QUERY_OVERRIDES = '{"query": {"bool": {"must": [{"exists": {"field": "so_detection.overrides"}}]}},"size": 10000}'
|
||||
OUTPUT_DIR = "/nsm/backup/detections/repo"
|
||||
AUTH_FILE = "/opt/so/conf/elasticsearch/curl.config"
|
||||
|
||||
def get_auth_credentials(auth_file):
|
||||
@@ -30,9 +31,10 @@ def get_auth_credentials(auth_file):
|
||||
if line.startswith('user ='):
|
||||
return line.split('=', 1)[1].strip().replace('"', '')
|
||||
|
||||
def query_elasticsearch(query, auth):
|
||||
def query_elasticsearch(query, auth, index):
|
||||
url = f"https://localhost:9200/{index}/_search"
|
||||
headers = {"Content-Type": "application/json"}
|
||||
response = requests.get(ES_URL, headers=headers, data=query, auth=auth, verify=False)
|
||||
response = requests.get(url, headers=headers, data=query, auth=auth, verify=False)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
@@ -47,12 +49,12 @@ def save_content(hit, base_folder, subfolder="", extension="txt"):
|
||||
f.write(content)
|
||||
return file_path
|
||||
|
||||
def save_overrides(hit):
|
||||
def save_overrides(hit, output_dir):
|
||||
so_detection = hit["_source"]["so_detection"]
|
||||
public_id = so_detection["publicId"]
|
||||
overrides = so_detection["overrides"]
|
||||
language = so_detection["language"]
|
||||
folder = os.path.join(OUTPUT_DIR, language, "overrides")
|
||||
folder = os.path.join(output_dir, language, "overrides")
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
extension = "yaml" if language == "sigma" else "txt"
|
||||
file_path = os.path.join(folder, f"{public_id}.{extension}")
|
||||
@@ -60,20 +62,20 @@ def save_overrides(hit):
|
||||
f.write('\n'.join(json.dumps(override) for override in overrides) if isinstance(overrides, list) else overrides)
|
||||
return file_path
|
||||
|
||||
def ensure_git_repo():
|
||||
if not os.path.isdir(os.path.join(OUTPUT_DIR, '.git')):
|
||||
def ensure_git_repo(output_dir):
|
||||
if not os.path.isdir(os.path.join(output_dir, '.git')):
|
||||
subprocess.run(["git", "config", "--global", "init.defaultBranch", "main"], check=True)
|
||||
subprocess.run(["git", "-C", OUTPUT_DIR, "init"], check=True)
|
||||
subprocess.run(["git", "-C", OUTPUT_DIR, "remote", "add", "origin", "default"], check=True)
|
||||
subprocess.run(["git", "-C", output_dir, "init"], check=True)
|
||||
subprocess.run(["git", "-C", output_dir, "remote", "add", "origin", "default"], check=True)
|
||||
|
||||
def commit_changes():
|
||||
ensure_git_repo()
|
||||
subprocess.run(["git", "-C", OUTPUT_DIR, "config", "user.email", "securityonion@local.invalid"], check=True)
|
||||
subprocess.run(["git", "-C", OUTPUT_DIR, "config", "user.name", "securityonion"], check=True)
|
||||
subprocess.run(["git", "-C", OUTPUT_DIR, "add", "."], check=True)
|
||||
status_result = subprocess.run(["git", "-C", OUTPUT_DIR, "status"], capture_output=True, text=True)
|
||||
def commit_changes(output_dir):
|
||||
ensure_git_repo(output_dir)
|
||||
subprocess.run(["git", "-C", output_dir, "config", "user.email", "securityonion@local.invalid"], check=True)
|
||||
subprocess.run(["git", "-C", output_dir, "config", "user.name", "securityonion"], check=True)
|
||||
subprocess.run(["git", "-C", output_dir, "add", "."], check=True)
|
||||
status_result = subprocess.run(["git", "-C", output_dir, "status"], capture_output=True, text=True)
|
||||
print(status_result.stdout)
|
||||
commit_result = subprocess.run(["git", "-C", OUTPUT_DIR, "commit", "-m", "Update detections and overrides"], check=False, capture_output=True)
|
||||
commit_result = subprocess.run(["git", "-C", output_dir, "commit", "-m", "Update detections and overrides"], check=False, capture_output=True)
|
||||
if commit_result.returncode == 1:
|
||||
print("No changes to commit.")
|
||||
elif commit_result.returncode == 0:
|
||||
@@ -81,29 +83,41 @@ def commit_changes():
|
||||
else:
|
||||
commit_result.check_returncode()
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Backup custom detections and overrides from Elasticsearch")
|
||||
parser.add_argument("--output", "-o", default=DEFAULT_OUTPUT_DIR,
|
||||
help=f"Output directory for backups (default: {DEFAULT_OUTPUT_DIR})")
|
||||
parser.add_argument("--index", "-i", default=DEFAULT_INDEX,
|
||||
help=f"Elasticsearch index to query (default: {DEFAULT_INDEX})")
|
||||
return parser.parse_args()
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
output_dir = args.output
|
||||
index = args.index
|
||||
|
||||
try:
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"Backing up Custom Detections and all Overrides to {OUTPUT_DIR} - {timestamp}\n")
|
||||
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
print(f"Backing up Custom Detections and all Overrides to {output_dir} - {timestamp}\n")
|
||||
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
auth_credentials = get_auth_credentials(AUTH_FILE)
|
||||
username, password = auth_credentials.split(':', 1)
|
||||
auth = HTTPBasicAuth(username, password)
|
||||
|
||||
|
||||
# Query and save custom detections
|
||||
detections = query_elasticsearch(QUERY_DETECTIONS, auth)["hits"]["hits"]
|
||||
detections = query_elasticsearch(QUERY_DETECTIONS, auth, index)["hits"]["hits"]
|
||||
for hit in detections:
|
||||
save_content(hit, OUTPUT_DIR, hit["_source"]["so_detection"]["language"], "yaml" if hit["_source"]["so_detection"]["language"] == "sigma" else "txt")
|
||||
|
||||
save_content(hit, output_dir, hit["_source"]["so_detection"]["language"], "yaml" if hit["_source"]["so_detection"]["language"] == "sigma" else "txt")
|
||||
|
||||
# Query and save overrides
|
||||
overrides = query_elasticsearch(QUERY_OVERRIDES, auth)["hits"]["hits"]
|
||||
overrides = query_elasticsearch(QUERY_OVERRIDES, auth, index)["hits"]["hits"]
|
||||
for hit in overrides:
|
||||
save_overrides(hit)
|
||||
|
||||
commit_changes()
|
||||
|
||||
save_overrides(hit, output_dir)
|
||||
|
||||
commit_changes(output_dir)
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"Backup Completed - {timestamp}")
|
||||
except Exception as e:
|
||||
|
||||
@@ -57,12 +57,12 @@ class TestBackupScript(unittest.TestCase):
|
||||
mock_response.json.return_value = {'hits': {'hits': []}}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
response = ds.query_elasticsearch(ds.QUERY_DETECTIONS, self.auth)
|
||||
|
||||
|
||||
response = ds.query_elasticsearch(ds.QUERY_DETECTIONS, self.auth, ds.DEFAULT_INDEX)
|
||||
|
||||
self.assertEqual(response, {'hits': {'hits': []}})
|
||||
mock_get.assert_called_once_with(
|
||||
ds.ES_URL,
|
||||
f"https://localhost:9200/{ds.DEFAULT_INDEX}/_search",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=ds.QUERY_DETECTIONS,
|
||||
auth=self.auth,
|
||||
@@ -81,7 +81,7 @@ class TestBackupScript(unittest.TestCase):
|
||||
@patch('os.makedirs')
|
||||
@patch('builtins.open', new_callable=mock_open)
|
||||
def test_save_overrides(self, mock_file, mock_makedirs):
|
||||
file_path = ds.save_overrides(self.mock_override_hit)
|
||||
file_path = ds.save_overrides(self.mock_override_hit, self.output_dir)
|
||||
expected_path = f'{self.output_dir}/sigma/overrides/test_id.yaml'
|
||||
self.assertEqual(file_path, expected_path)
|
||||
mock_makedirs.assert_called_once_with(f'{self.output_dir}/sigma/overrides', exist_ok=True)
|
||||
@@ -90,9 +90,9 @@ class TestBackupScript(unittest.TestCase):
|
||||
@patch('subprocess.run')
|
||||
def test_ensure_git_repo(self, mock_run):
|
||||
mock_run.return_value = MagicMock(returncode=0)
|
||||
|
||||
ds.ensure_git_repo()
|
||||
|
||||
|
||||
ds.ensure_git_repo(self.output_dir)
|
||||
|
||||
mock_run.assert_has_calls([
|
||||
call(["git", "config", "--global", "init.defaultBranch", "main"], check=True),
|
||||
call(["git", "-C", self.output_dir, "init"], check=True),
|
||||
@@ -106,9 +106,9 @@ class TestBackupScript(unittest.TestCase):
|
||||
mock_commit_result = MagicMock(returncode=1)
|
||||
# Ensure sufficient number of MagicMock instances for each subprocess.run call
|
||||
mock_run.side_effect = [mock_status_result, mock_commit_result, MagicMock(returncode=0), MagicMock(returncode=0), MagicMock(returncode=0), MagicMock(returncode=0), MagicMock(returncode=0), MagicMock(returncode=0)]
|
||||
|
||||
|
||||
print("Running test_commit_changes...")
|
||||
ds.commit_changes()
|
||||
ds.commit_changes(self.output_dir)
|
||||
print("Finished test_commit_changes.")
|
||||
|
||||
mock_run.assert_has_calls([
|
||||
@@ -120,39 +120,45 @@ class TestBackupScript(unittest.TestCase):
|
||||
])
|
||||
|
||||
@patch('builtins.print')
|
||||
@patch('so-detections-backup.commit_changes')
|
||||
@patch('so-detections-backup.save_overrides')
|
||||
@patch('so-detections-backup.save_content')
|
||||
@patch('so-detections-backup.query_elasticsearch')
|
||||
@patch('so-detections-backup.get_auth_credentials')
|
||||
@patch.object(ds, 'commit_changes')
|
||||
@patch.object(ds, 'save_overrides')
|
||||
@patch.object(ds, 'save_content')
|
||||
@patch.object(ds, 'query_elasticsearch')
|
||||
@patch.object(ds, 'get_auth_credentials')
|
||||
@patch('os.makedirs')
|
||||
def test_main(self, mock_makedirs, mock_get_auth, mock_query, mock_save_content, mock_save_overrides, mock_commit, mock_print):
|
||||
@patch.object(ds, 'parse_args')
|
||||
def test_main(self, mock_parse_args, mock_makedirs, mock_get_auth, mock_query, mock_save_content, mock_save_overrides, mock_commit, mock_print):
|
||||
mock_args = MagicMock()
|
||||
mock_args.output = self.output_dir
|
||||
mock_args.index = ds.DEFAULT_INDEX
|
||||
mock_parse_args.return_value = mock_args
|
||||
mock_get_auth.return_value = self.auth_credentials
|
||||
mock_query.side_effect = [
|
||||
{'hits': {'hits': [{"_source": {"so_detection": {"publicId": "1", "content": "content1", "language": "sigma"}}}]}},
|
||||
{'hits': {'hits': [{"_source": {"so_detection": {"publicId": "2", "overrides": [{"key": "value"}], "language": "suricata"}}}]}}
|
||||
]
|
||||
|
||||
|
||||
with patch('datetime.datetime') as mock_datetime:
|
||||
mock_datetime.now.return_value.strftime.return_value = "2024-05-23 20:49:44"
|
||||
ds.main()
|
||||
|
||||
|
||||
mock_makedirs.assert_called_once_with(self.output_dir, exist_ok=True)
|
||||
mock_get_auth.assert_called_once_with(ds.AUTH_FILE)
|
||||
mock_query.assert_has_calls([
|
||||
call(ds.QUERY_DETECTIONS, self.auth),
|
||||
call(ds.QUERY_OVERRIDES, self.auth)
|
||||
call(ds.QUERY_DETECTIONS, self.auth, ds.DEFAULT_INDEX),
|
||||
call(ds.QUERY_OVERRIDES, self.auth, ds.DEFAULT_INDEX)
|
||||
])
|
||||
mock_save_content.assert_called_once_with(
|
||||
{"_source": {"so_detection": {"publicId": "1", "content": "content1", "language": "sigma"}}},
|
||||
self.output_dir,
|
||||
"sigma",
|
||||
{"_source": {"so_detection": {"publicId": "1", "content": "content1", "language": "sigma"}}},
|
||||
self.output_dir,
|
||||
"sigma",
|
||||
"yaml"
|
||||
)
|
||||
mock_save_overrides.assert_called_once_with(
|
||||
{"_source": {"so_detection": {"publicId": "2", "overrides": [{"key": "value"}], "language": "suricata"}}}
|
||||
{"_source": {"so_detection": {"publicId": "2", "overrides": [{"key": "value"}], "language": "suricata"}}},
|
||||
self.output_dir
|
||||
)
|
||||
mock_commit.assert_called_once()
|
||||
mock_commit.assert_called_once_with(self.output_dir)
|
||||
mock_print.assert_called()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
{# Define the Detections custom ruleset that should always be present #}
|
||||
{% set CUSTOM_RULESET = {
|
||||
'name': 'custom',
|
||||
'name': '__custom__',
|
||||
'description': 'User-created custom rules created via the Detections module in the SOC UI',
|
||||
'sourceType': 'elasticsearch',
|
||||
'sourcePath': 'so_detection.ruleset:__custom__',
|
||||
@@ -83,7 +83,7 @@
|
||||
{# Always append the custom ruleset to suricataengine.rulesetSources if not already present #}
|
||||
{% if SOCMERGED.config.server.modules.suricataengine is defined and SOCMERGED.config.server.modules.suricataengine.rulesetSources is defined %}
|
||||
{% if SOCMERGED.config.server.modules.suricataengine.rulesetSources is not mapping %}
|
||||
{% set custom_names = SOCMERGED.config.server.modules.suricataengine.rulesetSources | selectattr('name', 'equalto', 'custom') | list %}
|
||||
{% set custom_names = SOCMERGED.config.server.modules.suricataengine.rulesetSources | selectattr('name', 'equalto', '__custom__') | list %}
|
||||
{% if custom_names | length == 0 %}
|
||||
{% do SOCMERGED.config.server.modules.suricataengine.rulesetSources.append(CUSTOM_RULESET) %}
|
||||
{% endif %}
|
||||
@@ -108,21 +108,39 @@
|
||||
{% if ruleset.name == 'Emerging-Threats' %}
|
||||
{% if ruleset.licenseKey and ruleset.licenseKey != '' %}
|
||||
{# License key is defined - transform to ETPRO #}
|
||||
{# Engine Version is hardcoded in the URL - this does not change often: https://community.emergingthreats.net/t/supported-engines/71 #}
|
||||
{% do ruleset.update({
|
||||
'name': 'ETPRO',
|
||||
'sourcePath': 'https://rules.emergingthreatspro.com/' ~ ruleset.licenseKey ~ '/suricata-7.0.3/etpro.rules.tar.gz',
|
||||
'urlHash': 'https://rules.emergingthreatspro.com/' ~ ruleset.licenseKey ~ '/suricata-7.0.3/etpro.rules.tar.gz.md5',
|
||||
'license': 'Commercial'
|
||||
}) %}
|
||||
{% if ruleset.sourceType == 'directory' %}
|
||||
{# Airgap mode - update directory path #}
|
||||
{% do ruleset.update({
|
||||
'name': 'ETPRO',
|
||||
'sourcePath': '/nsm/rules/custom-local-repos/local-etpro-suricata/etpro.rules.tar.gz',
|
||||
'license': 'Commercial'
|
||||
}) %}
|
||||
{% else %}
|
||||
{# Engine Version is hardcoded in the URL - this does not change often: https://community.emergingthreats.net/t/supported-engines/71 #}
|
||||
{% do ruleset.update({
|
||||
'name': 'ETPRO',
|
||||
'sourcePath': 'https://rules.emergingthreatspro.com/' ~ ruleset.licenseKey ~ '/suricata-7.0.3/etpro.rules.tar.gz',
|
||||
'urlHash': 'https://rules.emergingthreatspro.com/' ~ ruleset.licenseKey ~ '/suricata-7.0.3/etpro.rules.tar.gz.md5',
|
||||
'license': 'Commercial'
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{# No license key - explicitly set to ETOPEN #}
|
||||
{% do ruleset.update({
|
||||
'name': 'ETOPEN',
|
||||
'sourcePath': 'https://rules.emergingthreats.net/open/suricata-7.0.3/emerging.rules.tar.gz',
|
||||
'urlHash': 'https://rules.emergingthreats.net/open/suricata-7.0.3/emerging.rules.tar.gz.md5',
|
||||
'license': 'BSD'
|
||||
}) %}
|
||||
{% if ruleset.sourceType == 'directory' %}
|
||||
{# Airgap mode - update directory path #}
|
||||
{% do ruleset.update({
|
||||
'name': 'ETOPEN',
|
||||
'sourcePath': '/nsm/rules/suricata/etopen/',
|
||||
'license': 'BSD'
|
||||
}) %}
|
||||
{% else %}
|
||||
{% do ruleset.update({
|
||||
'name': 'ETOPEN',
|
||||
'sourcePath': 'https://rules.emergingthreats.net/open/suricata-7.0.3/emerging.rules.tar.gz',
|
||||
'urlHash': 'https://rules.emergingthreats.net/open/suricata-7.0.3/emerging.rules.tar.gz.md5',
|
||||
'license': 'BSD'
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -608,6 +608,18 @@ soc:
|
||||
label: Delete Unreferenced (Deletes rules that are no longer referenced by ruleset source)
|
||||
forcedType: bool
|
||||
required: False
|
||||
- field: proxyURL
|
||||
label: HTTP/HTTPS proxy URL for downloading the ruleset.
|
||||
required: False
|
||||
- field: proxyUsername
|
||||
label: Proxy authentication username.
|
||||
required: False
|
||||
- field: proxyPassword
|
||||
label: Proxy authentication password.
|
||||
required: False
|
||||
- field: proxyCACert
|
||||
label: Path to CA certificate file for MITM proxy verification.
|
||||
required: False
|
||||
airgap: *serulesetSources
|
||||
navigator:
|
||||
intervalMinutes:
|
||||
@@ -640,7 +652,6 @@ soc:
|
||||
assistant:
|
||||
apiUrl:
|
||||
description: The URL of the AI gateway.
|
||||
advanced: True
|
||||
global: True
|
||||
healthTimeoutSeconds:
|
||||
description: Timeout in seconds for the Onion AI health check.
|
||||
@@ -696,6 +707,9 @@ soc:
|
||||
- field: displayName
|
||||
label: Display Name
|
||||
required: True
|
||||
- field: origin
|
||||
label: Country of Origin for the Model Training
|
||||
required: false
|
||||
- field: contextLimitSmall
|
||||
label: Context Limit (Small)
|
||||
forcedType: int
|
||||
|
||||
@@ -124,7 +124,7 @@ surirulesync:
|
||||
- name: /opt/so/rules/suricata/
|
||||
- source: salt://suricata/rules/
|
||||
- user: 940
|
||||
- group: 940
|
||||
- group: 939
|
||||
- show_changes: False
|
||||
|
||||
surilogscript:
|
||||
@@ -160,7 +160,6 @@ surithresholding:
|
||||
- source: salt://suricata/files/threshold.conf
|
||||
- user: 940
|
||||
- group: 940
|
||||
- onlyif: salt://suricata/files/threshold.conf
|
||||
|
||||
suriclassifications:
|
||||
file.managed:
|
||||
@@ -178,6 +177,14 @@ so-suricata-eve-clean:
|
||||
- template: jinja
|
||||
- source: salt://suricata/cron/so-suricata-eve-clean
|
||||
|
||||
so-suricata-rulestats:
|
||||
file.managed:
|
||||
- name: /usr/sbin/so-suricata-rulestats
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: 755
|
||||
- source: salt://suricata/cron/so-suricata-rulestats
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
|
||||
39
salt/suricata/cron/so-suricata-rulestats
Normal file
39
salt/suricata/cron/so-suricata-rulestats
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Query Suricata for ruleset stats and reload time, write to JSON file for Telegraf to consume
|
||||
|
||||
OUTFILE="/opt/so/log/suricata/rulestats.json"
|
||||
SURICATASC="docker exec so-suricata /opt/suricata/bin/suricatasc"
|
||||
SOCKET="/var/run/suricata/suricata-command.socket"
|
||||
|
||||
query() {
|
||||
timeout 10 $SURICATASC -c "$1" "$SOCKET" 2>/dev/null
|
||||
}
|
||||
|
||||
STATS=$(query "ruleset-stats")
|
||||
RELOAD=$(query "ruleset-reload-time")
|
||||
[ -z "$RELOAD" ] && RELOAD='{}'
|
||||
|
||||
# Outputs valid JSON on success, empty on failure
|
||||
OUTPUT=$(jq -n \
|
||||
--argjson stats "$STATS" \
|
||||
--argjson reload "$RELOAD" \
|
||||
'if $stats.return == "OK" and ($stats.message[0].rules_loaded | type) == "number" and ($stats.message[0].rules_failed | type) == "number" then
|
||||
{
|
||||
rules_loaded: $stats.message[0].rules_loaded,
|
||||
rules_failed: $stats.message[0].rules_failed,
|
||||
last_reload: ($reload.message[0].last_reload // ""),
|
||||
return: "OK"
|
||||
}
|
||||
else empty end' 2>/dev/null)
|
||||
|
||||
if [ -n "$OUTPUT" ]; then
|
||||
echo "$OUTPUT" > "$OUTFILE"
|
||||
else
|
||||
echo '{"return":"FAIL"}' > "$OUTFILE"
|
||||
fi
|
||||
@@ -23,6 +23,11 @@ clean_suricata_eve_files:
|
||||
cron.absent:
|
||||
- identifier: clean_suricata_eve_files
|
||||
|
||||
# Remove rulestats cron
|
||||
rulestats:
|
||||
cron.absent:
|
||||
- identifier: suricata_rulestats
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
|
||||
@@ -90,6 +90,18 @@ clean_suricata_eve_files:
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
|
||||
# Add rulestats cron - runs every minute to query Suricata for rule load status
|
||||
suricata_rulestats:
|
||||
cron.present:
|
||||
- name: /usr/sbin/so-suricata-rulestats > /dev/null 2>&1
|
||||
- identifier: suricata_rulestats
|
||||
- user: root
|
||||
- minute: '*'
|
||||
- hour: '*'
|
||||
- daymonth: '*'
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
|
||||
2
salt/suricata/files/threshold.conf
Normal file
2
salt/suricata/files/threshold.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
# Threshold configuration generated by Security Onion
|
||||
# This file is automatically generated - do not edit manually
|
||||
@@ -21,6 +21,7 @@ telegraf:
|
||||
- sostatus.sh
|
||||
- stenoloss.sh
|
||||
- suriloss.sh
|
||||
- surirules.sh
|
||||
- zeekcaptureloss.sh
|
||||
- zeekloss.sh
|
||||
standalone:
|
||||
@@ -36,6 +37,7 @@ telegraf:
|
||||
- sostatus.sh
|
||||
- stenoloss.sh
|
||||
- suriloss.sh
|
||||
- surirules.sh
|
||||
- zeekcaptureloss.sh
|
||||
- zeekloss.sh
|
||||
- features.sh
|
||||
@@ -81,6 +83,7 @@ telegraf:
|
||||
- sostatus.sh
|
||||
- stenoloss.sh
|
||||
- suriloss.sh
|
||||
- surirules.sh
|
||||
- zeekcaptureloss.sh
|
||||
- zeekloss.sh
|
||||
- features.sh
|
||||
@@ -95,6 +98,7 @@ telegraf:
|
||||
- sostatus.sh
|
||||
- stenoloss.sh
|
||||
- suriloss.sh
|
||||
- surirules.sh
|
||||
- zeekcaptureloss.sh
|
||||
- zeekloss.sh
|
||||
idh:
|
||||
|
||||
34
salt/telegraf/scripts/surirules.sh
Normal file
34
salt/telegraf/scripts/surirules.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Read Suricata ruleset stats from JSON file written by so-suricata-rulestats cron job
|
||||
# JSON format: {"rules_loaded":45879,"rules_failed":1,"last_reload":"2025-12-04T14:10:57+0000","return":"OK"}
|
||||
# or on failure: {"return":"FAIL"}
|
||||
|
||||
# if this script isn't already running
|
||||
if [[ ! "`pidof -x $(basename $0) -o %PPID`" ]]; then
|
||||
|
||||
STATSFILE="/var/log/suricata/rulestats.json"
|
||||
|
||||
# Check file exists, is less than 90 seconds old, and has valid data
|
||||
if [ -f "$STATSFILE" ] && [ $(($(date +%s) - $(stat -c %Y "$STATSFILE"))) -lt 90 ] && jq -e '.return == "OK" and .rules_loaded != null and .rules_failed != null' "$STATSFILE" > /dev/null 2>&1; then
|
||||
LOADED=$(jq -r '.rules_loaded' "$STATSFILE")
|
||||
FAILED=$(jq -r '.rules_failed' "$STATSFILE")
|
||||
RELOAD_TIME=$(jq -r 'if .last_reload then .last_reload else "" end' "$STATSFILE")
|
||||
|
||||
if [ -n "$RELOAD_TIME" ]; then
|
||||
echo "surirules loaded=${LOADED}i,failed=${FAILED}i,reload_time=\"${RELOAD_TIME}\",status=\"ok\""
|
||||
else
|
||||
echo "surirules loaded=${LOADED}i,failed=${FAILED}i,status=\"ok\""
|
||||
fi
|
||||
else
|
||||
echo "surirules loaded=0i,failed=0i,status=\"unknown\""
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -656,11 +656,11 @@ check_requirements() {
|
||||
fi
|
||||
|
||||
if [[ $total_mem_hr -lt $req_mem ]]; then
|
||||
whiptail_requirements_error "memory" "${total_mem_hr} GB" "${req_mem} GB"
|
||||
if [[ $is_standalone || $is_heavynode ]]; then
|
||||
echo "This install type will fail with less than $req_mem GB of memory. Exiting setup."
|
||||
exit 0
|
||||
fi
|
||||
whiptail_requirements_error "memory" "${total_mem_hr} GB" "${req_mem} GB"
|
||||
fi
|
||||
if [[ $is_standalone || $is_heavynode ]]; then
|
||||
if [[ $total_mem_hr -gt 15 && $total_mem_hr -lt 24 ]]; then
|
||||
@@ -1598,16 +1598,21 @@ proxy_validate() {
|
||||
|
||||
reserve_group_ids() {
|
||||
# This is a hack to fix OS from taking group IDs that we need
|
||||
logCmd "groupadd -g 920 docker"
|
||||
logCmd "groupadd -g 928 kratos"
|
||||
logCmd "groupadd -g 930 elasticsearch"
|
||||
logCmd "groupadd -g 931 logstash"
|
||||
logCmd "groupadd -g 932 kibana"
|
||||
logCmd "groupadd -g 933 elastalert"
|
||||
logCmd "groupadd -g 937 zeek"
|
||||
logCmd "groupadd -g 938 salt"
|
||||
logCmd "groupadd -g 939 socore"
|
||||
logCmd "groupadd -g 940 suricata"
|
||||
logCmd "groupadd -g 948 elastic-agent-pr"
|
||||
logCmd "groupadd -g 949 elastic-agent"
|
||||
logCmd "groupadd -g 941 stenographer"
|
||||
logCmd "groupadd -g 945 ossec"
|
||||
logCmd "groupadd -g 946 cyberchef"
|
||||
logCmd "groupadd -g 947 elastic-fleet"
|
||||
logCmd "groupadd -g 960 kafka"
|
||||
}
|
||||
|
||||
reserve_ports() {
|
||||
|
||||
@@ -682,6 +682,8 @@ if ! [[ -f $install_opt_file ]]; then
|
||||
fi
|
||||
info "Reserving ports"
|
||||
reserve_ports
|
||||
info "Reserving group ids"
|
||||
reserve_group_ids
|
||||
info "Setting Paths"
|
||||
# Set the paths
|
||||
set_path
|
||||
@@ -840,7 +842,10 @@ if ! [[ -f $install_opt_file ]]; then
|
||||
if [[ $monints ]]; then
|
||||
configure_network_sensor
|
||||
fi
|
||||
info "Reserving ports"
|
||||
reserve_ports
|
||||
info "Reserving group ids"
|
||||
reserve_group_ids
|
||||
# Set the version
|
||||
mark_version
|
||||
# Disable the setup from prompting at login
|
||||
|
||||
BIN
sigs/securityonion-2.4.200-20251216.iso.sig
Normal file
BIN
sigs/securityonion-2.4.200-20251216.iso.sig
Normal file
Binary file not shown.
Reference in New Issue
Block a user