mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-02-20 14:05:26 +01:00
Compare commits
296 Commits
87477ae4f6
...
mwright/ge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90eee49ab6 | ||
|
|
f025886b31 | ||
|
|
7fa01f5fd5 | ||
|
|
93f52453b4 | ||
|
|
a9307aa308 | ||
|
|
8fc3011f92 | ||
|
|
911c9d56db | ||
|
|
c1273c3d2c | ||
|
|
d0018c9333 | ||
|
|
3349c1a936 | ||
|
|
32819c8635 | ||
|
|
58c0a9183c | ||
|
|
7dfd212519 | ||
|
|
b8fb0fa735 | ||
|
|
e6f767b613 | ||
|
|
d00fb4ccf7 | ||
|
|
a29eff37a0 | ||
|
|
534a0ad41f | ||
|
|
4c86275cd6 | ||
|
|
a1c806a944 | ||
|
|
3d1a2c12ec | ||
|
|
8538e5572e | ||
|
|
9b525612a8 | ||
|
|
fb364aec5d | ||
|
|
ed014b431e | ||
|
|
82ca64d66f | ||
|
|
7e0fb73fec | ||
|
|
c28bcfa85e | ||
|
|
be6d94d65b | ||
|
|
ada463320b | ||
|
|
2b05583035 | ||
|
|
4d6b2de374 | ||
|
|
41d94b6bfd | ||
|
|
2d74002e9e | ||
|
|
04a757dde0 | ||
|
|
e7e379ce82 | ||
|
|
fe0178b8ac | ||
|
|
0661c3af1a | ||
|
|
4778bd6680 | ||
|
|
5033462098 | ||
|
|
6b4b1d74fd | ||
|
|
f0df6a171c | ||
|
|
dc4cd93c02 | ||
|
|
19157aa76c | ||
|
|
1c092bf791 | ||
|
|
ff8790b35b | ||
|
|
c6168c1487 | ||
|
|
c431ac3765 | ||
|
|
6ff4901067 | ||
|
|
eea14b493c | ||
|
|
6b98c5a10d | ||
|
|
6d5ac5a16b | ||
|
|
42060a9112 | ||
|
|
cca4bec43f | ||
|
|
9e7c304ea1 | ||
|
|
d540b024b2 | ||
|
|
cf1c3ac38d | ||
|
|
a99c553ada | ||
|
|
b1575237fc | ||
|
|
6ce6eb95d6 | ||
|
|
b3d1dd51a4 | ||
|
|
fd29fdd975 | ||
|
|
2de98b1397 | ||
|
|
1d57c02608 | ||
|
|
ebeeb91297 | ||
|
|
6282beb6bd | ||
|
|
1c06bddb09 | ||
|
|
36f8c490c8 | ||
|
|
94c1a641d8 | ||
|
|
057131dce7 | ||
|
|
e5226b50ed | ||
|
|
ff4ec69f7c | ||
|
|
4ad6136d98 | ||
|
|
006c17bdca | ||
|
|
6b1939b827 | ||
|
|
2038227308 | ||
|
|
950852d673 | ||
|
|
8900f9ade3 | ||
|
|
8cf0d59560 | ||
|
|
a78e0b0871 | ||
|
|
32f030f6f6 | ||
|
|
b0d87b49c6 | ||
|
|
55b3fa389e | ||
|
|
b3ae716929 | ||
|
|
5d0c187497 | ||
|
|
30d8cf5a6c | ||
|
|
07dbdb9f8f | ||
|
|
b4c8f7924a | ||
|
|
809422c517 | ||
|
|
bb7593a53a | ||
|
|
8e3ba8900f | ||
|
|
005ec87248 | ||
|
|
4c6ff0641b | ||
|
|
3e242913e9 | ||
|
|
ba68e3c9bd | ||
|
|
e1199a91b9 | ||
|
|
d381248e30 | ||
|
|
f4f0218cae | ||
|
|
7a38e52b01 | ||
|
|
959fd55e32 | ||
|
|
a8e218a9ff | ||
|
|
3f5cd46d7d | ||
|
|
627f0c2bcc | ||
|
|
f6bde3eb04 | ||
|
|
f6e95c17a0 | ||
|
|
1234cbd04b | ||
|
|
fd5b93542e | ||
|
|
a192455fae | ||
|
|
66f17e95aa | ||
|
|
6f4b96b61b | ||
|
|
9905d23976 | ||
|
|
17532fe49d | ||
|
|
074158b495 | ||
|
|
82d5115b3f | ||
|
|
5c63111002 | ||
|
|
6eda7932e8 | ||
|
|
399b7567dd | ||
|
|
2133ada3a1 | ||
|
|
4f6d4738c4 | ||
|
|
d430ed6727 | ||
|
|
596bc178df | ||
|
|
0cd3d7b5a8 | ||
|
|
349d77ffdf | ||
|
|
c3283b04e5 | ||
|
|
0da0788e6b | ||
|
|
6f7e249aa2 | ||
|
|
dfaeed54b6 | ||
|
|
4f59e46235 | ||
|
|
bf4cc7befb | ||
|
|
c63c6dc68b | ||
|
|
e4225d6e9b | ||
|
|
3fb153c43e | ||
|
|
6de20c63d4 | ||
|
|
00fbc1c259 | ||
|
|
3bc552ef38 | ||
|
|
ee70d94e15 | ||
|
|
1887d2c0e9 | ||
|
|
c99dd4e44f | ||
|
|
541b8b288d | ||
|
|
db168a0452 | ||
|
|
aa96cf44d4 | ||
|
|
0d59c35d2a | ||
|
|
8463bde90d | ||
|
|
150c31009e | ||
|
|
693494024d | ||
|
|
ee66d6c7d1 | ||
|
|
3effd30f7e | ||
|
|
4ab20c2454 | ||
|
|
c075b5a1a7 | ||
|
|
cb1e59fa49 | ||
|
|
588aa435ec | ||
|
|
752c764066 | ||
|
|
af604c2ea8 | ||
|
|
6c3f9f149d | ||
|
|
152f2e03f1 | ||
|
|
605797c86a | ||
|
|
1ee5b1611a | ||
|
|
5028729e4c | ||
|
|
ab00fa8809 | ||
|
|
2d705e7caa | ||
|
|
f2370043a8 | ||
|
|
3b349b9803 | ||
|
|
f2b7ffe0eb | ||
|
|
3a410eed1a | ||
|
|
a53619f10f | ||
|
|
893aaafa1b | ||
|
|
33c34cdeca | ||
|
|
9b411867df | ||
|
|
fd1596b3a0 | ||
|
|
b05de22f58 | ||
|
|
e9341ee8d3 | ||
|
|
f666ad600f | ||
|
|
9345718967 | ||
|
|
6c879cbd13 | ||
|
|
089b5aaf44 | ||
|
|
b61885add5 | ||
|
|
702ba2e0a4 | ||
|
|
5cb1e284af | ||
|
|
e3a4f0873e | ||
|
|
7977a020ac | ||
|
|
1d63269883 | ||
|
|
dd8027480b | ||
|
|
c45bd77e44 | ||
|
|
032e0abd61 | ||
|
|
8509d1e454 | ||
|
|
8ff0c6828b | ||
|
|
ddd6935e50 | ||
|
|
5588a56b24 | ||
|
|
12aed6e280 | ||
|
|
b2a469e08c | ||
|
|
285b0e4af9 | ||
|
|
f9edfd6391 | ||
|
|
c0845e1612 | ||
|
|
9878d9d37e | ||
|
|
a2196085d5 | ||
|
|
ba62a8c10c | ||
|
|
38f38e2789 | ||
|
|
1475f0fc2f | ||
|
|
a3396b77a3 | ||
|
|
8158fee8fc | ||
|
|
f6301bc3e5 | ||
|
|
6c5c176b7d | ||
|
|
c6d52b5eb1 | ||
|
|
7cac528389 | ||
|
|
d518f75468 | ||
|
|
c6fac8c36b | ||
|
|
17b5b81696 | ||
|
|
9960db200c | ||
|
|
b9ff1704b0 | ||
|
|
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 | ||
|
|
545060103a | ||
|
|
e010b5680a | ||
|
|
8620d3987e | ||
|
|
30487a54c1 | ||
|
|
f15a39c153 | ||
|
|
aed27fa111 | ||
|
|
822c411e83 | ||
|
|
41b3ac7554 | ||
|
|
23575fdf6c | ||
|
|
52f70dc49a | ||
|
|
79c9749ff7 | ||
|
|
8d2701e143 | ||
|
|
877444ac29 | ||
|
|
b0d9426f1b | ||
|
|
18accae47e | ||
|
|
55e3a2c6b6 | ||
|
|
ef092e2893 | ||
|
|
89eb95c077 | ||
|
|
e871ec358e | ||
|
|
271a2f74ad | ||
|
|
d6bd951c37 | ||
|
|
8abd4c9c78 | ||
|
|
45a8c0acd1 | ||
|
|
c372cd533d | ||
|
|
999f83ce57 | ||
|
|
6fbed2dd9f | ||
|
|
36a6a59d55 | ||
|
|
875de88cb4 | ||
|
|
63bb44886e | ||
|
|
bda83a47a2 | ||
|
|
e96cfd35f7 | ||
|
|
65c96b2edf | ||
|
|
edf3c9464f | ||
|
|
cc8fb96047 | ||
|
|
3339b50daf | ||
|
|
415ea07a4f | ||
|
|
b80ec95fa8 | ||
|
|
99cb51482f | ||
|
|
90638f7a43 | ||
|
|
1fb00c8eb6 | ||
|
|
4490ea7635 | ||
|
|
bce7a20d8b | ||
|
|
b52dd53e29 | ||
|
|
a155f45036 | ||
|
|
de4424fab0 | ||
|
|
33ada95bbc |
2
.github/DISCUSSION_TEMPLATE/2-4.yml
vendored
2
.github/DISCUSSION_TEMPLATE/2-4.yml
vendored
@@ -33,6 +33,8 @@ body:
|
||||
- 2.4.180
|
||||
- 2.4.190
|
||||
- 2.4.200
|
||||
- 2.4.201
|
||||
- 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.201-20260114 ISO image released on 2026/1/15
|
||||
|
||||
|
||||
### Download and Verify
|
||||
|
||||
2.4.190-20251024 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.4.190-20251024.iso
|
||||
2.4.201-20260114 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.4.201-20260114.iso
|
||||
|
||||
MD5: 25358481FB876226499C011FC0710358
|
||||
SHA1: 0B26173C0CE136F2CA40A15046D1DFB78BCA1165
|
||||
SHA256: 4FD9F62EDA672408828B3C0C446FE5EA9FF3C4EE8488A7AB1101544A3C487872
|
||||
MD5: 20E926E433203798512EF46E590C89B9
|
||||
SHA1: 779E4084A3E1A209B494493B8F5658508B6014FA
|
||||
SHA256: 3D10E7C885AEC5C5D4F4E50F9644FF9728E8C0A2E36EBB8C96B32569685A7C40
|
||||
|
||||
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.201-20260114.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.201-20260114.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.201-20260114.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.201-20260114.iso.sig securityonion-2.4.201-20260114.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 Wed 14 Jan 2026 05:23:39 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.
|
||||
|
||||
2
pillar/ca/init.sls
Normal file
2
pillar/ca/init.sls
Normal file
@@ -0,0 +1,2 @@
|
||||
ca:
|
||||
server:
|
||||
@@ -1,5 +1,6 @@
|
||||
base:
|
||||
'*':
|
||||
- ca
|
||||
- global.soc_global
|
||||
- global.adv_global
|
||||
- docker.soc_docker
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
'salt.minion-check',
|
||||
'sensoroni',
|
||||
'salt.lasthighstate',
|
||||
'salt.minion'
|
||||
] %}
|
||||
|
||||
{% set ssl_states = [
|
||||
'ssl',
|
||||
'salt.minion',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'schedule',
|
||||
@@ -28,7 +24,7 @@
|
||||
|
||||
{% set manager_states = [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'ca.server',
|
||||
'registry',
|
||||
'manager',
|
||||
'nginx',
|
||||
@@ -75,28 +71,24 @@
|
||||
{# Map role-specific states #}
|
||||
{% set role_states = {
|
||||
'so-eval': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
sensor_states +
|
||||
elastic_stack_states | reject('equalto', 'logstash') | list
|
||||
elastic_stack_states | reject('equalto', 'logstash') | list +
|
||||
['logstash.ssl']
|
||||
),
|
||||
'so-heavynode': (
|
||||
ssl_states +
|
||||
sensor_states +
|
||||
['elasticagent', 'elasticsearch', 'logstash', 'redis', 'nginx']
|
||||
),
|
||||
'so-idh': (
|
||||
ssl_states +
|
||||
['idh']
|
||||
),
|
||||
'so-import': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
sensor_states | reject('equalto', 'strelka') | reject('equalto', 'healthcheck') | list +
|
||||
['elasticsearch', 'elasticsearch.auth', 'kibana', 'kibana.secrets', 'strelka.manager']
|
||||
['elasticsearch', 'elasticsearch.auth', 'kibana', 'kibana.secrets', 'logstash.ssl', 'strelka.manager']
|
||||
),
|
||||
'so-manager': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'libvirt.packages', 'libvirt.ssh.users', 'strelka.manager'] +
|
||||
stig_states +
|
||||
@@ -104,7 +96,6 @@
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-managerhype': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'strelka.manager', 'hypervisor', 'libvirt'] +
|
||||
stig_states +
|
||||
@@ -112,7 +103,6 @@
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-managersearch': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'libvirt.packages', 'libvirt.ssh.users', 'strelka.manager'] +
|
||||
stig_states +
|
||||
@@ -120,12 +110,10 @@
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-searchnode': (
|
||||
ssl_states +
|
||||
['kafka.ca', 'kafka.ssl', 'elasticsearch', 'logstash', 'nginx'] +
|
||||
stig_states
|
||||
),
|
||||
'so-standalone': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'libvirt.packages', 'libvirt.ssh.users'] +
|
||||
sensor_states +
|
||||
@@ -134,29 +122,24 @@
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-sensor': (
|
||||
ssl_states +
|
||||
sensor_states +
|
||||
['nginx'] +
|
||||
stig_states
|
||||
),
|
||||
'so-fleet': (
|
||||
ssl_states +
|
||||
stig_states +
|
||||
['logstash', 'nginx', 'healthcheck', 'elasticfleet']
|
||||
),
|
||||
'so-receiver': (
|
||||
ssl_states +
|
||||
kafka_states +
|
||||
stig_states +
|
||||
['logstash', 'redis']
|
||||
),
|
||||
'so-hypervisor': (
|
||||
ssl_states +
|
||||
stig_states +
|
||||
['hypervisor', 'libvirt']
|
||||
),
|
||||
'so-desktop': (
|
||||
['ssl', 'docker_clean', 'telegraf'] +
|
||||
stig_states
|
||||
)
|
||||
} %}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if PCAPBPF %}
|
||||
{% set PCAP_BPF_CALC = salt['cmd.run_all']('/usr/sbin/so-bpf-compile ' ~ GLOBALS.sensor.interface ~ ' ' ~ PCAPBPF|join(" "), cwd='/root') %}
|
||||
{% set PCAP_BPF_CALC = salt['cmd.script']('salt://common/tools/sbin/so-bpf-compile', GLOBALS.sensor.interface + ' ' + PCAPBPF|join(" "),cwd='/root') %}
|
||||
{% if PCAP_BPF_CALC['retcode'] == 0 %}
|
||||
{% set PCAP_BPF_STATUS = 1 %}
|
||||
{% set STENO_BPF_COMPILED = ",\\\"--filter=" + PCAP_BPF_CALC['stdout'] + "\\\"" %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% set SURICATABPF = BPFMERGED.suricata %}
|
||||
|
||||
{% if SURICATABPF %}
|
||||
{% set SURICATA_BPF_CALC = salt['cmd.run_all']('/usr/sbin/so-bpf-compile ' ~ GLOBALS.sensor.interface ~ ' ' ~ SURICATABPF|join(" "), cwd='/root') %}
|
||||
{% set SURICATA_BPF_CALC = salt['cmd.script']('salt://common/tools/sbin/so-bpf-compile', GLOBALS.sensor.interface + ' ' + SURICATABPF|join(" "),cwd='/root') %}
|
||||
{% if SURICATA_BPF_CALC['retcode'] == 0 %}
|
||||
{% set SURICATA_BPF_STATUS = 1 %}
|
||||
{% endif %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% set ZEEKBPF = BPFMERGED.zeek %}
|
||||
|
||||
{% if ZEEKBPF %}
|
||||
{% set ZEEK_BPF_CALC = salt['cmd.run_all']('/usr/sbin/so-bpf-compile ' ~ GLOBALS.sensor.interface ~ ' ' ~ ZEEKBPF|join(" "), cwd='/root') %}
|
||||
{% set ZEEK_BPF_CALC = salt['cmd.script']('salt://common/tools/sbin/so-bpf-compile', GLOBALS.sensor.interface + ' ' + ZEEKBPF|join(" "),cwd='/root') %}
|
||||
{% if ZEEK_BPF_CALC['retcode'] == 0 %}
|
||||
{% set ZEEK_BPF_STATUS = 1 %}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
pki_issued_certs:
|
||||
file.directory:
|
||||
- name: /etc/pki/issued_certs
|
||||
- makedirs: True
|
||||
@@ -3,70 +3,10 @@
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
{% from 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
|
||||
include:
|
||||
- ca.dirs
|
||||
|
||||
/etc/salt/minion.d/signing_policies.conf:
|
||||
file.managed:
|
||||
- source: salt://ca/files/signing_policies.conf
|
||||
|
||||
pki_private_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/ca.key
|
||||
- keysize: 4096
|
||||
- passphrase:
|
||||
- backup: True
|
||||
{% if salt['file.file_exists']('/etc/pki/ca.key') -%}
|
||||
- prereq:
|
||||
- x509: /etc/pki/ca.crt
|
||||
{%- endif %}
|
||||
|
||||
pki_public_ca_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/ca.crt
|
||||
- signing_private_key: /etc/pki/ca.key
|
||||
- CN: {{ GLOBALS.manager }}
|
||||
- C: US
|
||||
- ST: Utah
|
||||
- L: Salt Lake City
|
||||
- basicConstraints: "critical CA:true"
|
||||
- keyUsage: "critical cRLSign, keyCertSign"
|
||||
- extendedkeyUsage: "serverAuth, clientAuth"
|
||||
- subjectKeyIdentifier: hash
|
||||
- authorityKeyIdentifier: keyid:always, issuer
|
||||
- days_valid: 3650
|
||||
- days_remaining: 0
|
||||
- backup: True
|
||||
- replace: False
|
||||
- require:
|
||||
- sls: ca.dirs
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
mine_update_ca_crt:
|
||||
module.run:
|
||||
- mine.update: []
|
||||
- onchanges:
|
||||
- x509: pki_public_ca_crt
|
||||
|
||||
cakeyperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/ca.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% if GLOBALS.is_manager %}
|
||||
- ca.server
|
||||
{% endif %}
|
||||
- ca.trustca
|
||||
|
||||
3
salt/ca/map.jinja
Normal file
3
salt/ca/map.jinja
Normal file
@@ -0,0 +1,3 @@
|
||||
{% set CA = {
|
||||
'server': pillar.ca.server
|
||||
}%}
|
||||
@@ -1,7 +1,35 @@
|
||||
pki_private_key:
|
||||
# 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.
|
||||
|
||||
{% set setup_running = salt['cmd.retcode']('pgrep -x so-setup') == 0 %}
|
||||
|
||||
{% if setup_running%}
|
||||
|
||||
include:
|
||||
- ssl.remove
|
||||
|
||||
remove_pki_private_key:
|
||||
file.absent:
|
||||
- name: /etc/pki/ca.key
|
||||
|
||||
pki_public_ca_crt:
|
||||
remove_pki_public_ca_crt:
|
||||
file.absent:
|
||||
- name: /etc/pki/ca.crt
|
||||
|
||||
remove_trusttheca:
|
||||
file.absent:
|
||||
- name: /etc/pki/tls/certs/intca.crt
|
||||
|
||||
remove_pki_public_ca_crt_symlink:
|
||||
file.absent:
|
||||
- name: /opt/so/saltstack/local/salt/ca/files/ca.crt
|
||||
|
||||
{% else %}
|
||||
|
||||
so-setup_not_running:
|
||||
test.show_notification:
|
||||
- text: "This state is reserved for usage during so-setup."
|
||||
|
||||
{% endif %}
|
||||
|
||||
63
salt/ca/server.sls
Normal file
63
salt/ca/server.sls
Normal file
@@ -0,0 +1,63 @@
|
||||
# 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 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
pki_private_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/ca.key
|
||||
- keysize: 4096
|
||||
- passphrase:
|
||||
- backup: True
|
||||
{% if salt['file.file_exists']('/etc/pki/ca.key') -%}
|
||||
- prereq:
|
||||
- x509: /etc/pki/ca.crt
|
||||
{%- endif %}
|
||||
|
||||
pki_public_ca_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/ca.crt
|
||||
- signing_private_key: /etc/pki/ca.key
|
||||
- CN: {{ GLOBALS.manager }}
|
||||
- C: US
|
||||
- ST: Utah
|
||||
- L: Salt Lake City
|
||||
- basicConstraints: "critical CA:true"
|
||||
- keyUsage: "critical cRLSign, keyCertSign"
|
||||
- extendedkeyUsage: "serverAuth, clientAuth"
|
||||
- subjectKeyIdentifier: hash
|
||||
- authorityKeyIdentifier: keyid:always, issuer
|
||||
- days_valid: 3650
|
||||
- days_remaining: 7
|
||||
- backup: True
|
||||
- replace: False
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
pki_public_ca_crt_symlink:
|
||||
file.symlink:
|
||||
- name: /opt/so/saltstack/local/salt/ca/files/ca.crt
|
||||
- target: /etc/pki/ca.crt
|
||||
- require:
|
||||
- x509: pki_public_ca_crt
|
||||
|
||||
cakeyperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/ca.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
15
salt/ca/signing_policy.sls
Normal file
15
salt/ca/signing_policy.sls
Normal file
@@ -0,0 +1,15 @@
|
||||
# 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.
|
||||
|
||||
# when the salt-minion signs the cert, a copy is stored here
|
||||
issued_certs_copypath:
|
||||
file.directory:
|
||||
- name: /etc/pki/issued_certs
|
||||
- makedirs: True
|
||||
|
||||
signing_policy:
|
||||
file.managed:
|
||||
- name: /etc/salt/minion.d/signing_policies.conf
|
||||
- source: salt://ca/files/signing_policies.conf
|
||||
26
salt/ca/trustca.sls
Normal file
26
salt/ca/trustca.sls
Normal file
@@ -0,0 +1,26 @@
|
||||
# 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 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
include:
|
||||
- docker
|
||||
|
||||
# Trust the CA
|
||||
trusttheca:
|
||||
file.managed:
|
||||
- name: /etc/pki/tls/certs/intca.crt
|
||||
- source: salt://ca/files/ca.crt
|
||||
- watch_in:
|
||||
- service: docker_running
|
||||
- show_changes: False
|
||||
- makedirs: True
|
||||
|
||||
{% if GLOBALS.os_family == 'Debian' %}
|
||||
symlinkca:
|
||||
file.symlink:
|
||||
- target: /etc/pki/tls/certs/intca.crt
|
||||
- name: /etc/ssl/certs/intca.crt
|
||||
{% endif %}
|
||||
@@ -177,7 +177,7 @@ so-status_script:
|
||||
- source: salt://common/tools/sbin/so-status
|
||||
- mode: 755
|
||||
|
||||
{% if GLOBALS.role in GLOBALS.sensor_roles %}
|
||||
{% if GLOBALS.is_sensor %}
|
||||
# Add sensor cleanup
|
||||
so-sensor-clean:
|
||||
cron.present:
|
||||
|
||||
@@ -404,6 +404,25 @@ is_single_node_grid() {
|
||||
grep "role: so-" /etc/salt/grains | grep -E "eval|standalone|import" &> /dev/null
|
||||
}
|
||||
|
||||
initialize_elasticsearch_indices() {
|
||||
local index_names=$1
|
||||
local default_entry=${2:-'{"@timestamp":"0"}'}
|
||||
|
||||
for idx in $index_names; do
|
||||
if ! so-elasticsearch-query "$idx" --fail --retry 3 --retry-delay 30 >/dev/null 2>&1; then
|
||||
echo "Index does not already exist. Initializing $idx index."
|
||||
|
||||
if retry 3 10 "so-elasticsearch-query "$idx/_doc" -d '$default_entry' -XPOST --fail 2>/dev/null" '"successful":1'; then
|
||||
echo "Successfully initialized $idx index."
|
||||
else
|
||||
echo "Failed to initialize $idx index after 3 attempts."
|
||||
fi
|
||||
else
|
||||
echo "Index $idx already exists. No action needed."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
lookup_bond_interfaces() {
|
||||
cat /proc/net/bonding/bond0 | grep "Slave Interface:" | sed -e "s/Slave Interface: //g"
|
||||
}
|
||||
@@ -554,21 +573,39 @@ run_check_net_err() {
|
||||
}
|
||||
|
||||
wait_for_salt_minion() {
|
||||
local minion="$1"
|
||||
local timeout="${2:-5}"
|
||||
local logfile="${3:-'/dev/stdout'}"
|
||||
retry 60 5 "journalctl -u salt-minion.service | grep 'Minion is ready to receive requests'" >> "$logfile" 2>&1 || fail
|
||||
local attempt=0
|
||||
# each attempts would take about 15 seconds
|
||||
local maxAttempts=20
|
||||
until check_salt_minion_status "$minion" "$timeout" "$logfile"; do
|
||||
attempt=$((attempt+1))
|
||||
if [[ $attempt -eq $maxAttempts ]]; then
|
||||
return 1
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
return 0
|
||||
local minion="$1"
|
||||
local max_wait="${2:-30}"
|
||||
local interval="${3:-2}"
|
||||
local logfile="${4:-'/dev/stdout'}"
|
||||
local elapsed=0
|
||||
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - Waiting for salt-minion '$minion' to be ready..."
|
||||
|
||||
while [ $elapsed -lt $max_wait ]; do
|
||||
# Check if service is running
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - Check if salt-minion service is running"
|
||||
if ! systemctl is-active --quiet salt-minion; then
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - salt-minion service not running (elapsed: ${elapsed}s)"
|
||||
sleep $interval
|
||||
elapsed=$((elapsed + interval))
|
||||
continue
|
||||
fi
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - salt-minion service is running"
|
||||
|
||||
# Check if minion responds to ping
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - Check if $minion responds to ping"
|
||||
if salt "$minion" test.ping --timeout=3 --out=json 2>> "$logfile" | grep -q "true"; then
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - salt-minion '$minion' is connected and ready!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - Waiting... (${elapsed}s / ${max_wait}s)"
|
||||
sleep $interval
|
||||
elapsed=$((elapsed + interval))
|
||||
done
|
||||
|
||||
echo "$(date '+%a %d %b %Y %H:%M:%S.%6N') - ERROR: salt-minion '$minion' not ready after $max_wait seconds"
|
||||
return 1
|
||||
}
|
||||
|
||||
salt_minion_count() {
|
||||
|
||||
@@ -129,6 +129,8 @@ 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
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Cancelling deferred write event maybeFenceReplicas because the event queue is now closed" # Kafka controller log during shutdown/restart
|
||||
fi
|
||||
|
||||
if [[ $EXCLUDE_FALSE_POSITIVE_ERRORS == 'Y' ]]; then
|
||||
@@ -159,7 +161,9 @@ if [[ $EXCLUDE_FALSE_POSITIVE_ERRORS == 'Y' ]]; then
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|adding ingest pipeline" # false positive (elasticsearch ingest pipeline names contain 'error')
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|updating index template" # false positive (elasticsearch index or template names contain 'error')
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|updating component template" # false positive (elasticsearch index or template names contain 'error')
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|upgrading component template" # false positive (elasticsearch index or template names contain 'error')
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|upgrading composable template" # false positive (elasticsearch composable template names contain 'error')
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Error while parsing document for index \[.ds-logs-kratos-so-.*object mapping for \[file\]" # false positive (mapping error occuring BEFORE kratos index has rolled over in 2.4.210)
|
||||
fi
|
||||
|
||||
if [[ $EXCLUDE_KNOWN_ERRORS == 'Y' ]]; then
|
||||
@@ -223,6 +227,7 @@ if [[ $EXCLUDE_KNOWN_ERRORS == 'Y' ]]; then
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|from NIC checksum offloading" # zeek reporter.log
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|marked for removal" # docker container getting recycled
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|tcp 127.0.0.1:6791: bind: address already in use" # so-elastic-fleet agent restarting. Seen starting w/ 8.18.8 https://github.com/elastic/kibana/issues/201459
|
||||
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|TransformTask\] \[logs-(tychon|aws_billing|microsoft_defender_endpoint).*user so_kibana lacks the required permissions \[logs-\1" # Known issue with 3 integrations using kibana_system role vs creating unique api creds with proper permissions.
|
||||
fi
|
||||
|
||||
RESULT=0
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -3,29 +3,16 @@
|
||||
{# we only want this state to run it is CentOS #}
|
||||
{% if GLOBALS.os == 'OEL' %}
|
||||
|
||||
{% set global_ca_text = [] %}
|
||||
{% set global_ca_server = [] %}
|
||||
{% set manager = GLOBALS.manager %}
|
||||
{% set x509dict = salt['mine.get'](manager | lower~'*', 'x509.get_pem_entries') %}
|
||||
{% for host in x509dict %}
|
||||
{% if host.split('_')|last in ['manager', 'managersearch', 'standalone', 'import', 'eval'] %}
|
||||
{% do global_ca_text.append(x509dict[host].get('/etc/pki/ca.crt')|replace('\n', '')) %}
|
||||
{% do global_ca_server.append(host) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% set trusttheca_text = global_ca_text[0] %}
|
||||
{% set ca_server = global_ca_server[0] %}
|
||||
|
||||
trusted_ca:
|
||||
x509.pem_managed:
|
||||
file.managed:
|
||||
- name: /etc/pki/ca-trust/source/anchors/ca.crt
|
||||
- text: {{ trusttheca_text }}
|
||||
- source: salt://ca/files/ca.crt
|
||||
|
||||
update_ca_certs:
|
||||
cmd.run:
|
||||
- name: update-ca-trust
|
||||
- onchanges:
|
||||
- x509: trusted_ca
|
||||
- file: trusted_ca
|
||||
|
||||
{% else %}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
{% from 'docker/docker.map.jinja' import DOCKER %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
# include ssl since docker service requires the intca
|
||||
# docker service requires the ca.crt
|
||||
include:
|
||||
- ssl
|
||||
- ca
|
||||
|
||||
dockergroup:
|
||||
group.present:
|
||||
@@ -20,20 +20,20 @@ dockergroup:
|
||||
dockerheldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.7.21-1
|
||||
- docker-ce: 5:27.2.0-1~debian.12~bookworm
|
||||
- docker-ce-cli: 5:27.2.0-1~debian.12~bookworm
|
||||
- docker-ce-rootless-extras: 5:27.2.0-1~debian.12~bookworm
|
||||
- containerd.io: 2.2.1-1~debian.12~bookworm
|
||||
- docker-ce: 5:29.2.1-1~debian.12~bookworm
|
||||
- docker-ce-cli: 5:29.2.1-1~debian.12~bookworm
|
||||
- docker-ce-rootless-extras: 5:29.2.1-1~debian.12~bookworm
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% elif grains.oscodename == 'jammy' %}
|
||||
dockerheldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.7.21-1
|
||||
- docker-ce: 5:27.2.0-1~ubuntu.22.04~jammy
|
||||
- docker-ce-cli: 5:27.2.0-1~ubuntu.22.04~jammy
|
||||
- docker-ce-rootless-extras: 5:27.2.0-1~ubuntu.22.04~jammy
|
||||
- containerd.io: 2.2.1-1~ubuntu.22.04~jammy
|
||||
- docker-ce: 5:29.2.1-1~ubuntu.22.04~jammy
|
||||
- docker-ce-cli: 5:29.2.1-1~ubuntu.22.04~jammy
|
||||
- docker-ce-rootless-extras: 5:29.2.1-1~ubuntu.22.04~jammy
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% else %}
|
||||
@@ -51,10 +51,10 @@ dockerheldpackages:
|
||||
dockerheldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.7.21-3.1.el9
|
||||
- docker-ce: 3:27.2.0-1.el9
|
||||
- docker-ce-cli: 1:27.2.0-1.el9
|
||||
- docker-ce-rootless-extras: 27.2.0-1.el9
|
||||
- containerd.io: 2.2.1-1.el9
|
||||
- docker-ce: 3:29.2.1-1.el9
|
||||
- docker-ce-cli: 1:29.2.1-1.el9
|
||||
- docker-ce-rootless-extras: 29.2.1-1.el9
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% endif %}
|
||||
@@ -89,10 +89,9 @@ docker_running:
|
||||
- enable: True
|
||||
- watch:
|
||||
- file: docker_daemon
|
||||
- x509: trusttheca
|
||||
- require:
|
||||
- file: docker_daemon
|
||||
- x509: trusttheca
|
||||
- file: trusttheca
|
||||
|
||||
|
||||
# Reserve OS ports for Docker proxy in case boot settings are not already applied/present
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
{% from 'docker/docker.map.jinja' import DOCKER %}
|
||||
|
||||
include:
|
||||
- ca
|
||||
- elasticagent.config
|
||||
- elasticagent.sostatus
|
||||
|
||||
@@ -55,8 +56,10 @@ so-elastic-agent:
|
||||
{% endif %}
|
||||
- require:
|
||||
- file: create-elastic-agent-config
|
||||
- file: trusttheca
|
||||
- watch:
|
||||
- file: create-elastic-agent-config
|
||||
- file: trusttheca
|
||||
|
||||
delete_so-elastic-agent_so-status.disabled:
|
||||
file.uncomment:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{%- set ES_PASS = salt['pillar.get']('elasticsearch:auth:users:so_elastic_user:pass', '') %}
|
||||
|
||||
id: aea1ba80-1065-11ee-a369-97538913b6a9
|
||||
revision: 1
|
||||
revision: 4
|
||||
outputs:
|
||||
default:
|
||||
type: elasticsearch
|
||||
@@ -22,242 +22,133 @@ agent:
|
||||
metrics: false
|
||||
features: {}
|
||||
inputs:
|
||||
- id: logfile-logs-fefef78c-422f-4cfa-8abf-4cd1b9428f62
|
||||
name: import-evtx-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: fefef78c-422f-4cfa-8abf-4cd1b9428f62
|
||||
streams:
|
||||
- id: logfile-log.log-fefef78c-422f-4cfa-8abf-4cd1b9428f62
|
||||
data_stream:
|
||||
dataset: import
|
||||
paths:
|
||||
- /nsm/import/*/evtx/*.json
|
||||
processors:
|
||||
- dissect:
|
||||
field: log.file.path
|
||||
tokenizer: '/nsm/import/%{import.id}/evtx/%{import.file}'
|
||||
target_prefix: ''
|
||||
- decode_json_fields:
|
||||
fields:
|
||||
- message
|
||||
target: ''
|
||||
- drop_fields:
|
||||
ignore_missing: true
|
||||
fields:
|
||||
- host
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: system.security
|
||||
type: logs
|
||||
namespace: default
|
||||
target: data_stream
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: system.security
|
||||
module: system
|
||||
imported: true
|
||||
target: event
|
||||
- then:
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: windows.sysmon_operational
|
||||
target: data_stream
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: windows.sysmon_operational
|
||||
module: windows
|
||||
imported: true
|
||||
target: event
|
||||
if:
|
||||
equals:
|
||||
winlog.channel: Microsoft-Windows-Sysmon/Operational
|
||||
- then:
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: system.application
|
||||
target: data_stream
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: system.application
|
||||
target: event
|
||||
if:
|
||||
equals:
|
||||
winlog.channel: Application
|
||||
- then:
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: system.system
|
||||
target: data_stream
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: system.system
|
||||
target: event
|
||||
if:
|
||||
equals:
|
||||
winlog.channel: System
|
||||
- then:
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: windows.powershell_operational
|
||||
target: data_stream
|
||||
- add_fields:
|
||||
fields:
|
||||
dataset: windows.powershell_operational
|
||||
module: windows
|
||||
target: event
|
||||
if:
|
||||
equals:
|
||||
winlog.channel: Microsoft-Windows-PowerShell/Operational
|
||||
tags:
|
||||
- import
|
||||
- id: logfile-redis-fc98c947-7d17-4861-a318-7ad075f6d1b0
|
||||
name: redis-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: redis
|
||||
version:
|
||||
data_stream:
|
||||
namespace: default
|
||||
package_policy_id: fc98c947-7d17-4861-a318-7ad075f6d1b0
|
||||
streams:
|
||||
- id: logfile-redis.log-fc98c947-7d17-4861-a318-7ad075f6d1b0
|
||||
data_stream:
|
||||
dataset: redis.log
|
||||
type: logs
|
||||
exclude_files:
|
||||
- .gz$
|
||||
paths:
|
||||
- /opt/so/log/redis/redis.log
|
||||
tags:
|
||||
- redis-log
|
||||
exclude_lines:
|
||||
- '^\s+[\-`(''.|_]'
|
||||
- id: logfile-logs-3b56803d-5ade-4c93-b25e-9b37182f66b8
|
||||
- id: filestream-filestream-85820eb0-25ef-11f0-a18d-1b26f69b8310
|
||||
name: import-suricata-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
revision: 3
|
||||
type: filestream
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
name: filestream
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: 3b56803d-5ade-4c93-b25e-9b37182f66b8
|
||||
package_policy_id: 85820eb0-25ef-11f0-a18d-1b26f69b8310
|
||||
streams:
|
||||
- id: logfile-log.log-3b56803d-5ade-4c93-b25e-9b37182f66b8
|
||||
- id: filestream-filestream.generic-85820eb0-25ef-11f0-a18d-1b26f69b8310
|
||||
data_stream:
|
||||
dataset: import
|
||||
pipeline: suricata.common
|
||||
paths:
|
||||
- /nsm/import/*/suricata/eve*.json
|
||||
pipeline: suricata.common
|
||||
prospector.scanner.recursive_glob: true
|
||||
prospector.scanner.exclude_files:
|
||||
- \.gz$
|
||||
ignore_older: 72h
|
||||
clean_inactive: -1
|
||||
parsers: null
|
||||
processors:
|
||||
- add_fields:
|
||||
target: event
|
||||
fields:
|
||||
category: network
|
||||
module: suricata
|
||||
imported: true
|
||||
category: network
|
||||
target: event
|
||||
- dissect:
|
||||
tokenizer: /nsm/import/%{import.id}/suricata/%{import.file}
|
||||
field: log.file.path
|
||||
tokenizer: '/nsm/import/%{import.id}/suricata/%{import.file}'
|
||||
target_prefix: ''
|
||||
- id: logfile-logs-c327e1a3-1ebe-449c-a8eb-f6f35032e69d
|
||||
name: soc-server-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
file_identity.native: null
|
||||
prospector.scanner.fingerprint.enabled: false
|
||||
- id: filestream-filestream-86b4e960-25ef-11f0-a18d-1b26f69b8310
|
||||
name: import-zeek-logs
|
||||
revision: 3
|
||||
type: filestream
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
name: filestream
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: c327e1a3-1ebe-449c-a8eb-f6f35032e69d
|
||||
package_policy_id: 86b4e960-25ef-11f0-a18d-1b26f69b8310
|
||||
streams:
|
||||
- id: logfile-log.log-c327e1a3-1ebe-449c-a8eb-f6f35032e69d
|
||||
- id: filestream-filestream.generic-86b4e960-25ef-11f0-a18d-1b26f69b8310
|
||||
data_stream:
|
||||
dataset: soc
|
||||
pipeline: common
|
||||
dataset: import
|
||||
paths:
|
||||
- /opt/so/log/soc/sensoroni-server.log
|
||||
- /nsm/import/*/zeek/logs/*.log
|
||||
prospector.scanner.recursive_glob: true
|
||||
prospector.scanner.exclude_files:
|
||||
- >-
|
||||
(broker|capture_loss|cluster|conn-summary|console|ecat_arp_info|known_certs|known_hosts|known_services|loaded_scripts|ntp|ocsp|packet_filter|reporter|stats|stderr|stdout).log$
|
||||
clean_inactive: -1
|
||||
parsers: null
|
||||
processors:
|
||||
- decode_json_fields:
|
||||
add_error_key: true
|
||||
process_array: true
|
||||
max_depth: 2
|
||||
fields:
|
||||
- message
|
||||
target: soc
|
||||
- dissect:
|
||||
tokenizer: /nsm/import/%{import.id}/zeek/logs/%{import.file}
|
||||
field: log.file.path
|
||||
target_prefix: ''
|
||||
- script:
|
||||
lang: javascript
|
||||
source: |
|
||||
function process(event) {
|
||||
var pl = event.Get("import.file").slice(0,-4);
|
||||
event.Put("@metadata.pipeline", "zeek." + pl);
|
||||
}
|
||||
- add_fields:
|
||||
fields:
|
||||
module: soc
|
||||
dataset_temp: server
|
||||
category: host
|
||||
target: event
|
||||
- rename:
|
||||
ignore_missing: true
|
||||
fields:
|
||||
- from: soc.fields.sourceIp
|
||||
to: source.ip
|
||||
- from: soc.fields.status
|
||||
to: http.response.status_code
|
||||
- from: soc.fields.method
|
||||
to: http.request.method
|
||||
- from: soc.fields.path
|
||||
to: url.path
|
||||
- from: soc.message
|
||||
to: event.action
|
||||
- from: soc.level
|
||||
to: log.level
|
||||
tags:
|
||||
- so-soc
|
||||
- id: logfile-logs-906e0d4c-9ec3-4c6a-bef6-e347ec9fd073
|
||||
category: network
|
||||
module: zeek
|
||||
imported: true
|
||||
- add_tags:
|
||||
tags: ics
|
||||
when:
|
||||
regexp:
|
||||
import.file: >-
|
||||
^bacnet*|^bsap*|^cip*|^cotp*|^dnp3*|^ecat*|^enip*|^modbus*|^opcua*|^profinet*|^s7comm*
|
||||
file_identity.native: null
|
||||
prospector.scanner.fingerprint.enabled: false
|
||||
- id: filestream-filestream-91741240-25ef-11f0-a18d-1b26f69b8310
|
||||
name: soc-sensoroni-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
revision: 3
|
||||
type: filestream
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
name: filestream
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: 906e0d4c-9ec3-4c6a-bef6-e347ec9fd073
|
||||
package_policy_id: 91741240-25ef-11f0-a18d-1b26f69b8310
|
||||
streams:
|
||||
- id: logfile-log.log-906e0d4c-9ec3-4c6a-bef6-e347ec9fd073
|
||||
- id: filestream-filestream.generic-91741240-25ef-11f0-a18d-1b26f69b8310
|
||||
data_stream:
|
||||
dataset: soc
|
||||
pipeline: common
|
||||
paths:
|
||||
- /opt/so/log/sensoroni/sensoroni.log
|
||||
pipeline: common
|
||||
prospector.scanner.recursive_glob: true
|
||||
prospector.scanner.exclude_files:
|
||||
- \.gz$
|
||||
clean_inactive: -1
|
||||
parsers: null
|
||||
processors:
|
||||
- decode_json_fields:
|
||||
add_error_key: true
|
||||
process_array: true
|
||||
max_depth: 2
|
||||
fields:
|
||||
- message
|
||||
target: sensoroni
|
||||
process_array: true
|
||||
max_depth: 2
|
||||
add_error_key: true
|
||||
- add_fields:
|
||||
target: event
|
||||
fields:
|
||||
category: host
|
||||
module: soc
|
||||
dataset_temp: sensoroni
|
||||
category: host
|
||||
target: event
|
||||
- rename:
|
||||
ignore_missing: true
|
||||
fields:
|
||||
- from: sensoroni.fields.sourceIp
|
||||
to: source.ip
|
||||
@@ -271,141 +162,100 @@ inputs:
|
||||
to: event.action
|
||||
- from: sensoroni.level
|
||||
to: log.level
|
||||
- id: logfile-logs-df0d7f2c-221f-433b-b18b-d1cf83250515
|
||||
name: soc-salt-relay-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: df0d7f2c-221f-433b-b18b-d1cf83250515
|
||||
streams:
|
||||
- id: logfile-log.log-df0d7f2c-221f-433b-b18b-d1cf83250515
|
||||
data_stream:
|
||||
dataset: soc
|
||||
pipeline: common
|
||||
paths:
|
||||
- /opt/so/log/soc/salt-relay.log
|
||||
processors:
|
||||
- dissect:
|
||||
field: message
|
||||
tokenizer: '%{soc.ts} | %{event.action}'
|
||||
target_prefix: ''
|
||||
- add_fields:
|
||||
fields:
|
||||
module: soc
|
||||
dataset_temp: salt_relay
|
||||
category: host
|
||||
target: event
|
||||
tags:
|
||||
- so-soc
|
||||
- id: logfile-logs-74bd2366-fe52-493c-bddc-843a017fc4d0
|
||||
name: soc-auth-sync-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: 74bd2366-fe52-493c-bddc-843a017fc4d0
|
||||
streams:
|
||||
- id: logfile-log.log-74bd2366-fe52-493c-bddc-843a017fc4d0
|
||||
data_stream:
|
||||
dataset: soc
|
||||
pipeline: common
|
||||
paths:
|
||||
- /opt/so/log/soc/sync.log
|
||||
processors:
|
||||
- dissect:
|
||||
field: message
|
||||
tokenizer: '%{event.action}'
|
||||
target_prefix: ''
|
||||
- add_fields:
|
||||
fields:
|
||||
module: soc
|
||||
dataset_temp: auth_sync
|
||||
category: host
|
||||
target: event
|
||||
tags:
|
||||
- so-soc
|
||||
- id: logfile-logs-d151d9bf-ff2a-4529-9520-c99244bc0253
|
||||
ignore_missing: true
|
||||
file_identity.native: null
|
||||
prospector.scanner.fingerprint.enabled: false
|
||||
- id: filestream-filestream-976e3900-25ef-11f0-a18d-1b26f69b8310
|
||||
name: suricata-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
revision: 3
|
||||
type: filestream
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
name: filestream
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: d151d9bf-ff2a-4529-9520-c99244bc0253
|
||||
package_policy_id: 976e3900-25ef-11f0-a18d-1b26f69b8310
|
||||
streams:
|
||||
- id: logfile-log.log-d151d9bf-ff2a-4529-9520-c99244bc0253
|
||||
- id: filestream-filestream.generic-976e3900-25ef-11f0-a18d-1b26f69b8310
|
||||
data_stream:
|
||||
dataset: suricata
|
||||
pipeline: suricata.common
|
||||
paths:
|
||||
- /nsm/suricata/eve*.json
|
||||
pipeline: suricata.common
|
||||
prospector.scanner.recursive_glob: true
|
||||
prospector.scanner.exclude_files:
|
||||
- \.gz$
|
||||
clean_inactive: -1
|
||||
parsers: null
|
||||
processors:
|
||||
- add_fields:
|
||||
fields:
|
||||
module: suricata
|
||||
category: network
|
||||
target: event
|
||||
- id: logfile-logs-31f94d05-ae75-40ee-b9c5-0e0356eff327
|
||||
fields:
|
||||
category: network
|
||||
module: suricata
|
||||
file_identity.native: null
|
||||
prospector.scanner.fingerprint.enabled: false
|
||||
- id: filestream-filestream-95091fe0-25ef-11f0-a18d-1b26f69b8310
|
||||
name: strelka-logs
|
||||
revision: 2
|
||||
type: logfile
|
||||
revision: 3
|
||||
type: filestream
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
name: filestream
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: 31f94d05-ae75-40ee-b9c5-0e0356eff327
|
||||
package_policy_id: 95091fe0-25ef-11f0-a18d-1b26f69b8310
|
||||
streams:
|
||||
- id: logfile-log.log-31f94d05-ae75-40ee-b9c5-0e0356eff327
|
||||
- id: filestream-filestream.generic-95091fe0-25ef-11f0-a18d-1b26f69b8310
|
||||
data_stream:
|
||||
dataset: strelka
|
||||
pipeline: strelka.file
|
||||
paths:
|
||||
- /nsm/strelka/log/strelka.log
|
||||
pipeline: strelka.file
|
||||
prospector.scanner.recursive_glob: true
|
||||
prospector.scanner.exclude_files:
|
||||
- \.gz$
|
||||
clean_inactive: -1
|
||||
parsers: null
|
||||
processors:
|
||||
- add_fields:
|
||||
fields:
|
||||
module: strelka
|
||||
category: file
|
||||
target: event
|
||||
- id: logfile-logs-6197fe84-9b58-4d9b-8464-3d517f28808d
|
||||
fields:
|
||||
category: file
|
||||
module: strelka
|
||||
file_identity.native: null
|
||||
prospector.scanner.fingerprint.enabled: false
|
||||
- id: filestream-filestream-9f309ca0-25ef-11f0-a18d-1b26f69b8310
|
||||
name: zeek-logs
|
||||
revision: 1
|
||||
type: logfile
|
||||
revision: 2
|
||||
type: filestream
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: log
|
||||
version:
|
||||
name: filestream
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: 6197fe84-9b58-4d9b-8464-3d517f28808d
|
||||
package_policy_id: 9f309ca0-25ef-11f0-a18d-1b26f69b8310
|
||||
streams:
|
||||
- id: logfile-log.log-6197fe84-9b58-4d9b-8464-3d517f28808d
|
||||
- id: filestream-filestream.generic-9f309ca0-25ef-11f0-a18d-1b26f69b8310
|
||||
data_stream:
|
||||
dataset: zeek
|
||||
paths:
|
||||
- /nsm/zeek/logs/current/*.log
|
||||
prospector.scanner.recursive_glob: true
|
||||
prospector.scanner.exclude_files:
|
||||
- >-
|
||||
(broker|capture_loss|cluster|conn-summary|console|ecat_arp_info|known_certs|known_hosts|known_services|loaded_scripts|ntp|ocsp|packet_filter|reporter|stats|stderr|stdout).log$
|
||||
clean_inactive: -1
|
||||
parsers: null
|
||||
processors:
|
||||
- dissect:
|
||||
tokenizer: '/nsm/zeek/logs/current/%{pipeline}.log'
|
||||
tokenizer: /nsm/zeek/logs/current/%{pipeline}.log
|
||||
field: log.file.path
|
||||
trim_chars: .log
|
||||
target_prefix: ''
|
||||
@@ -427,18 +277,17 @@ inputs:
|
||||
regexp:
|
||||
pipeline: >-
|
||||
^bacnet*|^bsap*|^cip*|^cotp*|^dnp3*|^ecat*|^enip*|^modbus*|^opcua*|^profinet*|^s7comm*
|
||||
exclude_files:
|
||||
- >-
|
||||
broker|capture_loss|cluster|ecat_arp_info|known_hosts|known_services|loaded_scripts|ntp|ocsp|packet_filter|reporter|stats|stderr|stdout.log$
|
||||
file_identity.native: null
|
||||
prospector.scanner.fingerprint.enabled: false
|
||||
- id: udp-udp-35051de0-46a5-11ee-8d5d-9f98c8182f60
|
||||
name: syslog-udp-514
|
||||
revision: 3
|
||||
revision: 4
|
||||
type: udp
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: udp
|
||||
version: 1.10.0
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: 35051de0-46a5-11ee-8d5d-9f98c8182f60
|
||||
@@ -458,13 +307,13 @@ inputs:
|
||||
- syslog
|
||||
- id: tcp-tcp-33d37bb0-46a5-11ee-8d5d-9f98c8182f60
|
||||
name: syslog-tcp-514
|
||||
revision: 3
|
||||
revision: 4
|
||||
type: tcp
|
||||
use_output: default
|
||||
meta:
|
||||
package:
|
||||
name: tcp
|
||||
version: 1.10.0
|
||||
version:
|
||||
data_stream:
|
||||
namespace: so
|
||||
package_policy_id: 33d37bb0-46a5-11ee-8d5d-9f98c8182f60
|
||||
|
||||
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 '' %}
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
include:
|
||||
- elasticfleet.artifact_registry
|
||||
- elasticfleet.ssl
|
||||
|
||||
# Add EA Group
|
||||
elasticfleetgroup:
|
||||
@@ -95,6 +96,9 @@ soresourcesrepoclone:
|
||||
- rev: 'main'
|
||||
- depth: 1
|
||||
- force_reset: True
|
||||
- retry:
|
||||
attempts: 3
|
||||
interval: 10
|
||||
{% endif %}
|
||||
|
||||
elasticdefendconfdir:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
{% set SERVICETOKEN = salt['pillar.get']('elasticfleet:config:server:es_token','') %}
|
||||
|
||||
include:
|
||||
- ca
|
||||
- logstash.ssl
|
||||
- elasticfleet.config
|
||||
- elasticfleet.sostatus
|
||||
- ssl
|
||||
|
||||
{% if grains.role not in ['so-fleet'] %}
|
||||
# Wait for Elasticsearch to be ready - no reason to try running Elastic Fleet server if ES is not ready
|
||||
@@ -32,6 +33,17 @@ so-elastic-fleet-auto-configure-logstash-outputs:
|
||||
- retry:
|
||||
attempts: 4
|
||||
interval: 30
|
||||
|
||||
{# 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 --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
|
||||
@@ -122,6 +134,11 @@ so-elastic-fleet:
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
- watch:
|
||||
- file: trusttheca
|
||||
- x509: etc_elasticfleet_key
|
||||
- x509: etc_elasticfleet_crt
|
||||
- require:
|
||||
- file: trusttheca
|
||||
- x509: etc_elasticfleet_key
|
||||
- x509: etc_elasticfleet_crt
|
||||
{% endif %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{%- raw -%}
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "import-zeek-logs",
|
||||
@@ -10,19 +10,31 @@
|
||||
"description": "Zeek Import logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/import/*/zeek/logs/*.log"
|
||||
],
|
||||
"data_stream.dataset": "import",
|
||||
"tags": [],
|
||||
"pipeline": "",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": ["({%- endraw -%}{{ ELASTICFLEETMERGED.logging.zeek.excluded | join('|') }}{%- raw -%})(\\..+)?\\.log$"],
|
||||
"include_files": [],
|
||||
"processors": "- dissect:\n tokenizer: \"/nsm/import/%{import.id}/zeek/logs/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"\n- script:\n lang: javascript\n source: >\n function process(event) {\n var pl = event.Get(\"import.file\").slice(0,-4);\n event.Put(\"@metadata.pipeline\", \"zeek.\" + pl);\n }\n- add_fields:\n target: event\n fields:\n category: network\n module: zeek\n imported: true\n- add_tags:\n tags: \"ics\"\n when:\n regexp:\n import.file: \"^bacnet*|^bsap*|^cip*|^cotp*|^dnp3*|^ecat*|^enip*|^modbus*|^opcua*|^profinet*|^s7comm*\"",
|
||||
"custom": "exclude_files: [\"{%- endraw -%}{{ ELASTICFLEETMERGED.logging.zeek.excluded | join('|') }}{%- raw -%}.log$\"]\n"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,36 +11,51 @@
|
||||
{%- endif -%}
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "kratos-logs",
|
||||
"namespace": "so",
|
||||
"description": "Kratos logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/kratos/kratos.log"
|
||||
],
|
||||
"data_stream.dataset": "kratos",
|
||||
"tags": ["so-kratos"],
|
||||
"pipeline": "kratos",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
{%- if valid_identities -%}
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n add_error_key: true\n- add_fields:\n target: event\n fields:\n category: iam\n module: kratos\n- if:\n has_fields:\n - identity_id\n then:{% for id, email in identities %}\n - if:\n equals:\n identity_id: \"{{ id }}\"\n then:\n - add_fields:\n target: ''\n fields:\n user.name: \"{{ email }}\"{% endfor %}",
|
||||
{%- else -%}
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n add_error_key: true\n- add_fields:\n target: event\n fields:\n category: iam\n module: kratos",
|
||||
{%- endif -%}
|
||||
"custom": "pipeline: kratos"
|
||||
"tags": [
|
||||
"so-kratos"
|
||||
],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,28 +2,38 @@
|
||||
{%- raw -%}
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"id": "zeek-logs",
|
||||
"name": "zeek-logs",
|
||||
"namespace": "so",
|
||||
"description": "Zeek logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/zeek/logs/current/*.log"
|
||||
],
|
||||
"data_stream.dataset": "zeek",
|
||||
"tags": [],
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": ["({%- endraw -%}{{ ELASTICFLEETMERGED.logging.zeek.excluded | join('|') }}{%- raw -%})(\\..+)?\\.log$"],
|
||||
"include_files": [],
|
||||
"processors": "- dissect:\n tokenizer: \"/nsm/zeek/logs/current/%{pipeline}.log\"\n field: \"log.file.path\"\n trim_chars: \".log\"\n target_prefix: \"\"\n- script:\n lang: javascript\n source: >\n function process(event) {\n var pl = event.Get(\"pipeline\");\n event.Put(\"@metadata.pipeline\", \"zeek.\" + pl);\n }\n- add_fields:\n target: event\n fields:\n category: network\n module: zeek\n- add_tags:\n tags: \"ics\"\n when:\n regexp:\n pipeline: \"^bacnet*|^bsap*|^cip*|^cotp*|^dnp3*|^ecat*|^enip*|^modbus*|^opcua*|^profinet*|^s7comm*\"",
|
||||
"custom": "exclude_files: [\"{%- endraw -%}{{ ELASTICFLEETMERGED.logging.zeek.excluded | join('|') }}{%- raw -%}.log$\"]\n"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,4 +41,4 @@
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
{%- endraw -%}
|
||||
{%- endraw -%}
|
||||
@@ -5,7 +5,7 @@
|
||||
"package": {
|
||||
"name": "endpoint",
|
||||
"title": "Elastic Defend",
|
||||
"version": "8.18.1",
|
||||
"version": "9.0.2",
|
||||
"requires_root": true
|
||||
},
|
||||
"enabled": true,
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "hydra-logs",
|
||||
"namespace": "so",
|
||||
"description": "Hydra logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/hydra/hydra.log"
|
||||
],
|
||||
"data_stream.dataset": "hydra",
|
||||
"tags": ["so-hydra"],
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n add_error_key: true \n- add_fields:\n target: event\n fields:\n category: iam\n module: hydra",
|
||||
"custom": "pipeline: hydra"
|
||||
"pipeline": "hydra",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n add_error_key: true\n- add_fields:\n target: event\n fields:\n category: iam\n module: hydra",
|
||||
"tags": [
|
||||
"so-hydra"
|
||||
],
|
||||
"recursive_glob": true,
|
||||
"ignore_older": "72h",
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,3 +45,5 @@
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +1,44 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "idh-logs",
|
||||
"namespace": "so",
|
||||
"description": "IDH integration",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/idh/opencanary.log"
|
||||
],
|
||||
"data_stream.dataset": "idh",
|
||||
"tags": [],
|
||||
"pipeline": "common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "\n- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n add_error_key: true\n- convert:\n fields:\n - {from: \"logtype\", to: \"event.code\", type: \"string\"}\n- drop_fields:\n when:\n equals:\n event.code: \"1001\"\n fields: [\"src_host\", \"src_port\", \"dst_host\", \"dst_port\" ]\n ignore_missing: true\n- rename:\n fields:\n - from: \"src_host\"\n to: \"source.ip\"\n - from: \"src_port\"\n to: \"source.port\"\n - from: \"dst_host\"\n to: \"destination.host\"\n - from: \"dst_port\"\n to: \"destination.port\"\n ignore_missing: true\n- drop_fields:\n fields: '[\"prospector\", \"input\", \"offset\", \"beat\"]'\n- add_fields:\n target: event\n fields:\n category: host\n module: opencanary",
|
||||
"custom": "pipeline: common"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,46 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "import-evtx-logs",
|
||||
"namespace": "so",
|
||||
"description": "Import Windows EVTX logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"vars": {},
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/import/*/evtx/*.json"
|
||||
],
|
||||
"data_stream.dataset": "import",
|
||||
"custom": "",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- dissect:\n tokenizer: \"/nsm/import/%{import.id}/evtx/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"\n- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n- drop_fields:\n fields: [\"host\"]\n ignore_missing: true\n- add_fields:\n target: data_stream\n fields:\n type: logs\n dataset: system.security\n- add_fields:\n target: event\n fields:\n dataset: system.security\n module: system\n imported: true\n- add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.security-2.6.1\n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-Sysmon/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.sysmon_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.sysmon_operational\n module: windows\n imported: true\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.sysmon_operational-3.1.2\n- if:\n equals:\n winlog.channel: 'Application'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.application\n - add_fields:\n target: event\n fields:\n dataset: system.application\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.application-2.6.1\n- if:\n equals:\n winlog.channel: 'System'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.system\n - add_fields:\n target: event\n fields:\n dataset: system.system\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.system-2.6.1\n \n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-PowerShell/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.powershell_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.powershell_operational\n module: windows\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.powershell_operational-3.1.2\n- add_fields:\n target: data_stream\n fields:\n dataset: import",
|
||||
"tags": [
|
||||
"import"
|
||||
]
|
||||
],
|
||||
"recursive_glob": true,
|
||||
"ignore_older": "72h",
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,45 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "import-suricata-logs",
|
||||
"namespace": "so",
|
||||
"description": "Import Suricata logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/import/*/suricata/eve*.json"
|
||||
],
|
||||
"data_stream.dataset": "import",
|
||||
"pipeline": "suricata.common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- add_fields:\n target: event\n fields:\n category: network\n module: suricata\n imported: true\n- dissect:\n tokenizer: \"/nsm/import/%{import.id}/suricata/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"\n",
|
||||
"tags": [],
|
||||
"processors": "- add_fields:\n target: event\n fields:\n category: network\n module: suricata\n imported: true\n- dissect:\n tokenizer: \"/nsm/import/%{import.id}/suricata/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"",
|
||||
"custom": "pipeline: suricata.common"
|
||||
"recursive_glob": true,
|
||||
"ignore_older": "72h",
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/redis/redis.log"
|
||||
"/opt/so/log/redis/redis-server.log"
|
||||
],
|
||||
"tags": [
|
||||
"redis-log"
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "rita-logs",
|
||||
"namespace": "so",
|
||||
"description": "RITA Logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"vars": {},
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
@@ -20,15 +19,28 @@
|
||||
"/nsm/rita/exploded-dns.csv",
|
||||
"/nsm/rita/long-connections.csv"
|
||||
],
|
||||
"exclude_files": [],
|
||||
"ignore_older": "72h",
|
||||
"data_stream.dataset": "rita",
|
||||
"tags": [],
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- dissect:\n tokenizer: \"/nsm/rita/%{pipeline}.csv\"\n field: \"log.file.path\"\n trim_chars: \".csv\"\n target_prefix: \"\"\n- script:\n lang: javascript\n source: >\n function process(event) {\n var pl = event.Get(\"pipeline\").split(\"-\");\n if (pl.length > 1) {\n pl = pl[1];\n }\n else {\n pl = pl[0];\n }\n event.Put(\"@metadata.pipeline\", \"rita.\" + pl);\n }\n- add_fields:\n target: event\n fields:\n category: network\n module: rita",
|
||||
"custom": "exclude_lines: ['^Score', '^Source', '^Domain', '^No results']"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"ignore_older": "72h",
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
|
||||
@@ -1,29 +1,41 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "so-ip-mappings",
|
||||
"namespace": "so",
|
||||
"description": "IP Description mappings",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"vars": {},
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/custom-mappings/ip-descriptions.csv"
|
||||
],
|
||||
"data_stream.dataset": "hostnamemappings",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- decode_csv_fields:\n fields:\n message: decoded.csv\n separator: \",\"\n ignore_missing: false\n overwrite_keys: true\n trim_leading_space: true\n fail_on_error: true\n\n- extract_array:\n field: decoded.csv\n mappings:\n so.ip_address: '0'\n so.description: '1'\n\n- script:\n lang: javascript\n source: >\n function process(event) {\n var ip = event.Get('so.ip_address');\n var validIpRegex = /^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)$/\n if (!validIpRegex.test(ip)) {\n event.Cancel();\n }\n }\n- fingerprint:\n fields: [\"so.ip_address\"]\n target_field: \"@metadata._id\"\n",
|
||||
"tags": [
|
||||
"so-ip-mappings"
|
||||
],
|
||||
"processors": "- decode_csv_fields:\n fields:\n message: decoded.csv\n separator: \",\"\n ignore_missing: false\n overwrite_keys: true\n trim_leading_space: true\n fail_on_error: true\n\n- extract_array:\n field: decoded.csv\n mappings:\n so.ip_address: '0'\n so.description: '1'\n\n- script:\n lang: javascript\n source: >\n function process(event) {\n var ip = event.Get('so.ip_address');\n var validIpRegex = /^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)$/\n if (!validIpRegex.test(ip)) {\n event.Cancel();\n }\n }\n- fingerprint:\n fields: [\"so.ip_address\"]\n target_field: \"@metadata._id\"\n",
|
||||
"custom": ""
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,5 +43,3 @@
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +1,44 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "soc-auth-sync-logs",
|
||||
"namespace": "so",
|
||||
"description": "Security Onion - Elastic Auth Sync - Logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/soc/sync.log"
|
||||
],
|
||||
"data_stream.dataset": "soc",
|
||||
"tags": ["so-soc"],
|
||||
"pipeline": "common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- dissect:\n tokenizer: \"%{event.action}\"\n field: \"message\"\n target_prefix: \"\"\n- add_fields:\n target: event\n fields:\n category: host\n module: soc\n dataset_temp: auth_sync",
|
||||
"custom": "pipeline: common"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,48 @@
|
||||
{
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "soc-detections-logs",
|
||||
"description": "Security Onion Console - Detections Logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/soc/detections_runtime-status_sigma.log",
|
||||
"/opt/so/log/soc/detections_runtime-status_yara.log"
|
||||
],
|
||||
"exclude_files": [],
|
||||
"ignore_older": "72h",
|
||||
"data_stream.dataset": "soc",
|
||||
"pipeline": "common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"soc\"\n process_array: true\n max_depth: 2\n add_error_key: true \n- add_fields:\n target: event\n fields:\n category: host\n module: soc\n dataset_temp: detections\n- rename:\n fields:\n - from: \"soc.fields.sourceIp\"\n to: \"source.ip\"\n - from: \"soc.fields.status\"\n to: \"http.response.status_code\"\n - from: \"soc.fields.method\"\n to: \"http.request.method\"\n - from: \"soc.fields.path\"\n to: \"url.path\"\n - from: \"soc.message\"\n to: \"event.action\"\n - from: \"soc.level\"\n to: \"log.level\"\n ignore_missing: true",
|
||||
"tags": [
|
||||
"so-soc"
|
||||
],
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"soc\"\n process_array: true\n max_depth: 2\n add_error_key: true \n- add_fields:\n target: event\n fields:\n category: host\n module: soc\n dataset_temp: detections\n- rename:\n fields:\n - from: \"soc.fields.sourceIp\"\n to: \"source.ip\"\n - from: \"soc.fields.status\"\n to: \"http.response.status_code\"\n - from: \"soc.fields.method\"\n to: \"http.request.method\"\n - from: \"soc.fields.path\"\n to: \"url.path\"\n - from: \"soc.message\"\n to: \"event.action\"\n - from: \"soc.level\"\n to: \"log.level\"\n ignore_missing: true",
|
||||
"custom": "pipeline: common"
|
||||
"recursive_glob": true,
|
||||
"ignore_older": "72h",
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,46 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "soc-salt-relay-logs",
|
||||
"namespace": "so",
|
||||
"description": "Security Onion - Salt Relay - Logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/soc/salt-relay.log"
|
||||
],
|
||||
"data_stream.dataset": "soc",
|
||||
"tags": ["so-soc"],
|
||||
"pipeline": "common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- dissect:\n tokenizer: \"%{soc.ts} | %{event.action}\"\n field: \"message\"\n target_prefix: \"\"\n- add_fields:\n target: event\n fields:\n category: host\n module: soc\n dataset_temp: salt_relay",
|
||||
"custom": "pipeline: common"
|
||||
"tags": [
|
||||
"so-soc"
|
||||
],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,44 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "soc-sensoroni-logs",
|
||||
"namespace": "so",
|
||||
"description": "Security Onion - Sensoroni - Logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/sensoroni/sensoroni.log"
|
||||
],
|
||||
"data_stream.dataset": "soc",
|
||||
"tags": [],
|
||||
"pipeline": "common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"sensoroni\"\n process_array: true\n max_depth: 2\n add_error_key: true \n- add_fields:\n target: event\n fields:\n category: host\n module: soc\n dataset_temp: sensoroni\n- rename:\n fields:\n - from: \"sensoroni.fields.sourceIp\"\n to: \"source.ip\"\n - from: \"sensoroni.fields.status\"\n to: \"http.response.status_code\"\n - from: \"sensoroni.fields.method\"\n to: \"http.request.method\"\n - from: \"sensoroni.fields.path\"\n to: \"url.path\"\n - from: \"sensoroni.message\"\n to: \"event.action\"\n - from: \"sensoroni.level\"\n to: \"log.level\"\n ignore_missing: true",
|
||||
"custom": "pipeline: common"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
"force": true
|
||||
}
|
||||
@@ -1,30 +1,46 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "soc-server-logs",
|
||||
"namespace": "so",
|
||||
"description": "Security Onion Console Logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/soc/sensoroni-server.log"
|
||||
],
|
||||
"data_stream.dataset": "soc",
|
||||
"tags": ["so-soc"],
|
||||
"pipeline": "common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"soc\"\n process_array: true\n max_depth: 2\n add_error_key: true \n- add_fields:\n target: event\n fields:\n category: host\n module: soc\n dataset_temp: server\n- rename:\n fields:\n - from: \"soc.fields.sourceIp\"\n to: \"source.ip\"\n - from: \"soc.fields.status\"\n to: \"http.response.status_code\"\n - from: \"soc.fields.method\"\n to: \"http.request.method\"\n - from: \"soc.fields.path\"\n to: \"url.path\"\n - from: \"soc.message\"\n to: \"event.action\"\n - from: \"soc.level\"\n to: \"log.level\"\n ignore_missing: true",
|
||||
"custom": "pipeline: common"
|
||||
"tags": [
|
||||
"so-soc"
|
||||
],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,44 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "strelka-logs",
|
||||
"namespace": "so",
|
||||
"description": "Strelka logs",
|
||||
"description": "Strelka Logs",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/strelka/log/strelka.log"
|
||||
],
|
||||
"data_stream.dataset": "strelka",
|
||||
"tags": [],
|
||||
"pipeline": "strelka.file",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- add_fields:\n target: event\n fields:\n category: file\n module: strelka",
|
||||
"custom": "pipeline: strelka.file"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,44 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "log",
|
||||
"name": "filestream",
|
||||
"version": ""
|
||||
},
|
||||
"name": "suricata-logs",
|
||||
"namespace": "so",
|
||||
"description": "Suricata integration",
|
||||
"policy_id": "so-grid-nodes_general",
|
||||
"namespace": "so",
|
||||
"inputs": {
|
||||
"logs-logfile": {
|
||||
"filestream-filestream": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"log.logs": {
|
||||
"filestream.generic": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/nsm/suricata/eve*.json"
|
||||
],
|
||||
"data_stream.dataset": "suricata",
|
||||
"tags": [],
|
||||
"data_stream.dataset": "filestream.generic",
|
||||
"pipeline": "suricata.common",
|
||||
"parsers": "#- ndjson:\n# target: \"\"\n# message_key: msg\n#- multiline:\n# type: count\n# count_lines: 3\n",
|
||||
"exclude_files": [
|
||||
"\\.gz$"
|
||||
],
|
||||
"include_files": [],
|
||||
"processors": "- add_fields:\n target: event\n fields:\n category: network\n module: suricata",
|
||||
"custom": "pipeline: suricata.common"
|
||||
"tags": [],
|
||||
"recursive_glob": true,
|
||||
"clean_inactive": -1,
|
||||
"harvester_limit": 0,
|
||||
"fingerprint": false,
|
||||
"fingerprint_offset": 0,
|
||||
"fingerprint_length": "64",
|
||||
"file_identity_native": true,
|
||||
"exclude_lines": [],
|
||||
"include_lines": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "elasticsearch",
|
||||
"version": ""
|
||||
},
|
||||
"name": "elasticsearch-grid-nodes_heavy",
|
||||
"namespace": "default",
|
||||
"description": "Elasticsearch Logs",
|
||||
"policy_id": "so-grid-nodes_heavy",
|
||||
"inputs": {
|
||||
"elasticsearch-logfile": {
|
||||
"enabled": true,
|
||||
"streams": {
|
||||
"elasticsearch.audit": {
|
||||
"enabled": false,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/var/log/elasticsearch/*_audit.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"elasticsearch.deprecation": {
|
||||
"enabled": false,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/var/log/elasticsearch/*_deprecation.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"elasticsearch.gc": {
|
||||
"enabled": false,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/var/log/elasticsearch/gc.log.[0-9]*",
|
||||
"/var/log/elasticsearch/gc.log"
|
||||
]
|
||||
}
|
||||
},
|
||||
"elasticsearch.server": {
|
||||
"enabled": true,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/opt/so/log/elasticsearch/*.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"elasticsearch.slowlog": {
|
||||
"enabled": false,
|
||||
"vars": {
|
||||
"paths": [
|
||||
"/var/log/elasticsearch/*_index_search_slowlog.json",
|
||||
"/var/log/elasticsearch/*_index_indexing_slowlog.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"elasticsearch-elasticsearch/metrics": {
|
||||
"enabled": false,
|
||||
"vars": {
|
||||
"hosts": [
|
||||
"http://localhost:9200"
|
||||
],
|
||||
"scope": "node"
|
||||
},
|
||||
"streams": {
|
||||
"elasticsearch.stack_monitoring.ccr": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.cluster_stats": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.enrich": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.index": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.index_recovery": {
|
||||
"enabled": false,
|
||||
"vars": {
|
||||
"active.only": true
|
||||
}
|
||||
},
|
||||
"elasticsearch.stack_monitoring.index_summary": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.ml_job": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.node": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.node_stats": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.pending_tasks": {
|
||||
"enabled": false
|
||||
},
|
||||
"elasticsearch.stack_monitoring.shard": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"force": true
|
||||
}
|
||||
@@ -8,7 +8,9 @@
|
||||
{% endif %}
|
||||
|
||||
{% set AGENT_STATUS = salt['service.available']('elastic-agent') %}
|
||||
{% if not AGENT_STATUS %}
|
||||
{% set AGENT_EXISTS = salt['file.file_exists']('/opt/Elastic/Agent/elastic-agent') %}
|
||||
|
||||
{% if not AGENT_STATUS or not AGENT_EXISTS %}
|
||||
|
||||
pull_agent_installer:
|
||||
file.managed:
|
||||
@@ -19,7 +21,7 @@ pull_agent_installer:
|
||||
|
||||
run_installer:
|
||||
cmd.run:
|
||||
- name: ./so-elastic-agent_linux_amd64 -token={{ GRIDNODETOKEN }}
|
||||
- name: ./so-elastic-agent_linux_amd64 -token={{ GRIDNODETOKEN }} -force
|
||||
- cwd: /opt/so
|
||||
- retry:
|
||||
attempts: 3
|
||||
|
||||
@@ -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.
|
||||
|
||||
186
salt/elasticfleet/ssl.sls
Normal file
186
salt/elasticfleet/ssl.sls
Normal file
@@ -0,0 +1,186 @@
|
||||
# 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 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
|
||||
{% from 'ca/map.jinja' import CA %}
|
||||
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-heavynode', 'so-fleet', 'so-receiver'] %}
|
||||
|
||||
{% if grains['role'] not in [ 'so-heavynode', 'so-receiver'] %}
|
||||
# Start -- Elastic Fleet Host Cert
|
||||
etc_elasticfleet_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/elasticfleet-server.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/elasticfleet-server.key') -%}
|
||||
- prereq:
|
||||
- x509: etc_elasticfleet_crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
etc_elasticfleet_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/elasticfleet-server.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: elasticfleet
|
||||
- private_key: /etc/pki/elasticfleet-server.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }},DNS:{{ GLOBALS.url_base }},IP:{{ GLOBALS.node_ip }}{% if ELASTICFLEETMERGED.config.server.custom_fqdn | length > 0 %},DNS:{{ ELASTICFLEETMERGED.config.server.custom_fqdn | join(',DNS:') }}{% endif %}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
efperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-server.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
chownelasticfleetcrt:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-server.crt
|
||||
- mode: 640
|
||||
- user: 947
|
||||
- group: 939
|
||||
|
||||
chownelasticfleetkey:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-server.key
|
||||
- mode: 640
|
||||
- user: 947
|
||||
- group: 939
|
||||
# End -- Elastic Fleet Host Cert
|
||||
{% endif %} # endif is for not including HeavyNodes & Receivers
|
||||
|
||||
|
||||
# Start -- Elastic Fleet Client Cert for Agent (Mutual Auth with Logstash Output)
|
||||
etc_elasticfleet_agent_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/elasticfleet-agent.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/elasticfleet-agent.key') -%}
|
||||
- prereq:
|
||||
- x509: etc_elasticfleet_agent_crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
etc_elasticfleet_agent_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/elasticfleet-agent.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: elasticfleet
|
||||
- private_key: /etc/pki/elasticfleet-agent.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
cmd.run:
|
||||
- name: "/usr/bin/openssl pkcs8 -in /etc/pki/elasticfleet-agent.key -topk8 -out /etc/pki/elasticfleet-agent.p8 -nocrypt"
|
||||
- onchanges:
|
||||
- x509: etc_elasticfleet_agent_key
|
||||
|
||||
efagentperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-agent.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
chownelasticfleetagentcrt:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-agent.crt
|
||||
- mode: 640
|
||||
- user: 947
|
||||
- group: 939
|
||||
|
||||
chownelasticfleetagentkey:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-agent.key
|
||||
- mode: 640
|
||||
- user: 947
|
||||
- group: 939
|
||||
# End -- Elastic Fleet Client Cert for Agent (Mutual Auth with Logstash Output)
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone'] %}
|
||||
elasticfleet_kafka_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/elasticfleet-kafka.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/elasticfleet-kafka.key') -%}
|
||||
- prereq:
|
||||
- x509: elasticfleet_kafka_crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
elasticfleet_kafka_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/elasticfleet-kafka.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: kafka
|
||||
- private_key: /etc/pki/elasticfleet-kafka.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
elasticfleet_kafka_cert_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-kafka.crt
|
||||
- mode: 640
|
||||
- user: 947
|
||||
- group: 939
|
||||
|
||||
elasticfleet_kafka_key_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-kafka.key
|
||||
- mode: 640
|
||||
- user: 947
|
||||
- group: 939
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
@@ -17,9 +17,9 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
|
||||
|
||||
# Third, configure Elastic Defend Integration seperately
|
||||
/usr/sbin/so-elastic-fleet-integration-policy-elastic-defend
|
||||
|
||||
# Initial Endpoints
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/endpoints-initial/*.json
|
||||
do
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/endpoints-initial/*.json; do
|
||||
printf "\n\nInitial Endpoints Policy - Loading $INTEGRATION\n"
|
||||
elastic_fleet_integration_check "endpoints-initial" "$INTEGRATION"
|
||||
if [ -n "$INTEGRATION_ID" ]; then
|
||||
@@ -40,8 +40,7 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
|
||||
done
|
||||
|
||||
# Grid Nodes - General
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/grid-nodes_general/*.json
|
||||
do
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/grid-nodes_general/*.json; do
|
||||
printf "\n\nGrid Nodes Policy_General - Loading $INTEGRATION\n"
|
||||
elastic_fleet_integration_check "so-grid-nodes_general" "$INTEGRATION"
|
||||
if [ -n "$INTEGRATION_ID" ]; then
|
||||
@@ -60,13 +59,9 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ "$RETURN_CODE" != "1" ]]; then
|
||||
touch /opt/so/state/eaintegrations.txt
|
||||
fi
|
||||
|
||||
# Grid Nodes - Heavy
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/grid-nodes_heavy/*.json
|
||||
do
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/grid-nodes_heavy/*.json; do
|
||||
printf "\n\nGrid Nodes Policy_Heavy - Loading $INTEGRATION\n"
|
||||
elastic_fleet_integration_check "so-grid-nodes_heavy" "$INTEGRATION"
|
||||
if [ -n "$INTEGRATION_ID" ]; then
|
||||
@@ -78,22 +73,16 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
|
||||
fi
|
||||
else
|
||||
printf "\n\nIntegration does not exist - Creating integration\n"
|
||||
if [ "$NAME" != "elasticsearch-logs" ]; then
|
||||
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
|
||||
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
|
||||
RETURN_CODE=1
|
||||
continue
|
||||
fi
|
||||
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
|
||||
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
|
||||
RETURN_CODE=1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ "$RETURN_CODE" != "1" ]]; then
|
||||
touch /opt/so/state/eaintegrations.txt
|
||||
fi
|
||||
|
||||
# Fleet Server - Optional integrations
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/*.json
|
||||
do
|
||||
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/*.json; do
|
||||
if ! [ "$INTEGRATION" == "/opt/so/conf/elastic-fleet/integrations-optional/FleetServer*/*.json" ]; then
|
||||
FLEET_POLICY=`echo "$INTEGRATION"| cut -d'/' -f7`
|
||||
printf "\n\nFleet Server Policy - Loading $INTEGRATION\n"
|
||||
@@ -117,6 +106,8 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Only create the state file if all policies were created/updated successfully
|
||||
if [[ "$RETURN_CODE" != "1" ]]; then
|
||||
touch /opt/so/state/eaintegrations.txt
|
||||
fi
|
||||
|
||||
@@ -14,7 +14,7 @@ if ! is_manager_node; then
|
||||
fi
|
||||
|
||||
# Get current list of Grid Node Agents that need to be upgraded
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/agents?perPage=20&page=1&kuery=NOT%20agent.version%20:%20%22{{ELASTICSEARCHDEFAULTS.elasticsearch.version}}%22%20and%20policy_id%20:%20%22so-grid-nodes_general%22&showInactive=false&getStatusSummary=true")
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/agents?perPage=20&page=1&kuery=NOT%20agent.version%3A%20{{ELASTICSEARCHDEFAULTS.elasticsearch.version}}%20AND%20policy_id%3A%20so-grid-nodes_%2A&showInactive=false&getStatusSummary=true" --retry 3 --retry-delay 30 --fail 2>/dev/null)
|
||||
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.page' <<< "$RAW_JSON")
|
||||
|
||||
@@ -26,7 +26,7 @@ function update_es_urls() {
|
||||
}
|
||||
|
||||
# Get current list of Fleet Elasticsearch URLs
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/outputs/so-manager_elasticsearch')
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/outputs/so-manager_elasticsearch' --retry 3 --retry-delay 30 --fail 2>/dev/null)
|
||||
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.item.id' <<< "$RAW_JSON")
|
||||
|
||||
@@ -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,11 +3,36 @@
|
||||
# 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
|
||||
-f|--force)
|
||||
FORCE_UPDATE=true
|
||||
shift
|
||||
;;
|
||||
-c| --certs)
|
||||
UPDATE_CERTS=true
|
||||
FORCE_UPDATE=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option $1"
|
||||
echo "Usage: $0 [-f|--force] [-c|--certs]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Only run on Managers
|
||||
if ! is_manager_node; then
|
||||
printf "Not a Manager Node... Exiting"
|
||||
@@ -17,17 +42,49 @@ fi
|
||||
function update_logstash_outputs() {
|
||||
if logstash_policy=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_logstash" --retry 3 --retry-delay 10 --fail 2>/dev/null); then
|
||||
SSL_CONFIG=$(echo "$logstash_policy" | jq -r '.item.ssl')
|
||||
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
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--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}')
|
||||
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":$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":$CONFIG_YAML,"ssl": {"certificate": $LOGSTASHCRT,"certificate_authorities":[ $LOGSTASHCA ]},"secrets": {"ssl":{"key": $LOGSTASHKEY }}}')
|
||||
fi
|
||||
else
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--argjson SSL_CONFIG "$SSL_CONFIG" \
|
||||
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":"","ssl": $SSL_CONFIG}')
|
||||
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":$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":$CONFIG_YAML,"ssl": {"certificate": $LOGSTASHCRT,"key": $LOGSTASHKEY,"certificate_authorities":[ $LOGSTASHCA ]}}')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -38,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
|
||||
@@ -62,7 +142,7 @@ function update_kafka_outputs() {
|
||||
|
||||
{% if GLOBALS.pipeline == "KAFKA" %}
|
||||
# Get current list of Kafka Outputs
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/outputs/so-manager_kafka')
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/outputs/so-manager_kafka' --retry 3 --retry-delay 30 --fail 2>/dev/null)
|
||||
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.item.id' <<< "$RAW_JSON")
|
||||
@@ -73,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=()
|
||||
|
||||
@@ -88,7 +168,7 @@ function update_kafka_outputs() {
|
||||
{# If global pipeline isn't set to KAFKA then assume default of REDIS / logstash #}
|
||||
{% else %}
|
||||
# Get current list of Logstash Outputs
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/outputs/so-manager_logstash')
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/outputs/so-manager_logstash' --retry 3 --retry-delay 30 --fail 2>/dev/null)
|
||||
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.item.id' <<< "$RAW_JSON")
|
||||
@@ -96,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=()
|
||||
|
||||
@@ -148,10 +237,10 @@ 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" ]; then
|
||||
if [[ "$NEW_HASH" = "$CURRENT_HASH" ]] && [[ "$FORCE_UPDATE" != "true" ]]; then
|
||||
printf "\nHashes match - no update needed.\n"
|
||||
printf "Current List: $CURRENT_LIST\nNew List: $NEW_LIST_JSON\n"
|
||||
|
||||
|
||||
@@ -241,9 +241,11 @@ printf '%s\n'\
|
||||
"" >> "$global_pillar_file"
|
||||
|
||||
# Call Elastic-Fleet Salt State
|
||||
printf "\nApplying elasticfleet state"
|
||||
salt-call state.apply elasticfleet queue=True
|
||||
|
||||
# Generate installers & install Elastic Agent on the node
|
||||
so-elastic-agent-gen-installers
|
||||
printf "\nApplying elasticfleet.install_agent_grid state"
|
||||
salt-call state.apply elasticfleet.install_agent_grid queue=True
|
||||
exit 0
|
||||
|
||||
@@ -23,7 +23,7 @@ function update_fleet_urls() {
|
||||
}
|
||||
|
||||
# Get current list of Fleet Server URLs
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/fleet_server_hosts/grid-default')
|
||||
RAW_JSON=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/fleet_server_hosts/grid-default' --retry 3 --retry-delay 30 --fail 2>/dev/null)
|
||||
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.item.id' <<< "$RAW_JSON")
|
||||
|
||||
@@ -34,6 +34,11 @@ if [[ "$RETURN_CODE" != "0" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f /etc/pki/elasticfleet-kafka.crt || ! -f /etc/pki/elasticfleet-kafka.key ]]; then
|
||||
echo -e "\nKafka certificates not found, can't setup Elastic Fleet output policy for Kafka...\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt)
|
||||
KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key)
|
||||
KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
|
||||
@@ -47,7 +52,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 +72,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"
|
||||
|
||||
@@ -26,14 +26,14 @@ catrustscript:
|
||||
GLOBALS: {{ GLOBALS }}
|
||||
{% endif %}
|
||||
|
||||
cacertz:
|
||||
elasticsearch_cacerts:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/ca/cacerts
|
||||
- source: salt://elasticsearch/cacerts
|
||||
- user: 939
|
||||
- group: 939
|
||||
|
||||
capemz:
|
||||
elasticsearch_capems:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/ca/tls-ca-bundle.pem
|
||||
- source: salt://elasticsearch/tls-ca-bundle.pem
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
|
||||
{% from 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states %}
|
||||
|
||||
include:
|
||||
- ssl
|
||||
- elasticsearch.ca
|
||||
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCHMERGED %}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
elasticsearch:
|
||||
enabled: false
|
||||
version: 8.18.8
|
||||
version: 9.0.8
|
||||
index_clean: true
|
||||
config:
|
||||
action:
|
||||
destructive_requires_name: true
|
||||
cluster:
|
||||
logsdb:
|
||||
enabled: false
|
||||
routing:
|
||||
allocation:
|
||||
disk:
|
||||
@@ -72,6 +74,8 @@ elasticsearch:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 0
|
||||
allocate:
|
||||
number_of_replicas: ""
|
||||
min_age: 60d
|
||||
delete:
|
||||
actions:
|
||||
@@ -84,11 +88,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 +263,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-detection:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -284,6 +301,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 +614,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-import:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -664,7 +693,6 @@ elasticsearch:
|
||||
match_mapping_type: string
|
||||
settings:
|
||||
index:
|
||||
final_pipeline: .fleet_final_pipeline-1
|
||||
lifecycle:
|
||||
name: so-import-logs
|
||||
mapping:
|
||||
@@ -830,53 +858,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 +918,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-hydra:
|
||||
close: 30
|
||||
delete: 365
|
||||
@@ -1043,7 +1028,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-lists:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -1127,6 +1111,8 @@ elasticsearch:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 0
|
||||
allocate:
|
||||
number_of_replicas: ""
|
||||
min_age: 60d
|
||||
delete:
|
||||
actions:
|
||||
@@ -1139,11 +1125,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 +3123,6 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
warm: 7
|
||||
so-logs-system_x_application:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
{% from 'elasticsearch/template.map.jinja' import ES_INDEX_SETTINGS %}
|
||||
|
||||
include:
|
||||
- ca
|
||||
- elasticsearch.ca
|
||||
- elasticsearch.ssl
|
||||
- elasticsearch.config
|
||||
- elasticsearch.sostatus
|
||||
|
||||
@@ -61,11 +64,7 @@ so-elasticsearch:
|
||||
- /nsm/elasticsearch:/usr/share/elasticsearch/data:rw
|
||||
- /opt/so/log/elasticsearch:/var/log/elasticsearch:rw
|
||||
- /opt/so/conf/ca/cacerts:/usr/share/elasticsearch/jdk/lib/security/cacerts:ro
|
||||
{% if GLOBALS.is_manager %}
|
||||
- /etc/pki/ca.crt:/usr/share/elasticsearch/config/ca.crt:ro
|
||||
{% else %}
|
||||
- /etc/pki/tls/certs/intca.crt:/usr/share/elasticsearch/config/ca.crt:ro
|
||||
{% endif %}
|
||||
- /etc/pki/elasticsearch.crt:/usr/share/elasticsearch/config/elasticsearch.crt:ro
|
||||
- /etc/pki/elasticsearch.key:/usr/share/elasticsearch/config/elasticsearch.key:ro
|
||||
- /etc/pki/elasticsearch.p12:/usr/share/elasticsearch/config/elasticsearch.p12:ro
|
||||
@@ -82,22 +81,21 @@ so-elasticsearch:
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
- watch:
|
||||
- file: cacertz
|
||||
- file: trusttheca
|
||||
- x509: elasticsearch_crt
|
||||
- x509: elasticsearch_key
|
||||
- file: elasticsearch_cacerts
|
||||
- file: esyml
|
||||
- require:
|
||||
- file: trusttheca
|
||||
- x509: elasticsearch_crt
|
||||
- x509: elasticsearch_key
|
||||
- file: elasticsearch_cacerts
|
||||
- file: esyml
|
||||
- file: eslog4jfile
|
||||
- file: nsmesdir
|
||||
- file: eslogdir
|
||||
- file: cacertz
|
||||
- x509: /etc/pki/elasticsearch.crt
|
||||
- x509: /etc/pki/elasticsearch.key
|
||||
- file: elasticp12perms
|
||||
{% if GLOBALS.is_manager %}
|
||||
- x509: pki_public_ca_crt
|
||||
{% else %}
|
||||
- x509: trusttheca
|
||||
{% endif %}
|
||||
- cmd: auth_users_roles_inode
|
||||
- cmd: auth_users_inode
|
||||
|
||||
|
||||
@@ -1,31 +1,212 @@
|
||||
{
|
||||
"version": 3,
|
||||
"_meta": {
|
||||
"managed_by": "securityonion",
|
||||
"managed": true
|
||||
},
|
||||
"description": "Custom pipeline for processing all incoming Fleet Agent documents. \n",
|
||||
"processors": [
|
||||
{ "set": { "ignore_failure": true, "field": "event.module", "value": "elastic_agent" } },
|
||||
{ "split": { "if": "ctx.event?.dataset != null && ctx.event.dataset.contains('.')", "field": "event.dataset", "separator": "\\.", "target_field": "module_temp" } },
|
||||
{ "split": { "if": "ctx.data_stream?.dataset != null && ctx.data_stream?.dataset.contains('.')", "field":"data_stream.dataset", "separator":"\\.", "target_field":"datastream_dataset_temp", "ignore_missing":true } },
|
||||
{ "set": { "if": "ctx.module_temp != null", "override": true, "field": "event.module", "value": "{{module_temp.0}}" } },
|
||||
{ "set": { "if": "ctx.datastream_dataset_temp != null && ctx.datastream_dataset_temp[0] == 'network_traffic'", "field":"event.module", "value":"{{ datastream_dataset_temp.0 }}", "ignore_failure":true, "ignore_empty_value":true, "description":"Fix EA network packet capture" } },
|
||||
{ "gsub": { "if": "ctx.event?.dataset != null && ctx.event.dataset.contains('.')", "field": "event.dataset", "pattern": "^[^.]*.", "replacement": "", "target_field": "dataset_tag_temp" } },
|
||||
{ "append": { "if": "ctx.dataset_tag_temp != null", "field": "tags", "value": "{{dataset_tag_temp}}", "allow_duplicates": false } },
|
||||
{ "set": { "if": "ctx.network?.direction == 'egress'", "override": true, "field": "network.initiated", "value": "true" } },
|
||||
{ "set": { "if": "ctx.network?.direction == 'ingress'", "override": true, "field": "network.initiated", "value": "false" } },
|
||||
{ "set": { "if": "ctx.network?.type == 'ipv4'", "override": true, "field": "destination.ipv6", "value": "false" } },
|
||||
{ "set": { "if": "ctx.network?.type == 'ipv6'", "override": true, "field": "destination.ipv6", "value": "true" } },
|
||||
{ "set": { "if": "ctx.tags != null && ctx.tags.contains('import')", "override": true, "field": "data_stream.dataset", "value": "import" } },
|
||||
{ "set": { "if": "ctx.tags != null && ctx.tags.contains('import')", "override": true, "field": "data_stream.namespace", "value": "so" } },
|
||||
{ "community_id":{ "if": "ctx.event?.dataset == 'endpoint.events.network'", "ignore_failure":true } },
|
||||
{ "set": { "if": "ctx.event?.module == 'fim'", "override": true, "field": "event.module", "value": "file_integrity" } },
|
||||
{ "rename": { "if": "ctx.winlog?.provider_name == 'Microsoft-Windows-Windows Defender'", "ignore_missing": true, "field": "winlog.event_data.Threat Name", "target_field": "winlog.event_data.threat_name" } },
|
||||
{ "set": { "if": "ctx?.metadata?.kafka != null" , "field": "kafka.id", "value": "{{metadata.kafka.partition}}{{metadata.kafka.offset}}{{metadata.kafka.timestamp}}", "ignore_failure": true } },
|
||||
{ "set": { "if": "ctx.event?.dataset != null && ctx.event?.dataset == 'elasticsearch.server'", "field": "event.module", "value":"elasticsearch" }},
|
||||
{"append": {"field":"related.ip","value":["{{source.ip}}","{{destination.ip}}"],"allow_duplicates":false,"if":"ctx?.event?.dataset == 'endpoint.events.network' && ctx?.source?.ip != null","ignore_failure":true}},
|
||||
{"foreach": {"field":"host.ip","processor":{"append":{"field":"related.ip","value":"{{_ingest._value}}","allow_duplicates":false}},"if":"ctx?.event?.module == 'endpoint' && ctx?.host?.ip != null","ignore_missing":true, "description":"Extract IPs from Elastic Agent events (host.ip) and adds them to related.ip"}},
|
||||
{ "remove": { "field": [ "message2", "type", "fields", "category", "module", "dataset", "event.dataset_temp", "dataset_tag_temp", "module_temp", "datastream_dataset_temp" ], "ignore_missing": true, "ignore_failure": true } }
|
||||
]
|
||||
}
|
||||
"version": 3,
|
||||
"_meta": {
|
||||
"managed_by": "securityonion",
|
||||
"managed": true
|
||||
},
|
||||
"description": "Custom pipeline for processing all incoming Fleet Agent documents. \n",
|
||||
"processors": [
|
||||
{
|
||||
"set": {
|
||||
"ignore_failure": true,
|
||||
"field": "event.module",
|
||||
"value": "elastic_agent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"split": {
|
||||
"if": "ctx.event?.dataset != null && ctx.event.dataset.contains('.')",
|
||||
"field": "event.dataset",
|
||||
"separator": "\\.",
|
||||
"target_field": "module_temp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"split": {
|
||||
"if": "ctx.data_stream?.dataset != null && ctx.data_stream?.dataset.contains('.')",
|
||||
"field": "data_stream.dataset",
|
||||
"separator": "\\.",
|
||||
"target_field": "datastream_dataset_temp",
|
||||
"ignore_missing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.module_temp != null",
|
||||
"override": true,
|
||||
"field": "event.module",
|
||||
"value": "{{module_temp.0}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.datastream_dataset_temp != null && ctx.datastream_dataset_temp[0] == 'network_traffic'",
|
||||
"field": "event.module",
|
||||
"value": "{{ datastream_dataset_temp.0 }}",
|
||||
"ignore_failure": true,
|
||||
"ignore_empty_value": true,
|
||||
"description": "Fix EA network packet capture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gsub": {
|
||||
"if": "ctx.event?.dataset != null && ctx.event.dataset.contains('.')",
|
||||
"field": "event.dataset",
|
||||
"pattern": "^[^.]*.",
|
||||
"replacement": "",
|
||||
"target_field": "dataset_tag_temp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"append": {
|
||||
"if": "ctx.dataset_tag_temp != null",
|
||||
"field": "tags",
|
||||
"value": "{{dataset_tag_temp}}",
|
||||
"allow_duplicates": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.network?.direction == 'egress'",
|
||||
"override": true,
|
||||
"field": "network.initiated",
|
||||
"value": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.network?.direction == 'ingress'",
|
||||
"override": true,
|
||||
"field": "network.initiated",
|
||||
"value": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.network?.type == 'ipv4'",
|
||||
"override": true,
|
||||
"field": "destination.ipv6",
|
||||
"value": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.network?.type == 'ipv6'",
|
||||
"override": true,
|
||||
"field": "destination.ipv6",
|
||||
"value": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.tags != null && ctx.tags.contains('import')",
|
||||
"override": true,
|
||||
"field": "data_stream.dataset",
|
||||
"value": "import"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.tags != null && ctx.tags.contains('import')",
|
||||
"override": true,
|
||||
"field": "data_stream.namespace",
|
||||
"value": "so"
|
||||
}
|
||||
},
|
||||
{
|
||||
"community_id": {
|
||||
"if": "ctx.event?.dataset == 'endpoint.events.network'",
|
||||
"ignore_failure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.event?.module == 'fim'",
|
||||
"override": true,
|
||||
"field": "event.module",
|
||||
"value": "file_integrity"
|
||||
}
|
||||
},
|
||||
{
|
||||
"rename": {
|
||||
"if": "ctx.winlog?.provider_name == 'Microsoft-Windows-Windows Defender'",
|
||||
"ignore_missing": true,
|
||||
"field": "winlog.event_data.Threat Name",
|
||||
"target_field": "winlog.event_data.threat_name"
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx?.metadata?.kafka != null",
|
||||
"field": "kafka.id",
|
||||
"value": "{{metadata.kafka.partition}}{{metadata.kafka.offset}}{{metadata.kafka.timestamp}}",
|
||||
"ignore_failure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"set": {
|
||||
"if": "ctx.event?.dataset != null && ctx.event?.dataset == 'elasticsearch.server'",
|
||||
"field": "event.module",
|
||||
"value": "elasticsearch"
|
||||
}
|
||||
},
|
||||
{
|
||||
"append": {
|
||||
"field": "related.ip",
|
||||
"value": [
|
||||
"{{source.ip}}",
|
||||
"{{destination.ip}}"
|
||||
],
|
||||
"allow_duplicates": false,
|
||||
"if": "ctx?.event?.dataset == 'endpoint.events.network' && ctx?.source?.ip != null",
|
||||
"ignore_failure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"foreach": {
|
||||
"field": "host.ip",
|
||||
"processor": {
|
||||
"append": {
|
||||
"field": "related.ip",
|
||||
"value": "{{_ingest._value}}",
|
||||
"allow_duplicates": false
|
||||
}
|
||||
},
|
||||
"if": "ctx?.event?.module == 'endpoint' && ctx?.host?.ip != null",
|
||||
"ignore_missing": true,
|
||||
"description": "Extract IPs from Elastic Agent events (host.ip) and adds them to related.ip"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pipeline": {
|
||||
"name": ".fleet_final_pipeline-1",
|
||||
"ignore_missing_pipeline": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"remove": {
|
||||
"field": "event.agent_id_status",
|
||||
"ignore_missing": true,
|
||||
"if": "ctx?.event?.agent_id_status == 'auth_metadata_missing'"
|
||||
}
|
||||
},
|
||||
{
|
||||
"remove": {
|
||||
"field": [
|
||||
"message2",
|
||||
"type",
|
||||
"fields",
|
||||
"category",
|
||||
"module",
|
||||
"dataset",
|
||||
"event.dataset_temp",
|
||||
"dataset_tag_temp",
|
||||
"module_temp",
|
||||
"datastream_dataset_temp"
|
||||
],
|
||||
"ignore_missing": true,
|
||||
"ignore_failure": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -27,6 +27,13 @@ elasticsearch:
|
||||
readonly: True
|
||||
global: True
|
||||
helpLink: elasticsearch.html
|
||||
logsdb:
|
||||
enabled:
|
||||
description: Enables or disables the Elasticsearch logsdb index mode. When enabled, most logs-* datastreams will convert to logsdb from standard after rolling over.
|
||||
forcedType: bool
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: elasticsearch.html
|
||||
routing:
|
||||
allocation:
|
||||
disk:
|
||||
@@ -131,6 +138,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 +192,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 +212,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 +387,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 +455,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 +517,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.
|
||||
|
||||
66
salt/elasticsearch/ssl.sls
Normal file
66
salt/elasticsearch/ssl.sls
Normal file
@@ -0,0 +1,66 @@
|
||||
# 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 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'ca/map.jinja' import CA %}
|
||||
|
||||
# Create a cert for elasticsearch
|
||||
elasticsearch_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/elasticsearch.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/elasticsearch.key') -%}
|
||||
- prereq:
|
||||
- x509: /etc/pki/elasticsearch.crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
elasticsearch_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/elasticsearch.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: registry
|
||||
- private_key: /etc/pki/elasticsearch.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
cmd.run:
|
||||
- name: "/usr/bin/openssl pkcs12 -inkey /etc/pki/elasticsearch.key -in /etc/pki/elasticsearch.crt -export -out /etc/pki/elasticsearch.p12 -nodes -passout pass:"
|
||||
- onchanges:
|
||||
- x509: /etc/pki/elasticsearch.key
|
||||
|
||||
elastickeyperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticsearch.key
|
||||
- mode: 640
|
||||
- group: 930
|
||||
|
||||
elasticp12perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticsearch.p12
|
||||
- mode: 640
|
||||
- group: 930
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
@@ -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 %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"template": {
|
||||
"settings": {
|
||||
"index": {
|
||||
"final_pipeline": ".fleet_final_pipeline-1"
|
||||
"final_pipeline": "global@custom"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
|
||||
@@ -14,8 +14,9 @@ set -e
|
||||
# Check to see if we have extracted the ca cert.
|
||||
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
|
||||
# Make sure symbolic links are followed when copying from container
|
||||
docker cp -L so-elasticsearchca:/usr/share/elasticsearch/jdk/lib/security/cacerts /opt/so/saltstack/local/salt/elasticsearch/cacerts
|
||||
docker cp -L 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
|
||||
|
||||
@@ -121,7 +121,7 @@ if [ ! -f $STATE_FILE_SUCCESS ]; then
|
||||
echo "Loading Security Onion index templates..."
|
||||
shopt -s extglob
|
||||
{% if GLOBALS.role == 'so-heavynode' %}
|
||||
pattern="!(*1password*|*aws*|*azure*|*cloudflare*|*elastic_agent*|*fim*|*github*|*google*|*osquery*|*system*|*windows*)"
|
||||
pattern="!(*1password*|*aws*|*azure*|*cloudflare*|*elastic_agent*|*fim*|*github*|*google*|*osquery*|*system*|*windows*|*endpoint*|*elasticsearch*|*generic*|*fleet_server*|*soc*)"
|
||||
{% else %}
|
||||
pattern="*"
|
||||
{% endif %}
|
||||
|
||||
@@ -32,7 +32,7 @@ global:
|
||||
readonly: True
|
||||
advanced: True
|
||||
url_base:
|
||||
description: Used for handling of authentication cookies.
|
||||
description: The base URL for the Security Onion Console. Must be accessible by all nodes in the grid, as well as all analysts. Also used for handling of authentication cookies. Can be an IP address or a hostname/FQDN. Do not include protocol (http/https) or port number.
|
||||
global: True
|
||||
airgap:
|
||||
description: Airgapped systems do not have network connectivity to the internet. This setting represents how this grid was configured during initial setup. While it is technically possible to manually switch systems between airgap and non-airgap, there are some nuances and additional steps involved. For that reason this setting is marked read-only. Contact your support representative for guidance if there is a need to change this setting.
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
include:
|
||||
- salt.minion
|
||||
- ssl
|
||||
|
||||
# Influx DB
|
||||
influxconfdir:
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
{% set TOKEN = salt['pillar.get']('influxdb:token') %}
|
||||
|
||||
include:
|
||||
- influxdb.ssl
|
||||
- influxdb.config
|
||||
- influxdb.sostatus
|
||||
|
||||
@@ -59,6 +60,8 @@ so-influxdb:
|
||||
{% endif %}
|
||||
- watch:
|
||||
- file: influxdbconf
|
||||
- x509: influxdb_key
|
||||
- x509: influxdb_crt
|
||||
- require:
|
||||
- file: influxdbconf
|
||||
- x509: influxdb_key
|
||||
|
||||
55
salt/influxdb/ssl.sls
Normal file
55
salt/influxdb/ssl.sls
Normal file
@@ -0,0 +1,55 @@
|
||||
# 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 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'ca/map.jinja' import CA %}
|
||||
|
||||
influxdb_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/influxdb.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/influxdb.key') -%}
|
||||
- prereq:
|
||||
- x509: /etc/pki/influxdb.crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
# Create a cert for the talking to influxdb
|
||||
influxdb_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/influxdb.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: influxdb
|
||||
- private_key: /etc/pki/influxdb.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
influxkeyperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/influxdb.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
@@ -68,6 +68,8 @@ so-kafka:
|
||||
- file: kafka_server_jaas_properties
|
||||
{% endif %}
|
||||
- file: kafkacertz
|
||||
- x509: kafka_crt
|
||||
- file: kafka_pkcs12_perms
|
||||
- require:
|
||||
- file: kafkacertz
|
||||
|
||||
@@ -95,4 +97,4 @@ include:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -6,22 +6,13 @@
|
||||
{% from 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states or sls in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'ca/map.jinja' import CA %}
|
||||
{% set kafka_password = salt['pillar.get']('kafka:config:password') %}
|
||||
|
||||
include:
|
||||
- ca.dirs
|
||||
{% set global_ca_server = [] %}
|
||||
{% set x509dict = salt['mine.get'](GLOBALS.manager | lower~'*', 'x509.get_pem_entries') %}
|
||||
{% for host in x509dict %}
|
||||
{% if 'manager' in host.split('_')|last or host.split('_')|last == 'standalone' %}
|
||||
{% do global_ca_server.append(host) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% set ca_server = global_ca_server[0] %}
|
||||
- ca
|
||||
|
||||
{% if GLOBALS.pipeline == "KAFKA" %}
|
||||
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone'] %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone'] %}
|
||||
kafka_client_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/kafka-client.key
|
||||
@@ -39,12 +30,12 @@ kafka_client_key:
|
||||
kafka_client_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/kafka-client.crt
|
||||
- ca_server: {{ ca_server }}
|
||||
- ca_server: {{ CA.server }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- signing_policy: kafka
|
||||
- private_key: /etc/pki/kafka-client.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- days_remaining: 0
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
@@ -67,9 +58,9 @@ kafka_client_crt_perms:
|
||||
- mode: 640
|
||||
- user: 960
|
||||
- group: 939
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch','so-receiver', 'so-standalone'] %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch','so-receiver', 'so-standalone'] %}
|
||||
kafka_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/kafka.key
|
||||
@@ -87,12 +78,12 @@ kafka_key:
|
||||
kafka_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/kafka.crt
|
||||
- ca_server: {{ ca_server }}
|
||||
- ca_server: {{ CA.server }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- signing_policy: kafka
|
||||
- private_key: /etc/pki/kafka.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- days_remaining: 0
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
@@ -103,6 +94,7 @@ kafka_crt:
|
||||
- name: "/usr/bin/openssl pkcs12 -inkey /etc/pki/kafka.key -in /etc/pki/kafka.crt -export -out /etc/pki/kafka.p12 -nodes -passout pass:{{ kafka_password }}"
|
||||
- onchanges:
|
||||
- x509: /etc/pki/kafka.key
|
||||
|
||||
kafka_key_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
@@ -126,11 +118,11 @@ kafka_pkcs12_perms:
|
||||
- mode: 640
|
||||
- user: 960
|
||||
- group: 939
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
# Standalone needs kafka-logstash for automated testing. Searchnode/manager search need it for logstash to consume from Kafka.
|
||||
# Manager will have cert, but be unused until a pipeline is created and logstash enabled.
|
||||
{% if GLOBALS.role in ['so-standalone', 'so-managersearch', 'so-searchnode', 'so-manager'] %}
|
||||
{% if GLOBALS.role in ['so-standalone', 'so-managersearch', 'so-searchnode', 'so-manager'] %}
|
||||
kafka_logstash_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/kafka-logstash.key
|
||||
@@ -148,12 +140,12 @@ kafka_logstash_key:
|
||||
kafka_logstash_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/kafka-logstash.crt
|
||||
- ca_server: {{ ca_server }}
|
||||
- ca_server: {{ CA.server }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- signing_policy: kafka
|
||||
- private_key: /etc/pki/kafka-logstash.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- days_remaining: 0
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
@@ -189,7 +181,6 @@ kafka_logstash_pkcs12_perms:
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
@@ -198,4 +189,4 @@ kafka_logstash_pkcs12_perms:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -25,11 +25,10 @@ kibana:
|
||||
discardCorruptObjects: "8.18.8"
|
||||
telemetry:
|
||||
enabled: False
|
||||
security:
|
||||
showInsecureClusterWarning: False
|
||||
xpack:
|
||||
security:
|
||||
secureCookies: true
|
||||
showInsecureClusterWarning: false
|
||||
reporting:
|
||||
kibanaServer:
|
||||
hostname: localhost
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
{% from 'logstash/map.jinja' import LOGSTASH_MERGED %}
|
||||
{% set ASSIGNED_PIPELINES = LOGSTASH_MERGED.assigned_pipelines.roles[GLOBALS.role.split('-')[1]] %}
|
||||
|
||||
{% if GLOBALS.role not in ['so-receiver','so-fleet'] %}
|
||||
include:
|
||||
- ssl
|
||||
{% if GLOBALS.role not in ['so-receiver','so-fleet'] %}
|
||||
- elasticsearch
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
# Create the logstash group
|
||||
logstashgroup:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
{% set lsheap = LOGSTASH_MERGED.settings.lsheap %}
|
||||
|
||||
include:
|
||||
- ca
|
||||
{% if GLOBALS.role not in ['so-receiver','so-fleet'] %}
|
||||
- elasticsearch.ca
|
||||
{% endif %}
|
||||
@@ -20,9 +21,9 @@ include:
|
||||
- kafka.ca
|
||||
- kafka.ssl
|
||||
{% endif %}
|
||||
- logstash.ssl
|
||||
- logstash.config
|
||||
- logstash.sostatus
|
||||
- ssl
|
||||
|
||||
so-logstash:
|
||||
docker_container.running:
|
||||
@@ -65,22 +66,18 @@ so-logstash:
|
||||
- /opt/so/log/logstash:/var/log/logstash:rw
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
- /opt/so/conf/logstash/etc/certs:/usr/share/logstash/certs:ro
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone', 'so-import', 'so-heavynode', 'so-receiver'] %}
|
||||
- /etc/pki/filebeat.crt:/usr/share/logstash/filebeat.crt:ro
|
||||
- /etc/pki/filebeat.p8:/usr/share/logstash/filebeat.key:ro
|
||||
{% endif %}
|
||||
- /etc/pki/tls/certs/intca.crt:/usr/share/filebeat/ca.crt:ro
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-fleet', 'so-heavynode', 'so-receiver'] %}
|
||||
- /etc/pki/elasticfleet-logstash.crt:/usr/share/logstash/elasticfleet-logstash.crt:ro
|
||||
- /etc/pki/elasticfleet-logstash.key:/usr/share/logstash/elasticfleet-logstash.key:ro
|
||||
- /etc/pki/elasticfleet-lumberjack.crt:/usr/share/logstash/elasticfleet-lumberjack.crt:ro
|
||||
- /etc/pki/elasticfleet-lumberjack.key:/usr/share/logstash/elasticfleet-lumberjack.key:ro
|
||||
{% if GLOBALS.role != 'so-fleet' %}
|
||||
- /etc/pki/filebeat.crt:/usr/share/logstash/filebeat.crt:ro
|
||||
- /etc/pki/filebeat.p8:/usr/share/logstash/filebeat.key:ro
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone', 'so-import'] %}
|
||||
- /etc/pki/ca.crt:/usr/share/filebeat/ca.crt:ro
|
||||
{% else %}
|
||||
- /etc/pki/tls/certs/intca.crt:/usr/share/filebeat/ca.crt:ro
|
||||
{% endif %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone', 'so-import', 'so-heavynode', 'so-searchnode' ] %}
|
||||
{% if GLOBALS.role not in ['so-receiver','so-fleet'] %}
|
||||
- /opt/so/conf/ca/cacerts:/etc/pki/ca-trust/extracted/java/cacerts:ro
|
||||
- /opt/so/conf/ca/tls-ca-bundle.pem:/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:ro
|
||||
{% endif %}
|
||||
@@ -100,11 +97,22 @@ so-logstash:
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
- watch:
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-fleet', 'so-receiver'] %}
|
||||
- x509: etc_elasticfleet_logstash_key
|
||||
- x509: etc_elasticfleet_logstash_crt
|
||||
{% endif %}
|
||||
- file: lsetcsync
|
||||
- file: trusttheca
|
||||
{% if GLOBALS.is_manager %}
|
||||
- file: elasticsearch_cacerts
|
||||
- file: elasticsearch_capems
|
||||
{% endif %}
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-fleet', 'so-heavynode', 'so-receiver'] %}
|
||||
- x509: etc_elasticfleet_logstash_crt
|
||||
- x509: etc_elasticfleet_logstash_key
|
||||
- x509: etc_elasticfleetlumberjack_crt
|
||||
- x509: etc_elasticfleetlumberjack_key
|
||||
{% if GLOBALS.role != 'so-fleet' %}
|
||||
- x509: etc_filebeat_crt
|
||||
- file: logstash_filebeat_p8
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% for assigned_pipeline in LOGSTASH_MERGED.assigned_pipelines.roles[GLOBALS.role.split('-')[1]] %}
|
||||
- file: ls_pipeline_{{assigned_pipeline}}
|
||||
{% for CONFIGFILE in LOGSTASH_MERGED.defined_pipelines[assigned_pipeline] %}
|
||||
@@ -115,17 +123,20 @@ so-logstash:
|
||||
- file: kafkacertz
|
||||
{% endif %}
|
||||
- require:
|
||||
{% if grains['role'] in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone', 'so-import', 'so-heavynode', 'so-receiver'] %}
|
||||
- file: trusttheca
|
||||
{% if GLOBALS.is_manager %}
|
||||
- file: elasticsearch_cacerts
|
||||
- file: elasticsearch_capems
|
||||
{% endif %}
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-fleet', 'so-heavynode', 'so-receiver'] %}
|
||||
- x509: etc_elasticfleet_logstash_crt
|
||||
- x509: etc_elasticfleet_logstash_key
|
||||
- x509: etc_elasticfleetlumberjack_crt
|
||||
- x509: etc_elasticfleetlumberjack_key
|
||||
{% if GLOBALS.role != 'so-fleet' %}
|
||||
- x509: etc_filebeat_crt
|
||||
{% endif %}
|
||||
{% if grains['role'] in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone', 'so-import'] %}
|
||||
- x509: pki_public_ca_crt
|
||||
{% else %}
|
||||
- x509: trusttheca
|
||||
{% endif %}
|
||||
{% if grains.role in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone', 'so-import'] %}
|
||||
- file: cacertz
|
||||
- file: capemz
|
||||
- file: logstash_filebeat_p8
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if GLOBALS.pipeline == 'KAFKA' and GLOBALS.role in ['so-manager', 'so-managerhype', 'so-managersearch', 'so-standalone', 'so-searchnode'] %}
|
||||
- file: kafkacertz
|
||||
|
||||
@@ -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
|
||||
|
||||
287
salt/logstash/ssl.sls
Normal file
287
salt/logstash/ssl.sls
Normal file
@@ -0,0 +1,287 @@
|
||||
# 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 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls in allowed_states or sls.split('.')[0] in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
|
||||
{% from 'ca/map.jinja' import CA %}
|
||||
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-heavynode', 'so-fleet', 'so-receiver'] %}
|
||||
|
||||
{% if grains['role'] not in [ 'so-heavynode'] %}
|
||||
# Start -- Elastic Fleet Logstash Input Cert
|
||||
etc_elasticfleet_logstash_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/elasticfleet-logstash.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/elasticfleet-logstash.key') -%}
|
||||
- prereq:
|
||||
- x509: etc_elasticfleet_logstash_crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
etc_elasticfleet_logstash_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/elasticfleet-logstash.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: elasticfleet
|
||||
- private_key: /etc/pki/elasticfleet-logstash.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }},DNS:{{ GLOBALS.url_base }},IP:{{ GLOBALS.node_ip }}{% if ELASTICFLEETMERGED.config.server.custom_fqdn | length > 0 %},DNS:{{ ELASTICFLEETMERGED.config.server.custom_fqdn | join(',DNS:') }}{% endif %}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
cmd.run:
|
||||
- name: "/usr/bin/openssl pkcs8 -in /etc/pki/elasticfleet-logstash.key -topk8 -out /etc/pki/elasticfleet-logstash.p8 -nocrypt"
|
||||
- onchanges:
|
||||
- x509: etc_elasticfleet_logstash_key
|
||||
|
||||
eflogstashperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-logstash.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
chownelasticfleetlogstashcrt:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-logstash.crt
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
chownelasticfleetlogstashkey:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-logstash.key
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
# End -- Elastic Fleet Logstash Input Cert
|
||||
{% endif %} # endif is for not including HeavyNodes
|
||||
|
||||
# Start -- Elastic Fleet Node - Logstash Lumberjack Input / Output
|
||||
# Cert needed on: Managers, Receivers
|
||||
etc_elasticfleetlumberjack_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/elasticfleet-lumberjack.key
|
||||
- bits: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/elasticfleet-lumberjack.key') -%}
|
||||
- prereq:
|
||||
- x509: etc_elasticfleetlumberjack_crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
etc_elasticfleetlumberjack_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/elasticfleet-lumberjack.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: elasticfleet
|
||||
- private_key: /etc/pki/elasticfleet-lumberjack.key
|
||||
- CN: {{ GLOBALS.node_ip }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
cmd.run:
|
||||
- name: "/usr/bin/openssl pkcs8 -in /etc/pki/elasticfleet-lumberjack.key -topk8 -out /etc/pki/elasticfleet-lumberjack.p8 -nocrypt"
|
||||
- onchanges:
|
||||
- x509: etc_elasticfleetlumberjack_key
|
||||
|
||||
eflogstashlumberjackperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-lumberjack.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
chownilogstashelasticfleetlumberjackp8:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-lumberjack.p8
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
chownilogstashelasticfleetlogstashlumberjackcrt:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-lumberjack.crt
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
chownilogstashelasticfleetlogstashlumberjackkey:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/elasticfleet-lumberjack.key
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
# End -- Elastic Fleet Node - Logstash Lumberjack Input / Output
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-heavynode', 'so-receiver'] %}
|
||||
etc_filebeat_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/filebeat.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/filebeat.key') -%}
|
||||
- prereq:
|
||||
- x509: etc_filebeat_crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
# Request a cert and drop it where it needs to go to be distributed
|
||||
etc_filebeat_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/filebeat.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: filebeat
|
||||
- private_key: /etc/pki/filebeat.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
cmd.run:
|
||||
- name: "/usr/bin/openssl pkcs8 -in /etc/pki/filebeat.key -topk8 -out /etc/pki/filebeat.p8 -nocrypt"
|
||||
- onchanges:
|
||||
- x509: etc_filebeat_key
|
||||
|
||||
fbperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/filebeat.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
logstash_filebeat_p8:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/filebeat.p8
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
{% if grains.role not in ['so-heavynode', 'so-receiver'] %}
|
||||
# Create Symlinks to the keys so I can distribute it to all the things
|
||||
filebeatdir:
|
||||
file.directory:
|
||||
- name: /opt/so/saltstack/local/salt/filebeat/files
|
||||
- makedirs: True
|
||||
|
||||
fbkeylink:
|
||||
file.symlink:
|
||||
- name: /opt/so/saltstack/local/salt/filebeat/files/filebeat.p8
|
||||
- target: /etc/pki/filebeat.p8
|
||||
- user: socore
|
||||
- group: socore
|
||||
|
||||
fbcrtlink:
|
||||
file.symlink:
|
||||
- name: /opt/so/saltstack/local/salt/filebeat/files/filebeat.crt
|
||||
- target: /etc/pki/filebeat.crt
|
||||
- user: socore
|
||||
- group: socore
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-sensor', 'so-searchnode', 'so-heavynode', 'so-fleet', 'so-idh', 'so-receiver'] %}
|
||||
|
||||
fbcertdir:
|
||||
file.directory:
|
||||
- name: /opt/so/conf/filebeat/etc/pki
|
||||
- makedirs: True
|
||||
|
||||
conf_filebeat_key:
|
||||
x509.private_key_managed:
|
||||
- name: /opt/so/conf/filebeat/etc/pki/filebeat.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/opt/so/conf/filebeat/etc/pki/filebeat.key') -%}
|
||||
- prereq:
|
||||
- x509: conf_filebeat_crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
# Request a cert and drop it where it needs to go to be distributed
|
||||
conf_filebeat_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /opt/so/conf/filebeat/etc/pki/filebeat.crt
|
||||
- ca_server: {{ CA.server }}
|
||||
- signing_policy: filebeat
|
||||
- private_key: /opt/so/conf/filebeat/etc/pki/filebeat.key
|
||||
- CN: {{ GLOBALS.hostname }}
|
||||
- subjectAltName: DNS:{{ GLOBALS.hostname }}, IP:{{ GLOBALS.node_ip }}
|
||||
- days_remaining: 7
|
||||
- days_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
# Convert the key to pkcs#8 so logstash will work correctly.
|
||||
filebeatpkcs:
|
||||
cmd.run:
|
||||
- name: "/usr/bin/openssl pkcs8 -in /opt/so/conf/filebeat/etc/pki/filebeat.key -topk8 -out /opt/so/conf/filebeat/etc/pki/filebeat.p8 -passout pass:"
|
||||
- onchanges:
|
||||
- x509: conf_filebeat_key
|
||||
|
||||
filebeatkeyperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /opt/so/conf/filebeat/etc/pki/filebeat.key
|
||||
- mode: 640
|
||||
- group: 939
|
||||
|
||||
chownfilebeatp8:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /opt/so/conf/filebeat/etc/pki/filebeat.p8
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
@@ -1,3 +1,8 @@
|
||||
# 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.
|
||||
|
||||
elastic_curl_config_distributed:
|
||||
file.managed:
|
||||
- name: /opt/so/saltstack/local/salt/elasticsearch/curl.config
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 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.
|
||||
|
||||
kibana_curl_config_distributed:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/kibana/curl.config
|
||||
@@ -5,4 +10,4 @@ kibana_curl_config_distributed:
|
||||
- template: jinja
|
||||
- mode: 600
|
||||
- show_changes: False
|
||||
- makedirs: True
|
||||
- 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 }}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 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.
|
||||
|
||||
include:
|
||||
- elasticsearch.auth
|
||||
- kratos
|
||||
|
||||
@@ -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
|
||||
@@ -716,6 +716,18 @@ function checkMine() {
|
||||
}
|
||||
}
|
||||
|
||||
function create_ca_pillar() {
|
||||
local capillar=/opt/so/saltstack/local/pillar/ca/init.sls
|
||||
printf '%s\n'\
|
||||
"ca:"\
|
||||
" server: $MINION_ID"\
|
||||
" " > $capillar
|
||||
if [ $? -ne 0 ]; then
|
||||
log "ERROR" "Failed to add $MINION_ID to $capillar"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function createEVAL() {
|
||||
log "INFO" "Creating EVAL configuration for minion $MINION_ID"
|
||||
is_pcaplimit=true
|
||||
@@ -827,7 +839,6 @@ function createHEAVYNODE() {
|
||||
add_elastic_agent_to_minion || return 1
|
||||
add_sensor_to_minion || return 1
|
||||
add_strelka_to_minion || return 1
|
||||
add_redis_to_minion || return 1
|
||||
add_telegraf_to_minion || return 1
|
||||
}
|
||||
|
||||
@@ -1013,6 +1024,7 @@ function setupMinionFiles() {
|
||||
managers=("EVAL" "STANDALONE" "IMPORT" "MANAGER" "MANAGERSEARCH")
|
||||
if echo "${managers[@]}" | grep -qw "$NODETYPE"; then
|
||||
add_sensoroni_with_analyze_to_minion || return 1
|
||||
create_ca_pillar || return 1
|
||||
else
|
||||
add_sensoroni_to_minion || return 1
|
||||
fi
|
||||
|
||||
@@ -9,6 +9,7 @@ import os
|
||||
import sys
|
||||
import time
|
||||
import yaml
|
||||
import json
|
||||
|
||||
lockFile = "/tmp/so-yaml.lock"
|
||||
|
||||
@@ -16,18 +17,24 @@ lockFile = "/tmp/so-yaml.lock"
|
||||
def showUsage(args):
|
||||
print('Usage: {} <COMMAND> <YAML_FILE> [ARGS...]'.format(sys.argv[0]), file=sys.stderr)
|
||||
print(' General commands:', file=sys.stderr)
|
||||
print(' append - Append a list item to a yaml key, if it exists and is a list. Requires KEY and LISTITEM args.', file=sys.stderr)
|
||||
print(' add - Add a new key and set its value. Fails if key already exists. Requires KEY and VALUE args.', file=sys.stderr)
|
||||
print(' get - Displays (to stdout) the value stored in the given key. Requires KEY arg.', file=sys.stderr)
|
||||
print(' remove - Removes a yaml key, if it exists. Requires KEY arg.', file=sys.stderr)
|
||||
print(' replace - Replaces (or adds) a new key and set its value. Requires KEY and VALUE args.', file=sys.stderr)
|
||||
print(' help - Prints this usage information.', file=sys.stderr)
|
||||
print(' append - Append a list item to a yaml key, if it exists and is a list. Requires KEY and LISTITEM args.', file=sys.stderr)
|
||||
print(' appendlistobject - Append an object to a yaml list key. Requires KEY and JSON_OBJECT args.', file=sys.stderr)
|
||||
print(' removelistitem - Remove a list item from a yaml key, if it exists and is a list. Requires KEY and LISTITEM args.', file=sys.stderr)
|
||||
print(' replacelistobject - Replace a list object based on a condition. Requires KEY, CONDITION_FIELD, CONDITION_VALUE, and JSON_OBJECT args.', file=sys.stderr)
|
||||
print(' add - Add a new key and set its value. Fails if key already exists. Requires KEY and VALUE args.', file=sys.stderr)
|
||||
print(' get - Displays (to stdout) the value stored in the given key. Requires KEY arg.', file=sys.stderr)
|
||||
print(' remove - Removes a yaml key, if it exists. Requires KEY arg.', file=sys.stderr)
|
||||
print(' replace - Replaces (or adds) a new key and set its value. Requires KEY and VALUE args.', file=sys.stderr)
|
||||
print(' help - Prints this usage information.', file=sys.stderr)
|
||||
print('', file=sys.stderr)
|
||||
print(' Where:', file=sys.stderr)
|
||||
print(' YAML_FILE - Path to the file that will be modified. Ex: /opt/so/conf/service/conf.yaml', file=sys.stderr)
|
||||
print(' KEY - YAML key, does not support \' or " characters at this time. Ex: level1.level2', file=sys.stderr)
|
||||
print(' VALUE - Value to set for a given key. Can be a literal value or file:<path> to load from a YAML file.', file=sys.stderr)
|
||||
print(' LISTITEM - Item to append to a given key\'s list value. Can be a literal value or file:<path> to load from a YAML file.', file=sys.stderr)
|
||||
print(' YAML_FILE - Path to the file that will be modified. Ex: /opt/so/conf/service/conf.yaml', file=sys.stderr)
|
||||
print(' KEY - YAML key, does not support \' or " characters at this time. Ex: level1.level2', file=sys.stderr)
|
||||
print(' VALUE - Value to set for a given key. Can be a literal value or file:<path> to load from a YAML file.', file=sys.stderr)
|
||||
print(' LISTITEM - Item to append to a given key\'s list value. Can be a literal value or file:<path> to load from a YAML file.', file=sys.stderr)
|
||||
print(' JSON_OBJECT - JSON string representing an object to append to a list.', file=sys.stderr)
|
||||
print(' CONDITION_FIELD - Field name to match in list items (e.g., "name").', file=sys.stderr)
|
||||
print(' CONDITION_VALUE - Value to match for the condition field.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -57,6 +64,24 @@ def appendItem(content, key, listItem):
|
||||
return 1
|
||||
|
||||
|
||||
def removeListItem(content, key, listItem):
|
||||
pieces = key.split(".", 1)
|
||||
if len(pieces) > 1:
|
||||
removeListItem(content[pieces[0]], pieces[1], listItem)
|
||||
else:
|
||||
try:
|
||||
if not isinstance(content[key], list):
|
||||
raise AttributeError("Value is not a list")
|
||||
if listItem in content[key]:
|
||||
content[key].remove(listItem)
|
||||
except (AttributeError, TypeError):
|
||||
print("The existing value for the given key is not a list. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
except KeyError:
|
||||
print("The key provided does not exist. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def convertType(value):
|
||||
if isinstance(value, str) and value.startswith("file:"):
|
||||
path = value[5:] # Remove "file:" prefix
|
||||
@@ -103,6 +128,131 @@ def append(args):
|
||||
return 0
|
||||
|
||||
|
||||
def appendListObjectItem(content, key, listObject):
|
||||
pieces = key.split(".", 1)
|
||||
if len(pieces) > 1:
|
||||
appendListObjectItem(content[pieces[0]], pieces[1], listObject)
|
||||
else:
|
||||
try:
|
||||
if not isinstance(content[key], list):
|
||||
raise AttributeError("Value is not a list")
|
||||
content[key].append(listObject)
|
||||
except AttributeError:
|
||||
print("The existing value for the given key is not a list. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
except KeyError:
|
||||
print("The key provided does not exist. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def appendlistobject(args):
|
||||
if len(args) != 3:
|
||||
print('Missing filename, key arg, or JSON object to append', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
jsonString = args[2]
|
||||
|
||||
try:
|
||||
# Parse the JSON string into a Python dictionary
|
||||
listObject = json.loads(jsonString)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f'Invalid JSON string: {e}', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Verify that the parsed content is a dictionary (object)
|
||||
if not isinstance(listObject, dict):
|
||||
print('The JSON string must represent an object (dictionary), not an array or primitive value.', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
content = loadYaml(filename)
|
||||
appendListObjectItem(content, key, listObject)
|
||||
writeYaml(filename, content)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def removelistitem(args):
|
||||
if len(args) != 3:
|
||||
print('Missing filename, key arg, or list item to remove', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
listItem = args[2]
|
||||
|
||||
content = loadYaml(filename)
|
||||
removeListItem(content, key, convertType(listItem))
|
||||
writeYaml(filename, content)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def replaceListObjectByCondition(content, key, conditionField, conditionValue, newObject):
|
||||
pieces = key.split(".", 1)
|
||||
if len(pieces) > 1:
|
||||
replaceListObjectByCondition(content[pieces[0]], pieces[1], conditionField, conditionValue, newObject)
|
||||
else:
|
||||
try:
|
||||
if not isinstance(content[key], list):
|
||||
raise AttributeError("Value is not a list")
|
||||
|
||||
# Find and replace the item that matches the condition
|
||||
found = False
|
||||
for i, item in enumerate(content[key]):
|
||||
if isinstance(item, dict) and item.get(conditionField) == conditionValue:
|
||||
content[key][i] = newObject
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
print(f"No list item found with {conditionField}={conditionValue}. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
except AttributeError:
|
||||
print("The existing value for the given key is not a list. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
except KeyError:
|
||||
print("The key provided does not exist. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def replacelistobject(args):
|
||||
if len(args) != 5:
|
||||
print('Missing filename, key arg, condition field, condition value, or JSON object', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
conditionField = args[2]
|
||||
conditionValue = args[3]
|
||||
jsonString = args[4]
|
||||
|
||||
try:
|
||||
# Parse the JSON string into a Python dictionary
|
||||
newObject = json.loads(jsonString)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f'Invalid JSON string: {e}', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Verify that the parsed content is a dictionary (object)
|
||||
if not isinstance(newObject, dict):
|
||||
print('The JSON string must represent an object (dictionary), not an array or primitive value.', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
content = loadYaml(filename)
|
||||
result = replaceListObjectByCondition(content, key, conditionField, conditionValue, newObject)
|
||||
|
||||
if result != 1:
|
||||
writeYaml(filename, content)
|
||||
|
||||
return result if result is not None else 0
|
||||
|
||||
|
||||
def addKey(content, key, value):
|
||||
pieces = key.split(".", 1)
|
||||
if len(pieces) > 1:
|
||||
@@ -211,6 +361,9 @@ def main():
|
||||
"help": showUsage,
|
||||
"add": add,
|
||||
"append": append,
|
||||
"appendlistobject": appendlistobject,
|
||||
"removelistitem": removelistitem,
|
||||
"replacelistobject": replacelistobject,
|
||||
"get": get,
|
||||
"remove": remove,
|
||||
"replace": replace,
|
||||
|
||||
@@ -457,3 +457,463 @@ class TestRemove(unittest.TestCase):
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("Missing filename or key arg", mock_stderr.getvalue())
|
||||
sysmock.assert_called_once_with(1)
|
||||
|
||||
|
||||
class TestRemoveListItem(unittest.TestCase):
|
||||
|
||||
def test_removelistitem_missing_arg(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.removelistitem(["file", "key"])
|
||||
sysmock.assert_called()
|
||||
self.assertIn("Missing filename, key arg, or list item to remove", mock_stderr.getvalue())
|
||||
|
||||
def test_removelistitem(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: abc }, key2: false, key3: [a,b,c]}")
|
||||
file.close()
|
||||
|
||||
soyaml.removelistitem([filename, "key3", "b"])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n child1: 123\n child2: abc\nkey2: false\nkey3:\n- a\n- c\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_removelistitem_nested(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: [a,b,c] }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
soyaml.removelistitem([filename, "key1.child2", "b"])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n child1: 123\n child2:\n - a\n - c\nkey2: false\nkey3:\n- e\n- f\n- g\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_removelistitem_nested_deep(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
soyaml.removelistitem([filename, "key1.child2.deep2", "b"])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n child1: 123\n child2:\n deep1: 45\n deep2:\n - a\n - c\nkey2: false\nkey3:\n- e\n- f\n- g\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_removelistitem_item_not_in_list(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [a,b,c]}")
|
||||
file.close()
|
||||
|
||||
soyaml.removelistitem([filename, "key1", "d"])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n- a\n- b\n- c\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_removelistitem_key_noexist(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "removelistitem", filename, "key4", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_removelistitem_key_noexist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "removelistitem", filename, "key1.child2.deep3", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_removelistitem_key_nonlist(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "removelistitem", filename, "key1", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_removelistitem_key_nonlist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-removelistitem.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "removelistitem", filename, "key1.child2.deep1", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
|
||||
class TestAppendListObject(unittest.TestCase):
|
||||
|
||||
def test_appendlistobject_missing_arg(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.appendlistobject(["file", "key"])
|
||||
sysmock.assert_called()
|
||||
self.assertIn("Missing filename, key arg, or JSON object to append", mock_stderr.getvalue())
|
||||
|
||||
def test_appendlistobject(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123 }, key2: [{name: item1, value: 10}]}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"name": "item2", "value": 20}'
|
||||
soyaml.appendlistobject([filename, "key2", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n child1: 123\nkey2:\n- name: item1\n value: 10\n- name: item2\n value: 20\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_appendlistobject_nested(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: [{name: a, id: 1}], child2: abc }, key2: false}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"name": "b", "id": 2}'
|
||||
soyaml.appendlistobject([filename, "key1.child1", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
# YAML doesn't guarantee key order in dictionaries, so check for content
|
||||
self.assertIn("child1:", actual)
|
||||
self.assertIn("name: a", actual)
|
||||
self.assertIn("id: 1", actual)
|
||||
self.assertIn("name: b", actual)
|
||||
self.assertIn("id: 2", actual)
|
||||
self.assertIn("child2: abc", actual)
|
||||
self.assertIn("key2: false", actual)
|
||||
|
||||
def test_appendlistobject_nested_deep(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [{x: 1}] } }, key2: false}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"x": 2, "y": 3}'
|
||||
soyaml.appendlistobject([filename, "key1.child2.deep2", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n child1: 123\n child2:\n deep1: 45\n deep2:\n - x: 1\n - x: 2\n y: 3\nkey2: false\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_appendlistobject_invalid_json(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
result = soyaml.appendlistobject([filename, "key1", "{invalid json"])
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("Invalid JSON string:", mock_stderr.getvalue())
|
||||
|
||||
def test_appendlistobject_not_dict(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
# Try to append an array instead of an object
|
||||
result = soyaml.appendlistobject([filename, "key1", "[1, 2, 3]"])
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("The JSON string must represent an object (dictionary)", mock_stderr.getvalue())
|
||||
|
||||
def test_appendlistobject_not_dict_primitive(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
# Try to append a primitive value
|
||||
result = soyaml.appendlistobject([filename, "key1", "123"])
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("The JSON string must represent an object (dictionary)", mock_stderr.getvalue())
|
||||
|
||||
def test_appendlistobject_key_noexist(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "appendlistobject", filename, "key2", '{"name": "item2"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_appendlistobject_key_noexist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: [{name: a}] }}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "appendlistobject", filename, "key1.child2", '{"name": "b"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_appendlistobject_key_nonlist(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123 }}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "appendlistobject", filename, "key1", '{"name": "item"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_appendlistobject_key_nonlist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-appendlistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45 } }}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "appendlistobject", filename, "key1.child2.deep1", '{"name": "item"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
|
||||
class TestReplaceListObject(unittest.TestCase):
|
||||
|
||||
def test_replacelistobject_missing_arg(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.replacelistobject(["file", "key", "field"])
|
||||
sysmock.assert_called()
|
||||
self.assertIn("Missing filename, key arg, condition field, condition value, or JSON object", mock_stderr.getvalue())
|
||||
|
||||
def test_replacelistobject(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1, value: 10}, {name: item2, value: 20}]}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"name": "item2", "value": 25, "extra": "field"}'
|
||||
soyaml.replacelistobject([filename, "key1", "name", "item2", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n- name: item1\n value: 10\n- extra: field\n name: item2\n value: 25\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_replacelistobject_nested(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: [{id: '1', status: active}, {id: '2', status: inactive}] }}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"id": "2", "status": "active", "updated": true}'
|
||||
soyaml.replacelistobject([filename, "key1.child1", "id", "2", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n child1:\n - id: '1'\n status: active\n - id: '2'\n status: active\n updated: true\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_replacelistobject_nested_deep(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [{name: a, val: 1}, {name: b, val: 2}] } }}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"name": "b", "val": 99}'
|
||||
soyaml.replacelistobject([filename, "key1.child2.deep2", "name", "b", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n child1: 123\n child2:\n deep1: 45\n deep2:\n - name: a\n val: 1\n - name: b\n val: 99\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_replacelistobject_invalid_json(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
result = soyaml.replacelistobject([filename, "key1", "name", "item1", "{invalid json"])
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("Invalid JSON string:", mock_stderr.getvalue())
|
||||
|
||||
def test_replacelistobject_not_dict(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
result = soyaml.replacelistobject([filename, "key1", "name", "item1", "[1, 2, 3]"])
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("The JSON string must represent an object (dictionary)", mock_stderr.getvalue())
|
||||
|
||||
def test_replacelistobject_condition_not_found(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1, value: 10}, {name: item2, value: 20}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
json_obj = '{"name": "item3", "value": 30}'
|
||||
result = soyaml.replacelistobject([filename, "key1", "name", "item3", json_obj])
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("No list item found with name=item3", mock_stderr.getvalue())
|
||||
|
||||
# Verify file was not modified
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
self.assertIn("item1", actual)
|
||||
self.assertIn("item2", actual)
|
||||
self.assertNotIn("item3", actual)
|
||||
|
||||
def test_replacelistobject_key_noexist(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1}]}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "replacelistobject", filename, "key2", "name", "item1", '{"name": "item2"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_replacelistobject_key_noexist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: [{name: a}] }}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "replacelistobject", filename, "key1.child2", "name", "a", '{"name": "b"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_replacelistobject_key_nonlist(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123 }}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "replacelistobject", filename, "key1", "name", "item", '{"name": "item"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_replacelistobject_key_nonlist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45 } }}")
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "replacelistobject", filename, "key1.child2.deep1", "name", "item", '{"name": "item"}']
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_replacelistobject_string_condition_value(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{name: item1, value: 10}, {name: item2, value: 20}]}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"name": "item1", "value": 15}'
|
||||
soyaml.replacelistobject([filename, "key1", "name", "item1", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n- name: item1\n value: 15\n- name: item2\n value: 20\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_replacelistobject_numeric_condition_value(self):
|
||||
filename = "/tmp/so-yaml_test-replacelistobject.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: [{id: '1', status: active}, {id: '2', status: inactive}]}")
|
||||
file.close()
|
||||
|
||||
json_obj = '{"id": "1", "status": "updated"}'
|
||||
soyaml.replacelistobject([filename, "key1", "id", "1", json_obj])
|
||||
|
||||
file = open(filename, "r")
|
||||
actual = file.read()
|
||||
file.close()
|
||||
|
||||
expected = "key1:\n- id: '1'\n status: updated\n- id: '2'\n status: inactive\n"
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user