Compare commits

...

486 Commits

Author SHA1 Message Date
Jorge Reyes
bbc4b1b502 Merge pull request #15241 from Security-Onion-Solutions/reyesj2/advilm
FEATURE: Advanced ILM actions via SOC UI
2025-12-04 14:43:12 -06:00
reyesj2
0b127582cb 2.4.200 soup changes 2025-12-03 20:49:25 -06:00
reyesj2
6e9b8791c8 Merge branch '2.4/dev' of github.com:Security-Onion-Solutions/securityonion into reyesj2/advilm 2025-12-03 20:27:13 -06:00
reyesj2
ef87ad77c3 Merge branch 'reyesj2/advilm' of github.com:Security-Onion-Solutions/securityonion into reyesj2/advilm 2025-12-03 20:23:03 -06:00
reyesj2
8477420911 logstash adv config state file 2025-12-03 20:10:06 -06:00
Jason Ertel
f5741e318f Merge pull request #15281 from Security-Onion-Solutions/jertel/wip
skip continue prompt if user cannot actually continue
2025-12-03 16:37:07 -05:00
Josh Patterson
e010b5680a Merge pull request #15280 from Security-Onion-Solutions/reservegid
reserve group ids
2025-12-03 16:24:12 -05:00
Josh Patterson
8620d3987e add saltgid 2025-12-03 15:04:28 -05:00
Jason Ertel
30487a54c1 skip continue prompt if user cannot actually contine 2025-12-03 11:52:10 -05:00
Josh Patterson
aed27fa111 reserve group ids 2025-12-03 11:19:46 -05:00
Jorge Reyes
8d2701e143 Merge branch '2.4/dev' into reyesj2/advilm 2025-12-02 15:42:15 -06:00
reyesj2
877444ac29 cert update is a forced update 2025-12-02 15:16:59 -06:00
reyesj2
b0d9426f1b automated cert update for kafka fleet output policy 2025-12-02 15:11:00 -06:00
reyesj2
18accae47e annotation typo 2025-12-02 15:10:29 -06:00
Josh Patterson
55e3a2c6b6 Merge pull request #15277 from Security-Onion-Solutions/soyamllistremove
need additional line bw class
2025-12-02 15:09:47 -05:00
Josh Patterson
ef092e2893 rename to removelistitem 2025-12-02 15:01:32 -05:00
Josh Patterson
89eb95c077 add removefromlist 2025-12-02 14:46:24 -05:00
Josh Patterson
e871ec358e need additional line bw class 2025-12-02 14:43:33 -05:00
Josh Patterson
271a2f74ad Merge pull request #15275 from Security-Onion-Solutions/soyamllistremove
add new so-yaml_test for removefromlist
2025-12-02 14:34:09 -05:00
Josh Patterson
d6bd951c37 add new so-yaml_test for removefromlist 2025-12-02 14:31:57 -05:00
reyesj2
45a8c0acd1 merge 2.4/dev 2025-12-02 11:16:08 -06:00
Jorge Reyes
6fbed2dd9f Merge pull request #15264 from Security-Onion-Solutions/reyesj2-patch-2
add force & certs flag to update fleet certs as needed
2025-12-01 11:11:25 -06:00
Mike Reeves
875de88cb4 Merge pull request #15271 from Security-Onion-Solutions/TOoSmOotH-patch-2
Add JA4D option to config.zeek.ja4
2025-12-01 10:03:12 -05:00
Mike Reeves
63bb44886e Add JA4D option to config.zeek.ja4 2025-12-01 10:00:42 -05:00
reyesj2
edf3c9464f add --certs flag to update certs. Used with --force, to ensure certs are updated even if hosts update isn't needed 2025-11-25 16:16:19 -06:00
reyesj2
cc8fb96047 valid config for number_of_replicas in allocate action includes 0 2025-11-24 11:12:09 -06:00
reyesj2
3339b50daf drop forcemerge when max_num_segements doesn't exist or empty 2025-11-21 16:39:45 -06:00
reyesj2
415ea07a4f clean up 2025-11-21 16:04:26 -06:00
reyesj2
b80ec95fa8 update regex, revert to default will allow setting value back to '' | None 2025-11-21 14:41:03 -06:00
reyesj2
99cb51482f unneeded 'set' 2025-11-21 14:32:58 -06:00
reyesj2
90638f7a43 Merge branch 'reyesj2/advea' into reyesj2/advilm 2025-11-21 14:25:28 -06:00
reyesj2
1fb00c8eb6 update so-elastic-fleet-outputs-update to use advanced output options when set, else empty "". Also trigger update_logstash_outputs() when hash of config_yaml has changed 2025-11-21 14:22:42 -06:00
reyesj2
4490ea7635 format EA logstash output adv config items 2025-11-21 14:21:17 -06:00
reyesj2
bce7a20d8b soc configurable EA logstash output adv settings 2025-11-21 14:19:51 -06:00
Josh Patterson
9c06713f32 Merge pull request #15251 from Security-Onion-Solutions/bravo
use timestamp in volume path to prevent duplicates
2025-11-21 14:54:30 -05:00
Josh Patterson
23da0d4ba0 use timestamp in filename to prevent duplicates 2025-11-21 14:49:03 -05:00
Josh Patterson
d5f2cfb354 Merge pull request #15248 from Security-Onion-Solutions/bravo
clarify hypervisor annotation
2025-11-20 17:28:32 -05:00
Josh Patterson
fb5ad4193d indicate base image download start 2025-11-20 17:13:36 -05:00
Josh Patterson
1f5f283c06 update hypervisor annotaion. preinit instead of initialized 2025-11-20 16:53:55 -05:00
Josh Patterson
cf048030c4 Merge pull request #15247 from Security-Onion-Solutions/bravo
Notify user of hypervisor environment setup failures
2025-11-20 16:04:49 -05:00
Josh Patterson
2d716b44a8 update comment 2025-11-20 15:52:21 -05:00
Jorge Reyes
d70d652310 Merge pull request #15244 from Security-Onion-Solutions/reyesj2/suricapfile
suricata capture file
2025-11-20 14:31:43 -06:00
reyesj2
c5db7c8752 suricata.capture_file keyword 2025-11-20 14:26:12 -06:00
reyesj2
6f42ff3442 suricata capture_file 2025-11-20 14:16:49 -06:00
reyesj2
433dab7376 format json 2025-11-20 14:16:10 -06:00
Josh Patterson
97c1a46013 update annotation for general failure 2025-11-20 15:08:04 -05:00
Josh Patterson
fbe97221bb set initialized status 2025-11-20 14:43:09 -05:00
Josh Patterson
841ce6b6ec update hypervisor annotation for image download or ssh key creation failure 2025-11-20 13:55:22 -05:00
Josh Patterson
dd0b4c3820 fix failed or hung qcow2 image download 2025-11-19 15:48:53 -05:00
reyesj2
b52dd53e29 advanced ilm actions 2025-11-19 13:24:55 -06:00
reyesj2
a155f45036 always update annotation / defaults for managed integrations 2025-11-19 13:24:29 -06:00
Josh Patterson
b407c68d88 Merge remote-tracking branch 'origin/2.4/dev' into bravo 2025-11-19 10:23:11 -05:00
Josh Patterson
5b6a7035af need python_shell for pipes 2025-11-19 10:22:58 -05:00
Jason Ertel
12d490ad4a Merge pull request #15240 from Security-Onion-Solutions/jertel/wip
communicate to the viewer that OS patches may take some time
2025-11-19 10:01:03 -05:00
Jason Ertel
76cbd18d2c communicate to the viewer that OS patches may take some time 2025-11-19 09:56:42 -05:00
Jorge Reyes
a7337c95e1 Merge pull request #15234 from Security-Onion-Solutions/reyesj2/pipeline-upd
update zeek pipelines
2025-11-17 10:36:10 -06:00
Josh Patterson
3f7c3326ea Merge pull request #15237 from Security-Onion-Solutions/bravo
rm salt keyring and repo file for deb
2025-11-17 09:27:53 -05:00
Josh Patterson
bf41de8c14 rm salt keyring and repo file for deb 2025-11-17 08:56:02 -05:00
reyesj2
de4424fab0 remove typos 2025-11-14 19:15:51 -06:00
reyesj2
136a829509 detect-sqli deprecated in favor of detect-sql-injection 2025-11-14 16:51:00 -06:00
reyesj2
bcec999be4 zeek.dns reduce errors 2025-11-14 15:47:29 -06:00
reyesj2
7c73b4713f update analyzer pipeline 2025-11-14 15:47:29 -06:00
reyesj2
45b4b1d963 ingest zeek analyzer.log + update dpd dashboard with analyzer tag 2025-11-14 15:47:29 -06:00
reyesj2
fcfd74ec1e zeek.analyzer format json 2025-11-14 15:47:29 -06:00
reyesj2
68b0cd7549 rename zeek.dpd zeek.analyzer 2025-11-14 15:47:29 -06:00
reyesj2
715d801ce8 format json zeek.dns 2025-11-14 15:47:19 -06:00
Jorge Reyes
4a810696e7 Merge pull request #15231 from Security-Onion-Solutions/reyesj2/bond0
fix so-setup error duplicate bond0
2025-11-14 12:12:46 -06:00
reyesj2
6b525a2c21 fix so-setup error duplicate bond0 2025-11-14 11:19:32 -06:00
Jorge Reyes
a5d8385f07 Merge pull request #15230 from Security-Onion-Solutions/reyesj2/pipeline-upd
suricata pipeline updates
2025-11-14 10:43:33 -06:00
reyesj2
211bf7e77b ignore errors on tld script 2025-11-14 09:25:19 -06:00
reyesj2
1542b74133 move dns tld fields to its own pipeline 2025-11-14 09:24:58 -06:00
reyesj2
4314c79f85 bump suricata dns logging version 2025-11-14 08:24:31 -06:00
reyesj2
da9717bc79 don't attempt rename if field doesn't exist -- reducing pipeline stat errors 2025-11-14 08:15:40 -06:00
Jason Ertel
045cf7866c Merge pull request #15225 from Security-Onion-Solutions/jertel/wip
pcap annotations
2025-11-14 08:37:37 -05:00
reyesj2
431e0b0780 format suricata.alert json 2025-11-13 19:29:50 -06:00
reyesj2
e782266caa suricata 8 dns v3 2025-11-13 19:21:31 -06:00
coreyogburn
a4666b2c08 Merge pull request #15229 from Security-Onion-Solutions/cogburn/toggle-models
Add Enabled Flag to Models
2025-11-13 16:13:24 -07:00
Corey Ogburn
dcc3206e51 Add Enabled Flag to Models 2025-11-13 15:32:28 -07:00
Josh Patterson
8358b6ea6f Merge pull request #15228 from Security-Onion-Solutions/bravo
wait for 200 from registry before proceeding
2025-11-13 16:34:43 -05:00
coreyogburn
d1a66a91c6 Merge pull request #15221 from Security-Onion-Solutions/cogburn/compress-context
CompressContextPrompt
2025-11-13 14:33:56 -07:00
Josh Patterson
7fdcb92614 wait for 200 from registry before proceeding 2025-11-13 16:30:58 -05:00
Jason Ertel
cec1890b6b pcap annotations 2025-11-13 16:15:47 -05:00
Corey Ogburn
b1b66045ea Change in prompt wording 2025-11-13 12:08:47 -07:00
Corey Ogburn
33b22bf2e4 Shorten Prompt 2025-11-13 11:09:09 -07:00
Corey Ogburn
3a38886345 CompressContextPrompt 2025-11-13 11:09:08 -07:00
reyesj2
7be70faab6 format json 2025-11-13 10:49:37 -06:00
Josh Patterson
2729fdbea6 Merge pull request #15223 from Security-Onion-Solutions/bravo
configure salt, then install. update bootstrap-salt. reduce salt install fail timeout
2025-11-13 11:35:43 -05:00
Jorge Reyes
bfd08d1d2e Merge pull request #15204 from Security-Onion-Solutions/reyesj2/retention
update so-elasticsearch-retention-estimate
2025-11-13 10:05:49 -06:00
Josh Patterson
fed75c7b39 use -r with bootstrap to disable script repo 2025-11-12 19:47:25 -05:00
Josh Patterson
3427df2a54 update bootstrap-salt to latest 2025-11-12 18:07:14 -05:00
Josh Patterson
be11c718f6 configure salt then install it 2025-11-12 18:06:55 -05:00
Josh Patterson
235dfd78f1 Revert "salt-minion service KillMode to control-group"
This reverts commit 7c8b9b4374.
2025-11-12 14:20:28 -05:00
Josh Patterson
7c8b9b4374 salt-minion service KillMode to control-group 2025-11-12 12:30:29 -05:00
Jason Ertel
7762faf075 Merge pull request #15219 from Security-Onion-Solutions/jertel/wip
add support to so-yaml for using yaml file content for values
2025-11-12 08:12:23 -05:00
Jason Ertel
80fbb31372 fix test 2025-11-11 17:04:19 -05:00
Jason Ertel
7c45db2295 add support to so-yaml for using yaml file content for values 2025-11-11 16:57:54 -05:00
Jason Ertel
0545e1d33b add support to so-yaml for using yaml file content for values 2025-11-11 16:55:00 -05:00
Jason Ertel
08147e27b0 Merge pull request #15213 from Security-Onion-Solutions/jertel/wip
reduce pcapMaxCount to fit better with max upload size
2025-11-10 19:08:58 -05:00
Josh Patterson
c9153617be Merge pull request #15211 from Security-Onion-Solutions/bravo
Suricata 8.0.2
2025-11-10 17:09:43 -05:00
Josh Patterson
245ceb2d49 suricata defaults and annotation 2025-11-10 16:40:11 -05:00
Jason Ertel
4c65975907 reduce pcapMaxCount to fit better with max upload size 2025-11-10 15:44:05 -05:00
Mike Reeves
dfef7036ce Merge pull request #15209 from Security-Onion-Solutions/TOoSmOotH-patch-1
Update defaults.yaml
2025-11-10 14:53:00 -05:00
Mike Reeves
44594ba726 Update defaults.yaml 2025-11-10 14:24:27 -05:00
Josh Patterson
1876c4d9df fix var name 2025-11-10 14:16:16 -05:00
Josh Patterson
a2ff66b5d0 update annotation 2025-11-10 14:12:20 -05:00
Josh Patterson
e3972dc5af Merge remote-tracking branch 'origin/2.4/dev' into bravo 2025-11-10 13:28:42 -05:00
Josh Patterson
18c0f197b2 suricata bpf 2025-11-10 13:28:19 -05:00
Jorge Reyes
5b371c220c Merge pull request #15207 from Security-Onion-Solutions/reyesj2/forwardnode-sensor 2025-11-10 08:46:12 -06:00
Josh Patterson
78c193f0a2 handle bpf for suricata 8 pcap 2025-11-07 17:40:24 -05:00
Josh Patterson
274295bc97 return exit codes 2025-11-07 17:39:13 -05:00
Josh Patterson
6c7ef622c1 spaces removed from expected output 2025-11-07 17:08:33 -05:00
Josh Patterson
da1cac0d53 tls-log, http-log and syslog outputs deprecated https://github.com/Security-Onion-Solutions/securityonion/issues/15203 2025-11-06 16:32:55 -05:00
reyesj2
a84df14137 rename forward node -> sensor node 2025-11-06 15:23:55 -06:00
Jorge Reyes
4a49f9d004 Merge branch '2.4/dev' into reyesj2/retention 2025-11-06 14:29:08 -06:00
reyesj2
1eb4b5379a show 30d scheduled deletions or 7d scheduled deletions depending on what historical data is available 2025-11-06 14:25:25 -06:00
reyesj2
35c7fc06d7 fix bug showing duplicate backing indices in recommendations 2025-11-06 14:24:58 -06:00
reyesj2
b69d453a68 typo 2025-11-06 14:24:29 -06:00
Josh Patterson
b7e1989d45 resolve block-size not large enough for max fragmented IP packet size warning 2025-11-06 09:49:46 -05:00
Jorge Reyes
202b03b32b Merge pull request #15201 from Security-Onion-Solutions/reyesj2-patch-5
update so-elasticsearch-retention-estimate
2025-11-06 08:18:38 -06:00
reyesj2
1aa871ec94 small fixes 2025-11-05 17:55:57 -06:00
Josh Patterson
4ffbb0bbd9 Merge remote-tracking branch 'origin/2.4/dev' into bravo 2025-11-05 15:22:11 -05:00
Jorge Reyes
f859fe6517 Merge pull request #15192 from Security-Onion-Solutions/securityonion-strelka
strelka use single master image
2025-11-05 08:07:01 -06:00
Jason Ertel
021b425b8b Merge pull request #15198 from Security-Onion-Solutions/jertel/wip
ensure previous setup outcomes are cleared
2025-11-04 16:10:53 -05:00
Jason Ertel
d95122ca01 ensure previous setup outcomes are cleared 2025-11-04 16:02:39 -05:00
Josh Patterson
81d3c7351b Merge pull request #15194 from Security-Onion-Solutions/reyesj2/ea-policy
move off of cmd.script with args \
2025-11-03 17:16:35 -05:00
Josh Patterson
ccb8ffd6eb Update install_agent_grid.sls 2025-11-03 17:05:48 -05:00
reyesj2
5a8ea57a1b move off of cmd.script with args \
https://github.com/saltstack/salt/issues/68298
2025-11-03 15:31:14 -06:00
Josh Patterson
60228ec6e6 Merge pull request #15193 from Security-Onion-Solutions/salt300616
Salt 3006.16
2025-11-03 16:02:25 -05:00
Josh Patterson
574703e551 unlock/lock salt-cloud if installed 2025-11-03 15:39:19 -05:00
Josh Patterson
fa154f1a8f update salt cloud config if configured 2025-11-03 14:12:19 -05:00
reyesj2
635545630b strelka use single master image 2025-11-03 09:36:46 -06:00
Mike Reeves
df8afda999 Merge pull request #15188 from Security-Onion-Solutions/cogburn/multiple-models
Available Models
2025-11-03 09:39:16 -05:00
Corey Ogburn
f80b090c93 Update limits 2025-10-31 14:48:30 -06:00
Corey Ogburn
806173f7e3 Available Models
Utilizes Jason's new Array of Objects UI.
2025-10-31 14:07:30 -06:00
Josh Patterson
2f6c1b82a6 Merge pull request #15185 from Security-Onion-Solutions/salt300616
Upgrade Salt 3006.16
2025-10-31 09:47:01 -04:00
Josh Patterson
b8c2808abe update salt-cloud profile after new code copied 2025-10-30 15:09:40 -04:00
Josh Patterson
9027e4e065 update salt-cloud profile after new code copied 2025-10-30 14:48:48 -04:00
Josh Patterson
8ca5276a0e update cloud profile with local and point to new code 2025-10-30 13:59:08 -04:00
Josh Patterson
ee45a5524d Merge remote-tracking branch 'origin/2.4/dev' into salt300616 2025-10-30 13:13:55 -04:00
Josh Patterson
70d4223a75 update salt-cloud config if salt was upgraded 2025-10-30 13:13:16 -04:00
Jorge Reyes
7ab2840381 Merge pull request #15182 from Security-Onion-Solutions/reyesj2-influxdb-metrics
add manager role to elasticsearch ingest time spent
2025-10-30 12:03:58 -05:00
reyesj2
78c951cb70 add manager role to elastic ingest time spent 2025-10-30 11:15:58 -05:00
Josh Patterson
a0a3a80151 Merge remote-tracking branch 'origin/2.4/dev' into salt300616 2025-10-30 11:57:15 -04:00
Josh Patterson
3ecffd5588 Merge pull request #15181 from Security-Onion-Solutions/volumes
create libvirt volumes directory
2025-10-30 11:31:30 -04:00
Josh Patterson
8ea66bb0e9 create libvirt volumes directory 2025-10-30 11:02:36 -04:00
Jorge Reyes
9359fbbad6 Merge pull request #15176 from Security-Onion-Solutions/reyesj2/ilmpolicyhelp 2025-10-29 16:49:07 -05:00
Josh Patterson
1949be90c2 allow to preserve files 2025-10-29 16:49:59 -04:00
Josh Patterson
30970acfaf var for SALTVERSION in cloud config 2025-10-29 16:05:12 -04:00
Josh Patterson
6d12a8bfa1 handle salt-cloud upgrade during soup 2025-10-29 15:31:46 -04:00
reyesj2
2fb41c8d65 elasticsearch retention estimate 2025-10-29 14:24:43 -05:00
reyesj2
835b2609b6 telegraf - increase esindexsize.sh script timeout 2025-10-29 13:45:55 -05:00
Josh Patterson
10ae53f108 upgrade salt 3006.16 2025-10-29 10:23:44 -04:00
Jason Ertel
68bfceb727 Merge pull request #15170 from Security-Onion-Solutions/jertel/wip
bump version
2025-10-24 16:46:24 -04:00
Jason Ertel
f348c7168f bump version 2025-10-24 16:19:24 -04:00
Jason Ertel
627d9bf45d Merge pull request #15169 from Security-Onion-Solutions/jertel/wip
bump version
2025-10-24 16:18:43 -04:00
Jason Ertel
2aee8ab511 bump version 2025-10-24 16:11:50 -04:00
Mike Reeves
de9d3c9726 Merge pull request #15166 from Security-Onion-Solutions/2.4.190
2.4.190
2025-10-23 14:09:13 -04:00
Mike Reeves
39572f36f4 2.4.190 2025-10-23 14:07:05 -04:00
Jason Ertel
0994cd515a Merge pull request #15161 from Security-Onion-Solutions/jertel/wip
add exclusion toggle
2025-10-21 09:36:45 -04:00
Jason Ertel
bdcd1e099d add exclusion toggle 2025-10-21 09:33:41 -04:00
Jorge Reyes
c64760b5f4 Merge pull request #15153 from Security-Onion-Solutions/reyesj2-patch-1 2025-10-17 07:50:36 -05:00
Jorge Reyes
d2aa60b961 log4j2 settings 2025-10-17 07:40:44 -05:00
Jorge Reyes
83d615d236 Merge pull request #15151 from Security-Onion-Solutions/reyesj2-patch-9
update log4j2 policy for ES json output
2025-10-16 16:25:47 -05:00
reyesj2
e910de0a06 update log4j2 policy for ES json output
Signed-off-by: reyesj2 <94730068+reyesj2@users.noreply.github.com>
2025-10-16 16:19:55 -05:00
Josh Patterson
26b80aba38 Merge pull request #15148 from Security-Onion-Solutions/m0duspwnens-patch-1
do not log set_timezone in setup
2025-10-15 16:58:34 -04:00
Josh Patterson
ee617eeff4 do not log set_timezone in setup
creates additional sosetup.log file
2025-10-15 16:44:24 -04:00
Josh Patterson
463766782c Merge pull request #15147 from Security-Onion-Solutions/amv
omit new hypervisor state name fp
2025-10-15 15:03:31 -04:00
Josh Patterson
d9f70898dd omit new hypervisor state name fp 2025-10-15 14:59:37 -04:00
Mike Reeves
7e15c89510 Merge pull request #15145 from Security-Onion-Solutions/cogburn/add-multiline
Should be multiline
2025-10-15 13:20:26 -04:00
Corey Ogburn
ed5bd19f0e Should be multiline 2025-10-15 09:00:27 -06:00
Josh Patterson
feba97738f Merge pull request #15144 from Security-Onion-Solutions/amv
implement host os overhead based on role
2025-10-15 10:36:24 -04:00
Josh Patterson
348809bdbb implement host os overhead based on role 2025-10-15 10:30:14 -04:00
Jorge Reyes
ca0edb1cab Merge pull request #15141 from Security-Onion-Solutions/reyesj2-logstash 2025-10-14 16:01:01 -05:00
reyesj2
0172f64f15 Merge branch '2.4/dev' of github.com:Security-Onion-Solutions/securityonion into reyesj2-logstash 2025-10-14 15:58:38 -05:00
Jorge Reyes
48f8944e3b Merge pull request #15139 from Security-Onion-Solutions/reyesj2-patch-4
event.module elasticsearch
2025-10-14 15:58:00 -05:00
reyesj2
3e22043ea6 es logging retention 2025-10-14 15:08:51 -05:00
coreyogburn
e572b854b9 Merge pull request #15142 from Security-Onion-Solutions/cogburn/append-prompt
New Config Entries
2025-10-14 13:46:15 -06:00
Corey Ogburn
c8aad2b03b New Config Entries 2025-10-14 13:24:43 -06:00
reyesj2
8773ebc3dc logstash wrappers for troubleshooting 2025-10-14 13:34:33 -05:00
reyesj2
2baf2478da add additional elasticsearch log output in json format for elasticsearch log integration to parse 2025-10-14 12:47:03 -05:00
reyesj2
378d37d74e add event.module to elasticsearch server logs 2025-10-14 12:44:51 -05:00
Josh Patterson
f8c8e5d8e5 Merge pull request #15063 from Security-Onion-Solutions/impssu
Update so-saltstack-update
2025-10-14 11:27:29 -04:00
Josh Patterson
dca38c286a Merge pull request #15137 from Security-Onion-Solutions/amv
allow user to create VMs that mount virtual disk for /nsm. new nsm_total grain
2025-10-14 11:25:57 -04:00
Josh Patterson
860710f5f9 remove .log extension 2025-10-14 11:03:00 -04:00
Josh Patterson
d56af4acab remove .log extension 2025-10-14 10:58:57 -04:00
Josh Patterson
793e98f75c update annotation after failed vm removal from VMs file 2025-10-14 10:37:16 -04:00
Josh Patterson
f9c5aa3fef remove PROCESS_STEPS from hypervisor annotation 2025-10-14 09:36:05 -04:00
Josh Patterson
254e782da6 add volume creation and configuration process steps 2025-10-10 22:15:20 -04:00
Josh Patterson
fe3caf66a1 update failure description 2025-10-10 17:21:09 -04:00
Josh Patterson
09d699432a ui notification of nsm volume creation failure and cleanup of vm inventory in soc grid config for hypervisor 2025-10-10 17:07:02 -04:00
Jason Ertel
79b44586ce Merge pull request #15130 from Security-Onion-Solutions/jertel/wip
missed commit
2025-10-09 20:55:20 -04:00
Jason Ertel
feddd90e41 missed commit 2025-10-09 20:50:09 -04:00
Jason Ertel
ca935e4272 Merge pull request #15127 from Security-Onion-Solutions/jertel/wip
csv delimiter and query name
2025-10-09 15:48:37 -04:00
Jason Ertel
8f75bfb0a4 csv delimiter 2025-10-09 13:02:02 -04:00
Josh Patterson
e551c6e037 owner and perms of volumes 2025-10-09 10:19:25 -04:00
Jorge Reyes
1c5a72ee85 Merge pull request #15124 from Security-Onion-Solutions/reyesj2/es-8188
ignore error for elastic-fleet agent
2025-10-08 14:13:46 -05:00
reyesj2
8a8ea04088 ignore error for elastic-fleet agent 2025-10-08 14:01:18 -05:00
Josh Patterson
92be8df95d Merge pull request #15122 from Security-Onion-Solutions/amv
nsm virtual disk and new nsm_total grain
2025-10-08 14:15:51 -04:00
Josh Patterson
f730e23e30 Merge remote-tracking branch 'origin/2.4/dev' into amv 2025-10-08 14:06:48 -04:00
Josh Patterson
a3e7649a3c minor hypervisor annotation 2025-10-08 13:52:34 -04:00
Josh Patterson
af42c31740 update yaml for annotation 2025-10-08 13:24:54 -04:00
Jason Ertel
a22c9f6bcf Merge pull request #15118 from Security-Onion-Solutions/jertel/wip
support non-async state apply
2025-10-08 13:15:05 -04:00
Jason Ertel
bad9a16ebb support non-async state apply 2025-10-08 13:02:44 -04:00
Josh Patterson
7827e05c24 handle mounting vdb as nsm when nsm set in soc grid config 2025-10-08 12:18:34 -04:00
Josh Patterson
e45b0bf871 var and comment update 2025-10-08 11:51:35 -04:00
Josh Patterson
659c039ba8 handle nsm volume size and non disk passthrough 2025-10-08 10:51:04 -04:00
Josh Patterson
c7edaac42a nsm volume as vdb, os vda by ordering pci slots 2025-10-07 17:20:11 -04:00
Josh Patterson
a1a8f75409 create and mount volume. being mounted as vda 2025-10-07 16:36:23 -04:00
Jorge Reyes
23e25fa2d7 Merge pull request #15111 from Security-Onion-Solutions/reyesj2/es-8188
UPGRADE: ES 8.18.8
2025-10-07 14:03:45 -05:00
Mike Reeves
f077484121 Merge pull request #15114 from Security-Onion-Solutions/filters
Filters
2025-10-07 14:35:00 -04:00
Mike Reeves
c16bf50493 Update files 2025-10-07 14:20:25 -04:00
reyesj2
564374a8fb generate new elastic agents in post soup 2025-10-07 12:21:26 -05:00
Josh Patterson
4ab4264f77 merge 2025-10-07 12:26:58 -04:00
Josh Patterson
60cccb21b4 create volume 2025-10-07 12:20:42 -04:00
reyesj2
39432198cc Elastic 8.18.8 elastic agent build 2025-10-06 16:25:52 -05:00
reyesj2
7af95317db es upgrade 8.18.8 pipeline updates 2025-10-06 16:23:22 -05:00
reyesj2
8675193d1f elasticsearch upgrade 8.18.8 2025-10-06 12:56:31 -05:00
Josh Patterson
ac0d6c57e1 create common.grains state and nsm_total grain 2025-10-06 11:52:35 -04:00
Jorge Reyes
3db6542398 Merge pull request #15105 from Security-Onion-Solutions/reyesj2/logstashout
update logstash fleet output policy
2025-10-03 12:07:36 -05:00
reyesj2
9fd1b9aec1 make sure to pass in variables to json_string.. 2025-10-02 16:38:47 -05:00
reyesj2
e5563eb9b8 send full new ssl config 2025-10-02 15:29:55 -05:00
Josh Patterson
e8de9e3c26 Merge pull request #15103 from Security-Onion-Solutions/byoh
byoh
2025-10-02 15:50:34 -04:00
reyesj2
c8a3603577 update logstash fleet output policy 2025-10-02 14:47:38 -05:00
Josh Patterson
05321cf1ed add --force-cleanup to nvme raid script 2025-10-02 15:03:11 -04:00
Josh Patterson
7deef44ff6 check defaults or pillar file 2025-10-02 11:55:50 -04:00
Mike Reeves
9752d61699 Add Filters 2025-10-01 19:59:28 -04:00
Mike Reeves
6b8e2e2643 Add Filters 2025-10-01 19:58:07 -04:00
Josh Patterson
b1acbf3114 Merge pull request #15098 from Security-Onion-Solutions/byoh
Byoh
2025-10-01 15:06:01 -04:00
Josh Patterson
e3ac1dd1b4 Merge remote-tracking branch 'origin/2.4/dev' into byoh 2025-10-01 14:57:51 -04:00
Josh Patterson
86eca53d4b support for byodmodel 2025-10-01 14:57:25 -04:00
Jason Ertel
bfd3d822b1 Merge pull request #15092 from Security-Onion-Solutions/jertel/wip
updates for wiretap lib
2025-10-01 12:20:06 -04:00
Jason Ertel
030e4961d7 updates for wiretap lib 2025-10-01 12:13:56 -04:00
Matthew Wright
14bd92067b Merge pull request #15091 from Security-Onion-Solutions/mwright/soc_soc-fix
Made lowBalanceColorAlert global
2025-10-01 11:03:50 -04:00
Matthew Wright
066e227325 made lowBalanceColorAlert global 2025-10-01 11:01:10 -04:00
coreyogburn
f1cfb9cd91 Merge pull request #15087 from Security-Onion-Solutions/cogburn/health-timeout
New field for assistant health check
2025-09-30 15:49:52 -06:00
Corey Ogburn
5a2e704909 New field for assistant health check
The health check has a smaller, configurable timeout.
2025-09-30 15:33:20 -06:00
Jorge Reyes
f04e54d1d5 Merge pull request #15086 from Security-Onion-Solutions/reyesj2/fltpatch
less strict exits for fleet configuration
2025-09-30 15:26:50 -05:00
reyesj2
e9af46a8cb less strict exits for fleet configuration 2025-09-30 14:28:42 -05:00
Josh Patterson
b4b051908b Merge pull request #15082 from Security-Onion-Solutions/vlb2
fix hypervisor bridge setup
2025-09-29 17:19:22 -04:00
Jason Ertel
0148e5638c Merge pull request #15080 from Security-Onion-Solutions/jertel/wip
restart registry after upgrading images (in airgap mode)
2025-09-29 17:02:47 -04:00
Josh Patterson
c8814d0632 removed commented code 2025-09-29 16:58:45 -04:00
Jason Ertel
6c892fed78 restart registry after upgrading images (in airgap mode) 2025-09-29 16:47:05 -04:00
Josh Patterson
8043e09ec1 Merge pull request #15076 from Security-Onion-Solutions/vlb2
Vlb2
2025-09-26 15:44:53 -04:00
Josh Patterson
e775299480 so-user target minions with pillar elasticsearch:enabled:true 2025-09-26 15:43:49 -04:00
Josh Patterson
c4ca9c62aa Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-09-26 12:52:37 -04:00
Jorge Reyes
c37aeff364 Merge pull request #15075 from Security-Onion-Solutions/reyesj2/esfleetpatch
update so-elastic-fleet-setup
2025-09-26 11:36:35 -05:00
reyesj2
cdac49052f Merge branch '2.4/dev' of github.com:Security-Onion-Solutions/securityonion into reyesj2/esfleetpatch 2025-09-26 11:32:44 -05:00
reyesj2
8e5fa9576c create disabled so-manager_elasticsearch output policy first, update it then verify it is the only active output 2025-09-26 11:32:25 -05:00
Josh Patterson
25c746bb14 Merge pull request #15067 from Security-Onion-Solutions/vlb2
Vlb2
2025-09-25 16:12:52 -04:00
Josh Patterson
cd04d1e5a7 Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-09-25 16:06:36 -04:00
Josh Patterson
1fb558cc77 managerhype br0 setup 2025-09-25 16:06:25 -04:00
Jason Ertel
7f1b76912c Merge pull request #15072 from Security-Onion-Solutions/jertel/wip
retry kratos pulls since this is the first image to install during setup
2025-09-25 15:45:02 -04:00
Jason Ertel
3a2ceb0b6f retry kratos pulls since this is the first image to install during setup 2025-09-25 15:40:00 -04:00
Matthew Wright
1345756fce Merge pull request #15071 from Security-Onion-Solutions/mwright/temp
Updated default investigation prompt
2025-09-25 15:18:20 -04:00
Matthew Wright
d81d9a0722 small tweak to investigation prompt 2025-09-25 14:45:06 -04:00
Jorge Reyes
55074fda69 Merge pull request #15070 from Security-Onion-Solutions/reyesj2-patch-1
make sure fleet-default-output is not set as either default output p…
2025-09-25 09:55:54 -05:00
Jorge Reyes
23e12811a1 make sure fleet-default-output is not set as either default output policy 2025-09-25 09:51:32 -05:00
Josh Patterson
5d1edf6d86 Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-09-24 17:32:08 -04:00
Josh Patterson
a91e8b26f6 Merge pull request #15066 from Security-Onion-Solutions/vlb2
set interface for network.ip_addrs for hypervisors
2025-09-24 16:51:07 -04:00
Josh Patterson
c836dd2acd set interface for network.ip_addrs for hypervisors 2025-09-24 16:50:29 -04:00
Josh Patterson
e826ea5d04 Merge pull request #15065 from Security-Onion-Solutions/vlb2
update service file, use salt.minion state to update mine_functions
2025-09-24 15:20:31 -04:00
Josh Patterson
3a87af805f update service file, use salt.minion state to update mine_functions 2025-09-24 15:19:46 -04:00
Jorge Reyes
328ac329ec Merge pull request #15064 from Security-Onion-Solutions/reyesj2-patch-1
typo
2025-09-24 09:04:14 -05:00
Jorge Reyes
a3401aad11 typo 2025-09-24 08:56:40 -05:00
Josh Patterson
5a67b89a80 Update so-saltstack-update
add -v -vv and test / dry run mode
2025-09-24 09:49:02 -04:00
Jorge Reyes
431f71cc82 Merge pull request #15047 from Security-Onion-Solutions/reyesj2/es-fleet-patch
rework fleet scripts
2025-09-24 07:45:43 -05:00
Josh Patterson
23a9780ebb Merge pull request #15061 from Security-Onion-Solutions/vlb2
only update mine for managerhype during setup
2025-09-23 15:56:47 -04:00
Josh Patterson
4587301cca only update mine for managerhype during setup 2025-09-23 15:56:00 -04:00
Josh Patterson
9cb8ebbaa7 Merge pull request #15056 from Security-Onion-Solutions/vlb2
Vlb2
2025-09-23 09:05:55 -04:00
Josh Patterson
14ddbd32ad salt-minion service file changes for hypervisor and managerhype 2025-09-22 16:38:40 -04:00
Josh Patterson
4599b95ae7 separate salt-minion service file 2025-09-22 16:37:16 -04:00
reyesj2
c92dc580a2 centralize MINION_ROLE lookup_role 2025-09-19 13:17:52 -05:00
reyesj2
4666aa9818 Merge branch 'reyesj2/es-fleet-patch' of github.com:Security-Onion-Solutions/securityonion into reyesj2/es-fleet-patch 2025-09-19 12:55:08 -05:00
reyesj2
f066baf6ba use only the characters up to the last seen '_' 2025-09-19 12:54:04 -05:00
Jorge Reyes
ba710c9944 import or eval should get updated 2025-09-19 12:26:08 -05:00
reyesj2
198695af03 Merge branch '2.4/dev' of github.com:Security-Onion-Solutions/securityonion into reyesj2/es-fleet-patch 2025-09-19 11:56:53 -05:00
Jorge Reyes
fec78f5fb5 Merge pull request #15051 from Security-Onion-Solutions/reyesj2/patch-lgchk
add oom check to so-log-check
2025-09-19 11:41:55 -05:00
reyesj2
d03dd7ac2d check for oom kill only in the last 24 hours
Signed-off-by: reyesj2 <94730068+reyesj2@users.noreply.github.com>
2025-09-19 11:32:13 -05:00
reyesj2
d2dd52b42a Merge branch 'reyesj2/patch-lgchk' of github.com:Security-Onion-Solutions/securityonion into reyesj2/es-fleet-patch 2025-09-19 11:12:09 -05:00
reyesj2
c9db52433f add oom check to so-log-check
Signed-off-by: reyesj2 <94730068+reyesj2@users.noreply.github.com>
2025-09-19 11:08:42 -05:00
reyesj2
138849d258 more typos 2025-09-18 17:33:42 -05:00
reyesj2
a9ec12e402 Merge branch 'reyesj2/es-fleet-patch' of github.com:Security-Onion-Solutions/securityonion into reyesj2/es-fleet-patch 2025-09-18 16:41:34 -05:00
reyesj2
87281efc24 typo 2025-09-18 16:41:33 -05:00
reyesj2
29ac4f23c6 typo 2025-09-18 16:26:37 -05:00
reyesj2
878a3f8962 flip logic to check there aren't two default policies and fleet-default-output is disabled 2025-09-18 16:05:34 -05:00
reyesj2
21e27bce87 Merge branch 'reyesj2/es-fleet-patch' of github.com:Security-Onion-Solutions/securityonion into reyesj2/es-fleet-patch 2025-09-18 15:42:28 -05:00
reyesj2
336ca0dbbd typos 2025-09-18 15:42:25 -05:00
reyesj2
d9eba3cd0e typo 2025-09-18 15:17:22 -05:00
reyesj2
81b7e2b420 Merge remote-tracking branch 'origin' into reyesj2/es-fleet-patch 2025-09-18 14:34:41 -05:00
reyesj2
cd5483623b update import/eval fleet output config -- try to prevent corrupt dual 'default' output polices from having a successful installation 2025-09-18 14:33:34 -05:00
reyesj2
faa112eddf update last so-elastic-fleet-common functions 2025-09-18 12:18:16 -05:00
reyesj2
f663f22628 elastic_fleet_integration_id 2025-09-18 10:27:54 -05:00
reyesj2
8b07ff453d elastic_fleet_integration_policy_package_version 2025-09-18 10:21:07 -05:00
reyesj2
24a0fa3f6d add fleet_api wrapper for curl retries 2025-09-18 10:15:57 -05:00
reyesj2
a5011b398d add err check and retries to elastic_fleet_integration_policy_package_name and associated scripts 2025-09-18 09:39:56 -05:00
reyesj2
5b70398c0a add error check & retries to elastic_fleet_integration_policy_names and associated scripts 2025-09-17 15:35:20 -05:00
reyesj2
f3aaee1e41 update elastic_fleet_agent_policy_ids scripts already check rc 2025-09-17 14:59:41 -05:00
reyesj2
d0e875928d add error checking and retries for elastic_fleet_installed_packages & associated script 2025-09-17 14:59:13 -05:00
reyesj2
3e16bc8335 Merge branch '2.4/dev' of github.com:Security-Onion-Solutions/securityonion into reyesj2/es-fleet-patch 2025-09-17 14:37:43 -05:00
Doug Burks
c1d85493df Merge pull request #15045 from Security-Onion-Solutions/dougburks-patch-1
Update 2-4.yml
2025-09-17 14:23:23 -04:00
Doug Burks
e01d0f81ea Update 2-4.yml 2025-09-17 14:22:40 -04:00
Jason Ertel
376d0f3295 Merge pull request #15044 from Security-Onion-Solutions/jertel/wip
bump version
2025-09-17 14:22:02 -04:00
Jason Ertel
4418623f73 bump version 2025-09-17 14:20:44 -04:00
Doug Burks
d1f4e26e29 Merge pull request #15043 from Security-Onion-Solutions/2.4/dev
2.4.180
2025-09-17 14:15:32 -04:00
Doug Burks
5166db1caa Merge pull request #15042 from Security-Onion-Solutions/2.4/main
Merge pull request #14917 from Security-Onion-Solutions/2.4/dev
2025-09-17 13:13:46 -04:00
Doug Burks
ff5ad586af Merge pull request #15040 from Security-Onion-Solutions/dougburks-patch-1
2.4.180
2025-09-17 13:00:26 -04:00
reyesj2
9e24d21282 remove unused functions from so-elastic-fleet-common 2025-09-17 11:41:27 -05:00
reyesj2
5806999f63 add error check & retries to elastic_fleet_bulk_package_install 2025-09-17 11:39:06 -05:00
Doug Burks
4dae1afe0b Add files via upload 2025-09-17 12:37:29 -04:00
Doug Burks
456cad1ada Update DOWNLOAD_AND_VERIFY_ISO.md for 2.4.180 2025-09-17 12:36:55 -04:00
reyesj2
063a2b3348 update elastic_fleet_package_version_check & elastic_fleet_package_install to add error checking + retries. Update related scripts 2025-09-16 21:56:53 -05:00
reyesj2
bcd2e95fbe add error checking and retries to elastic_fleet_integration_policy_upgrade 2025-09-16 21:22:03 -05:00
reyesj2
94e8cd84e6 because of more aggressive exits use salt to rerun script as needed 2025-09-16 21:07:33 -05:00
reyesj2
948d72c282 add error check and retry to elastic_fleet_integration_update 2025-09-16 21:07:02 -05:00
reyesj2
bdeb92ab05 add err check and retries for elastic_fleet_integration_create 2025-09-16 20:30:45 -05:00
reyesj2
fdb5ad810a add err check and retries around func elastic_fleet_policy_create 2025-09-16 20:10:48 -05:00
reyesj2
f588a80ec7 fix jq error when indices don't exist (seen on fresh installs when fleet hasn't ever been installed) 2025-09-16 10:37:26 -05:00
Jorge Reyes
562b7e54cb Merge pull request #15031 from Security-Onion-Solutions/reyesj2/kfoutput
fix case of broken kafka output policy when new receiver is added and…
2025-09-15 15:33:48 -05:00
Jorge Reyes
3c847bca8b Merge pull request #15034 from Security-Onion-Solutions/reyesj2/patch31
run so-elastic-agent-gen-installers
2025-09-15 15:28:42 -05:00
reyesj2
ce2cc26224 run so-elastic-agent-gen-installers 2025-09-15 15:25:38 -05:00
Jorge Reyes
f3c574679c Merge pull request #15033 from Security-Onion-Solutions/reyesj2/patch31
8.18.6 agent
2025-09-15 15:21:46 -05:00
reyesj2
5da3fed1ce 8.18.6 agent 2025-09-15 15:19:43 -05:00
reyesj2
e6bcf5db6b fix case of broken kafka output policy when new receiver is added and secret storage was overwritten 2025-09-15 13:46:02 -05:00
Jorge Reyes
4d24c57903 Merge pull request #15028 from Security-Onion-Solutions/reyesj2/ea-alerter
agent monitor template & dataset name update
2025-09-12 14:45:20 -05:00
reyesj2
0606c0a454 agent monitor template & dataset name update 2025-09-12 14:26:22 -05:00
Josh Patterson
bb984e05e3 Merge pull request #15026 from Security-Onion-Solutions/vlb2
fix role check
2025-09-12 14:34:18 -04:00
Jorge Reyes
b35b0aaf2c Merge pull request #14941 from Security-Onion-Solutions/reyesj2/lgest
zeek dns.resolved_ip
2025-09-12 13:22:40 -05:00
Josh Patterson
62f04fa5dd fix role check 2025-09-12 14:09:30 -04:00
Josh Brower
d89df5f0dd Merge pull request #15025 from Security-Onion-Solutions/2.4/fixes
Parsing fix
2025-09-12 13:44:03 -04:00
DefensiveDepth
f0c1922600 Support endpoint logs with no host.ip field 2025-09-12 13:31:34 -04:00
DefensiveDepth
ab2cdd18ed Support endpoint logs with no host.ip field 2025-09-12 13:29:43 -04:00
Jorge Reyes
889bb7ddf4 Merge pull request #15024 from Security-Onion-Solutions/reyesj2/pypy
fix analyzers and upgrade deps
2025-09-12 11:11:34 -05:00
reyesj2
a959f90d0b Merge remote-tracking branch 'origin/2.4/dev' into reyesj2/pypy 2025-09-12 11:05:54 -05:00
Jorge Reyes
a54cd004d6 Merge pull request #15013 from Security-Onion-Solutions/reyesj2/kfoutput
update kafka output policy
2025-09-12 07:34:54 -05:00
Jorge Reyes
5100032fbd Merge pull request #15022 from Security-Onion-Solutions/reyesj2/cfqdn-recv
receiver custom fqdn
2025-09-11 16:33:41 -05:00
reyesj2
0f235baa7e receiver custom fqdn 2025-09-11 16:14:43 -05:00
Jorge Reyes
e5660b8c8e Merge pull request #15020 from Security-Onion-Solutions/reyesj2/essuriroll
suricata metadata index rollover 1d -> 30d
2025-09-11 16:03:30 -05:00
reyesj2
588a1b86d1 suricata metadata index rollover 1d -> 30d 2025-09-11 15:46:45 -05:00
Jorge Reyes
46f0afa24b Merge pull request #15019 from Security-Onion-Solutions/reyesj2/ea-alerter
lower filestream fingerprint length
2025-09-11 14:34:46 -05:00
reyesj2
a7651b2734 lower filestream fingerprint length 2025-09-11 14:30:49 -05:00
reyesj2
890f76e45c avoid delay in log ingest after a forced kafka output policy update 2025-09-10 20:21:11 -05:00
Josh Patterson
03892bad5e Merge pull request #15015 from Security-Onion-Solutions/vlb2
Vlb2
2025-09-10 14:58:41 -04:00
Jorge Reyes
e6eecc93c8 Merge pull request #15012 from Security-Onion-Solutions/reyesj2/ea-alerter
add configurable realert threshold per agent
2025-09-10 13:19:21 -05:00
reyesj2
8dc0f8d20e fix elastic agent ssl unpack error 2025-09-10 12:49:30 -05:00
reyesj2
fbdc0c4705 add configurable realert threshold per agent 2025-09-10 10:56:09 -05:00
Josh Patterson
d1a2b57aa2 Merge pull request #15011 from Security-Onion-Solutions/hideroni
don't show sensoroni config changes
2025-09-10 09:15:55 -04:00
Josh Patterson
f5ec1d4b7c don't show sensoroni config changes 2025-09-10 09:09:02 -04:00
Jorge Reyes
0aa556e375 Merge pull request #15009 from Security-Onion-Solutions/reyesj2/ea-alerter
so-elastic-agent-monitor
2025-09-09 17:00:39 -05:00
Josh Patterson
d9e86c15bc Merge pull request #15010 from Security-Onion-Solutions/vlb2
fix repo files to remove
2025-09-09 17:15:52 -04:00
Josh Patterson
4107fa006f fix repo files to remove 2025-09-09 16:51:42 -04:00
reyesj2
29980ea958 offline threshold check 2025-09-09 15:39:55 -05:00
reyesj2
8f36d2ec00 update log file name 2025-09-09 15:38:50 -05:00
coreyogburn
10511b8431 Merge pull request #15008 from Security-Onion-Solutions/cogburn/fix-templates
Fix Index Patterns
2025-09-09 14:03:36 -06:00
Corey Ogburn
2535ae953d Fix Index Patterns
so-assistant-chat and so-assistant-session both had templates with a trailing dash that prevented the pattern from applying to the name of the indices.
2025-09-09 14:00:01 -06:00
coreyogburn
2f68cd7483 Merge pull request #14991 from Security-Onion-Solutions/cogburn/wip-module
Cogburn/wip module
2025-09-09 10:32:06 -06:00
reyesj2
6655276410 force update to kafka-fleet-output-policy 2025-09-08 21:13:29 -05:00
reyesj2
9f7bcb0f7d add --force flag to so-kafka-fleet-output-policy & default to using fleet secret storage for client key 2025-09-08 21:13:11 -05:00
Corey Ogburn
aa43177d8c Fix Setting Name
enabledInSoc => enabled
2025-09-08 09:13:25 -06:00
Matthew Wright
12959d114c added threshold config fields for assistant 2025-09-08 09:13:25 -06:00
reyesj2
855b489c4b datastream 2025-09-08 09:13:24 -06:00
Corey Ogburn
673f9cb544 Responding to Feedback 2025-09-08 09:13:24 -06:00
Corey Ogburn
0a3ff47008 Cleanup Annotations
Removed fields no longer need annotations.
2025-09-08 09:13:24 -06:00
Corey Ogburn
834e34128d Non-dev URL 2025-09-08 09:13:23 -06:00
Corey Ogburn
73776f8d11 Cleaning up New ES Indexes 2025-09-08 09:13:23 -06:00
Corey Ogburn
120e61e45c ClientParams
Removed investigation prompt from module settings and moved to client settings, added enabledInSoc.
2025-09-08 09:13:23 -06:00
Corey Ogburn
fc2d450de0 Update Settings
The apiKey will be built off of the license rather than a new setting. The model is hardcoded for now at the AI Gateway level. We're going to use the investigationPrompt as a trigger for the feature being visible in the UI but by default will be blank for now.
2025-09-08 09:13:22 -06:00
Corey Ogburn
cea4eaf081 Updated Assistant Mapping 2025-09-08 09:13:22 -06:00
Corey Ogburn
b1753f86f9 New Message Structure 2025-09-08 09:13:22 -06:00
Corey Ogburn
6323fbf46b Content Object 2025-09-08 09:13:21 -06:00
Corey Ogburn
ba601c39b3 Rough Go at New Mappings/Settings 2025-09-08 09:13:21 -06:00
Corey Ogburn
ec27517bdd New Config Values
New config values with annotations and defaults.

