mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2026-03-24 13:32:37 +01:00
Compare commits
414 Commits
2.4.70-202
...
2.4.90-202
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
945d04a510 | ||
|
|
658db27a46 | ||
|
|
3e248da14d | ||
|
|
ed7f8dbf1d | ||
|
|
d6af3aab6d | ||
|
|
0cb067f6f2 | ||
|
|
ccf88fa62b | ||
|
|
20f915f649 | ||
|
|
f447b6b698 | ||
|
|
66b087f12f | ||
|
|
224c668c31 | ||
|
|
2e17e93cfe | ||
|
|
7dfb75ba6b | ||
|
|
d97400e6f5 | ||
|
|
cf1335dd84 | ||
|
|
be74449fb9 | ||
|
|
45b2413175 | ||
|
|
022df966c7 | ||
|
|
92385d652e | ||
|
|
4478d7b55a | ||
|
|
18df491f7e | ||
|
|
cee6ee7a2a | ||
|
|
6d18177f98 | ||
|
|
72ad49ed12 | ||
|
|
d11f4ef9ba | ||
|
|
03ca7977a0 | ||
|
|
91b2e7d400 | ||
|
|
34c3a58efe | ||
|
|
a867557f54 | ||
|
|
b814f32e0a | ||
|
|
2df44721d0 | ||
|
|
d0565baaa3 | ||
|
|
1b623c5c7a | ||
|
|
542a116b8c | ||
|
|
e7b6496f98 | ||
|
|
3991c7b5fe | ||
|
|
bf07d56da6 | ||
|
|
cdbffa2323 | ||
|
|
55469ebd24 | ||
|
|
4e81860a13 | ||
|
|
a23789287e | ||
|
|
fe1824aedd | ||
|
|
e58b2c45dd | ||
|
|
5d322ebc0b | ||
|
|
7ea8d5efd0 | ||
|
|
4182ff66a0 | ||
|
|
ff29d9ca51 | ||
|
|
4a88dedcb8 | ||
|
|
cfe5c1d76a | ||
|
|
ebf5159c95 | ||
|
|
d432019ad9 | ||
|
|
0d8fd42be3 | ||
|
|
d5faf535c3 | ||
|
|
8e1edd1d91 | ||
|
|
d791b23838 | ||
|
|
0db0754ee5 | ||
|
|
1f5a990b1e | ||
|
|
7a2f01be53 | ||
|
|
dadb0db8f3 | ||
|
|
dfd8ac3626 | ||
|
|
9716e09b83 | ||
|
|
669f68ad88 | ||
|
|
32af2d8436 | ||
|
|
24e945eee4 | ||
|
|
8615e5d5ea | ||
|
|
2dd5ff4333 | ||
|
|
6a396ec1aa | ||
|
|
34f558c023 | ||
|
|
9504f0885a | ||
|
|
ef59678441 | ||
|
|
c6f6811f47 | ||
|
|
ce8f9fe024 | ||
|
|
40b7999786 | ||
|
|
69be03f86a | ||
|
|
8dc8092241 | ||
|
|
578c6c567f | ||
|
|
662df1208d | ||
|
|
745b6775f1 | ||
|
|
176aaa8f3d | ||
|
|
4d499be1a8 | ||
|
|
c27225d91f | ||
|
|
1b47d5c622 | ||
|
|
32d7927a49 | ||
|
|
861630681c | ||
|
|
9d725f2b0b | ||
|
|
132263ac1a | ||
|
|
92a847e3bd | ||
|
|
75bbc41d38 | ||
|
|
7716f4aff8 | ||
|
|
8eb6dcc5b7 | ||
|
|
847638442b | ||
|
|
5743189eef | ||
|
|
81d874c6ae | ||
|
|
bfe8a3a01b | ||
|
|
71ed9204ff | ||
|
|
222ebbdec1 | ||
|
|
260d4e44bc | ||
|
|
0c5b3f7c1c | ||
|
|
feee80cad9 | ||
|
|
5f69456e22 | ||
|
|
e59d124c82 | ||
|
|
13d4738e8f | ||
|
|
abdfbba32a | ||
|
|
7d0a961482 | ||
|
|
0f226cc08e | ||
|
|
cfcfc6819f | ||
|
|
fe4e2a9540 | ||
|
|
492554d951 | ||
|
|
dfd5e95c93 | ||
|
|
50f0c43212 | ||
|
|
7fe8715bce | ||
|
|
f837ea944a | ||
|
|
c2d43e5d22 | ||
|
|
51bb4837f5 | ||
|
|
caec424e44 | ||
|
|
156176c628 | ||
|
|
81b4c4e2c0 | ||
|
|
d4107dc60a | ||
|
|
d34605a512 | ||
|
|
af5e7cd72c | ||
|
|
93378e92e6 | ||
|
|
81ce762250 | ||
|
|
cb727bf48d | ||
|
|
9a0bad88cc | ||
|
|
680e84851b | ||
|
|
ea771ed21b | ||
|
|
c332cd777c | ||
|
|
9fce85c988 | ||
|
|
6141c7a849 | ||
|
|
bf91030204 | ||
|
|
9577c3f59d | ||
|
|
77dedc575e | ||
|
|
0295b8d658 | ||
|
|
6a9d78fa7c | ||
|
|
b84521cdd2 | ||
|
|
ff4679ec08 | ||
|
|
c5ce7102e8 | ||
|
|
70c001e22b | ||
|
|
f1dc22a200 | ||
|
|
aae1b69093 | ||
|
|
469ca44016 | ||
|
|
81fcd68e9b | ||
|
|
8781419b4a | ||
|
|
2eea671857 | ||
|
|
73acfbf864 | ||
|
|
ae0e994461 | ||
|
|
07b9011636 | ||
|
|
bc2b3b7f8f | ||
|
|
ea02a2b868 | ||
|
|
ba3a6cbe87 | ||
|
|
268dcbe00b | ||
|
|
6be97f13d0 | ||
|
|
95d6c93a07 | ||
|
|
a2bb220043 | ||
|
|
911d6dcce1 | ||
|
|
5f6a9850eb | ||
|
|
de18bf06c3 | ||
|
|
73473d671d | ||
|
|
3fbab7c3af | ||
|
|
521cccaed6 | ||
|
|
35da3408dc | ||
|
|
c03096e806 | ||
|
|
2afc947d6c | ||
|
|
076da649cf | ||
|
|
55f8303dc2 | ||
|
|
93ced0959c | ||
|
|
6f13fa50bf | ||
|
|
3bface12e0 | ||
|
|
b584c8e353 | ||
|
|
6caf87df2d | ||
|
|
4d1f2c2bc1 | ||
|
|
0b1175b46c | ||
|
|
4e50dabc56 | ||
|
|
ce45a5926a | ||
|
|
c540a4f257 | ||
|
|
7af94c172f | ||
|
|
7556587e35 | ||
|
|
a0030b27e2 | ||
|
|
8080e05444 | ||
|
|
af11879545 | ||
|
|
c89f1c9d95 | ||
|
|
b7ac599a42 | ||
|
|
8363877c66 | ||
|
|
4bcb4b5b9c | ||
|
|
68302e14b9 | ||
|
|
c1abc7a7f1 | ||
|
|
484717d57d | ||
|
|
b91c608fcf | ||
|
|
8f8ece2b34 | ||
|
|
9b5c1c01e9 | ||
|
|
816a1d446e | ||
|
|
19bfd5beca | ||
|
|
9ac7e051b3 | ||
|
|
80b1d51f76 | ||
|
|
6340ebb36d | ||
|
|
70721afa51 | ||
|
|
9c31622598 | ||
|
|
f372b0907b | ||
|
|
fac96e0b08 | ||
|
|
2bc53f9868 | ||
|
|
e8106befe9 | ||
|
|
83412b813f | ||
|
|
b56d497543 | ||
|
|
dd40962288 | ||
|
|
b7eebad2a5 | ||
|
|
8f8698fd02 | ||
|
|
092f716f12 | ||
|
|
c38f48c7f2 | ||
|
|
98837bc379 | ||
|
|
0f243bb6ec | ||
|
|
88fc1bbe32 | ||
|
|
d5ef0e5744 | ||
|
|
2ecac38f6d | ||
|
|
e90557d7dc | ||
|
|
628893fd5b | ||
|
|
a81e4c3362 | ||
|
|
ca7b89c308 | ||
|
|
03335cc015 | ||
|
|
08557ae287 | ||
|
|
08d2a6242d | ||
|
|
4b481bd405 | ||
|
|
0b1e3b2a7f | ||
|
|
dbd9873450 | ||
|
|
c6d0a17669 | ||
|
|
adeab10f6d | ||
|
|
824f852ed7 | ||
|
|
284c1be85f | ||
|
|
7ad6baf483 | ||
|
|
f1638faa3a | ||
|
|
dea786abfa | ||
|
|
f96b82b112 | ||
|
|
95fe11c6b4 | ||
|
|
f2f688b9b8 | ||
|
|
0139e18271 | ||
|
|
657995d744 | ||
|
|
4057238185 | ||
|
|
fb07ff65c9 | ||
|
|
dbc56ffee7 | ||
|
|
ee696be51d | ||
|
|
5d3fd3d389 | ||
|
|
fa063722e1 | ||
|
|
f5cc35509b | ||
|
|
d39c8fae54 | ||
|
|
d3b81babec | ||
|
|
f35f6bd4c8 | ||
|
|
d5cfef94a3 | ||
|
|
f37f5ba97b | ||
|
|
42818a9950 | ||
|
|
e85c3e5b27 | ||
|
|
a39c88c7b4 | ||
|
|
73ebf5256a | ||
|
|
6d31cd2a41 | ||
|
|
5600fed9c4 | ||
|
|
6920b77b4a | ||
|
|
ccd6b3914c | ||
|
|
c4723263a4 | ||
|
|
4581a46529 | ||
|
|
33a2c5dcd8 | ||
|
|
f6a8a21f94 | ||
|
|
ff5773c837 | ||
|
|
66f8084916 | ||
|
|
a2467d0418 | ||
|
|
3b0339a9b3 | ||
|
|
fb1d4fdd3c | ||
|
|
56a16539ae | ||
|
|
c0b2cf7388 | ||
|
|
d9c58d9333 | ||
|
|
ef3a52468f | ||
|
|
c88b731793 | ||
|
|
2e85a28c02 | ||
|
|
964fef1aab | ||
|
|
1a832fa0a5 | ||
|
|
75bdc92bbf | ||
|
|
a8c231ad8c | ||
|
|
f396247838 | ||
|
|
e3ea4776c7 | ||
|
|
37a928b065 | ||
|
|
85c269e697 | ||
|
|
6e70268ab9 | ||
|
|
fb8929ea37 | ||
|
|
5d9c0dd8b5 | ||
|
|
debf093c54 | ||
|
|
00b5a5cc0c | ||
|
|
dbb99d0367 | ||
|
|
7702f05756 | ||
|
|
2c635bce62 | ||
|
|
48713a4e7b | ||
|
|
e831354401 | ||
|
|
55c5ea5c4c | ||
|
|
1fd5165079 | ||
|
|
949cea95f4 | ||
|
|
12762e08ef | ||
|
|
62bdb2627a | ||
|
|
386be4e746 | ||
|
|
d9ec556061 | ||
|
|
876d860488 | ||
|
|
59097070ef | ||
|
|
77b5aa4369 | ||
|
|
0d7c331ff0 | ||
|
|
1c1a1a1d3f | ||
|
|
47efcfd6e2 | ||
|
|
15a0b959aa | ||
|
|
fcb6a47e8c | ||
|
|
b5f656ae58 | ||
|
|
382cd24a57 | ||
|
|
b1beb617b3 | ||
|
|
91f8b1fef7 | ||
|
|
2ad87bf1fe | ||
|
|
eca2a4a9c8 | ||
|
|
dff609d829 | ||
|
|
e960ae66a3 | ||
|
|
093cbc5ebc | ||
|
|
f663ef8c16 | ||
|
|
de9f6425f9 | ||
|
|
47ced60243 | ||
|
|
58ebbfba20 | ||
|
|
e164d15ec6 | ||
|
|
3efdb4e532 | ||
|
|
de0af58cf8 | ||
|
|
84abfa6881 | ||
|
|
6b60e85a33 | ||
|
|
63f3e23e2b | ||
|
|
eb1249618b | ||
|
|
cef9bb1487 | ||
|
|
bb49944b96 | ||
|
|
fcc4050f86 | ||
|
|
9c83a52c6d | ||
|
|
a6e8b25969 | ||
|
|
529bc01d69 | ||
|
|
11055b1d32 | ||
|
|
fd9a91420d | ||
|
|
529c8d7cf2 | ||
|
|
086ebe1a7c | ||
|
|
29c964cca1 | ||
|
|
36573d6005 | ||
|
|
aa0c589361 | ||
|
|
685b80e519 | ||
|
|
5a401af1fd | ||
|
|
25d63f7516 | ||
|
|
6c5e0579cf | ||
|
|
4ac04a1a46 | ||
|
|
746128e37b | ||
|
|
fe81ffaf78 | ||
|
|
1f6eb9cdc3 | ||
|
|
5cc358de4e | ||
|
|
610dd2c08d | ||
|
|
506bbd314d | ||
|
|
4caa6a10b5 | ||
|
|
665b7197a6 | ||
|
|
4b79623ce3 | ||
|
|
c4994a208b | ||
|
|
eedea2ca88 | ||
|
|
de6ea29e3b | ||
|
|
bb983d4ba2 | ||
|
|
c014508519 | ||
|
|
fcfbb1e857 | ||
|
|
911ee579a9 | ||
|
|
a6ff92b099 | ||
|
|
d73ba7dd3e | ||
|
|
04ddcd5c93 | ||
|
|
af29ae1968 | ||
|
|
fbd3cff90d | ||
|
|
0ed9894b7e | ||
|
|
a54a72c269 | ||
|
|
f514e5e9bb | ||
|
|
3955587372 | ||
|
|
6b28dc72e8 | ||
|
|
ca7253a589 | ||
|
|
af53dcda1b | ||
|
|
d3bd56b131 | ||
|
|
e9e61ea2d8 | ||
|
|
86b984001d | ||
|
|
fa7f8104c8 | ||
|
|
bd5fe43285 | ||
|
|
d38051e806 | ||
|
|
daa5342986 | ||
|
|
c48436ccbf | ||
|
|
7aa00faa6c | ||
|
|
6217a7b9a9 | ||
|
|
d67ebabc95 | ||
|
|
65274e89d7 | ||
|
|
721e04f793 | ||
|
|
433309ef1a | ||
|
|
735cfb4c29 | ||
|
|
6202090836 | ||
|
|
436cbc1f06 | ||
|
|
40b08d737c | ||
|
|
4c5b42b898 | ||
|
|
7a6b72ebac | ||
|
|
1b8584d4bb | ||
|
|
13105c4ab3 | ||
|
|
dc27bbb01d | ||
|
|
b863060df1 | ||
|
|
18f95e867f | ||
|
|
ed6137a76a | ||
|
|
c3f02a698e | ||
|
|
db106f8ca1 | ||
|
|
8e47cc73a5 | ||
|
|
639bf05081 | ||
|
|
4e142e0212 | ||
|
|
c9bf1c86c6 | ||
|
|
82830c8173 | ||
|
|
7f5741c43b | ||
|
|
643d4831c1 | ||
|
|
b032eed22a | ||
|
|
1b49c8540e | ||
|
|
f7534a0ae3 | ||
|
|
780ad9eb10 | ||
|
|
e25bc8efe4 | ||
|
|
26abe90671 | ||
|
|
446f1ffdf5 | ||
|
|
2168698595 | ||
|
|
8cf29682bb | ||
|
|
86dc7cc804 |
2
.github/.gitleaks.toml
vendored
2
.github/.gitleaks.toml
vendored
@@ -536,7 +536,7 @@ secretGroup = 4
|
||||
|
||||
[allowlist]
|
||||
description = "global allow lists"
|
||||
regexes = ['''219-09-9999''', '''078-05-1120''', '''(9[0-9]{2}|666)-\d{2}-\d{4}''', '''RPM-GPG-KEY.*''', '''.*:.*StrelkaHexDump.*''', '''.*:.*PLACEHOLDER.*''']
|
||||
regexes = ['''219-09-9999''', '''078-05-1120''', '''(9[0-9]{2}|666)-\d{2}-\d{4}''', '''RPM-GPG-KEY.*''', '''.*:.*StrelkaHexDump.*''', '''.*:.*PLACEHOLDER.*''', '''ssl_.*password''']
|
||||
paths = [
|
||||
'''gitleaks.toml''',
|
||||
'''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$''',
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
### 2.4.70-20240529 ISO image released on 2024/05/29
|
||||
### 2.4.90-20240729 ISO image released on 2024/07/29
|
||||
|
||||
|
||||
### Download and Verify
|
||||
|
||||
2.4.70-20240529 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.4.70-20240529.iso
|
||||
2.4.90-20240729 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.4.90-20240729.iso
|
||||
|
||||
MD5: 8FCCF31C2470D1ABA380AF196B611DEC
|
||||
SHA1: EE5E8F8C14819E7A1FE423E6920531A97F39600B
|
||||
SHA256: EF5E781D50D50660F452ADC54FD4911296ECBECED7879FA8E04687337CA89BEC
|
||||
MD5: 9A7714F5922EE555F08675D25E6237D5
|
||||
SHA1: D3B331452627DB716906BA9F3922574DFA3852DC
|
||||
SHA256: 5B0CE32543944DBC50C4E906857384211E1BE83EF409619778F18FC62017E0E0
|
||||
|
||||
Signature for ISO image:
|
||||
https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.70-20240529.iso.sig
|
||||
https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.90-20240729.iso.sig
|
||||
|
||||
Signing key:
|
||||
https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.4/main/KEYS
|
||||
@@ -25,27 +25,29 @@ 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.70-20240529.iso.sig
|
||||
wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.90-20240729.iso.sig
|
||||
```
|
||||
|
||||
Download the ISO image:
|
||||
```
|
||||
wget https://download.securityonion.net/file/securityonion/securityonion-2.4.70-20240529.iso
|
||||
wget https://download.securityonion.net/file/securityonion/securityonion-2.4.90-20240729.iso
|
||||
```
|
||||
|
||||
Verify the downloaded ISO image using the signature file:
|
||||
```
|
||||
gpg --verify securityonion-2.4.70-20240529.iso.sig securityonion-2.4.70-20240529.iso
|
||||
gpg --verify securityonion-2.4.90-20240729.iso.sig securityonion-2.4.90-20240729.iso
|
||||
```
|
||||
|
||||
The output should show "Good signature" and the Primary key fingerprint should match what's shown below:
|
||||
```
|
||||
gpg: Signature made Wed 29 May 2024 11:40:59 AM EDT using RSA key ID FE507013
|
||||
gpg: Signature made Thu 25 Jul 2024 06:51:11 PM EDT 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.
|
||||
Primary key fingerprint: C804 A93D 36BE 0C73 3EA1 9644 7C10 60B7 FE50 7013
|
||||
```
|
||||
|
||||
If it fails to verify, try downloading again. If it still fails to verify, try downloading from another computer or another network.
|
||||
|
||||
Once you've verified the ISO image, you're ready to proceed to our Installation guide:
|
||||
https://docs.securityonion.net/en/2.4/installation.html
|
||||
|
||||
@@ -19,4 +19,4 @@ role:
|
||||
receiver:
|
||||
standalone:
|
||||
searchnode:
|
||||
sensor:
|
||||
sensor:
|
||||
34
pillar/elasticsearch/nodes.sls
Normal file
34
pillar/elasticsearch/nodes.sls
Normal file
@@ -0,0 +1,34 @@
|
||||
{% set node_types = {} %}
|
||||
{% for minionid, ip in salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='elasticsearch:enabled:true',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='pillar') | dictsort()
|
||||
%}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% set hostname = minionid.split('_') | first %}
|
||||
{% set node_type = minionid.split('_') | last %}
|
||||
{% if node_type not in node_types.keys() %}
|
||||
{% do node_types.update({node_type: {hostname: ip[0]}}) %}
|
||||
{% else %}
|
||||
{% if hostname not in node_types[node_type] %}
|
||||
{% do node_types[node_type].update({hostname: ip[0]}) %}
|
||||
{% else %}
|
||||
{% do node_types[node_type][hostname].update(ip[0]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
elasticsearch:
|
||||
nodes:
|
||||
{% for node_type, values in node_types.items() %}
|
||||
{{node_type}}:
|
||||
{% for hostname, ip in values.items() %}
|
||||
{{hostname}}:
|
||||
ip: {{ip}}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
2
pillar/kafka/nodes.sls
Normal file
2
pillar/kafka/nodes.sls
Normal file
@@ -0,0 +1,2 @@
|
||||
kafka:
|
||||
nodes:
|
||||
@@ -1,16 +1,15 @@
|
||||
{% set node_types = {} %}
|
||||
{% set cached_grains = salt.saltutil.runner('cache.grains', tgt='*') %}
|
||||
{% for minionid, ip in salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='G@role:so-manager or G@role:so-managersearch or G@role:so-standalone or G@role:so-searchnode or G@role:so-heavynode or G@role:so-receiver or G@role:so-fleet ',
|
||||
tgt='logstash:enabled:true',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='compound') | dictsort()
|
||||
tgt_type='pillar') | dictsort()
|
||||
%}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% set hostname = cached_grains[minionid]['host'] %}
|
||||
{% set node_type = minionid.split('_')[1] %}
|
||||
{% set hostname = minionid.split('_') | first %}
|
||||
{% set node_type = minionid.split('_') | last %}
|
||||
{% if node_type not in node_types.keys() %}
|
||||
{% do node_types.update({node_type: {hostname: ip[0]}}) %}
|
||||
{% else %}
|
||||
|
||||
34
pillar/redis/nodes.sls
Normal file
34
pillar/redis/nodes.sls
Normal file
@@ -0,0 +1,34 @@
|
||||
{% set node_types = {} %}
|
||||
{% for minionid, ip in salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='redis:enabled:true',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='pillar') | dictsort()
|
||||
%}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% set hostname = minionid.split('_') | first %}
|
||||
{% set node_type = minionid.split('_') | last %}
|
||||
{% if node_type not in node_types.keys() %}
|
||||
{% do node_types.update({node_type: {hostname: ip[0]}}) %}
|
||||
{% else %}
|
||||
{% if hostname not in node_types[node_type] %}
|
||||
{% do node_types[node_type].update({hostname: ip[0]}) %}
|
||||
{% else %}
|
||||
{% do node_types[node_type][hostname].update(ip[0]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
redis:
|
||||
nodes:
|
||||
{% for node_type, values in node_types.items() %}
|
||||
{{node_type}}:
|
||||
{% for hostname, ip in values.items() %}
|
||||
{{hostname}}:
|
||||
ip: {{ip}}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -47,10 +47,12 @@ base:
|
||||
- kibana.adv_kibana
|
||||
- kratos.soc_kratos
|
||||
- kratos.adv_kratos
|
||||
- redis.nodes
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- influxdb.soc_influxdb
|
||||
- influxdb.adv_influxdb
|
||||
- elasticsearch.nodes
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
- elasticfleet.soc_elasticfleet
|
||||
@@ -61,6 +63,9 @@ base:
|
||||
- backup.adv_backup
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- kafka.adv_kafka
|
||||
- stig.soc_stig
|
||||
|
||||
'*_sensor':
|
||||
@@ -144,10 +149,12 @@ base:
|
||||
- idstools.adv_idstools
|
||||
- kratos.soc_kratos
|
||||
- kratos.adv_kratos
|
||||
- redis.nodes
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- influxdb.soc_influxdb
|
||||
- influxdb.adv_influxdb
|
||||
- elasticsearch.nodes
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
- elasticfleet.soc_elasticfleet
|
||||
@@ -176,6 +183,9 @@ base:
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- stig.soc_stig
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- kafka.adv_kafka
|
||||
|
||||
'*_heavynode':
|
||||
- elasticsearch.auth
|
||||
@@ -209,17 +219,22 @@ base:
|
||||
- logstash.nodes
|
||||
- logstash.soc_logstash
|
||||
- logstash.adv_logstash
|
||||
- elasticsearch.nodes
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/elasticsearch/auth.sls') %}
|
||||
- elasticsearch.auth
|
||||
{% endif %}
|
||||
- redis.nodes
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- stig.soc_stig
|
||||
- soc.license
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- kafka.adv_kafka
|
||||
|
||||
'*_receiver':
|
||||
- logstash.nodes
|
||||
@@ -232,6 +247,10 @@ base:
|
||||
- redis.adv_redis
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- kafka.adv_kafka
|
||||
- soc.license
|
||||
|
||||
'*_import':
|
||||
- secrets
|
||||
|
||||
14
pyci.sh
14
pyci.sh
@@ -15,12 +15,16 @@ TARGET_DIR=${1:-.}
|
||||
|
||||
PATH=$PATH:/usr/local/bin
|
||||
|
||||
if ! which pytest &> /dev/null || ! which flake8 &> /dev/null ; then
|
||||
echo "Missing dependencies. Consider running the following command:"
|
||||
echo " python -m pip install flake8 pytest pytest-cov"
|
||||
if [ ! -d .venv ]; then
|
||||
python -m venv .venv
|
||||
fi
|
||||
|
||||
source .venv/bin/activate
|
||||
|
||||
if ! pip install flake8 pytest pytest-cov pyyaml; then
|
||||
echo "Unable to install dependencies."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pip install pytest pytest-cov
|
||||
flake8 "$TARGET_DIR" "--config=${HOME_DIR}/pytest.ini"
|
||||
python3 -m pytest "--cov-config=${HOME_DIR}/pytest.ini" "--cov=$TARGET_DIR" --doctest-modules --cov-report=term --cov-fail-under=100 "$TARGET_DIR"
|
||||
python3 -m pytest "--cov-config=${HOME_DIR}/pytest.ini" "--cov=$TARGET_DIR" --doctest-modules --cov-report=term --cov-fail-under=100 "$TARGET_DIR"
|
||||
|
||||
@@ -103,7 +103,8 @@
|
||||
'utility',
|
||||
'schedule',
|
||||
'docker_clean',
|
||||
'stig'
|
||||
'stig',
|
||||
'kafka'
|
||||
],
|
||||
'so-managersearch': [
|
||||
'salt.master',
|
||||
@@ -125,7 +126,8 @@
|
||||
'utility',
|
||||
'schedule',
|
||||
'docker_clean',
|
||||
'stig'
|
||||
'stig',
|
||||
'kafka'
|
||||
],
|
||||
'so-searchnode': [
|
||||
'ssl',
|
||||
@@ -134,7 +136,9 @@
|
||||
'firewall',
|
||||
'schedule',
|
||||
'docker_clean',
|
||||
'stig'
|
||||
'stig',
|
||||
'kafka.ca',
|
||||
'kafka.ssl'
|
||||
],
|
||||
'so-standalone': [
|
||||
'salt.master',
|
||||
@@ -159,7 +163,8 @@
|
||||
'schedule',
|
||||
'tcpreplay',
|
||||
'docker_clean',
|
||||
'stig'
|
||||
'stig',
|
||||
'kafka'
|
||||
],
|
||||
'so-sensor': [
|
||||
'ssl',
|
||||
@@ -190,7 +195,9 @@
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'schedule',
|
||||
'docker_clean'
|
||||
'docker_clean',
|
||||
'kafka',
|
||||
'stig'
|
||||
],
|
||||
'so-desktop': [
|
||||
'ssl',
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
mine_functions:
|
||||
x509.get_pem_entries: [/etc/pki/ca.crt]
|
||||
|
||||
x509_signing_policies:
|
||||
filebeat:
|
||||
- minions: '*'
|
||||
@@ -70,3 +67,17 @@ x509_signing_policies:
|
||||
- authorityKeyIdentifier: keyid,issuer:always
|
||||
- days_valid: 820
|
||||
- copypath: /etc/pki/issued_certs/
|
||||
kafka:
|
||||
- minions: '*'
|
||||
- signing_private_key: /etc/pki/ca.key
|
||||
- signing_cert: /etc/pki/ca.crt
|
||||
- C: US
|
||||
- ST: Utah
|
||||
- L: Salt Lake City
|
||||
- basicConstraints: "critical CA:false"
|
||||
- keyUsage: "digitalSignature, keyEncipherment"
|
||||
- subjectKeyIdentifier: hash
|
||||
- authorityKeyIdentifier: keyid,issuer:always
|
||||
- extendedKeyUsage: "serverAuth, clientAuth"
|
||||
- days_valid: 820
|
||||
- copypath: /etc/pki/issued_certs/
|
||||
|
||||
@@ -57,6 +57,12 @@ copy_so-yaml_manager_tools_sbin:
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-repo-sync_manager_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/manager/tools/sbin/so-repo-sync
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-repo-sync
|
||||
- preserve: True
|
||||
|
||||
# This section is used to put the new script in place so that it can be called during soup.
|
||||
# It is faster than calling the states that normally manage them to put them in place.
|
||||
copy_so-common_sbin:
|
||||
@@ -94,6 +100,13 @@ copy_so-yaml_sbin:
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-repo-sync_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/so-repo-sync
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-repo-sync
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
{% else %}
|
||||
fix_23_soup_sbin:
|
||||
cmd.run:
|
||||
|
||||
@@ -31,6 +31,11 @@ if ! echo "$PATH" | grep -q "/usr/sbin"; then
|
||||
export PATH="$PATH:/usr/sbin"
|
||||
fi
|
||||
|
||||
# See if a proxy is set. If so use it.
|
||||
if [ -f /etc/profile.d/so-proxy.sh ]; then
|
||||
. /etc/profile.d/so-proxy.sh
|
||||
fi
|
||||
|
||||
# Define a banner to separate sections
|
||||
banner="========================================================================="
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ container_list() {
|
||||
"so-idh"
|
||||
"so-idstools"
|
||||
"so-influxdb"
|
||||
"so-kafka"
|
||||
"so-kibana"
|
||||
"so-kratos"
|
||||
"so-logstash"
|
||||
@@ -64,7 +65,7 @@ container_list() {
|
||||
"so-strelka-manager"
|
||||
"so-suricata"
|
||||
"so-telegraf"
|
||||
"so-zeek"
|
||||
"so-zeek"
|
||||
)
|
||||
else
|
||||
TRUSTED_CONTAINERS=(
|
||||
|
||||
@@ -241,6 +241,7 @@ exclude_log "mysqld.log" # MySQL is removed as of 2.4.70, logs may still be on
|
||||
exclude_log "soctopus.log" # Soctopus is removed as of 2.4.70, logs may still be on disk
|
||||
exclude_log "agentstatus.log" # ignore this log since it tracks agents in error state
|
||||
exclude_log "detections_runtime-status_yara.log" # temporarily ignore this log until Detections is more stable
|
||||
exclude_log "/nsm/kafka/data/" # ignore Kafka data directory from log check.
|
||||
|
||||
for log_file in $(cat /tmp/log_check_files); do
|
||||
status "Checking log file $log_file"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
. /usr/sbin/so-common
|
||||
. /usr/sbin/so-image-common
|
||||
|
||||
REPLAYIFACE=${REPLAYIFACE:-$(lookup_pillar interface sensor)}
|
||||
REPLAYIFACE=${REPLAYIFACE:-"{{salt['pillar.get']('sensor:interface', '')}}"}
|
||||
REPLAYSPEED=${REPLAYSPEED:-10}
|
||||
|
||||
mkdir -p /opt/so/samples
|
||||
@@ -187,3 +187,12 @@ docker:
|
||||
custom_bind_mounts: []
|
||||
extra_hosts: []
|
||||
extra_env: []
|
||||
'so-kafka':
|
||||
final_octet: 88
|
||||
port_bindings:
|
||||
- 0.0.0.0:9092:9092
|
||||
- 0.0.0.0:9093:9093
|
||||
- 0.0.0.0:8778:8778
|
||||
custom_bind_mounts: []
|
||||
extra_hosts: []
|
||||
extra_env: []
|
||||
|
||||
@@ -20,30 +20,30 @@ dockergroup:
|
||||
dockerheldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.6.21-1
|
||||
- docker-ce: 5:24.0.3-1~debian.12~bookworm
|
||||
- docker-ce-cli: 5:24.0.3-1~debian.12~bookworm
|
||||
- docker-ce-rootless-extras: 5:24.0.3-1~debian.12~bookworm
|
||||
- containerd.io: 1.6.33-1
|
||||
- docker-ce: 5:26.1.4-1~debian.12~bookworm
|
||||
- docker-ce-cli: 5:26.1.4-1~debian.12~bookworm
|
||||
- docker-ce-rootless-extras: 5:26.1.4-1~debian.12~bookworm
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% elif grains.oscodename == 'jammy' %}
|
||||
dockerheldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.6.21-1
|
||||
- docker-ce: 5:24.0.2-1~ubuntu.22.04~jammy
|
||||
- docker-ce-cli: 5:24.0.2-1~ubuntu.22.04~jammy
|
||||
- docker-ce-rootless-extras: 5:24.0.2-1~ubuntu.22.04~jammy
|
||||
- containerd.io: 1.6.33-1
|
||||
- docker-ce: 5:26.1.4-1~ubuntu.22.04~jammy
|
||||
- docker-ce-cli: 5:26.1.4-1~ubuntu.22.04~jammy
|
||||
- docker-ce-rootless-extras: 5:26.1.4-1~ubuntu.22.04~jammy
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% else %}
|
||||
dockerheldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.4.9-1
|
||||
- docker-ce: 5:20.10.8~3-0~ubuntu-focal
|
||||
- docker-ce-cli: 5:20.10.5~3-0~ubuntu-focal
|
||||
- docker-ce-rootless-extras: 5:20.10.5~3-0~ubuntu-focal
|
||||
- containerd.io: 1.6.33-1
|
||||
- docker-ce: 5:26.1.4-1~ubuntu.20.04~focal
|
||||
- docker-ce-cli: 5:26.1.4-1~ubuntu.20.04~focal
|
||||
- docker-ce-rootless-extras: 5:26.1.4-1~ubuntu.20.04~focal
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% endif %}
|
||||
@@ -51,10 +51,10 @@ dockerheldpackages:
|
||||
dockerheldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.6.21-3.1.el9
|
||||
- docker-ce: 24.0.4-1.el9
|
||||
- docker-ce-cli: 24.0.4-1.el9
|
||||
- docker-ce-rootless-extras: 24.0.4-1.el9
|
||||
- containerd.io: 1.6.33-3.1.el9
|
||||
- docker-ce: 3:26.1.4-1.el9
|
||||
- docker-ce-cli: 1:26.1.4-1.el9
|
||||
- docker-ce-rootless-extras: 26.1.4-1.el9
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% endif %}
|
||||
|
||||
@@ -101,3 +101,4 @@ docker:
|
||||
multiline: True
|
||||
forcedType: "[]string"
|
||||
so-zeek: *dockerOptions
|
||||
so-kafka: *dockerOptions
|
||||
@@ -27,7 +27,9 @@ wait_for_elasticsearch_elasticfleet:
|
||||
so-elastic-fleet-auto-configure-logstash-outputs:
|
||||
cmd.run:
|
||||
- name: /usr/sbin/so-elastic-fleet-outputs-update
|
||||
- retry: True
|
||||
- retry:
|
||||
attempts: 4
|
||||
interval: 30
|
||||
{% endif %}
|
||||
|
||||
# If enabled, automatically update Fleet Server URLs & ES Connection
|
||||
@@ -35,7 +37,9 @@ so-elastic-fleet-auto-configure-logstash-outputs:
|
||||
so-elastic-fleet-auto-configure-server-urls:
|
||||
cmd.run:
|
||||
- name: /usr/sbin/so-elastic-fleet-urls-update
|
||||
- retry: True
|
||||
- retry:
|
||||
attempts: 4
|
||||
interval: 30
|
||||
{% endif %}
|
||||
|
||||
# Automatically update Fleet Server Elasticsearch URLs & Agent Artifact URLs
|
||||
@@ -43,12 +47,16 @@ so-elastic-fleet-auto-configure-server-urls:
|
||||
so-elastic-fleet-auto-configure-elasticsearch-urls:
|
||||
cmd.run:
|
||||
- name: /usr/sbin/so-elastic-fleet-es-url-update
|
||||
- retry: True
|
||||
- retry:
|
||||
attempts: 4
|
||||
interval: 30
|
||||
|
||||
so-elastic-fleet-auto-configure-artifact-urls:
|
||||
cmd.run:
|
||||
- name: /usr/sbin/so-elastic-fleet-artifacts-url-update
|
||||
- retry: True
|
||||
- retry:
|
||||
attempts: 4
|
||||
interval: 30
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ NUM_RUNNING=$(pgrep -cf "/bin/bash /sbin/so-elastic-agent-gen-installers")
|
||||
|
||||
for i in {1..30}
|
||||
do
|
||||
ENROLLMENTOKEN=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "localhost:5601/api/fleet/enrollment_api_keys" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' | jq .list | jq -r -c '.[] | select(.policy_id | contains("endpoints-initial")) | .api_key')
|
||||
ENROLLMENTOKEN=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "localhost:5601/api/fleet/enrollment_api_keys?perPage=100" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' | jq .list | jq -r -c '.[] | select(.policy_id | contains("endpoints-initial")) | .api_key')
|
||||
FLEETHOST=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/fleet_server_hosts/grid-default' | jq -r '.item.host_urls[]' | paste -sd ',')
|
||||
if [[ $FLEETHOST ]] && [[ $ENROLLMENTOKEN ]]; then break; else sleep 10; fi
|
||||
done
|
||||
|
||||
@@ -21,64 +21,104 @@ function update_logstash_outputs() {
|
||||
# Update Logstash Outputs
|
||||
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_logstash" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" | jq
|
||||
}
|
||||
function update_kafka_outputs() {
|
||||
# Make sure SSL configuration is included in policy updates for Kafka output. SSL is configured in so-elastic-fleet-setup
|
||||
SSL_CONFIG=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" | jq -r '.item.ssl')
|
||||
|
||||
# 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')
|
||||
JSON_STRING=$(jq -n \
|
||||
--arg UPDATEDLIST "$NEW_LIST_JSON" \
|
||||
--argjson SSL_CONFIG "$SSL_CONFIG" \
|
||||
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": $SSL_CONFIG}')
|
||||
# Update Kafka outputs
|
||||
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" | jq
|
||||
}
|
||||
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.item.id' <<< "$RAW_JSON")
|
||||
if [ "$CHECKSUM" != "so-manager_logstash" ]; then
|
||||
printf "Failed to query for current Logstash Outputs..."
|
||||
exit 1
|
||||
fi
|
||||
{% 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')
|
||||
|
||||
# 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}')
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.item.id' <<< "$RAW_JSON")
|
||||
if [ "$CHECKSUM" != "so-manager_kafka" ]; then
|
||||
printf "Failed to query for current Kafka Outputs..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare -a NEW_LIST=()
|
||||
# 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}')
|
||||
|
||||
declare -a NEW_LIST=()
|
||||
|
||||
# Query for the current Grid Nodes that are running kafka
|
||||
KAFKANODES=$(salt-call --out=json pillar.get kafka:nodes | jq '.local')
|
||||
|
||||
# Query for Kafka nodes with Broker role and add hostname to list
|
||||
while IFS= read -r line; do
|
||||
NEW_LIST+=("$line")
|
||||
done < <(jq -r 'to_entries | .[] | select(.value.role | contains("broker")) | .key + ":9092"' <<< $KAFKANODES)
|
||||
|
||||
{# 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')
|
||||
|
||||
# Check to make sure that the server responded with good data - else, bail from script
|
||||
CHECKSUM=$(jq -r '.item.id' <<< "$RAW_JSON")
|
||||
if [ "$CHECKSUM" != "so-manager_logstash" ]; then
|
||||
printf "Failed to query for current Logstash Outputs..."
|
||||
exit 1
|
||||
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}')
|
||||
|
||||
declare -a NEW_LIST=()
|
||||
|
||||
{# If we select to not send to manager via SOC, then omit the code that adds manager to NEW_LIST #}
|
||||
{% if ELASTICFLEETMERGED.enable_manager_output %}
|
||||
# Create array & add initial elements
|
||||
if [ "{{ GLOBALS.hostname }}" = "{{ GLOBALS.url_base }}" ]; then
|
||||
NEW_LIST+=("{{ GLOBALS.url_base }}:5055")
|
||||
else
|
||||
NEW_LIST+=("{{ GLOBALS.url_base }}:5055" "{{ GLOBALS.hostname }}:5055")
|
||||
fi
|
||||
{% endif %}
|
||||
|
||||
# Query for FQDN entries & add them to the list
|
||||
{% if ELASTICFLEETMERGED.config.server.custom_fqdn | length > 0 %}
|
||||
CUSTOMFQDNLIST=('{{ ELASTICFLEETMERGED.config.server.custom_fqdn | join(' ') }}')
|
||||
readarray -t -d ' ' CUSTOMFQDN < <(printf '%s' "$CUSTOMFQDNLIST")
|
||||
for CUSTOMNAME in "${CUSTOMFQDN[@]}"
|
||||
do
|
||||
NEW_LIST+=("$CUSTOMNAME:5055")
|
||||
done
|
||||
{% endif %}
|
||||
|
||||
# Query for the current Grid Nodes that are running Logstash
|
||||
LOGSTASHNODES=$(salt-call --out=json pillar.get logstash:nodes | jq '.local')
|
||||
|
||||
# Query for Receiver Nodes & add them to the list
|
||||
if grep -q "receiver" <<< $LOGSTASHNODES; then
|
||||
readarray -t RECEIVERNODES < <(jq -r ' .receiver | keys_unsorted[]' <<< $LOGSTASHNODES)
|
||||
for NODE in "${RECEIVERNODES[@]}"
|
||||
do
|
||||
NEW_LIST+=("$NODE:5055")
|
||||
done
|
||||
fi
|
||||
|
||||
# Query for Fleet Nodes & add them to the list
|
||||
if grep -q "fleet" <<< $LOGSTASHNODES; then
|
||||
readarray -t FLEETNODES < <(jq -r ' .fleet | keys_unsorted[]' <<< $LOGSTASHNODES)
|
||||
for NODE in "${FLEETNODES[@]}"
|
||||
do
|
||||
NEW_LIST+=("$NODE:5055")
|
||||
done
|
||||
fi
|
||||
|
||||
{# If we select to not send to manager via SOC, then omit the code that adds manager to NEW_LIST #}
|
||||
{% if ELASTICFLEETMERGED.enable_manager_output %}
|
||||
# Create array & add initial elements
|
||||
if [ "{{ GLOBALS.hostname }}" = "{{ GLOBALS.url_base }}" ]; then
|
||||
NEW_LIST+=("{{ GLOBALS.url_base }}:5055")
|
||||
else
|
||||
NEW_LIST+=("{{ GLOBALS.url_base }}:5055" "{{ GLOBALS.hostname }}:5055")
|
||||
fi
|
||||
{% endif %}
|
||||
|
||||
# Query for FQDN entries & add them to the list
|
||||
{% if ELASTICFLEETMERGED.config.server.custom_fqdn | length > 0 %}
|
||||
CUSTOMFQDNLIST=('{{ ELASTICFLEETMERGED.config.server.custom_fqdn | join(' ') }}')
|
||||
readarray -t -d ' ' CUSTOMFQDN < <(printf '%s' "$CUSTOMFQDNLIST")
|
||||
for CUSTOMNAME in "${CUSTOMFQDN[@]}"
|
||||
do
|
||||
NEW_LIST+=("$CUSTOMNAME:5055")
|
||||
done
|
||||
{% endif %}
|
||||
|
||||
# Query for the current Grid Nodes that are running Logstash
|
||||
LOGSTASHNODES=$(salt-call --out=json pillar.get logstash:nodes | jq '.local')
|
||||
|
||||
# Query for Receiver Nodes & add them to the list
|
||||
if grep -q "receiver" <<< $LOGSTASHNODES; then
|
||||
readarray -t RECEIVERNODES < <(jq -r ' .receiver | keys_unsorted[]' <<< $LOGSTASHNODES)
|
||||
for NODE in "${RECEIVERNODES[@]}"
|
||||
do
|
||||
NEW_LIST+=("$NODE:5055")
|
||||
done
|
||||
fi
|
||||
|
||||
# Query for Fleet Nodes & add them to the list
|
||||
if grep -q "fleet" <<< $LOGSTASHNODES; then
|
||||
readarray -t FLEETNODES < <(jq -r ' .fleet | keys_unsorted[]' <<< $LOGSTASHNODES)
|
||||
for NODE in "${FLEETNODES[@]}"
|
||||
do
|
||||
NEW_LIST+=("$NODE:5055")
|
||||
done
|
||||
fi
|
||||
|
||||
# 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}')
|
||||
@@ -87,9 +127,28 @@ NEW_HASH=$(sha1sum <<< "$NEW_LIST_JSON" | awk '{print $1}')
|
||||
if [ "$NEW_HASH" = "$CURRENT_HASH" ]; then
|
||||
printf "\nHashes match - no update needed.\n"
|
||||
printf "Current List: $CURRENT_LIST\nNew List: $NEW_LIST_JSON\n"
|
||||
|
||||
# Since output can be KAFKA or LOGSTASH, we need to check if the policy set as default matches the value set in GLOBALS.pipeline and update if needed
|
||||
printf "Checking if the correct output policy is set as default\n"
|
||||
OUTPUT_DEFAULT=$(jq -r '.item.is_default' <<< $RAW_JSON)
|
||||
OUTPUT_DEFAULT_MONITORING=$(jq -r '.item.is_default_monitoring' <<< $RAW_JSON)
|
||||
if [[ "$OUTPUT_DEFAULT" = "false" || "$OUTPUT_DEFAULT_MONITORING" = "false" ]]; then
|
||||
printf "Default output policy needs to be updated.\n"
|
||||
{%- if GLOBALS.pipeline == "KAFKA" and 'gmd' in salt['pillar.get']('features', []) %}
|
||||
update_kafka_outputs
|
||||
{%- else %}
|
||||
update_logstash_outputs
|
||||
{%- endif %}
|
||||
else
|
||||
printf "Default output policy is set - no update needed.\n"
|
||||
fi
|
||||
exit 0
|
||||
else
|
||||
printf "\nHashes don't match - update needed.\n"
|
||||
printf "Current List: $CURRENT_LIST\nNew List: $NEW_LIST_JSON\n"
|
||||
{%- if GLOBALS.pipeline == "KAFKA" and 'gmd' in salt['pillar.get']('features', []) %}
|
||||
update_kafka_outputs
|
||||
{%- else %}
|
||||
update_logstash_outputs
|
||||
{%- endif %}
|
||||
fi
|
||||
|
||||
@@ -77,6 +77,11 @@ curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fl
|
||||
printf "\n\n"
|
||||
{%- endif %}
|
||||
|
||||
printf "\nCreate Kafka Output Config if node is not an Import or Eval install\n"
|
||||
{% if grains.role not in ['so-import', 'so-eval'] %}
|
||||
/usr/sbin/so-kafka-fleet-output-policy
|
||||
{% endif %}
|
||||
|
||||
# Add Manager Hostname & URL Base to Fleet Host URLs
|
||||
printf "\nAdd SO-Manager Fleet URL\n"
|
||||
if [ "{{ GLOBALS.hostname }}" = "{{ GLOBALS.url_base }}" ]; then
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-standalone', 'so-managersearch'] %}
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
# Check to make sure that Kibana API is up & ready
|
||||
RETURN_CODE=0
|
||||
wait_for_web_response "http://localhost:5601/api/fleet/settings" "fleet" 300 "curl -K /opt/so/conf/elasticsearch/curl.config"
|
||||
RETURN_CODE=$?
|
||||
|
||||
if [[ "$RETURN_CODE" != "0" ]]; then
|
||||
printf "Kibana API not accessible, can't setup Elastic Fleet output policy for Kafka..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs" | jq -r .items[].id)
|
||||
|
||||
if ! echo "$output" | grep -q "so-manager_kafka"; then
|
||||
KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt)
|
||||
KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key)
|
||||
KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
|
||||
KAFKA_OUTPUT_VERSION="2.6.0"
|
||||
JSON_STRING=$( jq -n \
|
||||
--arg KAFKACRT "$KAFKACRT" \
|
||||
--arg KAFKAKEY "$KAFKAKEY" \
|
||||
--arg KAFKACA "$KAFKACA" \
|
||||
--arg MANAGER_IP "{{ GLOBALS.manager_ip }}:9092" \
|
||||
--arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \
|
||||
'{ "name": "grid-kafka", "id": "so-manager_kafka", "type": "kafka", "hosts": [ $MANAGER_IP ], "is_default": false, "is_default_monitoring": false, "config_yaml": "", "ssl": { "certificate_authorities": [ $KAFKACA ], "certificate": $KAFKACRT, "key": $KAFKAKEY, "verification_mode": "full" }, "proxy_id": null, "client_id": "Elastic", "version": $KAFKA_OUTPUT_VERSION, "compression": "none", "auth_type": "ssl", "partition": "round_robin", "round_robin": { "group_events": 1 }, "topics":[{"topic":"%{[event.module]}-securityonion","when":{"type":"regexp","condition":"event.module:.+"}},{"topic":"default-securityonion"}], "headers": [ { "key": "", "value": "" } ], "timeout": 30, "broker_timeout": 30, "required_acks": 1 }'
|
||||
)
|
||||
curl -sK /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/outputs" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" -o /dev/null
|
||||
refresh_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs" | jq -r .items[].id)
|
||||
|
||||
if ! echo "$refresh_output" | grep -q "so-manager_kafka"; then
|
||||
echo -e "\nFailed to setup Elastic Fleet output policy for Kafka...\n"
|
||||
exit 1
|
||||
elif echo "$refresh_output" | grep -q "so-manager_kafka"; then
|
||||
echo -e "\nSuccessfully setup Elastic Fleet output policy for Kafka...\n"
|
||||
fi
|
||||
|
||||
elif echo "$output" | grep -q "so-manager_kafka"; then
|
||||
echo -e "\nElastic Fleet output policy for Kafka already exists...\n"
|
||||
fi
|
||||
{% else %}
|
||||
echo -e "\nNo update required...\n"
|
||||
{% endif %}
|
||||
@@ -4,7 +4,7 @@
|
||||
# Elastic License 2.0.
|
||||
|
||||
{% from 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states or sls in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
# Move our new CA over so Elastic and Logstash can use SSL with the internal CA
|
||||
|
||||
@@ -1,23 +1,37 @@
|
||||
{# 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 %}
|
||||
{% import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS with context %}
|
||||
|
||||
{% set HIGHLANDER = salt['pillar.get']('global:highlander', False) %}
|
||||
|
||||
{# ES_LOGSTASH_NODES is the same as LOGSTASH_NODES from logstash/map.jinja but heavynodes and fleet nodes are removed #}
|
||||
{% set ES_LOGSTASH_NODES = [] %}
|
||||
{% set node_data = salt['pillar.get']('logstash:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
{# this is a list of dicts containing hostname:ip for elasticsearch nodes that need to know about each other for cluster #}
|
||||
{% set ELASTICSEARCH_SEED_HOSTS = [] %}
|
||||
{% set node_data = salt['pillar.get']('elasticsearch:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
{% for node_type, node_details in node_data.items() | sort %}
|
||||
{% if node_type not in ['heavynode', 'fleet'] %}
|
||||
{% if node_type != 'heavynode' %}
|
||||
{% for hostname in node_data[node_type].keys() %}
|
||||
{% do ES_LOGSTASH_NODES.append({hostname:node_details[hostname].ip}) %}
|
||||
{% do ELASTICSEARCH_SEED_HOSTS.append({hostname:node_details[hostname].ip}) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# this is a list of dicts containing hostname:ip of all nodes running elasticsearch #}
|
||||
{% set ELASTICSEARCH_NODES = [] %}
|
||||
{% set node_data = salt['pillar.get']('elasticsearch:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
{% for node_type, node_details in node_data.items() %}
|
||||
{% for hostname in node_data[node_type].keys() %}
|
||||
{% do ELASTICSEARCH_NODES.append({hostname:node_details[hostname].ip}) %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% if grains.id.split('_') | last in ['manager','managersearch','standalone'] %}
|
||||
{% if ES_LOGSTASH_NODES | length > 1 %}
|
||||
{% if ELASTICSEARCH_SEED_HOSTS | length > 1 %}
|
||||
{% do ELASTICSEARCHDEFAULTS.elasticsearch.config.update({'discovery': {'seed_hosts': []}}) %}
|
||||
{% for NODE in ES_LOGSTASH_NODES %}
|
||||
{% for NODE in ELASTICSEARCH_SEED_HOSTS %}
|
||||
{% do ELASTICSEARCHDEFAULTS.elasticsearch.config.discovery.seed_hosts.append(NODE.keys()|first) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
@@ -170,6 +170,78 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
so-items:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
composed_of:
|
||||
- so-items-mappings
|
||||
index_patterns:
|
||||
- .items-default-**
|
||||
priority: 500
|
||||
template:
|
||||
mappings:
|
||||
date_detection: false
|
||||
settings:
|
||||
index:
|
||||
lifecycle:
|
||||
name: so-items-logs
|
||||
rollover_alias: ".items-default"
|
||||
routing:
|
||||
allocation:
|
||||
include:
|
||||
_tier_preference: "data_content"
|
||||
mapping:
|
||||
total_fields:
|
||||
limit: 10000
|
||||
number_of_replicas: 0
|
||||
number_of_shards: 1
|
||||
refresh_interval: 30s
|
||||
sort:
|
||||
field: '@timestamp'
|
||||
order: desc
|
||||
policy:
|
||||
phases:
|
||||
hot:
|
||||
actions:
|
||||
rollover:
|
||||
max_size: 50gb
|
||||
min_age: 0ms
|
||||
so-lists:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
composed_of:
|
||||
- so-lists-mappings
|
||||
index_patterns:
|
||||
- .lists-default-**
|
||||
priority: 500
|
||||
template:
|
||||
mappings:
|
||||
date_detection: false
|
||||
settings:
|
||||
index:
|
||||
lifecycle:
|
||||
name: so-lists-logs
|
||||
rollover_alias: ".lists-default"
|
||||
routing:
|
||||
allocation:
|
||||
include:
|
||||
_tier_preference: "data_content"
|
||||
mapping:
|
||||
total_fields:
|
||||
limit: 10000
|
||||
number_of_replicas: 0
|
||||
number_of_shards: 1
|
||||
refresh_interval: 30s
|
||||
sort:
|
||||
field: '@timestamp'
|
||||
order: desc
|
||||
policy:
|
||||
phases:
|
||||
hot:
|
||||
actions:
|
||||
rollover:
|
||||
max_size: 50gb
|
||||
min_age: 0ms
|
||||
so-case:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
@@ -224,7 +296,7 @@ elasticsearch:
|
||||
limit: 1500
|
||||
number_of_replicas: 0
|
||||
number_of_shards: 1
|
||||
refresh_interval: 30s
|
||||
refresh_interval: 1s
|
||||
sort:
|
||||
field: '@timestamp'
|
||||
order: desc
|
||||
@@ -11088,6 +11160,117 @@ elasticsearch:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
so-suricata_x_alerts:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
composed_of:
|
||||
- agent-mappings
|
||||
- dtc-agent-mappings
|
||||
- base-mappings
|
||||
- dtc-base-mappings
|
||||
- client-mappings
|
||||
- dtc-client-mappings
|
||||
- cloud-mappings
|
||||
- container-mappings
|
||||
- data_stream-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
|
||||
- network-mappings
|
||||
- dtc-network-mappings
|
||||
- observer-mappings
|
||||
- dtc-observer-mappings
|
||||
- orchestrator-mappings
|
||||
- organization-mappings
|
||||
- package-mappings
|
||||
- process-mappings
|
||||
- dtc-process-mappings
|
||||
- registry-mappings
|
||||
- related-mappings
|
||||
- rule-mappings
|
||||
- dtc-rule-mappings
|
||||
- server-mappings
|
||||
- service-mappings
|
||||
- dtc-service-mappings
|
||||
- source-mappings
|
||||
- dtc-source-mappings
|
||||
- pb-override-source-mappings
|
||||
- suricata-mappings
|
||||
- threat-mappings
|
||||
- tls-mappings
|
||||
- tracing-mappings
|
||||
- url-mappings
|
||||
- user_agent-mappings
|
||||
- dtc-user_agent-mappings
|
||||
- vulnerability-mappings
|
||||
- common-settings
|
||||
- common-dynamic-mappings
|
||||
data_stream: {}
|
||||
index_patterns:
|
||||
- logs-suricata.alerts-*
|
||||
priority: 500
|
||||
template:
|
||||
mappings:
|
||||
date_detection: false
|
||||
dynamic_templates:
|
||||
- strings_as_keyword:
|
||||
mapping:
|
||||
ignore_above: 1024
|
||||
type: keyword
|
||||
match_mapping_type: string
|
||||
settings:
|
||||
index:
|
||||
lifecycle:
|
||||
name: so-suricata.alerts-logs
|
||||
mapping:
|
||||
total_fields:
|
||||
limit: 5000
|
||||
number_of_replicas: 0
|
||||
number_of_shards: 1
|
||||
refresh_interval: 30s
|
||||
sort:
|
||||
field: '@timestamp'
|
||||
order: desc
|
||||
policy:
|
||||
phases:
|
||||
cold:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 0
|
||||
min_age: 60d
|
||||
delete:
|
||||
actions:
|
||||
delete: {}
|
||||
min_age: 365d
|
||||
hot:
|
||||
actions:
|
||||
rollover:
|
||||
max_age: 1d
|
||||
max_primary_shard_size: 50gb
|
||||
set_priority:
|
||||
priority: 100
|
||||
min_age: 0ms
|
||||
warm:
|
||||
actions:
|
||||
set_priority:
|
||||
priority: 50
|
||||
min_age: 30d
|
||||
so-syslog:
|
||||
index_sorting: false
|
||||
index_template:
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
{% if sls.split('.')[0] in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'docker/docker.map.jinja' import DOCKER %}
|
||||
{% from 'logstash/map.jinja' import LOGSTASH_NODES %}
|
||||
{% from 'elasticsearch/config.map.jinja' import ES_LOGSTASH_NODES %}
|
||||
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCH_NODES %}
|
||||
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCH_SEED_HOSTS %}
|
||||
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCHMERGED %}
|
||||
{% set TEMPLATES = salt['pillar.get']('elasticsearch:templates', {}) %}
|
||||
{% from 'elasticsearch/template.map.jinja' import ES_INDEX_SETTINGS %}
|
||||
@@ -27,7 +27,7 @@ so-elasticsearch:
|
||||
- sobridge:
|
||||
- ipv4_address: {{ DOCKER.containers['so-elasticsearch'].ip }}
|
||||
- extra_hosts:
|
||||
{% for node in LOGSTASH_NODES %}
|
||||
{% for node in ELASTICSEARCH_NODES %}
|
||||
{% for hostname, ip in node.items() %}
|
||||
- {{hostname}}:{{ip}}
|
||||
{% endfor %}
|
||||
@@ -38,7 +38,7 @@ so-elasticsearch:
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
- environment:
|
||||
{% if ES_LOGSTASH_NODES | length == 1 or GLOBALS.role == 'so-heavynode' %}
|
||||
{% if ELASTICSEARCH_SEED_HOSTS | length == 1 or GLOBALS.role == 'so-heavynode' %}
|
||||
- discovery.type=single-node
|
||||
{% endif %}
|
||||
- ES_JAVA_OPTS=-Xms{{ GLOBALS.elasticsearch.es_heap }} -Xmx{{ GLOBALS.elasticsearch.es_heap }} -Des.transport.cname_in_publish_address=true -Dlog4j2.formatMsgNoLookups=true
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
{ "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 } },
|
||||
{ "remove": { "field": [ "message2", "type", "fields", "category", "module", "dataset", "event.dataset_temp", "dataset_tag_temp", "module_temp" ], "ignore_missing": true, "ignore_failure": true } }
|
||||
],
|
||||
"on_failure": [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"description" : "suricata.alert",
|
||||
"processors" : [
|
||||
{ "set": { "field": "_index", "value": "logs-suricata.alerts-so" } },
|
||||
{ "set": { "field": "tags","value": "alert" }},
|
||||
{ "rename":{ "field": "message2.alert", "target_field": "rule", "ignore_failure": true } },
|
||||
{ "rename":{ "field": "rule.signature", "target_field": "rule.name", "ignore_failure": true } },
|
||||
|
||||
@@ -521,6 +521,7 @@ elasticsearch:
|
||||
so-endgame: *indexSettings
|
||||
so-idh: *indexSettings
|
||||
so-suricata: *indexSettings
|
||||
so-suricata_x_alerts: *indexSettings
|
||||
so-import: *indexSettings
|
||||
so-kratos: *indexSettings
|
||||
so-kismet: *indexSettings
|
||||
|
||||
@@ -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. #}
|
||||
|
||||
{% import_yaml 'elasticsearch/defaults.yaml' as ELASTICSEARCHDEFAULTS %}
|
||||
{% set DEFAULT_GLOBAL_OVERRIDES = ELASTICSEARCHDEFAULTS.elasticsearch.index_settings.pop('global_overrides') %}
|
||||
|
||||
@@ -17,10 +22,26 @@
|
||||
{% set ES_INDEX_SETTINGS = {} %}
|
||||
{% do ES_INDEX_SETTINGS_GLOBAL_OVERRIDES.update(salt['defaults.merge'](ES_INDEX_SETTINGS_GLOBAL_OVERRIDES, ES_INDEX_PILLAR, in_place=False)) %}
|
||||
{% for index, settings in ES_INDEX_SETTINGS_GLOBAL_OVERRIDES.items() %}
|
||||
{# if policy isn't defined in the original index settings, then dont merge policy from the global_overrides #}
|
||||
{# this will prevent so-elasticsearch-ilm-policy-load from trying to load policy on non ILM manged indices #}
|
||||
{% if not ES_INDEX_SETTINGS_ORIG[index].policy is defined and ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index].policy is defined %}
|
||||
{% do ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index].pop('policy') %}
|
||||
|
||||
{# prevent this action from being performed on custom defined indices. #}
|
||||
{# the custom defined index is not present in either of the dictionaries and fails to reder. #}
|
||||
{% if index in ES_INDEX_SETTINGS_ORIG and index in ES_INDEX_SETTINGS_GLOBAL_OVERRIDES %}
|
||||
|
||||
{# dont merge policy from the global_overrides if policy isn't defined in the original index settingss #}
|
||||
{# this will prevent so-elasticsearch-ilm-policy-load from trying to load policy on non ILM manged indices #}
|
||||
{% if not ES_INDEX_SETTINGS_ORIG[index].policy is defined and ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index].policy is defined %}
|
||||
{% do ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index].pop('policy') %}
|
||||
{% endif %}
|
||||
|
||||
{# this prevents and index from inderiting a policy phase from global overrides if it wasnt defined in the defaults. #}
|
||||
{% if ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index].policy is defined %}
|
||||
{% for phase in ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index].policy.phases.copy() %}
|
||||
{% if ES_INDEX_SETTINGS_ORIG[index].policy.phases[phase] is not defined %}
|
||||
{% do ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index].policy.phases.pop(phase) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if settings.index_template is defined %}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"template": {
|
||||
"mappings": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"binary": {
|
||||
"type": "binary"
|
||||
},
|
||||
"boolean": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"byte": {
|
||||
"type": "byte"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"date": {
|
||||
"type": "date"
|
||||
},
|
||||
"date_nanos": {
|
||||
"type": "date_nanos"
|
||||
},
|
||||
"date_range": {
|
||||
"type": "date_range"
|
||||
},
|
||||
"deserializer": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"double": {
|
||||
"type": "double"
|
||||
},
|
||||
"double_range": {
|
||||
"type": "double_range"
|
||||
},
|
||||
"float": {
|
||||
"type": "float"
|
||||
},
|
||||
"float_range": {
|
||||
"type": "float_range"
|
||||
},
|
||||
"geo_point": {
|
||||
"type": "geo_point"
|
||||
},
|
||||
"geo_shape": {
|
||||
"type": "geo_shape"
|
||||
},
|
||||
"half_float": {
|
||||
"type": "half_float"
|
||||
},
|
||||
"integer": {
|
||||
"type": "integer"
|
||||
},
|
||||
"integer_range": {
|
||||
"type": "integer_range"
|
||||
},
|
||||
"ip": {
|
||||
"type": "ip"
|
||||
},
|
||||
"ip_range": {
|
||||
"type": "ip_range"
|
||||
},
|
||||
"keyword": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"list_id": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"long": {
|
||||
"type": "long"
|
||||
},
|
||||
"long_range": {
|
||||
"type": "long_range"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"enabled": false
|
||||
},
|
||||
"serializer": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"shape": {
|
||||
"type": "shape"
|
||||
},
|
||||
"short": {
|
||||
"type": "short"
|
||||
},
|
||||
"text": {
|
||||
"type": "text"
|
||||
},
|
||||
"tie_breaker_id": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"updated_by": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"aliases": {}
|
||||
},
|
||||
"version": 2,
|
||||
"_meta": {
|
||||
"managed": true,
|
||||
"description": "default mappings for the .items index template installed by Kibana/Security"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"template": {
|
||||
"mappings": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"deserializer": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"immutable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"enabled": false
|
||||
},
|
||||
"name": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"serializer": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"tie_breaker_id": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"updated_by": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"version": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"aliases": {}
|
||||
},
|
||||
"version": 2,
|
||||
"_meta": {
|
||||
"managed": true,
|
||||
"description": "default mappings for the .lists index template installed by Kibana/Security"
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
# 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 %}
|
||||
{%- set node_data = salt['pillar.get']('logstash:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
{%- set node_data = salt['pillar.get']('elasticsearch:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
'so-elastic-fleet',
|
||||
'so-elastic-fleet-package-registry',
|
||||
'so-influxdb',
|
||||
'so-kafka',
|
||||
'so-kibana',
|
||||
'so-kratos',
|
||||
'so-logstash',
|
||||
@@ -80,6 +81,7 @@
|
||||
{% set NODE_CONTAINERS = [
|
||||
'so-logstash',
|
||||
'so-redis',
|
||||
'so-kafka'
|
||||
] %}
|
||||
|
||||
{% elif GLOBALS.role == 'so-idh' %}
|
||||
|
||||
@@ -90,12 +90,20 @@ firewall:
|
||||
tcp:
|
||||
- 8086
|
||||
udp: []
|
||||
kafka_controller:
|
||||
tcp:
|
||||
- 9093
|
||||
udp: []
|
||||
kafka_data:
|
||||
tcp:
|
||||
- 9092
|
||||
udp: []
|
||||
kibana:
|
||||
tcp:
|
||||
- 5601
|
||||
udp: []
|
||||
localrules:
|
||||
tcp:
|
||||
tcp:
|
||||
- 7788
|
||||
udp: []
|
||||
nginx:
|
||||
@@ -753,7 +761,6 @@ firewall:
|
||||
- beats_5044
|
||||
- beats_5644
|
||||
- beats_5056
|
||||
- redis
|
||||
- elasticsearch_node
|
||||
- elastic_agent_control
|
||||
- elastic_agent_data
|
||||
@@ -1267,35 +1274,51 @@ firewall:
|
||||
chain:
|
||||
DOCKER-USER:
|
||||
hostgroups:
|
||||
desktop:
|
||||
portgroups:
|
||||
- elastic_agent_data
|
||||
fleet:
|
||||
portgroups:
|
||||
- beats_5056
|
||||
- elastic_agent_data
|
||||
idh:
|
||||
portgroups:
|
||||
- elastic_agent_data
|
||||
sensor:
|
||||
portgroups:
|
||||
- beats_5044
|
||||
- beats_5644
|
||||
- elastic_agent_data
|
||||
searchnode:
|
||||
portgroups:
|
||||
- redis
|
||||
- beats_5644
|
||||
- elastic_agent_data
|
||||
standalone:
|
||||
portgroups:
|
||||
- redis
|
||||
- elastic_agent_data
|
||||
manager:
|
||||
portgroups:
|
||||
- elastic_agent_data
|
||||
managersearch:
|
||||
portgroups:
|
||||
- redis
|
||||
- beats_5644
|
||||
- elastic_agent_data
|
||||
self:
|
||||
portgroups:
|
||||
- redis
|
||||
- beats_5644
|
||||
- elastic_agent_data
|
||||
beats_endpoint:
|
||||
portgroups:
|
||||
- beats_5044
|
||||
beats_endpoint_ssl:
|
||||
portgroups:
|
||||
- beats_5644
|
||||
elastic_agent_endpoint:
|
||||
portgroups:
|
||||
- elastic_agent_data
|
||||
endgame:
|
||||
portgroups:
|
||||
- endgame
|
||||
receiver:
|
||||
portgroups: []
|
||||
customhostgroup0:
|
||||
portgroups: []
|
||||
customhostgroup1:
|
||||
|
||||
@@ -18,4 +18,28 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{# Only add Kafka firewall items when Kafka enabled #}
|
||||
{% set role = GLOBALS.role.split('-')[1] %}
|
||||
|
||||
{% if GLOBALS.pipeline == 'KAFKA' and role in ['manager', 'managersearch', 'standalone'] %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[role].portgroups.append('kafka_controller') %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.receiver.portgroups.append('kafka_controller') %}
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.pipeline == 'KAFKA' and role == 'receiver' %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.self.portgroups.append('kafka_controller') %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.standalone.portgroups.append('kafka_controller') %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.manager.portgroups.append('kafka_controller') %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.managersearch.portgroups.append('kafka_controller') %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.receiver.portgroups.append('kafka_controller') %}
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.pipeline == 'KAFKA' and role in ['manager', 'managersearch', 'standalone', 'receiver'] %}
|
||||
{% for r in ['manager', 'managersearch', 'standalone', 'receiver', 'fleet', 'idh', 'sensor', 'searchnode','heavynode', 'elastic_agent_endpoint', 'desktop'] %}
|
||||
{% if FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[r] is defined %}
|
||||
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[r].portgroups.append('kafka_data') %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% set FIREWALL_MERGED = salt['pillar.get']('firewall', FIREWALL_DEFAULT.firewall, merge=True) %}
|
||||
|
||||
@@ -120,6 +120,9 @@ firewall:
|
||||
influxdb:
|
||||
tcp: *tcpsettings
|
||||
udp: *udpsettings
|
||||
kafka:
|
||||
tcp: *tcpsettings
|
||||
udp: *udpsettings
|
||||
kibana:
|
||||
tcp: *tcpsettings
|
||||
udp: *udpsettings
|
||||
@@ -939,7 +942,6 @@ firewall:
|
||||
portgroups: *portgroupshost
|
||||
customhostgroup9:
|
||||
portgroups: *portgroupshost
|
||||
|
||||
idh:
|
||||
chain:
|
||||
DOCKER-USER:
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
global:
|
||||
pcapengine: STENO
|
||||
pcapengine: STENO
|
||||
pipeline: REDIS
|
||||
@@ -36,9 +36,10 @@ global:
|
||||
global: True
|
||||
advanced: True
|
||||
pipeline:
|
||||
description: Sets which pipeline technology for events to use. Currently only Redis is supported.
|
||||
description: Sets which pipeline technology for events to use. Currently only Redis is fully supported. Kafka is experimental and requires a Security Onion Pro license.
|
||||
regex: ^(REDIS|KAFKA)$
|
||||
regexFailureMessage: You must enter either REDIS or KAFKA.
|
||||
global: True
|
||||
readonly: True
|
||||
advanced: True
|
||||
repo_host:
|
||||
description: Specify the host where operating system packages will be served from.
|
||||
|
||||
@@ -33,6 +33,19 @@ idstools_sbin_jinja:
|
||||
- file_mode: 755
|
||||
- template: jinja
|
||||
|
||||
suricatacustomdirsfile:
|
||||
file.directory:
|
||||
- name: /nsm/rules/detect-suricata/custom_file
|
||||
- user: 939
|
||||
- group: 939
|
||||
- makedirs: True
|
||||
|
||||
suricatacustomdirsurl:
|
||||
file.directory:
|
||||
- name: /nsm/rules/detect-suricata/custom_temp
|
||||
- user: 939
|
||||
- group: 939
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{%- from 'vars/globals.map.jinja' import GLOBALS -%}
|
||||
{%- from 'idstools/map.jinja' import IDSTOOLSMERGED -%}
|
||||
{%- from 'soc/merged.map.jinja' import SOCMERGED -%}
|
||||
--suricata-version=6.0
|
||||
--merged=/opt/so/rules/nids/suri/all.rules
|
||||
--output=/nsm/rules/detect-suricata/custom_temp
|
||||
--local=/opt/so/rules/nids/suri/local.rules
|
||||
{%- if GLOBALS.md_engine == "SURICATA" %}
|
||||
--local=/opt/so/rules/nids/suri/extraction.rules
|
||||
@@ -10,8 +12,12 @@
|
||||
--disable=/opt/so/idstools/etc/disable.conf
|
||||
--enable=/opt/so/idstools/etc/enable.conf
|
||||
--modify=/opt/so/idstools/etc/modify.conf
|
||||
{%- if IDSTOOLSMERGED.config.urls | length > 0 %}
|
||||
{%- for URL in IDSTOOLSMERGED.config.urls %}
|
||||
--url={{ URL }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{%- if SOCMERGED.config.server.modules.suricataengine.customRulesets %}
|
||||
{%- for ruleset in SOCMERGED.config.server.modules.suricataengine.customRulesets %}
|
||||
{%- if 'url' in ruleset %}
|
||||
--url={{ ruleset.url }}
|
||||
{%- elif 'file' in ruleset %}
|
||||
--local={{ ruleset.file }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
@@ -11,8 +11,8 @@ if [[ ! "`pidof -x $(basename $0) -o %PPID`" ]]; then
|
||||
{%- set proxy = salt['pillar.get']('manager:proxy') %}
|
||||
{%- set noproxy = salt['pillar.get']('manager:no_proxy', '') %}
|
||||
|
||||
# Download the rules from the internet
|
||||
{%- if proxy %}
|
||||
# Download the rules from the internet
|
||||
export http_proxy={{ proxy }}
|
||||
export https_proxy={{ proxy }}
|
||||
export no_proxy="{{ noproxy }}"
|
||||
@@ -20,14 +20,12 @@ if [[ ! "`pidof -x $(basename $0) -o %PPID`" ]]; then
|
||||
|
||||
mkdir -p /nsm/rules/suricata
|
||||
chown -R socore:socore /nsm/rules/suricata
|
||||
{%- if not GLOBALS.airgap %}
|
||||
# Download the rules from the internet
|
||||
{%- if GLOBALS.airgap != 'True' %}
|
||||
{%- if IDSTOOLSMERGED.config.ruleset == 'ETOPEN' %}
|
||||
docker exec so-idstools idstools-rulecat -v --suricata-version 6.0 -o /nsm/rules/suricata/ --merged=/nsm/rules/suricata/emerging-all.rules --force
|
||||
{%- elif IDSTOOLSMERGED.config.ruleset == 'ETPRO' %}
|
||||
docker exec so-idstools idstools-rulecat -v --suricata-version 6.0 -o /nsm/rules/suricata/ --merged=/nsm/rules/suricata/emerging-all.rules --force --etpro={{ IDSTOOLSMERGED.config.oinkcode }}
|
||||
{%- elif IDSTOOLSMERGED.config.ruleset == 'TALOS' %}
|
||||
docker exec so-idstools idstools-rulecat -v --suricata-version 6.0 -o /nsm/rules/suricata/ --merged=/nsm/rules/suricata/emerging-all.rules --force --url=https://www.snort.org/rules/snortrules-snapshot-2983.tar.gz?oinkcode={{ IDSTOOLSMERGED.config.oinkcode }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
37
salt/kafka/ca.sls
Normal file
37
salt/kafka/ca.sls
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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 or sls in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% set KAFKATRUST = salt['pillar.get']('kafka:truststore') %}
|
||||
|
||||
kafkaconfdir:
|
||||
file.directory:
|
||||
- name: /opt/so/conf/kafka
|
||||
- user: 960
|
||||
- group: 960
|
||||
- makedirs: True
|
||||
|
||||
{% if GLOBALS.is_manager %}
|
||||
# Manager runs so-kafka-trust to create truststore for Kafka ssl communication
|
||||
kafka_truststore:
|
||||
cmd.script:
|
||||
- source: salt://kafka/tools/sbin_jinja/so-kafka-trust
|
||||
- template: jinja
|
||||
- cwd: /opt/so
|
||||
- defaults:
|
||||
GLOBALS: {{ GLOBALS }}
|
||||
KAFKATRUST: {{ KAFKATRUST }}
|
||||
{% endif %}
|
||||
|
||||
kafkacertz:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/kafka/kafka-truststore.jks
|
||||
- source: salt://kafka/files/kafka-truststore
|
||||
- user: 960
|
||||
- group: 931
|
||||
|
||||
{% endif %}
|
||||
85
salt/kafka/config.map.jinja
Normal file
85
salt/kafka/config.map.jinja
Normal file
@@ -0,0 +1,85 @@
|
||||
{# 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 'kafka/map.jinja' import KAFKAMERGED %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
{% set KAFKA_NODES_PILLAR = salt['pillar.get']('kafka:nodes') %}
|
||||
{% set KAFKA_PASSWORD = salt['pillar.get']('kafka:config:password') %}
|
||||
{% set KAFKA_TRUSTPASS = salt['pillar.get']('kafka:config:trustpass') %}
|
||||
|
||||
{# Create list of KRaft controllers #}
|
||||
{% set controllers = [] %}
|
||||
|
||||
{# Check for Kafka nodes with controller in process_x_roles #}
|
||||
{% for node in KAFKA_NODES_PILLAR %}
|
||||
{% if 'controller' in KAFKA_NODES_PILLAR[node].role %}
|
||||
{% do controllers.append(KAFKA_NODES_PILLAR[node].nodeid ~ "@" ~ node ~ ":9093") %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set kafka_controller_quorum_voters = ','.join(controllers) %}
|
||||
|
||||
{# By default all Kafka eligible nodes are given the role of broker, except for
|
||||
grid MANAGER (broker,controller) until overridden through SOC UI #}
|
||||
{% set node_type = salt['pillar.get']('kafka:nodes:'+ GLOBALS.hostname + ':role') %}
|
||||
|
||||
{# Generate server.properties for 'broker' , 'controller', 'broker,controller' node types
|
||||
anything above this line is a configuration needed for ALL Kafka nodes #}
|
||||
{% if node_type == 'broker' %}
|
||||
{% do KAFKAMERGED.config.broker.update({'advertised_x_listeners': 'BROKER://'+ GLOBALS.node_ip +':9092' }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'controller_x_quorum_x_voters': kafka_controller_quorum_voters }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'node_x_id': salt['pillar.get']('kafka:nodes:'+ GLOBALS.hostname +':nodeid') }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'ssl_x_keystore_x_password': KAFKA_PASSWORD }) %}
|
||||
|
||||
{# Nodes with only the 'broker' role need to have the below settings for communicating with controller nodes #}
|
||||
{% do KAFKAMERGED.config.broker.update({'controller_x_listener_x_names': KAFKAMERGED.config.controller.controller_x_listener_x_names }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({
|
||||
'listener_x_security_x_protocol_x_map': KAFKAMERGED.config.broker.listener_x_security_x_protocol_x_map
|
||||
+ ',' + KAFKAMERGED.config.controller.listener_x_security_x_protocol_x_map })
|
||||
%}
|
||||
{% endif %}
|
||||
|
||||
{% if node_type == 'controller' %}
|
||||
{% do KAFKAMERGED.config.controller.update({'controller_x_quorum_x_voters': kafka_controller_quorum_voters }) %}
|
||||
{% do KAFKAMERGED.config.controller.update({'node_x_id': salt['pillar.get']('kafka:nodes:'+ GLOBALS.hostname +':nodeid') }) %}
|
||||
{% do KAFKAMERGED.config.controller.update({'ssl_x_keystore_x_password': KAFKA_PASSWORD }) %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{# Kafka nodes of this type are not recommended for use outside of development / testing. #}
|
||||
{% if node_type == 'broker,controller' %}
|
||||
{% do KAFKAMERGED.config.broker.update({'advertised_x_listeners': 'BROKER://'+ GLOBALS.node_ip +':9092' }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'controller_x_listener_x_names': KAFKAMERGED.config.controller.controller_x_listener_x_names }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'controller_x_quorum_x_voters': kafka_controller_quorum_voters }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'node_x_id': salt['pillar.get']('kafka:nodes:'+ GLOBALS.hostname +':nodeid') }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'process_x_roles': 'broker,controller' }) %}
|
||||
{% do KAFKAMERGED.config.broker.update({'ssl_x_keystore_x_password': KAFKA_PASSWORD }) %}
|
||||
|
||||
{% do KAFKAMERGED.config.broker.update({
|
||||
'listeners': KAFKAMERGED.config.broker.listeners + ',' + KAFKAMERGED.config.controller.listeners })
|
||||
%}
|
||||
|
||||
{% do KAFKAMERGED.config.broker.update({
|
||||
'listener_x_security_x_protocol_x_map': KAFKAMERGED.config.broker.listener_x_security_x_protocol_x_map
|
||||
+ ',' + KAFKAMERGED.config.controller.listener_x_security_x_protocol_x_map })
|
||||
%}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{# Truststore config #}
|
||||
{% do KAFKAMERGED.config.broker.update({'ssl_x_truststore_x_password': KAFKA_TRUSTPASS }) %}
|
||||
{% do KAFKAMERGED.config.controller.update({'ssl_x_truststore_x_password': KAFKA_TRUSTPASS }) %}
|
||||
{% do KAFKAMERGED.config.client.update({'ssl_x_truststore_x_password': KAFKA_TRUSTPASS }) %}
|
||||
|
||||
{# Client properties stuff #}
|
||||
{% do KAFKAMERGED.config.client.update({'ssl_x_keystore_x_password': KAFKA_PASSWORD }) %}
|
||||
|
||||
{% if 'broker' in node_type %}
|
||||
{% set KAFKACONFIG = KAFKAMERGED.config.broker %}
|
||||
{% else %}
|
||||
{% set KAFKACONFIG = KAFKAMERGED.config.controller %}
|
||||
{% endif %}
|
||||
|
||||
{% set KAFKACLIENT = KAFKAMERGED.config.client %}
|
||||
84
salt/kafka/config.sls
Normal file
84
salt/kafka/config.sls
Normal file
@@ -0,0 +1,84 @@
|
||||
# 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 %}
|
||||
|
||||
kafka_group:
|
||||
group.present:
|
||||
- name: kafka
|
||||
- gid: 960
|
||||
|
||||
kafka_user:
|
||||
user.present:
|
||||
- name: kafka
|
||||
- uid: 960
|
||||
- gid: 960
|
||||
- home: /opt/so/conf/kafka
|
||||
- createhome: False
|
||||
|
||||
kafka_home_dir:
|
||||
file.absent:
|
||||
- name: /home/kafka
|
||||
|
||||
kafka_sbin_tools:
|
||||
file.recurse:
|
||||
- name: /usr/sbin
|
||||
- source: salt://kafka/tools/sbin
|
||||
- user: 960
|
||||
- group: 960
|
||||
- file_mode: 755
|
||||
|
||||
kafka_sbin_jinja_tools:
|
||||
file.recurse:
|
||||
- name: /usr/sbin
|
||||
- source: salt://kafka/tools/sbin_jinja
|
||||
- user: 960
|
||||
- group: 960
|
||||
- file_mode: 755
|
||||
- template: jinja
|
||||
- defaults:
|
||||
GLOBALS: {{ GLOBALS }}
|
||||
|
||||
kafka_log_dir:
|
||||
file.directory:
|
||||
- name: /opt/so/log/kafka
|
||||
- user: 960
|
||||
- group: 960
|
||||
- makedirs: True
|
||||
|
||||
kafka_data_dir:
|
||||
file.directory:
|
||||
- name: /nsm/kafka/data
|
||||
- user: 960
|
||||
- group: 960
|
||||
- makedirs: True
|
||||
|
||||
{% for sc in ['server', 'client'] %}
|
||||
kafka_kraft_{{sc}}_properties:
|
||||
file.managed:
|
||||
- source: salt://kafka/etc/{{sc}}.properties.jinja
|
||||
- name: /opt/so/conf/kafka/{{sc}}.properties
|
||||
- template: jinja
|
||||
- user: 960
|
||||
- group: 960
|
||||
- makedirs: True
|
||||
- show_changes: False
|
||||
{% endfor %}
|
||||
|
||||
reset_quorum_on_changes:
|
||||
cmd.run:
|
||||
- name: rm -f /nsm/kafka/data/__cluster_metadata-0/quorum-state
|
||||
- onchanges:
|
||||
- file: /opt/so/conf/kafka/server.properties
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
64
salt/kafka/defaults.yaml
Normal file
64
salt/kafka/defaults.yaml
Normal file
@@ -0,0 +1,64 @@
|
||||
kafka:
|
||||
enabled: False
|
||||
cluster_id:
|
||||
controllers:
|
||||
reset:
|
||||
logstash: []
|
||||
config:
|
||||
password:
|
||||
trustpass:
|
||||
broker:
|
||||
advertised_x_listeners:
|
||||
auto_x_create_x_topics_x_enable: true
|
||||
controller_x_quorum_x_voters:
|
||||
default_x_replication_x_factor: 1
|
||||
inter_x_broker_x_listener_x_name: BROKER
|
||||
listeners: BROKER://0.0.0.0:9092
|
||||
listener_x_security_x_protocol_x_map: BROKER:SSL
|
||||
log_x_dirs: /nsm/kafka/data
|
||||
log_x_retention_x_check_x_interval_x_ms: 300000
|
||||
log_x_retention_x_hours: 168
|
||||
log_x_segment_x_bytes: 1073741824
|
||||
node_x_id:
|
||||
num_x_io_x_threads: 8
|
||||
num_x_network_x_threads: 3
|
||||
num_x_partitions: 3
|
||||
num_x_recovery_x_threads_x_per_x_data_x_dir: 1
|
||||
offsets_x_topic_x_replication_x_factor: 1
|
||||
process_x_roles: broker
|
||||
socket_x_receive_x_buffer_x_bytes: 102400
|
||||
socket_x_request_x_max_x_bytes: 104857600
|
||||
socket_x_send_x_buffer_x_bytes: 102400
|
||||
ssl_x_keystore_x_location: /etc/pki/kafka.p12
|
||||
ssl_x_keystore_x_type: PKCS12
|
||||
ssl_x_keystore_x_password:
|
||||
ssl_x_truststore_x_location: /etc/pki/kafka-truststore.jks
|
||||
ssl_x_truststore_x_type: JKS
|
||||
ssl_x_truststore_x_password:
|
||||
transaction_x_state_x_log_x_min_x_isr: 1
|
||||
transaction_x_state_x_log_x_replication_x_factor: 1
|
||||
client:
|
||||
security_x_protocol: SSL
|
||||
ssl_x_truststore_x_location: /etc/pki/kafka-truststore.jks
|
||||
ssl_x_truststore_x_type: JKS
|
||||
ssl_x_truststore_x_password:
|
||||
ssl_x_keystore_x_location: /etc/pki/kafka.p12
|
||||
ssl_x_keystore_x_type: PKCS12
|
||||
ssl_x_keystore_x_password:
|
||||
controller:
|
||||
controller_x_listener_x_names: CONTROLLER
|
||||
controller_x_quorum_x_voters:
|
||||
listeners: CONTROLLER://0.0.0.0:9093
|
||||
listener_x_security_x_protocol_x_map: CONTROLLER:SSL
|
||||
log_x_dirs: /nsm/kafka/data
|
||||
log_x_retention_x_check_x_interval_x_ms: 300000
|
||||
log_x_retention_x_hours: 168
|
||||
log_x_segment_x_bytes: 1073741824
|
||||
node_x_id:
|
||||
process_x_roles: controller
|
||||
ssl_x_keystore_x_location: /etc/pki/kafka.p12
|
||||
ssl_x_keystore_x_type: PKCS12
|
||||
ssl_x_keystore_x_password:
|
||||
ssl_x_truststore_x_location: /etc/pki/kafka-truststore.jks
|
||||
ssl_x_truststore_x_type: JKS
|
||||
ssl_x_truststore_x_password:
|
||||
34
salt/kafka/disabled.sls
Normal file
34
salt/kafka/disabled.sls
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 'kafka/map.jinja' import KAFKAMERGED %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
so-kafka:
|
||||
docker_container.absent:
|
||||
- force: True
|
||||
|
||||
so-kafka_so-status.disabled:
|
||||
file.comment:
|
||||
- name: /opt/so/conf/so-status/so-status.conf
|
||||
- regex: ^so-kafka$
|
||||
- onlyif: grep -q '^so-kafka$' /opt/so/conf/so-status/so-status.conf
|
||||
|
||||
{% if GLOBALS.is_manager and KAFKAMERGED.enabled or GLOBALS.pipeline == "KAFKA" %}
|
||||
ensure_default_pipeline:
|
||||
cmd.run:
|
||||
- name: |
|
||||
/usr/sbin/so-yaml.py replace /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.enabled False;
|
||||
/usr/sbin/so-yaml.py replace /opt/so/saltstack/local/pillar/global/soc_global.sls global.pipeline REDIS
|
||||
{% endif %}
|
||||
|
||||
{# If Kafka has never been manually enabled, the 'Kafka' user does not exist. In this case certs for Kafka should not exist since they'll be owned by uid 960 #}
|
||||
{% for cert in ['kafka-client.crt','kafka-client.key','kafka.crt','kafka.key','kafka-logstash.crt','kafka-logstash.key','kafka-logstash.p12','kafka.p12','elasticfleet-kafka.p8'] %}
|
||||
check_kafka_cert_{{cert}}:
|
||||
file.absent:
|
||||
- name: /etc/pki/{{cert}}
|
||||
- onlyif: stat -c %U /etc/pki/{{cert}} | grep -q UNKNOWN
|
||||
- show_changes: False
|
||||
{% endfor %}
|
||||
90
salt/kafka/enabled.sls
Normal file
90
salt/kafka/enabled.sls
Normal file
@@ -0,0 +1,90 @@
|
||||
# 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.
|
||||
#
|
||||
# Note: Per the Elastic License 2.0, the second limitation states:
|
||||
#
|
||||
# "You may not move, change, disable, or circumvent the license key functionality
|
||||
# in the software, and you may not remove or obscure any functionality in the
|
||||
# software that is protected by the license key."
|
||||
|
||||
{% from 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls.split('.')[0] in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% from 'docker/docker.map.jinja' import DOCKER %}
|
||||
{% set KAFKANODES = salt['pillar.get']('kafka:nodes') %}
|
||||
{% if 'gmd' in salt['pillar.get']('features', []) %}
|
||||
|
||||
include:
|
||||
- kafka.ca
|
||||
- kafka.config
|
||||
- kafka.ssl
|
||||
- kafka.storage
|
||||
- kafka.sostatus
|
||||
|
||||
so-kafka:
|
||||
docker_container.running:
|
||||
- image: {{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-kafka:{{ GLOBALS.so_version }}
|
||||
- hostname: so-kafka
|
||||
- name: so-kafka
|
||||
- networks:
|
||||
- sobridge:
|
||||
- ipv4_address: {{ DOCKER.containers['so-kafka'].ip }}
|
||||
- user: kafka
|
||||
- environment:
|
||||
KAFKA_HEAP_OPTS: -Xmx2G -Xms1G
|
||||
KAFKA_OPTS: -javaagent:/opt/jolokia/agents/jolokia-agent-jvm-javaagent.jar=port=8778,host={{ DOCKER.containers['so-kafka'].ip }},policyLocation=file:/opt/jolokia/jolokia.xml
|
||||
- extra_hosts:
|
||||
{% for node in KAFKANODES %}
|
||||
- {{ node }}:{{ KAFKANODES[node].ip }}
|
||||
{% endfor %}
|
||||
{% if DOCKER.containers['so-kafka'].extra_hosts %}
|
||||
{% for XTRAHOST in DOCKER.containers['so-kafka'].extra_hosts %}
|
||||
- {{ XTRAHOST }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
- port_bindings:
|
||||
{% for BINDING in DOCKER.containers['so-kafka'].port_bindings %}
|
||||
- {{ BINDING }}
|
||||
{% endfor %}
|
||||
- binds:
|
||||
- /etc/pki/kafka.p12:/etc/pki/kafka.p12:ro
|
||||
- /opt/so/conf/kafka/kafka-truststore.jks:/etc/pki/kafka-truststore.jks:ro
|
||||
- /nsm/kafka/data/:/nsm/kafka/data/:rw
|
||||
- /opt/so/log/kafka:/opt/kafka/logs/:rw
|
||||
- /opt/so/conf/kafka/server.properties:/opt/kafka/config/kraft/server.properties:ro
|
||||
- /opt/so/conf/kafka/client.properties:/opt/kafka/config/kraft/client.properties
|
||||
- watch:
|
||||
{% for sc in ['server', 'client'] %}
|
||||
- file: kafka_kraft_{{sc}}_properties
|
||||
{% endfor %}
|
||||
- file: kafkacertz
|
||||
- require:
|
||||
- file: kafkacertz
|
||||
|
||||
delete_so-kafka_so-status.disabled:
|
||||
file.uncomment:
|
||||
- name: /opt/so/conf/so-status/so-status.conf
|
||||
- regex: ^so-kafka$
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_no_license_detected:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_no_license_detected
|
||||
- comment:
|
||||
- "Kafka for Guaranteed Message Delivery is a feature supported only for customers with a valid license.
|
||||
Contact Security Onion Solutions, LLC via our website at https://securityonionsolutions.com
|
||||
for more information about purchasing a license to enable this feature."
|
||||
include:
|
||||
- kafka.disabled
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
7
salt/kafka/etc/client.properties.jinja
Normal file
7
salt/kafka/etc/client.properties.jinja
Normal file
@@ -0,0 +1,7 @@
|
||||
{# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
Elastic License 2.0. #}
|
||||
|
||||
{% from 'kafka/config.map.jinja' import KAFKACLIENT -%}
|
||||
{{ KAFKACLIENT | yaml(False) | replace("_x_", ".") }}
|
||||
7
salt/kafka/etc/server.properties.jinja
Normal file
7
salt/kafka/etc/server.properties.jinja
Normal file
@@ -0,0 +1,7 @@
|
||||
{# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
Elastic License 2.0. #}
|
||||
|
||||
{% from 'kafka/config.map.jinja' import KAFKACONFIG -%}
|
||||
{{ KAFKACONFIG | yaml(False) | replace("_x_", ".") }}
|
||||
10
salt/kafka/files/managed_node_pillar.jinja
Normal file
10
salt/kafka/files/managed_node_pillar.jinja
Normal file
@@ -0,0 +1,10 @@
|
||||
kafka:
|
||||
nodes:
|
||||
{% for node, values in COMBINED_KAFKANODES.items() %}
|
||||
{{ node }}:
|
||||
ip: {{ values['ip'] }}
|
||||
nodeid: {{ values['nodeid'] }}
|
||||
{%- if values['role'] != none %}
|
||||
role: {{ values['role'] }}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
29
salt/kafka/init.sls
Normal file
29
salt/kafka/init.sls
Normal file
@@ -0,0 +1,29 @@
|
||||
# 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.
|
||||
#
|
||||
# Note: Per the Elastic License 2.0, the second limitation states:
|
||||
#
|
||||
# "You may not move, change, disable, or circumvent the license key functionality
|
||||
# in the software, and you may not remove or obscure any functionality in the
|
||||
# software that is protected by the license key."
|
||||
|
||||
{% from 'kafka/map.jinja' import KAFKAMERGED %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
include:
|
||||
{# Run kafka/nodes.sls before Kafka is enabled, so kafka nodes pillar is setup #}
|
||||
{% if grains.role in ['so-manager','so-managersearch', 'so-standalone'] %}
|
||||
- kafka.nodes
|
||||
{% endif %}
|
||||
{% if GLOBALS.pipeline == "KAFKA" and KAFKAMERGED.enabled %}
|
||||
{% if grains.role in ['so-manager', 'so-managersearch', 'so-standalone', 'so-receiver'] %}
|
||||
- kafka.enabled
|
||||
{# Searchnodes only run kafka.ssl state when Kafka is enabled #}
|
||||
{% elif grains.role == "so-searchnode" %}
|
||||
- kafka.ssl
|
||||
{% endif %}
|
||||
{% else %}
|
||||
- kafka.disabled
|
||||
{% endif %}
|
||||
10
salt/kafka/map.jinja
Normal file
10
salt/kafka/map.jinja
Normal file
@@ -0,0 +1,10 @@
|
||||
{# 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. #}
|
||||
|
||||
{# This is only used to determine if Kafka is enabled / disabled. Configuration is found in kafka/config.map.jinja #}
|
||||
{# kafka/config.map.jinja depends on there being a kafka nodes pillar being populated #}
|
||||
|
||||
{% import_yaml 'kafka/defaults.yaml' as KAFKADEFAULTS %}
|
||||
{% set KAFKAMERGED = salt['pillar.get']('kafka', KAFKADEFAULTS.kafka, merge=True) %}
|
||||
88
salt/kafka/nodes.map.jinja
Normal file
88
salt/kafka/nodes.map.jinja
Normal file
@@ -0,0 +1,88 @@
|
||||
{# 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. #}
|
||||
|
||||
{# USED TO GENERATE PILLAR/KAFKA/NODES.SLS. #}
|
||||
{% import_yaml 'kafka/defaults.yaml' as KAFKADEFAULTS %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
{% set process_x_roles = KAFKADEFAULTS.kafka.config.broker.process_x_roles %}
|
||||
|
||||
{% set current_kafkanodes = salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='G@role:so-manager or G@role:so-managersearch or G@role:so-standalone or G@role:so-receiver',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='compound') %}
|
||||
|
||||
{% set STORED_KAFKANODES = salt['pillar.get']('kafka:nodes', default=None) %}
|
||||
{% set KAFKA_CONTROLLERS_PILLAR = salt['pillar.get']('kafka:controllers', default=None) %}
|
||||
|
||||
{% set existing_ids = [] %}
|
||||
|
||||
{# Check STORED_KAFKANODES for existing kafka nodes and pull their IDs so they are not reused across the grid #}
|
||||
{% if STORED_KAFKANODES != none %}
|
||||
{% for node, values in STORED_KAFKANODES.items() %}
|
||||
{% if values.get('nodeid') %}
|
||||
{% do existing_ids.append(values['nodeid']) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{# Create list of possible node ids #}
|
||||
{% set all_possible_ids = range(1, 2000)|list %}
|
||||
|
||||
{# Create list of available node ids by looping through all_possible_ids and ensuring it isn't in existing_ids #}
|
||||
{% set available_ids = [] %}
|
||||
{% for id in all_possible_ids %}
|
||||
{% if id not in existing_ids %}
|
||||
{% do available_ids.append(id) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# Collect kafka eligible nodes and check if they're already in STORED_KAFKANODES to avoid potentially reassigning a nodeid #}
|
||||
{% set NEW_KAFKANODES = {} %}
|
||||
{% for minionid, ip in current_kafkanodes.items() %}
|
||||
{% set hostname = minionid.split('_')[0] %}
|
||||
{% if not STORED_KAFKANODES or hostname not in STORED_KAFKANODES %}
|
||||
{% set new_id = available_ids.pop(0) %}
|
||||
{% do NEW_KAFKANODES.update({hostname: {'nodeid': new_id, 'ip': ip[0], 'role': process_x_roles }}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# Combine STORED_KAFKANODES and NEW_KAFKANODES for writing to the pillar/kafka/nodes.sls #}
|
||||
{% set COMBINED_KAFKANODES = {} %}
|
||||
{% for node, details in NEW_KAFKANODES.items() %}
|
||||
{% do COMBINED_KAFKANODES.update({node: details}) %}
|
||||
{% endfor %}
|
||||
{% if STORED_KAFKANODES != none %}
|
||||
{% for node, details in STORED_KAFKANODES.items() %}
|
||||
{% do COMBINED_KAFKANODES.update({node: details}) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{# Update the process_x_roles value for any host in the kafka_controllers_pillar configured from SOC UI #}
|
||||
{% set ns = namespace(has_controller=false) %}
|
||||
{% if KAFKA_CONTROLLERS_PILLAR != none %}
|
||||
{% set KAFKA_CONTROLLERS_PILLAR_LIST = KAFKA_CONTROLLERS_PILLAR.split(',') %}
|
||||
{% for hostname in KAFKA_CONTROLLERS_PILLAR_LIST %}
|
||||
{% if hostname in COMBINED_KAFKANODES %}
|
||||
{% do COMBINED_KAFKANODES[hostname].update({'role': 'controller'}) %}
|
||||
{% set ns.has_controller = true %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for hostname in COMBINED_KAFKANODES %}
|
||||
{% if hostname not in KAFKA_CONTROLLERS_PILLAR_LIST %}
|
||||
{% do COMBINED_KAFKANODES[hostname].update({'role': 'broker'}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{# If the kafka_controllers_pillar is NOT empty check that atleast one node contains the controller role.
|
||||
otherwise default to GLOBALS.manager having broker,controller role #}
|
||||
{% if not ns.has_controller %}
|
||||
{% do COMBINED_KAFKANODES[GLOBALS.manager].update({'role': 'broker,controller'}) %}
|
||||
{% endif %}
|
||||
{# If kafka_controllers_pillar is empty, default to having grid manager as 'broker,controller'
|
||||
so there is always atleast 1 controller in the cluster #}
|
||||
{% else %}
|
||||
{% do COMBINED_KAFKANODES[GLOBALS.manager].update({'role': 'broker,controller'}) %}
|
||||
{% endif %}
|
||||
18
salt/kafka/nodes.sls
Normal file
18
salt/kafka/nodes.sls
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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 'kafka/nodes.map.jinja' import COMBINED_KAFKANODES %}
|
||||
{% set kafka_cluster_id = salt['pillar.get']('kafka:cluster_id', default=None) %}
|
||||
|
||||
{# Write Kafka pillar, so all grid members have access to nodeid of other kafka nodes and their roles #}
|
||||
write_kafka_pillar_yaml:
|
||||
file.managed:
|
||||
- name: /opt/so/saltstack/local/pillar/kafka/nodes.sls
|
||||
- mode: 644
|
||||
- user: socore
|
||||
- source: salt://kafka/files/managed_node_pillar.jinja
|
||||
- template: jinja
|
||||
- context:
|
||||
COMBINED_KAFKANODES: {{ COMBINED_KAFKANODES }}
|
||||
9
salt/kafka/reset.sls
Normal file
9
salt/kafka/reset.sls
Normal file
@@ -0,0 +1,9 @@
|
||||
# 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.
|
||||
|
||||
wipe_kafka_data:
|
||||
file.absent:
|
||||
- name: /nsm/kafka/data/
|
||||
- force: True
|
||||
229
salt/kafka/soc_kafka.yaml
Normal file
229
salt/kafka/soc_kafka.yaml
Normal file
@@ -0,0 +1,229 @@
|
||||
kafka:
|
||||
enabled:
|
||||
description: Set to True to enable Kafka. To avoid grid problems, do not enable Kafka until the related configuration is in place. Requires a valid Security Onion license key.
|
||||
helpLink: kafka.html
|
||||
cluster_id:
|
||||
description: The ID of the Kafka cluster.
|
||||
readonly: True
|
||||
advanced: True
|
||||
sensitive: True
|
||||
helpLink: kafka.html
|
||||
controllers:
|
||||
description: A comma-separated list of hostnames that will act as Kafka controllers. These hosts will be responsible for managing the Kafka cluster. Note that only manager and receiver nodes are eligible to run Kafka. This configuration needs to be set before enabling Kafka. Failure to do so may result in Kafka topics becoming unavailable requiring manual intervention to restore functionality or reset Kafka, either of which can result in data loss.
|
||||
forcedType: string
|
||||
helpLink: kafka.html
|
||||
reset:
|
||||
description: Disable and reset the Kafka cluster. This will remove all Kafka data including logs that may have not yet been ingested into Elasticsearch and reverts the grid to using REDIS as the global pipeline. This is useful when testing different Kafka configurations such as rearranging Kafka brokers / controllers allowing you to reset the cluster rather than manually fixing any issues arising from attempting to reassign a Kafka broker into a controller. Enter 'YES_RESET_KAFKA' and submit to disable and reset Kafka. Make any configuration changes required and re-enable Kafka when ready. This action CANNOT be reversed.
|
||||
advanced: True
|
||||
helpLink: kafka.html
|
||||
logstash:
|
||||
description: By default logstash is disabled when Kafka is enabled. This option allows you to specify any hosts you would like to re-enable logstash on alongside Kafka.
|
||||
forcedType: "[]string"
|
||||
multiline: True
|
||||
advanced: True
|
||||
helpLink: kafka.html
|
||||
config:
|
||||
password:
|
||||
description: The password used for the Kafka certificates.
|
||||
readonly: True
|
||||
sensitive: True
|
||||
helpLink: kafka.html
|
||||
trustpass:
|
||||
description: The password used for the Kafka truststore.
|
||||
readonly: True
|
||||
sensitive: True
|
||||
helpLink: kafka.html
|
||||
broker:
|
||||
advertised_x_listeners:
|
||||
description: Specify the list of listeners (hostname and port) that Kafka brokers provide to clients for communication.
|
||||
title: advertised.listeners
|
||||
helpLink: kafka.html
|
||||
auto_x_create_x_topics_x_enable:
|
||||
description: Enable the auto creation of topics.
|
||||
title: auto.create.topics.enable
|
||||
forcedType: bool
|
||||
helpLink: kafka.html
|
||||
default_x_replication_x_factor:
|
||||
description: The default replication factor for automatically created topics. This value must be less than the amount of brokers in the cluster. Hosts specified in controllers should not be counted towards total broker count.
|
||||
title: default.replication.factor
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
inter_x_broker_x_listener_x_name:
|
||||
description: The name of the listener used for inter-broker communication.
|
||||
title: inter.broker.listener.name
|
||||
helpLink: kafka.html
|
||||
listeners:
|
||||
description: Set of URIs that is listened on and the listener names in a comma-seperated list.
|
||||
helpLink: kafka.html
|
||||
listener_x_security_x_protocol_x_map:
|
||||
description: Comma-seperated mapping of listener name and security protocols.
|
||||
title: listener.security.protocol.map
|
||||
helpLink: kafka.html
|
||||
log_x_dirs:
|
||||
description: Where Kafka logs are stored within the Docker container.
|
||||
title: log.dirs
|
||||
helpLink: kafka.html
|
||||
log_x_retention_x_check_x_interval_x_ms:
|
||||
description: Frequency at which log files are checked if they are qualified for deletion.
|
||||
title: log.retention.check.interval.ms
|
||||
helpLink: kafka.html
|
||||
log_x_retention_x_hours:
|
||||
description: How long, in hours, a log file is kept.
|
||||
title: log.retention.hours
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
log_x_segment_x_bytes:
|
||||
description: The maximum allowable size for a log file.
|
||||
title: log.segment.bytes
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
num_x_io_x_threads:
|
||||
description: The number of threads used by Kafka.
|
||||
title: num.io.threads
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
num_x_network_x_threads:
|
||||
description: The number of threads used for network communication.
|
||||
title: num.network.threads
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
num_x_partitions:
|
||||
description: The number of log partitions assigned per topic.
|
||||
title: num.partitions
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
num_x_recovery_x_threads_x_per_x_data_x_dir:
|
||||
description: The number of threads used for log recuperation at startup and purging at shutdown. This ammount of threads is used per data directory.
|
||||
title: num.recovery.threads.per.data.dir
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
offsets_x_topic_x_replication_x_factor:
|
||||
description: The offsets topic replication factor.
|
||||
title: offsets.topic.replication.factor
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
process_x_roles:
|
||||
description: The role performed by Kafka brokers.
|
||||
title: process.roles
|
||||
readonly: True
|
||||
helpLink: kafka.html
|
||||
socket_x_receive_x_buffer_x_bytes:
|
||||
description: Size, in bytes of the SO_RCVBUF buffer. A value of -1 will use the OS default.
|
||||
title: socket.receive.buffer.bytes
|
||||
#forcedType: int - soc needs to allow -1 as an int before we can use this
|
||||
helpLink: kafka.html
|
||||
socket_x_request_x_max_x_bytes:
|
||||
description: The maximum bytes allowed for a request to the socket.
|
||||
title: socket.request.max.bytes
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
socket_x_send_x_buffer_x_bytes:
|
||||
description: Size, in bytes of the SO_SNDBUF buffer. A value of -1 will use the OS default.
|
||||
title: socket.send.buffer.byte
|
||||
#forcedType: int - soc needs to allow -1 as an int before we can use this
|
||||
helpLink: kafka.html
|
||||
ssl_x_keystore_x_location:
|
||||
description: The key store file location within the Docker container.
|
||||
title: ssl.keystore.location
|
||||
helpLink: kafka.html
|
||||
ssl_x_keystore_x_password:
|
||||
description: The key store file password. Invalid for PEM format.
|
||||
title: ssl.keystore.password
|
||||
sensitive: True
|
||||
helpLink: kafka.html
|
||||
ssl_x_keystore_x_type:
|
||||
description: The key store file format.
|
||||
title: ssl.keystore.type
|
||||
regex: ^(JKS|PKCS12|PEM)$
|
||||
helpLink: kafka.html
|
||||
ssl_x_truststore_x_location:
|
||||
description: The trust store file location within the Docker container.
|
||||
title: ssl.truststore.location
|
||||
helpLink: kafka.html
|
||||
ssl_x_truststore_x_type:
|
||||
description: The trust store file format.
|
||||
title: ssl.truststore.type
|
||||
helpLink: kafka.html
|
||||
ssl_x_truststore_x_password:
|
||||
description: The trust store file password. If null, the trust store file is still use, but integrity checking is disabled. Invalid for PEM format.
|
||||
title: ssl.truststore.password
|
||||
sensitive: True
|
||||
helpLink: kafka.html
|
||||
transaction_x_state_x_log_x_min_x_isr:
|
||||
description: Overrides min.insync.replicas for the transaction topic. When a producer configures acks to "all" (or "-1"), this setting determines the minimum number of replicas required to acknowledge a write as successful. Failure to meet this minimum triggers an exception (either NotEnoughReplicas or NotEnoughReplicasAfterAppend). When used in conjunction, min.insync.replicas and acks enable stronger durability guarantees. For instance, creating a topic with a replication factor of 3, setting min.insync.replicas to 2, and using acks of "all" ensures that the producer raises an exception if a majority of replicas fail to receive a write.
|
||||
title: transaction.state.log.min.isr
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
transaction_x_state_x_log_x_replication_x_factor:
|
||||
description: Set the replication factor higher for the transaction topic to ensure availability. Internal topic creation will not proceed until the cluster size satisfies this replication factor prerequisite.
|
||||
title: transaction.state.log.replication.factor
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
client:
|
||||
security_x_protocol:
|
||||
description: 'Broker communication protocol. Options are: SASL_SSL, PLAINTEXT, SSL, SASL_PLAINTEXT'
|
||||
title: security.protocol
|
||||
regex: ^(SASL_SSL|PLAINTEXT|SSL|SASL_PLAINTEXT)
|
||||
helpLink: kafka.html
|
||||
ssl_x_keystore_x_location:
|
||||
description: The key store file location within the Docker container.
|
||||
title: ssl.keystore.location
|
||||
helpLink: kafka.html
|
||||
ssl_x_keystore_x_password:
|
||||
description: The key store file password. Invalid for PEM format.
|
||||
title: ssl.keystore.password
|
||||
sensitive: True
|
||||
helpLink: kafka.html
|
||||
ssl_x_keystore_x_type:
|
||||
description: The key store file format.
|
||||
title: ssl.keystore.type
|
||||
regex: ^(JKS|PKCS12|PEM)$
|
||||
helpLink: kafka.html
|
||||
ssl_x_truststore_x_location:
|
||||
description: The trust store file location within the Docker container.
|
||||
title: ssl.truststore.location
|
||||
helpLink: kafka.html
|
||||
ssl_x_truststore_x_type:
|
||||
description: The trust store file format.
|
||||
title: ssl.truststore.type
|
||||
helpLink: kafka.html
|
||||
ssl_x_truststore_x_password:
|
||||
description: The trust store file password. If null, the trust store file is still use, but integrity checking is disabled. Invalid for PEM format.
|
||||
title: ssl.truststore.password
|
||||
sensitive: True
|
||||
helpLink: kafka.html
|
||||
controller:
|
||||
controller_x_listener_x_names:
|
||||
description: Set listeners used by the controller in a comma-seperated list.
|
||||
title: controller.listener.names
|
||||
helpLink: kafka.html
|
||||
listeners:
|
||||
description: Set of URIs that is listened on and the listener names in a comma-seperated list.
|
||||
helpLink: kafka.html
|
||||
listener_x_security_x_protocol_x_map:
|
||||
description: Comma-seperated mapping of listener name and security protocols.
|
||||
title: listener.security.protocol.map
|
||||
helpLink: kafka.html
|
||||
log_x_dirs:
|
||||
description: Where Kafka logs are stored within the Docker container.
|
||||
title: log.dirs
|
||||
helpLink: kafka.html
|
||||
log_x_retention_x_check_x_interval_x_ms:
|
||||
description: Frequency at which log files are checked if they are qualified for deletion.
|
||||
title: log.retention.check.interval.ms
|
||||
helpLink: kafka.html
|
||||
log_x_retention_x_hours:
|
||||
description: How long, in hours, a log file is kept.
|
||||
title: log.retention.hours
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
log_x_segment_x_bytes:
|
||||
description: The maximum allowable size for a log file.
|
||||
title: log.segment.bytes
|
||||
forcedType: int
|
||||
helpLink: kafka.html
|
||||
process_x_roles:
|
||||
description: The role performed by controller node.
|
||||
title: process.roles
|
||||
readonly: True
|
||||
helpLink: kafka.html
|
||||
21
salt/kafka/sostatus.sls
Normal file
21
salt/kafka/sostatus.sls
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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 %}
|
||||
|
||||
append_so-kafka_so-status.conf:
|
||||
file.append:
|
||||
- name: /opt/so/conf/so-status/so-status.conf
|
||||
- text: so-kafka
|
||||
- unless: grep -q so-kafka /opt/so/conf/so-status/so-status.conf
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
201
salt/kafka/ssl.sls
Normal file
201
salt/kafka/ssl.sls
Normal file
@@ -0,0 +1,201 @@
|
||||
# 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 or sls in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% 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] %}
|
||||
|
||||
{% if GLOBALS.pipeline == "KAFKA" %}
|
||||
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone'] %}
|
||||
kafka_client_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/kafka-client.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/kafka-client.key') -%}
|
||||
- prereq:
|
||||
- x509: /etc/pki/kafka-client.crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
kafka_client_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/kafka-client.crt
|
||||
- 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_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
kafka_client_key_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/kafka-client.key
|
||||
- mode: 640
|
||||
- user: 960
|
||||
- group: 939
|
||||
|
||||
kafka_client_crt_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/kafka-client.crt
|
||||
- mode: 640
|
||||
- user: 960
|
||||
- group: 939
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch','so-receiver', 'so-standalone'] %}
|
||||
kafka_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/kafka.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/kafka.key') -%}
|
||||
- prereq:
|
||||
- x509: /etc/pki/kafka.crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
kafka_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/kafka.crt
|
||||
- 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_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
cmd.run:
|
||||
- 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
|
||||
- name: /etc/pki/kafka.key
|
||||
- mode: 640
|
||||
- user: 960
|
||||
- group: 939
|
||||
|
||||
kafka_crt_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/kafka.crt
|
||||
- mode: 640
|
||||
- user: 960
|
||||
- group: 939
|
||||
|
||||
kafka_pkcs12_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/kafka.p12
|
||||
- mode: 640
|
||||
- user: 960
|
||||
- group: 939
|
||||
{% 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'] %}
|
||||
kafka_logstash_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/kafka-logstash.key
|
||||
- keysize: 4096
|
||||
- backup: True
|
||||
- new: True
|
||||
{% if salt['file.file_exists']('/etc/pki/kafka-logstash.key') -%}
|
||||
- prereq:
|
||||
- x509: /etc/pki/kafka-logstash.crt
|
||||
{%- endif %}
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
kafka_logstash_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/kafka-logstash.crt
|
||||
- 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_valid: 820
|
||||
- backup: True
|
||||
- timeout: 30
|
||||
- retry:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
cmd.run:
|
||||
- name: "/usr/bin/openssl pkcs12 -inkey /etc/pki/kafka-logstash.key -in /etc/pki/kafka-logstash.crt -export -out /etc/pki/kafka-logstash.p12 -nodes -passout pass:{{ kafka_password }}"
|
||||
- onchanges:
|
||||
- x509: /etc/pki/kafka-logstash.key
|
||||
|
||||
kafka_logstash_key_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/kafka-logstash.key
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
kafka_logstash_crt_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/kafka-logstash.crt
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
kafka_logstash_pkcs12_perms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
- name: /etc/pki/kafka-logstash.p12
|
||||
- mode: 640
|
||||
- user: 931
|
||||
- group: 939
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
30
salt/kafka/storage.sls
Normal file
30
salt/kafka/storage.sls
Normal file
@@ -0,0 +1,30 @@
|
||||
# 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 %}
|
||||
{% set kafka_cluster_id = salt['pillar.get']('kafka:cluster_id') %}
|
||||
|
||||
{# Initialize kafka storage if it doesn't already exist. Just looking for meta.properties in /nsm/kafka/data #}
|
||||
{% if not salt['file.file_exists']('/nsm/kafka/data/meta.properties') %}
|
||||
kafka_storage_init:
|
||||
cmd.run:
|
||||
- name: |
|
||||
docker run -v /nsm/kafka/data:/nsm/kafka/data -v /opt/so/conf/kafka/server.properties:/opt/kafka/config/kraft/newserver.properties --name so-kafkainit --user root --entrypoint /opt/kafka/bin/kafka-storage.sh {{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-kafka:{{ GLOBALS.so_version }} format -t {{ kafka_cluster_id }} -c /opt/kafka/config/kraft/newserver.properties
|
||||
kafka_rm_kafkainit:
|
||||
cmd.run:
|
||||
- name: |
|
||||
docker rm so-kafkainit
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
test.fail_without_changes:
|
||||
- name: {{sls}}_state_not_allowed
|
||||
|
||||
{% endif %}
|
||||
47
salt/kafka/tools/sbin/so-kafka-cli
Normal file
47
salt/kafka/tools/sbin/so-kafka-cli
Normal file
@@ -0,0 +1,47 @@
|
||||
#! /bin/bash
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
if [ -z "$NOROOT" ]; then
|
||||
# Check for prerequisites
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run using sudo!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
function usage() {
|
||||
echo -e "\nUsage: $0 <script> [options]"
|
||||
echo ""
|
||||
echo "Available scripts:"
|
||||
show_available_kafka_cli_tools
|
||||
}
|
||||
|
||||
function show_available_kafka_cli_tools(){
|
||||
docker exec so-kafka ls /opt/kafka/bin | grep kafka
|
||||
}
|
||||
|
||||
if [ -z $1 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
available_tools=$(show_available_kafka_cli_tools)
|
||||
script_exists=false
|
||||
|
||||
for script in $available_tools; do
|
||||
if [ "$script" == "$1" ]; then
|
||||
script_exists=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$script_exists" == true ]; then
|
||||
docker exec so-kafka /opt/kafka/bin/$1 "${@:2}"
|
||||
else
|
||||
echo -e "\nInvalid script: $1"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
87
salt/kafka/tools/sbin/so-kafka-config-update
Normal file
87
salt/kafka/tools/sbin/so-kafka-config-update
Normal file
@@ -0,0 +1,87 @@
|
||||
#! /bin/bash
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
if [ -z "$NOROOT" ]; then
|
||||
# Check for prerequisites
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run using sudo!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
usage() {
|
||||
cat <<USAGE_EOF
|
||||
|
||||
Usage: $0 <operation> [parameters]
|
||||
|
||||
Where <operation> is one of the following:
|
||||
|
||||
topic-partitions: Increase the number of partitions for a Kafka topic
|
||||
Required arguments: topic-partitions <topic name> <# partitions>
|
||||
Example: $0 topic-partitions suricata-topic 6
|
||||
|
||||
list-topics: List of Kafka topics
|
||||
Example: $0 list-topics
|
||||
|
||||
USAGE_EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ $# -lt 1 || $1 == --help || $1 == -h ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
kafka_client_config="/opt/kafka/config/kraft/client.properties"
|
||||
|
||||
too_few_arguments() {
|
||||
echo -e "\nMissing one or more required arguments!\n"
|
||||
usage
|
||||
}
|
||||
|
||||
get_kafka_brokers() {
|
||||
brokers_cache="/opt/so/state/kafka_brokers"
|
||||
broker_port="9092"
|
||||
if [[ ! -f "$brokers_cache" ]] || [[ $(find "/$brokers_cache" -mmin +120) ]]; then
|
||||
echo "Refreshing Kafka brokers list"
|
||||
salt-call pillar.get kafka:nodes --out=json | jq -r --arg broker_port "$broker_port" '.local | to_entries[] | select(.value.role | contains("broker")) | "\(.value.ip):\($broker_port)"' | paste -sd "," - > "$brokers_cache"
|
||||
else
|
||||
echo "Using cached Kafka brokers list"
|
||||
fi
|
||||
brokers=$(cat "$brokers_cache")
|
||||
}
|
||||
|
||||
increase_topic_partitions() {
|
||||
get_kafka_brokers
|
||||
command=$(so-kafka-cli kafka-topics.sh --bootstrap-server $brokers --command-config $kafka_client_config --alter --topic $topic --partitions $partition_count)
|
||||
if $command; then
|
||||
echo -e "Successfully increased the number of partitions for topic $topic to $partition_count\n"
|
||||
so-kafka-cli kafka-topics.sh --bootstrap-server $brokers --command-config $kafka_client_config --describe --topic $topic
|
||||
fi
|
||||
}
|
||||
|
||||
get_kafka_topics_list() {
|
||||
get_kafka_brokers
|
||||
so-kafka-cli kafka-topics.sh --bootstrap-server $brokers --command-config $kafka_client_config --exclude-internal --list | sort
|
||||
}
|
||||
|
||||
operation=$1
|
||||
case "${operation}" in
|
||||
"topic-partitions")
|
||||
if [[ $# -lt 3 ]]; then
|
||||
too_few_arguments
|
||||
fi
|
||||
topic=$2
|
||||
partition_count=$3
|
||||
increase_topic_partitions
|
||||
;;
|
||||
"list-topics")
|
||||
get_kafka_topics_list
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
13
salt/kafka/tools/sbin_jinja/so-kafka-trust
Normal file
13
salt/kafka/tools/sbin_jinja/so-kafka-trust
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
{% set TRUSTPASS = salt['pillar.get']('kafka:config:trustpass') %}
|
||||
|
||||
if [ ! -f /opt/so/saltstack/local/salt/kafka/files/kafka-truststore ]; then
|
||||
docker run -v /etc/pki/ca.crt:/etc/pki/ca.crt --name so-kafkatrust --user root --entrypoint /opt/java/openjdk/bin/keytool {{ GLOBALS.registry_host }}:5000/{{ GLOBALS.image_repo }}/so-kafka:{{ GLOBALS.so_version }} -import -file /etc/pki/ca.crt -alias SOS -keystore /etc/pki/kafka-truststore -storepass {{ TRUSTPASS }} -storetype jks -noprompt
|
||||
docker cp so-kafkatrust:/etc/pki/kafka-truststore /opt/so/saltstack/local/salt/kafka/files/kafka-truststore
|
||||
docker rm so-kafkatrust
|
||||
fi
|
||||
@@ -7,9 +7,7 @@ logstash:
|
||||
- search
|
||||
receiver:
|
||||
- receiver
|
||||
heavynode:
|
||||
- manager
|
||||
- search
|
||||
heavynode: []
|
||||
searchnode:
|
||||
- search
|
||||
manager:
|
||||
@@ -27,7 +25,7 @@ logstash:
|
||||
- so/0011_input_endgame.conf
|
||||
- so/0012_input_elastic_agent.conf.jinja
|
||||
- so/0013_input_lumberjack_fleet.conf
|
||||
- so/9999_output_redis.conf.jinja
|
||||
- so/9999_output_redis.conf.jinja
|
||||
receiver:
|
||||
- so/0011_input_endgame.conf
|
||||
- so/0012_input_elastic_agent.conf.jinja
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
include:
|
||||
{% if GLOBALS.role not in ['so-receiver','so-fleet'] %}
|
||||
- elasticsearch.ca
|
||||
{% endif %}
|
||||
{# Kafka ca runs on nodes that can run logstash for Kafka input / output. Only when Kafka is global pipeline #}
|
||||
{% if GLOBALS.role in ['so-searchnode', 'so-manager', 'so-managersearch', 'so-receiver', 'so-standalone'] and GLOBALS.pipeline == 'KAFKA' %}
|
||||
- kafka.ca
|
||||
- kafka.ssl
|
||||
{% endif %}
|
||||
- logstash.config
|
||||
- logstash.sostatus
|
||||
@@ -75,10 +80,14 @@ so-logstash:
|
||||
{% else %}
|
||||
- /etc/pki/tls/certs/intca.crt:/usr/share/filebeat/ca.crt:ro
|
||||
{% endif %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone', 'so-import', 'so-heavynode', 'so-searchnode'] %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone', 'so-import', 'so-heavynode', 'so-searchnode' ] %}
|
||||
- /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 %}
|
||||
{% if GLOBALS.pipeline == "KAFKA" and GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone', 'so-searchnode'] %}
|
||||
- /etc/pki/kafka-logstash.p12:/usr/share/logstash/kafka-logstash.p12:ro
|
||||
- /opt/so/conf/kafka/kafka-truststore.jks:/etc/pki/kafka-truststore.jks:ro
|
||||
{% endif %}
|
||||
{% if GLOBALS.role == 'so-eval' %}
|
||||
- /nsm/zeek:/nsm/zeek:ro
|
||||
- /nsm/suricata:/suricata:ro
|
||||
@@ -102,6 +111,9 @@ so-logstash:
|
||||
- file: ls_pipeline_{{assigned_pipeline}}_{{CONFIGFILE.split('.')[0] | replace("/","_") }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% if GLOBALS.pipeline == 'KAFKA' and GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone', 'so-searchnode'] %}
|
||||
- file: kafkacertz
|
||||
{% endif %}
|
||||
- require:
|
||||
{% if grains['role'] in ['so-manager', 'so-managersearch', 'so-standalone', 'so-import', 'so-heavynode', 'so-receiver'] %}
|
||||
- x509: etc_filebeat_crt
|
||||
@@ -115,6 +127,9 @@ so-logstash:
|
||||
- file: cacertz
|
||||
- file: capemz
|
||||
{% endif %}
|
||||
{% if GLOBALS.pipeline == 'KAFKA' and GLOBALS.role in ['so-manager', 'so-managersearch', 'so-standalone', 'so-searchnode'] %}
|
||||
- file: kafkacertz
|
||||
{% endif %}
|
||||
|
||||
delete_so-logstash_so-status.disabled:
|
||||
file.uncomment:
|
||||
|
||||
@@ -6,24 +6,40 @@
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% import_yaml 'logstash/defaults.yaml' as LOGSTASH_DEFAULTS %}
|
||||
{% set LOGSTASH_MERGED = salt['pillar.get']('logstash', LOGSTASH_DEFAULTS.logstash, merge=True) %}
|
||||
{% set KAFKA_LOGSTASH = salt['pillar.get']('kafka:logstash', []) %}
|
||||
|
||||
{% set REDIS_NODES = [] %}
|
||||
{# LOGSTASH_NODES is the same as ES_LOGSTASH_NODES from elasticsearch/config.map.jinja but heavynodes are present #}
|
||||
{# used to store the redis nodes that logstash needs to know about to pull from the queue #}
|
||||
{% set LOGSTASH_REDIS_NODES = [] %}
|
||||
{# stores all logstash nodes #}
|
||||
{% set LOGSTASH_NODES = [] %}
|
||||
{% set node_data = salt['pillar.get']('logstash:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
{% set logstash_node_data = salt['pillar.get']('logstash:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
{% set redis_node_data = salt['pillar.get']('redis:nodes', {GLOBALS.role.split('-')[1]: {GLOBALS.hostname: {'ip': GLOBALS.node_ip}}}) %}
|
||||
|
||||
{% for node_type, node_details in node_data.items() | sort %}
|
||||
{% for node_type, node_details in redis_node_data.items() | sort %}
|
||||
{% if GLOBALS.role in ['so-searchnode', 'so-standalone', 'so-managersearch', 'so-fleet'] %}
|
||||
{% if node_type in ['manager', 'managersearch', 'standalone', 'receiver' ] %}
|
||||
{% for hostname in node_data[node_type].keys() %}
|
||||
{% do REDIS_NODES.append({hostname:node_details[hostname].ip}) %}
|
||||
{% for hostname in redis_node_data[node_type].keys() %}
|
||||
{% do LOGSTASH_REDIS_NODES.append({hostname:node_details[hostname].ip}) %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% do REDIS_NODES.append({GLOBALS.hostname:GLOBALS.node_ip}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for hostname in node_data[node_type].keys() %}
|
||||
{% for node_type, node_details in logstash_node_data.items() | sort %}
|
||||
{% for hostname in logstash_node_data[node_type].keys() %}
|
||||
{% do LOGSTASH_NODES.append({hostname:node_details[hostname].ip}) %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{# Append Kafka input pipeline when Kafka is enabled #}
|
||||
{% if GLOBALS.pipeline == 'KAFKA' %}
|
||||
{% do LOGSTASH_MERGED.defined_pipelines.search.remove('so/0900_input_redis.conf.jinja') %}
|
||||
{% do LOGSTASH_MERGED.defined_pipelines.search.append('so/0800_input_kafka.conf.jinja') %}
|
||||
{% do LOGSTASH_MERGED.defined_pipelines.manager.append('so/0800_input_kafka.conf.jinja') %}
|
||||
{# Disable logstash on manager & receiver nodes unless it has an override configured #}
|
||||
{% if not KAFKA_LOGSTASH %}
|
||||
{% if GLOBALS.role in ['so-manager', 'so-receiver'] and GLOBALS.hostname not in KAFKA_LOGSTASH %}
|
||||
{% do LOGSTASH_MERGED.update({'enabled': False}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,39 @@
|
||||
{%- set kafka_password = salt['pillar.get']('kafka:config:password') %}
|
||||
{%- set kafka_trustpass = salt['pillar.get']('kafka:config:trustpass') %}
|
||||
{%- set kafka_brokers = salt['pillar.get']('kafka:nodes', {}) %}
|
||||
{%- set brokers = [] %}
|
||||
|
||||
{%- if kafka_brokers %}
|
||||
{%- for key, values in kafka_brokers.items() %}
|
||||
{%- if 'broker' in values['role'] %}
|
||||
{%- do brokers.append(key ~ ':9092') %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- set bootstrap_servers = ','.join(brokers) %}
|
||||
|
||||
input {
|
||||
kafka {
|
||||
codec => json
|
||||
topics_pattern => '.*-securityonion$'
|
||||
group_id => 'searchnodes'
|
||||
consumer_threads => 3
|
||||
client_id => '{{ GLOBALS.hostname }}'
|
||||
security_protocol => 'SSL'
|
||||
bootstrap_servers => '{{ bootstrap_servers }}'
|
||||
ssl_keystore_location => '/usr/share/logstash/kafka-logstash.p12'
|
||||
ssl_keystore_password => '{{ kafka_password }}'
|
||||
ssl_keystore_type => 'PKCS12'
|
||||
ssl_truststore_location => '/etc/pki/kafka-truststore.jks'
|
||||
ssl_truststore_password => '{{ kafka_trustpass }}'
|
||||
decorate_events => true
|
||||
tags => [ "elastic-agent", "input-{{ GLOBALS.hostname}}", "kafka" ]
|
||||
}
|
||||
}
|
||||
filter {
|
||||
if ![metadata] {
|
||||
mutate {
|
||||
rename => { "@metadata" => "metadata" }
|
||||
}
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
@@ -1,8 +1,8 @@
|
||||
{%- from 'logstash/map.jinja' import REDIS_NODES with context %}
|
||||
{%- from 'logstash/map.jinja' import LOGSTASH_REDIS_NODES with context %}
|
||||
{%- set REDIS_PASS = salt['pillar.get']('redis:config:requirepass') %}
|
||||
|
||||
{%- for index in range(REDIS_NODES|length) %}
|
||||
{%- for host in REDIS_NODES[index] %}
|
||||
{%- for index in range(LOGSTASH_REDIS_NODES|length) %}
|
||||
{%- for host in LOGSTASH_REDIS_NODES[index] %}
|
||||
input {
|
||||
redis {
|
||||
host => '{{ host }}'
|
||||
|
||||
@@ -2,4 +2,6 @@ manager:
|
||||
reposync:
|
||||
enabled: True
|
||||
hour: 3
|
||||
minute: 0
|
||||
minute: 0
|
||||
additionalCA: ''
|
||||
insecureSkipVerify: False
|
||||
|
||||
@@ -73,6 +73,15 @@ manager_sbin:
|
||||
- exclude_pat:
|
||||
- "*_test.py"
|
||||
|
||||
manager_sbin_jinja:
|
||||
file.recurse:
|
||||
- name: /usr/sbin/
|
||||
- source: salt://manager/tools/sbin_jinja/
|
||||
- user: socore
|
||||
- group: socore
|
||||
- file_mode: 755
|
||||
- template: jinja
|
||||
|
||||
so-repo-file:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/reposync/repodownload.conf
|
||||
|
||||
7
salt/manager/map.jinja
Normal file
7
salt/manager/map.jinja
Normal file
@@ -0,0 +1,7 @@
|
||||
{# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
Elastic License 2.0. #}
|
||||
|
||||
{% import_yaml 'manager/defaults.yaml' as MANAGERDEFAULTS %}
|
||||
{% set MANAGERMERGED = salt['pillar.get']('manager', MANAGERDEFAULTS.manager, merge=True) %}
|
||||
@@ -7,7 +7,7 @@ manager:
|
||||
hour:
|
||||
description: The hour of the day in which the repo sync takes place.
|
||||
global: True
|
||||
helpLink: soup.html
|
||||
helpLink: soup.html
|
||||
minute:
|
||||
description: The minute within the hour to run the repo sync.
|
||||
global: True
|
||||
@@ -16,11 +16,24 @@ manager:
|
||||
description: Enable elastalert 1=enabled 0=disabled.
|
||||
global: True
|
||||
helpLink: elastalert.html
|
||||
no_proxy:
|
||||
description: String of hosts to ignore the proxy settings for.
|
||||
no_proxy:
|
||||
description: String of hosts to ignore the proxy settings for.
|
||||
global: True
|
||||
helpLink: proxy.html
|
||||
proxy:
|
||||
description: Proxy server to use for updates.
|
||||
global: True
|
||||
helpLink: proxy.html
|
||||
additionalCA:
|
||||
description: Additional CA certificates to trust in PEM format.
|
||||
global: True
|
||||
advanced: True
|
||||
multiline: True
|
||||
forcedType: string
|
||||
helpLink: proxy.html
|
||||
insecureSkipVerify:
|
||||
description: Disable TLS verification for outgoing requests. This will make your installation less secure to MITM attacks. Recommended only for debugging purposes.
|
||||
advanced: True
|
||||
forcedType: bool
|
||||
global: True
|
||||
helpLink: proxy.html
|
||||
|
||||
0
salt/manager/tools/sbin/so-elasticagent-status
Normal file → Executable file
0
salt/manager/tools/sbin/so-elasticagent-status
Normal file → Executable file
@@ -112,8 +112,8 @@ function testMinion() {
|
||||
result=$?
|
||||
|
||||
# If this so-minion script is not running on the given minion ID, run so-test remotely on the sensor as well
|
||||
local_id=$(lookup_grain id)
|
||||
if [[ ! "$local_id" =~ "${MINION_ID}_" ]]; then
|
||||
local_id=$(lookup_grain id)
|
||||
if [[ ! "$local_id" =~ "${MINION_ID}_" && "$local_id" != "${MINION_ID}" ]]; then
|
||||
salt "$MINION_ID" cmd.run 'so-test'
|
||||
result=$?
|
||||
fi
|
||||
@@ -528,7 +528,6 @@ function createHEAVYNODE() {
|
||||
pcapspace
|
||||
add_elasticsearch_to_minion
|
||||
add_elastic_agent_to_minion
|
||||
add_logstash_to_minion
|
||||
add_sensor_to_minion
|
||||
add_strelka_to_minion
|
||||
add_redis_to_minion
|
||||
@@ -604,6 +603,10 @@ function updateMineAndApplyStates() {
|
||||
if [[ "$NODETYPE" == "SEARCHNODE" || "$NODETYPE" == "HEAVYNODE" ]]; then
|
||||
salt-run state.orch orch.container_download pillar="{'setup': {'newnode': $MINION_ID }}" > /dev/null 2>&1 &
|
||||
fi
|
||||
if [[ "$NODETYPE" == "RECEIVER" ]]; then
|
||||
# Setup nodeid for Kafka
|
||||
salt-call state.apply kafka.nodes queue=True
|
||||
fi
|
||||
# $MINIONID is the minion id of the manager and $MINION_ID is the target node or the node being configured
|
||||
salt-run state.orch orch.deploy_newnode pillar="{'setup': {'manager': $MINIONID, 'newnode': $MINION_ID }}" > /dev/null 2>&1 &
|
||||
}
|
||||
|
||||
0
salt/manager/tools/sbin/so-repo-sync
Normal file → Executable file
0
salt/manager/tools/sbin/so-repo-sync
Normal file → Executable file
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
so-user add --email $1
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
so-user disable --email $1
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
so-user enable --email $1
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
so-user list
|
||||
@@ -14,19 +14,20 @@ lockFile = "/tmp/so-yaml.lock"
|
||||
|
||||
|
||||
def showUsage(args):
|
||||
print('Usage: {} <COMMAND> <YAML_FILE> [ARGS...]'.format(sys.argv[0]))
|
||||
print(' General commands:')
|
||||
print(' append - Append a list item to a yaml key, if it exists and is a list. Requires KEY and LISTITEM args.')
|
||||
print(' add - Add a new key and set its value. Fails if key already exists. Requires KEY and VALUE args.')
|
||||
print(' remove - Removes a yaml key, if it exists. Requires KEY arg.')
|
||||
print(' replace - Replaces (or adds) a new key and set its value. Requires KEY and VALUE args.')
|
||||
print(' help - Prints this usage information.')
|
||||
print('')
|
||||
print(' Where:')
|
||||
print(' YAML_FILE - Path to the file that will be modified. Ex: /opt/so/conf/service/conf.yaml')
|
||||
print(' KEY - YAML key, does not support \' or " characters at this time. Ex: level1.level2')
|
||||
print(' VALUE - Value to set for a given key')
|
||||
print(' LISTITEM - Item to append to a given key\'s list value')
|
||||
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('', 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', file=sys.stderr)
|
||||
print(' LISTITEM - Item to append to a given key\'s list value', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -38,7 +39,7 @@ def loadYaml(filename):
|
||||
|
||||
def writeYaml(filename, content):
|
||||
file = open(filename, "w")
|
||||
return yaml.dump(content, file)
|
||||
return yaml.safe_dump(content, file)
|
||||
|
||||
|
||||
def appendItem(content, key, listItem):
|
||||
@@ -49,15 +50,15 @@ def appendItem(content, key, listItem):
|
||||
try:
|
||||
content[key].append(listItem)
|
||||
except AttributeError:
|
||||
print("The existing value for the given key is not a list. No action was taken on the file.")
|
||||
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.")
|
||||
print("The key provided does not exist. No action was taken on the file.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def convertType(value):
|
||||
if len(value) > 0 and (not value.startswith("0") or len(value) == 1):
|
||||
if isinstance(value, str) and len(value) > 0 and (not value.startswith("0") or len(value) == 1):
|
||||
if "." in value:
|
||||
try:
|
||||
value = float(value)
|
||||
@@ -83,7 +84,7 @@ def append(args):
|
||||
if len(args) != 3:
|
||||
print('Missing filename, key arg, or list item to append', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
@@ -112,7 +113,7 @@ def add(args):
|
||||
if len(args) != 3:
|
||||
print('Missing filename, key arg, and/or value', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
@@ -137,7 +138,7 @@ def remove(args):
|
||||
if len(args) != 2:
|
||||
print('Missing filename or key arg', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
@@ -153,7 +154,7 @@ def replace(args):
|
||||
if len(args) != 3:
|
||||
print('Missing filename, key arg, and/or value', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
@@ -167,6 +168,32 @@ def replace(args):
|
||||
return 0
|
||||
|
||||
|
||||
def getKeyValue(content, key):
|
||||
pieces = key.split(".", 1)
|
||||
if len(pieces) > 1 and pieces[0] in content:
|
||||
return getKeyValue(content[pieces[0]], pieces[1])
|
||||
return content.get(key, None)
|
||||
|
||||
|
||||
def get(args):
|
||||
if len(args) != 2:
|
||||
print('Missing filename or key arg', file=sys.stderr)
|
||||
showUsage(None)
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
key = args[1]
|
||||
|
||||
content = loadYaml(filename)
|
||||
output = getKeyValue(content, key)
|
||||
if output is None:
|
||||
print("Not found", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
print(yaml.safe_dump(output))
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
|
||||
@@ -178,6 +205,7 @@ def main():
|
||||
"help": showUsage,
|
||||
"add": add,
|
||||
"append": append,
|
||||
"get": get,
|
||||
"remove": remove,
|
||||
"replace": replace,
|
||||
}
|
||||
@@ -195,11 +223,11 @@ def main():
|
||||
break
|
||||
except Exception:
|
||||
if lockAttempts == 1:
|
||||
print("Waiting for lock file to be released from another process...")
|
||||
print("Waiting for lock file to be released from another process...", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
if lockAttempts == maxAttempts:
|
||||
print("Lock file (" + lockFile + ") could not be created; proceeding without lock.")
|
||||
print("Lock file (" + lockFile + ") could not be created; proceeding without lock.", file=sys.stderr)
|
||||
|
||||
cmd = commands.get(args[0], showUsage)
|
||||
code = cmd(args[1:])
|
||||
|
||||
@@ -15,40 +15,40 @@ class TestRemove(unittest.TestCase):
|
||||
|
||||
def test_main_missing_input(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called_once_with(1)
|
||||
self.assertIn(mock_stdout.getvalue(), "Usage:")
|
||||
self.assertIn("Usage:", mock_stderr.getvalue())
|
||||
|
||||
def test_main_help_locked(self):
|
||||
filename = "/tmp/so-yaml.lock"
|
||||
file = open(filename, "w")
|
||||
file.write = "fake lock file"
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
with patch('time.sleep', new=MagicMock()) as mock_sleep:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
mock_sleep.assert_called_with(2)
|
||||
self.assertIn(mock_stdout.getvalue(), "Usage:")
|
||||
self.assertIn("Usage:", mock_stderr.getvalue())
|
||||
|
||||
def test_main_help(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertIn(mock_stdout.getvalue(), "Usage:")
|
||||
self.assertIn("Usage:", mock_stderr.getvalue())
|
||||
|
||||
def test_remove_missing_arg(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.remove(["file"])
|
||||
sysmock.assert_called()
|
||||
self.assertIn(mock_stdout.getvalue(), "Missing filename or key arg\n")
|
||||
self.assertIn("Missing filename or key arg\n", mock_stderr.getvalue())
|
||||
|
||||
def test_remove(self):
|
||||
filename = "/tmp/so-yaml_test-remove.yaml"
|
||||
@@ -97,7 +97,7 @@ class TestRemove(unittest.TestCase):
|
||||
|
||||
def test_remove_missing_args(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
filename = "/tmp/so-yaml_test-remove.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: abc }, key2: false}")
|
||||
@@ -112,15 +112,15 @@ class TestRemove(unittest.TestCase):
|
||||
expected = "{key1: { child1: 123, child2: abc }, key2: false}"
|
||||
self.assertEqual(actual, expected)
|
||||
sysmock.assert_called_once_with(1)
|
||||
self.assertIn(mock_stdout.getvalue(), "Missing filename or key arg\n")
|
||||
self.assertIn("Missing filename or key arg\n", mock_stderr.getvalue())
|
||||
|
||||
def test_append_missing_arg(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.append(["file", "key"])
|
||||
sysmock.assert_called()
|
||||
self.assertIn(mock_stdout.getvalue(), "Missing filename, key arg, or list item to append\n")
|
||||
self.assertIn("Missing filename, key arg, or list item to append\n", mock_stderr.getvalue())
|
||||
|
||||
def test_append(self):
|
||||
filename = "/tmp/so-yaml_test-remove.yaml"
|
||||
@@ -173,11 +173,11 @@ class TestRemove(unittest.TestCase):
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "append", filename, "key4", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual(mock_stdout.getvalue(), "The key provided does not exist. No action was taken on the file.\n")
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_append_key_noexist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-append.yaml"
|
||||
@@ -186,11 +186,11 @@ class TestRemove(unittest.TestCase):
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "append", filename, "key1.child2.deep3", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual(mock_stdout.getvalue(), "The key provided does not exist. No action was taken on the file.\n")
|
||||
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
|
||||
|
||||
def test_append_key_nonlist(self):
|
||||
filename = "/tmp/so-yaml_test-append.yaml"
|
||||
@@ -199,11 +199,11 @@ class TestRemove(unittest.TestCase):
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "append", filename, "key1", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual(mock_stdout.getvalue(), "The existing value for the given key is not a list. No action was taken on the file.\n")
|
||||
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_append_key_nonlist_deep(self):
|
||||
filename = "/tmp/so-yaml_test-append.yaml"
|
||||
@@ -212,11 +212,11 @@ class TestRemove(unittest.TestCase):
|
||||
file.close()
|
||||
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "append", filename, "key1.child2.deep1", "h"]
|
||||
soyaml.main()
|
||||
sysmock.assert_called()
|
||||
self.assertEqual(mock_stdout.getvalue(), "The existing value for the given key is not a list. No action was taken on the file.\n")
|
||||
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_add_key(self):
|
||||
content = {}
|
||||
@@ -244,11 +244,11 @@ class TestRemove(unittest.TestCase):
|
||||
|
||||
def test_add_missing_arg(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.add(["file", "key"])
|
||||
sysmock.assert_called()
|
||||
self.assertIn(mock_stdout.getvalue(), "Missing filename, key arg, and/or value\n")
|
||||
self.assertIn("Missing filename, key arg, and/or value\n", mock_stderr.getvalue())
|
||||
|
||||
def test_add(self):
|
||||
filename = "/tmp/so-yaml_test-add.yaml"
|
||||
@@ -296,11 +296,11 @@ class TestRemove(unittest.TestCase):
|
||||
|
||||
def test_replace_missing_arg(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stdout:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
sys.argv = ["cmd", "help"]
|
||||
soyaml.replace(["file", "key"])
|
||||
sysmock.assert_called()
|
||||
self.assertIn(mock_stdout.getvalue(), "Missing filename, key arg, and/or value\n")
|
||||
self.assertIn("Missing filename, key arg, and/or value\n", mock_stderr.getvalue())
|
||||
|
||||
def test_replace(self):
|
||||
filename = "/tmp/so-yaml_test-add.yaml"
|
||||
@@ -360,3 +360,77 @@ class TestRemove(unittest.TestCase):
|
||||
self.assertEqual(soyaml.convertType("false"), False)
|
||||
self.assertEqual(soyaml.convertType("FALSE"), False)
|
||||
self.assertEqual(soyaml.convertType(""), "")
|
||||
|
||||
def test_get_int(self):
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
filename = "/tmp/so-yaml_test-get.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45 } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
result = soyaml.get([filename, "key1.child2.deep1"])
|
||||
self.assertEqual(result, 0)
|
||||
self.assertIn("45\n...", mock_stdout.getvalue())
|
||||
|
||||
def test_get_str(self):
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
filename = "/tmp/so-yaml_test-get.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: \"hello\" } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
result = soyaml.get([filename, "key1.child2.deep1"])
|
||||
self.assertEqual(result, 0)
|
||||
self.assertIn("hello\n...", mock_stdout.getvalue())
|
||||
|
||||
def test_get_list(self):
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
filename = "/tmp/so-yaml_test-get.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: \"hello\" } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
result = soyaml.get([filename, "key3"])
|
||||
self.assertEqual(result, 0)
|
||||
self.assertIn("- e\n- f\n- g\n", mock_stdout.getvalue())
|
||||
|
||||
def test_get_dict(self):
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
filename = "/tmp/so-yaml_test-get.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: \"hello\" } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
result = soyaml.get([filename, "key1"])
|
||||
self.assertEqual(result, 0)
|
||||
self.assertIn("child1: 123\nchild2:\n deep1: hello\n", mock_stdout.getvalue())
|
||||
|
||||
def test_get_missing(self):
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
filename = "/tmp/so-yaml_test-get.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45 } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
result = soyaml.get([filename, "key1.child2.deep3"])
|
||||
self.assertEqual(result, 2)
|
||||
self.assertEqual("", mock_stdout.getvalue())
|
||||
|
||||
def test_get_missing_parent(self):
|
||||
with patch('sys.stdout', new=StringIO()) as mock_stdout:
|
||||
filename = "/tmp/so-yaml_test-get.yaml"
|
||||
file = open(filename, "w")
|
||||
file.write("{key1: { child1: 123, child2: { deep1: 45 } }, key2: false, key3: [e,f,g]}")
|
||||
file.close()
|
||||
|
||||
result = soyaml.get([filename, "key1.child3.deep3"])
|
||||
self.assertEqual(result, 2)
|
||||
self.assertEqual("", mock_stdout.getvalue())
|
||||
|
||||
def test_get_usage(self):
|
||||
with patch('sys.exit', new=MagicMock()) as sysmock:
|
||||
with patch('sys.stderr', new=StringIO()) as mock_stderr:
|
||||
result = soyaml.get([])
|
||||
self.assertEqual(result, 1)
|
||||
self.assertIn("Missing filename or key arg", mock_stderr.getvalue())
|
||||
sysmock.assert_called_once_with(1)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# 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.
|
||||
|
||||
@@ -19,6 +19,9 @@ SOUP_LOG=/root/soup.log
|
||||
WHATWOULDYOUSAYYAHDOHERE=soup
|
||||
whiptail_title='Security Onion UPdater'
|
||||
NOTIFYCUSTOMELASTICCONFIG=false
|
||||
# used to display messages to the user at the end of soup
|
||||
declare -a FINAL_MESSAGE_QUEUE=()
|
||||
|
||||
|
||||
check_err() {
|
||||
local exit_code=$1
|
||||
@@ -27,7 +30,7 @@ check_err() {
|
||||
[[ $ERR_HANDLED == true ]] && exit $exit_code
|
||||
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
|
||||
|
||||
set +e
|
||||
systemctl_func "start" "$cron_service_name"
|
||||
systemctl_func "start" "salt-master"
|
||||
@@ -105,7 +108,7 @@ add_common() {
|
||||
}
|
||||
|
||||
airgap_mounted() {
|
||||
# Let's see if the ISO is already mounted.
|
||||
# Let's see if the ISO is already mounted.
|
||||
if [[ -f /tmp/soagupdate/SecurityOnion/VERSION ]]; then
|
||||
echo "The ISO is already mounted"
|
||||
else
|
||||
@@ -113,8 +116,8 @@ airgap_mounted() {
|
||||
echo "This is airgap. Ask for a location."
|
||||
echo ""
|
||||
cat << EOF
|
||||
In order for soup to proceed, the path to the downloaded Security Onion ISO file, or the path to the CD-ROM or equivalent device containing the ISO media must be provided.
|
||||
For example, if you have copied the new Security Onion ISO file to your home directory, then the path might look like /home/myuser/securityonion-2.x.y.iso.
|
||||
In order for soup to proceed, the path to the downloaded Security Onion ISO file, or the path to the CD-ROM or equivalent device containing the ISO media must be provided.
|
||||
For example, if you have copied the new Security Onion ISO file to your home directory, then the path might look like /home/myuser/securityonion-2.x.y.iso.
|
||||
Or, if you have burned the new ISO onto an optical disk then the path might look like /dev/cdrom.
|
||||
|
||||
EOF
|
||||
@@ -131,7 +134,7 @@ EOF
|
||||
exit 0
|
||||
else
|
||||
echo "ISO has been mounted!"
|
||||
fi
|
||||
fi
|
||||
elif [[ -f $ISOLOC/SecurityOnion/VERSION ]]; then
|
||||
ln -s $ISOLOC /tmp/soagupdate
|
||||
echo "Found the update content"
|
||||
@@ -146,7 +149,7 @@ EOF
|
||||
echo "Device has been mounted!"
|
||||
fi
|
||||
else
|
||||
echo "Could not find Security Onion ISO content at ${ISOLOC}"
|
||||
echo "Could not find Security Onion ISO content at ${ISOLOC}"
|
||||
echo "Ensure the path you entered is correct, and that you verify the ISO that you downloaded."
|
||||
exit 0
|
||||
fi
|
||||
@@ -192,7 +195,7 @@ check_airgap() {
|
||||
UPDATE_DIR=/tmp/soagupdate/SecurityOnion
|
||||
AGDOCKER=/tmp/soagupdate/docker
|
||||
AGREPO=/tmp/soagupdate/minimal/Packages
|
||||
else
|
||||
else
|
||||
is_airgap=1
|
||||
fi
|
||||
}
|
||||
@@ -305,6 +308,21 @@ clone_to_tmp() {
|
||||
fi
|
||||
}
|
||||
|
||||
disable_logstash_heavynodes() {
|
||||
c=0
|
||||
printf "\nChecking for heavynodes and disabling Logstash if they exist\n"
|
||||
for file in /opt/so/saltstack/local/pillar/minions/*.sls; do
|
||||
if [[ "$file" =~ "_heavynode.sls" && ! "$file" =~ "/opt/so/saltstack/local/pillar/minions/adv_" ]]; then
|
||||
if [ "$c" -eq 0 ]; then
|
||||
c=$((c + 1))
|
||||
FINAL_MESSAGE_QUEUE+=("Logstash has been disabled on all heavynodes. It can be re-enabled via Grid Configuration in SOC.")
|
||||
fi
|
||||
echo "Disabling Logstash for: $file"
|
||||
so-yaml.py replace "$file" logstash.enabled False
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
enable_highstate() {
|
||||
echo "Enabling highstate."
|
||||
salt-call state.enable highstate -l info --local
|
||||
@@ -344,6 +362,29 @@ masterunlock() {
|
||||
mv -v $BACKUPTOPFILE $TOPFILE
|
||||
}
|
||||
|
||||
phases_pillar_2_4_80() {
|
||||
echo "Checking if pillar value: elasticsearch.index_settings.global_overrides.index_template.phases exists"
|
||||
set +e
|
||||
PHASES=$(so-yaml.py get /opt/so/saltstack/local/pillar/elasticsearch/soc_elasticsearch.sls elasticsearch.index_settings.global_overrides.index_template.phases)
|
||||
case $? in
|
||||
0)
|
||||
so-yaml.py remove /opt/so/saltstack/local/pillar/elasticsearch/soc_elasticsearch.sls elasticsearch.index_settings.global_overrides.index_template.phases
|
||||
read -r -d '' msg <<- EOF
|
||||
Found elasticsearch.index_settings.global_overrides.index_template.phases was set to:
|
||||
${PHASES}
|
||||
|
||||
Removed unused pillar value: elasticsearch.index_settings.global_overrides.index_template.phases
|
||||
To set policies, navigate to the SOC Grid Configuration UI at elasticsearch.index_settings.global_overrides.policy.phases
|
||||
A backup of all pillar files was saved to /nsm/backup/
|
||||
EOF
|
||||
FINAL_MESSAGE_QUEUE+=("$msg")
|
||||
;;
|
||||
2) echo "Pillar elasticsearch.index_settings.global_overrides.index_template.phases does not exist. No action taken." ;;
|
||||
*) echo "so-yaml.py returned something other than 0 or 2 exit code" ;; # we shouldn't see this
|
||||
esac
|
||||
set -e
|
||||
}
|
||||
|
||||
preupgrade_changes() {
|
||||
# This function is to add any new pillar items if needed.
|
||||
echo "Checking to see if changes are needed."
|
||||
@@ -358,23 +399,27 @@ preupgrade_changes() {
|
||||
[[ "$INSTALLEDVERSION" == 2.4.40 ]] && up_to_2.4.50
|
||||
[[ "$INSTALLEDVERSION" == 2.4.50 ]] && up_to_2.4.60
|
||||
[[ "$INSTALLEDVERSION" == 2.4.60 ]] && up_to_2.4.70
|
||||
[[ "$INSTALLEDVERSION" == 2.4.70 ]] && up_to_2.4.80
|
||||
[[ "$INSTALLEDVERSION" == 2.4.80 ]] && up_to_2.4.90
|
||||
true
|
||||
}
|
||||
|
||||
postupgrade_changes() {
|
||||
# This function is to add any new pillar items if needed.
|
||||
echo "Running post upgrade processes."
|
||||
|
||||
|
||||
[[ "$POSTVERSION" == 2.4.2 ]] && post_to_2.4.3
|
||||
[[ "$POSTVERSION" == 2.4.3 ]] && post_to_2.4.4
|
||||
[[ "$POSTVERSION" == 2.4.4 ]] && post_to_2.4.5
|
||||
[[ "$POSTVERSION" == 2.4.5 ]] && post_to_2.4.10
|
||||
[[ "$POSTVERSION" == 2.4.4 ]] && post_to_2.4.5
|
||||
[[ "$POSTVERSION" == 2.4.5 ]] && post_to_2.4.10
|
||||
[[ "$POSTVERSION" == 2.4.10 ]] && post_to_2.4.20
|
||||
[[ "$POSTVERSION" == 2.4.20 ]] && post_to_2.4.30
|
||||
[[ "$POSTVERSION" == 2.4.30 ]] && post_to_2.4.40
|
||||
[[ "$POSTVERSION" == 2.4.40 ]] && post_to_2.4.50
|
||||
[[ "$POSTVERSION" == 2.4.50 ]] && post_to_2.4.60
|
||||
[[ "$POSTVERSION" == 2.4.60 ]] && post_to_2.4.70
|
||||
[[ "$POSTVERSION" == 2.4.70 ]] && post_to_2.4.80
|
||||
[[ "$POSTVERSION" == 2.4.80 ]] && post_to_2.4.90
|
||||
true
|
||||
}
|
||||
|
||||
@@ -448,6 +493,17 @@ post_to_2.4.70() {
|
||||
POSTVERSION=2.4.70
|
||||
}
|
||||
|
||||
post_to_2.4.80() {
|
||||
echo -e "\nChecking if update to Elastic Fleet output policy is required\n"
|
||||
so-kafka-fleet-output-policy
|
||||
POSTVERSION=2.4.80
|
||||
}
|
||||
|
||||
post_to_2.4.90() {
|
||||
disable_logstash_heavynodes
|
||||
POSTVERSION=2.4.90
|
||||
}
|
||||
|
||||
repo_sync() {
|
||||
echo "Sync the local repo."
|
||||
su socore -c '/usr/sbin/so-repo-sync' || fail "Unable to complete so-repo-sync."
|
||||
@@ -513,13 +569,13 @@ up_to_2.4.5() {
|
||||
|
||||
up_to_2.4.10() {
|
||||
echo "Nothing to do for 2.4.10"
|
||||
|
||||
|
||||
INSTALLEDVERSION=2.4.10
|
||||
}
|
||||
|
||||
up_to_2.4.20() {
|
||||
echo "Nothing to do for 2.4.20"
|
||||
|
||||
|
||||
INSTALLEDVERSION=2.4.20
|
||||
}
|
||||
|
||||
@@ -572,7 +628,7 @@ up_to_2.4.50() {
|
||||
mkdir /opt/so/rules/nids/suri
|
||||
chown socore:socore /opt/so/rules/nids/suri
|
||||
mv -v /opt/so/rules/nids/*.rules /opt/so/rules/nids/suri/.
|
||||
|
||||
|
||||
echo "Adding /nsm/elastic-fleet/artifacts to file_roots in /etc/salt/master using so-yaml"
|
||||
so-yaml.py append /etc/salt/master file_roots.base /nsm/elastic-fleet/artifacts
|
||||
|
||||
@@ -592,9 +648,44 @@ up_to_2.4.70() {
|
||||
suricata_idstools_migration
|
||||
toggle_telemetry
|
||||
add_detection_test_pillars
|
||||
|
||||
INSTALLEDVERSION=2.4.70
|
||||
}
|
||||
|
||||
up_to_2.4.80() {
|
||||
phases_pillar_2_4_80
|
||||
# Kafka configuration changes
|
||||
|
||||
# Global pipeline changes to REDIS or KAFKA
|
||||
echo "Removing global.pipeline pillar configuration"
|
||||
sed -i '/pipeline:/d' /opt/so/saltstack/local/pillar/global/soc_global.sls
|
||||
# Kafka pillars
|
||||
mkdir -p /opt/so/saltstack/local/pillar/kafka
|
||||
touch /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls
|
||||
touch /opt/so/saltstack/local/pillar/kafka/adv_kafka.sls
|
||||
echo 'kafka: ' > /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls
|
||||
kafka_cluster_id=$(get_random_value 22)
|
||||
echo ' cluster_id: '$kafka_cluster_id >> /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls
|
||||
kafkapass=$(get_random_value)
|
||||
echo ' password: '$kafkapass >> /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls
|
||||
|
||||
INSTALLEDVERSION=2.4.80
|
||||
}
|
||||
|
||||
up_to_2.4.90() {
|
||||
kafkatrust=$(get_random_value)
|
||||
# rearranging the kafka pillar to reduce clutter in SOC UI
|
||||
kafkasavedpass=$(so-yaml.py get /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.password)
|
||||
kafkatrimpass=$(echo "$kafkasavedpass" | sed -n '1 p' )
|
||||
echo "Making changes to the Kafka pillar layout"
|
||||
so-yaml.py remove /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.password
|
||||
so-yaml.py add /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.config.password "$kafkatrimpass"
|
||||
so-yaml.py add /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.config.trustpass "$kafkatrust"
|
||||
so-elasticsearch-query so-detection*/_settings -X PUT -d '{"index":{"refresh_interval":"1s"}}'
|
||||
|
||||
INSTALLEDVERSION=2.4.90
|
||||
}
|
||||
|
||||
add_detection_test_pillars() {
|
||||
if [[ -n "$SOUP_INTERNAL_TESTING" ]]; then
|
||||
echo "Adding detection pillar values for automated testing"
|
||||
@@ -624,7 +715,7 @@ Documentation: https://docs.securityonion.net/en/2.4/telemetry.html
|
||||
ASSIST_EOF
|
||||
|
||||
echo -n "Continue the upgrade with SOC Telemetry enabled [Y/n]? "
|
||||
|
||||
|
||||
read -r input
|
||||
input=$(echo "${input,,}" | xargs echo -n)
|
||||
echo ""
|
||||
@@ -665,7 +756,7 @@ suricata_idstools_migration() {
|
||||
rsync -av /opt/so/rules/nids/suri/local.rules /nsm/backup/detections-migration/suricata/local-rules
|
||||
if [[ -f /opt/so/saltstack/local/salt/idstools/rules/local.rules ]]; then
|
||||
rsync -av /opt/so/saltstack/local/salt/idstools/rules/local.rules /nsm/backup/detections-migration/suricata/local-rules/local.rules.bak
|
||||
fi
|
||||
fi
|
||||
|
||||
#Tell SOC to migrate
|
||||
mkdir -p /opt/so/conf/soc/migrations
|
||||
@@ -682,7 +773,7 @@ playbook_migration() {
|
||||
crontab -l | grep -v 'so-playbook-ruleupdate_cron' | crontab -
|
||||
|
||||
if grep -A 1 'playbook:' /opt/so/saltstack/local/pillar/minions/* | grep -q 'enabled: True'; then
|
||||
|
||||
|
||||
# Check for active Elastalert rules
|
||||
active_rules_count=$(find /opt/so/rules/elastalert/playbook/ -type f \( -name "*.yaml" -o -name "*.yml" \) | wc -l)
|
||||
|
||||
@@ -774,7 +865,7 @@ upgrade_space() {
|
||||
fi
|
||||
else
|
||||
echo "You have enough space for upgrade. Proceeding with soup."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
unmount_update() {
|
||||
@@ -832,7 +923,7 @@ upgrade_check() {
|
||||
fi
|
||||
else
|
||||
is_hotfix=false
|
||||
fi
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
@@ -844,7 +935,7 @@ upgrade_check_salt() {
|
||||
echo "Salt needs to be upgraded to $NEWSALTVERSION."
|
||||
UPGRADESALT=1
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
upgrade_salt() {
|
||||
SALTUPGRADED=True
|
||||
@@ -962,7 +1053,7 @@ apply_hotfix() {
|
||||
mv /etc/pki/managerssl.key /etc/pki/managerssl.key.old
|
||||
systemctl_func "start" "salt-minion"
|
||||
(wait_for_salt_minion "$MINIONID" "5" '/dev/stdout' || fail "Salt minion was not running or ready.") 2>&1 | tee -a "$SOUP_LOG"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "No actions required. ($INSTALLEDVERSION/$HOTFIXVERSION)"
|
||||
fi
|
||||
@@ -991,7 +1082,7 @@ apply_hotfix() {
|
||||
|
||||
main() {
|
||||
trap 'check_err $?' EXIT
|
||||
|
||||
|
||||
if [ -n "$BRANCH" ]; then
|
||||
echo "SOUP will use the $BRANCH branch."
|
||||
echo ""
|
||||
@@ -1175,7 +1266,7 @@ main() {
|
||||
echo "Waiting on the Salt Master service to be ready."
|
||||
check_salt_master_status || fail "Can't access salt master or it is not ready. Check $SOUP_LOG for details."
|
||||
set -e
|
||||
|
||||
|
||||
echo "Running a highstate to complete the Security Onion upgrade on this manager. This could take several minutes."
|
||||
(wait_for_salt_minion "$MINIONID" "5" '/dev/stdout' || fail "Salt minion was not running or ready.") 2>&1 | tee -a "$SOUP_LOG"
|
||||
highstate
|
||||
@@ -1226,9 +1317,9 @@ main() {
|
||||
if [[ $NUM_MINIONS -gt 1 ]]; then
|
||||
|
||||
cat << EOF
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
This appears to be a distributed deployment. Other nodes should update themselves at the next Salt highstate (typically within 15 minutes). Do not manually restart anything until you know that all the search/heavy nodes in your deployment are updated. This is especially important if you are using true clustering for Elasticsearch.
|
||||
|
||||
Each minion is on a random 15 minute check-in period and things like network bandwidth can be a factor in how long the actual upgrade takes. If you have a heavy node on a slow link, it is going to take a while to get the containers to it. Depending on what changes happened between the versions, Elasticsearch might not be able to talk to said heavy node until the update is complete.
|
||||
@@ -1255,6 +1346,15 @@ EOF
|
||||
|
||||
fi
|
||||
|
||||
# check if the FINAL_MESSAGE_QUEUE is not empty
|
||||
if (( ${#FINAL_MESSAGE_QUEUE[@]} != 0 )); then
|
||||
echo "The following additional information applies specifically to your grid:"
|
||||
for m in "${FINAL_MESSAGE_QUEUE[@]}"; do
|
||||
echo "$m"
|
||||
echo
|
||||
done
|
||||
fi
|
||||
|
||||
echo "### soup has been served at $(date) ###"
|
||||
}
|
||||
|
||||
@@ -1272,13 +1372,13 @@ while getopts ":b:f:y" opt; do
|
||||
echo "Cannot run soup in unattended mode. You must run soup manually to accept the Elastic License."
|
||||
exit 1
|
||||
else
|
||||
UNATTENDED=true
|
||||
UNATTENDED=true
|
||||
fi
|
||||
;;
|
||||
f )
|
||||
ISOLOC="$OPTARG"
|
||||
;;
|
||||
\? )
|
||||
\? )
|
||||
echo "Usage: soup [-b] [-y] [-f <iso location>]"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
@@ -15,12 +15,11 @@ Access the Security Onion web interface at https://{{ GLOBALS.url_base }}
|
||||
{%- endfor -%}
|
||||
|
||||
{%- if minions_need_restarted | length > 0 %}
|
||||
****************************************************************************************************
|
||||
* The following nodes in your Security Onion grid may need to be restarted due to package updates. *
|
||||
* If the node has already been patched, restarted and been up for less than 15 minutes, then it *
|
||||
* may not have updated it's restart_needed status yet. This will cause it to be listed below, even *
|
||||
* if it has already been restarted. This feature will be improved in the future. *
|
||||
****************************************************************************************************
|
||||
####################################################################################################
|
||||
# The following nodes in your Security Onion grid may need to be restarted due to package updates. #
|
||||
# If a node has already been patched and restarted but has been up for less than 15 minutes, #
|
||||
# then it may not have updated its status yet. #
|
||||
####################################################################################################
|
||||
|
||||
{% for minion in minions_need_restarted -%}
|
||||
{{ minion }}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
# Elastic License 2.0.
|
||||
|
||||
{% from 'redis/map.jinja' import REDISMERGED %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
include:
|
||||
{% if REDISMERGED.enabled %}
|
||||
{% if GLOBALS.pipeline == "REDIS" and REDISMERGED.enabled %}
|
||||
- redis.enabled
|
||||
{% else %}
|
||||
- redis.disabled
|
||||
|
||||
@@ -2,7 +2,7 @@ mine_interval: 25
|
||||
mine_functions:
|
||||
network.ip_addrs:
|
||||
- interface: {{ pillar.host.mainint }}
|
||||
{% if grains.role in ['so-eval','so-import','so-manager','so-managersearch','so-standalone'] -%}
|
||||
{%- if grains.role in ['so-eval','so-import','so-manager','so-managersearch','so-standalone'] %}
|
||||
x509.get_pem_entries:
|
||||
- glob_path: '/etc/pki/ca.crt'
|
||||
{% endif -%}
|
||||
|
||||
@@ -30,4 +30,65 @@ engines:
|
||||
'*':
|
||||
- cmd.run:
|
||||
cmd: /usr/sbin/so-rule-update
|
||||
- files:
|
||||
- /opt/so/saltstack/local/pillar/global/soc_global.sls
|
||||
- /opt/so/saltstack/local/pillar/global/adv_global.sls
|
||||
pillar: global.pipeline
|
||||
default: REDIS
|
||||
actions:
|
||||
from:
|
||||
'*':
|
||||
to:
|
||||
'KAFKA':
|
||||
- cmd.run:
|
||||
cmd: /usr/sbin/so-yaml.py replace /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.enabled True
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver or G@role:so-searchnode' saltutil.kill_all_jobs
|
||||
- cmd.run:
|
||||
cmd: salt-call state.apply kafka.nodes
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver or G@role:so-searchnode' state.highstate
|
||||
'KAFKA':
|
||||
to:
|
||||
'REDIS':
|
||||
- cmd.run:
|
||||
cmd: /usr/sbin/so-yaml.py replace /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.enabled False
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver or G@role:so-searchnode' saltutil.kill_all_jobs
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver or G@role:so-searchnode' state.highstate
|
||||
- files:
|
||||
- /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls
|
||||
- /opt/so/saltstack/local/pillar/kafka/adv_kafka.sls
|
||||
pillar: kafka.controllers
|
||||
default: ''
|
||||
actions:
|
||||
from:
|
||||
'*':
|
||||
to:
|
||||
'*':
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver' saltutil.kill_all_jobs
|
||||
- cmd.run:
|
||||
cmd: salt-call state.apply kafka.nodes
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver' state.apply kafka
|
||||
- cmd.run:
|
||||
cmd: salt-call state.apply elasticfleet
|
||||
- files:
|
||||
- /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls
|
||||
- /opt/so/saltstack/local/pillar/kafka/adv_kafka.sls
|
||||
pillar: kafka.reset
|
||||
default: ''
|
||||
actions:
|
||||
from:
|
||||
'*':
|
||||
to:
|
||||
'YES_RESET_KAFKA':
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver' saltutil.kill_all_jobs
|
||||
- cmd.run:
|
||||
cmd: salt -C 'G@role:so-standalone or G@role:so-manager or G@role:so-managersearch or G@role:so-receiver' state.apply kafka.disabled,kafka.reset
|
||||
- cmd.run:
|
||||
cmd: /usr/sbin/so-yaml.py remove /opt/so/saltstack/local/pillar/kafka/soc_kafka.sls kafka.reset
|
||||
interval: 10
|
||||
|
||||
@@ -13,6 +13,9 @@ include:
|
||||
- systemd.reload
|
||||
- repo.client
|
||||
- salt.mine_functions
|
||||
{% if GLOBALS.role in GLOBALS.manager_roles %}
|
||||
- ca
|
||||
{% endif %}
|
||||
|
||||
{% if INSTALLEDSALTVERSION|string != SALTVERSION|string %}
|
||||
|
||||
@@ -98,5 +101,8 @@ salt_minion_service:
|
||||
- file: mine_functions
|
||||
{% if INSTALLEDSALTVERSION|string == SALTVERSION|string %}
|
||||
- file: set_log_levels
|
||||
{% endif %}
|
||||
{% if GLOBALS.role in GLOBALS.manager_roles %}
|
||||
- file: /etc/salt/minion.d/signing_policies.conf
|
||||
{% endif %}
|
||||
- order: last
|
||||
|
||||
@@ -9,4 +9,4 @@ execute_checksum:
|
||||
cmd.run:
|
||||
- name: /etc/NetworkManager/dispatcher.d/pre-up.d/99-so-checksum-offload-disable
|
||||
- onchanges:
|
||||
- file: offload_script
|
||||
- file: offload_script
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{% endfor %}
|
||||
|
||||
{# add all grid heavy nodes to soc.server.modules.elastic.remoteHostUrls #}
|
||||
{% for node_type, minions in salt['pillar.get']('logstash:nodes', {}).items() %}
|
||||
{% for node_type, minions in salt['pillar.get']('elasticsearch:nodes', {}).items() %}
|
||||
{% if node_type in ['heavynode'] %}
|
||||
{% for m in minions.keys() %}
|
||||
{% do SOCDEFAULTS.soc.config.server.modules.elastic.remoteHostUrls.append('https://' ~ m ~ ':9200') %}
|
||||
|
||||
@@ -72,6 +72,18 @@ soc:
|
||||
target: ''
|
||||
links:
|
||||
- '/#/hunt?q=(process.entity_id:"{:process.entity_id}") | groupby event.dataset | groupby -sankey event.dataset event.action | groupby event.action | groupby process.name | groupby process.command_line | groupby host.name user.name | groupby source.ip source.port destination.ip destination.port | groupby dns.question.name | groupby dns.answers.data | groupby file.path | groupby registry.path | groupby dll.path'
|
||||
- name: actionProcessChildInfo
|
||||
description: actionProcessChildInfoHelp
|
||||
icon: fa-users-line
|
||||
target: ''
|
||||
links:
|
||||
- '/#/hunt?q=(process.entity_id:"{:process.entity_id}" OR process.parent.entity_id:"{:process.entity_id}") | groupby event.dataset | groupby -sankey event.dataset event.action | groupby event.action | groupby process.name | groupby process.command_line | groupby host.name user.name | groupby source.ip source.port destination.ip destination.port | groupby dns.question.name | groupby dns.answers.data | groupby file.path | groupby registry.path | groupby dll.path'
|
||||
- name: actionProcessAllInfo
|
||||
description: actionProcessAllInfoHelp
|
||||
icon: fa-users-between-lines
|
||||
target: ''
|
||||
links:
|
||||
- '/#/hunt?q=({:process.entity_id}) | groupby event.dataset | groupby -sankey event.dataset event.action | groupby event.action | groupby process.name | groupby process.command_line | groupby host.name user.name | groupby source.ip source.port destination.ip destination.port | groupby dns.question.name | groupby dns.answers.data | groupby file.path | groupby registry.path | groupby dll.path'
|
||||
- name: actionProcessAncestors
|
||||
description: actionProcessAncestorsHelp
|
||||
icon: fa-people-roof
|
||||
@@ -84,6 +96,12 @@ soc:
|
||||
links:
|
||||
- '/#/alerts?q=rule.uuid: {:so_detection.publicId|escape} | groupby rule.name event.module* event.severity_label'
|
||||
target: ''
|
||||
- name: actionAdd
|
||||
description: actionAddHelp
|
||||
icon: fa-plus
|
||||
links:
|
||||
- '/#/config?s=soc.config.actions'
|
||||
target: ''
|
||||
eventFields:
|
||||
default:
|
||||
- soc_timestamp
|
||||
@@ -1293,7 +1311,6 @@ soc:
|
||||
kratos:
|
||||
hostUrl:
|
||||
elastalertengine:
|
||||
allowRegex: ''
|
||||
autoUpdateEnabled: true
|
||||
autoEnabledSigmaRules:
|
||||
default:
|
||||
@@ -1309,12 +1326,11 @@ soc:
|
||||
communityRulesImportFrequencySeconds: 86400
|
||||
communityRulesImportErrorSeconds: 300
|
||||
failAfterConsecutiveErrorCount: 10
|
||||
denyRegex: ''
|
||||
elastAlertRulesFolder: /opt/sensoroni/elastalert
|
||||
reposFolder: /opt/sensoroni/sigma/repos
|
||||
rulesFingerprintFile: /opt/sensoroni/fingerprints/sigma.fingerprint
|
||||
stateFilePath: /opt/sensoroni/fingerprints/elastalertengine.state
|
||||
integrityCheckFrequencySeconds: 600
|
||||
integrityCheckFrequencySeconds: 1200
|
||||
rulesRepos:
|
||||
default:
|
||||
- repo: https://github.com/Security-Onion-Solutions/securityonion-resources
|
||||
@@ -1348,6 +1364,8 @@ soc:
|
||||
maxLogLength: 1024
|
||||
asyncThreshold: 10
|
||||
lookupTunnelParent: true
|
||||
maxScrollSize: 10000
|
||||
bulkIndexerWorkerCount: -1
|
||||
influxdb:
|
||||
hostUrl:
|
||||
token:
|
||||
@@ -1372,7 +1390,6 @@ soc:
|
||||
userFiles:
|
||||
- rbac/users_roles
|
||||
strelkaengine:
|
||||
allowRegex: ''
|
||||
autoEnabledYaraRules:
|
||||
- securityonion-yara
|
||||
autoUpdateEnabled: true
|
||||
@@ -1380,7 +1397,6 @@ soc:
|
||||
communityRulesImportErrorSeconds: 300
|
||||
failAfterConsecutiveErrorCount: 10
|
||||
compileYaraPythonScriptPath: /opt/sensoroni/yara/compile_yara.py
|
||||
denyRegex: ''
|
||||
reposFolder: /opt/sensoroni/yara/repos
|
||||
rulesRepos:
|
||||
default:
|
||||
@@ -1393,18 +1409,19 @@ soc:
|
||||
community: true
|
||||
yaraRulesFolder: /opt/sensoroni/yara/rules
|
||||
stateFilePath: /opt/sensoroni/fingerprints/strelkaengine.state
|
||||
integrityCheckFrequencySeconds: 600
|
||||
integrityCheckFrequencySeconds: 1200
|
||||
suricataengine:
|
||||
allowRegex: ''
|
||||
autoUpdateEnabled: true
|
||||
communityRulesImportFrequencySeconds: 86400
|
||||
communityRulesImportErrorSeconds: 300
|
||||
customRulesets:
|
||||
disableRegex: []
|
||||
enableRegex: []
|
||||
failAfterConsecutiveErrorCount: 10
|
||||
communityRulesFile: /nsm/rules/suricata/emerging-all.rules
|
||||
denyRegex: ''
|
||||
rulesFingerprintFile: /opt/sensoroni/fingerprints/emerging-all.fingerprint
|
||||
stateFilePath: /opt/sensoroni/fingerprints/suricataengine.state
|
||||
integrityCheckFrequencySeconds: 600
|
||||
integrityCheckFrequencySeconds: 1200
|
||||
client:
|
||||
enableReverseLookup: false
|
||||
docsUrl: /docs/
|
||||
@@ -2250,6 +2267,78 @@ soc:
|
||||
- suricata
|
||||
- sigma
|
||||
- yara
|
||||
license:
|
||||
customEnabled: true
|
||||
labels:
|
||||
- None
|
||||
- Apache-2.0
|
||||
- AGPL-3.0-only
|
||||
- BSD-3-Clause
|
||||
- DRL-1.1
|
||||
- GPL-2.0-only
|
||||
- GPL-3.0-only
|
||||
- MIT
|
||||
severityTranslations:
|
||||
minor: low
|
||||
major: high
|
||||
templateDetections:
|
||||
suricata: |
|
||||
# This is a Suricata rule template. Replace all template values with your own values.
|
||||
# The rule identifier [sid] is pregenerated and known to be unique for this Security Onion installation.
|
||||
# Docs: https://docs.suricata.io/en/latest/rules/intro.html
|
||||
# Delete these comments before attempting to "Create" the rule
|
||||
|
||||
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"Example Rule Title - 'example' String Detected"; content:"example"; sid:[publicId]; rev:1;)
|
||||
strelka: |
|
||||
/*
|
||||
This is a YARA rule template. Replace all template values with your own values.
|
||||
The YARA rule name is the unique identifier for the rule.
|
||||
Docs: https://yara.readthedocs.io/en/stable/writingrules.html#writing-yara-rules
|
||||
*/
|
||||
|
||||
rule Example // This identifier _must_ be unique
|
||||
{
|
||||
meta:
|
||||
description = "Generic YARA Rule"
|
||||
author = "@SecurityOnion"
|
||||
date = "YYYY-MM-DD"
|
||||
reference = "https://local.invalid"
|
||||
strings:
|
||||
$my_text_string = "text here"
|
||||
$my_hex_string = { E2 34 A1 C8 23 FB }
|
||||
condition:
|
||||
filesize < 3MB and ($my_text_string or $my_hex_string)
|
||||
}
|
||||
elastalert: |
|
||||
# This is a Sigma rule template, which uses YAML. Replace all template values with your own values.
|
||||
# The id (UUIDv4) is pregenerated and can safely be used.
|
||||
# Click "Convert" to convert the Sigma rule to use Security Onion field mappings within an EQL query
|
||||
#
|
||||
# Rule Creation Guide: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide
|
||||
# Logsources: https://sigmahq.io/docs/basics/log-sources.html
|
||||
|
||||
title: 'A Short Capitalized Title With Less Than 50 Characters'
|
||||
id: [publicId]
|
||||
status: 'experimental'
|
||||
description: |
|
||||
This should be a detailed description of what this Detection focuses on: what we are trying to find and why we are trying to find it.
|
||||
For example, from rule 97a80ec7-0e2f-4d05-9ef4-65760e634f6b: "Detects a whoami.exe executed with the /priv command line flag instructing the tool to show all current user privileges. This is often used after a privilege escalation attempt."
|
||||
references:
|
||||
- 'https://local.invalid'
|
||||
author: '@SecurityOnion'
|
||||
date: 'YYYY/MM/DD'
|
||||
tags:
|
||||
- detection.threat_hunting
|
||||
- attack.technique_id
|
||||
logsource:
|
||||
category: process_creation
|
||||
product: windows
|
||||
detection:
|
||||
selection_img:
|
||||
- Image|endswith: '\whoami.exe'
|
||||
- OriginalFileName: 'whoami.exe'
|
||||
selection_cli:
|
||||
CommandLine|contains|windash:
|
||||
- ' -priv'
|
||||
condition: all of selection_*
|
||||
level: 'high' # info | low | medium | high | critical
|
||||
|
||||
@@ -27,6 +27,7 @@ so-soc:
|
||||
- /opt/so/conf/strelka:/opt/sensoroni/yara:rw
|
||||
- /opt/so/conf/sigma:/opt/sensoroni/sigma:rw
|
||||
- /opt/so/rules/elastalert/rules:/opt/sensoroni/elastalert:rw
|
||||
- /opt/so/rules/nids/suri:/opt/sensoroni/nids:ro
|
||||
- /opt/so/conf/soc/fingerprints:/opt/sensoroni/fingerprints:rw
|
||||
- /nsm/soc/jobs:/opt/sensoroni/jobs:rw
|
||||
- /nsm/soc/uploads:/nsm/soc/uploads:rw
|
||||
|
||||
@@ -1,20 +1,36 @@
|
||||
## Getting Started
|
||||
|
||||
New to Security Onion 2? Click the menu in the upper-right corner and you'll find links for [Help](/docs/) and a [Cheat Sheet](/docs/cheatsheet.pdf) that will help you best utilize Security Onion to hunt for evil! In addition, check out our free Security Onion 2 Essentials online course, available on our [Training](https://securityonionsolutions.com/training) website.
|
||||
New to Security Onion? Click the menu in the upper-right corner and you'll find links for [Help](/docs/) and a [Cheat Sheet](/docs/cheatsheet.pdf) that will help you best utilize Security Onion to hunt for evil! In addition, check out our free Security Onion Essentials online course, available on our [Training](https://securityonion.com/training) website.
|
||||
|
||||
If you're ready to dive in, take a look at the [Alerts](/#/alerts) interface to see what Security Onion has detected so far. Then go to the [Dashboards](/#/dashboards) interface for a general overview of all logs collected or go to the [Hunt](/#/hunt) interface for more focused threat hunting. Once you've found something of interest, escalate it to [Cases](/#/cases) to then collect evidence and analyze observables as you work towards closing the case.
|
||||
If you're ready to dive in, take a look at the [Alerts](/#/alerts) interface to see what Security Onion has detected so far. If you find any false positives, then you can tune those in [Detections](/#/detections).
|
||||
|
||||
Next, go to the [Dashboards](/#/dashboards) interface for a general overview of all logs collected. Here are a few overview dashboards to get you started:
|
||||
|
||||
[Overview Dashboard](/#/dashboards) | [Elastic Agent Overview](/#/dashboards?q=event.module%3Aendpoint%20%7C%20groupby%20event.dataset%20%7C%20groupby%20host.name%20%7C%20groupby%20-sankey%20host.name%20user.name%20%7C%20groupby%20user.name%20%7C%20groupby%20-sankey%20user.name%20process.name%20%7C%20groupby%20process.name) | [Network Connection Overview](/#/dashboards?q=tags%3Aconn%20%7C%20groupby%20source.ip%20%7C%20groupby%20destination.ip%20%7C%20groupby%20destination.port%20%7C%20groupby%20-sankey%20destination.port%20network.protocol%20%7C%20groupby%20network.protocol%20%7C%20groupby%20network.transport%20%7C%20groupby%20connection.history%20%7C%20groupby%20connection.state%20%7C%20groupby%20connection.state_description%20%7C%20groupby%20source.geo.country_name%20%7C%20groupby%20destination.geo.country_name%20%7C%20groupby%20client.ip_bytes%20%7C%20groupby%20server.ip_bytes%20%7C%20groupby%20client.oui) | [DNS](/#/dashboards?q=tags%3Adns%20%7C%20groupby%20dns.query.name%20%7C%20groupby%20source.ip%20%7C%20groupby%20-sankey%20source.ip%20destination.ip%20%7C%20groupby%20destination.ip%20%7C%20groupby%20destination.port%20%7C%20groupby%20dns.highest_registered_domain%20%7C%20groupby%20dns.parent_domain%20%7C%20groupby%20dns.query.type_name%20%7C%20groupby%20dns.response.code_name%20%7C%20groupby%20dns.answers.name%20%7C%20groupby%20destination_geo.organization_name) | [Files](/#/dashboards?q=tags%3Afile%20%7C%20groupby%20file.mime_type%20%7C%20groupby%20-sankey%20file.mime_type%20file.source%20%7C%20groupby%20file.source%20%7C%20groupby%20file.bytes.total%20%7C%20groupby%20source.ip%20%7C%20groupby%20destination.ip%20%7C%20groupby%20destination_geo.organization_name) | [HTTP](/#/dashboards?q=tags%3Ahttp%20%7C%20groupby%20http.method%20%7C%20groupby%20-sankey%20http.method%20http.virtual_host%20%7C%20groupby%20http.virtual_host%20%7C%20groupby%20http.uri%20%7C%20groupby%20http.useragent%20%7C%20groupby%20http.status_code%20%7C%20groupby%20http.status_message%20%7C%20groupby%20file.resp_mime_types%20%7C%20groupby%20source.ip%20%7C%20groupby%20destination.ip%20%7C%20groupby%20destination.port%20%7C%20groupby%20destination_geo.organization_name) | [SSL](/#/dashboards?q=tags%3Assl%20%7C%20groupby%20ssl.version%20%7C%20groupby%20-sankey%20ssl.version%20ssl.server_name%20%7C%20groupby%20ssl.server_name%20%7C%20groupby%20source.ip%20%7C%20groupby%20destination.ip%20%7C%20groupby%20destination.port%20%7C%20groupby%20destination_geo.organization_name)
|
||||
|
||||
Click the drop-down menu in Dashboards to find many more dashboards. You might also want to explore the [Hunt](/#/hunt) interface for more focused threat hunting.
|
||||
|
||||
Once you've found something of interest, escalate it to [Cases](/#/cases) to then collect evidence and analyze observables as you work towards closing the case.
|
||||
|
||||
If you want to check the health of your deployment, check out the [Grid](/#/grid) interface.
|
||||
|
||||
For more coverage of your enterprise, you can deploy the Elastic Agent to endpoints by going to the [Downloads](/#/downloads) page.
|
||||
|
||||
## What's New
|
||||
|
||||
To see all the latest features and fixes in this version of Security Onion, click the upper-right menu and then click the [What's New](/docs/release-notes.html) link.
|
||||
|
||||
## Security Onion Pro
|
||||
|
||||
Need enterprise features and premium support? Check out [Security Onion Pro](https://securityonion.com/pro/)!
|
||||
|
||||
## Enterprise Appliances
|
||||
|
||||
Want the best hardware for your enterprise deployment? Check out our [enterprise appliances](https://securityonionsolutions.com/hardware/)!
|
||||
Want the best hardware for your enterprise deployment? Check out our [enterprise appliances](https://securityonion.com/hardware/)!
|
||||
|
||||
## Premium Support
|
||||
|
||||
Experiencing difficulties and need priority support or remote assistance? We offer a [premium support plan](https://securityonionsolutions.com/support/) to assist corporate, educational, and government organizations.
|
||||
Experiencing difficulties and need priority support or remote assistance? We offer a [premium support plan](https://securityonion.com/support/) to assist corporate, educational, and government organizations.
|
||||
|
||||
## Customize This Space
|
||||
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
{# 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
|
||||
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 %}
|
||||
{% from 'soc/defaults.map.jinja' import SOCDEFAULTS with context %}
|
||||
{% from 'logstash/map.jinja' import LOGSTASH_NODES %}
|
||||
{% set DOCKER_EXTRA_HOSTS = LOGSTASH_NODES %}
|
||||
{% from 'elasticsearch/config.map.jinja' import ELASTICSEARCH_NODES %}
|
||||
{% from 'manager/map.jinja' import MANAGERMERGED %}
|
||||
{% set DOCKER_EXTRA_HOSTS = ELASTICSEARCH_NODES %}
|
||||
{% do DOCKER_EXTRA_HOSTS.append({GLOBALS.influxdb_host:pillar.node_data[GLOBALS.influxdb_host].ip}) %}
|
||||
|
||||
{% set SOCMERGED = salt['pillar.get']('soc', SOCDEFAULTS, merge=true) %}
|
||||
|
||||
{% do SOCMERGED.config.server.update({'proxy': MANAGERMERGED.proxy}) %}
|
||||
{% do SOCMERGED.config.server.update({'additionalCA': MANAGERMERGED.additionalCA}) %}
|
||||
{% do SOCMERGED.config.server.update({'insecureSkipVerify': MANAGERMERGED.insecureSkipVerify}) %}
|
||||
|
||||
{# if SOCMERGED.config.server.modules.cases == httpcase details come from the soc pillar #}
|
||||
{% if SOCMERGED.config.server.modules.cases != 'soc' %}
|
||||
{% do SOCMERGED.config.server.modules.elastic.update({'casesEnabled': false}) %}
|
||||
|
||||
@@ -90,11 +90,6 @@ soc:
|
||||
helpLink: sigma.html
|
||||
forcedType: "[]string"
|
||||
multiline: True
|
||||
allowRegex:
|
||||
description: 'Regex used to filter imported Sigma rules. Deny regex takes precedence over the Allow regex setting.'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: sigma.html
|
||||
autoEnabledSigmaRules:
|
||||
default: &autoEnabledSigmaRules
|
||||
description: 'Sigma rules to automatically enable on initial import. Format is $Ruleset+$Level - for example, for the core community ruleset and critical level rules: core+critical. These will be applied based on role if defined and default if not.'
|
||||
@@ -103,11 +98,6 @@ soc:
|
||||
helpLink: sigma.html
|
||||
so-eval: *autoEnabledSigmaRules
|
||||
so-import: *autoEnabledSigmaRules
|
||||
denyRegex:
|
||||
description: 'Regex used to filter imported Sigma rules. Deny regex takes precedence over the Allow regex setting.'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: sigma.html
|
||||
communityRulesImportFrequencySeconds:
|
||||
description: 'How often to check for new Sigma rules (in seconds). This applies to both Community Rule Packages and any configured Git repos.'
|
||||
global: True
|
||||
@@ -119,7 +109,7 @@ soc:
|
||||
advanced: True
|
||||
rulesRepos:
|
||||
default: &eerulesRepos
|
||||
description: "Custom Git repositories to pull Sigma rules from. 'license' field is required, 'folder' is optional. 'community' disables some management options for the imported rules - they can't be deleted or edited, just tuned, duplicated and Enabled | Disabled. The new settings will be applied within 15 minutes. At that point, you will need to wait for the scheduled rule update to take place (by default, every 24 hours), or you can force the update by nagivating to Detections --> Options dropdown menu --> Elastalert --> Full Update."
|
||||
description: "Custom Git repositories to pull Sigma rules from. 'license' field is required, 'folder' is optional. 'community' disables some management options for the imported rules - they can't be deleted or edited, just tuned, duplicated and Enabled | Disabled. The new settings will be applied within 15 minutes. At that point, you will need to wait for the scheduled rule update to take place (by default, every 24 hours), or you can force the update by nagivating to Detections --> Options dropdown menu --> Elastalert --> Full Update."
|
||||
global: True
|
||||
advanced: True
|
||||
forcedType: "[]{}"
|
||||
@@ -174,6 +164,10 @@ soc:
|
||||
lookupTunnelParent:
|
||||
description: When true, if a pivoted event appears to be encapsulated, such as in a VXLAN packet, then SOC will pivot to the VXLAN packet stream. When false, SOC will attempt to pivot to the encapsulated packet stream itself, but at the risk that it may be unable to locate it in the stored PCAP data.
|
||||
global: True
|
||||
maxScrollSize:
|
||||
description: The maximum number of documents to request in a single Elasticsearch scroll request.
|
||||
bulkIndexWorkerCount:
|
||||
description: The number of worker threads to use when bulk indexing data into Elasticsearch. A value below 1 will default to the number of CPUs available.
|
||||
sostatus:
|
||||
refreshIntervalMs:
|
||||
description: Duration (in milliseconds) between refreshes of the grid status. Shortening this duration may not have expected results, as the backend systems feeding this sostatus data will continue their updates as scheduled.
|
||||
@@ -195,21 +189,11 @@ soc:
|
||||
advanced: True
|
||||
forcedType: int
|
||||
strelkaengine:
|
||||
allowRegex:
|
||||
description: 'Regex used to filter imported YARA rules. Deny regex takes precedence over the Allow regex setting.'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: yara.html
|
||||
autoEnabledYaraRules:
|
||||
description: 'YARA rules to automatically enable on initial import. Format is $Ruleset - for example, for the default shipped ruleset: securityonion-yara'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: sigma.html
|
||||
denyRegex:
|
||||
description: 'Regex used to filter imported YARA rules. Deny regex takes precedence over the Allow regex setting.'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: yara.html
|
||||
communityRulesImportFrequencySeconds:
|
||||
description: 'How often to check for new YARA rules (in seconds). This applies to both Community Rules and any configured Git repos.'
|
||||
global: True
|
||||
@@ -228,25 +212,29 @@ soc:
|
||||
helpLink: yara.html
|
||||
airgap: *serulesRepos
|
||||
suricataengine:
|
||||
allowRegex:
|
||||
description: 'Regex used to filter imported Suricata rules. Deny regex takes precedence over the Allow regex setting.'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: suricata.html
|
||||
denyRegex:
|
||||
description: 'Regex used to filter imported Suricata rules. Deny regex takes precedence over the Allow regex setting.'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: suricata.html
|
||||
communityRulesImportFrequencySeconds:
|
||||
description: 'How often to check for new Suricata rules (in seconds).'
|
||||
global: True
|
||||
advanced: True
|
||||
helpLink: suricata.html
|
||||
disableRegex:
|
||||
description: A list of regular expressions used to automatically disable rules that match any of them. Each regular expression is tested against the rule's content.
|
||||
global: True
|
||||
forcedType: "[]string"
|
||||
enableRegex:
|
||||
description: A list of regular expressions used to automatically enable rules that match any of them. Each regular expression is tested against the rule's content. Takes priority over disableRegex matches.
|
||||
global: True
|
||||
forcedType: "[]string"
|
||||
integrityCheckFrequencySeconds:
|
||||
description: 'How often the Suricata integrity checker runs (in seconds). This verifies the integrity of deployed rules.'
|
||||
global: True
|
||||
advanced: True
|
||||
customRulesets:
|
||||
description: 'URLs and/or Local File configurations for Suricata custom rulesets. Refer to the linked documentation for important specification and file placement information'
|
||||
global: True
|
||||
advanced: True
|
||||
forcedType: "[]{}"
|
||||
helpLink: suricata.html
|
||||
client:
|
||||
enableReverseLookup:
|
||||
description: Set to true to enable reverse DNS lookups for IP addresses in the SOC UI.
|
||||
@@ -319,6 +307,17 @@ soc:
|
||||
cases: *appSettings
|
||||
dashboards: *appSettings
|
||||
detections: *appSettings
|
||||
detection:
|
||||
templateDetections:
|
||||
suricata:
|
||||
description: The template used when creating a new Suricata detection. [publicId] will be replaced with an unused Public Id.
|
||||
multiline: True
|
||||
strelka:
|
||||
description: The template used when creating a new Strelka detection.
|
||||
multiline: True
|
||||
elastalert:
|
||||
description: The template used when creating a new ElastAlert detection. [publicId] will be replaced with an unused Public Id.
|
||||
multiline: True
|
||||
grid:
|
||||
maxUploadSize:
|
||||
description: The maximum number of bytes for an uploaded PCAP import file.
|
||||
|
||||
@@ -661,9 +661,56 @@ elastickeyperms:
|
||||
- name: /etc/pki/elasticsearch.key
|
||||
- mode: 640
|
||||
- group: 930
|
||||
|
||||
{%- endif %}
|
||||
|
||||
{% if grains['role'] in ['so-manager', '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: 0
|
||||
- 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:
|
||||
|
||||
@@ -71,3 +71,20 @@ fleet_crt:
|
||||
fbcertdir:
|
||||
file.absent:
|
||||
- name: /opt/so/conf/filebeat/etc/pki
|
||||
|
||||
kafka_crt:
|
||||
file.absent:
|
||||
- name: /etc/pki/kafka.crt
|
||||
kafka_key:
|
||||
file.absent:
|
||||
- name: /etc/pki/kafka.key
|
||||
|
||||
kafka_logstash_crt:
|
||||
file.absent:
|
||||
- name: /etc/pki/kafka-logstash.crt
|
||||
kafka_logstash_key:
|
||||
file.absent:
|
||||
- name: /etc/pki/kafka-logstash.key
|
||||
kafka_logstash_keystore:
|
||||
file.absent:
|
||||
- name: /etc/pki/kafka-logstash.p12
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user