Updated Nginx config to allow streaming requests to not be buffered on the way to the client.
2025-09-08 09:13:08 -06:00
Josh Brower
624ec3c93e Merge pull request #15003 from Security-Onion-Solutions/fix/wording
Make it clear that Fleet Nodes will need to be reinstalled
2025-09-08 09:10:43 -04:00
Josh Brower
f318a84c18 Update so-elastic-fleet-reset 2025-09-08 09:03:33 -04:00
Josh Patterson
8cca58dba9 Merge pull request #14998 from Security-Onion-Solutions/vlb2
manager do hypervisor things
2025-09-05 17:13:37 -04:00
Jason Ertel
6c196ea61a Merge branch '2.4/dev' into vlb2 2025-09-05 17:11:10 -04:00
Josh Patterson
207572f2f9 remove debug added to fail_setup 2025-09-05 14:16:03 -04:00
Josh Patterson
4afc986f48 firewall and logstash pipeline for managerhype 2025-09-05 13:14:47 -04:00
Jorge Reyes
ba5d140d4b Merge pull request #14996 from Security-Onion-Solutions/reyesj2/ea-alerter
so-elastic-agent-monitor
2025-09-05 10:41:59 -05:00
reyesj2
348f9dcaec prevent multiple script instances using file lock 2025-09-05 10:01:24 -05:00
reyesj2
915b9e7bd7 use logrotate 2025-09-05 09:22:44 -05:00
reyesj2
dfec29d18e custom kquery 2025-09-04 15:37:28 -05:00
Josh Patterson
77fef02116 Merge pull request #14994 from Security-Onion-Solutions/vlb2
pass pillar properly
2025-09-04 11:06:31 -04:00
Josh Patterson
38ef4a6046 pass pillar properly 2025-09-04 11:02:27 -04:00
Josh Patterson
f3328c41fb Merge pull request #14990 from Security-Onion-Solutions/vlb2
merge with 2.4/dev
2025-09-03 10:37:46 -04:00
Josh Patterson
a007fa6505 Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-09-03 09:52:49 -04:00
reyesj2
1a32a0897c Merge remote-tracking branch 'origin/2.4/dev' into reyesj2/ea-alerter 2025-09-02 17:11:21 -05:00
reyesj2
e26310d172 elastic agent offline alerter
Signed-off-by: reyesj2 <94730068+reyesj2@users.noreply.github.com>
2025-09-02 17:00:03 -05:00
coreyogburn
c7cdb0b466 Merge pull request #14986 from Security-Onion-Solutions/cogburn/internal-reverse
Move EnableReverseLookup
2025-09-02 15:25:19 -06:00
Corey Ogburn
df0b484b45 More Descriptive Description
Include instructions for how to add local lookups and a help link.
2025-09-02 15:07:13 -06:00
Corey Ogburn
2181cddf49 Move EnableReverseLookup
Move EnableReverseLookup and it's annotation from ClientParams to ServerConfig.
2025-09-02 14:09:55 -06:00
Jorge Reyes
a2b6968cef Merge pull request #14975 from Security-Onion-Solutions/reyesj2/es8186
ES 8.18.6 upgrade
2025-09-02 10:14:33 -05:00
Josh Patterson
285fbc2783 Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-09-02 09:23:24 -04:00
Josh Patterson
94c5a1fd98 Merge pull request #14980 from Security-Onion-Solutions/mikebond
Mikebond
2025-08-29 11:08:17 -04:00
Mike Reeves
19362fe5e5 Update so-combine-bond 2025-08-29 11:06:25 -04:00
Josh Patterson
a7a81e9825 always manage script, only run it if bond0 exists 2025-08-29 11:05:42 -04:00
Mike Reeves
31484d1158 Merge pull request #14978 from Security-Onion-Solutions/mikebond
only manage bond script if bond0 exists
2025-08-29 10:07:24 -04:00
Josh Patterson
f51cd008f2 only manage bond script if bond0 exists 2025-08-29 10:04:56 -04:00
reyesj2
a5675a79fe es 8.18.6 pipeline upd 2025-08-28 19:45:17 -05:00
reyesj2
1ea7b3c09f es 8.18.6 2025-08-28 18:27:56 -05:00
Jorge Reyes
d9127a288f Merge pull request #14957 from Security-Onion-Solutions/reyesj2-patch-6
enable additional fleetnode state
2025-08-28 14:19:03 -05:00
Josh Patterson
23ae259c82 Merge pull request #14972 from Security-Onion-Solutions/vlb2
Vlb2
2025-08-28 10:41:23 -04:00
Josh Patterson
ebb78bc9bd Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-08-28 09:21:33 -04:00
Josh Patterson
e5920b6465 add managerhype back to whiptail 2025-08-28 09:21:20 -04:00
Mike Reeves
153a99a002 Merge pull request #14971 from Security-Onion-Solutions/mikebond
and nic channel customization
2025-08-27 18:42:18 -04:00
Josh Patterson
69a5e1e2f5 remove md file 2025-08-27 15:14:15 -04:00
Josh Patterson
0858160be2 support for modifying nic channels 2025-08-27 14:51:57 -04:00
Mike Reeves
ccd79c814d Add script for bond0 channels 2025-08-27 09:53:37 -04:00
Josh Patterson
45f25ca62d Merge pull request #14966 from Security-Onion-Solutions/vlb2
managerhype
2025-08-26 15:07:36 -04:00
Josh Patterson
a8a01b8191 Merge branch 'bravo' into vlb2 2025-08-26 14:59:23 -04:00
Josh Patterson
ac2c044a94 Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-08-26 14:55:06 -04:00
Josh Patterson
e10d00d114 support for managerhype 2025-08-26 14:54:37 -04:00
Josh Patterson
cbdd369a18 ensure x509 in mine 2025-08-25 08:39:55 -04:00
reyesj2
b2e7f58b3d analyzer test updates 2025-08-22 17:36:48 -05:00
reyesj2
a6600b8762 elasticsearch dep upgrades 2025-08-22 17:11:06 -05:00
reyesj2
5479d49379 greynoise breakup long line for linter 2025-08-22 16:00:05 -05:00
Jason Ertel
304985b61e Merge pull request #14959 from Security-Onion-Solutions/jertel/wip
rpt
2025-08-22 16:55:45 -04:00
coreyogburn
d6c725299b Merge pull request #14956 from Security-Onion-Solutions/cogburn/playbook-repo-name
Ruleset Name UiElement
2025-08-22 14:02:42 -06:00
Corey Ogburn
d99857002d Improved Label
The underlying field is called "rulesetName" but for playbook repos we're not talking about rulesets. Improved the label for user experience.
2025-08-22 13:18:22 -06:00
Corey Ogburn
2a6c74917e Ruleset Name UiElement
Add a missing UiElement so all the repo fields are represented in the UI.
2025-08-22 13:00:17 -06:00
reyesj2
9f0bd4bad3 spamhaus enable multiline annotation on nameservers entries 2025-08-22 13:51:05 -05:00
reyesj2
924b06976c spamhaus config typos 2025-08-22 13:50:40 -05:00
Jason Ertel
1357f19e48 update wording 2025-08-22 13:25:25 -04:00
Jason Ertel
c91e9ea4e0 return to normalcy 2025-08-22 13:23:19 -04:00
reyesj2
c2c96dad6e bump version 2025-08-22 08:43:48 -05:00
reyesj2
1a08833e77 typo 2025-08-22 08:41:03 -05:00
reyesj2
d16dfcf4e8 emailrep dep upgrades 2025-08-21 16:22:48 -05:00
reyesj2
b79c7b0540 sublime dep upgrades 2025-08-21 16:17:44 -05:00
reyesj2
9f45792217 pulsedive dep upgrades 2025-08-21 16:07:08 -05:00
reyesj2
d3108c3549 greynoise dep upgrade + use community version with no auth 2025-08-21 14:30:21 -05:00
reyesj2
7d883cb5e0 echotrail api no longer available 2025-08-21 12:38:00 -05:00
reyesj2
ebd81c1df9 otx dep upgrades 2025-08-21 12:22:47 -05:00
reyesj2
418dbee9fa virustotal dep upgrades 2025-08-21 12:15:13 -05:00
reyesj2
cccc3bf625 urlscan dep upgrades 2025-08-21 12:06:35 -05:00
reyesj2
a3e0072631 update readme threatfox uses auth for api now 2025-08-21 11:48:17 -05:00
reyesj2
220e485312 threatfox dep upgrade + use auth for api access 2025-08-21 11:47:54 -05:00
reyesj2
67f8fca043 spamhaus dep upgrades 2025-08-21 11:32:13 -05:00
reyesj2
0e0ab8384c localfile dep upgrade 2025-08-21 11:26:59 -05:00
reyesj2
58228f70ca malwarehashregistry dep upgrades 2025-08-21 11:16:28 -05:00
reyesj2
7968de06b4 enable access to global stig pillar 2025-08-21 11:06:29 -05:00
Mike Reeves
87fdd90f56 Merge branch '2.4/dev' of github.com:Security-Onion-Solutions/securityonion into 2.4/dev 2025-08-21 10:39:34 -04:00
Josh Patterson
65e7e56fbe Merge pull request #14950 from Security-Onion-Solutions/180soup
180 soup base
2025-08-21 09:50:53 -04:00
Josh Patterson
424fdff934 180 soup base 2025-08-21 09:43:30 -04:00
Jorge Reyes
f72996d9d1 Merge pull request #14949 from Security-Onion-Solutions/reyesj2-patch-7
update pcap permissions when no stenographer user exists
2025-08-21 08:33:30 -05:00
reyesj2
d77556c672 pcap dir 2025-08-21 08:25:48 -05:00
reyesj2
c412e9bad2 malwarebazaar api uses auth 2025-08-20 21:04:05 -05:00
reyesj2
87a28e8ce7 malwarebazaar dep upgrades + use auth 2025-08-20 20:59:23 -05:00
reyesj2
9ca0c7d53a urlhaus dep upgrades + update to use authenticated abusech api 2025-08-20 17:20:10 -05:00
reyesj2
2e94e452ed whoislookup py 3.13 2025-08-20 16:39:13 -05:00
reyesj2
6a0d40ee0d leave requirements.txt as is 2025-08-20 16:20:26 -05:00
reyesj2
0cebcf4432 upgrade whoislookup deps 2025-08-20 16:09:08 -05:00
reyesj2
ed0e24fcaf Merge remote-tracking branch 'origin/2.4/dev' into reyesj2/ol9stg 2025-08-20 12:10:04 -05:00
reyesj2
24be2f869b enable stig on fleet nodes 2025-08-20 12:08:50 -05:00
reyesj2
f8058a4a3a disable showing large stig profile update in salt log 2025-08-20 12:06:54 -05:00
reyesj2
d0ba6df2fc remove any "" from dns.resolved_ip 2025-08-19 13:44:24 -05:00
reyesj2
95bee91b12 zeek dns.resolved_ip 2025-08-19 11:20:59 -05:00
Jason Ertel
751b5bd556 switch version for tests 2025-08-19 10:11:50 -04:00
Jason Ertel
77273449c9 fix typo 2025-08-18 16:58:52 -04:00
Jason Ertel
46e1f1bc5c fix typo 2025-08-18 16:12:34 -04:00
Jason Ertel
884bec7465 fix typo 2025-08-18 15:01:49 -04:00
Jason Ertel
8d3220f94b fix salt issue 2025-08-18 14:31:01 -04:00
Jason Ertel
9cb42911dc Merge branch '2.4/dev' into jertel/wip 2025-08-18 09:54:58 -04:00
Jason Ertel
a3cc6f025e reports 2025-08-18 09:54:40 -04:00
Jorge Reyes
6fae4a9974 Merge pull request #14933 from Security-Onion-Solutions/reyesj2/ol9stg
profile update
2025-08-15 16:26:11 -05:00
reyesj2
f7a1a3a172 gui / nongui profile 2025-08-15 16:07:54 -05:00
reyesj2
292e1ad782 use chrony system default 2025-08-15 15:19:31 -05:00
reyesj2
af1fe86586 update chrony config 2025-08-15 15:16:36 -05:00
Josh Patterson
97100cdfdd Merge pull request #14930 from Security-Onion-Solutions/vlb2
Vlb2
2025-08-14 16:37:15 -04:00
Josh Patterson
5f60ef1541 Merge remote-tracking branch 'origin/2.4/dev' into vlb2 2025-08-14 16:36:37 -04:00
Josh Patterson
c7e7a0a871 add more detail to fail_setup output 2025-08-14 16:36:09 -04:00
reyesj2
f09eff530e profile upd 2025-08-14 15:17:01 -05:00
reyesj2
50b34a116a disable rpm verify hash, salt packages are modified before install for salt bootstrap process 2025-08-14 15:02:59 -05:00
reyesj2
42874fb0d0 Merge remote-tracking branch 'origin/2.4/dev' into reyesj2/ol9stg 2025-08-13 12:50:24 -05:00
Josh Patterson
482847187c Merge pull request #14925 from Security-Onion-Solutions/vlb2
firewall allow hypervisor for managersearch and standalone
2025-08-12 16:45:27 -04:00
reyesj2
a19b99268d don't create unused zeek home directory 2025-08-12 15:44:50 -05:00
reyesj2
3c5a03d7b6 fix /nsm/pcap no group/user ownership 2025-08-12 15:35:30 -05:00
reyesj2
c1a5c2b2d1 set elasticfleet aritifact registry artifact file permissions 2025-08-12 14:39:35 -05:00
Josh Patterson
baf0f7ba95 firewall allow hypervisor for managersearch and standalone 2025-08-12 14:08:15 -04:00
Mike Reeves
ee27965314 Merge pull request #14922 from Security-Onion-Solutions/TOoSmOotH-patch-1
Update VERSION
2025-08-12 11:00:20 -04:00
Mike Reeves
d02093295b Update 2-4.yml 2025-08-12 10:59:17 -04:00
Mike Reeves
6381444fdc Update VERSION 2025-08-12 10:58:11 -04:00
Mike Reeves
b307667ae2 Merge remote-tracking branch 'origin/2.4/main' into 2.4/dev 2025-08-12 09:44:02 -04:00
reyesj2
4a4146f515 ol9 profile update 2025-08-05 13:02:44 -05:00
Josh Patterson
378ecad94c Merge pull request #14893 from Security-Onion-Solutions/vlb2
Vlb2
2025-07-30 16:38:47 -04:00
Josh Patterson
58ffe576d7 add pci mappings for sos hw 2025-07-16 12:09:39 -04:00
Josh Patterson
b0a515f2c3 update base cloud image location 2025-07-16 12:09:01 -04:00
Josh Patterson
59f8544324 Merge pull request #14778 from Security-Onion-Solutions/vlb2
hardware virtualization
2025-06-25 17:22:53 -04:00
357 changed files with 189061 additions and 162940 deletions

View File

@@ -30,6 +30,9 @@ body:
- 2.4.150
- 2.4.160
- 2.4.170
- 2.4.180
- 2.4.190
- 2.4.200
- Other (please provide detail below)
validations:
required: true

View File

@@ -4,7 +4,7 @@ on:
pull_request:
paths:
- "salt/sensoroni/files/analyzers/**"
- "salt/manager/tools/sbin"
- "salt/manager/tools/sbin/**"
jobs:
build:

View File

@@ -1,17 +1,17 @@
### 2.4.170-20250812 ISO image released on 2025/08/12
### 2.4.190-20251024 ISO image released on 2025/10/24
### Download and Verify
2.4.170-20250812 ISO image:
https://download.securityonion.net/file/securityonion/securityonion-2.4.170-20250812.iso
2.4.190-20251024 ISO image:
https://download.securityonion.net/file/securityonion/securityonion-2.4.190-20251024.iso
MD5: 50ECAAD05736298452DECEAE074FA773
SHA1: 1B1EB520DE61ECC4BF34E512DAFE307317D7666A
SHA256: 87D176A48A58BAD1C2D57196F999BED23DE9B526226E3754F0C166C866CCDC1A
MD5: 25358481FB876226499C011FC0710358
SHA1: 0B26173C0CE136F2CA40A15046D1DFB78BCA1165
SHA256: 4FD9F62EDA672408828B3C0C446FE5EA9FF3C4EE8488A7AB1101544A3C487872
Signature for ISO image:
https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.170-20250812.iso.sig
https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.190-20251024.iso.sig
Signing key:
https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.4/main/KEYS
@@ -25,22 +25,22 @@ wget https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.
Download the signature file for the ISO:
```
wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.170-20250812.iso.sig
wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.190-20251024.iso.sig
```
Download the ISO image:
```
wget https://download.securityonion.net/file/securityonion/securityonion-2.4.170-20250812.iso
wget https://download.securityonion.net/file/securityonion/securityonion-2.4.190-20251024.iso
```
Verify the downloaded ISO image using the signature file:
```
gpg --verify securityonion-2.4.170-20250812.iso.sig securityonion-2.4.170-20250812.iso
gpg --verify securityonion-2.4.190-20251024.iso.sig securityonion-2.4.190-20251024.iso
```
The output should show "Good signature" and the Primary key fingerprint should match what's shown below:
```
gpg: Signature made Fri 08 Aug 2025 06:24:56 PM EDT using RSA key ID FE507013
gpg: Signature made Thu 23 Oct 2025 07:21:46 AM 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.

View File

@@ -1 +1 @@
2.4.170
2.4.200

View File

@@ -262,6 +262,9 @@ base:
- minions.adv_{{ grains.id }}
- kafka.nodes
- kafka.soc_kafka
- stig.soc_stig
- elasticfleet.soc_elasticfleet
- elasticfleet.adv_elasticfleet
'*_import':
- node_data.ips
@@ -319,10 +322,12 @@ base:
- elasticfleet.adv_elasticfleet
- minions.{{ grains.id }}
- minions.adv_{{ grains.id }}
- stig.soc_stig
'*_hypervisor':
- minions.{{ grains.id }}
- minions.adv_{{ grains.id }}
- stig.soc_stig
'*_desktop':
- minions.{{ grains.id }}

View File

@@ -0,0 +1,91 @@
#!/opt/saltstack/salt/bin/python3
# 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."
"""
Salt execution module for hypervisor operations.
This module provides functions for managing hypervisor configurations,
including VM file management.
"""
import json
import logging
import os
log = logging.getLogger(__name__)
__virtualname__ = 'hypervisor'
def __virtual__():
"""
Only load this module if we're on a system that can manage hypervisors.
"""
return __virtualname__
def remove_vm_from_vms_file(vms_file_path, vm_hostname, vm_role):
"""
Remove a VM entry from the hypervisorVMs file.
Args:
vms_file_path (str): Path to the hypervisorVMs file
vm_hostname (str): Hostname of the VM to remove (without role suffix)
vm_role (str): Role of the VM
Returns:
dict: Result dictionary with success status and message
CLI Example:
salt '*' hypervisor.remove_vm_from_vms_file /opt/so/saltstack/local/salt/hypervisor/hosts/hypervisor1VMs node1 nsm
"""
try:
# Check if file exists
if not os.path.exists(vms_file_path):
msg = f"VMs file not found: {vms_file_path}"
log.error(msg)
return {'result': False, 'comment': msg}
# Read current VMs
with open(vms_file_path, 'r') as f:
content = f.read().strip()
vms = json.loads(content) if content else []
# Find and remove the VM entry
original_count = len(vms)
vms = [vm for vm in vms if not (vm.get('hostname') == vm_hostname and vm.get('role') == vm_role)]
if len(vms) < original_count:
# VM was found and removed, write back to file
with open(vms_file_path, 'w') as f:
json.dump(vms, f, indent=2)
# Set socore:socore ownership (939:939)
os.chown(vms_file_path, 939, 939)
msg = f"Removed VM {vm_hostname}_{vm_role} from {vms_file_path}"
log.info(msg)
return {'result': True, 'comment': msg}
else:
msg = f"VM {vm_hostname}_{vm_role} not found in {vms_file_path}"
log.warning(msg)
return {'result': False, 'comment': msg}
except json.JSONDecodeError as e:
msg = f"Failed to parse JSON in {vms_file_path}: {str(e)}"
log.error(msg)
return {'result': False, 'comment': msg}
except Exception as e:
msg = f"Failed to remove VM {vm_hostname}_{vm_role} from {vms_file_path}: {str(e)}"
log.error(msg)
return {'result': False, 'comment': msg}

View File

@@ -7,12 +7,14 @@
"""
Salt module for managing QCOW2 image configurations and VM hardware settings. This module provides functions
for modifying network configurations within QCOW2 images and adjusting virtual machine hardware settings.
It serves as a Salt interface to the so-qcow2-modify-network and so-kvm-modify-hardware scripts.
for modifying network configurations within QCOW2 images, adjusting virtual machine hardware settings, and
creating virtual storage volumes. It serves as a Salt interface to the so-qcow2-modify-network,
so-kvm-modify-hardware, and so-kvm-create-volume scripts.
The module offers two main capabilities:
The module offers three main capabilities:
1. Network Configuration: Modify network settings (DHCP/static IP) within QCOW2 images
2. Hardware Configuration: Adjust VM hardware settings (CPU, memory, PCI passthrough)
3. Volume Management: Create and attach virtual storage volumes for NSM data
This module is intended to work with Security Onion's virtualization infrastructure and is typically
used in conjunction with salt-cloud for VM provisioning and management.
@@ -244,3 +246,90 @@ def modify_hardware_config(vm_name, cpu=None, memory=None, pci=None, start=False
except Exception as e:
log.error('qcow2 module: An error occurred while executing the script: {}'.format(e))
raise
def create_volume_config(vm_name, size_gb, start=False):
'''
Usage:
salt '*' qcow2.create_volume_config vm_name=<name> size_gb=<size> [start=<bool>]
Options:
vm_name
Name of the virtual machine to attach the volume to
size_gb
Volume size in GB (positive integer)
This determines the capacity of the virtual storage volume
start
Boolean flag to start the VM after volume creation
Optional - defaults to False
Examples:
1. **Create 500GB Volume:**
```bash
salt '*' qcow2.create_volume_config vm_name='sensor1_sensor' size_gb=500
```
This creates a 500GB virtual volume for NSM storage
2. **Create 1TB Volume and Start VM:**
```bash
salt '*' qcow2.create_volume_config vm_name='sensor1_sensor' size_gb=1000 start=True
```
This creates a 1TB volume and starts the VM after attachment
Notes:
- VM must be stopped before volume creation
- Volume is created as a qcow2 image and attached to the VM
- This is an alternative to disk passthrough via modify_hardware_config
- Volume is automatically attached to the VM's libvirt configuration
- Requires so-kvm-create-volume script to be installed
- Volume files are stored in the hypervisor's VM storage directory
Description:
This function creates and attaches a virtual storage volume to a KVM virtual machine
using the so-kvm-create-volume script. It creates a qcow2 disk image of the specified
size and attaches it to the VM for NSM (Network Security Monitoring) storage purposes.
This provides an alternative to physical disk passthrough, allowing flexible storage
allocation without requiring dedicated hardware. The VM can optionally be started
after the volume is successfully created and attached.
Exit Codes:
0: Success
1: Invalid parameters
2: VM state error (running when should be stopped)
3: Volume creation error
4: System command error
255: Unexpected error
Logging:
- All operations are logged to the salt minion log
- Log entries are prefixed with 'qcow2 module:'
- Volume creation and attachment operations are logged
- Errors include detailed messages and stack traces
- Final status of volume creation is logged
'''
# Validate size_gb parameter
if not isinstance(size_gb, int) or size_gb <= 0:
raise ValueError('size_gb must be a positive integer.')
cmd = ['/usr/sbin/so-kvm-create-volume', '-v', vm_name, '-s', str(size_gb)]
if start:
cmd.append('-S')
log.info('qcow2 module: Executing command: {}'.format(' '.join(shlex.quote(arg) for arg in cmd)))
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
ret = {
'retcode': result.returncode,
'stdout': result.stdout,
'stderr': result.stderr
}
if result.returncode != 0:
log.error('qcow2 module: Script execution failed with return code {}: {}'.format(result.returncode, result.stderr))
else:
log.info('qcow2 module: Script executed successfully.')
return ret
except Exception as e:
log.error('qcow2 module: An error occurred while executing the script: {}'.format(e))
raise

View File

@@ -172,7 +172,15 @@ MANAGER_HOSTNAME = socket.gethostname()
def _download_image():
"""
Download and validate the Oracle Linux KVM image.
Download and validate the Oracle Linux KVM image with retry logic and progress monitoring.
Features:
- Detects stalled downloads (no progress for 30 seconds)
- Retries up to 3 times on failure
- Connection timeout of 30 seconds
- Read timeout of 60 seconds
- Cleans up partial downloads on failure
Returns:
bool: True if successful or file exists with valid checksum, False on error
"""
@@ -185,45 +193,107 @@ def _download_image():
os.unlink(IMAGE_PATH)
log.info("Starting image download process")
# Retry configuration
max_attempts = 3
retry_delay = 5 # seconds to wait between retry attempts
stall_timeout = 30 # seconds without progress before considering download stalled
connection_timeout = 30 # seconds to establish connection
read_timeout = 60 # seconds to wait for data chunks
for attempt in range(1, max_attempts + 1):
log.info("Download attempt %d of %d", attempt, max_attempts)
try:
# Download file with timeouts
log.info("Downloading Oracle Linux KVM image from %s to %s", IMAGE_URL, IMAGE_PATH)
response = requests.get(
IMAGE_URL,
stream=True,
timeout=(connection_timeout, read_timeout)
)
response.raise_for_status()
try:
# Download file
log.info("Downloading Oracle Linux KVM image from %s to %s", IMAGE_URL, IMAGE_PATH)
response = requests.get(IMAGE_URL, stream=True)
response.raise_for_status()
# Get total file size for progress tracking
total_size = int(response.headers.get('content-length', 0))
downloaded_size = 0
last_log_time = 0
last_progress_time = time.time()
last_downloaded_size = 0
# Get total file size for progress tracking
total_size = int(response.headers.get('content-length', 0))
downloaded_size = 0
last_log_time = 0
# Save file with progress logging and stall detection
with salt.utils.files.fopen(IMAGE_PATH, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
downloaded_size += len(chunk)
current_time = time.time()
# Check for stalled download
if downloaded_size > last_downloaded_size:
# Progress made, reset stall timer
last_progress_time = current_time
last_downloaded_size = downloaded_size
elif current_time - last_progress_time > stall_timeout:
# No progress for stall_timeout seconds
raise Exception(
f"Download stalled: no progress for {stall_timeout} seconds "
f"at {downloaded_size}/{total_size} bytes"
)
# Log progress every second
if current_time - last_log_time >= 1:
progress = (downloaded_size / total_size) * 100 if total_size > 0 else 0
log.info("Progress - %.1f%% (%d/%d bytes)",
progress, downloaded_size, total_size)
last_log_time = current_time
# Save file with progress logging
with salt.utils.files.fopen(IMAGE_PATH, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
downloaded_size += len(chunk)
# Validate downloaded file
log.info("Download complete, validating checksum...")
if not _validate_image_checksum(IMAGE_PATH, IMAGE_SHA256):
log.error("Checksum validation failed on attempt %d", attempt)
os.unlink(IMAGE_PATH)
if attempt < max_attempts:
log.info("Will retry download...")
continue
else:
log.error("All download attempts failed due to checksum mismatch")
return False
log.info("Successfully downloaded and validated Oracle Linux KVM image")
return True
except requests.exceptions.Timeout as e:
log.error("Download attempt %d failed: Timeout - %s", attempt, str(e))
if os.path.exists(IMAGE_PATH):
os.unlink(IMAGE_PATH)
if attempt < max_attempts:
log.info("Will retry download in %d seconds...", retry_delay)
time.sleep(retry_delay)
else:
log.error("All download attempts failed due to timeout")
# Log progress every second
current_time = time.time()
if current_time - last_log_time >= 1:
progress = (downloaded_size / total_size) * 100 if total_size > 0 else 0
log.info("Progress - %.1f%% (%d/%d bytes)",
progress, downloaded_size, total_size)
last_log_time = current_time
# Validate downloaded file
if not _validate_image_checksum(IMAGE_PATH, IMAGE_SHA256):
os.unlink(IMAGE_PATH)
return False
log.info("Successfully downloaded and validated Oracle Linux KVM image")
return True
except Exception as e:
log.error("Error downloading hypervisor image: %s", str(e))
if os.path.exists(IMAGE_PATH):
os.unlink(IMAGE_PATH)
return False
except requests.exceptions.RequestException as e:
log.error("Download attempt %d failed: Network error - %s", attempt, str(e))
if os.path.exists(IMAGE_PATH):
os.unlink(IMAGE_PATH)
if attempt < max_attempts:
log.info("Will retry download in %d seconds...", retry_delay)
time.sleep(retry_delay)
else:
log.error("All download attempts failed due to network errors")
except Exception as e:
log.error("Download attempt %d failed: %s", attempt, str(e))
if os.path.exists(IMAGE_PATH):
os.unlink(IMAGE_PATH)
if attempt < max_attempts:
log.info("Will retry download in %d seconds...", retry_delay)
time.sleep(retry_delay)
else:
log.error("All download attempts failed")
return False
def _check_ssh_keys_exist():
"""
@@ -419,25 +489,28 @@ def _ensure_hypervisor_host_dir(minion_id: str = None):
log.error(f"Error creating hypervisor host directory: {str(e)}")
return False
def _apply_dyanno_hypervisor_state():
def _apply_dyanno_hypervisor_state(status):
"""
Apply the soc.dyanno.hypervisor state on the salt master.
This function applies the soc.dyanno.hypervisor state on the salt master
to update the hypervisor annotation and ensure all hypervisor host directories exist.
Args:
status: Status passed to the hypervisor annotation state
Returns:
bool: True if state was applied successfully, False otherwise
"""
try:
log.info("Applying soc.dyanno.hypervisor state on salt master")
log.info(f"Applying soc.dyanno.hypervisor state on salt master with status: {status}")
# Initialize the LocalClient
local = salt.client.LocalClient()
# Target the salt master to apply the soc.dyanno.hypervisor state
target = MANAGER_HOSTNAME + '_*'
state_result = local.cmd(target, 'state.apply', ['soc.dyanno.hypervisor', "pillar={'baseDomain': {'status': 'PreInit'}}", 'concurrent=True'], tgt_type='glob')
state_result = local.cmd(target, 'state.apply', ['soc.dyanno.hypervisor', f"pillar={{'baseDomain': {{'status': '{status}'}}}}", 'concurrent=True'], tgt_type='glob')
log.debug(f"state_result: {state_result}")
# Check if state was applied successfully
if state_result:
@@ -454,17 +527,17 @@ def _apply_dyanno_hypervisor_state():
success = False
if success:
log.info("Successfully applied soc.dyanno.hypervisor state")
log.info(f"Successfully applied soc.dyanno.hypervisor state with status: {status}")
return True
else:
log.error("Failed to apply soc.dyanno.hypervisor state")
log.error(f"Failed to apply soc.dyanno.hypervisor state with status: {status}")
return False
else:
log.error("No response from salt master when applying soc.dyanno.hypervisor state")
log.error(f"No response from salt master when applying soc.dyanno.hypervisor state with status: {status}")
return False
except Exception as e:
log.error(f"Error applying soc.dyanno.hypervisor state: {str(e)}")
log.error(f"Error applying soc.dyanno.hypervisor state with status: {status}: {str(e)}")
return False
def _apply_cloud_config_state():
@@ -598,11 +671,6 @@ def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G', minion_id
log.warning("Failed to apply salt.cloud.config state, continuing with setup")
# We don't return an error here as we want to continue with the setup process
# Apply the soc.dyanno.hypervisor state on the salt master
if not _apply_dyanno_hypervisor_state():
log.warning("Failed to apply soc.dyanno.hypervisor state, continuing with setup")
# We don't return an error here as we want to continue with the setup process
log.info("Starting setup_environment in setup_hypervisor runner")
# Check if environment is already set up
@@ -616,9 +684,12 @@ def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G', minion_id
# Handle image setup if needed
if not image_valid:
_apply_dyanno_hypervisor_state('ImageDownloadStart')
log.info("Starting image download/validation process")
if not _download_image():
log.error("Image download failed")
# Update hypervisor annotation with failure status
_apply_dyanno_hypervisor_state('ImageDownloadFailed')
return {
'success': False,
'error': 'Image download failed',
@@ -631,6 +702,8 @@ def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G', minion_id
log.info("Setting up SSH keys")
if not _setup_ssh_keys():
log.error("SSH key setup failed")
# Update hypervisor annotation with failure status
_apply_dyanno_hypervisor_state('SSHKeySetupFailed')
return {
'success': False,
'error': 'SSH key setup failed',
@@ -655,6 +728,12 @@ def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G', minion_id
success = vm_result.get('success', False)
log.info("Setup environment completed with status: %s", "SUCCESS" if success else "FAILED")
# Update hypervisor annotation with success status
if success:
_apply_dyanno_hypervisor_state('PreInit')
else:
_apply_dyanno_hypervisor_state('SetupFailed')
# If setup was successful and we have a minion_id, run highstate
if success and minion_id:
log.info("Running highstate on hypervisor %s", minion_id)

View File

@@ -143,6 +143,7 @@
),
'so-fleet': (
ssl_states +
stig_states +
['logstash', 'nginx', 'healthcheck', 'elasticfleet']
),
'so-receiver': (

View File

@@ -1,4 +1,7 @@
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% set PCAP_BPF_STATUS = 0 %}
{% set STENO_BPF_COMPILED = "" %}
{% if GLOBALS.pcap_engine == "TRANSITION" %}
{% set PCAPBPF = ["ip and host 255.255.255.1 and port 1"] %}
{% else %}
@@ -8,3 +11,11 @@
{{ MACROS.remove_comments(BPFMERGED, 'pcap') }}
{% set PCAPBPF = BPFMERGED.pcap %}
{% endif %}
{% if PCAPBPF %}
{% set PCAP_BPF_CALC = salt['cmd.run_all']('/usr/sbin/so-bpf-compile ' ~ GLOBALS.sensor.interface ~ ' ' ~ PCAPBPF|join(" "), cwd='/root') %}
{% if PCAP_BPF_CALC['retcode'] == 0 %}
{% set PCAP_BPF_STATUS = 1 %}
{% set STENO_BPF_COMPILED = ",\\\"--filter=" + PCAP_BPF_CALC['stdout'] + "\\\"" %}
{% endif %}
{% endif %}

View File

@@ -1,11 +1,11 @@
bpf:
pcap:
description: List of BPF filters to apply to Stenographer.
description: List of BPF filters to apply to the PCAP engine.
multiline: True
forcedType: "[]string"
helpLink: bpf.html
suricata:
description: List of BPF filters to apply to Suricata.
description: List of BPF filters to apply to Suricata. This will apply to alerts and, if enabled, to metadata and PCAP logs generated by Suricata.
multiline: True
forcedType: "[]string"
helpLink: bpf.html

View File

@@ -1,7 +1,16 @@
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% import_yaml 'bpf/defaults.yaml' as BPFDEFAULTS %}
{% set BPFMERGED = salt['pillar.get']('bpf', BPFDEFAULTS.bpf, merge=True) %}
{% set SURICATA_BPF_STATUS = 0 %}
{% import 'bpf/macros.jinja' as MACROS %}
{{ MACROS.remove_comments(BPFMERGED, 'suricata') }}
{% set SURICATABPF = BPFMERGED.suricata %}
{% if SURICATABPF %}
{% set SURICATA_BPF_CALC = salt['cmd.run_all']('/usr/sbin/so-bpf-compile ' ~ GLOBALS.sensor.interface ~ ' ' ~ SURICATABPF|join(" "), cwd='/root') %}
{% if SURICATA_BPF_CALC['retcode'] == 0 %}
{% set SURICATA_BPF_STATUS = 1 %}
{% endif %}
{% endif %}

View File

@@ -1,7 +1,16 @@
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% import_yaml 'bpf/defaults.yaml' as BPFDEFAULTS %}
{% set BPFMERGED = salt['pillar.get']('bpf', BPFDEFAULTS.bpf, merge=True) %}
{% set ZEEK_BPF_STATUS = 0 %}
{% import 'bpf/macros.jinja' as MACROS %}
{{ MACROS.remove_comments(BPFMERGED, 'zeek') }}
{% set ZEEKBPF = BPFMERGED.zeek %}
{% if ZEEKBPF %}
{% set ZEEK_BPF_CALC = salt['cmd.run_all']('/usr/sbin/so-bpf-compile ' ~ GLOBALS.sensor.interface ~ ' ' ~ ZEEKBPF|join(" "), cwd='/root') %}
{% if ZEEK_BPF_CALC['retcode'] == 0 %}
{% set ZEEK_BPF_STATUS = 1 %}
{% endif %}
{% endif %}

21
salt/common/grains.sls Normal file
View 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.
{% set nsm_exists = salt['file.directory_exists']('/nsm') %}
{% if nsm_exists %}
{% set nsm_total = salt['cmd.shell']('df -BG /nsm | tail -1 | awk \'{print $2}\'') %}
nsm_total:
grains.present:
- name: nsm_total
- value: {{ nsm_total }}
{% else %}
nsm_missing:
test.succeed_without_changes:
- name: /nsm does not exist, skipping grain assignment
{% endif %}

View File

@@ -4,6 +4,7 @@
{% from 'vars/globals.map.jinja' import GLOBALS %}
include:
- common.grains
- common.packages
{% if GLOBALS.role in GLOBALS.manager_roles %}
- manager.elasticsearch # needed for elastic_curl_config state

View File

@@ -29,9 +29,26 @@ fi
interface="$1"
shift
tcpdump -i $interface -ddd $@ | tail -n+2 |
while read line; do
# Capture tcpdump output and exit code
tcpdump_output=$(tcpdump -i "$interface" -ddd "$@" 2>&1)
tcpdump_exit=$?
if [ $tcpdump_exit -ne 0 ]; then
echo "$tcpdump_output" >&2
exit $tcpdump_exit
fi
# Process the output, skipping the first line
echo "$tcpdump_output" | tail -n+2 | while read -r line; do
cols=( $line )
printf "%04x%02x%02x%08x" ${cols[0]} ${cols[1]} ${cols[2]} ${cols[3]}
printf "%04x%02x%02x%08x" "${cols[0]}" "${cols[1]}" "${cols[2]}" "${cols[3]}"
done
# Check if the pipeline succeeded
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
exit 1
fi
echo ""
exit 0

View File

@@ -220,12 +220,22 @@ compare_es_versions() {
}
copy_new_files() {
# Define files to exclude from deletion (relative to their respective base directories)
local EXCLUDE_FILES=(
"salt/hypervisor/soc_hypervisor.yaml"
)
# Build rsync exclude arguments
local EXCLUDE_ARGS=()
for file in "${EXCLUDE_FILES[@]}"; do
EXCLUDE_ARGS+=(--exclude="$file")
done
# Copy new files over to the salt dir
cd $UPDATE_DIR
rsync -a salt $DEFAULT_SALT_DIR/ --delete
rsync -a pillar $DEFAULT_SALT_DIR/ --delete
rsync -a salt $DEFAULT_SALT_DIR/ --delete "${EXCLUDE_ARGS[@]}"
rsync -a pillar $DEFAULT_SALT_DIR/ --delete "${EXCLUDE_ARGS[@]}"
chown -R socore:socore $DEFAULT_SALT_DIR/
chmod 755 $DEFAULT_SALT_DIR/pillar/firewall/addfirewall.sh
cd /tmp
}
@@ -385,7 +395,7 @@ is_manager_node() {
}
is_sensor_node() {
# Check to see if this is a sensor (forward) node
# Check to see if this is a sensor node
is_single_node_grid && return 0
grep "role: so-" /etc/salt/grains | grep -E "sensor|heavynode" &> /dev/null
}
@@ -441,8 +451,7 @@ lookup_grain() {
lookup_role() {
id=$(lookup_grain id)
pieces=($(echo $id | tr '_' ' '))
echo ${pieces[1]}
echo "${id##*_}"
}
is_feature_enabled() {

View File

@@ -62,8 +62,6 @@ container_list() {
"so-soc"
"so-steno"
"so-strelka-backend"
"so-strelka-filestream"
"so-strelka-frontend"
"so-strelka-manager"
"so-suricata"
"so-telegraf"

View File

@@ -222,6 +222,7 @@ if [[ $EXCLUDE_KNOWN_ERRORS == 'Y' ]]; then
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Initialized license manager" # SOC log: before fields.status was changed to fields.licenseStatus
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|from NIC checksum offloading" # zeek reporter.log
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|marked for removal" # docker container getting recycled
EXCLUDED_ERRORS="$EXCLUDED_ERRORS|tcp 127.0.0.1:6791: bind: address already in use" # so-elastic-fleet agent restarting. Seen starting w/ 8.18.8 https://github.com/elastic/kibana/issues/201459
fi
RESULT=0
@@ -268,6 +269,13 @@ for log_file in $(cat /tmp/log_check_files); do
tail -n $RECENT_LOG_LINES $log_file > /tmp/log_check
check_for_errors
done
# Look for OOM specific errors in /var/log/messages which can lead to odd behavior / test failures
if [[ -f /var/log/messages ]]; then
status "Checking log file /var/log/messages"
if journalctl --since "24 hours ago" | grep -iE 'out of memory|oom-kill'; then
RESULT=1
fi
fi
# Cleanup temp files
rm -f /tmp/log_check_files

View File

@@ -173,7 +173,7 @@ for PCAP in $INPUT_FILES; do
status "- assigning unique identifier to import: $HASH"
pcap_data=$(pcapinfo "${PCAP}")
if ! echo "$pcap_data" | grep -q "First packet time:" || echo "$pcap_data" |egrep -q "Last packet time: 1970-01-01|Last packet time: n/a"; then
if ! echo "$pcap_data" | grep -q "Earliest packet time:" || echo "$pcap_data" |egrep -q "Latest packet time: 1970-01-01|Latest packet time: n/a"; then
status "- this PCAP file is invalid; skipping"
INVALID_PCAPS_COUNT=$((INVALID_PCAPS_COUNT + 1))
else
@@ -205,8 +205,8 @@ for PCAP in $INPUT_FILES; do
HASHES="${HASHES} ${HASH}"
fi
START=$(pcapinfo "${PCAP}" -a |grep "First packet time:" | awk '{print $4}')
END=$(pcapinfo "${PCAP}" -e |grep "Last packet time:" | awk '{print $4}')
START=$(pcapinfo "${PCAP}" -a |grep "Earliest packet time:" | awk '{print $4}')
END=$(pcapinfo "${PCAP}" -e |grep "Latest packet time:" | awk '{print $4}')
status "- found PCAP data spanning dates $START through $END"
# compare $START to $START_OLDEST

View File

@@ -9,3 +9,6 @@ fleetartifactdir:
- user: 947
- group: 939
- makedirs: True
- recurse:
- user
- group

View File

@@ -0,0 +1,34 @@
{# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
https://securityonion.net/license; you may not use this file except in compliance with the
Elastic License 2.0. #}
{% from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
{# advanced config_yaml options for elasticfleet logstash output #}
{% set ADV_OUTPUT_LOGSTASH_RAW = ELASTICFLEETMERGED.config.outputs.logstash %}
{% set ADV_OUTPUT_LOGSTASH = {} %}
{% for k, v in ADV_OUTPUT_LOGSTASH_RAW.items() %}
{% if v != "" and v is not none %}
{% if k == 'queue_mem_events' %}
{# rename queue_mem_events queue.mem.events #}
{% do ADV_OUTPUT_LOGSTASH.update({'queue.mem.events':v}) %}
{% elif k == 'loadbalance' %}
{% if v %}
{# only include loadbalance config when its True #}
{% do ADV_OUTPUT_LOGSTASH.update({k:v}) %}
{% endif %}
{% else %}
{% do ADV_OUTPUT_LOGSTASH.update({k:v}) %}
{% endif %}
{% endif %}
{% endfor %}
{% set LOGSTASH_CONFIG_YAML_RAW = [] %}
{% if ADV_OUTPUT_LOGSTASH %}
{% for k, v in ADV_OUTPUT_LOGSTASH.items() %}
{% do LOGSTASH_CONFIG_YAML_RAW.append(k ~ ': ' ~ v) %}
{% endfor %}
{% endif %}
{% set LOGSTASH_CONFIG_YAML = LOGSTASH_CONFIG_YAML_RAW | join('\\n') if LOGSTASH_CONFIG_YAML_RAW else '' %}

View File

@@ -9,6 +9,9 @@
{% from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
{% set node_data = salt['pillar.get']('node_data') %}
include:
- elasticfleet.artifact_registry
# Add EA Group
elasticfleetgroup:
group.present:

View File

@@ -10,12 +10,19 @@ elasticfleet:
grid_enrollment: ''
defend_filters:
enable_auto_configuration: False
outputs:
logstash:
bulk_max_size: ''
worker: ''
queue_mem_events: ''
timeout: ''
loadbalance: False
compression_level: ''
subscription_integrations: False
auto_upgrade_integrations: False
logging:
zeek:
excluded:
- analyzer
- broker
- capture_loss
- cluster
@@ -38,6 +45,7 @@ elasticfleet:
- elasticsearch
- endpoint
- fleet_server
- filestream
- http_endpoint
- httpjson
- log

View File

@@ -32,6 +32,17 @@ so-elastic-fleet-auto-configure-logstash-outputs:
- retry:
attempts: 4
interval: 30
{# Separate from above in order to catch elasticfleet-logstash.crt changes and force update to fleet output policy #}
so-elastic-fleet-auto-configure-logstash-outputs-force:
cmd.run:
- name: /usr/sbin/so-elastic-fleet-outputs-update --certs
- retry:
attempts: 4
interval: 30
- onchanges:
- x509: etc_elasticfleet_logstash_crt
- x509: elasticfleet_kafka_crt
{% endif %}
# If enabled, automatically update Fleet Server URLs & ES Connection
@@ -67,6 +78,8 @@ so-elastic-fleet-auto-configure-artifact-urls:
elasticagent_syncartifacts:
file.recurse:
- name: /nsm/elastic-fleet/artifacts/beats
- user: 947
- group: 947
- source: salt://beats
{% endif %}
@@ -133,12 +146,18 @@ so-elastic-fleet-package-statefile:
so-elastic-fleet-package-upgrade:
cmd.run:
- name: /usr/sbin/so-elastic-fleet-package-upgrade
- retry:
attempts: 3
interval: 10
- onchanges:
- file: /opt/so/state/elastic_fleet_packages.txt
so-elastic-fleet-integrations:
cmd.run:
- name: /usr/sbin/so-elastic-fleet-integration-policy-load
- retry:
attempts: 3
interval: 10
so-elastic-agent-grid-upgrade:
cmd.run:
@@ -150,7 +169,11 @@ so-elastic-agent-grid-upgrade:
so-elastic-fleet-integration-upgrade:
cmd.run:
- name: /usr/sbin/so-elastic-fleet-integration-upgrade
- retry:
attempts: 3
interval: 10
{# Optional integrations script doesn't need the retries like so-elastic-fleet-integration-upgrade which loads the default integrations #}
so-elastic-fleet-addon-integrations:
cmd.run:
- name: /usr/sbin/so-elastic-fleet-optional-integrations-load

View File

@@ -0,0 +1,48 @@
{
"package": {
"name": "filestream",
"version": ""
},
"name": "agent-monitor",
"namespace": "",
"description": "",
"policy_ids": [
"so-grid-nodes_general"
],
"output_id": null,
"vars": {},
"inputs": {
"filestream-filestream": {
"enabled": true,
"streams": {
"filestream.generic": {
"enabled": true,
"vars": {
"paths": [
"/opt/so/log/agents/agent-monitor.log"
],
"data_stream.dataset": "agentmonitor",
"pipeline": "elasticagent.monitor",
"parsers": "",
"exclude_files": [
"\\.gz$"
],
"include_files": [],
"processors": "- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n- add_fields:\n target: event\n fields:\n module: gridmetrics",
"tags": [],
"recursive_glob": true,
"ignore_older": "72h",
"clean_inactive": -1,
"harvester_limit": 0,
"fingerprint": true,
"fingerprint_offset": 0,
"fingerprint_length": 64,
"file_identity_native": false,
"exclude_lines": [],
"include_lines": []
}
}
}
}
}
}

View File

@@ -40,7 +40,7 @@
"enabled": true,
"vars": {
"paths": [
"/opt/so/log/elasticsearch/*.log"
"/opt/so/log/elasticsearch/*.json"
]
}
},

View File

@@ -20,7 +20,7 @@
],
"data_stream.dataset": "import",
"custom": "",
"processors": "- dissect:\n tokenizer: \"/nsm/import/%{import.id}/evtx/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"\n- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n- drop_fields:\n fields: [\"host\"]\n ignore_missing: true\n- add_fields:\n target: data_stream\n fields:\n type: logs\n dataset: system.security\n- add_fields:\n target: event\n fields:\n dataset: system.security\n module: system\n imported: true\n- add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.security-2.3.3\n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-Sysmon/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.sysmon_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.sysmon_operational\n module: windows\n imported: true\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.sysmon_operational-3.1.0\n- if:\n equals:\n winlog.channel: 'Application'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.application\n - add_fields:\n target: event\n fields:\n dataset: system.application\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.application-2.3.3\n- if:\n equals:\n winlog.channel: 'System'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.system\n - add_fields:\n target: event\n fields:\n dataset: system.system\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.system-2.3.3\n \n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-PowerShell/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.powershell_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.powershell_operational\n module: windows\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.powershell_operational-3.1.0\n- add_fields:\n target: data_stream\n fields:\n dataset: import",
"processors": "- dissect:\n tokenizer: \"/nsm/import/%{import.id}/evtx/%{import.file}\"\n field: \"log.file.path\"\n target_prefix: \"\"\n- decode_json_fields:\n fields: [\"message\"]\n target: \"\"\n- drop_fields:\n fields: [\"host\"]\n ignore_missing: true\n- add_fields:\n target: data_stream\n fields:\n type: logs\n dataset: system.security\n- add_fields:\n target: event\n fields:\n dataset: system.security\n module: system\n imported: true\n- add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.security-2.6.1\n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-Sysmon/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.sysmon_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.sysmon_operational\n module: windows\n imported: true\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.sysmon_operational-3.1.2\n- if:\n equals:\n winlog.channel: 'Application'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.application\n - add_fields:\n target: event\n fields:\n dataset: system.application\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.application-2.6.1\n- if:\n equals:\n winlog.channel: 'System'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: system.system\n - add_fields:\n target: event\n fields:\n dataset: system.system\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-system.system-2.6.1\n \n- if:\n equals:\n winlog.channel: 'Microsoft-Windows-PowerShell/Operational'\n then: \n - add_fields:\n target: data_stream\n fields:\n dataset: windows.powershell_operational\n - add_fields:\n target: event\n fields:\n dataset: windows.powershell_operational\n module: windows\n - add_fields:\n target: \"@metadata\"\n fields:\n pipeline: logs-windows.powershell_operational-3.1.2\n- add_fields:\n target: data_stream\n fields:\n dataset: import",
"tags": [
"import"
]

View File

@@ -2,26 +2,30 @@
# or more contributor license agreements. Licensed under the Elastic License 2.0; you may not use
# this file except in compliance with the Elastic License 2.0.
{%- set GRIDNODETOKENGENERAL = salt['pillar.get']('global:fleet_grid_enrollment_token_general') -%}
{%- set GRIDNODETOKENHEAVY = salt['pillar.get']('global:fleet_grid_enrollment_token_heavy') -%}
{% set GRIDNODETOKEN = salt['pillar.get']('global:fleet_grid_enrollment_token_general') -%}
{% if grains.role == 'so-heavynode' %}
{% set GRIDNODETOKEN = salt['pillar.get']('global:fleet_grid_enrollment_token_heavy') -%}
{% endif %}
{% set AGENT_STATUS = salt['service.available']('elastic-agent') %}
{% if not AGENT_STATUS %}
{% if grains.role not in ['so-heavynode'] %}
run_installer:
cmd.script:
- name: salt://elasticfleet/files/so_agent-installers/so-elastic-agent_linux_amd64
- cwd: /opt/so
- args: -token={{ GRIDNODETOKENGENERAL }}
- retry: True
{% else %}
run_installer:
cmd.script:
- name: salt://elasticfleet/files/so_agent-installers/so-elastic-agent_linux_amd64
- cwd: /opt/so
- args: -token={{ GRIDNODETOKENHEAVY }}
- retry: True
{% endif %}
pull_agent_installer:
file.managed:
- name: /opt/so/so-elastic-agent_linux_amd64
- source: salt://elasticfleet/files/so_agent-installers/so-elastic-agent_linux_amd64
- mode: 755
- makedirs: True
run_installer:
cmd.run:
- name: ./so-elastic-agent_linux_amd64 -token={{ GRIDNODETOKEN }}
- cwd: /opt/so
- retry:
attempts: 3
interval: 20
cleanup_agent_installer:
file.absent:
- name: /opt/so/so-elastic-agent_linux_amd64
{% endif %}

View File

@@ -121,6 +121,9 @@
"phases": {
"cold": {
"actions": {
"allocate":{
"number_of_replicas": ""
},
"set_priority": {"priority": 0}
},
"min_age": "60d"
@@ -137,12 +140,31 @@
"max_age": "30d",
"max_primary_shard_size": "50gb"
},
"forcemerge":{
"max_num_segments": ""
},
"shrink":{
"max_primary_shard_size": "",
"method": "COUNT",
"number_of_shards": ""
},
"set_priority": {"priority": 100}
},
"min_age": "0ms"
},
"warm": {
"actions": {
"allocate": {
"number_of_replicas": ""
},
"forcemerge": {
"max_num_segments": ""
},
"shrink":{
"max_primary_shard_size": "",
"method": "COUNT",
"number_of_shards": ""
},
"set_priority": {"priority": 50}
},
"min_age": "30d"

View File

@@ -50,6 +50,46 @@ elasticfleet:
global: True
forcedType: bool
helpLink: elastic-fleet.html
outputs:
logstash:
bulk_max_size:
description: The maximum number of events to bulk in a single Logstash request.
global: True
forcedType: int
advanced: True
helpLink: elastic-fleet.html
worker:
description: The number of workers per configured host publishing events.
global: True
forcedType: int
advanced: true
helpLink: elastic-fleet.html
queue_mem_events:
title: queued events
description: The number of events the queue can store. This value should be evenly divisible by the smaller of 'bulk_max_size' to avoid sending partial batches to the output.
global: True
forcedType: int
advanced: True
helpLink: elastic-fleet.html
timeout:
description: The number of seconds to wait for responses from the Logstash server before timing out. Eg 30s
regex: ^[0-9]+s$
advanced: True
global: True
helpLink: elastic-fleet.html
loadbalance:
description: If true and multiple Logstash hosts are configured, the output plugin load balances published events onto all Logstash hosts. If false, the output plugin sends all events to one host (determined at random) and switches to another host if the selected one becomes unresponsive.
forcedType: bool
advanced: True
global: True
helpLink: elastic-fleet.html
compression_level:
description: The gzip compression level. The compression level must be in the range of 1 (best speed) to 9 (best compression).
regex: ^[1-9]$
forcedType: int
advanced: True
global: True
helpLink: elastic-fleet.html
server:
custom_fqdn:
description: Custom FQDN for Agents to connect to. One per line.

View File

@@ -23,6 +23,13 @@ fi
# Define a banner to separate sections
banner="========================================================================="
fleet_api() {
local QUERYPATH=$1
shift
curl -sK /opt/so/conf/elasticsearch/curl.config -L "localhost:5601/api/fleet/${QUERYPATH}" "$@" --retry 3 --retry-delay 10 --fail 2>/dev/null
}
elastic_fleet_integration_check() {
AGENT_POLICY=$1
@@ -39,7 +46,9 @@ elastic_fleet_integration_create() {
JSON_STRING=$1
curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/package_policies" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
if ! fleet_api "package_policies" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -XPOST -d "$JSON_STRING"; then
return 1
fi
}
@@ -56,7 +65,10 @@ elastic_fleet_integration_remove() {
'{"packagePolicyIds":[$INTEGRATIONID]}'
)
curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/package_policies/delete" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
if ! fleet_api "package_policies/delete" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"; then
echo "Error: Unable to delete '$NAME' from '$AGENT_POLICY'"
return 1
fi
}
elastic_fleet_integration_update() {
@@ -65,7 +77,9 @@ elastic_fleet_integration_update() {
JSON_STRING=$2
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/package_policies/$UPDATE_ID" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
if ! fleet_api "package_policies/$UPDATE_ID" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -XPUT -d "$JSON_STRING"; then
return 1
fi
}
elastic_fleet_integration_policy_upgrade() {
@@ -77,78 +91,83 @@ elastic_fleet_integration_policy_upgrade() {
'{"packagePolicyIds":[$INTEGRATIONID]}'
)
curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/package_policies/upgrade" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
if ! fleet_api "package_policies/upgrade" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"; then
return 1
fi
}
elastic_fleet_package_version_check() {
PACKAGE=$1
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET "localhost:5601/api/fleet/epm/packages/$PACKAGE" | jq -r '.item.version'
if output=$(fleet_api "epm/packages/$PACKAGE"); then
echo "$output" | jq -r '.item.version'
else
echo "Error: Failed to get current package version for '$PACKAGE'"
return 1
fi
}
elastic_fleet_package_latest_version_check() {
PACKAGE=$1
if output=$(curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET "localhost:5601/api/fleet/epm/packages/$PACKAGE" --fail); then
if output=$(fleet_api "epm/packages/$PACKAGE"); then
if version=$(jq -e -r '.item.latestVersion' <<< $output); then
echo "$version"
fi
else
echo "Error: Failed to get latest version for $PACKAGE"
echo "Error: Failed to get latest version for '$PACKAGE'"
return 1
fi
}
elastic_fleet_package_install() {
PKG=$1
VERSION=$2
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X POST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '{"force":true}' "localhost:5601/api/fleet/epm/packages/$PKG/$VERSION"
if ! fleet_api "epm/packages/$PKG/$VERSION" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '{"force":true}'; then
return 1
fi
}
elastic_fleet_bulk_package_install() {
BULK_PKG_LIST=$1
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X POST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d@$1 "localhost:5601/api/fleet/epm/packages/_bulk"
}
elastic_fleet_package_is_installed() {
PACKAGE=$1
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET -H 'kbn-xsrf: true' "localhost:5601/api/fleet/epm/packages/$PACKAGE" | jq -r '.item.status'
}
elastic_fleet_installed_packages() {
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET -H 'kbn-xsrf: true' -H 'Content-Type: application/json' "localhost:5601/api/fleet/epm/packages/installed?perPage=500"
}
elastic_fleet_agent_policy_ids() {
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET "localhost:5601/api/fleet/agent_policies" | jq -r .items[].id
if [ $? -ne 0 ]; then
echo "Error: Failed to retrieve agent policies."
exit 1
if ! fleet_api "epm/packages/_bulk" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d@$BULK_PKG_LIST; then
return 1
fi
}
elastic_fleet_agent_policy_names() {
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET "localhost:5601/api/fleet/agent_policies" | jq -r .items[].name
if [ $? -ne 0 ]; then
elastic_fleet_installed_packages() {
if ! fleet_api "epm/packages/installed?perPage=500"; then
return 1
fi
}
elastic_fleet_agent_policy_ids() {
if output=$(fleet_api "agent_policies"); then
echo "$output" | jq -r .items[].id
else
echo "Error: Failed to retrieve agent policies."
exit 1
return 1
fi
}
elastic_fleet_integration_policy_names() {
AGENT_POLICY=$1
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET "localhost:5601/api/fleet/agent_policies/$AGENT_POLICY" | jq -r .item.package_policies[].name
if [ $? -ne 0 ]; then
if output=$(fleet_api "agent_policies/$AGENT_POLICY"); then
echo "$output" | jq -r .item.package_policies[].name
else
echo "Error: Failed to retrieve integrations for '$AGENT_POLICY'."
exit 1
return 1
fi
}
elastic_fleet_integration_policy_package_name() {
AGENT_POLICY=$1
INTEGRATION=$2
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET "localhost:5601/api/fleet/agent_policies/$AGENT_POLICY" | jq -r --arg INTEGRATION "$INTEGRATION" '.item.package_policies[] | select(.name==$INTEGRATION)| .package.name'
if [ $? -ne 0 ]; then
if output=$(fleet_api "agent_policies/$AGENT_POLICY"); then
echo "$output" | jq -r --arg INTEGRATION "$INTEGRATION" '.item.package_policies[] | select(.name==$INTEGRATION)| .package.name'
else
echo "Error: Failed to retrieve package name for '$INTEGRATION' in '$AGENT_POLICY'."
exit 1
return 1
fi
}
@@ -156,32 +175,32 @@ elastic_fleet_integration_policy_package_version() {
AGENT_POLICY=$1
INTEGRATION=$2
if output=$(curl -s -K /opt/so/conf/elasticsearch/curl.config -L -X GET "localhost:5601/api/fleet/agent_policies/$AGENT_POLICY" --fail); then
if version=$(jq -e -r --arg INTEGRATION "$INTEGRATION" '.item.package_policies[] | select(.name==$INTEGRATION)| .package.version' <<< $output); then
if output=$(fleet_api "agent_policies/$AGENT_POLICY"); then
if version=$(jq -e -r --arg INTEGRATION "$INTEGRATION" '.item.package_policies[] | select(.name==$INTEGRATION)| .package.version' <<< "$output"); then
echo "$version"
fi
else
echo "Error: Failed to retrieve agent policy $AGENT_POLICY"
exit 1
echo "Error: Failed to retrieve integration version for '$INTEGRATION' in policy '$AGENT_POLICY'"
return 1
fi
}
elastic_fleet_integration_id() {
AGENT_POLICY=$1
INTEGRATION=$2
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -L -X GET "localhost:5601/api/fleet/agent_policies/$AGENT_POLICY" | jq -r --arg INTEGRATION "$INTEGRATION" '.item.package_policies[] | select(.name==$INTEGRATION)| .id'
if [ $? -ne 0 ]; then
if output=$(fleet_api "agent_policies/$AGENT_POLICY"); then
echo "$output" | jq -r --arg INTEGRATION "$INTEGRATION" '.item.package_policies[] | select(.name==$INTEGRATION)| .id'
else
echo "Error: Failed to retrieve integration ID for '$INTEGRATION' in '$AGENT_POLICY'."
exit 1
return 1
fi
}
elastic_fleet_integration_policy_dryrun_upgrade() {
INTEGRATION_ID=$1
curl -s -K /opt/so/conf/elasticsearch/curl.config -b "sid=$SESSIONCOOKIE" -H "Content-Type: application/json" -H 'kbn-xsrf: true' -L -X POST "localhost:5601/api/fleet/package_policies/upgrade/dryrun" -d "{\"packagePolicyIds\":[\"$INTEGRATION_ID\"]}"
if [ $? -ne 0 ]; then
if ! fleet_api "package_policies/upgrade/dryrun" -H "Content-Type: application/json" -H 'kbn-xsrf: true' -XPOST -d "{\"packagePolicyIds\":[\"$INTEGRATION_ID\"]}"; then
echo "Error: Failed to complete dry run for '$INTEGRATION_ID'."
exit 1
return 1
fi
}
@@ -190,25 +209,18 @@ elastic_fleet_policy_create() {
NAME=$1
DESC=$2
FLEETSERVER=$3
TIMEOUT=$4
TIMEOUT=$4
JSON_STRING=$( jq -n \
--arg NAME "$NAME" \
--arg DESC "$DESC" \
--arg TIMEOUT $TIMEOUT \
--arg FLEETSERVER "$FLEETSERVER" \
'{"name": $NAME,"id":$NAME,"description":$DESC,"namespace":"default","monitoring_enabled":["logs"],"inactivity_timeout":$TIMEOUT,"has_fleet_server":$FLEETSERVER}'
)
--arg NAME "$NAME" \
--arg DESC "$DESC" \
--arg TIMEOUT $TIMEOUT \
--arg FLEETSERVER "$FLEETSERVER" \
'{"name": $NAME,"id":$NAME,"description":$DESC,"namespace":"default","monitoring_enabled":["logs"],"inactivity_timeout":$TIMEOUT,"has_fleet_server":$FLEETSERVER}'
)
# Create Fleet Policy
curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/agent_policies" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
if ! fleet_api "agent_policies" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"; then
return 1
fi
}
elastic_fleet_policy_update() {
POLICYID=$1
JSON_STRING=$2
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/agent_policies/$POLICYID" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
}

View File

@@ -8,6 +8,7 @@
. /usr/sbin/so-elastic-fleet-common
ERROR=false
# Manage Elastic Defend Integration for Initial Endpoints Policy
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/elastic-defend/*.json
do
@@ -15,9 +16,20 @@ do
elastic_fleet_integration_check "endpoints-initial" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Upgrading integration policy\n"
elastic_fleet_integration_policy_upgrade "$INTEGRATION_ID"
if ! elastic_fleet_integration_policy_upgrade "$INTEGRATION_ID"; then
echo -e "\nFailed to upgrade integration policy for ${INTEGRATION##*/}"
ERROR=true
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
elastic_fleet_integration_create "@$INTEGRATION"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
ERROR=true
continue
fi
fi
done
if [[ "$ERROR" == "true" ]]; then
exit 1
fi

View File

@@ -25,5 +25,9 @@ for POLICYNAME in $POLICY; do
.name = $name' /opt/so/conf/elastic-fleet/integrations/fleet-server/fleet-server.json)
# Now update the integration policy using the modified JSON
elastic_fleet_integration_update "$INTEGRATION_ID" "$UPDATED_INTEGRATION_POLICY"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "$UPDATED_INTEGRATION_POLICY"; then
# exit 1 on failure to update fleet integration policies, let salt handle retries
echo "Failed to update $POLICYNAME.."
exit 1
fi
done

View File

@@ -13,11 +13,10 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
/usr/sbin/so-elastic-fleet-package-upgrade
# Second, update Fleet Server policies
/sbin/so-elastic-fleet-integration-policy-elastic-fleet-server
/usr/sbin/so-elastic-fleet-integration-policy-elastic-fleet-server
# Third, configure Elastic Defend Integration seperately
/usr/sbin/so-elastic-fleet-integration-policy-elastic-defend
# Initial Endpoints
for INTEGRATION in /opt/so/conf/elastic-fleet/integrations/endpoints-initial/*.json
do
@@ -25,10 +24,18 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
elastic_fleet_integration_check "endpoints-initial" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
elastic_fleet_integration_create "@$INTEGRATION"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
done
@@ -39,10 +46,18 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
elastic_fleet_integration_check "so-grid-nodes_general" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
elastic_fleet_integration_create "@$INTEGRATION"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
done
if [[ "$RETURN_CODE" != "1" ]]; then
@@ -56,11 +71,19 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
elastic_fleet_integration_check "so-grid-nodes_heavy" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
if [ "$NAME" != "elasticsearch-logs" ]; then
elastic_fleet_integration_create "@$INTEGRATION"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
fi
done
@@ -77,11 +100,19 @@ if [ ! -f /opt/so/state/eaintegrations.txt ]; then
elastic_fleet_integration_check "$FLEET_POLICY" "$INTEGRATION"
if [ -n "$INTEGRATION_ID" ]; then
printf "\n\nIntegration $NAME exists - Updating integration\n"
elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"
if ! elastic_fleet_integration_update "$INTEGRATION_ID" "@$INTEGRATION"; then
echo -e "\nFailed to update integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
else
printf "\n\nIntegration does not exist - Creating integration\n"
if [ "$NAME" != "elasticsearch-logs" ]; then
elastic_fleet_integration_create "@$INTEGRATION"
if ! elastic_fleet_integration_create "@$INTEGRATION"; then
echo -e "\nFailed to create integration for ${INTEGRATION##*/}"
RETURN_CODE=1
continue
fi
fi
fi
fi

View File

@@ -24,12 +24,18 @@ fi
default_packages=({% for pkg in SUPPORTED_PACKAGES %}"{{ pkg }}"{% if not loop.last %} {% endif %}{% endfor %})
ERROR=false
for AGENT_POLICY in $agent_policies; do
integrations=$(elastic_fleet_integration_policy_names "$AGENT_POLICY")
if ! integrations=$(elastic_fleet_integration_policy_names "$AGENT_POLICY"); then
# this script upgrades default integration packages, exit 1 and let salt handle retrying
exit 1
fi
for INTEGRATION in $integrations; do
if ! [[ "$INTEGRATION" == "elastic-defend-endpoints" ]] && ! [[ "$INTEGRATION" == "fleet_server-"* ]]; then
# Get package name so we know what package to look for when checking the current and latest available version
PACKAGE_NAME=$(elastic_fleet_integration_policy_package_name "$AGENT_POLICY" "$INTEGRATION")
if ! PACKAGE_NAME=$(elastic_fleet_integration_policy_package_name "$AGENT_POLICY" "$INTEGRATION"); then
exit 1
fi
{%- if not AUTO_UPGRADE_INTEGRATIONS %}
if [[ " ${default_packages[@]} " =~ " $PACKAGE_NAME " ]]; then
{%- endif %}
@@ -48,7 +54,9 @@ for AGENT_POLICY in $agent_policies; do
fi
# Get integration ID
INTEGRATION_ID=$(elastic_fleet_integration_id "$AGENT_POLICY" "$INTEGRATION")
if ! INTEGRATION_ID=$(elastic_fleet_integration_id "$AGENT_POLICY" "$INTEGRATION"); then
exit 1
fi
if [[ "$PACKAGE_VERSION" != "$AVAILABLE_VERSION" ]]; then
# Dry run of the upgrade
@@ -56,20 +64,23 @@ for AGENT_POLICY in $agent_policies; do
echo "Current $PACKAGE_NAME package version ($PACKAGE_VERSION) is not the same as the latest available package ($AVAILABLE_VERSION)..."
echo "Upgrading $INTEGRATION..."
echo "Starting dry run..."
DRYRUN_OUTPUT=$(elastic_fleet_integration_policy_dryrun_upgrade "$INTEGRATION_ID")
if ! DRYRUN_OUTPUT=$(elastic_fleet_integration_policy_dryrun_upgrade "$INTEGRATION_ID"); then
exit 1
fi
DRYRUN_ERRORS=$(echo "$DRYRUN_OUTPUT" | jq .[].hasErrors)
# If no errors with dry run, proceed with actual upgrade
if [[ "$DRYRUN_ERRORS" == "false" ]]; then
echo "No errors detected. Proceeding with upgrade..."
elastic_fleet_integration_policy_upgrade "$INTEGRATION_ID"
if [ $? -ne 0 ]; then
if ! elastic_fleet_integration_policy_upgrade "$INTEGRATION_ID"; then
echo "Error: Upgrade failed for $PACKAGE_NAME with integration ID '$INTEGRATION_ID'."
exit 1
ERROR=true
continue
fi
else
echo "Errors detected during dry run for $PACKAGE_NAME policy upgrade..."
exit 1
ERROR=true
continue
fi
fi
{%- if not AUTO_UPGRADE_INTEGRATIONS %}
@@ -78,4 +89,7 @@ for AGENT_POLICY in $agent_policies; do
fi
done
done
if [[ "$ERROR" == "true" ]]; then
exit 1
fi
echo

View File

@@ -62,9 +62,17 @@ default_packages=({% for pkg in SUPPORTED_PACKAGES %}"{{ pkg }}"{% if not loop.l
in_use_integrations=()
for AGENT_POLICY in $agent_policies; do
integrations=$(elastic_fleet_integration_policy_names "$AGENT_POLICY")
if ! integrations=$(elastic_fleet_integration_policy_names "$AGENT_POLICY"); then
# skip the agent policy if we can't get required info, let salt retry. Integrations loaded by this script are non-default integrations.
echo "Skipping $AGENT_POLICY.. "
continue
fi
for INTEGRATION in $integrations; do
PACKAGE_NAME=$(elastic_fleet_integration_policy_package_name "$AGENT_POLICY" "$INTEGRATION")
if ! PACKAGE_NAME=$(elastic_fleet_integration_policy_package_name "$AGENT_POLICY" "$INTEGRATION"); then
echo "Not adding $INTEGRATION, couldn't get package name"
continue
fi
# non-default integrations that are in-use in any policy
if ! [[ " ${default_packages[@]} " =~ " $PACKAGE_NAME " ]]; then
in_use_integrations+=("$PACKAGE_NAME")
@@ -160,7 +168,11 @@ if [[ -f $STATE_FILE_SUCCESS ]]; then
for file in "${pkg_filename}_"*.json; do
[ -e "$file" ] || continue
elastic_fleet_bulk_package_install $file >> $BULK_INSTALL_OUTPUT
if ! elastic_fleet_bulk_package_install $file >> $BULK_INSTALL_OUTPUT; then
# integrations loaded my this script are non-essential and shouldn't cause exit, skip them for now next highstate run can retry
echo "Failed to complete a chunk of bulk package installs -- $file "
continue
fi
done
# cleanup any temp files for chunked package install
rm -f ${pkg_filename}_*.json $BULK_INSTALL_PACKAGE_LIST
@@ -168,8 +180,9 @@ if [[ -f $STATE_FILE_SUCCESS ]]; then
echo "Elastic integrations don't appear to need installation/updating..."
fi
# Write out file for generating index/component/ilm templates
latest_installed_package_list=$(elastic_fleet_installed_packages)
echo $latest_installed_package_list | jq '[.items[] | {name: .name, es_index_patterns: .dataStreams}]' > $PACKAGE_COMPONENTS
if latest_installed_package_list=$(elastic_fleet_installed_packages); then
echo $latest_installed_package_list | jq '[.items[] | {name: .name, es_index_patterns: .dataStreams}]' > $PACKAGE_COMPONENTS
fi
if retry 3 1 "so-elasticsearch-query / --fail --output /dev/null"; then
# Refresh installed component template list
latest_component_templates_list=$(so-elasticsearch-query _component_template | jq '.component_templates[] | .name' | jq -s '.')

View File

@@ -3,11 +3,36 @@
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
# or more contributor license agreements. Licensed under the Elastic License 2.0; you may not use
# this file except in compliance with the Elastic License 2.0.
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
{%- from 'vars/globals.map.jinja' import GLOBALS %}
{%- from 'elasticfleet/map.jinja' import ELASTICFLEETMERGED %}
{%- from 'elasticfleet/config.map.jinja' import LOGSTASH_CONFIG_YAML %}
. /usr/sbin/so-common
FORCE_UPDATE=false
UPDATE_CERTS=false
LOGSTASH_PILLAR_CONFIG_YAML="{{ LOGSTASH_CONFIG_YAML }}"
LOGSTASH_PILLAR_STATE_FILE="/opt/so/state/esfleet_logstash_config_pillar"
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force)
FORCE_UPDATE=true
shift
;;
-c| --certs)
UPDATE_CERTS=true
FORCE_UPDATE=true
shift
;;
*)
echo "Unknown option $1"
echo "Usage: $0 [-f|--force] [-c|--certs]"
exit 1
;;
esac
done
# Only run on Managers
if ! is_manager_node; then
printf "Not a Manager Node... Exiting"
@@ -15,22 +40,104 @@ if ! is_manager_node; then
fi
function update_logstash_outputs() {
# Generate updated JSON payload
JSON_STRING=$(jq -n --arg UPDATEDLIST $NEW_LIST_JSON '{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":""}')
if logstash_policy=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_logstash" --retry 3 --retry-delay 10 --fail 2>/dev/null); then
SSL_CONFIG=$(echo "$logstash_policy" | jq -r '.item.ssl')
LOGSTASHKEY=$(openssl rsa -in /etc/pki/elasticfleet-logstash.key)
LOGSTASHCRT=$(openssl x509 -in /etc/pki/elasticfleet-logstash.crt)
LOGSTASHCA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
# Revert escaped \\n to \n for jq
LOGSTASH_PILLAR_CONFIG_YAML=$(printf '%b' "$LOGSTASH_PILLAR_CONFIG_YAML")
if SECRETS=$(echo "$logstash_policy" | jq -er '.item.secrets' 2>/dev/null); then
if [[ "$UPDATE_CERTS" != "true" ]]; then
# Reuse existing secret
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
--argjson SECRETS "$SECRETS" \
--argjson SSL_CONFIG "$SSL_CONFIG" \
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": $SSL_CONFIG,"secrets": $SECRETS}')
else
# Update certs, creating new secret
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
--arg LOGSTASHKEY "$LOGSTASHKEY" \
--arg LOGSTASHCRT "$LOGSTASHCRT" \
--arg LOGSTASHCA "$LOGSTASHCA" \
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": {"certificate": $LOGSTASHCRT,"certificate_authorities":[ $LOGSTASHCA ]},"secrets": {"ssl":{"key": $LOGSTASHKEY }}}')
fi
else
if [[ "$UPDATE_CERTS" != "true" ]]; then
# Reuse existing ssl config
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
--argjson SSL_CONFIG "$SSL_CONFIG" \
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": $SSL_CONFIG}')
else
# Update ssl config
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--arg CONFIG_YAML "$LOGSTASH_PILLAR_CONFIG_YAML" \
--arg LOGSTASHKEY "$LOGSTASHKEY" \
--arg LOGSTASHCRT "$LOGSTASHCRT" \
--arg LOGSTASHCA "$LOGSTASHCA" \
'{"name":"grid-logstash","type":"logstash","hosts": $UPDATEDLIST,"is_default":true,"is_default_monitoring":true,"config_yaml":$CONFIG_YAML,"ssl": {"certificate": $LOGSTASHCRT,"key": $LOGSTASHKEY,"certificate_authorities":[ $LOGSTASHCA ]}}')
fi
fi
fi
# 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')
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--argjson SSL_CONFIG "$SSL_CONFIG" \
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": $SSL_CONFIG}')
# Update Kafka outputs
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" | jq
if kafka_policy=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" --fail 2>/dev/null); then
SSL_CONFIG=$(echo "$kafka_policy" | jq -r '.item.ssl')
KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key)
KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt)
KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
if SECRETS=$(echo "$kafka_policy" | jq -er '.item.secrets' 2>/dev/null); then
if [[ "$UPDATE_CERTS" != "true" ]]; then
# Update policy when fleet has secrets enabled
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--argjson SSL_CONFIG "$SSL_CONFIG" \
--argjson SECRETS "$SECRETS" \
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": $SSL_CONFIG,"secrets": $SECRETS}')
else
# Update certs, creating new secret
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--arg KAFKAKEY "$KAFKAKEY" \
--arg KAFKACRT "$KAFKACRT" \
--arg KAFKACA "$KAFKACA" \
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": {"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"secrets": {"ssl":{"key": $KAFKAKEY }}}')
fi
else
if [[ "$UPDATE_CERTS" != "true" ]]; then
# Update policy when fleet has secrets disabled or policy hasn't been force updated
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--argjson SSL_CONFIG "$SSL_CONFIG" \
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": $SSL_CONFIG}')
else
# Update ssl config
JSON_STRING=$(jq -n \
--arg UPDATEDLIST "$NEW_LIST_JSON" \
--arg KAFKAKEY "$KAFKAKEY" \
--arg KAFKACRT "$KAFKACRT" \
--arg KAFKACA "$KAFKACA" \
'{"name": "grid-kafka","type": "kafka","hosts": $UPDATEDLIST,"is_default": true,"is_default_monitoring": true,"config_yaml": "","ssl": { "certificate_authorities": [ $KAFKACA ], "certificate": $KAFKACRT, "key": $KAFKAKEY, "verification_mode": "full" }}')
fi
fi
# Update Kafka outputs
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" | jq
else
printf "Failed to get current Kafka output policy..."
exit 1
fi
}
{% if GLOBALS.pipeline == "KAFKA" %}
@@ -46,7 +153,7 @@ function update_kafka_outputs() {
# Get the current list of kafka outputs & hash them
CURRENT_LIST=$(jq -c -r '.item.hosts' <<< "$RAW_JSON")
CURRENT_HASH=$(sha1sum <<< "$CURRENT_LIST" | awk '{print $1}')
CURRENT_HASH=$(sha256sum <<< "$CURRENT_LIST" | awk '{print $1}')
declare -a NEW_LIST=()
@@ -69,10 +176,19 @@ function update_kafka_outputs() {
printf "Failed to query for current Logstash Outputs..."
exit 1
fi
# logstash adv config - compare pillar to last state file value
if [[ -f "$LOGSTASH_PILLAR_STATE_FILE" ]]; then
PREVIOUS_LOGSTASH_PILLAR_CONFIG_YAML=$(cat "$LOGSTASH_PILLAR_STATE_FILE")
if [[ "$LOGSTASH_PILLAR_CONFIG_YAML" != "$PREVIOUS_LOGSTASH_PILLAR_CONFIG_YAML" ]]; then
echo "Logstash pillar config has changed - forcing update"
FORCE_UPDATE=true
fi
echo "$LOGSTASH_PILLAR_CONFIG_YAML" > "$LOGSTASH_PILLAR_STATE_FILE"
fi
# Get the current list of Logstash outputs & hash them
CURRENT_LIST=$(jq -c -r '.item.hosts' <<< "$RAW_JSON")
CURRENT_HASH=$(sha1sum <<< "$CURRENT_LIST" | awk '{print $1}')
CURRENT_HASH=$(sha256sum <<< "$CURRENT_LIST" | awk '{print $1}')
declare -a NEW_LIST=()
@@ -121,10 +237,10 @@ function update_kafka_outputs() {
# Sort & hash the new list of Logstash Outputs
NEW_LIST_JSON=$(jq --compact-output --null-input '$ARGS.positional' --args -- "${NEW_LIST[@]}")
NEW_HASH=$(sha1sum <<< "$NEW_LIST_JSON" | awk '{print $1}')
NEW_HASH=$(sha256sum <<< "$NEW_LIST_JSON" | awk '{print $1}')
# Compare the current & new list of outputs - if different, update the Logstash outputs
if [ "$NEW_HASH" = "$CURRENT_HASH" ]; then
if [[ "$NEW_HASH" = "$CURRENT_HASH" ]] && [[ "$FORCE_UPDATE" != "true" ]]; then
printf "\nHashes match - no update needed.\n"
printf "Current List: $CURRENT_LIST\nNew List: $NEW_LIST_JSON\n"

View File

@@ -10,8 +10,16 @@
{%- for PACKAGE in SUPPORTED_PACKAGES %}
echo "Setting up {{ PACKAGE }} package..."
VERSION=$(elastic_fleet_package_version_check "{{ PACKAGE }}")
elastic_fleet_package_install "{{ PACKAGE }}" "$VERSION"
if VERSION=$(elastic_fleet_package_version_check "{{ PACKAGE }}"); then
if ! elastic_fleet_package_install "{{ PACKAGE }}" "$VERSION"; then
# packages loaded by this script should never fail to install and REQUIRED before an installation of SO can be considered successful
echo -e "\nERROR: Failed to install default integration package -- $PACKAGE $VERSION"
exit 1
fi
else
echo -e "\nERROR: Failed to get version information for integration $PACKAGE"
exit 1
fi
echo
{%- endfor %}
echo

View File

@@ -10,8 +10,15 @@
{%- for PACKAGE in SUPPORTED_PACKAGES %}
echo "Upgrading {{ PACKAGE }} package..."
VERSION=$(elastic_fleet_package_latest_version_check "{{ PACKAGE }}")
elastic_fleet_package_install "{{ PACKAGE }}" "$VERSION"
if VERSION=$(elastic_fleet_package_latest_version_check "{{ PACKAGE }}"); then
if ! elastic_fleet_package_install "{{ PACKAGE }}" "$VERSION"; then
# exit 1 on failure to upgrade a default package, allow salt to handle retries
echo -e "\nERROR: Failed to upgrade $PACKAGE to version: $VERSION"
exit 1
fi
else
echo -e "\nERROR: Failed to get version information for integration $PACKAGE"
fi
echo
{%- endfor %}
echo

View File

@@ -23,18 +23,17 @@ if [[ "$RETURN_CODE" != "0" ]]; then
exit 1
fi
ALIASES=".fleet-servers .fleet-policies-leader .fleet-policies .fleet-agents .fleet-artifacts .fleet-enrollment-api-keys .kibana_ingest"
for ALIAS in ${ALIASES}
do
ALIASES=(.fleet-servers .fleet-policies-leader .fleet-policies .fleet-agents .fleet-artifacts .fleet-enrollment-api-keys .kibana_ingest)
for ALIAS in "${ALIASES[@]}"; do
# Get all concrete indices from alias
INDXS=$(curl -K /opt/so/conf/kibana/curl.config -s -k -L -H "Content-Type: application/json" "https://localhost:9200/_resolve/index/${ALIAS}" | jq -r '.aliases[].indices[]')
# Delete all resolved indices
for INDX in ${INDXS}
do
if INDXS_RAW=$(curl -sK /opt/so/conf/kibana/curl.config -s -k -L -H "Content-Type: application/json" "https://localhost:9200/_resolve/index/${ALIAS}" --fail 2>/dev/null); then
INDXS=$(echo "$INDXS_RAW" | jq -r '.aliases[].indices[]')
# Delete all resolved indices
for INDX in ${INDXS}; do
status "Deleting $INDX"
curl -K /opt/so/conf/kibana/curl.config -s -k -L -H "Content-Type: application/json" "https://localhost:9200/${INDX}" -XDELETE
done
done
fi
done
# Restarting Kibana...
@@ -51,22 +50,61 @@ if [[ "$RETURN_CODE" != "0" ]]; then
fi
printf "\n### Create ES Token ###\n"
ESTOKEN=$(curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/service_tokens" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' | jq -r .value)
if ESTOKEN_RAW=$(fleet_api "service_tokens" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json'); then
ESTOKEN=$(echo "$ESTOKEN_RAW" | jq -r .value)
else
echo -e "\nFailed to create ES token..."
exit 1
fi
### Create Outputs, Fleet Policy and Fleet URLs ###
# Create the Manager Elasticsearch Output first and set it as the default output
printf "\nAdd Manager Elasticsearch Output...\n"
ESCACRT=$(openssl x509 -in $INTCA)
JSON_STRING=$( jq -n \
--arg ESCACRT "$ESCACRT" \
'{"name":"so-manager_elasticsearch","id":"so-manager_elasticsearch","type":"elasticsearch","hosts":["https://{{ GLOBALS.manager_ip }}:9200","https://{{ GLOBALS.manager }}:9200"],"is_default":true,"is_default_monitoring":true,"config_yaml":"","ssl":{"certificate_authorities": [$ESCACRT]}}' )
curl -K /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"
ESCACRT=$(openssl x509 -in "$INTCA" -outform DER | sha256sum | cut -d' ' -f1 | tr '[:lower:]' '[:upper:]')
JSON_STRING=$(jq -n \
--arg ESCACRT "$ESCACRT" \
'{"name":"so-manager_elasticsearch","id":"so-manager_elasticsearch","type":"elasticsearch","hosts":["https://{{ GLOBALS.manager_ip }}:9200","https://{{ GLOBALS.manager }}:9200"],"is_default":false,"is_default_monitoring":false,"config_yaml":"","ca_trusted_fingerprint": $ESCACRT}')
if ! fleet_api "outputs" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"; then
echo -e "\nFailed to create so-elasticsearch_manager policy..."
exit 1
fi
printf "\n\n"
# so-manager_elasticsearch should exist and be disabled. Now update it before checking its the only default policy
MANAGER_OUTPUT_ENABLED=$(echo "$JSON_STRING" | jq 'del(.id) | .is_default = true | .is_default_monitoring = true')
if ! curl -sK /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_elasticsearch" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$MANAGER_OUTPUT_ENABLED"; then
echo -e "\n failed to update so-manager_elasticsearch"
exit 1
fi
# At this point there should only be two policies. fleet-default-output & so-manager_elasticsearch
status "Verifying so-manager_elasticsearch policy is configured as the current default"
# Grab the fleet-default-output policy instead of so-manager_elasticsearch, because a weird state can exist where both fleet-default-output & so-elasticsearch_manager can be set as the active default output for logs / metrics. Resulting in logs not ingesting on import/eval nodes
if DEFAULTPOLICY=$(fleet_api "outputs/fleet-default-output"); then
fleet_default=$(echo "$DEFAULTPOLICY" | jq -er '.item.is_default')
fleet_default_monitoring=$(echo "$DEFAULTPOLICY" | jq -er '.item.is_default_monitoring')
# Check that fleet-default-output isn't configured as a default for anything ( both variables return false )
if [[ $fleet_default == "false" ]] && [[ $fleet_default_monitoring == "false" ]]; then
echo -e "\nso-manager_elasticsearch is configured as the current default policy..."
else
echo -e "\nVerification of so-manager_elasticsearch policy failed... The default 'fleet-default-output' output is still active..."
exit 1
fi
else
# fleet-output-policy is created automatically by fleet when started. Should always exist on any installation type
echo -e "\nDefault fleet-default-output policy doesn't exist...\n"
exit 1
fi
# Create the Manager Fleet Server Host Agent Policy
# This has to be done while the Elasticsearch Output is set to the default Output
printf "Create Manager Fleet Server Policy...\n"
elastic_fleet_policy_create "FleetServer_{{ GLOBALS.hostname }}" "Fleet Server - {{ GLOBALS.hostname }}" "false" "120"
if ! elastic_fleet_policy_create "FleetServer_{{ GLOBALS.hostname }}" "Fleet Server - {{ GLOBALS.hostname }}" "false" "120"; then
echo -e "\n Failed to create Manager fleet server policy..."
exit 1
fi
# Modify the default integration policy to update the policy_id with the correct naming
UPDATED_INTEGRATION_POLICY=$(jq --arg policy_id "FleetServer_{{ GLOBALS.hostname }}" --arg name "fleet_server-{{ GLOBALS.hostname }}" '
@@ -74,7 +112,10 @@ UPDATED_INTEGRATION_POLICY=$(jq --arg policy_id "FleetServer_{{ GLOBALS.hostname
.name = $name' /opt/so/conf/elastic-fleet/integrations/fleet-server/fleet-server.json)
# Add the Fleet Server Integration to the new Fleet Policy
elastic_fleet_integration_create "$UPDATED_INTEGRATION_POLICY"
if ! elastic_fleet_integration_create "$UPDATED_INTEGRATION_POLICY"; then
echo -e "\nFailed to create Fleet server integration for Manager.."
exit 1
fi
# Now we can create the Logstash Output and set it to to be the default Output
printf "\n\nCreate Logstash Output Config if node is not an Import or Eval install\n"
@@ -86,9 +127,12 @@ JSON_STRING=$( jq -n \
--arg LOGSTASHCRT "$LOGSTASHCRT" \
--arg LOGSTASHKEY "$LOGSTASHKEY" \
--arg LOGSTASHCA "$LOGSTASHCA" \
'{"name":"grid-logstash","is_default":true,"is_default_monitoring":true,"id":"so-manager_logstash","type":"logstash","hosts":["{{ GLOBALS.manager_ip }}:5055", "{{ GLOBALS.manager }}:5055"],"config_yaml":"","ssl":{"certificate": $LOGSTASHCRT,"key": $LOGSTASHKEY,"certificate_authorities":[ $LOGSTASHCA ]},"proxy_id":null}'
'{"name":"grid-logstash","is_default":true,"is_default_monitoring":true,"id":"so-manager_logstash","type":"logstash","hosts":["{{ GLOBALS.manager_ip }}:5055", "{{ GLOBALS.manager }}:5055"],"config_yaml":"","ssl":{"certificate": $LOGSTASHCRT,"certificate_authorities":[ $LOGSTASHCA ]},"secrets":{"ssl":{"key": $LOGSTASHKEY }},"proxy_id":null}'
)
curl -K /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"
if ! fleet_api "outputs" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"; then
echo -e "\nFailed to create logstash fleet output"
exit 1
fi
printf "\n\n"
{%- endif %}
@@ -106,7 +150,10 @@ else
fi
## This array replaces whatever URLs are currently configured
curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/fleet_server_hosts" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
if ! fleet_api "fleet_server_hosts" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"; then
echo -e "\nFailed to add manager fleet URL"
exit 1
fi
printf "\n\n"
### Create Policies & Associated Integration Configuration ###
@@ -117,13 +164,22 @@ printf "\n\n"
/usr/sbin/so-elasticsearch-templates-load
# Initial Endpoints Policy
elastic_fleet_policy_create "endpoints-initial" "Initial Endpoint Policy" "false" "1209600"
if ! elastic_fleet_policy_create "endpoints-initial" "Initial Endpoint Policy" "false" "1209600"; then
echo -e "\nFailed to create endpoints-initial policy..."
exit 1
fi
# Grid Nodes - General Policy
elastic_fleet_policy_create "so-grid-nodes_general" "SO Grid Nodes - General Purpose" "false" "1209600"
if ! elastic_fleet_policy_create "so-grid-nodes_general" "SO Grid Nodes - General Purpose" "false" "1209600"; then
echo -e "\nFailed to create so-grid-nodes_general policy..."
exit 1
fi
# Grid Nodes - Heavy Node Policy
elastic_fleet_policy_create "so-grid-nodes_heavy" "SO Grid Nodes - Heavy Node" "false" "1209600"
if ! elastic_fleet_policy_create "so-grid-nodes_heavy" "SO Grid Nodes - Heavy Node" "false" "1209600"; then
echo -e "\nFailed to create so-grid-nodes_heavy policy..."
exit 1
fi
# Load Integrations for default policies
so-elastic-fleet-integration-policy-load
@@ -135,14 +191,34 @@ JSON_STRING=$( jq -n \
'{"name":$NAME,"host":$URL,"is_default":true}'
)
curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/agent_download_sources" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
if ! fleet_api "agent_download_sources" -XPOST -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"; then
echo -e "\nFailed to update Elastic Agent artifact URL"
exit 1
fi
### Finalization ###
# Query for Enrollment Tokens for default policies
ENDPOINTSENROLLMENTOKEN=$(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')
GRIDNODESENROLLMENTOKENGENERAL=$(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("so-grid-nodes_general")) | .api_key')
GRIDNODESENROLLMENTOKENHEAVY=$(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("so-grid-nodes_heavy")) | .api_key')
if ENDPOINTSENROLLMENTOKEN_RAW=$(fleet_api "enrollment_api_keys" -H 'kbn-xsrf: true' -H 'Content-Type: application/json'); then
ENDPOINTSENROLLMENTOKEN=$(echo "$ENDPOINTSENROLLMENTOKEN_RAW" | jq .list | jq -r -c '.[] | select(.policy_id | contains("endpoints-initial")) | .api_key')
else
echo -e "\nFailed to query for Endpoints enrollment token"
exit 1
fi
if GRIDNODESENROLLMENTOKENGENERAL_RAW=$(fleet_api "enrollment_api_keys" -H 'kbn-xsrf: true' -H 'Content-Type: application/json'); then
GRIDNODESENROLLMENTOKENGENERAL=$(echo "$GRIDNODESENROLLMENTOKENGENERAL_RAW" | jq .list | jq -r -c '.[] | select(.policy_id | contains("so-grid-nodes_general")) | .api_key')
else
echo -e "\nFailed to query for Grid nodes - General enrollment token"
exit 1
fi
if GRIDNODESENROLLMENTOKENHEAVY_RAW=$(fleet_api "enrollment_api_keys" -H 'kbn-xsrf: true' -H 'Content-Type: application/json'); then
GRIDNODESENROLLMENTOKENHEAVY=$(echo "$GRIDNODESENROLLMENTOKENHEAVY_RAW" | jq .list | jq -r -c '.[] | select(.policy_id | contains("so-grid-nodes_heavy")) | .api_key')
else
echo -e "\nFailed to query for Grid nodes - Heavy enrollment token"
exit 1
fi
# Store needed data in minion pillar
pillar_file=/opt/so/saltstack/local/pillar/minions/{{ GLOBALS.minion_id }}.sls

View File

@@ -5,46 +5,78 @@
# Elastic License 2.0.
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% if GLOBALS.role in ['so-manager', 'so-standalone', 'so-managersearch'] %}
{% if GLOBALS.role in ['so-manager', 'so-standalone', 'so-managersearch', 'so-managerhype'] %}
. /usr/sbin/so-common
force=false
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force)
force=true
shift
;;
*)
echo "Unknown option $1"
echo "Usage: $0 [-f|--force]"
exit 1
;;
esac
done
# Check to make sure that Kibana API is up & ready
RETURN_CODE=0
wait_for_web_response "http://localhost:5601/api/fleet/settings" "fleet" 300 "curl -K /opt/so/conf/elasticsearch/curl.config"
RETURN_CODE=$?
if [[ "$RETURN_CODE" != "0" ]]; then
printf "Kibana API not accessible, can't setup Elastic Fleet output policy for Kafka..."
exit 1
echo -e "\nKibana API not accessible, can't setup Elastic Fleet output policy for Kafka...\n"
exit 1
fi
output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs" | jq -r .items[].id)
KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt)
KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key)
KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
KAFKA_OUTPUT_VERSION="2.6.0"
if ! echo "$output" | grep -q "so-manager_kafka"; then
KAFKACRT=$(openssl x509 -in /etc/pki/elasticfleet-kafka.crt)
KAFKAKEY=$(openssl rsa -in /etc/pki/elasticfleet-kafka.key)
KAFKACA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
KAFKA_OUTPUT_VERSION="2.6.0"
if ! kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" --fail 2>/dev/null); then
# Create a new output policy for Kafka. Default is disabled 'is_default: false & is_default_monitoring: false'
JSON_STRING=$( jq -n \
--arg KAFKACRT "$KAFKACRT" \
--arg KAFKAKEY "$KAFKAKEY" \
--arg KAFKACA "$KAFKACA" \
--arg MANAGER_IP "{{ GLOBALS.manager_ip }}:9092" \
--arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \
'{ "name": "grid-kafka", "id": "so-manager_kafka", "type": "kafka", "hosts": [ $MANAGER_IP ], "is_default": false, "is_default_monitoring": false, "config_yaml": "", "ssl": { "certificate_authorities": [ $KAFKACA ], "certificate": $KAFKACRT, "key": $KAFKAKEY, "verification_mode": "full" }, "proxy_id": null, "client_id": "Elastic", "version": $KAFKA_OUTPUT_VERSION, "compression": "none", "auth_type": "ssl", "partition": "round_robin", "round_robin": { "group_events": 10 }, "topics":[{"topic":"default-securityonion"}], "headers": [ { "key": "", "value": "" } ], "timeout": 30, "broker_timeout": 30, "required_acks": 1 }'
)
curl -sK /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/outputs" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" -o /dev/null
refresh_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs" | jq -r .items[].id)
if ! echo "$refresh_output" | grep -q "so-manager_kafka"; then
echo -e "\nFailed to setup Elastic Fleet output policy for Kafka...\n"
--arg KAFKACRT "$KAFKACRT" \
--arg KAFKAKEY "$KAFKAKEY" \
--arg KAFKACA "$KAFKACA" \
--arg MANAGER_IP "{{ GLOBALS.manager_ip }}:9092" \
--arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \
'{"name":"grid-kafka", "id":"so-manager_kafka","type":"kafka","hosts":[ $MANAGER_IP ],"is_default":false,"is_default_monitoring":false,"config_yaml":"","ssl":{"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"proxy_id":null,"client_id":"Elastic","version": $KAFKA_OUTPUT_VERSION ,"compression":"none","auth_type":"ssl","partition":"round_robin","round_robin":{"group_events":10},"topics":[{"topic":"default-securityonion"}],"headers":[{"key":"","value":""}],"timeout":30,"broker_timeout":30,"required_acks":1,"secrets":{"ssl":{"key": $KAFKAKEY }}}'
)
if ! response=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/outputs" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" --fail 2>/dev/null); then
echo -e "\nFailed to setup Elastic Fleet output policy for Kafka...\n"
exit 1
else
echo -e "\nSuccessfully setup Elastic Fleet output policy for Kafka...\n"
exit 0
fi
elif kafka_output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_kafka" --fail 2>/dev/null) && [[ "$force" == "true" ]]; then
# force an update to Kafka policy. Keep the current value of Kafka output policy (enabled/disabled).
ENABLED_DISABLED=$(echo "$kafka_output" | jq -e .item.is_default)
HOSTS=$(echo "$kafka_output" | jq -r '.item.hosts')
JSON_STRING=$( jq -n \
--arg KAFKACRT "$KAFKACRT" \
--arg KAFKAKEY "$KAFKAKEY" \
--arg KAFKACA "$KAFKACA" \
--arg ENABLED_DISABLED "$ENABLED_DISABLED"\
--arg KAFKA_OUTPUT_VERSION "$KAFKA_OUTPUT_VERSION" \
--argjson HOSTS "$HOSTS" \
'{"name":"grid-kafka","type":"kafka","hosts":$HOSTS,"is_default":$ENABLED_DISABLED,"is_default_monitoring":$ENABLED_DISABLED,"config_yaml":"","ssl":{"certificate_authorities":[ $KAFKACA ],"certificate": $KAFKACRT ,"key":"","verification_mode":"full"},"proxy_id":null,"client_id":"Elastic","version": $KAFKA_OUTPUT_VERSION ,"compression":"none","auth_type":"ssl","partition":"round_robin","round_robin":{"group_events":10},"topics":[{"topic":"default-securityonion"}],"headers":[{"key":"","value":""}],"timeout":30,"broker_timeout":30,"required_acks":1,"secrets":{"ssl":{"key": $KAFKAKEY }}}'
)
if ! response=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_kafka" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING" --fail 2>/dev/null); then
echo -e "\nFailed to force update to Elastic Fleet output policy for Kafka...\n"
exit 1
elif echo "$refresh_output" | grep -q "so-manager_kafka"; then
echo -e "\nSuccessfully setup Elastic Fleet output policy for Kafka...\n"
else
echo -e "\nForced update to Elastic Fleet output policy for Kafka...\n"
fi
elif echo "$output" | grep -q "so-manager_kafka"; then
else
echo -e "\nElastic Fleet output policy for Kafka already exists...\n"
fi
{% else %}

View File

@@ -1,6 +1,6 @@
elasticsearch:
enabled: false
version: 8.18.4
version: 8.18.8
index_clean: true
config:
action:
@@ -72,6 +72,8 @@ elasticsearch:
actions:
set_priority:
priority: 0
allocate:
number_of_replicas: ""
min_age: 60d
delete:
actions:
@@ -84,11 +86,25 @@ elasticsearch:
max_primary_shard_size: 50gb
set_priority:
priority: 100
forcemerge:
max_num_segments: ""
shrink:
max_primary_shard_size: ""
method: COUNT
number_of_shards: ""
min_age: 0ms
warm:
actions:
set_priority:
priority: 50
forcemerge:
max_num_segments: ""
shrink:
max_primary_shard_size: ""
method: COUNT
number_of_shards: ""
allocate:
number_of_replicas: ""
min_age: 30d
so-case:
index_sorting: false
@@ -245,7 +261,6 @@ elasticsearch:
set_priority:
priority: 50
min_age: 30d
warm: 7
so-detection:
index_sorting: false
index_template:
@@ -284,6 +299,86 @@ elasticsearch:
hot:
actions: {}
min_age: 0ms
so-assistant-chat:
index_sorting: false
index_template:
composed_of:
- assistant-chat-mappings
- assistant-chat-settings
data_stream:
allow_custom_routing: false
hidden: false
ignore_missing_component_templates: []
index_patterns:
- so-assistant-chat*
priority: 501
template:
mappings:
date_detection: false
dynamic_templates:
- strings_as_keyword:
mapping:
ignore_above: 1024
type: keyword
match_mapping_type: string
settings:
index:
lifecycle:
name: so-assistant-chat-logs
mapping:
total_fields:
limit: 1500
number_of_replicas: 0
number_of_shards: 1
refresh_interval: 1s
sort:
field: '@timestamp'
order: desc
policy:
phases:
hot:
actions: {}
min_age: 0ms
so-assistant-session:
index_sorting: false
index_template:
composed_of:
- assistant-session-mappings
- assistant-session-settings
data_stream:
allow_custom_routing: false
hidden: false
ignore_missing_component_templates: []
index_patterns:
- so-assistant-session*
priority: 501
template:
mappings:
date_detection: false
dynamic_templates:
- strings_as_keyword:
mapping:
ignore_above: 1024
type: keyword
match_mapping_type: string
settings:
index:
lifecycle:
name: so-assistant-session-logs
mapping:
total_fields:
limit: 1500
number_of_replicas: 0
number_of_shards: 1
refresh_interval: 1s
sort:
field: '@timestamp'
order: desc
policy:
phases:
hot:
actions: {}
min_age: 0ms
so-endgame:
index_sorting: false
index_template:
@@ -504,7 +599,6 @@ elasticsearch:
set_priority:
priority: 50
min_age: 30d
warm: 7
so-import:
index_sorting: false
index_template:
@@ -852,7 +946,6 @@ elasticsearch:
set_priority:
priority: 50
min_age: 30d
warm: 7
so-hydra:
close: 30
delete: 365
@@ -963,7 +1056,6 @@ elasticsearch:
set_priority:
priority: 50
min_age: 30d
warm: 7
so-lists:
index_sorting: false
index_template:
@@ -1047,6 +1139,8 @@ elasticsearch:
actions:
set_priority:
priority: 0
allocate:
number_of_replicas: ""
min_age: 60d
delete:
actions:
@@ -1059,11 +1153,25 @@ elasticsearch:
max_primary_shard_size: 50gb
set_priority:
priority: 100
forcemerge:
max_num_segments: ""
shrink:
max_primary_shard_size: ""
method: COUNT
number_of_shards: ""
min_age: 0ms
warm:
actions:
set_priority:
priority: 50
allocate:
number_of_replicas: ""
forcemerge:
max_num_segments: ""
shrink:
max_primary_shard_size: ""
method: COUNT
number_of_shards: ""
min_age: 30d
so-logs-detections_x_alerts:
index_sorting: false
@@ -1243,6 +1351,68 @@ elasticsearch:
set_priority:
priority: 50
min_age: 30d
so-elastic-agent-monitor:
index_sorting: false
index_template:
composed_of:
- event-mappings
- so-elastic-agent-monitor
- so-fleet_integrations.ip_mappings-1
- so-fleet_globals-1
- so-fleet_agent_id_verification-1
data_stream:
allow_custom_routing: false
hidden: false
index_patterns:
- logs-agentmonitor-*
priority: 501
template:
mappings:
_meta:
managed: true
managed_by: security_onion
package:
name: elastic_agent
settings:
index:
lifecycle:
name: so-elastic-agent-monitor-logs
mapping:
total_fields:
limit: 5000
number_of_replicas: 0
sort:
field: '@timestamp'
order: desc
policy:
_meta:
managed: true
managed_by: security_onion
package:
name: elastic_agent
phases:
cold:
actions:
set_priority:
priority: 0
min_age: 60d
delete:
actions:
delete: {}
min_age: 365d
hot:
actions:
rollover:
max_age: 30d
max_primary_shard_size: 50gb
set_priority:
priority: 100
min_age: 0ms
warm:
actions:
set_priority:
priority: 50
min_age: 30d
so-logs-elastic_agent_x_apm_server:
index_sorting: false
index_template:
@@ -1849,6 +2019,70 @@ elasticsearch:
set_priority:
priority: 50
min_age: 30d
so-logs-elasticsearch_x_server:
index_sorting: false
index_template:
composed_of:
- logs-elasticsearch.server@package
- logs-elasticsearch.server@custom
- so-fleet_integrations.ip_mappings-1
- so-fleet_globals-1
- so-fleet_agent_id_verification-1
data_stream:
allow_custom_routing: false
hidden: false
ignore_missing_component_templates:
- logs-elasticsearch.server@custom
index_patterns:
- logs-elasticsearch.server-*
priority: 501
template:
mappings:
_meta:
managed: true
managed_by: security_onion
package:
name: elastic_agent
settings:
index:
lifecycle:
name: so-logs-elasticsearch.server-logs
mapping:
total_fields:
limit: 5000
number_of_replicas: 0
sort:
field: '@timestamp'
order: desc
policy:
_meta:
managed: true
managed_by: security_onion
package:
name: elastic_agent
phases:
cold:
actions:
set_priority:
priority: 0
min_age: 60d
delete:
actions:
delete: {}
min_age: 365d
hot:
actions:
rollover:
max_age: 30d
max_primary_shard_size: 50gb
set_priority:
priority: 100
min_age: 0ms
warm:
actions:
set_priority:
priority: 50
min_age: 30d
so-logs-endpoint_x_actions:
index_sorting: false
index_template:
@@ -2917,7 +3151,6 @@ elasticsearch:
set_priority:
priority: 50
min_age: 30d
warm: 7
so-logs-system_x_application:
index_sorting: false
index_template:
@@ -4031,7 +4264,7 @@ elasticsearch:
hot:
actions:
rollover:
max_age: 1d
max_age: 30d
max_primary_shard_size: 50gb
set_priority:
priority: 100

View File

@@ -0,0 +1,22 @@
{
"processors": [
{
"convert": {
"field": "_ingest._value",
"type": "ip",
"target_field": "_ingest._temp_ip",
"ignore_failure": true
}
},
{
"append": {
"field": "temp._valid_ips",
"allow_duplicates": false,
"value": [
"{{{_ingest._temp_ip}}}"
],
"ignore_failure": true
}
}
]
}

View File

@@ -0,0 +1,36 @@
{
"processors": [
{
"set": {
"field": "event.dataset",
"value": "gridmetrics.agents",
"ignore_failure": true
}
},
{
"set": {
"field": "event.module",
"value": "gridmetrics",
"ignore_failure": true
}
},
{
"remove": {
"field": [
"host",
"elastic_agent",
"agent"
],
"ignore_missing": true,
"ignore_failure": true
}
},
{
"json": {
"field": "message",
"add_to_root": true,
"ignore_failure": true
}
}
]
}

View File

@@ -23,8 +23,9 @@
{ "set": { "if": "ctx.event?.module == 'fim'", "override": true, "field": "event.module", "value": "file_integrity" } },
{ "rename": { "if": "ctx.winlog?.provider_name == 'Microsoft-Windows-Windows Defender'", "ignore_missing": true, "field": "winlog.event_data.Threat Name", "target_field": "winlog.event_data.threat_name" } },
{ "set": { "if": "ctx?.metadata?.kafka != null" , "field": "kafka.id", "value": "{{metadata.kafka.partition}}{{metadata.kafka.offset}}{{metadata.kafka.timestamp}}", "ignore_failure": true } },
{ "set": { "if": "ctx.event?.dataset != null && ctx.event?.dataset == 'elasticsearch.server'", "field": "event.module", "value":"elasticsearch" }},
{"append": {"field":"related.ip","value":["{{source.ip}}","{{destination.ip}}"],"allow_duplicates":false,"if":"ctx?.event?.dataset == 'endpoint.events.network' && ctx?.source?.ip != null","ignore_failure":true}},
{"foreach": {"field":"host.ip","processor":{"append":{"field":"related.ip","value":"{{_ingest._value}}","allow_duplicates":false}},"if":"ctx?.event?.module == 'endpoint'","description":"Extract IPs from Elastic Agent events (host.ip) and adds them to related.ip"}},
{"foreach": {"field":"host.ip","processor":{"append":{"field":"related.ip","value":"{{_ingest._value}}","allow_duplicates":false}},"if":"ctx?.event?.module == 'endpoint' && ctx?.host?.ip != null","ignore_missing":true, "description":"Extract IPs from Elastic Agent events (host.ip) and adds them to related.ip"}},
{ "remove": { "field": [ "message2", "type", "fields", "category", "module", "dataset", "event.dataset_temp", "dataset_tag_temp", "module_temp", "datastream_dataset_temp" ], "ignore_missing": true, "ignore_failure": true } }
]
}

View File

@@ -107,61 +107,61 @@
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-firewall",
"name": "logs-pfsense.log-1.23.1-firewall",
"if": "ctx.event.provider == 'filterlog'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-openvpn",
"name": "logs-pfsense.log-1.23.1-openvpn",
"if": "ctx.event.provider == 'openvpn'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-ipsec",
"name": "logs-pfsense.log-1.23.1-ipsec",
"if": "ctx.event.provider == 'charon'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-dhcp",
"name": "logs-pfsense.log-1.23.1-dhcp",
"if": "[\"dhcpd\", \"dhclient\", \"dhcp6c\"].contains(ctx.event.provider)"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-unbound",
"name": "logs-pfsense.log-1.23.1-unbound",
"if": "ctx.event.provider == 'unbound'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-haproxy",
"name": "logs-pfsense.log-1.23.1-haproxy",
"if": "ctx.event.provider == 'haproxy'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-php-fpm",
"name": "logs-pfsense.log-1.23.1-php-fpm",
"if": "ctx.event.provider == 'php-fpm'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-squid",
"name": "logs-pfsense.log-1.23.1-squid",
"if": "ctx.event.provider == 'squid'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-snort",
"name": "logs-pfsense.log-1.23.1-snort",
"if": "ctx.event.provider == 'snort'"
}
},
{
"pipeline": {
"name": "logs-pfsense.log-1.23.0-suricata",
"name": "logs-pfsense.log-1.23.1-suricata",
"if": "ctx.event.provider == 'suricata'"
}
},

View File

@@ -1,15 +1,79 @@
{
"description" : "suricata.alert",
"processors" : [
{ "set": { "if": "ctx.event?.imported != true", "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 } },
{ "rename":{ "field": "rule.ref", "target_field": "rule.version", "ignore_failure": true } },
{ "rename":{ "field": "rule.signature_id", "target_field": "rule.uuid", "ignore_failure": true } },
{ "rename":{ "field": "rule.signature_id", "target_field": "rule.signature", "ignore_failure": true } },
{ "rename":{ "field": "message2.payload_printable", "target_field": "network.data.decoded", "ignore_failure": true } },
{ "dissect": { "field": "rule.rule", "pattern": "%{?prefix}content:\"%{dns.query_name}\"%{?remainder}", "ignore_missing": true, "ignore_failure": true } },
{ "pipeline": { "name": "common.nids" } }
]
"description": "suricata.alert",
"processors": [
{
"set": {
"if": "ctx.event?.imported != true",
"field": "_index",
"value": "logs-suricata.alerts-so"
}
},
{
"set": {
"field": "tags",
"value": "alert"
}
},
{
"rename": {
"field": "message2.alert",
"target_field": "rule",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"rename": {
"field": "rule.signature",
"target_field": "rule.name",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"rename": {
"field": "rule.ref",
"target_field": "rule.version",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"rename": {
"field": "rule.signature_id",
"target_field": "rule.uuid",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"rename": {
"field": "rule.signature_id",
"target_field": "rule.signature",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.payload_printable",
"target_field": "network.data.decoded",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"dissect": {
"field": "rule.rule",
"pattern": "%{?prefix}content:\"%{dns.query_name}\"%{?remainder}",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"pipeline": {
"name": "common.nids"
}
}
]
}

View File

@@ -1,30 +1,155 @@
{
"description" : "suricata.common",
"processors" : [
{ "json": { "field": "message", "target_field": "message2", "ignore_failure": true } },
{ "rename": { "field": "message2.pkt_src", "target_field": "network.packet_source","ignore_failure": true } },
{ "rename": { "field": "message2.proto", "target_field": "network.transport", "ignore_failure": true } },
{ "rename": { "field": "message2.in_iface", "target_field": "observer.ingress.interface.name", "ignore_failure": true } },
{ "rename": { "field": "message2.flow_id", "target_field": "log.id.uid", "ignore_failure": true } },
{ "rename": { "field": "message2.src_ip", "target_field": "source.ip", "ignore_failure": true } },
{ "rename": { "field": "message2.src_port", "target_field": "source.port", "ignore_failure": true } },
{ "rename": { "field": "message2.dest_ip", "target_field": "destination.ip", "ignore_failure": true } },
{ "rename": { "field": "message2.dest_port", "target_field": "destination.port", "ignore_failure": true } },
{ "rename": { "field": "message2.vlan", "target_field": "network.vlan.id", "ignore_failure": true } },
{ "rename": { "field": "message2.community_id", "target_field": "network.community_id", "ignore_missing": true } },
{ "rename": { "field": "message2.xff", "target_field": "xff.ip", "ignore_missing": true } },
{ "set": { "field": "event.dataset", "value": "{{ message2.event_type }}" } },
{ "set": { "field": "observer.name", "value": "{{agent.name}}" } },
{ "set": { "field": "event.ingested", "value": "{{@timestamp}}" } },
{ "date": { "field": "message2.timestamp", "target_field": "@timestamp", "formats": ["ISO8601", "UNIX"], "timezone": "UTC", "ignore_failure": true } },
{ "remove":{ "field": "agent", "ignore_failure": true } },
{"append":{"field":"related.ip","value":["{{source.ip}}","{{destination.ip}}"],"allow_duplicates":false,"ignore_failure":true}},
{
"script": {
"source": "boolean isPrivate(def ip) { if (ip == null) return false; int dot1 = ip.indexOf('.'); if (dot1 == -1) return false; int dot2 = ip.indexOf('.', dot1 + 1); if (dot2 == -1) return false; int first = Integer.parseInt(ip.substring(0, dot1)); if (first == 10) return true; if (first == 192 && ip.startsWith('168.', dot1 + 1)) return true; if (first == 172) { int second = Integer.parseInt(ip.substring(dot1 + 1, dot2)); return second >= 16 && second <= 31; } return false; } String[] fields = new String[] {\"source\", \"destination\"}; for (int i = 0; i < fields.length; i++) { def field = fields[i]; def ip = ctx[field]?.ip; if (ip != null) { if (ctx.network == null) ctx.network = new HashMap(); if (isPrivate(ip)) { if (ctx.network.private_ip == null) ctx.network.private_ip = new ArrayList(); if (!ctx.network.private_ip.contains(ip)) ctx.network.private_ip.add(ip); } else { if (ctx.network.public_ip == null) ctx.network.public_ip = new ArrayList(); if (!ctx.network.public_ip.contains(ip)) ctx.network.public_ip.add(ip); } } }",
"ignore_failure": false
}
},
{ "pipeline": { "if": "ctx?.event?.dataset != null", "name": "suricata.{{event.dataset}}" } }
]
}
"description": "suricata.common",
"processors": [
{
"json": {
"field": "message",
"target_field": "message2",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.pkt_src",
"target_field": "network.packet_source",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.proto",
"target_field": "network.transport",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.in_iface",
"target_field": "observer.ingress.interface.name",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.flow_id",
"target_field": "log.id.uid",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.src_ip",
"target_field": "source.ip",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.src_port",
"target_field": "source.port",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.dest_ip",
"target_field": "destination.ip",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.dest_port",
"target_field": "destination.port",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.vlan",
"target_field": "network.vlan.id",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.community_id",
"target_field": "network.community_id",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.xff",
"target_field": "xff.ip",
"ignore_missing": true
}
},
{
"set": {
"field": "event.dataset",
"value": "{{ message2.event_type }}"
}
},
{
"set": {
"field": "observer.name",
"value": "{{agent.name}}"
}
},
{
"set": {
"field": "event.ingested",
"value": "{{@timestamp}}"
}
},
{
"date": {
"field": "message2.timestamp",
"target_field": "@timestamp",
"formats": [
"ISO8601",
"UNIX"
],
"timezone": "UTC",
"ignore_failure": true
}
},
{
"remove": {
"field": "agent",
"ignore_failure": true
}
},
{
"append": {
"field": "related.ip",
"value": [
"{{source.ip}}",
"{{destination.ip}}"
],
"allow_duplicates": false,
"ignore_failure": true
}
},
{
"script": {
"source": "boolean isPrivate(def ip) { if (ip == null) return false; int dot1 = ip.indexOf('.'); if (dot1 == -1) return false; int dot2 = ip.indexOf('.', dot1 + 1); if (dot2 == -1) return false; int first = Integer.parseInt(ip.substring(0, dot1)); if (first == 10) return true; if (first == 192 && ip.startsWith('168.', dot1 + 1)) return true; if (first == 172) { int second = Integer.parseInt(ip.substring(dot1 + 1, dot2)); return second >= 16 && second <= 31; } return false; } String[] fields = new String[] {\"source\", \"destination\"}; for (int i = 0; i < fields.length; i++) { def field = fields[i]; def ip = ctx[field]?.ip; if (ip != null) { if (ctx.network == null) ctx.network = new HashMap(); if (isPrivate(ip)) { if (ctx.network.private_ip == null) ctx.network.private_ip = new ArrayList(); if (!ctx.network.private_ip.contains(ip)) ctx.network.private_ip.add(ip); } else { if (ctx.network.public_ip == null) ctx.network.public_ip = new ArrayList(); if (!ctx.network.public_ip.contains(ip)) ctx.network.public_ip.add(ip); } } }",
"ignore_failure": false
}
},
{
"rename": {
"field": "message2.capture_file",
"target_field": "suricata.capture_file",
"ignore_missing": true
}
},
{
"pipeline": {
"if": "ctx?.event?.dataset != null",
"name": "suricata.{{event.dataset}}"
}
}
]
}

View File

@@ -1,21 +1,136 @@
{
"description" : "suricata.dns",
"processors" : [
{ "rename": { "field": "message2.proto", "target_field": "network.transport", "ignore_missing": true } },
{ "rename": { "field": "message2.app_proto", "target_field": "network.protocol", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.type", "target_field": "dns.query.type", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.tx_id", "target_field": "dns.id", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.version", "target_field": "dns.version", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.rrname", "target_field": "dns.query.name", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.rrtype", "target_field": "dns.query.type_name", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.flags", "target_field": "dns.flags", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.qr", "target_field": "dns.qr", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.rd", "target_field": "dns.recursion.desired", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.ra", "target_field": "dns.recursion.available", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.rcode", "target_field": "dns.response.code_name", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.grouped.A", "target_field": "dns.answers.data", "ignore_missing": true } },
{ "rename": { "field": "message2.dns.grouped.CNAME", "target_field": "dns.answers.name", "ignore_missing": true } },
{ "pipeline": { "if": "ctx.dns.query?.name != null && ctx.dns.query.name.contains('.')", "name": "dns.tld" } },
{ "pipeline": { "name": "common" } }
]
}
"description": "suricata.dns",
"processors": [
{
"rename": {
"field": "message2.proto",
"target_field": "network.transport",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.app_proto",
"target_field": "network.protocol",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.type",
"target_field": "dns.query.type",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.tx_id",
"target_field": "dns.tx_id",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.id",
"target_field": "dns.id",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.version",
"target_field": "dns.version",
"ignore_missing": true
}
},
{
"pipeline": {
"name": "suricata.dnsv3",
"ignore_missing_pipeline": true,
"if": "ctx?.dns?.version != null && ctx?.dns?.version == 3",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.dns.rrname",
"target_field": "dns.query.name",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.rrtype",
"target_field": "dns.query.type_name",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.flags",
"target_field": "dns.flags",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.qr",
"target_field": "dns.qr",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.rd",
"target_field": "dns.recursion.desired",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.ra",
"target_field": "dns.recursion.available",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.opcode",
"target_field": "dns.opcode",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.rcode",
"target_field": "dns.response.code_name",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.grouped.A",
"target_field": "dns.answers.data",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.dns.grouped.CNAME",
"target_field": "dns.answers.name",
"ignore_missing": true
}
},
{
"pipeline": {
"if": "ctx.dns.query?.name != null && ctx.dns.query.name.contains('.')",
"name": "dns.tld"
}
},
{
"pipeline": {
"name": "common"
}
}
]
}

View File

@@ -0,0 +1,56 @@
{
"processors": [
{
"rename": {
"field": "message2.dns.queries",
"target_field": "dns.queries",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx?.dns?.queries != null && ctx?.dns?.queries.length > 0) {\n if (ctx.dns == null) {\n ctx.dns = new HashMap();\n }\n if (ctx.dns.query == null) {\n ctx.dns.query = new HashMap();\n }\n ctx.dns.query.name = ctx?.dns?.queries[0].rrname;\n}"
}
},
{
"script": {
"source": "if (ctx?.dns?.queries != null && ctx?.dns?.queries.length > 0) {\n if (ctx.dns == null) {\n ctx.dns = new HashMap();\n }\n if (ctx.dns.query == null) {\n ctx.dns.query = new HashMap();\n }\n ctx.dns.query.type_name = ctx?.dns?.queries[0].rrtype;\n}"
}
},
{
"foreach": {
"field": "dns.queries",
"processor": {
"rename": {
"field": "_ingest._value.rrname",
"target_field": "_ingest._value.name",
"ignore_missing": true
}
},
"ignore_failure": true
}
},
{
"foreach": {
"field": "dns.queries",
"processor": {
"rename": {
"field": "_ingest._value.rrtype",
"target_field": "_ingest._value.type_name",
"ignore_missing": true
}
},
"ignore_failure": true
}
},
{
"pipeline": {
"name": "suricata.tld",
"ignore_missing_pipeline": true,
"if": "ctx?.dns?.queries != null && ctx?.dns?.queries.length > 0",
"ignore_failure": true
}
}
]
}

View File

@@ -0,0 +1,52 @@
{
"processors": [
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n if (q.name != null && q.name.contains('.')) {\n q.top_level_domain = q.name.substring(q.name.lastIndexOf('.') + 1);\n }\n }\n}",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n if (q.name != null && q.name.contains('.')) {\n q.query_without_tld = q.name.substring(0, q.name.lastIndexOf('.'));\n }\n }\n}",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n if (q.query_without_tld != null && q.query_without_tld.contains('.')) {\n q.parent_domain = q.query_without_tld.substring(q.query_without_tld.lastIndexOf('.') + 1);\n }\n }\n}",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n if (q.query_without_tld != null && q.query_without_tld.contains('.')) {\n q.subdomain = q.query_without_tld.substring(0, q.query_without_tld.lastIndexOf('.'));\n }\n }\n}",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n if (q.parent_domain != null && q.top_level_domain != null) {\n q.highest_registered_domain = q.parent_domain + \".\" + q.top_level_domain;\n }\n }\n}",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n if (q.subdomain != null) {\n q.subdomain_length = q.subdomain.length();\n }\n }\n}",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n if (q.parent_domain != null) {\n q.parent_domain_length = q.parent_domain.length();\n }\n }\n}",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns != null && ctx.dns.queries != null) {\n for (def q : ctx.dns.queries) {\n q.remove('query_without_tld');\n }\n}",
"ignore_failure": true
}
}
]
}

View File

@@ -0,0 +1,61 @@
{
"description": "zeek.analyzer",
"processors": [
{
"set": {
"field": "event.dataset",
"value": "analyzer"
}
},
{
"remove": {
"field": [
"host"
],
"ignore_failure": true
}
},
{
"json": {
"field": "message",
"target_field": "message2",
"ignore_failure": true
}
},
{
"set": {
"field": "network.protocol",
"copy_from": "message2.analyzer_name",
"ignore_empty_value": true,
"if": "ctx?.message2?.analyzer_kind == 'protocol'"
}
},
{
"set": {
"field": "network.protocol",
"ignore_empty_value": true,
"if": "ctx?.message2?.analyzer_kind != 'protocol'",
"copy_from": "message2.proto"
}
},
{
"lowercase": {
"field": "network.protocol",
"ignore_missing": true,
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.failure_reason",
"target_field": "error.reason",
"ignore_missing": true
}
},
{
"pipeline": {
"name": "zeek.common"
}
}
]
}

View File

@@ -1,32 +1,227 @@
{
"description" : "zeek.dns",
"processors" : [
{ "set": { "field": "event.dataset", "value": "dns" } },
{ "remove": { "field": ["host"], "ignore_failure": true } },
{ "json": { "field": "message", "target_field": "message2", "ignore_failure": true } },
{ "dot_expander": { "field": "id.orig_h", "path": "message2", "ignore_failure": true } },
{ "rename": { "field": "message2.proto", "target_field": "network.transport", "ignore_missing": true } },
{ "rename": { "field": "message2.trans_id", "target_field": "dns.id", "ignore_missing": true } },
{ "rename": { "field": "message2.rtt", "target_field": "event.duration", "ignore_missing": true } },
{ "rename": { "field": "message2.query", "target_field": "dns.query.name", "ignore_missing": true } },
{ "rename": { "field": "message2.qclass", "target_field": "dns.query.class", "ignore_missing": true } },
{ "rename": { "field": "message2.qclass_name", "target_field": "dns.query.class_name", "ignore_missing": true } },
{ "rename": { "field": "message2.qtype", "target_field": "dns.query.type", "ignore_missing": true } },
{ "rename": { "field": "message2.qtype_name", "target_field": "dns.query.type_name", "ignore_missing": true } },
{ "rename": { "field": "message2.rcode", "target_field": "dns.response.code", "ignore_missing": true } },
{ "rename": { "field": "message2.rcode_name", "target_field": "dns.response.code_name", "ignore_missing": true } },
{ "rename": { "field": "message2.AA", "target_field": "dns.authoritative", "ignore_missing": true } },
{ "rename": { "field": "message2.TC", "target_field": "dns.truncated", "ignore_missing": true } },
{ "rename": { "field": "message2.RD", "target_field": "dns.recursion.desired", "ignore_missing": true } },
{ "rename": { "field": "message2.RA", "target_field": "dns.recursion.available", "ignore_missing": true } },
{ "rename": { "field": "message2.Z", "target_field": "dns.reserved", "ignore_missing": true } },
{ "rename": { "field": "message2.answers", "target_field": "dns.answers.name", "ignore_missing": true } },
{ "script": { "lang": "painless", "if": "ctx.dns != null && ctx.dns.answers != null && ctx.dns.answers.name != null", "source": "def ips = []; for (item in ctx.dns.answers.name) { if (item =~ /^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$/ || item =~ /^([a-fA-F0-9:]+:+)+[a-fA-F0-9]+$/) { ips.add(item); } } ctx.dns.resolved_ip = ips;" } },
{ "rename": { "field": "message2.TTLs", "target_field": "dns.ttls", "ignore_missing": true } },
{ "rename": { "field": "message2.rejected", "target_field": "dns.query.rejected", "ignore_missing": true } },
{ "script": { "lang": "painless", "source": "ctx.dns.query.length = ctx.dns.query.name.length()", "ignore_failure": true } },
{ "set": { "if": "ctx._index == 'so-zeek'", "field": "_index", "value": "so-zeek_dns", "override": true } },
{ "pipeline": { "if": "ctx.dns?.query?.name != null && ctx.dns.query.name.contains('.')", "name": "dns.tld" } },
{ "pipeline": { "name": "zeek.common" } }
]
"description": "zeek.dns",
"processors": [
{
"set": {
"field": "event.dataset",
"value": "dns"
}
},
{
"remove": {
"field": [
"host"
],
"ignore_failure": true
}
},
{
"json": {
"field": "message",
"target_field": "message2",
"ignore_failure": true
}
},
{
"dot_expander": {
"field": "id.orig_h",
"path": "message2",
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.proto",
"target_field": "network.transport",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.trans_id",
"target_field": "dns.id",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.rtt",
"target_field": "event.duration",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.query",
"target_field": "dns.query.name",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.qclass",
"target_field": "dns.query.class",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.qclass_name",
"target_field": "dns.query.class_name",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.qtype",
"target_field": "dns.query.type",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.qtype_name",
"target_field": "dns.query.type_name",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.rcode",
"target_field": "dns.response.code",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.rcode_name",
"target_field": "dns.response.code_name",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.AA",
"target_field": "dns.authoritative",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.TC",
"target_field": "dns.truncated",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.RD",
"target_field": "dns.recursion.desired",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.RA",
"target_field": "dns.recursion.available",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.Z",
"target_field": "dns.reserved",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.answers",
"target_field": "dns.answers.name",
"ignore_missing": true
}
},
{
"foreach": {
"field": "dns.answers.name",
"processor": {
"pipeline": {
"name": "common.ip_validation"
}
},
"if": "ctx.dns != null && ctx.dns.answers != null && ctx.dns.answers.name != null",
"ignore_failure": true
}
},
{
"foreach": {
"field": "temp._valid_ips",
"processor": {
"append": {
"field": "dns.resolved_ip",
"allow_duplicates": false,
"value": "{{{_ingest._value}}}",
"ignore_failure": true
}
},
"if": "ctx.dns != null && ctx.dns.answers != null && ctx.dns.answers.name != null",
"ignore_failure": true
}
},
{
"script": {
"source": "if (ctx.dns.resolved_ip != null && ctx.dns.resolved_ip instanceof List) {\n ctx.dns.resolved_ip.removeIf(item -> item == null || item.toString().trim().isEmpty());\n }",
"ignore_failure": true
}
},
{
"remove": {
"field": [
"temp"
],
"ignore_missing": true,
"ignore_failure": true
}
},
{
"rename": {
"field": "message2.TTLs",
"target_field": "dns.ttls",
"ignore_missing": true
}
},
{
"rename": {
"field": "message2.rejected",
"target_field": "dns.query.rejected",
"ignore_missing": true
}
},
{
"script": {
"lang": "painless",
"source": "ctx.dns.query.length = ctx.dns.query.name.length()",
"ignore_failure": true
}
},
{
"set": {
"if": "ctx._index == 'so-zeek'",
"field": "_index",
"value": "so-zeek_dns",
"override": true
}
},
{
"pipeline": {
"if": "ctx.dns?.query?.name != null && ctx.dns.query.name.contains('.')",
"name": "dns.tld"
}
},
{
"pipeline": {
"name": "zeek.common"
}
}
]
}

View File

@@ -1,20 +0,0 @@
{
"description" : "zeek.dpd",
"processors" : [
{ "set": { "field": "event.dataset", "value": "dpd" } },
{ "remove": { "field": ["host"], "ignore_failure": true } },
{ "json": { "field": "message", "target_field": "message2", "ignore_failure": true } },
{ "dot_expander": { "field": "id.orig_h", "path": "message2", "ignore_failure": true } },
{ "rename": { "field": "message2.id.orig_h", "target_field": "source.ip", "ignore_missing": true } },
{ "dot_expander": { "field": "id.orig_p", "path": "message2", "ignore_failure": true } },
{ "rename": { "field": "message2.id.orig_p", "target_field": "source.port", "ignore_missing": true } },
{ "dot_expander": { "field": "id.resp_h", "path": "message2", "ignore_failure": true } },
{ "rename": { "field": "message2.id.resp_h", "target_field": "destination.ip", "ignore_missing": true } },
{ "dot_expander": { "field": "id.resp_p", "path": "message2", "ignore_failure": true } },
{ "rename": { "field": "message2.id.resp_p", "target_field": "destination.port", "ignore_missing": true } },
{ "rename": { "field": "message2.proto", "target_field": "network.protocol", "ignore_missing": true } },
{ "rename": { "field": "message2.analyzer", "target_field": "observer.analyzer", "ignore_missing": true } },
{ "rename": { "field": "message2.failure_reason", "target_field": "error.reason", "ignore_missing": true } },
{ "pipeline": { "name": "zeek.common" } }
]
}

View File

@@ -20,8 +20,28 @@ appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.action.type = Delete
appender.rolling.strategy.action.basepath = /var/log/elasticsearch
appender.rolling.strategy.action.condition.type = IfFileName
appender.rolling.strategy.action.condition.glob = *.gz
appender.rolling.strategy.action.condition.glob = *.log.gz
appender.rolling.strategy.action.condition.nested_condition.type = IfLastModified
appender.rolling.strategy.action.condition.nested_condition.age = 7D
appender.rolling_json.type = RollingFile
appender.rolling_json.name = rolling_json
appender.rolling_json.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.json
appender.rolling_json.layout.type = ECSJsonLayout
appender.rolling_json.layout.dataset = elasticsearch.server
appender.rolling_json.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}.json.gz
appender.rolling_json.policies.type = Policies
appender.rolling_json.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling_json.policies.time.interval = 1
appender.rolling_json.policies.time.modulate = true
appender.rolling_json.strategy.type = DefaultRolloverStrategy
appender.rolling_json.strategy.action.type = Delete
appender.rolling_json.strategy.action.basepath = /var/log/elasticsearch
appender.rolling_json.strategy.action.condition.type = IfFileName
appender.rolling_json.strategy.action.condition.glob = *.json.gz
appender.rolling_json.strategy.action.condition.nested_condition.type = IfLastModified
appender.rolling_json.strategy.action.condition.nested_condition.age = 1D
rootLogger.level = info
rootLogger.appenderRef.rolling.ref = rolling
rootLogger.appenderRef.rolling_json.ref = rolling_json

View File

@@ -131,6 +131,47 @@ elasticsearch:
description: Maximum primary shard size. Once an index reaches this limit, it will be rolled over into a new index.
global: True
helpLink: elasticsearch.html
shrink:
method:
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
options:
- COUNT
- SIZE
global: True
advanced: True
forcedType: string
number_of_shards:
title: shard count
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
global: True
forcedType: int
advanced: True
max_primary_shard_size:
title: max shard size
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
regex: ^[0-9]+(?:gb|tb|pb)$
global: True
forcedType: string
advanced: True
allow_write_after_shrink:
description: Allow writes after shrink.
global: True
forcedType: bool
default: False
advanced: True
forcemerge:
max_num_segments:
description: Reduce the number of segments in each index shard and clean up deleted documents.
global: True
forcedType: int
advanced: True
index_codec:
title: compression
description: Use higher compression for stored fields at the cost of slower performance.
forcedType: bool
global: True
default: False
advanced: True
cold:
min_age:
description: Minimum age of index. ex. 60d - This determines when the index should be moved to the cold tier. While still searchable, this tier is typically optimized for lower storage costs rather than search speed. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and cold min_age set to 60 then there will be 30 days from index creation to rollover and then an additional 60 days before moving to cold tier.
@@ -144,6 +185,12 @@ elasticsearch:
description: Used for index recovery after a node restart. Indices with higher priorities are recovered before indices with lower priorities.
global: True
helpLink: elasticsearch.html
allocate:
number_of_replicas:
description: Set the number of replicas. Remains the same as the previous phase by default.
forcedType: int
global: True
advanced: True
warm:
min_age:
description: Minimum age of index. ex. 30d - This determines when the index should be moved to the warm tier. Nodes in the warm tier generally dont need to be as fast as those in the hot tier. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and warm min_age set to 30 then there will be 30 days from index creation to rollover and then an additional 30 days before moving to warm tier.
@@ -158,6 +205,52 @@ elasticsearch:
forcedType: int
global: True
helpLink: elasticsearch.html
shrink:
method:
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
options:
- COUNT
- SIZE
global: True
advanced: True
number_of_shards:
title: shard count
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
global: True
forcedType: int
advanced: True
max_primary_shard_size:
title: max shard size
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
regex: ^[0-9]+(?:gb|tb|pb)$
global: True
forcedType: string
advanced: True
allow_write_after_shrink:
description: Allow writes after shrink.
global: True
forcedType: bool
default: False
advanced: True
forcemerge:
max_num_segments:
description: Reduce the number of segments in each index shard and clean up deleted documents.
global: True
forcedType: int
advanced: True
index_codec:
title: compression
description: Use higher compression for stored fields at the cost of slower performance.
forcedType: bool
global: True
default: False
advanced: True
allocate:
number_of_replicas:
description: Set the number of replicas. Remains the same as the previous phase by default.
forcedType: int
global: True
advanced: True
delete:
min_age:
description: Minimum age of index. ex. 90d - This determines when the index should be deleted. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and delete min_age set to 90 then there will be 30 days from index creation to rollover and then an additional 90 days before deletion.
@@ -287,6 +380,47 @@ elasticsearch:
global: True
advanced: True
helpLink: elasticsearch.html
shrink:
method:
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
options:
- COUNT
- SIZE
global: True
advanced: True
forcedType: string
number_of_shards:
title: shard count
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
global: True
forcedType: int
advanced: True
max_primary_shard_size:
title: max shard size
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
regex: ^[0-9]+(?:gb|tb|pb)$
global: True
forcedType: string
advanced: True
allow_write_after_shrink:
description: Allow writes after shrink.
global: True
forcedType: bool
default: False
advanced: True
forcemerge:
max_num_segments:
description: Reduce the number of segments in each index shard and clean up deleted documents.
global: True
forcedType: int
advanced: True
index_codec:
title: compression
description: Use higher compression for stored fields at the cost of slower performance.
forcedType: bool
global: True
default: False
advanced: True
warm:
min_age:
description: Minimum age of index. ex. 30d - This determines when the index should be moved to the warm tier. Nodes in the warm tier generally dont need to be as fast as those in the hot tier. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and warm min_age set to 30 then there will be 30 days from index creation to rollover and then an additional 30 days before moving to warm tier.
@@ -314,6 +448,52 @@ elasticsearch:
global: True
advanced: True
helpLink: elasticsearch.html
shrink:
method:
description: Shrink the index to a new index with fewer primary shards. Shrink operation is by count or size.
options:
- COUNT
- SIZE
global: True
advanced: True
number_of_shards:
title: shard count
description: Desired shard count. Note that this value is only used when the shrink method selected is 'COUNT'.
global: True
forcedType: int
advanced: True
max_primary_shard_size:
title: max shard size
description: Desired shard size in gb/tb/pb eg. 100gb. Note that this value is only used when the shrink method selected is 'SIZE'.
regex: ^[0-9]+(?:gb|tb|pb)$
global: True
forcedType: string
advanced: True
allow_write_after_shrink:
description: Allow writes after shrink.
global: True
forcedType: bool
default: False
advanced: True
forcemerge:
max_num_segments:
description: Reduce the number of segments in each index shard and clean up deleted documents.
global: True
forcedType: int
advanced: True
index_codec:
title: compression
description: Use higher compression for stored fields at the cost of slower performance.
forcedType: bool
global: True
default: False
advanced: True
allocate:
number_of_replicas:
description: Set the number of replicas. Remains the same as the previous phase by default.
forcedType: int
global: True
advanced: True
cold:
min_age:
description: Minimum age of index. ex. 60d - This determines when the index should be moved to the cold tier. While still searchable, this tier is typically optimized for lower storage costs rather than search speed. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and cold min_age set to 60 then there will be 30 days from index creation to rollover and then an additional 60 days before moving to cold tier.
@@ -330,6 +510,12 @@ elasticsearch:
global: True
advanced: True
helpLink: elasticsearch.html
allocate:
number_of_replicas:
description: Set the number of replicas. Remains the same as the previous phase by default.
forcedType: int
global: True
advanced: True
delete:
min_age:
description: Minimum age of index. ex. 90d - This determines when the index should be deleted. Its important to note that this is calculated relative to the rollover date (NOT the original creation date of the index). For example, if you have an index that is set to rollover after 30 days and delete min_age set to 90 then there will be 30 days from index creation to rollover and then an additional 90 days before deletion.
@@ -392,6 +578,7 @@ elasticsearch:
so-logs-elastic_agent_x_metricbeat: *indexSettings
so-logs-elastic_agent_x_osquerybeat: *indexSettings
so-logs-elastic_agent_x_packetbeat: *indexSettings
so-logs-elasticsearch_x_server: *indexSettings
so-metrics-endpoint_x_metadata: *indexSettings
so-metrics-endpoint_x_metrics: *indexSettings
so-metrics-endpoint_x_policy: *indexSettings

View File

@@ -61,5 +61,55 @@
{% do settings.index_template.template.settings.index.pop('sort') %}
{% endif %}
{% endif %}
{# advanced ilm actions #}
{% if settings.policy is defined and settings.policy.phases is defined %}
{% set PHASE_NAMES = ["hot", "warm", "cold"] %}
{% for P in PHASE_NAMES %}
{% if settings.policy.phases[P] is defined and settings.policy.phases[P].actions is defined %}
{% set PHASE = settings.policy.phases[P].actions %}
{# remove allocate action if number_of_replicas isn't configured #}
{% if PHASE.allocate is defined %}
{% if PHASE.allocate.number_of_replicas is not defined or PHASE.allocate.number_of_replicas == "" %}
{% do PHASE.pop('allocate', none) %}
{% endif %}
{% endif %}
{# start shrink action #}
{% if PHASE.shrink is defined %}
{% if PHASE.shrink.method is defined %}
{% if PHASE.shrink.method == 'COUNT' and PHASE.shrink.number_of_shards is defined and PHASE.shrink.number_of_shards %}
{# remove max_primary_shard_size value when doing shrink operation by count vs size #}
{% do PHASE.shrink.pop('max_primary_shard_size', none) %}
{% elif PHASE.shrink.method == 'SIZE' and PHASE.shrink.max_primary_shard_size is defined and PHASE.shrink.max_primary_shard_size %}
{# remove number_of_shards value when doing shrink operation by size vs count #}
{% do PHASE.shrink.pop('number_of_shards', none) %}
{% else %}
{# method isn't defined or missing a required config number_of_shards/max_primary_shard_size #}
{% do PHASE.pop('shrink', none) %}
{% endif %}
{% endif %}
{% endif %}
{# always remove shrink method since its only used for SOC config, not in the actual ilm policy #}
{% if PHASE.shrink is defined %}
{% do PHASE.shrink.pop('method', none) %}
{% endif %}
{# end shrink action #}
{# start force merge #}
{% if PHASE.forcemerge is defined %}
{% if PHASE.forcemerge.index_codec is defined and PHASE.forcemerge.index_codec %}
{% do PHASE.forcemerge.update({'index_codec': 'best_compression'}) %}
{% else %}
{% do PHASE.forcemerge.pop('index_codec', none) %}
{% endif %}
{% if PHASE.forcemerge.max_num_segments is not defined or not PHASE.forcemerge.max_num_segments %}
{# max_num_segments is empty, drop it #}
{% do PHASE.pop('forcemerge', none) %}
{% endif %}
{% endif %}
{# end force merge #}
{% endif %}
{% endfor %}
{% endif %}
{% do ES_INDEX_SETTINGS.update({index | replace("_x_", "."): ES_INDEX_SETTINGS_GLOBAL_OVERRIDES[index]}) %}
{% endfor %}

View File

@@ -841,6 +841,10 @@
"type": "long"
}
}
},
"capture_file": {
"type": "keyword",
"ignore_above": 1024
}
}
}

View File

@@ -0,0 +1,43 @@
{
"template": {
"mappings": {
"properties": {
"agent": {
"type": "object",
"properties": {
"hostname": {
"ignore_above": 1024,
"type": "keyword"
},
"id": {
"ignore_above": 1024,
"type": "keyword"
},
"last_checkin_status": {
"ignore_above": 1024,
"type": "keyword"
},
"last_checkin": {
"type": "date"
},
"name": {
"ignore_above": 1024,
"type": "keyword"
},
"offline_duration_hours": {
"type": "integer"
},
"policy_id": {
"ignore_above": 1024,
"type": "keyword"
},
"status": {
"ignore_above": 1024,
"type": "keyword"
}
}
}
}
}
}
}

View File

@@ -0,0 +1,104 @@
{
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"so_kind": {
"ignore_above": 1024,
"type": "keyword"
},
"so_operation": {
"ignore_above": 1024,
"type": "keyword"
},
"so_chat": {
"properties": {
"role": {
"ignore_above": 1024,
"type": "keyword"
},
"content": {
"type": "object",
"enabled": false
},
"sessionId": {
"ignore_above": 1024,
"type": "keyword"
},
"createTime": {
"type": "date"
},
"deletedAt": {
"type": "date"
},
"tags": {
"ignore_above": 1024,
"type": "keyword"
},
"tool_use_id": {
"ignore_above": 1024,
"type": "keyword"
},
"userId": {
"ignore_above": 1024,
"type": "keyword"
},
"message": {
"properties": {
"id": {
"ignore_above": 1024,
"type": "keyword"
},
"type": {
"ignore_above": 1024,
"type": "keyword"
},
"role": {
"ignore_above": 1024,
"type": "keyword"
},
"model": {
"ignore_above": 1024,
"type": "keyword"
},
"contentStr": {
"type": "text"
},
"contentBlocks": {
"type": "nested",
"enabled": false
},
"stopReason": {
"ignore_above": 1024,
"type": "keyword"
},
"stopSequence": {
"ignore_above": 1024,
"type": "keyword"
},
"usage": {
"properties": {
"input_tokens": {
"type": "long"
},
"output_tokens": {
"type": "long"
},
"credits": {
"type": "long"
}
}
}
}
}
}
}
}
}
},
"_meta": {
"ecs_version": "1.12.2"
}
}

View File

@@ -0,0 +1,7 @@
{
"template": {},
"version": 1,
"_meta": {
"description": "default settings for common Security Onion Assistant indices"
}
}

View File

@@ -0,0 +1,44 @@
{
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"so_kind": {
"ignore_above": 1024,
"type": "keyword"
},
"so_session": {
"properties": {
"title": {
"ignore_above": 1024,
"type": "keyword"
},
"sessionId": {
"ignore_above": 1024,
"type": "keyword"
},
"createTime": {
"type": "date"
},
"deleteTime": {
"type": "date"
},
"tags": {
"ignore_above": 1024,
"type": "keyword"
},
"userId": {
"ignore_above": 1024,
"type": "keyword"
}
}
}
}
}
},
"_meta": {
"ecs_version": "1.12.2"
}
}

View File

@@ -0,0 +1,7 @@
{
"template": {},
"version": 1,
"_meta": {
"description": "default settings for common Security Onion Assistant indices"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -909,6 +909,15 @@ firewall:
- elastic_agent_control
- elastic_agent_data
- elastic_agent_update
hypervisor:
portgroups:
- yum
- docker_registry
- influxdb
- elastic_agent_control
- elastic_agent_data
- elastic_agent_update
- sensoroni
customhostgroup0:
portgroups: []
customhostgroup1:
@@ -961,6 +970,9 @@ firewall:
desktop:
portgroups:
- salt_manager
hypervisor:
portgroups:
- salt_manager
self:
portgroups:
- syslog
@@ -1113,6 +1125,15 @@ firewall:
- elastic_agent_control
- elastic_agent_data
- elastic_agent_update
hypervisor:
portgroups:
- yum
- docker_registry
- influxdb
- elastic_agent_control
- elastic_agent_data
- elastic_agent_update
- sensoroni
customhostgroup0:
portgroups: []
customhostgroup1:
@@ -1168,6 +1189,9 @@ firewall:
desktop:
portgroups:
- salt_manager
hypervisor:
portgroups:
- salt_manager
self:
portgroups:
- syslog
@@ -1206,6 +1230,10 @@ firewall:
portgroups:
- elasticsearch_node
- elasticsearch_rest
managerhype:
portgroups:
- elasticsearch_node
- elasticsearch_rest
standalone:
portgroups:
- elasticsearch_node
@@ -1353,6 +1381,10 @@ firewall:
portgroups:
- elasticsearch_node
- elasticsearch_rest
managerhype:
portgroups:
- elasticsearch_node
- elasticsearch_rest
standalone:
portgroups:
- elasticsearch_node
@@ -1555,6 +1587,9 @@ firewall:
portgroups:
- redis
- elastic_agent_data
managerhype:
portgroups:
- elastic_agent_data
self:
portgroups:
- redis
@@ -1672,6 +1707,9 @@ firewall:
managersearch:
portgroups:
- openssh
managerhype:
portgroups:
- openssh
standalone:
portgroups:
- openssh
@@ -1734,6 +1772,8 @@ firewall:
portgroups: []
managersearch:
portgroups: []
managerhype:
portgroups: []
standalone:
portgroups: []
customhostgroup0:

View File

@@ -91,7 +91,7 @@ COMMIT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p icmp -j ACCEPT
-A INPUT -j LOGGING
{% if GLOBALS.role in ['so-hypervisor', 'so-managerhyper'] -%}
{% if GLOBALS.role in ['so-hypervisor', 'so-managerhype'] -%}
-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i br0 -o br0 -j ACCEPT
{%- endif %}

View File

@@ -25,7 +25,7 @@
{% set KAFKA_EXTERNAL_ACCESS = salt['pillar.get']('kafka:config:external_access:enabled', default=False) %}
{% set kafka_node_type = salt['pillar.get']('kafka:nodes:'+ GLOBALS.hostname + ':role') %}
{% if role in ['manager', 'managersearch', 'standalone'] %}
{% if role.startswith('manager') or role == 'standalone' %}
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[role].portgroups.append('kafka_controller') %}
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.receiver.portgroups.append('kafka_controller') %}
{% endif %}
@@ -38,8 +38,8 @@
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.receiver.portgroups.append('kafka_controller') %}
{% endif %}
{% if role in ['manager', 'managersearch', 'standalone', 'receiver'] %}
{% for r in ['manager', 'managersearch', 'standalone', 'receiver', 'fleet', 'idh', 'sensor', 'searchnode','heavynode', 'elastic_agent_endpoint', 'desktop'] %}
{% if role.startswith('manager') or role in ['standalone', 'receiver'] %}
{% for r in ['manager', 'managersearch', 'managerhype', 'standalone', 'receiver', 'fleet', 'idh', 'sensor', 'searchnode','heavynode', 'elastic_agent_endpoint', 'desktop'] %}
{% if FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[r] is defined %}
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups[r].portgroups.append('kafka_data') %}
{% endif %}
@@ -48,11 +48,11 @@
{% if KAFKA_EXTERNAL_ACCESS %}
{# Kafka external access only applies for Kafka nodes with the broker role. #}
{% if role in ['manager', 'managersearch', 'standalone', 'receiver'] and 'broker' in kafka_node_type %}
{% if role.startswith('manager') or role in ['standalone', 'receiver'] and 'broker' in kafka_node_type %}
{% do FIREWALL_DEFAULT.firewall.role[role].chain["DOCKER-USER"].hostgroups.external_kafka.portgroups.append('kafka_external_access') %}
{% endif %}
{% endif %}
{% endif %}
{% set FIREWALL_MERGED = salt['pillar.get']('firewall', FIREWALL_DEFAULT.firewall, merge=True) %}
{% set FIREWALL_MERGED = salt['pillar.get']('firewall', FIREWALL_DEFAULT.firewall, merge=True) %}

View File

@@ -13,6 +13,7 @@
{# Import defaults.yaml for model hardware capabilities #}
{% import_yaml 'hypervisor/defaults.yaml' as DEFAULTS %}
{% set HYPERVISORMERGED = salt['pillar.get']('hypervisor', default=DEFAULTS.hypervisor, merge=True) %}
{# Get hypervisor nodes from pillar #}
{% set NODES = salt['pillar.get']('hypervisor:nodes', {}) %}
@@ -30,9 +31,10 @@
{% set model = '' %}
{% if grains %}
{% set minion_id = grains.keys() | first %}
{% set model = grains[minion_id].get('sosmodel', '') %}
{% set model = grains[minion_id].get('sosmodel', grains[minion_id].get('byodmodel', '')) %}
{% endif %}
{% set model_config = DEFAULTS.hypervisor.model.get(model, {}) %}
{% set model_config = HYPERVISORMERGED.model.get(model, {}) %}
{# Get VM list from VMs file #}
{% set vms = {} %}
@@ -56,10 +58,26 @@
{% set role = vm.get('role', '') %}
{% do salt.log.debug('salt/hypervisor/map.jinja: Processing VM - hostname: ' ~ hostname ~ ', role: ' ~ role) %}
{# Load VM configuration from config file #}
{# Try to load VM configuration from config file first, then .error file if config doesn't exist #}
{% set vm_file = 'hypervisor/hosts/' ~ hypervisor ~ '/' ~ hostname ~ '_' ~ role %}
{% set vm_error_file = vm_file ~ '.error' %}
{% do salt.log.debug('salt/hypervisor/map.jinja: VM config file: ' ~ vm_file) %}
{% import_json vm_file as vm_state %}
{# Check if base config file exists #}
{% set config_exists = salt['file.file_exists']('/opt/so/saltstack/local/salt/' ~ vm_file) %}
{% set error_exists = salt['file.file_exists']('/opt/so/saltstack/local/salt/' ~ vm_error_file) %}
{% set vm_state = none %}
{% if config_exists %}
{% import_json vm_file as vm_state %}
{% do salt.log.debug('salt/hypervisor/map.jinja: Loaded VM config from base file') %}
{% elif error_exists %}
{% import_json vm_error_file as vm_state %}
{% do salt.log.debug('salt/hypervisor/map.jinja: Loaded VM config from .error file') %}
{% else %}
{% do salt.log.warning('salt/hypervisor/map.jinja: No config or error file found for VM ' ~ hostname ~ '_' ~ role) %}
{% endif %}
{% if vm_state %}
{% do salt.log.debug('salt/hypervisor/map.jinja: VM config content: ' ~ vm_state | tojson) %}
{% set vm_data = {'config': vm_state.config} %}
@@ -83,7 +101,7 @@
{% endif %}
{% do vms.update({hostname ~ '_' ~ role: vm_data}) %}
{% else %}
{% do salt.log.debug('salt/hypervisor/map.jinja: Config file empty: ' ~ vm_file) %}
{% do salt.log.debug('salt/hypervisor/map.jinja: Skipping VM ' ~ hostname ~ '_' ~ role ~ ' - no config available') %}
{% endif %}
{% endfor %}

View File

@@ -30,7 +30,9 @@
#
# WARNING: This script will DESTROY all data on the target drives!
#
# USAGE: sudo ./so-nvme-raid1.sh
# USAGE:
# sudo ./so-nvme-raid1.sh # Normal operation
# sudo ./so-nvme-raid1.sh --force-cleanup # Force cleanup of existing RAID
#
#################################################################
@@ -41,6 +43,19 @@ set -e
RAID_ARRAY_NAME="md0"
RAID_DEVICE="/dev/${RAID_ARRAY_NAME}"
MOUNT_POINT="/nsm"
FORCE_CLEANUP=false
# Parse command line arguments
for arg in "$@"; do
case $arg in
--force-cleanup)
FORCE_CLEANUP=true
shift
;;
*)
;;
esac
done
# Function to log messages
log() {
@@ -55,6 +70,91 @@ check_root() {
fi
}
# Function to force cleanup all RAID components
force_cleanup_raid() {
log "=== FORCE CLEANUP MODE ==="
log "This will destroy all RAID configurations and data on target drives!"
# Stop all MD arrays
log "Stopping all MD arrays"
mdadm --stop --scan 2>/dev/null || true
# Wait for arrays to stop
sleep 2
# Remove any running md devices
for md in /dev/md*; do
if [ -b "$md" ]; then
log "Stopping $md"
mdadm --stop "$md" 2>/dev/null || true
fi
done
# Force cleanup both NVMe drives
for device in "/dev/nvme0n1" "/dev/nvme1n1"; do
log "Force cleaning $device"
# Kill any processes using the device
fuser -k "${device}"* 2>/dev/null || true
# Unmount any mounted partitions
for part in "${device}"*; do
if [ -b "$part" ]; then
umount -f "$part" 2>/dev/null || true
fi
done
# Force zero RAID superblocks on partitions
for part in "${device}"p*; do
if [ -b "$part" ]; then
log "Zeroing RAID superblock on $part"
mdadm --zero-superblock --force "$part" 2>/dev/null || true
fi
done
# Zero superblock on the device itself
log "Zeroing RAID superblock on $device"
mdadm --zero-superblock --force "$device" 2>/dev/null || true
# Remove LVM physical volumes
pvremove -ff -y "$device" 2>/dev/null || true
# Wipe all filesystem and partition signatures
log "Wiping all signatures from $device"
wipefs -af "$device" 2>/dev/null || true
# Overwrite the beginning of the drive (partition table area)
log "Clearing partition table on $device"
dd if=/dev/zero of="$device" bs=1M count=10 2>/dev/null || true
# Clear the end of the drive (backup partition table area)
local device_size=$(blockdev --getsz "$device" 2>/dev/null || echo "0")
if [ "$device_size" -gt 0 ]; then
dd if=/dev/zero of="$device" bs=512 seek=$(( device_size - 2048 )) count=2048 2>/dev/null || true
fi
# Force kernel to re-read partition table
blockdev --rereadpt "$device" 2>/dev/null || true
partprobe -s "$device" 2>/dev/null || true
done
# Clear mdadm configuration
log "Clearing mdadm configuration"
echo "DEVICE partitions" > /etc/mdadm.conf
# Remove any fstab entries for the RAID device or mount point
log "Cleaning fstab entries"
sed -i "\|${RAID_DEVICE}|d" /etc/fstab
sed -i "\|${MOUNT_POINT}|d" /etc/fstab
# Wait for system to settle
udevadm settle
sleep 5
log "Force cleanup complete!"
log "Proceeding with RAID setup..."
}
# Function to find MD arrays using specific devices
find_md_arrays_using_devices() {
local target_devices=("$@")
@@ -205,10 +305,15 @@ check_existing_raid() {
fi
log "Error: $device appears to be part of an existing RAID array"
log "To reuse this device, you must first:"
log "1. Unmount any filesystems"
log "2. Stop the RAID array: mdadm --stop $array_name"
log "3. Zero the superblock: mdadm --zero-superblock ${device}p1"
log "Old RAID metadata detected but array is not running."
log ""
log "To fix this, run the script with --force-cleanup:"
log " sudo $0 --force-cleanup"
log ""
log "Or manually clean up with:"
log "1. Stop any arrays: mdadm --stop --scan"
log "2. Zero superblocks: mdadm --zero-superblock --force ${device}p1"
log "3. Wipe signatures: wipefs -af $device"
exit 1
fi
done
@@ -238,7 +343,7 @@ ensure_devices_free() {
done
# Clear MD superblock
mdadm --zero-superblock "${device}"* 2>/dev/null || true
mdadm --zero-superblock --force "${device}"* 2>/dev/null || true
# Remove LVM PV if exists
pvremove -ff -y "$device" 2>/dev/null || true
@@ -263,6 +368,11 @@ main() {
# Check if running as root
check_root
# If force cleanup flag is set, do aggressive cleanup first
if [ "$FORCE_CLEANUP" = true ]; then
force_cleanup_raid
fi
# Check for existing RAID setup
check_existing_raid

View File

@@ -0,0 +1,591 @@
#!/usr/bin/python3
# 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."
{% if 'vrt' in salt['pillar.get']('features', []) %}
"""
Script for creating and attaching virtual volumes to KVM virtual machines for NSM storage.
This script provides functionality to create pre-allocated raw disk images and attach them
to VMs as virtio-blk devices for high-performance network security monitoring data storage.
The script handles the complete volume lifecycle:
1. Volume Creation: Creates pre-allocated raw disk images using qemu-img
2. Volume Attachment: Attaches volumes to VMs as virtio-blk devices
3. VM Management: Stops/starts VMs as needed during the process
This script is designed to work with Security Onion's virtualization infrastructure and is typically
used during VM provisioning to add dedicated NSM storage volumes.
**Usage:**
so-kvm-create-volume -v <vm_name> -s <size_gb> [-S]
**Options:**
-v, --vm Name of the virtual machine to attach the volume to (required).
-s, --size Size of the volume in GB (required, must be a positive integer).
-S, --start Start the VM after volume creation and attachment (optional).
**Examples:**
1. **Create and Attach 500GB Volume:**
```bash
so-kvm-create-volume -v vm1_sensor -s 500
```
This command creates and attaches a volume with the following settings:
- VM Name: `vm1_sensor`
- Volume Size: `500` GB
- Volume Path: `/nsm/libvirt/volumes/vm1_sensor-nsm-<epoch_timestamp>.img`
- Device: `/dev/vdb` (virtio-blk)
- VM remains stopped after attachment
2. **Create Volume and Start VM:**
```bash
so-kvm-create-volume -v vm2_sensor -s 1000 -S
```
This command creates a volume and starts the VM:
- VM Name: `vm2_sensor`
- Volume Size: `1000` GB (1 TB)
- VM is started after volume attachment due to the `-S` flag
3. **Create Large Volume for Heavy Traffic:**
```bash
so-kvm-create-volume -v vm3_sensor -s 2000 -S
```
This command creates a large volume for high-traffic environments:
- VM Name: `vm3_sensor`
- Volume Size: `2000` GB (2 TB)
- VM is started after attachment
**Notes:**
- The script automatically stops the VM if it's running before creating and attaching the volume.
- Volumes are created with full pre-allocation for optimal performance.
- Volume files are stored in `/nsm/libvirt/volumes/` with naming pattern `<vm_name>-nsm-<epoch_timestamp>.img`.
- The epoch timestamp ensures unique volume names and prevents conflicts.
- Volumes are attached as `/dev/vdb` using virtio-blk for high performance.
- The script checks available disk space before creating the volume.
- Ownership is set to `qemu:qemu` with permissions `640`.
- Without the `-S` flag, the VM remains stopped after volume attachment.
**Description:**
The `so-kvm-create-volume` script creates and attaches NSM storage volumes using the following process:
1. **Pre-flight Checks:**
- Validates input parameters (VM name, size)
- Checks available disk space in `/nsm/libvirt/volumes/`
- Ensures sufficient space for the requested volume size
2. **VM State Management:**
- Connects to the local libvirt daemon
- Stops the VM if it's currently running
- Retrieves current VM configuration
3. **Volume Creation:**
- Creates volume directory if it doesn't exist
- Uses `qemu-img create` with full pre-allocation
- Sets proper ownership (qemu:qemu) and permissions (640)
- Validates volume creation success
4. **Volume Attachment:**
- Modifies VM's libvirt XML configuration
- Adds disk element with virtio-blk driver
- Configures cache='none' and io='native' for performance
- Attaches volume as `/dev/vdb`
5. **VM Redefinition:**
- Applies the new configuration by redefining the VM
- Optionally starts the VM if requested
- Emits deployment status events for monitoring
6. **Error Handling:**
- Validates all input parameters
- Checks disk space before creation
- Handles volume creation failures
- Handles volume attachment failures
- Provides detailed error messages for troubleshooting
**Exit Codes:**
- `0`: Success
- `1`: An error occurred during execution
**Logging:**
- Logs are written to `/opt/so/log/hypervisor/so-kvm-create-volume.log`
- Both file and console logging are enabled for real-time monitoring
- Log entries include timestamps and severity levels
- Log prefixes: VOLUME:, VM:, HARDWARE:, SPACE:
- Detailed error messages are logged for troubleshooting
"""
import argparse
import sys
import os
import libvirt
import logging
import socket
import subprocess
import pwd
import grp
import time
import xml.etree.ElementTree as ET
from io import StringIO
from so_vm_utils import start_vm, stop_vm
from so_logging_utils import setup_logging
# Get hypervisor name from local hostname
HYPERVISOR = socket.gethostname()
# Volume storage directory
VOLUME_DIR = '/nsm/libvirt/volumes'
# Custom exception classes
class InsufficientSpaceError(Exception):
"""Raised when there is insufficient disk space for volume creation."""
pass
class VolumeCreationError(Exception):
"""Raised when volume creation fails."""
pass
class VolumeAttachmentError(Exception):
"""Raised when volume attachment fails."""
pass
# Custom log handler to capture output
class StringIOHandler(logging.Handler):
def __init__(self):
super().__init__()
self.strio = StringIO()
def emit(self, record):
msg = self.format(record)
self.strio.write(msg + '\n')
def get_value(self):
return self.strio.getvalue()
def parse_arguments():
"""Parse command-line arguments."""
parser = argparse.ArgumentParser(description='Create and attach a virtual volume to a KVM virtual machine for NSM storage.')
parser.add_argument('-v', '--vm', required=True, help='Name of the virtual machine to attach the volume to.')
parser.add_argument('-s', '--size', type=int, required=True, help='Size of the volume in GB (must be a positive integer).')
parser.add_argument('-S', '--start', action='store_true', help='Start the VM after volume creation and attachment.')
args = parser.parse_args()
# Validate size is positive
if args.size <= 0:
parser.error("Volume size must be a positive integer.")
return args
def check_disk_space(size_gb, logger):
"""
Check if there is sufficient disk space available for volume creation.
Args:
size_gb: Size of the volume in GB
logger: Logger instance
Raises:
InsufficientSpaceError: If there is not enough disk space
"""
try:
stat = os.statvfs(VOLUME_DIR)
# Available space in bytes
available_bytes = stat.f_bavail * stat.f_frsize
# Required space in bytes (add 10% buffer)
required_bytes = size_gb * 1024 * 1024 * 1024 * 1.1
available_gb = available_bytes / (1024 * 1024 * 1024)
required_gb = required_bytes / (1024 * 1024 * 1024)
logger.info(f"SPACE: Available: {available_gb:.2f} GB, Required: {required_gb:.2f} GB")
if available_bytes < required_bytes:
raise InsufficientSpaceError(
f"Insufficient disk space. Available: {available_gb:.2f} GB, Required: {required_gb:.2f} GB"
)
logger.info(f"SPACE: Sufficient disk space available for {size_gb} GB volume")
except OSError as e:
logger.error(f"SPACE: Failed to check disk space: {e}")
raise
def create_volume_file(vm_name, size_gb, logger):
"""
Create a pre-allocated raw disk image for the VM.
Args:
vm_name: Name of the VM
size_gb: Size of the volume in GB
logger: Logger instance
Returns:
Path to the created volume file
Raises:
VolumeCreationError: If volume creation fails
"""
# Generate epoch timestamp for unique volume naming
epoch_timestamp = int(time.time())
# Define volume path with epoch timestamp for uniqueness
volume_path = os.path.join(VOLUME_DIR, f"{vm_name}-nsm-{epoch_timestamp}.img")
# Check if volume already exists (shouldn't be possible with timestamp)
if os.path.exists(volume_path):
logger.error(f"VOLUME: Volume already exists: {volume_path}")
raise VolumeCreationError(f"Volume already exists: {volume_path}")
logger.info(f"VOLUME: Creating {size_gb} GB volume at {volume_path}")
# Create volume using qemu-img with full pre-allocation
try:
cmd = [
'qemu-img', 'create',
'-f', 'raw',
'-o', 'preallocation=full',
volume_path,
f"{size_gb}G"
]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
logger.info(f"VOLUME: Volume created successfully")
if result.stdout:
logger.debug(f"VOLUME: qemu-img output: {result.stdout.strip()}")
except subprocess.CalledProcessError as e:
logger.error(f"VOLUME: Failed to create volume: {e}")
if e.stderr:
logger.error(f"VOLUME: qemu-img error: {e.stderr.strip()}")
raise VolumeCreationError(f"Failed to create volume: {e}")
# Set ownership to qemu:qemu
try:
qemu_uid = pwd.getpwnam('qemu').pw_uid
qemu_gid = grp.getgrnam('qemu').gr_gid
os.chown(volume_path, qemu_uid, qemu_gid)
logger.info(f"VOLUME: Set ownership to qemu:qemu")
except (KeyError, OSError) as e:
logger.error(f"VOLUME: Failed to set ownership: {e}")
raise VolumeCreationError(f"Failed to set ownership: {e}")
# Set permissions to 640
try:
os.chmod(volume_path, 0o640)
logger.info(f"VOLUME: Set permissions to 640")
except OSError as e:
logger.error(f"VOLUME: Failed to set permissions: {e}")
raise VolumeCreationError(f"Failed to set permissions: {e}")
# Verify volume was created
if not os.path.exists(volume_path):
logger.error(f"VOLUME: Volume file not found after creation: {volume_path}")
raise VolumeCreationError(f"Volume file not found after creation: {volume_path}")
volume_size = os.path.getsize(volume_path)
logger.info(f"VOLUME: Volume created: {volume_path} ({volume_size} bytes)")
return volume_path
def attach_volume_to_vm(conn, vm_name, volume_path, logger):
"""
Attach the volume to the VM's libvirt XML configuration.
Args:
conn: Libvirt connection
vm_name: Name of the VM
volume_path: Path to the volume file
logger: Logger instance
Raises:
VolumeAttachmentError: If volume attachment fails
"""
try:
# Get the VM domain
dom = conn.lookupByName(vm_name)
# Get the XML description of the VM
xml_desc = dom.XMLDesc()
root = ET.fromstring(xml_desc)
# Find the devices element
devices_elem = root.find('./devices')
if devices_elem is None:
logger.error("VM: Could not find <devices> element in XML")
raise VolumeAttachmentError("Could not find <devices> element in VM XML")
# Log ALL devices with PCI addresses to find conflicts
logger.info("DISK_DEBUG: Examining ALL devices with PCI addresses")
for device in devices_elem:
address = device.find('./address')
if address is not None and address.get('type') == 'pci':
bus = address.get('bus', 'unknown')
slot = address.get('slot', 'unknown')
function = address.get('function', 'unknown')
logger.info(f"DISK_DEBUG: Device {device.tag}: bus={bus}, slot={slot}, function={function}")
# Log existing disk configuration for debugging
logger.info("DISK_DEBUG: Examining existing disk configuration")
existing_disks = devices_elem.findall('./disk')
for idx, disk in enumerate(existing_disks):
target = disk.find('./target')
source = disk.find('./source')
address = disk.find('./address')
dev_name = target.get('dev') if target is not None else 'unknown'
source_file = source.get('file') if source is not None else 'unknown'
if address is not None:
slot = address.get('slot', 'unknown')
bus = address.get('bus', 'unknown')
logger.info(f"DISK_DEBUG: Disk {idx}: dev={dev_name}, source={source_file}, slot={slot}, bus={bus}")
else:
logger.info(f"DISK_DEBUG: Disk {idx}: dev={dev_name}, source={source_file}, no address element")
# Check if vdb already exists
for disk in devices_elem.findall('./disk'):
target = disk.find('./target')
if target is not None and target.get('dev') == 'vdb':
logger.error("VM: Device vdb already exists in VM configuration")
raise VolumeAttachmentError("Device vdb already exists in VM configuration")
logger.info(f"VM: Attaching volume to {vm_name} as /dev/vdb")
# Create disk element
disk_elem = ET.SubElement(devices_elem, 'disk', attrib={
'type': 'file',
'device': 'disk'
})
# Add driver element
ET.SubElement(disk_elem, 'driver', attrib={
'name': 'qemu',
'type': 'raw',
'cache': 'none',
'io': 'native'
})
# Add source element
ET.SubElement(disk_elem, 'source', attrib={
'file': volume_path
})
# Add target element
ET.SubElement(disk_elem, 'target', attrib={
'dev': 'vdb',
'bus': 'virtio'
})
# Add address element
# Use bus 0x07 with slot 0x00 to ensure NSM volume appears after OS disk (which is on bus 0x04)
# Bus 0x05 is used by memballoon, bus 0x06 is used by rng device
# Libvirt requires slot <= 0 for non-zero buses
# This ensures vda = OS disk, vdb = NSM volume
ET.SubElement(disk_elem, 'address', attrib={
'type': 'pci',
'domain': '0x0000',
'bus': '0x07',
'slot': '0x00',
'function': '0x0'
})
logger.info(f"HARDWARE: Added disk configuration for vdb")
# Log disk ordering after adding new disk
logger.info("DISK_DEBUG: Disk configuration after adding NSM volume")
all_disks = devices_elem.findall('./disk')
for idx, disk in enumerate(all_disks):
target = disk.find('./target')
source = disk.find('./source')
address = disk.find('./address')
dev_name = target.get('dev') if target is not None else 'unknown'
source_file = source.get('file') if source is not None else 'unknown'
if address is not None:
slot = address.get('slot', 'unknown')
bus = address.get('bus', 'unknown')
logger.info(f"DISK_DEBUG: Disk {idx}: dev={dev_name}, source={source_file}, slot={slot}, bus={bus}")
else:
logger.info(f"DISK_DEBUG: Disk {idx}: dev={dev_name}, source={source_file}, no address element")
# Convert XML back to string
new_xml_desc = ET.tostring(root, encoding='unicode')
# Redefine the VM with the new XML
conn.defineXML(new_xml_desc)
logger.info(f"VM: VM redefined with volume attached")
except libvirt.libvirtError as e:
logger.error(f"VM: Failed to attach volume: {e}")
raise VolumeAttachmentError(f"Failed to attach volume: {e}")
except Exception as e:
logger.error(f"VM: Failed to attach volume: {e}")
raise VolumeAttachmentError(f"Failed to attach volume: {e}")
def emit_status_event(vm_name, status):
"""
Emit a deployment status event.
Args:
vm_name: Name of the VM
status: Status message
"""
try:
subprocess.run([
'so-salt-emit-vm-deployment-status-event',
'-v', vm_name,
'-H', HYPERVISOR,
'-s', status
], check=True)
except subprocess.CalledProcessError as e:
# Don't fail the entire operation if status event fails
pass
def main():
"""Main function to orchestrate volume creation and attachment."""
# Set up logging using the so_logging_utils library
string_handler = StringIOHandler()
string_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger = setup_logging(
logger_name='so-kvm-create-volume',
log_file_path='/opt/so/log/hypervisor/so-kvm-create-volume.log',
log_level=logging.INFO,
format_str='%(asctime)s - %(levelname)s - %(message)s'
)
logger.addHandler(string_handler)
vm_name = None
try:
# Parse arguments
args = parse_arguments()
vm_name = args.vm
size_gb = args.size
start_vm_flag = args.start
logger.info(f"VOLUME: Starting volume creation for VM '{vm_name}' with size {size_gb} GB")
# Emit start status event
emit_status_event(vm_name, 'Volume Creation')
# Ensure volume directory exists before checking disk space
try:
os.makedirs(VOLUME_DIR, mode=0o754, exist_ok=True)
qemu_uid = pwd.getpwnam('qemu').pw_uid
qemu_gid = grp.getgrnam('qemu').gr_gid
os.chown(VOLUME_DIR, qemu_uid, qemu_gid)
logger.debug(f"VOLUME: Ensured volume directory exists: {VOLUME_DIR}")
except Exception as e:
logger.error(f"VOLUME: Failed to create volume directory: {e}")
emit_status_event(vm_name, 'Volume Configuration Failed')
sys.exit(1)
# Check disk space
check_disk_space(size_gb, logger)
# Connect to libvirt
try:
conn = libvirt.open(None)
logger.info("VM: Connected to libvirt")
except libvirt.libvirtError as e:
logger.error(f"VM: Failed to open connection to libvirt: {e}")
emit_status_event(vm_name, 'Volume Configuration Failed')
sys.exit(1)
# Stop VM if running
dom = stop_vm(conn, vm_name, logger)
# Create volume file
volume_path = create_volume_file(vm_name, size_gb, logger)
# Attach volume to VM
attach_volume_to_vm(conn, vm_name, volume_path, logger)
# Start VM if -S or --start argument is provided
if start_vm_flag:
dom = conn.lookupByName(vm_name)
start_vm(dom, logger)
logger.info(f"VM: VM '{vm_name}' started successfully")
else:
logger.info("VM: Start flag not provided; VM will remain stopped")
# Close connection
conn.close()
# Emit success status event
emit_status_event(vm_name, 'Volume Configuration')
logger.info(f"VOLUME: Volume creation and attachment completed successfully for VM '{vm_name}'")
except KeyboardInterrupt:
error_msg = "Operation cancelled by user"
logger.error(error_msg)
if vm_name:
emit_status_event(vm_name, 'Volume Configuration Failed')
sys.exit(1)
except InsufficientSpaceError as e:
error_msg = f"SPACE: {str(e)}"
logger.error(error_msg)
if vm_name:
emit_status_event(vm_name, 'Volume Configuration Failed')
sys.exit(1)
except VolumeCreationError as e:
error_msg = f"VOLUME: {str(e)}"
logger.error(error_msg)
if vm_name:
emit_status_event(vm_name, 'Volume Configuration Failed')
sys.exit(1)
except VolumeAttachmentError as e:
error_msg = f"VM: {str(e)}"
logger.error(error_msg)
if vm_name:
emit_status_event(vm_name, 'Volume Configuration Failed')
sys.exit(1)
except Exception as e:
error_msg = f"An error occurred: {str(e)}"
logger.error(error_msg)
if vm_name:
emit_status_event(vm_name, 'Volume Configuration Failed')
sys.exit(1)
if __name__ == '__main__':
main()
{%- else -%}
echo "Hypervisor nodes are 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."
{% endif -%}

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ kibana:
- default
- file
migrations:
discardCorruptObjects: "8.18.4"
discardCorruptObjects: "8.18.8"
telemetry:
enabled: False
security:

View File

@@ -54,6 +54,9 @@ so-kratos:
- file: kratosconfig
- file: kratoslogdir
- file: kratosdir
- retry:
attempts: 10
interval: 10
delete_so-kratos_so-status.disabled:
file.uncomment:

View File

@@ -3,8 +3,10 @@
# https://securityonion.net/license; you may not use this file except in compliance with the
# Elastic License 2.0.
{% from 'libvirt/map.jinja' import LIBVIRTMERGED %}
{% from 'salt/map.jinja' import SYSTEMD_UNIT_FILE %}
# We do not import GLOBALS in this state because it is called during setup
include:
- salt.minion.service_file
- salt.mine_functions
down_original_mgmt_interface:
cmd.run:
@@ -29,21 +31,14 @@ wait_for_br0_ip:
- timeout: 95
- onchanges:
- cmd: down_original_mgmt_interface
update_mine_functions:
file.managed:
- name: /etc/salt/minion.d/mine_functions.conf
- contents: |
mine_interval: 25
mine_functions:
network.ip_addrs:
- interface: br0
- onchanges:
- cmd: wait_for_br0_ip
- onchanges_in:
- file: salt_minion_service_unit_file
- file: mine_functions
restart_salt_minion_service:
service.running:
- name: salt-minion
- enable: True
- listen:
- file: update_mine_functions
- file: salt_minion_service_unit_file
- file: mine_functions

View File

@@ -31,6 +31,19 @@ libvirt_conf_dir:
- group: 939
- makedirs: True
libvirt_volumes:
file.directory:
- name: /nsm/libvirt/volumes
- user: qemu
- group: qemu
- dir_mode: 755
- file_mode: 640
- recurse:
- user
- group
- mode
- makedirs: True
libvirt_config:
file.managed:
- name: /opt/so/conf/libvirt/libvirtd.conf

View File

@@ -268,3 +268,12 @@ logrotate:
- nocompress
- create
- sharedscripts
/opt/so/log/agents/agent-monitor*_x_log:
- daily
- rotate 14
- missingok
- compress
- create
- extension .log
- dateext
- dateyesterday

View File

@@ -175,3 +175,10 @@ logrotate:
multiline: True
global: True
forcedType: "[]string"
"/opt/so/log/agents/agent-monitor*_x_log":
description: List of logrotate options for this file.
title: /opt/so/log/agents/agent-monitor*.log
advanced: True
multiline: True
global: True
forcedType: "[]string"

View File

@@ -17,7 +17,7 @@
{% for node_type, node_details in redis_node_data.items() | sort %}
{% if GLOBALS.role in ['so-searchnode', 'so-standalone', 'so-managersearch', 'so-fleet'] %}
{% if node_type in ['manager', 'managersearch', 'standalone', 'receiver' ] %}
{% if node_type.startswith('manager') or node_type in ['standalone', 'receiver'] %}
{% for hostname in redis_node_data[node_type].keys() %}
{% do LOGSTASH_REDIS_NODES.append({hostname:node_details[hostname].ip}) %}
{% endfor %}
@@ -47,7 +47,7 @@
{% endif %}
{# Disable logstash on manager & receiver nodes unless it has an override configured #}
{% if not KAFKA_LOGSTASH %}
{% if GLOBALS.role in ['so-manager', 'so-receiver'] and GLOBALS.hostname not in KAFKA_LOGSTASH %}
{% if GLOBALS.role in ['so-manager', 'so-managerhype', 'so-receiver'] and GLOBALS.hostname not in KAFKA_LOGSTASH %}
{% do LOGSTASH_MERGED.update({'enabled': False}) %}
{% endif %}
{% endif %}

View File

@@ -0,0 +1,3 @@
#!/bin/bash
curl -s -L http://localhost:9600/_node/stats/flow | jq

View File

@@ -0,0 +1,3 @@
#!/bin/bash
curl -s -L http://localhost:9600/_health_report | jq

View File

@@ -0,0 +1,3 @@
#!/bin/bash
curl -s -L http://localhost:9600/_node/stats/jvm | jq

View File

@@ -5,3 +5,12 @@ manager:
minute: 0
additionalCA: ''
insecureSkipVerify: False
agent_monitoring:
enabled: False
config:
critical_agents: []
custom_kquery:
offline_threshold: 5
realert_threshold: 5
page_size: 250
run_interval: 5

View File

@@ -34,6 +34,26 @@ agents_log_dir:
- user
- group
agents_conf_dir:
file.directory:
- name: /opt/so/conf/agents
- user: root
- group: root
- recurse:
- user
- group
{% if MANAGERMERGED.agent_monitoring.config.critical_agents | length > 0 %}
critical_agents_patterns:
file.managed:
- name: /opt/so/conf/agents/critical-agents.txt
- contents: {{ MANAGERMERGED.agent_monitoring.config.critical_agents }}
{% else %}
remove_critical_agents_config:
file.absent:
- name: /opt/so/conf/agents/critical-agents.txt
{% endif %}
yara_log_dir:
file.directory:
- name: /opt/so/log/yarasync
@@ -127,6 +147,21 @@ so_fleetagent_status:
- month: '*'
- dayweek: '*'
so_fleetagent_monitor:
{% if MANAGERMERGED.agent_monitoring.enabled %}
cron.present:
{% else %}
cron.absent:
{% endif %}
- name: /bin/flock -n /opt/so/log/agents/agent-monitor.lock /usr/sbin/so-elastic-agent-monitor
- identifier: so_fleetagent_monitor
- user: root
- minute: '*/{{ MANAGERMERGED.agent_monitoring.config.run_interval }}'
- hour: '*'
- daymonth: '*'
- month: '*'
- dayweek: '*'
socore_own_saltstack_default:
file.directory:
- name: /opt/so/saltstack/default

View File

@@ -25,13 +25,11 @@
{% set index_settings = es.get('index_settings', {}) %}
{% set input = index_settings.get('so-logs', {}) %}
{% for k in matched_integration_names %}
{% if k not in index_settings %}
{% set _ = index_settings.update({k: input}) %}
{% endif %}
{% do index_settings.update({k: input}) %}
{% endfor %}
{% for k in addon_integration_keys %}
{% if k not in matched_integration_names and k in index_settings %}
{% set _ = index_settings.pop(k) %}
{% do index_settings.pop(k) %}
{% endif %}
{% endfor %}
{{ data }}
@@ -45,14 +43,12 @@
{% set es = data.get('elasticsearch', {}) %}
{% set index_settings = es.get('index_settings', {}) %}
{% for k in matched_integration_names %}
{% if k not in index_settings %}
{% set input = ADDON_INTEGRATION_DEFAULTS[k] %}
{% set _ = index_settings.update({k: input})%}
{% endif %}
{% set input = ADDON_INTEGRATION_DEFAULTS[k] %}
{% do index_settings.update({k: input})%}
{% endfor %}
{% for k in addon_integration_keys %}
{% if k not in matched_integration_names and k in index_settings %}
{% set _ = index_settings.pop(k) %}
{% do index_settings.pop(k) %}
{% endif %}
{% endfor %}
{{ data }}

View File

@@ -37,3 +37,44 @@ manager:
forcedType: bool
global: True
helpLink: proxy.html
agent_monitoring:
enabled:
description: Enable monitoring elastic agents for health issues. Can be used to trigger an alert when a 'critical' agent hasn't checked in with fleet for longer than the configured offline threshold.
global: True
helpLink: elastic-fleet.html
forcedType: bool
config:
critical_agents:
description: List of 'critical' agents to log when they haven't checked in longer than the maximum allowed time. If there are no 'critical' agents specified all offline agents will be logged once they reach the offline threshold.
global: True
multiline: True
helpLink: elastic-fleet.html
forcedType: "[]string"
custom_kquery:
description: For more granular control over what agents to monitor for offline|degraded status add a kquery here. It is recommended to create & test within Elastic Fleet first to ensure your agents are targeted correctly using the query. eg 'status:offline AND tags:INFRA'
global: True
helpLink: elastic-fleet.html
forcedType: string
advanced: True
offline_threshold:
description: The maximum allowed time in hours a 'critical' agent has been offline before being logged.
global: True
helpLink: elastic-fleet.html
forcedType: int
realert_threshold:
description: The time to pass before another alert for an offline agent exceeding the offline_threshold is generated.
global: True
helpLink: elastic-fleet.html
forcedType: int
page_size:
description: The amount of agents that can be processed per API request to fleet.
global: True
helpLink: elastic-fleet.html
forcedType: int
advanced: True
run_interval:
description: The time in minutes between checking fleet agent statuses.
global: True
advanced: True
helpLink: elastic-fleet.html
forcedType: int

View File

@@ -454,6 +454,7 @@ function add_sensor_to_minion() {
echo "sensor:"
echo " interface: '$INTERFACE'"
echo " mtu: 9000"
echo " channels: 1"
echo "zeek:"
echo " enabled: True"
echo " config:"

View File

@@ -5,10 +5,12 @@
# https://securityonion.net/license; you may not use this file except in compliance with the
# Elastic License 2.0.
default_salt_dir=/opt/so/saltstack/default
clone_to_tmp() {
VERBOSE=0
VERY_VERBOSE=0
TEST_MODE=0
clone_to_tmp() {
# TODO Need to add a air gap option
# Make a temp location for the files
mkdir /tmp/sogh
@@ -16,19 +18,110 @@ clone_to_tmp() {
#git clone -b dev https://github.com/Security-Onion-Solutions/securityonion.git
git clone https://github.com/Security-Onion-Solutions/securityonion.git
cd /tmp
}
show_file_changes() {
local source_dir="$1"
local dest_dir="$2"
local dir_type="$3" # "salt" or "pillar"
if [ $VERBOSE -eq 0 ]; then
return
fi
echo "=== Changes for $dir_type directory ==="
# Find all files in source directory
if [ -d "$source_dir" ]; then
find "$source_dir" -type f | while read -r source_file; do
# Get relative path
rel_path="${source_file#$source_dir/}"
dest_file="$dest_dir/$rel_path"
if [ ! -f "$dest_file" ]; then
echo "ADDED: $dest_file"
if [ $VERY_VERBOSE -eq 1 ]; then
echo " (New file - showing first 20 lines)"
head -n 20 "$source_file" | sed 's/^/ + /'
echo ""
fi
elif ! cmp -s "$source_file" "$dest_file"; then
echo "MODIFIED: $dest_file"
if [ $VERY_VERBOSE -eq 1 ]; then
echo " (Changes:)"
diff -u "$dest_file" "$source_file" | sed 's/^/ /'
echo ""
fi
fi
done
fi
# Find deleted files (exist in dest but not in source)
if [ -d "$dest_dir" ]; then
find "$dest_dir" -type f | while read -r dest_file; do
# Get relative path
rel_path="${dest_file#$dest_dir/}"
source_file="$source_dir/$rel_path"
if [ ! -f "$source_file" ]; then
echo "DELETED: $dest_file"
if [ $VERY_VERBOSE -eq 1 ]; then
echo " (File was deleted)"
echo ""
fi
fi
done
fi
echo ""
}
copy_new_files() {
# Copy new files over to the salt dir
cd /tmp/sogh/securityonion
git checkout $BRANCH
VERSION=$(cat VERSION)
if [ $TEST_MODE -eq 1 ]; then
echo "=== TEST MODE: Showing what would change without making changes ==="
echo "Branch: $BRANCH"
echo "Version: $VERSION"
echo ""
fi
# Show changes before copying if verbose mode is enabled OR if in test mode
if [ $VERBOSE -eq 1 ] || [ $TEST_MODE -eq 1 ]; then
if [ $TEST_MODE -eq 1 ]; then
# In test mode, force at least basic verbose output
local old_verbose=$VERBOSE
if [ $VERBOSE -eq 0 ]; then
VERBOSE=1
fi
fi
echo "Analyzing file changes..."
show_file_changes "$(pwd)/salt" "$default_salt_dir/salt" "salt"
show_file_changes "$(pwd)/pillar" "$default_salt_dir/pillar" "pillar"
if [ $TEST_MODE -eq 1 ] && [ $old_verbose -eq 0 ]; then
# Restore original verbose setting
VERBOSE=$old_verbose
fi
fi
# If in test mode, don't copy files
if [ $TEST_MODE -eq 1 ]; then
echo "=== TEST MODE: No files were modified ==="
echo "To apply these changes, run without --test option"
rm -rf /tmp/sogh
return
fi
# We need to overwrite if there is a repo file
if [ -d /opt/so/repo ]; then
tar -czf /opt/so/repo/"$VERSION".tar.gz -C "$(pwd)/.." .
fi
rsync -a salt $default_salt_dir/
rsync -a pillar $default_salt_dir/
chown -R socore:socore $default_salt_dir/salt
@@ -45,11 +138,64 @@ got_root(){
fi
}
got_root
if [ $# -ne 1 ] ; then
show_usage() {
echo "Usage: $0 [-v] [-vv] [--test] [branch]"
echo " -v Show verbose output (files changed/added/deleted)"
echo " -vv Show very verbose output (includes file diffs)"
echo " --test Test mode - show what would change without making changes"
echo " branch Git branch to checkout (default: 2.4/main)"
echo ""
echo "Examples:"
echo " $0 # Normal operation"
echo " $0 -v # Show which files change"
echo " $0 -vv # Show files and their diffs"
echo " $0 --test # See what would change (dry run)"
echo " $0 --test -vv # Test mode with detailed diffs"
echo " $0 -v dev-branch # Use specific branch with verbose output"
exit 1
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-v)
VERBOSE=1
shift
;;
-vv)
VERBOSE=1
VERY_VERBOSE=1
shift
;;
--test)
TEST_MODE=1
shift
;;
-h|--help)
show_usage
;;
-*)
echo "Unknown option $1"
show_usage
;;
*)
# This should be the branch name
if [ -z "$BRANCH" ]; then
BRANCH="$1"
else
echo "Too many arguments"
show_usage
fi
shift
;;
esac
done
# Set default branch if not provided
if [ -z "$BRANCH" ]; then
BRANCH=2.4/main
else
BRANCH=$1
fi
got_root
clone_to_tmp
copy_new_files

View File

@@ -387,7 +387,7 @@ function syncElastic() {
if [[ -z "$SKIP_STATE_APPLY" ]]; then
echo "Elastic state will be re-applied to affected minions. This will run in the background and may take several minutes to complete."
echo "Applying elastic state to elastic minions at $(date)" >> /opt/so/log/soc/sync.log 2>&1
salt --async -C 'G@role:so-standalone or G@role:so-eval or G@role:so-import or G@role:so-manager or G@role:so-managersearch or G@role:so-searchnode or G@role:so-heavynode' state.apply elasticsearch queue=True >> /opt/so/log/soc/sync.log 2>&1
salt --async -C 'I@elasticsearch:enabled:true' state.apply elasticsearch queue=True >> /opt/so/log/soc/sync.log 2>&1
fi
else
echo "Newly generated users/roles files are incomplete; aborting."

View File

@@ -17,6 +17,7 @@ def showUsage(args):
print('Usage: {} <COMMAND> <YAML_FILE> [ARGS...]'.format(sys.argv[0]), file=sys.stderr)
print(' General commands:', file=sys.stderr)
print(' append - Append a list item to a yaml key, if it exists and is a list. Requires KEY and LISTITEM args.', file=sys.stderr)
print(' removelistitem - Remove a list item from a yaml key, if it exists and is a list. Requires KEY and LISTITEM args.', file=sys.stderr)
print(' 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)
@@ -26,8 +27,8 @@ def showUsage(args):
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)
print(' VALUE - Value to set for a given key. Can be a literal value or file:<path> to load from a YAML file.', file=sys.stderr)
print(' LISTITEM - Item to append to a given key\'s list value. Can be a literal value or file:<path> to load from a YAML file.', file=sys.stderr)
sys.exit(1)
@@ -57,8 +58,32 @@ def appendItem(content, key, listItem):
return 1
def removeListItem(content, key, listItem):
pieces = key.split(".", 1)
if len(pieces) > 1:
removeListItem(content[pieces[0]], pieces[1], listItem)
else:
try:
if not isinstance(content[key], list):
raise AttributeError("Value is not a list")
if listItem in content[key]:
content[key].remove(listItem)
except (AttributeError, TypeError):
print("The existing value for the given key is not a list. No action was taken on the file.", file=sys.stderr)
return 1
except KeyError:
print("The key provided does not exist. No action was taken on the file.", file=sys.stderr)
return 1
def convertType(value):
if isinstance(value, str) and len(value) > 0 and (not value.startswith("0") or len(value) == 1):
if isinstance(value, str) and value.startswith("file:"):
path = value[5:] # Remove "file:" prefix
if not os.path.exists(path):
print(f"File '{path}' does not exist.", file=sys.stderr)
sys.exit(1)
return loadYaml(path)
elif isinstance(value, str) and len(value) > 0 and (not value.startswith("0") or len(value) == 1):
if "." in value:
try:
value = float(value)
@@ -97,6 +122,23 @@ def append(args):
return 0
def removelistitem(args):
if len(args) != 3:
print('Missing filename, key arg, or list item to remove', file=sys.stderr)
showUsage(None)
return 1
filename = args[0]
key = args[1]
listItem = args[2]
content = loadYaml(filename)
removeListItem(content, key, convertType(listItem))
writeYaml(filename, content)
return 0
def addKey(content, key, value):
pieces = key.split(".", 1)
if len(pieces) > 1:
@@ -205,6 +247,7 @@ def main():
"help": showUsage,
"add": add,
"append": append,
"removelistitem": removelistitem,
"get": get,
"remove": remove,
"replace": replace,

View File

@@ -361,6 +361,29 @@ class TestRemove(unittest.TestCase):
self.assertEqual(soyaml.convertType("FALSE"), False)
self.assertEqual(soyaml.convertType(""), "")
def test_convert_file(self):
import tempfile
import os
# Create a temporary YAML file
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
f.write("test:\n - name: hi\n color: blue\n")
temp_file = f.name
try:
result = soyaml.convertType(f"file:{temp_file}")
expected = {"test": [{"name": "hi", "color": "blue"}]}
self.assertEqual(result, expected)
finally:
os.unlink(temp_file)
def test_convert_file_nonexistent(self):
with self.assertRaises(SystemExit) as cm:
with patch('sys.stderr', new=StringIO()) as mock_stderr:
soyaml.convertType("file:/nonexistent/file.yaml")
self.assertEqual(cm.exception.code, 1)
self.assertIn("File '/nonexistent/file.yaml' does not exist.", mock_stderr.getvalue())
def test_get_int(self):
with patch('sys.stdout', new=StringIO()) as mock_stdout:
filename = "/tmp/so-yaml_test-get.yaml"
@@ -434,3 +457,126 @@ class TestRemove(unittest.TestCase):
self.assertEqual(result, 1)
self.assertIn("Missing filename or key arg", mock_stderr.getvalue())
sysmock.assert_called_once_with(1)
class TestRemoveListItem(unittest.TestCase):
def test_removelistitem_missing_arg(self):
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stderr:
sys.argv = ["cmd", "help"]
soyaml.removelistitem(["file", "key"])
sysmock.assert_called()
self.assertIn("Missing filename, key arg, or list item to remove", mock_stderr.getvalue())
def test_removelistitem(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: abc }, key2: false, key3: [a,b,c]}")
file.close()
soyaml.removelistitem([filename, "key3", "b"])
file = open(filename, "r")
actual = file.read()
file.close()
expected = "key1:\n child1: 123\n child2: abc\nkey2: false\nkey3:\n- a\n- c\n"
self.assertEqual(actual, expected)
def test_removelistitem_nested(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: [a,b,c] }, key2: false, key3: [e,f,g]}")
file.close()
soyaml.removelistitem([filename, "key1.child2", "b"])
file = open(filename, "r")
actual = file.read()
file.close()
expected = "key1:\n child1: 123\n child2:\n - a\n - c\nkey2: false\nkey3:\n- e\n- f\n- g\n"
self.assertEqual(actual, expected)
def test_removelistitem_nested_deep(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
file.close()
soyaml.removelistitem([filename, "key1.child2.deep2", "b"])
file = open(filename, "r")
actual = file.read()
file.close()
expected = "key1:\n child1: 123\n child2:\n deep1: 45\n deep2:\n - a\n - c\nkey2: false\nkey3:\n- e\n- f\n- g\n"
self.assertEqual(actual, expected)
def test_removelistitem_item_not_in_list(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: [a,b,c]}")
file.close()
soyaml.removelistitem([filename, "key1", "d"])
file = open(filename, "r")
actual = file.read()
file.close()
expected = "key1:\n- a\n- b\n- c\n"
self.assertEqual(actual, expected)
def test_removelistitem_key_noexist(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
file.close()
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stderr:
sys.argv = ["cmd", "removelistitem", filename, "key4", "h"]
soyaml.main()
sysmock.assert_called()
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
def test_removelistitem_key_noexist_deep(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
file.close()
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stderr:
sys.argv = ["cmd", "removelistitem", filename, "key1.child2.deep3", "h"]
soyaml.main()
sysmock.assert_called()
self.assertEqual("The key provided does not exist. No action was taken on the file.\n", mock_stderr.getvalue())
def test_removelistitem_key_nonlist(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
file.close()
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stderr:
sys.argv = ["cmd", "removelistitem", filename, "key1", "h"]
soyaml.main()
sysmock.assert_called()
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())
def test_removelistitem_key_nonlist_deep(self):
filename = "/tmp/so-yaml_test-removelistitem.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: { deep1: 45, deep2: [a,b,c] } }, key2: false, key3: [e,f,g]}")
file.close()
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stderr:
sys.argv = ["cmd", "removelistitem", filename, "key1.child2.deep1", "h"]
soyaml.main()
sysmock.assert_called()
self.assertEqual("The existing value for the given key is not a list. No action was taken on the file.\n", mock_stderr.getvalue())

View File

@@ -21,6 +21,9 @@ whiptail_title='Security Onion UPdater'
NOTIFYCUSTOMELASTICCONFIG=false
TOPFILE=/opt/so/saltstack/default/salt/top.sls
BACKUPTOPFILE=/opt/so/saltstack/default/salt/top.sls.backup
SALTUPGRADED=false
SALT_CLOUD_INSTALLED=false
SALT_CLOUD_CONFIGURED=false
# used to display messages to the user at the end of soup
declare -a FINAL_MESSAGE_QUEUE=()
@@ -169,6 +172,8 @@ airgap_update_dockers() {
tar xf "$AGDOCKER/registry.tar" -C /nsm/docker-registry/docker
echo "Add Registry back"
docker load -i "$AGDOCKER/registry_image.tar"
echo "Restart registry container"
salt-call state.apply registry queue=True
fi
fi
}
@@ -269,7 +274,7 @@ check_os_updates() {
if [[ "$confirm" == [cC] ]]; then
echo "Continuing without updating packages"
elif [[ "$confirm" == [uU] ]]; then
echo "Applying Grid Updates"
echo "Applying Grid Updates. The following patch.os salt state may take a while depending on how many packages need to be updated."
update_flag=true
else
echo "Exiting soup"
@@ -419,6 +424,9 @@ preupgrade_changes() {
[[ "$INSTALLEDVERSION" == 2.4.141 ]] && up_to_2.4.150
[[ "$INSTALLEDVERSION" == 2.4.150 ]] && up_to_2.4.160
[[ "$INSTALLEDVERSION" == 2.4.160 ]] && up_to_2.4.170
[[ "$INSTALLEDVERSION" == 2.4.170 ]] && up_to_2.4.180
[[ "$INSTALLEDVERSION" == 2.4.180 ]] && up_to_2.4.190
[[ "$INSTALLEDVERSION" == 2.4.190 ]] && up_to_2.4.200
true
}
@@ -448,6 +456,9 @@ postupgrade_changes() {
[[ "$POSTVERSION" == 2.4.141 ]] && post_to_2.4.150
[[ "$POSTVERSION" == 2.4.150 ]] && post_to_2.4.160
[[ "$POSTVERSION" == 2.4.160 ]] && post_to_2.4.170
[[ "$POSTVERSION" == 2.4.170 ]] && post_to_2.4.180
[[ "$POSTVERSION" == 2.4.180 ]] && post_to_2.4.190
[[ "$POSTVERSION" == 2.4.190 ]] && post_to_2.4.200
true
}
@@ -588,9 +599,6 @@ post_to_2.4.160() {
}
post_to_2.4.170() {
echo "Regenerating Elastic Agent Installers"
/sbin/so-elastic-agent-gen-installers
# Update kibana default space
salt-call state.apply kibana.config queue=True
echo "Updating Kibana default space"
@@ -599,6 +607,42 @@ post_to_2.4.170() {
POSTVERSION=2.4.170
}
post_to_2.4.180() {
# Force update to Kafka output policy
/usr/sbin/so-kafka-fleet-output-policy --force
POSTVERSION=2.4.180
}
post_to_2.4.190() {
echo "Regenerating Elastic Agent Installers"
/sbin/so-elastic-agent-gen-installers
# Only need to update import / eval nodes
if [[ "$MINION_ROLE" == "import" ]] || [[ "$MINION_ROLE" == "eval" ]]; then
update_import_fleet_output
fi
# Check if expected default policy is logstash (global.pipeline is REDIS or "")
pipeline=$(lookup_pillar "pipeline" "global")
if [[ -z "$pipeline" ]] || [[ "$pipeline" == "REDIS" ]]; then
# Check if this grid is currently affected by corrupt fleet output policy
if elastic-agent status | grep "config: key file not configured" > /dev/null 2>&1; then
echo "Elastic Agent shows an ssl error connecting to logstash output. Updating output policy..."
update_default_logstash_output
fi
fi
# Apply new elasticsearch.server index template
rollover_index "logs-elasticsearch.server-default"
POSTVERSION=2.4.190
}
post_to_2.4.200() {
echo "Nothing to apply"
POSTVERSION=2.4.200
}
repo_sync() {
echo "Sync the local repo."
su socore -c '/usr/sbin/so-repo-sync' || fail "Unable to complete so-repo-sync."
@@ -850,10 +894,26 @@ up_to_2.4.170() {
touch /opt/so/saltstack/local/pillar/$state/adv_$state.sls /opt/so/saltstack/local/pillar/$state/soc_$state.sls
done
INSTALLEDVERSION=2.4.170
}
up_to_2.4.180() {
echo "Nothing to do for 2.4.180"
INSTALLEDVERSION=2.4.180
}
up_to_2.4.190() {
# Elastic Update for this release, so download Elastic Agent files
determine_elastic_agent_upgrade
INSTALLEDVERSION=2.4.170
INSTALLEDVERSION=2.4.190
}
up_to_2.4.200() {
touch /opt/so/state/esfleet_logstash_config_pillar
INSTALLEDVERSION=2.4.200
}
add_hydra_pillars() {
@@ -1129,6 +1189,44 @@ update_elasticsearch_index_settings() {
done
}
update_import_fleet_output() {
if output=$(curl -sK /opt/so/conf/elasticsearch/curl.config -L "localhost:5601/api/fleet/outputs/so-manager_elasticsearch" --retry 3 --fail 2>/dev/null); then
# Update the current config of so-manager_elasticsearch output policy in place (leaving any customizations like having changed the preset value from 'balanced' to 'performance')
CAFINGERPRINT=$(openssl x509 -in /etc/pki/tls/certs/intca.crt -outform DER | sha256sum | cut -d' ' -f1 | tr '[:lower:]' '[:upper:]')
updated_policy=$(jq --arg CAFINGERPRINT "$CAFINGERPRINT" '.item | (del(.id) | .ca_trusted_fingerprint = $CAFINGERPRINT)' <<< "$output")
if curl -sK /opt/so/conf/elasticsearch/curl.config -L "localhost:5601/api/fleet/outputs/so-manager_elasticsearch" -XPUT -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$updated_policy" --retry 3 --fail 2>/dev/null; then
echo "Successfully updated so-manager_elasticsearch fleet output policy"
else
fail "Failed to update so-manager_elasticsearch fleet output policy"
fi
fi
}
update_default_logstash_output() {
echo "Updating fleet logstash output policy grid-logstash"
if logstash_policy=$(curl -K /opt/so/conf/elasticsearch/curl.config -L "http://localhost:5601/api/fleet/outputs/so-manager_logstash" --retry 3 --retry-delay 10 --fail 2>/dev/null); then
# Keep already configured hosts for this update, subsequent host updates come from so-elastic-fleet-outputs-update
HOSTS=$(echo "$logstash_policy" | jq -r '.item.hosts')
DEFAULT_ENABLED=$(echo "$logstash_policy" | jq -r '.item.is_default')
DEFAULT_MONITORING_ENABLED=$(echo "$logstash_policy" | jq -r '.item.is_default_monitoring')
LOGSTASHKEY=$(openssl rsa -in /etc/pki/elasticfleet-logstash.key)
LOGSTASHCRT=$(openssl x509 -in /etc/pki/elasticfleet-logstash.crt)
LOGSTASHCA=$(openssl x509 -in /etc/pki/tls/certs/intca.crt)
JSON_STRING=$(jq -n \
--argjson HOSTS "$HOSTS" \
--arg DEFAULT_ENABLED "$DEFAULT_ENABLED" \
--arg DEFAULT_MONITORING_ENABLED "$DEFAULT_MONITORING_ENABLED" \
--arg LOGSTASHKEY "$LOGSTASHKEY" \
--arg LOGSTASHCRT "$LOGSTASHCRT" \
--arg LOGSTASHCA "$LOGSTASHCA" \
'{"name":"grid-logstash","type":"logstash","hosts": $HOSTS,"is_default": $DEFAULT_ENABLED,"is_default_monitoring": $DEFAULT_MONITORING_ENABLED,"config_yaml":"","ssl":{"certificate": $LOGSTASHCRT,"certificate_authorities":[ $LOGSTASHCA ]},"secrets":{"ssl":{"key": $LOGSTASHKEY }}}')
fi
if 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" --retry 3 --retry-delay 10 --fail; then
echo "Successfully updated grid-logstash fleet output policy"
fi
}
update_salt_mine() {
echo "Populating the mine with mine_functions for each host."
set +e
@@ -1178,24 +1276,43 @@ upgrade_check_salt() {
}
upgrade_salt() {
SALTUPGRADED=True
echo "Performing upgrade of Salt from $INSTALLEDSALTVERSION to $NEWSALTVERSION."
echo ""
# If rhel family
if [[ $is_rpm ]]; then
# Check if salt-cloud is installed
if rpm -q salt-cloud &>/dev/null; then
SALT_CLOUD_INSTALLED=true
fi
# Check if salt-cloud is configured
if [[ -f /etc/salt/cloud.profiles.d/socloud.conf ]]; then
SALT_CLOUD_CONFIGURED=true
fi
echo "Removing yum versionlock for Salt."
echo ""
yum versionlock delete "salt"
yum versionlock delete "salt-minion"
yum versionlock delete "salt-master"
# Remove salt-cloud versionlock if installed
if [[ $SALT_CLOUD_INSTALLED == true ]]; then
yum versionlock delete "salt-cloud"
fi
echo "Updating Salt packages."
echo ""
set +e
# if oracle run with -r to ignore repos set by bootstrap
if [[ $OS == 'oracle' ]]; then
run_check_net_err \
"sh $UPDATE_DIR/salt/salt/scripts/bootstrap-salt.sh -X -r -F -M stable \"$NEWSALTVERSION\"" \
"Could not update salt, please check $SOUP_LOG for details."
# Add -L flag only if salt-cloud is already installed
if [[ $SALT_CLOUD_INSTALLED == true ]]; then
run_check_net_err \
"sh $UPDATE_DIR/salt/salt/scripts/bootstrap-salt.sh -X -r -L -F -M stable \"$NEWSALTVERSION\"" \
"Could not update salt, please check $SOUP_LOG for details."
else
run_check_net_err \
"sh $UPDATE_DIR/salt/salt/scripts/bootstrap-salt.sh -X -r -F -M stable \"$NEWSALTVERSION\"" \
"Could not update salt, please check $SOUP_LOG for details."
fi
# if another rhel family variant we want to run without -r to allow the bootstrap script to manage repos
else
run_check_net_err \
@@ -1208,8 +1325,14 @@ upgrade_salt() {
yum versionlock add "salt-0:$NEWSALTVERSION-0.*"
yum versionlock add "salt-minion-0:$NEWSALTVERSION-0.*"
yum versionlock add "salt-master-0:$NEWSALTVERSION-0.*"
# Add salt-cloud versionlock if installed
if [[ $SALT_CLOUD_INSTALLED == true ]]; then
yum versionlock add "salt-cloud-0:$NEWSALTVERSION-0.*"
fi
# Else do Ubuntu things
elif [[ $is_deb ]]; then
# ensure these files don't exist when upgrading from 3006.9 to 3006.16
rm -f /etc/apt/keyrings/salt-archive-keyring-2023.pgp /etc/apt/sources.list.d/salt.list
echo "Removing apt hold for Salt."
echo ""
apt-mark unhold "salt-common"
@@ -1240,6 +1363,7 @@ upgrade_salt() {
echo ""
exit 1
else
SALTUPGRADED=true
echo "Salt upgrade success."
echo ""
fi
@@ -1345,6 +1469,7 @@ main() {
fi
set_minionid
MINION_ROLE=$(lookup_role)
echo "Found that Security Onion $INSTALLEDVERSION is currently installed."
echo ""
if [[ $is_airgap -eq 0 ]]; then
@@ -1387,7 +1512,7 @@ main() {
if [ "$is_hotfix" == "true" ]; then
echo "Applying $HOTFIXVERSION hotfix"
# since we don't run the backup.config_backup state on import we wont snapshot previous version states and pillars
if [[ ! "$MINIONID" =~ "_import" ]]; then
if [[ ! "$MINION_ROLE" == "import" ]]; then
backup_old_states_pillars
fi
copy_new_files
@@ -1450,7 +1575,7 @@ main() {
fi
# since we don't run the backup.config_backup state on import we wont snapshot previous version states and pillars
if [[ ! "$MINIONID" =~ "_import" ]]; then
if [[ ! "$MINION_ROLE" == "import" ]]; then
echo ""
echo "Creating snapshots of default and local Salt states and pillars and saving to /nsm/backup/"
backup_old_states_pillars
@@ -1482,6 +1607,11 @@ main() {
# ensure the mine is updated and populated before highstates run, following the salt-master restart
update_salt_mine
if [[ $SALT_CLOUD_CONFIGURED == true && $SALTUPGRADED == true ]]; then
echo "Updating salt-cloud config to use the new Salt version"
salt-call state.apply salt.cloud.config concurrent=True
fi
enable_highstate
echo ""
@@ -1564,7 +1694,7 @@ This appears to be a distributed deployment. Other nodes should update themselve
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.
If it looks like youre missing data after the upgrade, please avoid restarting services and instead make sure at least one search node has completed its upgrade. The best way to do this is to run 'sudo salt-call state.highstate' from a search node and make sure there are no errors. Typically if it works on one node it will work on the rest. Forward nodes are less complex and will update as they check in so you can monitor those from the Grid section of SOC.
If it looks like youre missing data after the upgrade, please avoid restarting services and instead make sure at least one search node has completed its upgrade. The best way to do this is to run 'sudo salt-call state.highstate' from a search node and make sure there are no errors. Typically if it works on one node it will work on the rest. Sensor nodes are less complex and will update as they check in so you can monitor those from the Grid section of SOC.
For more information, please see $DOC_BASE_URL/soup.html#distributed-deployments.

View File

@@ -0,0 +1,254 @@
{%- from 'manager/map.jinja' import MANAGERMERGED -%}
{%- set OFFLINE_THRESHOLD_HOURS = MANAGERMERGED.agent_monitoring.config.offline_threshold -%}
{%- set PAGE_SIZE = MANAGERMERGED.agent_monitoring.config.page_size -%}
{%- set CUSTOM_KQUERY = MANAGERMERGED.agent_monitoring.config.custom_kquery -%}
{%- set REALERT_THRESHOLD = MANAGERMERGED.agent_monitoring.config.realert_threshold -%}
#!/bin/bash
set -euo pipefail
LOG_DIR="/opt/so/log/agents"
LOG_FILE="$LOG_DIR/agent-monitor.log"
CURL_CONFIG="/opt/so/conf/elasticsearch/curl.config"
FLEET_API="http://localhost:5601/api/fleet/agents"
{#- When using custom kquery ignore critical agents patterns. Since we want all the results of custom query logged #}
{%- if CUSTOM_KQUERY != None and CUSTOM_KQUERY | length > 0 %}
CRITICAL_AGENTS_FILE="/dev/null"
{%- else %}
CRITICAL_AGENTS_FILE="/opt/so/conf/agents/critical-agents.txt"
{%- endif %}
OFFLINE_THRESHOLD_HOURS={{ OFFLINE_THRESHOLD_HOURS }}
REALERT_THRESHOLD={{ REALERT_THRESHOLD }}
PAGE_SIZE="{{ PAGE_SIZE }}"
log_message() {
local level="$1"
local message="$2"
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") [$level] $message" >&2
}
matches_critical_pattern() {
local hostname="$1"
local pattern_file="$2"
# If critical agents file doesn't exist or is empty, match all
if [ ! -f "$pattern_file" ] || [ ! -s "$pattern_file" ]; then
return 0
fi
local hostname_lower=$(echo "$hostname" | tr '[:upper:]' '[:lower:]')
while IFS= read -r pattern || [ -n "$pattern" ]; do
# empty lines and comments
[[ -z "$pattern" || "$pattern" =~ ^[[:space:]]*# ]] && continue
# cut whitespace
pattern=$(echo "$pattern" | xargs)
local pattern_lower=$(echo "$pattern" | tr '[:upper:]' '[:lower:]')
# Replace * with bash wildcard
local bash_pattern="${pattern_lower//\*/.*}"
# Check if hostname matches the pattern
if [[ "$hostname_lower" =~ ^${bash_pattern}$ ]]; then
return 0
fi
done < "$pattern_file"
return 1
}
calculate_offline_hours() {
local last_checkin="$1"
local current_time=$(date +%s)
local checkin_time=$(date -d "$last_checkin" +%s 2>/dev/null || echo "0")
if [ "$checkin_time" -eq "0" ]; then
echo "0"
return
fi
local diff=$((current_time - checkin_time))
echo $((diff / 3600))
}
check_recent_log_entries() {
local agent_hostname="$1"
if [ ! -f "$LOG_FILE" ]; then
return 1
fi
local current_time=$(date +%s)
local threshold_seconds=$((REALERT_THRESHOLD * 3600))
local agent_hostname_lower=$(echo "$agent_hostname" | tr '[:upper:]' '[:lower:]')
local most_recent_timestamp=""
while IFS= read -r line; do
[ -z "$line" ] && continue
local logged_hostname=$(echo "$line" | jq -r '.["agent.hostname"] // empty' 2>/dev/null)
local logged_timestamp=$(echo "$line" | jq -r '.["@timestamp"] // empty' 2>/dev/null)
[ -z "$logged_hostname" ] || [ -z "$logged_timestamp" ] && continue
local logged_hostname_lower=$(echo "$logged_hostname" | tr '[:upper:]' '[:lower:]')
if [ "$logged_hostname_lower" = "$agent_hostname_lower" ]; then
most_recent_timestamp="$logged_timestamp"
fi
done < <(tail -n 1000 "$LOG_FILE" 2>/dev/null)
# If there is agent entry (within last 1000), check the time difference
if [ -n "$most_recent_timestamp" ]; then
local logged_time=$(date -d "$most_recent_timestamp" +%s 2>/dev/null || echo "0")
if [ "$logged_time" -ne "0" ]; then
local time_diff=$((current_time - logged_time))
local hours_diff=$((time_diff / 3600))
# Skip if last agent timestamp was more recent than realert threshold
if ((hours_diff < REALERT_THRESHOLD)); then
return 0
fi
fi
fi
# Agent has not been logged within realert threshold
return 1
}
main() {
log_message "INFO" "Starting Fleet agent status check"
# Check if critical agents file is configured
if [ -f "$CRITICAL_AGENTS_FILE" ] && [ -s "$CRITICAL_AGENTS_FILE" ]; then
log_message "INFO" "Using critical agents filter from: $CRITICAL_AGENTS_FILE"
log_message "INFO" "Patterns: $(grep -v '^#' "$CRITICAL_AGENTS_FILE" 2>/dev/null | xargs | tr ' ' ',')"
else
log_message "INFO" "No critical agents filter found, monitoring all agents"
fi
log_message "INFO" "Querying Fleet API"
local page=1
local total_agents=0
local processed_agents=0
local current_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
{%- if CUSTOM_KQUERY != None and CUSTOM_KQUERY | length > 0 %}
log_message "INFO" "Using custom kquery: {{ CUSTOM_KQUERY }}"
FLEET_QUERY="${FLEET_API}?kuery={{ CUSTOM_KQUERY | urlencode }}&perPage=${PAGE_SIZE}&page=${page}"
{%- else %}
log_message "INFO" "Using default query (all offline or degraded agents)"
FLEET_QUERY="${FLEET_API}?kuery=status%3Aoffline%20OR%20status%3Adegraded&perPage=${PAGE_SIZE}&page=${page}"
{%- endif %}
while true; do
log_message "INFO" "Fetching page $page (${PAGE_SIZE} agents per page)"
if ! response_body=$(curl -K "$CURL_CONFIG" \
-s --fail \
"$FLEET_QUERY" \
-H 'kbn-xsrf: true' 2>/dev/null); then
log_message "ERROR" "Failed to query Fleet API (page $page)"
exit 1
fi
# pagination info
current_total=$(echo "$response_body" | jq -r '.total // 0')
current_page=$(echo "$response_body" | jq -r '.page // 1')
agents_in_page=$(echo "$response_body" | jq -r '.list | length')
# Update total
if [ "$page" -eq 1 ]; then
total_agents="$current_total"
log_message "INFO" "Found $total_agents total agents across all pages"
fi
log_message "INFO" "Processing page $current_page with $agents_in_page agents"
# Process agents from current page
mapfile -t agents < <(echo "$response_body" | jq -c '.list[]')
for agent in "${agents[@]}"; do
# Grab agent details
agent_id=$(echo "$agent" | jq -r '.id // "unknown"')
agent_hostname=$(echo "$agent" | jq -r '.local_metadata.host.hostname // "unknown"')
agent_name=$(echo "$agent" | jq -r '.local_metadata.host.name // "unknown"')
agent_status=$(echo "$agent" | jq -r '.status // "unknown"')
last_checkin=$(echo "$agent" | jq -r '.last_checkin // ""')
last_checkin_status=$(echo "$agent" | jq -r '.last_checkin_status // "unknown"')
policy_id=$(echo "$agent" | jq -r '.policy_id // "unknown"')
# Only log agents that are offline or degraded (skip inactive agents)
# Fleetserver agents can show multiple versions as 'inactive'
if [ "$agent_status" = "offline" ] || [ "$agent_status" = "degraded" ]; then
# Check if agent matches critical agent patterns (if configured)
if ! matches_critical_pattern "$agent_hostname" "$CRITICAL_AGENTS_FILE"; then
log_message "WARN" "${agent_hostname^^} is ${agent_status^^}, but does not match configured critical agents patterns. Not logging ${agent_status^^} agent"
continue # Skip this agent if it doesn't match any critical agent pattern
fi
offline_hours=$(calculate_offline_hours "$last_checkin")
if [ "$offline_hours" -lt "$OFFLINE_THRESHOLD_HOURS" ]; then
log_message "INFO" "${agent_hostname^^} has been offline for ${offline_hours}h (threshold: ${OFFLINE_THRESHOLD_HOURS}h). Not logging ${agent_status^^} agent until it reaches threshold"
continue
fi
# Check if this agent was already logged within the realert_threshold
if check_recent_log_entries "$agent_hostname"; then
log_message "INFO" "Skipping $agent_hostname (status: $agent_status) - already logged within last ${REALERT_THRESHOLD}h"
continue
fi
log_entry=$(echo 'null' | jq -c \
--arg ts "$current_timestamp" \
--arg id "$agent_id" \
--arg hostname "$agent_hostname" \
--arg name "$agent_name" \
--arg status "$agent_status" \
--arg last_checkin "$last_checkin" \
--arg last_checkin_status "$last_checkin_status" \
--arg policy_id "$policy_id" \
--arg offline_hours "$offline_hours" \
'{
"@timestamp": $ts,
"agent.id": $id,
"agent.hostname": $hostname,
"agent.name": $name,
"agent.status": $status,
"agent.last_checkin": $last_checkin,
"agent.last_checkin_status": $last_checkin_status,
"agent.policy_id": $policy_id,
"agent.offline_duration_hours": ($offline_hours | tonumber)
}')
echo "$log_entry" >> "$LOG_FILE"
log_message "INFO" "Logged offline agent: $agent_hostname (status: $agent_status, offline: ${offline_hours}h)"
fi
done
processed_agents=$((processed_agents + agents_in_page))
if [ "$agents_in_page" -eq 0 ] || [ "$processed_agents" -ge "$total_agents" ]; then
log_message "INFO" "Completed processing all pages. Total processed: $processed_agents agents"
break
fi
page=$((page + 1))
# Limit pagination loops incase of any issues. If agent count is high enough increase page_size in SOC manager.agent_monitoring.config.page_size
if [ "$page" -gt 100 ]; then
log_message "ERROR" "Reached maximum page limit (100). Issue with script or extremely large fleet deployment. Consider increasing page_size in SOC -> manager.agent_monitoring.config.page_size"
break
fi
done
log_message "INFO" "Fleet agent status check completed. Processed $processed_agents out of $total_agents agents"
}
main "$@"

View File

@@ -15,6 +15,7 @@ require_manager
echo
echo "This script will remove the current Elastic Fleet install and all of its data and then rerun Elastic Fleet setup."
echo "Deployed Elastic Agents will no longer be enrolled and will need to be reinstalled."
echo "Only the Elastic Fleet instance on the Manager will be reinstalled - dedicated Fleet node config will removed and will need to be reinstalled."
echo "This script should only be used as a last resort to reinstall Elastic Fleet."
echo
echo "If you would like to proceed, then type AGREE and press ENTER."

View File

@@ -211,7 +211,7 @@ Exit Codes:
Logging:
- Logs are written to /opt/so/log/salt/so-salt-cloud.log.
- Logs are written to /opt/so/log/salt/so-salt-cloud.
- Both file and console logging are enabled for real-time monitoring.
"""
@@ -233,7 +233,7 @@ local = salt.client.LocalClient()
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
file_handler = logging.FileHandler('/opt/so/log/salt/so-salt-cloud.log')
file_handler = logging.FileHandler('/opt/so/log/salt/so-salt-cloud')
console_handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(message)s')
@@ -516,23 +516,85 @@ def run_qcow2_modify_hardware_config(profile, vm_name, cpu=None, memory=None, pc
target = hv_name + "_*"
try:
args_list = [
'vm_name=' + vm_name,
'cpu=' + str(cpu) if cpu else '',
'memory=' + str(memory) if memory else '',
'start=' + str(start)
]
args_list = ['vm_name=' + vm_name]
# Only add parameters that are actually specified
if cpu is not None:
args_list.append('cpu=' + str(cpu))
if memory is not None:
args_list.append('memory=' + str(memory))
# Add PCI devices if provided
if pci_list:
# Pass all PCI devices as a comma-separated list
args_list.append('pci=' + ','.join(pci_list))
# Always add start parameter
args_list.append('start=' + str(start))
result = local.cmd(target, 'qcow2.modify_hardware_config', args_list)
format_qcow2_output('Hardware configuration', result)
except Exception as e:
logger.error(f"An error occurred while running qcow2.modify_hardware_config: {e}")
def run_qcow2_create_volume_config(profile, vm_name, size_gb, cpu=None, memory=None, start=False):
"""Create a volume for the VM and optionally configure CPU/memory.
Args:
profile (str): The cloud profile name
vm_name (str): The name of the VM
size_gb (int): Size of the volume in GB
cpu (int, optional): Number of CPUs to assign
memory (int, optional): Amount of memory in MiB
start (bool): Whether to start the VM after configuration
"""
hv_name = profile.split('_')[1]
target = hv_name + "_*"
try:
# Step 1: Create the volume
logger.info(f"Creating {size_gb}GB volume for VM {vm_name}")
volume_result = local.cmd(
target,
'qcow2.create_volume_config',
kwarg={
'vm_name': vm_name,
'size_gb': size_gb,
'start': False # Don't start yet if we need to configure CPU/memory
}
)
format_qcow2_output('Volume creation', volume_result)
# Step 2: Configure CPU and memory if specified
if cpu or memory:
logger.info(f"Configuring hardware for VM {vm_name}: CPU={cpu}, Memory={memory}MiB")
hw_result = local.cmd(
target,
'qcow2.modify_hardware_config',
kwarg={
'vm_name': vm_name,
'cpu': cpu,
'memory': memory,
'start': start
}
)
format_qcow2_output('Hardware configuration', hw_result)
elif start:
# If no CPU/memory config needed but we need to start the VM
logger.info(f"Starting VM {vm_name}")
start_result = local.cmd(
target,
'qcow2.modify_hardware_config',
kwarg={
'vm_name': vm_name,
'start': True
}
)
format_qcow2_output('VM startup', start_result)
except Exception as e:
logger.error(f"An error occurred while creating volume and configuring hardware: {e}")
def run_qcow2_modify_network_config(profile, vm_name, mode, ip=None, gateway=None, dns=None, search_domain=None):
hv_name = profile.split('_')[1]
target = hv_name + "_*"
@@ -586,6 +648,7 @@ def parse_arguments():
network_group.add_argument('-c', '--cpu', type=int, help='Number of virtual CPUs to assign.')
network_group.add_argument('-m', '--memory', type=int, help='Amount of memory to assign in MiB.')
network_group.add_argument('-P', '--pci', action='append', help='PCI hardware ID(s) to passthrough to the VM (e.g., 0000:c7:00.0). Can be specified multiple times.')
network_group.add_argument('--nsm-size', type=int, help='Size in GB for NSM volume creation. Can be used with copper/sfp NICs (--pci). Only disk passthrough (without --nsm-size) prevents volume creation.')
args = parser.parse_args()
@@ -621,6 +684,8 @@ def main():
hw_config.append(f"{args.memory}MB RAM")
if args.pci:
hw_config.append(f"PCI devices: {', '.join(args.pci)}")
if args.nsm_size:
hw_config.append(f"NSM volume: {args.nsm_size}GB")
hw_string = f" and hardware config: {', '.join(hw_config)}" if hw_config else ""
logger.info(f"Received request to create VM '{args.vm_name}' using profile '{args.profile}' {network_config}{hw_string}")
@@ -643,8 +708,58 @@ def main():
# Step 2: Provision the VM (without starting it)
call_salt_cloud(args.profile, args.vm_name)
# Step 3: Modify hardware configuration
run_qcow2_modify_hardware_config(args.profile, args.vm_name, cpu=args.cpu, memory=args.memory, pci_list=args.pci, start=True)
# Step 3: Determine storage configuration approach
# Priority: disk passthrough > volume creation (but volume can coexist with copper/sfp NICs)
# Note: virtual_node_manager.py already filters out --nsm-size when disk is present,
# so if both --pci and --nsm-size are present here, the PCI devices are copper/sfp NICs
use_passthrough = False
use_volume_creation = False
has_nic_passthrough = False
if args.nsm_size:
# Validate nsm_size
if args.nsm_size <= 0:
logger.error(f"Invalid nsm_size value: {args.nsm_size}. Must be a positive integer.")
sys.exit(1)
use_volume_creation = True
logger.info(f"Using volume creation with size {args.nsm_size}GB (--nsm-size parameter specified)")
if args.pci:
# If both nsm_size and PCI are present, PCI devices are copper/sfp NICs
# (virtual_node_manager.py filters out nsm_size when disk is present)
has_nic_passthrough = True
logger.info(f"PCI devices (copper/sfp NICs) will be passed through along with volume: {', '.join(args.pci)}")
elif args.pci:
# Only PCI devices, no nsm_size - could be disk or NICs
# this script is called by virtual_node_manager and that strips any possibility that nsm_size and the disk pci slot is sent to this script
# we might have not specified a disk passthrough or nsm_size, but pass another pci slot and we end up here
use_passthrough = True
logger.info(f"Configuring PCI device passthrough.(--pci parameter specified without --nsm-size)")
# Step 4: Configure hardware based on storage approach
if use_volume_creation:
# Create volume first
run_qcow2_create_volume_config(args.profile, args.vm_name, size_gb=args.nsm_size, cpu=args.cpu, memory=args.memory, start=False)
# Then configure NICs if present
if has_nic_passthrough:
logger.info(f"Configuring NIC passthrough for VM {args.vm_name}")
run_qcow2_modify_hardware_config(args.profile, args.vm_name, cpu=None, memory=None, pci_list=args.pci, start=True)
else:
# No NICs, just start the VM
logger.info(f"Starting VM {args.vm_name}")
run_qcow2_modify_hardware_config(args.profile, args.vm_name, cpu=None, memory=None, pci_list=None, start=True)
elif use_passthrough:
# Use existing passthrough logic via modify_hardware_config
run_qcow2_modify_hardware_config(args.profile, args.vm_name, cpu=args.cpu, memory=args.memory, pci_list=args.pci, start=True)
else:
# No storage configuration, just configure CPU/memory if specified
if args.cpu or args.memory:
run_qcow2_modify_hardware_config(args.profile, args.vm_name, cpu=args.cpu, memory=args.memory, pci_list=None, start=True)
else:
# No hardware configuration needed, just start the VM
logger.info(f"No hardware configuration specified, starting VM {args.vm_name}")
run_qcow2_modify_hardware_config(args.profile, args.vm_name, cpu=None, memory=None, pci_list=None, start=True)
except KeyboardInterrupt:
logger.error("so-salt-cloud: Operation cancelled by user.")

View File

@@ -196,19 +196,23 @@ http {
}
location / {
auth_request /auth/sessions/whoami;
auth_request_set $userid $upstream_http_x_kratos_authenticated_identity_id;
proxy_set_header x-user-id $userid;
proxy_pass http://{{ GLOBALS.manager }}:9822/;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Proxy "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
auth_request /auth/sessions/whoami;
auth_request_set $userid $upstream_http_x_kratos_authenticated_identity_id;
proxy_set_header x-user-id $userid;
proxy_pass http://{{ GLOBALS.manager }}:9822/;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Proxy "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
proxy_request_buffering off;
}
location ~ ^/auth/.*?(login|oidc/callback) {

View File

@@ -1,7 +1,7 @@
# NTP server list
{%- for SERVER in NTPCONFIG.servers %}
server {{ SERVER }} iburst
server {{ SERVER }} iburst maxpoll 10
{%- endfor %}
# Config options
@@ -9,3 +9,5 @@ driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chrony
port 0
cmdport 0

View File

@@ -8,12 +8,9 @@
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% from "pcap/config.map.jinja" import PCAPMERGED %}
{% from 'bpf/pcap.map.jinja' import PCAPBPF %}
{% set BPF_COMPILED = "" %}
{% from 'bpf/pcap.map.jinja' import PCAPBPF, PCAP_BPF_STATUS, PCAP_BPF_CALC, STENO_BPF_COMPILED %}
# PCAP Section
stenographergroup:
group.present:
- name: stenographer
@@ -40,18 +37,12 @@ pcap_sbin:
- group: 939
- file_mode: 755
{% if PCAPBPF %}
{% set BPF_CALC = salt['cmd.script']('salt://common/tools/sbin/so-bpf-compile', GLOBALS.sensor.interface + ' ' + PCAPBPF|join(" "),cwd='/root') %}
{% if BPF_CALC['stderr'] == "" %}
{% set BPF_COMPILED = ",\\\"--filter=" + BPF_CALC['stdout'] + "\\\"" %}
{% else %}
bpfcompilationfailure:
{% if PCAPBPF and not PCAP_BPF_STATUS %}
stenoPCAPbpfcompilationfailure:
test.configurable_test_state:
- changes: False
- result: False
- comment: "BPF Compilation Failed - Discarding Specified BPF"
{% endif %}
- comment: "BPF Syntax Error - Discarding Specified BPF. Error: {{ PCAP_BPF_CALC['stderr'] }}"
{% endif %}
stenoconf:
@@ -64,7 +55,7 @@ stenoconf:
- template: jinja
- defaults:
PCAPMERGED: {{ PCAPMERGED }}
BPF_COMPILED: "{{ BPF_COMPILED }}"
STENO_BPF_COMPILED: "{{ STENO_BPF_COMPILED }}"
stenoca:
file.directory:

View File

@@ -6,6 +6,6 @@
, "Interface": "{{ pillar.sensor.interface }}"
, "Port": 1234
, "Host": "127.0.0.1"
, "Flags": ["-v", "--blocks={{ PCAPMERGED.config.blocks }}", "--preallocate_file_mb={{ PCAPMERGED.config.preallocate_file_mb }}", "--aiops={{ PCAPMERGED.config.aiops }}", "--uid=stenographer", "--gid=stenographer"{{ BPF_COMPILED }}]
, "Flags": ["-v", "--blocks={{ PCAPMERGED.config.blocks }}", "--preallocate_file_mb={{ PCAPMERGED.config.preallocate_file_mb }}", "--aiops={{ PCAPMERGED.config.aiops }}", "--uid=stenographer", "--gid=stenographer"{{ STENO_BPF_COMPILED }}]
, "CertPath": "/etc/stenographer/certs"
}

Some files were not shown because too many files have changed in this diff Show More