Compare commits
103 Commits
reyesj2-pa
...
2.3.170-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
116a6a0acd | ||
|
|
311b69dc4a | ||
|
|
fd59acce5d | ||
|
|
956d3e4345 | ||
|
|
b8355b3a03 | ||
|
|
535b9f86db | ||
|
|
97c66a5404 | ||
|
|
6553beec99 | ||
|
|
e171dd52b8 | ||
|
|
27a837369d | ||
|
|
043b9f78e2 | ||
|
|
2f260a785f | ||
|
|
001b2dc6cc | ||
|
|
b13eedfbc2 | ||
|
|
dd70ef17b9 | ||
|
|
82dff3e9da | ||
|
|
d9cfd92b8f | ||
|
|
33cb771780 | ||
|
|
76cca8594d | ||
|
|
5c9c95ba1f | ||
|
|
e62bebeafe | ||
|
|
8a0e92cc6f | ||
|
|
3f9259dd0a | ||
|
|
30b9868de1 | ||
|
|
e88243c306 | ||
|
|
2128550df2 | ||
|
|
db67c0ed94 | ||
|
|
2e32c0d236 | ||
|
|
4b1ad1910d | ||
|
|
c337145b2c | ||
|
|
bd7b4c92bc | ||
|
|
33ebed3468 | ||
|
|
616bc40412 | ||
|
|
f00d9074ff | ||
|
|
9a692288e2 | ||
|
|
fea2b481e3 | ||
|
|
c17f0081ef | ||
|
|
fbf0803906 | ||
|
|
5deda45b66 | ||
|
|
3b8d8163b3 | ||
|
|
2dfd41bd3c | ||
|
|
49eead1d55 | ||
|
|
54cb3c3a5a | ||
|
|
9f2b920454 | ||
|
|
604af45661 | ||
|
|
3f435c5c1a | ||
|
|
7769af4541 | ||
|
|
9903be8120 | ||
|
|
991a601a3d | ||
|
|
86519d43dc | ||
|
|
179f669acf | ||
|
|
a02f878dcc | ||
|
|
32c29b28eb | ||
|
|
7bf2603414 | ||
|
|
4003876465 | ||
|
|
4c677961c4 | ||
|
|
e950d865d8 | ||
|
|
fd7a118664 | ||
|
|
d7906945df | ||
|
|
cb384ae024 | ||
|
|
7caead2387 | ||
|
|
4827c9e0d4 | ||
|
|
3b62fc63c9 | ||
|
|
ad32c2b1a5 | ||
|
|
f02f431dab | ||
|
|
812964e4d8 | ||
|
|
99805cc326 | ||
|
|
8d2b3f3dfe | ||
|
|
15f7fd8920 | ||
|
|
50460bf91e | ||
|
|
ee654f767a | ||
|
|
8c694a7ca3 | ||
|
|
9ac640fa67 | ||
|
|
db8d9fff2c | ||
|
|
811063268f | ||
|
|
f2b10a5a86 | ||
|
|
c69cac0e5f | ||
|
|
fed4433088 | ||
|
|
839cfcaefa | ||
|
|
3123407ef0 | ||
|
|
d24125c9e6 | ||
|
|
64dc278c95 | ||
|
|
626a824cd6 | ||
|
|
10ba3b4b5a | ||
|
|
1d059fc96e | ||
|
|
4c1585f8d8 | ||
|
|
e235957c00 | ||
|
|
2cc665bac6 | ||
|
|
d6e118dcd3 | ||
|
|
1d2534b2a1 | ||
|
|
484aa7b207 | ||
|
|
6986448239 | ||
|
|
f1d74dcd67 | ||
|
|
dd48d66c1c | ||
|
|
440f4e75c1 | ||
|
|
c795a70e9c | ||
|
|
340dbe8547 | ||
|
|
52a5e743e9 | ||
|
|
5ceff52796 | ||
|
|
f3a0ab0b2d | ||
|
|
4a7c994b66 | ||
|
|
07b8785f3d | ||
|
|
9a1092ab01 |
6
.github/.gitleaks.toml
vendored
@@ -536,11 +536,11 @@ secretGroup = 4
|
||||
|
||||
[allowlist]
|
||||
description = "global allow lists"
|
||||
regexes = ['''219-09-9999''', '''078-05-1120''', '''(9[0-9]{2}|666)-\d{2}-\d{4}''', '''RPM-GPG-KEY.*''', '''.*:.*StrelkaHexDump.*''', '''.*:.*PLACEHOLDER.*''', '''ssl_.*password''', '''integration_key\s=\s"so-logs-"''']
|
||||
regexes = ['''219-09-9999''', '''078-05-1120''', '''(9[0-9]{2}|666)-\d{2}-\d{4}''']
|
||||
paths = [
|
||||
'''gitleaks.toml''',
|
||||
'''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$''',
|
||||
'''(go.mod|go.sum)$''',
|
||||
'''salt/nginx/files/enterprise-attack.json''',
|
||||
'''(.*?)whl$'''
|
||||
|
||||
'''salt/nginx/files/enterprise-attack.json'''
|
||||
]
|
||||
|
||||
202
.github/DISCUSSION_TEMPLATE/2-4.yml
vendored
@@ -1,202 +0,0 @@
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠️ This category is solely for conversations related to Security Onion 2.4 ⚠️
|
||||
|
||||
If your organization needs more immediate, enterprise grade professional support, with one-on-one virtual meetings and screensharing, contact us via our website: https://securityonion.com/support
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version of Security Onion 2.4.x are you asking about?
|
||||
options:
|
||||
-
|
||||
- 2.4.10
|
||||
- 2.4.20
|
||||
- 2.4.30
|
||||
- 2.4.40
|
||||
- 2.4.50
|
||||
- 2.4.60
|
||||
- 2.4.70
|
||||
- 2.4.80
|
||||
- 2.4.90
|
||||
- 2.4.100
|
||||
- 2.4.110
|
||||
- 2.4.111
|
||||
- 2.4.120
|
||||
- 2.4.130
|
||||
- 2.4.140
|
||||
- 2.4.141
|
||||
- 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
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Installation Method
|
||||
description: How did you install Security Onion?
|
||||
options:
|
||||
-
|
||||
- Security Onion ISO image
|
||||
- Cloud image (Amazon, Azure, Google)
|
||||
- Network installation on Red Hat derivative like Oracle, Rocky, Alma, etc. (unsupported)
|
||||
- Network installation on Ubuntu (unsupported)
|
||||
- Network installation on Debian (unsupported)
|
||||
- Other (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Description
|
||||
description: >
|
||||
Is this discussion about installation, configuration, upgrading, or other?
|
||||
options:
|
||||
-
|
||||
- installation
|
||||
- configuration
|
||||
- upgrading
|
||||
- other (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Installation Type
|
||||
description: >
|
||||
When you installed, did you choose Import, Eval, Standalone, Distributed, or something else?
|
||||
options:
|
||||
-
|
||||
- Import
|
||||
- Eval
|
||||
- Standalone
|
||||
- Distributed
|
||||
- other (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Location
|
||||
description: >
|
||||
Is this deployment in the cloud, on-prem with Internet access, or airgap?
|
||||
options:
|
||||
-
|
||||
- cloud
|
||||
- on-prem with Internet access
|
||||
- airgap
|
||||
- other (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Hardware Specs
|
||||
description: >
|
||||
Does your hardware meet or exceed the minimum requirements for your installation type as shown at https://docs.securityonion.net/en/2.4/hardware.html?
|
||||
options:
|
||||
-
|
||||
- Meets minimum requirements
|
||||
- Exceeds minimum requirements
|
||||
- Does not meet minimum requirements
|
||||
- other (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: CPU
|
||||
description: How many CPU cores do you have?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: RAM
|
||||
description: How much RAM do you have?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Storage for /
|
||||
description: How much storage do you have for the / partition?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Storage for /nsm
|
||||
description: How much storage do you have for the /nsm partition?
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Network Traffic Collection
|
||||
description: >
|
||||
Are you collecting network traffic from a tap or span port?
|
||||
options:
|
||||
-
|
||||
- tap
|
||||
- span port
|
||||
- other (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Network Traffic Speeds
|
||||
description: >
|
||||
How much network traffic are you monitoring?
|
||||
options:
|
||||
-
|
||||
- Less than 1Gbps
|
||||
- 1Gbps to 10Gbps
|
||||
- more than 10Gbps
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Status
|
||||
description: >
|
||||
Does SOC Grid show all services on all nodes as running OK?
|
||||
options:
|
||||
-
|
||||
- Yes, all services on all nodes are running OK
|
||||
- No, one or more services are failed (please provide detail below)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Salt Status
|
||||
description: >
|
||||
Do you get any failures when you run "sudo salt-call state.highstate"?
|
||||
options:
|
||||
-
|
||||
- Yes, there are salt failures (please provide detail below)
|
||||
- No, there are no failures
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Logs
|
||||
description: >
|
||||
Are there any additional clues in /opt/so/log/?
|
||||
options:
|
||||
-
|
||||
- Yes, there are additional clues in /opt/so/log/ (please provide detail below)
|
||||
- No, there are no additional clues
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Detail
|
||||
description: Please read our discussion guidelines at https://github.com/Security-Onion-Solutions/securityonion/discussions/1720 and then provide detailed information to help us help you.
|
||||
placeholder: |-
|
||||
STOP! Before typing, please read our discussion guidelines at https://github.com/Security-Onion-Solutions/securityonion/discussions/1720 in their entirety!
|
||||
|
||||
If your organization needs more immediate, enterprise grade professional support, with one-on-one virtual meetings and screensharing, contact us via our website: https://securityonion.com/support
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Guidelines
|
||||
options:
|
||||
- label: I have read the discussion guidelines at https://github.com/Security-Onion-Solutions/securityonion/discussions/1720 and assert that I have followed the guidelines.
|
||||
required: true
|
||||
12
.github/ISSUE_TEMPLATE
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
PLEASE STOP AND READ THIS INFORMATION!
|
||||
|
||||
If you are creating an issue just to ask a question, you will likely get faster and better responses by posting to our discussions forum instead:
|
||||
https://securityonion.net/discuss
|
||||
|
||||
If you think you have found a possible bug or are observing a behavior that you weren't expecting, use the discussion forum to start a conversation about it instead of creating an issue.
|
||||
|
||||
If you are very familiar with the latest version of the product and are confident you have found a bug in Security Onion, you can continue with creating an issue here, but please make sure you have done the following:
|
||||
- duplicated the issue on a fresh installation of the latest version
|
||||
- provide information about your system and how you installed Security Onion
|
||||
- include relevant log files
|
||||
- include reproduction steps
|
||||
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: This option is for experienced community members to report a confirmed, reproducible bug
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
PLEASE STOP AND READ THIS INFORMATION!
|
||||
|
||||
If you are creating an issue just to ask a question, you will likely get faster and better responses by posting to our discussions forum at https://securityonion.net/discuss.
|
||||
|
||||
If you think you have found a possible bug or are observing a behavior that you weren't expecting, use the discussion forum at https://securityonion.net/discuss to start a conversation about it instead of creating an issue.
|
||||
|
||||
If you are very familiar with the latest version of the product and are confident you have found a bug in Security Onion, you can continue with creating an issue here, but please make sure you have done the following:
|
||||
- duplicated the issue on a fresh installation of the latest version
|
||||
- provide information about your system and how you installed Security Onion
|
||||
- include relevant log files
|
||||
- include reproduction steps
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Security Onion Discussions
|
||||
url: https://securityonion.com/discussions
|
||||
about: Please ask and answer questions here
|
||||
33
.github/workflows/close-threads.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: 'Close Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '50 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: lock-threads
|
||||
|
||||
jobs:
|
||||
close-threads:
|
||||
if: github.repository_owner == 'security-onion-solutions'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: 60
|
||||
stale-issue-message: "This issue is stale because it has been inactive for an extended period. Stale issues convey that the issue, while important to someone, is not critical enough for the author, or other community members to work on, sponsor, or otherwise shepherd the issue through to a resolution."
|
||||
close-issue-message: "This issue was closed because it has been stale for an extended period. It will be automatically locked in 30 days, after which no further commenting will be available."
|
||||
days-before-pr-stale: 45
|
||||
days-before-pr-close: 60
|
||||
stale-pr-message: "This PR is stale because it has been inactive for an extended period. The longer a PR remains stale the more out of date with the main branch it becomes."
|
||||
close-pr-message: "This PR was closed because it has been stale for an extended period. It will be automatically locked in 30 days. If there is still a commitment to finishing this PR re-open it before it is locked."
|
||||
4
.github/workflows/contrib.yml
vendored
@@ -11,14 +11,14 @@ jobs:
|
||||
steps:
|
||||
- name: "Contributor Check"
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
|
||||
uses: cla-assistant/github-action@v2.3.1
|
||||
uses: cla-assistant/github-action@v2.1.3-beta
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
with:
|
||||
path-to-signatures: 'signatures_v1.json'
|
||||
path-to-document: 'https://securityonionsolutions.com/cla'
|
||||
allowlist: dependabot[bot],jertel,dougburks,TOoSmOotH,defensivedepth,m0duspwnens
|
||||
allowlist: dependabot[bot],jertel,dougburks,TOoSmOotH,weslambert,defensivedepth,m0duspwnens
|
||||
remote-organization-name: Security-Onion-Solutions
|
||||
remote-repository-name: licensing
|
||||
|
||||
|
||||
26
.github/workflows/lock-threads.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '50 2 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: lock-threads
|
||||
|
||||
jobs:
|
||||
lock-threads:
|
||||
if: github.repository_owner == 'security-onion-solutions'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: jertel/lock-threads@main
|
||||
with:
|
||||
include-discussion-currently-open: true
|
||||
discussion-inactive-days: 90
|
||||
issue-inactive-days: 30
|
||||
pr-inactive-days: 30
|
||||
12
.github/workflows/pythontest.yml
vendored
@@ -1,10 +1,6 @@
|
||||
name: python-test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "salt/sensoroni/files/analyzers/**"
|
||||
- "salt/manager/tools/sbin"
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -13,8 +9,8 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.13"]
|
||||
python-code-path: ["salt/sensoroni/files/analyzers", "salt/manager/tools/sbin"]
|
||||
python-version: ["3.10"]
|
||||
python-code-path: ["salt/sensoroni/files/analyzers"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -32,4 +28,4 @@ jobs:
|
||||
flake8 ${{ matrix.python-code-path }} --show-source --max-complexity=12 --doctests --max-line-length=200 --statistics
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
PYTHONPATH=${{ matrix.python-code-path }} pytest ${{ matrix.python-code-path }} --cov=${{ matrix.python-code-path }} --doctest-modules --cov-report=term --cov-fail-under=100 --cov-config=pytest.ini
|
||||
pytest ${{ matrix.python-code-path }} --cov=${{ matrix.python-code-path }} --doctest-modules --cov-report=term --cov-fail-under=100 --cov-config=${{ matrix.python-code-path }}/pytest.ini
|
||||
|
||||
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/macos,windows
|
||||
# Edit at https://www.gitignore.io/?templates=macos,windows
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
### 2.4.190-20251024 ISO image released on 2025/10/24
|
||||
|
||||
|
||||
### Download and Verify
|
||||
|
||||
2.4.190-20251024 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.4.190-20251024.iso
|
||||
|
||||
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.190-20251024.iso.sig
|
||||
|
||||
Signing key:
|
||||
https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.4/main/KEYS
|
||||
|
||||
For example, here are the steps you can use on most Linux distributions to download and verify our Security Onion ISO image.
|
||||
|
||||
Download and import the signing key:
|
||||
```
|
||||
wget https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.4/main/KEYS -O - | gpg --import -
|
||||
```
|
||||
|
||||
Download the signature file for the ISO:
|
||||
```
|
||||
wget https://github.com/Security-Onion-Solutions/securityonion/raw/2.4/main/sigs/securityonion-2.4.190-20251024.iso.sig
|
||||
```
|
||||
|
||||
Download the ISO image:
|
||||
```
|
||||
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.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 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.
|
||||
Primary key fingerprint: C804 A93D 36BE 0C73 3EA1 9644 7C10 60B7 FE50 7013
|
||||
```
|
||||
|
||||
If it fails to verify, try downloading again. If it still fails to verify, try downloading from another computer or another network.
|
||||
|
||||
Once you've verified the ISO image, you're ready to proceed to our Installation guide:
|
||||
https://docs.securityonion.net/en/2.4/installation.html
|
||||
53
LICENSE
@@ -1,53 +0,0 @@
|
||||
Elastic License 2.0 (ELv2)
|
||||
|
||||
Acceptance
|
||||
|
||||
By using the software, you agree to all of the terms and conditions below.
|
||||
|
||||
Copyright License
|
||||
|
||||
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below.
|
||||
|
||||
Limitations
|
||||
|
||||
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
|
||||
|
||||
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.
|
||||
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
Patents
|
||||
|
||||
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
||||
|
||||
Notices
|
||||
|
||||
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
|
||||
|
||||
If you modify the software, you must include in any modified copies of the software prominent notices stating that you have modified the software.
|
||||
|
||||
No Other Rights
|
||||
|
||||
These terms do not imply any licenses other than those expressly granted in these terms.
|
||||
|
||||
Termination
|
||||
|
||||
If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your licenses will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently.
|
||||
|
||||
No Liability
|
||||
|
||||
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
|
||||
|
||||
Definitions
|
||||
|
||||
The licensor is the entity offering these terms, and the software is the software the licensor makes available under these terms, including any portion of it.
|
||||
|
||||
you refers to the individual or entity agreeing to these terms.
|
||||
|
||||
your company is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
||||
|
||||
your licenses are all the licenses granted to you for the software under these terms.
|
||||
|
||||
use means anything you do with the software requiring one of your licenses.
|
||||
|
||||
trademark means trademarks, service marks, and similar rights.
|
||||
35
README.md
@@ -1,50 +1,41 @@
|
||||
## Security Onion 2.4
|
||||
## Security Onion 2.3.170
|
||||
|
||||
Security Onion 2.4 is here!
|
||||
Security Onion 2.3.170 is here!
|
||||
|
||||
## Screenshots
|
||||
|
||||
Alerts
|
||||

|
||||

|
||||
|
||||
Dashboards
|
||||

|
||||

|
||||
|
||||
Hunt
|
||||

|
||||

|
||||
|
||||
Detections
|
||||

|
||||
|
||||
PCAP
|
||||

|
||||
|
||||
Grid
|
||||

|
||||
|
||||
Config
|
||||

|
||||
Cases
|
||||

|
||||
|
||||
### Release Notes
|
||||
|
||||
https://docs.securityonion.net/en/2.4/release-notes.html
|
||||
https://docs.securityonion.net/en/2.3/release-notes.html
|
||||
|
||||
### Requirements
|
||||
|
||||
https://docs.securityonion.net/en/2.4/hardware.html
|
||||
https://docs.securityonion.net/en/2.3/hardware.html
|
||||
|
||||
### Download
|
||||
|
||||
https://docs.securityonion.net/en/2.4/download.html
|
||||
https://docs.securityonion.net/en/2.3/download.html
|
||||
|
||||
### Installation
|
||||
|
||||
https://docs.securityonion.net/en/2.4/installation.html
|
||||
https://docs.securityonion.net/en/2.3/installation.html
|
||||
|
||||
### FAQ
|
||||
|
||||
https://docs.securityonion.net/en/2.4/faq.html
|
||||
https://docs.securityonion.net/en/2.3/faq.html
|
||||
|
||||
### Feedback
|
||||
|
||||
https://docs.securityonion.net/en/2.4/community-support.html
|
||||
https://docs.securityonion.net/en/2.3/community-support.html
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.4.x | :white_check_mark: |
|
||||
| 2.3.x | :x: |
|
||||
| 2.x.x | :white_check_mark: |
|
||||
| 16.04.x | :x: |
|
||||
|
||||
Security Onion 2.3 has reached End Of Life and is no longer supported.
|
||||
|
||||
Security Onion 16.04 has reached End Of Life and is no longer supported.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
52
VERIFY_ISO.md
Normal file
@@ -0,0 +1,52 @@
|
||||
### 2.3.170-20220922 ISO image built on 2022/09/22
|
||||
|
||||
|
||||
|
||||
### Download and Verify
|
||||
|
||||
2.3.170-20220922 ISO image:
|
||||
https://download.securityonion.net/file/securityonion/securityonion-2.3.170-20220922.iso
|
||||
|
||||
MD5: B45E38F72500CF302AE7CB3A87B3DB4C
|
||||
SHA1: 06EC41B4B7E55453389952BE91B20AA465E18F33
|
||||
SHA256: 634A2E88250DC7583705360EB5AD966D282FAE77AFFAF81676CB6D66D7950A3E
|
||||
|
||||
Signature for ISO image:
|
||||
https://github.com/Security-Onion-Solutions/securityonion/raw/master/sigs/securityonion-2.3.170-20220922.iso.sig
|
||||
|
||||
Signing key:
|
||||
https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/master/KEYS
|
||||
|
||||
For example, here are the steps you can use on most Linux distributions to download and verify our Security Onion ISO image.
|
||||
|
||||
Download and import the signing key:
|
||||
```
|
||||
wget https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/master/KEYS -O - | gpg --import -
|
||||
```
|
||||
|
||||
Download the signature file for the ISO:
|
||||
```
|
||||
wget https://github.com/Security-Onion-Solutions/securityonion/raw/master/sigs/securityonion-2.3.170-20220922.iso.sig
|
||||
```
|
||||
|
||||
Download the ISO image:
|
||||
```
|
||||
wget https://download.securityonion.net/file/securityonion/securityonion-2.3.170-20220922.iso
|
||||
```
|
||||
|
||||
Verify the downloaded ISO image using the signature file:
|
||||
```
|
||||
gpg --verify securityonion-2.3.170-20220922.iso.sig securityonion-2.3.170-20220922.iso
|
||||
```
|
||||
|
||||
The output should show "Good signature" and the Primary key fingerprint should match what's shown below:
|
||||
```
|
||||
gpg: Signature made Thu 22 Sep 2022 11:48:42 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.
|
||||
Primary key fingerprint: C804 A93D 36BE 0C73 3EA1 9644 7C10 60B7 FE50 7013
|
||||
```
|
||||
|
||||
Once you've verified the ISO image, you're ready to proceed to our Installation guide:
|
||||
https://docs.securityonion.net/en/2.3/installation.html
|
||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,8 +1,8 @@
|
||||
{% import_yaml 'firewall/ports/ports.yaml' as default_portgroups %}
|
||||
{% set default_portgroups = default_portgroups.firewall.ports %}
|
||||
{% import_yaml 'firewall/ports/ports.local.yaml' as local_portgroups %}
|
||||
{% if local_portgroups.firewall.ports %}
|
||||
{% set local_portgroups = local_portgroups.firewall.ports %}
|
||||
{% import_yaml 'firewall/portgroups.yaml' as default_portgroups %}
|
||||
{% set default_portgroups = default_portgroups.firewall.aliases.ports %}
|
||||
{% import_yaml 'firewall/portgroups.local.yaml' as local_portgroups %}
|
||||
{% if local_portgroups.firewall.aliases.ports %}
|
||||
{% set local_portgroups = local_portgroups.firewall.aliases.ports %}
|
||||
{% else %}
|
||||
{% set local_portgroups = {} %}
|
||||
{% endif %}
|
||||
@@ -12,6 +12,7 @@ role:
|
||||
eval:
|
||||
fleet:
|
||||
heavynode:
|
||||
helixsensor:
|
||||
idh:
|
||||
import:
|
||||
manager:
|
||||
|
||||
82
files/firewall/hostgroups.local.yaml
Normal file
@@ -0,0 +1,82 @@
|
||||
firewall:
|
||||
hostgroups:
|
||||
analyst:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
beats_endpoint:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
beats_endpoint_ssl:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
elasticsearch_rest:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
endgame:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
fleet:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
heavy_node:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
idh:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
manager:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
minion:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
node:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
osquery_endpoint:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
receiver:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
search_node:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
sensor:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
strelka_frontend:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
syslog:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
wazuh_agent:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
wazuh_api:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
wazuh_authd:
|
||||
ips:
|
||||
delete:
|
||||
insert:
|
||||
3
files/firewall/portgroups.local.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
firewall:
|
||||
aliases:
|
||||
ports:
|
||||
@@ -1,2 +0,0 @@
|
||||
firewall:
|
||||
ports:
|
||||
@@ -41,8 +41,7 @@ file_roots:
|
||||
base:
|
||||
- /opt/so/saltstack/local/salt
|
||||
- /opt/so/saltstack/default/salt
|
||||
- /nsm/elastic-fleet/artifacts
|
||||
- /opt/so/rules/nids
|
||||
|
||||
|
||||
# The master_roots setting configures a master-only copy of the file_roots dictionary,
|
||||
# used by the state compiler.
|
||||
@@ -65,4 +64,10 @@ peer:
|
||||
.*:
|
||||
- x509.sign_remote_certificate
|
||||
|
||||
reactor:
|
||||
- 'so/fleet':
|
||||
- salt://reactor/fleet.sls
|
||||
- 'salt/beacon/*/watch_sqlite_db//opt/so/conf/kratos/db/sqlite.db':
|
||||
- salt://reactor/kratos.sls
|
||||
|
||||
|
||||
|
||||
@@ -45,10 +45,12 @@ echo " rootfs: $ROOTFS" >> $local_salt_dir/pillar/data/$TYPE.sls
|
||||
echo " nsmfs: $NSM" >> $local_salt_dir/pillar/data/$TYPE.sls
|
||||
if [ $TYPE == 'sensorstab' ]; then
|
||||
echo " monint: bond0" >> $local_salt_dir/pillar/data/$TYPE.sls
|
||||
salt-call state.apply grafana queue=True
|
||||
fi
|
||||
if [ $TYPE == 'evaltab' ] || [ $TYPE == 'standalonetab' ]; then
|
||||
echo " monint: bond0" >> $local_salt_dir/pillar/data/$TYPE.sls
|
||||
if [ ! $10 ]; then
|
||||
salt-call state.apply grafana queue=True
|
||||
salt-call state.apply utility queue=True
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{% set node_types = {} %}
|
||||
{% for minionid, ip in salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='elasticsearch:enabled:true',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='pillar') | dictsort()
|
||||
%}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% set hostname = minionid.split('_') | first %}
|
||||
{% set node_type = minionid.split('_') | last %}
|
||||
{% if node_type not in node_types.keys() %}
|
||||
{% do node_types.update({node_type: {hostname: ip[0]}}) %}
|
||||
{% else %}
|
||||
{% if hostname not in node_types[node_type] %}
|
||||
{% do node_types[node_type].update({hostname: ip[0]}) %}
|
||||
{% else %}
|
||||
{% do node_types[node_type][hostname].update(ip[0]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
elasticsearch:
|
||||
nodes:
|
||||
{% for node_type, values in node_types.items() %}
|
||||
{{node_type}}:
|
||||
{% for hostname, ip in values.items() %}
|
||||
{{hostname}}:
|
||||
ip: {{ip}}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -1,34 +0,0 @@
|
||||
{% set node_types = {} %}
|
||||
{% for minionid, ip in salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='G@role:so-hypervisor or G@role:so-managerhype',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='compound') | dictsort()
|
||||
%}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% set hostname = minionid.split('_') | first %}
|
||||
{% set node_type = minionid.split('_') | last %}
|
||||
{% if node_type not in node_types.keys() %}
|
||||
{% do node_types.update({node_type: {hostname: ip[0]}}) %}
|
||||
{% else %}
|
||||
{% if hostname not in node_types[node_type] %}
|
||||
{% do node_types[node_type].update({hostname: ip[0]}) %}
|
||||
{% else %}
|
||||
{% do node_types[node_type][hostname].update(ip[0]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
hypervisor:
|
||||
nodes:
|
||||
{% for node_type, values in node_types.items() %}
|
||||
{{node_type}}:
|
||||
{% for hostname, ip in values.items() %}
|
||||
{{hostname}}:
|
||||
ip: {{ip}}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -1,2 +0,0 @@
|
||||
kafka:
|
||||
nodes:
|
||||
13
pillar/logrotate/init.sls
Normal file
@@ -0,0 +1,13 @@
|
||||
logrotate:
|
||||
conf: |
|
||||
daily
|
||||
rotate 14
|
||||
missingok
|
||||
copytruncate
|
||||
compress
|
||||
create
|
||||
extension .log
|
||||
dateext
|
||||
dateyesterday
|
||||
group_conf: |
|
||||
su root socore
|
||||
42
pillar/logstash/helix.sls
Normal file
@@ -0,0 +1,42 @@
|
||||
logstash:
|
||||
pipelines:
|
||||
helix:
|
||||
config:
|
||||
- so/0010_input_hhbeats.conf
|
||||
- so/1033_preprocess_snort.conf
|
||||
- so/1100_preprocess_bro_conn.conf
|
||||
- so/1101_preprocess_bro_dhcp.conf
|
||||
- so/1102_preprocess_bro_dns.conf
|
||||
- so/1103_preprocess_bro_dpd.conf
|
||||
- so/1104_preprocess_bro_files.conf
|
||||
- so/1105_preprocess_bro_ftp.conf
|
||||
- so/1106_preprocess_bro_http.conf
|
||||
- so/1107_preprocess_bro_irc.conf
|
||||
- so/1108_preprocess_bro_kerberos.conf
|
||||
- so/1109_preprocess_bro_notice.conf
|
||||
- so/1110_preprocess_bro_rdp.conf
|
||||
- so/1111_preprocess_bro_signatures.conf
|
||||
- so/1112_preprocess_bro_smtp.conf
|
||||
- so/1113_preprocess_bro_snmp.conf
|
||||
- so/1114_preprocess_bro_software.conf
|
||||
- so/1115_preprocess_bro_ssh.conf
|
||||
- so/1116_preprocess_bro_ssl.conf
|
||||
- so/1117_preprocess_bro_syslog.conf
|
||||
- so/1118_preprocess_bro_tunnel.conf
|
||||
- so/1119_preprocess_bro_weird.conf
|
||||
- so/1121_preprocess_bro_mysql.conf
|
||||
- so/1122_preprocess_bro_socks.conf
|
||||
- so/1123_preprocess_bro_x509.conf
|
||||
- so/1124_preprocess_bro_intel.conf
|
||||
- so/1125_preprocess_bro_modbus.conf
|
||||
- so/1126_preprocess_bro_sip.conf
|
||||
- so/1127_preprocess_bro_radius.conf
|
||||
- so/1128_preprocess_bro_pe.conf
|
||||
- so/1129_preprocess_bro_rfb.conf
|
||||
- so/1130_preprocess_bro_dnp3.conf
|
||||
- so/1131_preprocess_bro_smb_files.conf
|
||||
- so/1132_preprocess_bro_smb_mapping.conf
|
||||
- so/1133_preprocess_bro_ntlm.conf
|
||||
- so/1134_preprocess_bro_dce_rpc.conf
|
||||
- so/8001_postprocess_common_ip_augmentation.conf
|
||||
- so/9997_output_helix.conf.jinja
|
||||
@@ -3,8 +3,6 @@ logstash:
|
||||
port_bindings:
|
||||
- 0.0.0.0:3765:3765
|
||||
- 0.0.0.0:5044:5044
|
||||
- 0.0.0.0:5055:5055
|
||||
- 0.0.0.0:5056:5056
|
||||
- 0.0.0.0:5644:5644
|
||||
- 0.0.0.0:6050:6050
|
||||
- 0.0.0.0:6051:6051
|
||||
|
||||
9
pillar/logstash/manager.sls
Normal file
@@ -0,0 +1,9 @@
|
||||
logstash:
|
||||
pipelines:
|
||||
manager:
|
||||
config:
|
||||
- so/0009_input_beats.conf
|
||||
- so/0010_input_hhbeats.conf
|
||||
- so/0011_input_endgame.conf
|
||||
- so/9999_output_redis.conf.jinja
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
{% set node_types = {} %}
|
||||
{% set cached_grains = salt.saltutil.runner('cache.grains', tgt='*') %}
|
||||
{% for minionid, ip in salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='logstash:enabled:true',
|
||||
tgt='G@role:so-manager or G@role:so-managersearch or G@role:so-standalone or G@role:so-node or G@role:so-heavynode or G@role:so-receiver or G@role:so-helix',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='pillar') | dictsort()
|
||||
tgt_type='compound') | dictsort()
|
||||
%}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% set hostname = minionid.split('_') | first %}
|
||||
{% set node_type = minionid.split('_') | last %}
|
||||
{% set hostname = cached_grains[minionid]['host'] %}
|
||||
{% set node_type = minionid.split('_')[1] %}
|
||||
{% if node_type not in node_types.keys() %}
|
||||
{% do node_types.update({node_type: {hostname: ip[0]}}) %}
|
||||
{% else %}
|
||||
@@ -19,10 +18,8 @@
|
||||
{% do node_types[node_type][hostname].update(ip[0]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
logstash:
|
||||
nodes:
|
||||
{% for node_type, values in node_types.items() %}
|
||||
|
||||
9
pillar/logstash/receiver.sls
Normal file
@@ -0,0 +1,9 @@
|
||||
logstash:
|
||||
pipelines:
|
||||
receiver:
|
||||
config:
|
||||
- so/0009_input_beats.conf
|
||||
- so/0010_input_hhbeats.conf
|
||||
- so/0011_input_endgame.conf
|
||||
- so/9999_output_redis.conf.jinja
|
||||
|
||||
18
pillar/logstash/search.sls
Normal file
@@ -0,0 +1,18 @@
|
||||
logstash:
|
||||
pipelines:
|
||||
search:
|
||||
config:
|
||||
- so/0900_input_redis.conf.jinja
|
||||
- so/9000_output_zeek.conf.jinja
|
||||
- so/9002_output_import.conf.jinja
|
||||
- so/9034_output_syslog.conf.jinja
|
||||
- so/9050_output_filebeatmodules.conf.jinja
|
||||
- so/9100_output_osquery.conf.jinja
|
||||
- so/9400_output_suricata.conf.jinja
|
||||
- so/9500_output_beats.conf.jinja
|
||||
- so/9600_output_ossec.conf.jinja
|
||||
- so/9700_output_strelka.conf.jinja
|
||||
- so/9800_output_logscan.conf.jinja
|
||||
- so/9801_output_rita.conf.jinja
|
||||
- so/9802_output_kratos.conf.jinja
|
||||
- so/9900_output_endgame.conf.jinja
|
||||
@@ -1,12 +1,11 @@
|
||||
{% set node_types = {} %}
|
||||
{% set manage_alived = salt.saltutil.runner('manage.alived', show_ip=True) %}
|
||||
{% set manager = grains.master %}
|
||||
{% set manager_type = manager.split('_')|last %}
|
||||
{% for minionid, ip in salt.saltutil.runner('mine.get', tgt='*', fun='network.ip_addrs', tgt_type='glob') | dictsort() %}
|
||||
{% set hostname = minionid.split('_')[0] %}
|
||||
{% set node_type = minionid.split('_')[1] %}
|
||||
{% set is_alive = False %}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% if minionid in manage_alived.keys() %}
|
||||
{% if ip[0] == manage_alived[minionid] %}
|
||||
{% set is_alive = True %}
|
||||
@@ -21,19 +20,14 @@
|
||||
{% do node_types[node_type][hostname].update({'ip':ip[0], 'alive':is_alive}) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if node_types %}
|
||||
node_data:
|
||||
{% for node_type, host_values in node_types.items() %}
|
||||
{{node_type}}:
|
||||
{% for hostname, details in host_values.items() %}
|
||||
{{hostname}}:
|
||||
ip: {{details.ip}}
|
||||
alive: {{ details.alive }}
|
||||
role: {{node_type}}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
node_data: False
|
||||
{% endif %}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{% set node_types = {} %}
|
||||
{% for minionid, ip in salt.saltutil.runner(
|
||||
'mine.get',
|
||||
tgt='redis:enabled:true',
|
||||
fun='network.ip_addrs',
|
||||
tgt_type='pillar') | dictsort()
|
||||
%}
|
||||
|
||||
# only add a node to the pillar if it returned an ip from the mine
|
||||
{% if ip | length > 0%}
|
||||
{% set hostname = minionid.split('_') | first %}
|
||||
{% set node_type = minionid.split('_') | last %}
|
||||
{% if node_type not in node_types.keys() %}
|
||||
{% do node_types.update({node_type: {hostname: ip[0]}}) %}
|
||||
{% else %}
|
||||
{% if hostname not in node_types[node_type] %}
|
||||
{% do node_types[node_type].update({hostname: ip[0]}) %}
|
||||
{% else %}
|
||||
{% do node_types[node_type][hostname].update(ip[0]) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
redis:
|
||||
nodes:
|
||||
{% for node_type, values in node_types.items() %}
|
||||
{{node_type}}:
|
||||
{% for hostname, ip in values.items() %}
|
||||
{{hostname}}:
|
||||
ip: {{ip}}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
@@ -1,14 +0,0 @@
|
||||
# Copyright Jason Ertel (github.com/jertel).
|
||||
# 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."
|
||||
|
||||
# This file is generated by Security Onion and contains a list of license-enabled features.
|
||||
features: []
|
||||
44
pillar/thresholding/pillar.example
Normal file
@@ -0,0 +1,44 @@
|
||||
thresholding:
|
||||
sids:
|
||||
8675309:
|
||||
- threshold:
|
||||
gen_id: 1
|
||||
type: threshold
|
||||
track: by_src
|
||||
count: 10
|
||||
seconds: 10
|
||||
- threshold:
|
||||
gen_id: 1
|
||||
type: limit
|
||||
track: by_dst
|
||||
count: 100
|
||||
seconds: 30
|
||||
- rate_filter:
|
||||
gen_id: 1
|
||||
track: by_rule
|
||||
count: 50
|
||||
seconds: 30
|
||||
new_action: alert
|
||||
timeout: 30
|
||||
- suppress:
|
||||
gen_id: 1
|
||||
track: by_either
|
||||
ip: 10.10.3.7
|
||||
11223344:
|
||||
- threshold:
|
||||
gen_id: 1
|
||||
type: limit
|
||||
track: by_dst
|
||||
count: 10
|
||||
seconds: 10
|
||||
- rate_filter:
|
||||
gen_id: 1
|
||||
track: by_src
|
||||
count: 50
|
||||
seconds: 20
|
||||
new_action: pass
|
||||
timeout: 60
|
||||
- suppress:
|
||||
gen_id: 1
|
||||
track: by_src
|
||||
ip: 10.10.3.0/24
|
||||
20
pillar/thresholding/pillar.usage
Normal file
@@ -0,0 +1,20 @@
|
||||
thresholding:
|
||||
sids:
|
||||
<signature id>:
|
||||
- threshold:
|
||||
gen_id: <generator id>
|
||||
type: <threshold | limit | both>
|
||||
track: <by_src | by_dst>
|
||||
count: <count>
|
||||
seconds: <seconds>
|
||||
- rate_filter:
|
||||
gen_id: <generator id>
|
||||
track: <by_src | by_dst | by_rule | by_both>
|
||||
count: <count>
|
||||
seconds: <seconds>
|
||||
new_action: <alert | pass>
|
||||
timeout: <seconds>
|
||||
- suppress:
|
||||
gen_id: <generator id>
|
||||
track: <by_src | by_dst | by_either>
|
||||
ip: <ip | subnet>
|
||||
332
pillar/top.sls
@@ -1,39 +1,30 @@
|
||||
base:
|
||||
'*':
|
||||
- global.soc_global
|
||||
- global.adv_global
|
||||
- docker.soc_docker
|
||||
- docker.adv_docker
|
||||
- influxdb.token
|
||||
- logrotate.soc_logrotate
|
||||
- logrotate.adv_logrotate
|
||||
- ntp.soc_ntp
|
||||
- ntp.adv_ntp
|
||||
- patch.needs_restarting
|
||||
- patch.soc_patch
|
||||
- patch.adv_patch
|
||||
- sensoroni.soc_sensoroni
|
||||
- sensoroni.adv_sensoroni
|
||||
- telegraf.soc_telegraf
|
||||
- telegraf.adv_telegraf
|
||||
- versionlock.soc_versionlock
|
||||
- versionlock.adv_versionlock
|
||||
- soc.license
|
||||
- logrotate
|
||||
|
||||
'* and not *_desktop':
|
||||
- firewall.soc_firewall
|
||||
- firewall.adv_firewall
|
||||
- nginx.soc_nginx
|
||||
- nginx.adv_nginx
|
||||
'* and not *_eval and not *_import':
|
||||
- logstash.nodes
|
||||
|
||||
'salt-cloud:driver:libvirt':
|
||||
- match: grain
|
||||
- vm.soc_vm
|
||||
- vm.adv_vm
|
||||
|
||||
'*_manager or *_managersearch or *_managerhype':
|
||||
'*_eval or *_helixsensor or *_heavynode or *_sensor or *_standalone or *_import':
|
||||
- match: compound
|
||||
- node_data.ips
|
||||
- zeek
|
||||
|
||||
'*_managersearch or *_heavynode':
|
||||
- match: compound
|
||||
- logstash
|
||||
- logstash.manager
|
||||
- logstash.search
|
||||
- elasticsearch.index_templates
|
||||
|
||||
'*_manager':
|
||||
- logstash
|
||||
- logstash.manager
|
||||
- elasticsearch.index_templates
|
||||
|
||||
'*_manager or *_managersearch':
|
||||
- match: compound
|
||||
- data.*
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/elasticsearch/auth.sls') %}
|
||||
- elasticsearch.auth
|
||||
{% endif %}
|
||||
@@ -41,63 +32,18 @@ base:
|
||||
- kibana.secrets
|
||||
{% endif %}
|
||||
- secrets
|
||||
- manager.soc_manager
|
||||
- manager.adv_manager
|
||||
- idstools.soc_idstools
|
||||
- idstools.adv_idstools
|
||||
- logstash.nodes
|
||||
- logstash.soc_logstash
|
||||
- logstash.adv_logstash
|
||||
- soc.soc_soc
|
||||
- soc.adv_soc
|
||||
- kibana.soc_kibana
|
||||
- kibana.adv_kibana
|
||||
- kratos.soc_kratos
|
||||
- kratos.adv_kratos
|
||||
- hydra.soc_hydra
|
||||
- hydra.adv_hydra
|
||||
- redis.nodes
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- influxdb.soc_influxdb
|
||||
- influxdb.adv_influxdb
|
||||
- elasticsearch.nodes
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
- elasticfleet.soc_elasticfleet
|
||||
- elasticfleet.adv_elasticfleet
|
||||
- elastalert.soc_elastalert
|
||||
- elastalert.adv_elastalert
|
||||
- backup.soc_backup
|
||||
- backup.adv_backup
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- kafka.adv_kafka
|
||||
- hypervisor.nodes
|
||||
- hypervisor.soc_hypervisor
|
||||
- hypervisor.adv_hypervisor
|
||||
- stig.soc_stig
|
||||
|
||||
'*_sensor':
|
||||
- zeeklogs
|
||||
- healthcheck.sensor
|
||||
- strelka.soc_strelka
|
||||
- strelka.adv_strelka
|
||||
- zeek.soc_zeek
|
||||
- zeek.adv_zeek
|
||||
- bpf.soc_bpf
|
||||
- bpf.adv_bpf
|
||||
- pcap.soc_pcap
|
||||
- pcap.adv_pcap
|
||||
- suricata.soc_suricata
|
||||
- suricata.adv_suricata
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- stig.soc_stig
|
||||
|
||||
'*_eval':
|
||||
- node_data.ips
|
||||
- data.*
|
||||
- zeeklogs
|
||||
- secrets
|
||||
- healthcheck.eval
|
||||
- elasticsearch.index_templates
|
||||
@@ -107,48 +53,13 @@ base:
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/kibana/secrets.sls') %}
|
||||
- kibana.secrets
|
||||
{% endif %}
|
||||
- kratos.soc_kratos
|
||||
- kratos.adv_kratos
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
- elasticfleet.soc_elasticfleet
|
||||
- elasticfleet.adv_elasticfleet
|
||||
- elastalert.soc_elastalert
|
||||
- elastalert.adv_elastalert
|
||||
- manager.soc_manager
|
||||
- manager.adv_manager
|
||||
- idstools.soc_idstools
|
||||
- idstools.adv_idstools
|
||||
- soc.soc_soc
|
||||
- soc.adv_soc
|
||||
- kibana.soc_kibana
|
||||
- kibana.adv_kibana
|
||||
- strelka.soc_strelka
|
||||
- strelka.adv_strelka
|
||||
- hydra.soc_hydra
|
||||
- hydra.adv_hydra
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- influxdb.soc_influxdb
|
||||
- influxdb.adv_influxdb
|
||||
- backup.soc_backup
|
||||
- backup.adv_backup
|
||||
- zeek.soc_zeek
|
||||
- zeek.adv_zeek
|
||||
- bpf.soc_bpf
|
||||
- bpf.adv_bpf
|
||||
- pcap.soc_pcap
|
||||
- pcap.adv_pcap
|
||||
- suricata.soc_suricata
|
||||
- suricata.adv_suricata
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
|
||||
'*_standalone':
|
||||
- node_data.ips
|
||||
- logstash.nodes
|
||||
- logstash.soc_logstash
|
||||
- logstash.adv_logstash
|
||||
- logstash
|
||||
- logstash.manager
|
||||
- logstash.search
|
||||
- elasticsearch.index_templates
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/elasticsearch/auth.sls') %}
|
||||
- elasticsearch.auth
|
||||
@@ -156,118 +67,60 @@ base:
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/kibana/secrets.sls') %}
|
||||
- kibana.secrets
|
||||
{% endif %}
|
||||
- data.*
|
||||
- zeeklogs
|
||||
- secrets
|
||||
- healthcheck.standalone
|
||||
- idstools.soc_idstools
|
||||
- idstools.adv_idstools
|
||||
- kratos.soc_kratos
|
||||
- kratos.adv_kratos
|
||||
- hydra.soc_hydra
|
||||
- hydra.adv_hydra
|
||||
- redis.nodes
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- influxdb.soc_influxdb
|
||||
- influxdb.adv_influxdb
|
||||
- elasticsearch.nodes
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
- elasticfleet.soc_elasticfleet
|
||||
- elasticfleet.adv_elasticfleet
|
||||
- elastalert.soc_elastalert
|
||||
- elastalert.adv_elastalert
|
||||
- manager.soc_manager
|
||||
- manager.adv_manager
|
||||
- soc.soc_soc
|
||||
- soc.adv_soc
|
||||
- kibana.soc_kibana
|
||||
- kibana.adv_kibana
|
||||
- strelka.soc_strelka
|
||||
- strelka.adv_strelka
|
||||
- backup.soc_backup
|
||||
- backup.adv_backup
|
||||
- zeek.soc_zeek
|
||||
- zeek.adv_zeek
|
||||
- bpf.soc_bpf
|
||||
- bpf.adv_bpf
|
||||
- pcap.soc_pcap
|
||||
- pcap.adv_pcap
|
||||
- suricata.soc_suricata
|
||||
- suricata.adv_suricata
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
|
||||
'*_node':
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- stig.soc_stig
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- kafka.adv_kafka
|
||||
|
||||
'*_heavynode':
|
||||
- zeeklogs
|
||||
- elasticsearch.auth
|
||||
- logstash.nodes
|
||||
- logstash.soc_logstash
|
||||
- logstash.adv_logstash
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- zeek.soc_zeek
|
||||
- zeek.adv_zeek
|
||||
- bpf.soc_bpf
|
||||
- bpf.adv_bpf
|
||||
- pcap.soc_pcap
|
||||
- pcap.adv_pcap
|
||||
- suricata.soc_suricata
|
||||
- suricata.adv_suricata
|
||||
- strelka.soc_strelka
|
||||
- strelka.adv_strelka
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
|
||||
'*_helixsensor':
|
||||
- fireeye
|
||||
- zeeklogs
|
||||
- logstash
|
||||
- logstash.helix
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
|
||||
'*_fleet':
|
||||
- data.*
|
||||
- secrets
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
|
||||
'*_idh':
|
||||
- idh.soc_idh
|
||||
- idh.adv_idh
|
||||
- data.*
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
|
||||
'*_searchnode':
|
||||
- logstash.nodes
|
||||
- logstash.soc_logstash
|
||||
- logstash.adv_logstash
|
||||
- elasticsearch.nodes
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/elasticsearch/auth.sls') %}
|
||||
- logstash
|
||||
- logstash.search
|
||||
- elasticsearch.index_templates
|
||||
- elasticsearch.auth
|
||||
{% endif %}
|
||||
- redis.nodes
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- stig.soc_stig
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- kafka.adv_kafka
|
||||
- data.nodestab
|
||||
|
||||
'*_receiver':
|
||||
- logstash.nodes
|
||||
- logstash.soc_logstash
|
||||
- logstash.adv_logstash
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/elasticsearch/auth.sls') %}
|
||||
- logstash
|
||||
- logstash.receiver
|
||||
- elasticsearch.auth
|
||||
{% endif %}
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- kafka.nodes
|
||||
- kafka.soc_kafka
|
||||
- stig.soc_stig
|
||||
- elasticfleet.soc_elasticfleet
|
||||
- elasticfleet.adv_elasticfleet
|
||||
|
||||
'*_import':
|
||||
- node_data.ips
|
||||
- zeeklogs
|
||||
- secrets
|
||||
- elasticsearch.index_templates
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/elasticsearch/auth.sls') %}
|
||||
@@ -276,61 +129,8 @@ base:
|
||||
{% if salt['file.file_exists']('/opt/so/saltstack/local/pillar/kibana/secrets.sls') %}
|
||||
- kibana.secrets
|
||||
{% endif %}
|
||||
- kratos.soc_kratos
|
||||
- kratos.adv_kratos
|
||||
- elasticsearch.soc_elasticsearch
|
||||
- elasticsearch.adv_elasticsearch
|
||||
- elasticfleet.soc_elasticfleet
|
||||
- elasticfleet.adv_elasticfleet
|
||||
- elastalert.soc_elastalert
|
||||
- elastalert.adv_elastalert
|
||||
- manager.soc_manager
|
||||
- manager.adv_manager
|
||||
- soc.soc_soc
|
||||
- soc.adv_soc
|
||||
- kibana.soc_kibana
|
||||
- kibana.adv_kibana
|
||||
- backup.soc_backup
|
||||
- backup.adv_backup
|
||||
- hydra.soc_hydra
|
||||
- hydra.adv_hydra
|
||||
- redis.soc_redis
|
||||
- redis.adv_redis
|
||||
- influxdb.soc_influxdb
|
||||
- influxdb.adv_influxdb
|
||||
- zeek.soc_zeek
|
||||
- zeek.adv_zeek
|
||||
- bpf.soc_bpf
|
||||
- bpf.adv_bpf
|
||||
- pcap.soc_pcap
|
||||
- pcap.adv_pcap
|
||||
- suricata.soc_suricata
|
||||
- suricata.adv_suricata
|
||||
- strelka.soc_strelka
|
||||
- strelka.adv_strelka
|
||||
- global
|
||||
- minions.{{ grains.id }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
|
||||
'*_fleet':
|
||||
- node_data.ips
|
||||
- backup.soc_backup
|
||||
- backup.adv_backup
|
||||
- logstash.nodes
|
||||
- logstash.soc_logstash
|
||||
- logstash.adv_logstash
|
||||
- elasticfleet.soc_elasticfleet
|
||||
- elasticfleet.adv_elasticfleet
|
||||
'*_workstation':
|
||||
- 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 }}
|
||||
- minions.adv_{{ grains.id }}
|
||||
- stig.soc_stig
|
||||
|
||||
|
||||
@@ -1 +1,55 @@
|
||||
zeek:
|
||||
zeekctl:
|
||||
MailTo: root@localhost
|
||||
MailConnectionSummary: 1
|
||||
MinDiskSpace: 5
|
||||
MailHostUpDown: 1
|
||||
LogRotationInterval: 3600
|
||||
LogExpireInterval: 0
|
||||
StatsLogEnable: 1
|
||||
StatsLogExpireInterval: 0
|
||||
StatusCmdShowAll: 0
|
||||
CrashExpireInterval: 0
|
||||
SitePolicyScripts: local.zeek
|
||||
LogDir: /nsm/zeek/logs
|
||||
SpoolDir: /nsm/zeek/spool
|
||||
CfgDir: /opt/zeek/etc
|
||||
CompressLogs: 1
|
||||
local:
|
||||
'@load':
|
||||
- misc/loaded-scripts
|
||||
- tuning/defaults
|
||||
- misc/capture-loss
|
||||
- misc/stats
|
||||
- frameworks/software/vulnerable
|
||||
- frameworks/software/version-changes
|
||||
- protocols/ftp/software
|
||||
- protocols/smtp/software
|
||||
- protocols/ssh/software
|
||||
- protocols/http/software
|
||||
- protocols/dns/detect-external-names
|
||||
- protocols/ftp/detect
|
||||
- protocols/conn/known-hosts
|
||||
- protocols/conn/known-services
|
||||
- protocols/ssl/known-certs
|
||||
- protocols/ssl/validate-certs
|
||||
- protocols/ssl/log-hostcerts-only
|
||||
- protocols/ssh/geo-data
|
||||
- protocols/ssh/detect-bruteforcing
|
||||
- protocols/ssh/interesting-hostnames
|
||||
- protocols/http/detect-sqli
|
||||
- frameworks/files/hash-all-files
|
||||
- frameworks/files/detect-MHR
|
||||
- policy/frameworks/notice/extend-email/hostnames
|
||||
- ja3
|
||||
- hassh
|
||||
- intel
|
||||
- cve-2020-0601
|
||||
- securityonion/bpfconf
|
||||
- securityonion/communityid
|
||||
- securityonion/file-extraction
|
||||
'@load-sigs':
|
||||
- frameworks/signatures/detect-windows-shells
|
||||
redef:
|
||||
- LogAscii::use_json = T;
|
||||
- CaptureLoss::watch_interval = 5 mins;
|
||||
|
||||
30
pyci.sh
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "Usage: $0 <python_script_dir>"
|
||||
echo "Runs tests on all *_test.py files in the given directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HOME_DIR=$(dirname "$0")
|
||||
TARGET_DIR=${1:-.}
|
||||
|
||||
PATH=$PATH:/usr/local/bin
|
||||
|
||||
if [ ! -d .venv ]; then
|
||||
python -m venv .venv
|
||||
fi
|
||||
|
||||
source .venv/bin/activate
|
||||
|
||||
if ! pip install flake8 pytest pytest-cov pyyaml; then
|
||||
echo "Unable to install dependencies."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
flake8 "$TARGET_DIR" "--config=${HOME_DIR}/pytest.ini"
|
||||
python3 -m pytest "--cov-config=${HOME_DIR}/pytest.ini" "--cov=$TARGET_DIR" --doctest-modules --cov-report=term --cov-fail-under=100 "$TARGET_DIR"
|
||||
@@ -1,91 +0,0 @@
|
||||
#!/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}
|
||||
@@ -3,14 +3,14 @@ import subprocess
|
||||
|
||||
def check():
|
||||
|
||||
osfam = __grains__['os_family']
|
||||
os = __grains__['os']
|
||||
retval = 'False'
|
||||
|
||||
if osfam == 'Debian':
|
||||
if os == 'Ubuntu':
|
||||
if path.exists('/var/run/reboot-required'):
|
||||
retval = 'True'
|
||||
|
||||
elif osfam == 'RedHat':
|
||||
elif os == 'CentOS':
|
||||
cmd = 'needs-restarting -r > /dev/null 2>&1'
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,335 +0,0 @@
|
||||
#!py
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Salt module for managing QCOW2 image configurations and VM hardware settings. This module provides functions
|
||||
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 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.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import shlex
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = 'qcow2'
|
||||
|
||||
def __virtual__():
|
||||
return __virtualname__
|
||||
|
||||
def modify_network_config(image, interface, mode, vm_name, ip4=None, gw4=None, dns4=None, search4=None):
|
||||
'''
|
||||
Usage:
|
||||
salt '*' qcow2.modify_network_config image=<path> interface=<iface> mode=<mode> vm_name=<name> [ip4=<addr>] [gw4=<addr>] [dns4=<servers>] [search4=<domain>]
|
||||
|
||||
Options:
|
||||
image
|
||||
Path to the QCOW2 image file that will be modified
|
||||
interface
|
||||
Network interface name to configure (e.g., 'enp1s0')
|
||||
mode
|
||||
Network configuration mode, either 'dhcp4' or 'static4'
|
||||
vm_name
|
||||
Full name of the VM (hostname_role)
|
||||
ip4
|
||||
IPv4 address with CIDR notation (e.g., '192.168.1.10/24')
|
||||
Required when mode='static4'
|
||||
gw4
|
||||
IPv4 gateway address (e.g., '192.168.1.1')
|
||||
Required when mode='static4'
|
||||
dns4
|
||||
Comma-separated list of IPv4 DNS servers (e.g., '8.8.8.8,8.8.4.4')
|
||||
Optional for both DHCP and static configurations
|
||||
search4
|
||||
DNS search domain for IPv4 (e.g., 'example.local')
|
||||
Optional for both DHCP and static configurations
|
||||
|
||||
Examples:
|
||||
1. **Configure DHCP:**
|
||||
```bash
|
||||
salt '*' qcow2.modify_network_config image='/nsm/libvirt/images/sool9/sool9.qcow2' interface='enp1s0' mode='dhcp4'
|
||||
```
|
||||
This configures enp1s0 to use DHCP for IP assignment
|
||||
|
||||
2. **Configure Static IP:**
|
||||
```bash
|
||||
salt '*' qcow2.modify_network_config image='/nsm/libvirt/images/sool9/sool9.qcow2' interface='enp1s0' mode='static4' ip4='192.168.1.10/24' gw4='192.168.1.1' dns4='192.168.1.1,8.8.8.8' search4='example.local'
|
||||
```
|
||||
This sets a static IP configuration with DNS servers and search domain
|
||||
|
||||
Notes:
|
||||
- The QCOW2 image must be accessible and writable by the salt minion
|
||||
- The image should not be in use by a running VM when modified
|
||||
- Network changes take effect on next VM boot
|
||||
- Requires so-qcow2-modify-network script to be installed
|
||||
|
||||
Description:
|
||||
This function modifies network configuration within a QCOW2 image file by executing
|
||||
the so-qcow2-modify-network script. It supports both DHCP and static IPv4 configuration.
|
||||
The script mounts the image, modifies the network configuration files, and unmounts
|
||||
safely. All operations are logged for troubleshooting purposes.
|
||||
|
||||
Exit Codes:
|
||||
0: Success
|
||||
1: Invalid parameters or configuration
|
||||
2: Image access or mounting error
|
||||
3: Network configuration 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:'
|
||||
- Error conditions include detailed error messages and stack traces
|
||||
- Success/failure status is logged for verification
|
||||
'''
|
||||
|
||||
cmd = ['/usr/sbin/so-qcow2-modify-network', '-I', image, '-i', interface, '-n', vm_name]
|
||||
|
||||
if mode.lower() == 'dhcp4':
|
||||
cmd.append('--dhcp4')
|
||||
elif mode.lower() == 'static4':
|
||||
cmd.append('--static4')
|
||||
if not ip4 or not gw4:
|
||||
raise ValueError('Both ip4 and gw4 are required for static configuration.')
|
||||
cmd.extend(['--ip4', ip4, '--gw4', gw4])
|
||||
if dns4:
|
||||
cmd.extend(['--dns4', dns4])
|
||||
if search4:
|
||||
cmd.extend(['--search4', search4])
|
||||
else:
|
||||
raise ValueError("Invalid mode '{}'. Expected 'dhcp4' or 'static4'.".format(mode))
|
||||
|
||||
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
|
||||
|
||||
def modify_hardware_config(vm_name, cpu=None, memory=None, pci=None, start=False):
|
||||
'''
|
||||
Usage:
|
||||
salt '*' qcow2.modify_hardware_config vm_name=<name> [cpu=<count>] [memory=<size>] [pci=<id>] [pci=<id>] [start=<bool>]
|
||||
|
||||
Options:
|
||||
vm_name
|
||||
Name of the virtual machine to modify
|
||||
cpu
|
||||
Number of virtual CPUs to assign (positive integer)
|
||||
Optional - VM's current CPU count retained if not specified
|
||||
memory
|
||||
Amount of memory to assign in MiB (positive integer)
|
||||
Optional - VM's current memory size retained if not specified
|
||||
pci
|
||||
PCI hardware ID(s) to passthrough to the VM (e.g., '0000:c7:00.0')
|
||||
Can be specified multiple times for multiple devices
|
||||
Optional - no PCI passthrough if not specified
|
||||
start
|
||||
Boolean flag to start the VM after modification
|
||||
Optional - defaults to False
|
||||
|
||||
Examples:
|
||||
1. **Modify CPU and Memory:**
|
||||
```bash
|
||||
salt '*' qcow2.modify_hardware_config vm_name='sensor1' cpu=4 memory=8192
|
||||
```
|
||||
This assigns 4 CPUs and 8GB memory to the VM
|
||||
|
||||
2. **Enable PCI Passthrough:**
|
||||
```bash
|
||||
salt '*' qcow2.modify_hardware_config vm_name='sensor1' pci='0000:c7:00.0' pci='0000:c4:00.0' start=True
|
||||
```
|
||||
This configures PCI passthrough and starts the VM
|
||||
|
||||
3. **Complete Hardware Configuration:**
|
||||
```bash
|
||||
salt '*' qcow2.modify_hardware_config vm_name='sensor1' cpu=8 memory=16384 pci='0000:c7:00.0' start=True
|
||||
```
|
||||
This sets CPU, memory, PCI passthrough, and starts the VM
|
||||
|
||||
Notes:
|
||||
- VM must be stopped before modification unless only the start flag is set
|
||||
- Memory is specified in MiB (1024 = 1GB)
|
||||
- PCI devices must be available and not in use by the host
|
||||
- CPU count should align with host capabilities
|
||||
- Requires so-kvm-modify-hardware script to be installed
|
||||
|
||||
Description:
|
||||
This function modifies the hardware configuration of a KVM virtual machine using
|
||||
the so-kvm-modify-hardware script. It can adjust CPU count, memory allocation,
|
||||
and PCI device passthrough. Changes are applied to the VM's libvirt configuration.
|
||||
The VM can optionally be started after modifications are complete.
|
||||
|
||||
Exit Codes:
|
||||
0: Success
|
||||
1: Invalid parameters
|
||||
2: VM state error (running when should be stopped)
|
||||
3: Hardware configuration 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:'
|
||||
- Hardware configuration changes are logged
|
||||
- Errors include detailed messages and stack traces
|
||||
- Final status of modification is logged
|
||||
'''
|
||||
|
||||
cmd = ['/usr/sbin/so-kvm-modify-hardware', '-v', vm_name]
|
||||
|
||||
if cpu is not None:
|
||||
if isinstance(cpu, int) and cpu > 0:
|
||||
cmd.extend(['-c', str(cpu)])
|
||||
else:
|
||||
raise ValueError('cpu must be a positive integer.')
|
||||
if memory is not None:
|
||||
if isinstance(memory, int) and memory > 0:
|
||||
cmd.extend(['-m', str(memory)])
|
||||
else:
|
||||
raise ValueError('memory must be a positive integer.')
|
||||
if pci:
|
||||
# Handle PCI IDs (can be a single device or comma-separated list)
|
||||
if isinstance(pci, str):
|
||||
devices = [dev.strip() for dev in pci.split(',') if dev.strip()]
|
||||
elif isinstance(pci, list):
|
||||
devices = pci
|
||||
else:
|
||||
devices = [pci]
|
||||
|
||||
# Add each device with its own -p flag
|
||||
for device in devices:
|
||||
cmd.extend(['-p', str(device)])
|
||||
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
|
||||
|
||||
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
|
||||
@@ -5,8 +5,6 @@ import logging
|
||||
def status():
|
||||
return __salt__['cmd.run']('/usr/sbin/so-status')
|
||||
|
||||
def version():
|
||||
return __salt__['cp.get_file_str']('/etc/soversion')
|
||||
|
||||
def mysql_conn(retry):
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,180 +1,324 @@
|
||||
{# 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 ZEEKVER = salt['pillar.get']('global:mdengine', '') %}
|
||||
{% set WAZUH = salt['pillar.get']('global:wazuh', '0') %}
|
||||
{% set PLAYBOOK = salt['pillar.get']('manager:playbook', '0') %}
|
||||
{% set FREQSERVER = salt['pillar.get']('manager:freq', '0') %}
|
||||
{% set DOMAINSTATS = salt['pillar.get']('manager:domainstats', '0') %}
|
||||
{% set FLEETMANAGER = salt['pillar.get']('global:fleet_manager', False) %}
|
||||
{% set FLEETNODE = salt['pillar.get']('global:fleet_node', False) %}
|
||||
{% set ELASTALERT = salt['pillar.get']('elastalert:enabled', True) %}
|
||||
{% set ELASTICSEARCH = salt['pillar.get']('elasticsearch:enabled', True) %}
|
||||
{% set FILEBEAT = salt['pillar.get']('filebeat:enabled', True) %}
|
||||
{% set KIBANA = salt['pillar.get']('kibana:enabled', True) %}
|
||||
{% set LOGSTASH = salt['pillar.get']('logstash:enabled', True) %}
|
||||
{% set CURATOR = salt['pillar.get']('curator:enabled', True) %}
|
||||
{% set REDIS = salt['pillar.get']('redis:enabled', True) %}
|
||||
{% set STRELKA = salt['pillar.get']('strelka:enabled', '0') %}
|
||||
{% set ISAIRGAP = salt['pillar.get']('global:airgap', False) %}
|
||||
{% import_yaml 'salt/minion.defaults.yaml' as saltversion %}
|
||||
{% set saltversion = saltversion.salt.minion.version %}
|
||||
|
||||
{# Define common state groups to reduce redundancy #}
|
||||
{% set base_states = [
|
||||
'common',
|
||||
'patch.os.schedule',
|
||||
'motd',
|
||||
'salt.minion-check',
|
||||
'sensoroni',
|
||||
'salt.lasthighstate',
|
||||
'salt.minion'
|
||||
] %}
|
||||
{# this is the list we are returning from this map file, it gets built below #}
|
||||
{% set allowed_states= [] %}
|
||||
|
||||
{% set ssl_states = [
|
||||
{% if grains.saltversion | string == saltversion | string %}
|
||||
|
||||
{% set allowed_states= salt['grains.filter_by']({
|
||||
'so-eval': [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'ssl',
|
||||
'registry',
|
||||
'manager',
|
||||
'nginx',
|
||||
'telegraf',
|
||||
'influxdb',
|
||||
'grafana',
|
||||
'soc',
|
||||
'kratos',
|
||||
'firewall',
|
||||
'idstools',
|
||||
'suricata.manager',
|
||||
'healthcheck',
|
||||
'pcap',
|
||||
'suricata',
|
||||
'utility',
|
||||
'schedule',
|
||||
'soctopus',
|
||||
'tcpreplay',
|
||||
'docker_clean',
|
||||
'learn'
|
||||
],
|
||||
'so-heavynode': [
|
||||
'ssl',
|
||||
'nginx',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'pcap',
|
||||
'suricata',
|
||||
'healthcheck',
|
||||
'schedule',
|
||||
'tcpreplay',
|
||||
'docker_clean'
|
||||
],
|
||||
'so-helixsensor': [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'ssl',
|
||||
'registry',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'idstools',
|
||||
'suricata.manager',
|
||||
'zeek',
|
||||
'redis',
|
||||
'elasticsearch',
|
||||
'logstash',
|
||||
'schedule',
|
||||
'tcpreplay',
|
||||
'docker_clean'
|
||||
],
|
||||
'so-fleet': [
|
||||
'ssl',
|
||||
'nginx',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'mysql',
|
||||
'redis',
|
||||
'fleet',
|
||||
'fleet.install_package',
|
||||
'filebeat',
|
||||
'schedule',
|
||||
'docker_clean'
|
||||
],
|
||||
'so-idh': [
|
||||
'ssl',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'fleet.install_package',
|
||||
'filebeat',
|
||||
'idh',
|
||||
'schedule',
|
||||
'docker_clean'
|
||||
],
|
||||
'so-import': [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'ssl',
|
||||
'registry',
|
||||
'manager',
|
||||
'nginx',
|
||||
'soc',
|
||||
'kratos',
|
||||
'firewall',
|
||||
'idstools',
|
||||
'suricata.manager',
|
||||
'pcap',
|
||||
'utility',
|
||||
'suricata',
|
||||
'zeek',
|
||||
'schedule',
|
||||
'tcpreplay',
|
||||
'docker_clean',
|
||||
'learn'
|
||||
],
|
||||
'so-manager': [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'ssl',
|
||||
'registry',
|
||||
'manager',
|
||||
'nginx',
|
||||
'telegraf',
|
||||
'influxdb',
|
||||
'grafana',
|
||||
'soc',
|
||||
'kratos',
|
||||
'firewall',
|
||||
'idstools',
|
||||
'suricata.manager',
|
||||
'utility',
|
||||
'schedule',
|
||||
'soctopus',
|
||||
'docker_clean',
|
||||
'learn'
|
||||
],
|
||||
'so-managersearch': [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'ssl',
|
||||
'registry',
|
||||
'nginx',
|
||||
'telegraf',
|
||||
'influxdb',
|
||||
'grafana',
|
||||
'soc',
|
||||
'kratos',
|
||||
'firewall',
|
||||
'manager',
|
||||
'idstools',
|
||||
'suricata.manager',
|
||||
'utility',
|
||||
'schedule',
|
||||
'soctopus',
|
||||
'docker_clean',
|
||||
'learn'
|
||||
],
|
||||
'so-node': [
|
||||
'ssl',
|
||||
'nginx',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'schedule',
|
||||
'docker_clean'
|
||||
],
|
||||
'so-standalone': [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'ssl',
|
||||
'registry',
|
||||
'manager',
|
||||
'nginx',
|
||||
'telegraf',
|
||||
'influxdb',
|
||||
'grafana',
|
||||
'soc',
|
||||
'kratos',
|
||||
'firewall',
|
||||
'idstools',
|
||||
'suricata.manager',
|
||||
'pcap',
|
||||
'suricata',
|
||||
'healthcheck',
|
||||
'utility',
|
||||
'schedule',
|
||||
'soctopus',
|
||||
'tcpreplay',
|
||||
'docker_clean',
|
||||
'learn'
|
||||
],
|
||||
'so-sensor': [
|
||||
'ssl',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'nginx',
|
||||
'pcap',
|
||||
'suricata',
|
||||
'healthcheck',
|
||||
'wazuh',
|
||||
'filebeat',
|
||||
'schedule',
|
||||
'tcpreplay',
|
||||
'docker_clean'
|
||||
],
|
||||
'so-receiver': [
|
||||
'ssl',
|
||||
'telegraf',
|
||||
'firewall',
|
||||
'schedule',
|
||||
'docker_clean'
|
||||
] %}
|
||||
],
|
||||
'so-workstation': [
|
||||
],
|
||||
}, grain='role') %}
|
||||
|
||||
{% set manager_states = [
|
||||
'salt.master',
|
||||
'ca',
|
||||
'registry',
|
||||
'manager',
|
||||
'nginx',
|
||||
'influxdb',
|
||||
'soc',
|
||||
'kratos',
|
||||
'hydra',
|
||||
'elasticfleet',
|
||||
'elastic-fleet-package-registry',
|
||||
'idstools',
|
||||
'suricata.manager',
|
||||
'utility'
|
||||
] %}
|
||||
|
||||
{% set sensor_states = [
|
||||
'pcap',
|
||||
'suricata',
|
||||
'healthcheck',
|
||||
'tcpreplay',
|
||||
'zeek',
|
||||
'strelka'
|
||||
] %}
|
||||
|
||||
{% set kafka_states = [
|
||||
'kafka'
|
||||
] %}
|
||||
|
||||
{% set stig_states = [
|
||||
'stig'
|
||||
] %}
|
||||
|
||||
{% set elastic_stack_states = [
|
||||
'elasticsearch',
|
||||
'elasticsearch.auth',
|
||||
'kibana',
|
||||
'kibana.secrets',
|
||||
'elastalert',
|
||||
'logstash',
|
||||
'redis'
|
||||
] %}
|
||||
|
||||
{# Initialize the allowed_states list #}
|
||||
{% set allowed_states = [] %}
|
||||
|
||||
{% if grains.saltversion | string == saltversion | string %}
|
||||
{# Map role-specific states #}
|
||||
{% set role_states = {
|
||||
'so-eval': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
sensor_states +
|
||||
elastic_stack_states | reject('equalto', 'logstash') | list
|
||||
),
|
||||
'so-heavynode': (
|
||||
ssl_states +
|
||||
sensor_states +
|
||||
['elasticagent', 'elasticsearch', 'logstash', 'redis', 'nginx']
|
||||
),
|
||||
'so-idh': (
|
||||
ssl_states +
|
||||
['idh']
|
||||
),
|
||||
'so-import': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
sensor_states | reject('equalto', 'strelka') | reject('equalto', 'healthcheck') | list +
|
||||
['elasticsearch', 'elasticsearch.auth', 'kibana', 'kibana.secrets', 'strelka.manager']
|
||||
),
|
||||
'so-manager': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'libvirt.packages', 'libvirt.ssh.users', 'strelka.manager'] +
|
||||
stig_states +
|
||||
kafka_states +
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-managerhype': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'strelka.manager', 'hypervisor', 'libvirt'] +
|
||||
stig_states +
|
||||
kafka_states +
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-managersearch': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'libvirt.packages', 'libvirt.ssh.users', 'strelka.manager'] +
|
||||
stig_states +
|
||||
kafka_states +
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-searchnode': (
|
||||
ssl_states +
|
||||
['kafka.ca', 'kafka.ssl', 'elasticsearch', 'logstash', 'nginx'] +
|
||||
stig_states
|
||||
),
|
||||
'so-standalone': (
|
||||
ssl_states +
|
||||
manager_states +
|
||||
['salt.cloud', 'libvirt.packages', 'libvirt.ssh.users'] +
|
||||
sensor_states +
|
||||
stig_states +
|
||||
kafka_states +
|
||||
elastic_stack_states
|
||||
),
|
||||
'so-sensor': (
|
||||
ssl_states +
|
||||
sensor_states +
|
||||
['nginx'] +
|
||||
stig_states
|
||||
),
|
||||
'so-fleet': (
|
||||
ssl_states +
|
||||
stig_states +
|
||||
['logstash', 'nginx', 'healthcheck', 'elasticfleet']
|
||||
),
|
||||
'so-receiver': (
|
||||
ssl_states +
|
||||
kafka_states +
|
||||
stig_states +
|
||||
['logstash', 'redis']
|
||||
),
|
||||
'so-hypervisor': (
|
||||
ssl_states +
|
||||
stig_states +
|
||||
['hypervisor', 'libvirt']
|
||||
),
|
||||
'so-desktop': (
|
||||
['ssl', 'docker_clean', 'telegraf'] +
|
||||
stig_states
|
||||
)
|
||||
} %}
|
||||
|
||||
{# Get states for the current role #}
|
||||
{% if grains.role in role_states %}
|
||||
{% set allowed_states = role_states[grains.role] %}
|
||||
{% if FILEBEAT and grains.role in ['so-helixsensor', 'so-eval', 'so-manager', 'so-standalone', 'so-node', 'so-managersearch', 'so-heavynode', 'so-import', 'so-receiver'] %}
|
||||
{% do allowed_states.append('filebeat') %}
|
||||
{% endif %}
|
||||
|
||||
{# Add base states that apply to all roles #}
|
||||
{% for state in base_states %}
|
||||
{% do allowed_states.append(state) %}
|
||||
{% endfor %}
|
||||
{% if ((FLEETMANAGER or FLEETNODE) or PLAYBOOK != 0) and grains.role in ['so-eval', 'so-manager', 'so-managersearch', 'so-standalone'] %}
|
||||
{% do allowed_states.append('mysql') %}
|
||||
{% endif %}
|
||||
|
||||
{# Add airgap state if needed #}
|
||||
{% if (FLEETMANAGER or FLEETNODE) and grains.role in ['so-sensor', 'so-eval', 'so-manager', 'so-standalone', 'so-node', 'so-managersearch', 'so-heavynode', 'so-receiver'] %}
|
||||
{% do allowed_states.append('fleet.install_package') %}
|
||||
{% endif %}
|
||||
|
||||
{% if (FLEETMANAGER or FLEETNODE) and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch', 'so-heavynode'] %}
|
||||
{% do allowed_states.append('fleet') %}
|
||||
{% endif %}
|
||||
|
||||
{% if (FLEETMANAGER or FLEETNODE) and grains.role in ['so-eval'] %}
|
||||
{% do allowed_states.append('redis') %}
|
||||
{% endif %}
|
||||
|
||||
{%- if ZEEKVER != 'SURICATA' and grains.role in ['so-sensor', 'so-eval', 'so-standalone', 'so-heavynode'] %}
|
||||
{% do allowed_states.append('zeek') %}
|
||||
{%- endif %}
|
||||
|
||||
{% if STRELKA and grains.role in ['so-sensor', 'so-eval', 'so-standalone', 'so-heavynode'] %}
|
||||
{% do allowed_states.append('strelka') %}
|
||||
{% endif %}
|
||||
|
||||
{% if WAZUH and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-node', 'so-managersearch', 'so-heavynode', 'so-receiver','so-idh']%}
|
||||
{% do allowed_states.append('wazuh') %}
|
||||
{% endif %}
|
||||
|
||||
{% if ELASTICSEARCH and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-node', 'so-managersearch', 'so-heavynode', 'so-import'] %}
|
||||
{% do allowed_states.append('elasticsearch') %}
|
||||
{% endif %}
|
||||
|
||||
{% if ELASTICSEARCH and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch', 'so-import'] %}
|
||||
{% do allowed_states.append('elasticsearch.auth') %}
|
||||
{% endif %}
|
||||
|
||||
{% if KIBANA and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch', 'so-import'] %}
|
||||
{% do allowed_states.append('kibana') %}
|
||||
{% do allowed_states.append('kibana.secrets') %}
|
||||
{% endif %}
|
||||
|
||||
{% if grains.role in ['so-eval', 'so-standalone', 'so-node', 'so-managersearch', 'so-heavynode', 'so-manager'] %}
|
||||
{% do allowed_states.append('curator') %}
|
||||
{% endif %}
|
||||
|
||||
{% if ELASTALERT and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch'] %}
|
||||
{% do allowed_states.append('elastalert') %}
|
||||
{% endif %}
|
||||
|
||||
{% if (PLAYBOOK !=0) and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch'] %}
|
||||
{% do allowed_states.append('playbook') %}
|
||||
{% endif %}
|
||||
|
||||
{% if (PLAYBOOK !=0) and grains.role in ['so-eval'] %}
|
||||
{% do allowed_states.append('redis') %}
|
||||
{% endif %}
|
||||
|
||||
{% if (FREQSERVER !=0) and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch'] %}
|
||||
{% do allowed_states.append('freqserver') %}
|
||||
{% endif %}
|
||||
|
||||
{% if (DOMAINSTATS !=0) and grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch'] %}
|
||||
{% do allowed_states.append('domainstats') %}
|
||||
{% endif %}
|
||||
|
||||
{% if LOGSTASH and grains.role in ['so-helixsensor', 'so-manager', 'so-standalone', 'so-node', 'so-managersearch', 'so-heavynode', 'so-receiver'] %}
|
||||
{% do allowed_states.append('logstash') %}
|
||||
{% endif %}
|
||||
|
||||
{% if REDIS and grains.role in ['so-manager', 'so-standalone', 'so-managersearch', 'so-heavynode', 'so-receiver'] %}
|
||||
{% do allowed_states.append('redis') %}
|
||||
{% endif %}
|
||||
|
||||
{% if grains.os == 'CentOS' %}
|
||||
{% if not ISAIRGAP %}
|
||||
{% do allowed_states.append('yum') %}
|
||||
{% endif %}
|
||||
{% do allowed_states.append('yum.packages') %}
|
||||
{% endif %}
|
||||
|
||||
{# all nodes on the right salt version can run the following states #}
|
||||
{% do allowed_states.append('common') %}
|
||||
{% do allowed_states.append('patch.os.schedule') %}
|
||||
{% do allowed_states.append('motd') %}
|
||||
{% do allowed_states.append('salt.minion-check') %}
|
||||
{% do allowed_states.append('sensoroni') %}
|
||||
{% do allowed_states.append('salt.lasthighstate') %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if ISAIRGAP %}
|
||||
{% do allowed_states.append('airgap') %}
|
||||
{% endif %}
|
||||
|
||||
{# all nodes can always run salt.minion state #}
|
||||
{% do allowed_states.append('salt.minion') %}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{% from 'backup/map.jinja' import BACKUP_MERGED %}
|
||||
|
||||
# Lock permissions on the backup directory
|
||||
backupdir:
|
||||
file.directory:
|
||||
- name: /nsm/backup
|
||||
- user: 0
|
||||
- group: 0
|
||||
- makedirs: True
|
||||
- mode: 700
|
||||
|
||||
config_backup_script:
|
||||
file.managed:
|
||||
- name: /usr/sbin/so-config-backup
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: 755
|
||||
- template: jinja
|
||||
- source: salt://backup/tools/sbin/so-config-backup.jinja
|
||||
- defaults:
|
||||
BACKUPLOCATIONS: {{ BACKUP_MERGED.locations }}
|
||||
DESTINATION: {{ BACKUP_MERGED.destination }}
|
||||
|
||||
# Add config backup
|
||||
so_config_backup:
|
||||
cron.present:
|
||||
- name: /usr/sbin/so-config-backup > /dev/null 2>&1
|
||||
- identifier: so_config_backup
|
||||
- user: root
|
||||
- minute: '1'
|
||||
- hour: '0'
|
||||
- daymonth: '*'
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
@@ -1,8 +0,0 @@
|
||||
backup:
|
||||
locations:
|
||||
- /opt/so/saltstack/local
|
||||
- /etc/pki
|
||||
- /etc/salt
|
||||
- /nsm/kratos
|
||||
- /nsm/hydra
|
||||
destination: "/nsm/backup"
|
||||
@@ -1,2 +0,0 @@
|
||||
{% import_yaml 'backup/defaults.yaml' as BACKUP_DEFAULTS %}
|
||||
{% set BACKUP_MERGED = salt['pillar.get']('backup', BACKUP_DEFAULTS.backup, merge=true, merge_nested_lists=true) %}
|
||||
@@ -1,10 +0,0 @@
|
||||
backup:
|
||||
locations:
|
||||
description: List of locations to back up to the destination.
|
||||
helpLink: backup.html
|
||||
global: True
|
||||
destination:
|
||||
description: Directory to store the configuration backups in.
|
||||
helpLink: backup.html
|
||||
global: True
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
TODAY=$(date '+%Y_%m_%d')
|
||||
BACKUPDIR={{ DESTINATION }}
|
||||
BACKUPFILE="$BACKUPDIR/so-config-backup-$TODAY.tar"
|
||||
MAXBACKUPS=7
|
||||
EXCLUSIONS=(
|
||||
"--exclude=/opt/so/saltstack/local/salt/elasticfleet/files/so_agent-installers"
|
||||
)
|
||||
|
||||
|
||||
# Create backup dir if it does not exist
|
||||
mkdir -p /nsm/backup
|
||||
|
||||
# If we haven't already written a backup file for today, let's do so
|
||||
if [ ! -f $BACKUPFILE ]; then
|
||||
|
||||
# Create empty backup file
|
||||
tar -cf $BACKUPFILE -T /dev/null
|
||||
|
||||
# Loop through all paths defined in global.sls, and append them to backup file
|
||||
{%- for LOCATION in BACKUPLOCATIONS %}
|
||||
tar -rf $BACKUPFILE "${EXCLUSIONS[@]}" {{ LOCATION }}
|
||||
{%- endfor %}
|
||||
|
||||
fi
|
||||
|
||||
# Find oldest backup files and remove them
|
||||
NUMBACKUPS=$(find /nsm/backup/ -type f -name "so-config-backup*" | wc -l)
|
||||
while [ "$NUMBACKUPS" -gt "$MAXBACKUPS" ]; do
|
||||
OLDESTBACKUP=$(find /nsm/backup/ -type f -name "so-config-backup*" -type f -printf '%T+ %p\n' | sort | head -n 1 | awk -F" " '{print $2}')
|
||||
rm -f $OLDESTBACKUP
|
||||
NUMBACKUPS=$(find /nsm/backup/ -type f -name "so-config-backup*" | wc -l)
|
||||
done
|
||||
@@ -1,4 +0,0 @@
|
||||
bpf:
|
||||
pcap: []
|
||||
suricata: []
|
||||
zeek: []
|
||||
@@ -1,10 +0,0 @@
|
||||
{% macro remove_comments(bpfmerged, app) %}
|
||||
|
||||
{# remove comments from the bpf #}
|
||||
{% for bpf in bpfmerged[app] %}
|
||||
{% if bpf.strip().startswith('#') %}
|
||||
{% do bpfmerged[app].pop(loop.index0) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endmacro %}
|
||||
@@ -1,10 +0,0 @@
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% if GLOBALS.pcap_engine == "TRANSITION" %}
|
||||
{% set PCAPBPF = ["ip and host 255.255.255.1 and port 1"] %}
|
||||
{% else %}
|
||||
{% import_yaml 'bpf/defaults.yaml' as BPFDEFAULTS %}
|
||||
{% set BPFMERGED = salt['pillar.get']('bpf', BPFDEFAULTS.bpf, merge=True) %}
|
||||
{% import 'bpf/macros.jinja' as MACROS %}
|
||||
{{ MACROS.remove_comments(BPFMERGED, 'pcap') }}
|
||||
{% set PCAPBPF = BPFMERGED.pcap %}
|
||||
{% endif %}
|
||||
@@ -1,16 +0,0 @@
|
||||
bpf:
|
||||
pcap:
|
||||
description: List of BPF filters to apply to Stenographer.
|
||||
multiline: True
|
||||
forcedType: "[]string"
|
||||
helpLink: bpf.html
|
||||
suricata:
|
||||
description: List of BPF filters to apply to Suricata.
|
||||
multiline: True
|
||||
forcedType: "[]string"
|
||||
helpLink: bpf.html
|
||||
zeek:
|
||||
description: List of BPF filters to apply to Zeek.
|
||||
multiline: True
|
||||
forcedType: "[]string"
|
||||
helpLink: bpf.html
|
||||
@@ -1,7 +0,0 @@
|
||||
{% import_yaml 'bpf/defaults.yaml' as BPFDEFAULTS %}
|
||||
{% set BPFMERGED = salt['pillar.get']('bpf', BPFDEFAULTS.bpf, merge=True) %}
|
||||
{% import 'bpf/macros.jinja' as MACROS %}
|
||||
|
||||
{{ MACROS.remove_comments(BPFMERGED, 'suricata') }}
|
||||
|
||||
{% set SURICATABPF = BPFMERGED.suricata %}
|
||||
@@ -1,7 +0,0 @@
|
||||
{% import_yaml 'bpf/defaults.yaml' as BPFDEFAULTS %}
|
||||
{% set BPFMERGED = salt['pillar.get']('bpf', BPFDEFAULTS.bpf, merge=True) %}
|
||||
{% import 'bpf/macros.jinja' as MACROS %}
|
||||
|
||||
{{ MACROS.remove_comments(BPFMERGED, 'zeek') }}
|
||||
|
||||
{% set ZEEKBPF = BPFMERGED.zeek %}
|
||||
@@ -1,3 +1,6 @@
|
||||
mine_functions:
|
||||
x509.get_pem_entries: [/etc/pki/ca.crt]
|
||||
|
||||
x509_signing_policies:
|
||||
filebeat:
|
||||
- minions: '*'
|
||||
@@ -34,7 +37,7 @@ x509_signing_policies:
|
||||
- ST: Utah
|
||||
- L: Salt Lake City
|
||||
- basicConstraints: "critical CA:false"
|
||||
- keyUsage: "critical keyEncipherment digitalSignature"
|
||||
- keyUsage: "critical keyEncipherment"
|
||||
- subjectKeyIdentifier: hash
|
||||
- authorityKeyIdentifier: keyid,issuer:always
|
||||
- extendedKeyUsage: serverAuth
|
||||
@@ -54,7 +57,7 @@ x509_signing_policies:
|
||||
- extendedKeyUsage: serverAuth
|
||||
- days_valid: 820
|
||||
- copypath: /etc/pki/issued_certs/
|
||||
elasticfleet:
|
||||
fleet:
|
||||
- minions: '*'
|
||||
- signing_private_key: /etc/pki/ca.key
|
||||
- signing_cert: /etc/pki/ca.crt
|
||||
@@ -62,22 +65,9 @@ x509_signing_policies:
|
||||
- ST: Utah
|
||||
- L: Salt Lake City
|
||||
- basicConstraints: "critical CA:false"
|
||||
- keyUsage: "digitalSignature, nonRepudiation"
|
||||
- keyUsage: "critical keyEncipherment"
|
||||
- subjectKeyIdentifier: hash
|
||||
- authorityKeyIdentifier: keyid,issuer:always
|
||||
- days_valid: 820
|
||||
- copypath: /etc/pki/issued_certs/
|
||||
kafka:
|
||||
- minions: '*'
|
||||
- signing_private_key: /etc/pki/ca.key
|
||||
- signing_cert: /etc/pki/ca.crt
|
||||
- C: US
|
||||
- ST: Utah
|
||||
- L: Salt Lake City
|
||||
- basicConstraints: "critical CA:false"
|
||||
- keyUsage: "digitalSignature, keyEncipherment"
|
||||
- subjectKeyIdentifier: hash
|
||||
- authorityKeyIdentifier: keyid,issuer:always
|
||||
- extendedKeyUsage: "serverAuth, clientAuth"
|
||||
- extendedKeyUsage: serverAuth
|
||||
- days_valid: 820
|
||||
- copypath: /etc/pki/issued_certs/
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
{% from 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls in allowed_states %}
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
|
||||
|
||||
include:
|
||||
- ca.dirs
|
||||
|
||||
{% set manager = salt['grains.get']('master') %}
|
||||
/etc/salt/minion.d/signing_policies.conf:
|
||||
file.managed:
|
||||
- source: salt://ca/files/signing_policies.conf
|
||||
@@ -18,8 +12,9 @@ include:
|
||||
pki_private_key:
|
||||
x509.private_key_managed:
|
||||
- name: /etc/pki/ca.key
|
||||
- keysize: 4096
|
||||
- bits: 4096
|
||||
- passphrase:
|
||||
- cipher: aes_256_cbc
|
||||
- backup: True
|
||||
{% if salt['file.file_exists']('/etc/pki/ca.key') -%}
|
||||
- prereq:
|
||||
@@ -30,7 +25,7 @@ pki_public_ca_crt:
|
||||
x509.certificate_managed:
|
||||
- name: /etc/pki/ca.crt
|
||||
- signing_private_key: /etc/pki/ca.key
|
||||
- CN: {{ GLOBALS.manager }}
|
||||
- CN: {{ manager }}
|
||||
- C: US
|
||||
- ST: Utah
|
||||
- L: Salt Lake City
|
||||
@@ -38,7 +33,7 @@ pki_public_ca_crt:
|
||||
- keyUsage: "critical cRLSign, keyCertSign"
|
||||
- extendedkeyUsage: "serverAuth, clientAuth"
|
||||
- subjectKeyIdentifier: hash
|
||||
- authorityKeyIdentifier: keyid:always, issuer
|
||||
- authorityKeyIdentifier: keyid,issuer:always
|
||||
- days_valid: 3650
|
||||
- days_remaining: 0
|
||||
- backup: True
|
||||
@@ -50,12 +45,6 @@ pki_public_ca_crt:
|
||||
attempts: 5
|
||||
interval: 30
|
||||
|
||||
mine_update_ca_crt:
|
||||
module.run:
|
||||
- mine.update: []
|
||||
- onchanges:
|
||||
- x509: pki_public_ca_crt
|
||||
|
||||
cakeyperms:
|
||||
file.managed:
|
||||
- replace: False
|
||||
|
||||
2
salt/common/cron/common-rotate
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
/usr/sbin/logrotate -f /opt/so/conf/log-rotate.conf > /dev/null 2>&1
|
||||
2
salt/common/cron/sensor-rotate
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
/usr/sbin/logrotate -f /opt/so/conf/sensor-rotate.conf > /dev/null 2>&1
|
||||
79
salt/common/files/analyst/README
Normal file
@@ -0,0 +1,79 @@
|
||||
The following GUI tools are available on the analyst workstation:
|
||||
|
||||
chromium
|
||||
url: https://www.chromium.org/Home
|
||||
To run chromium, click Applications > Internet > Chromium Web Browser
|
||||
|
||||
Wireshark
|
||||
url: https://www.wireshark.org/
|
||||
To run Wireshark, click Applications > Internet > Wireshark Network Analyzer
|
||||
|
||||
NetworkMiner
|
||||
url: https://www.netresec.com
|
||||
To run NetworkMiner, click Applications > Internet > NetworkMiner
|
||||
|
||||
The following CLI tools are available on the analyst workstation:
|
||||
|
||||
bit-twist
|
||||
url: http://bittwist.sourceforge.net
|
||||
To run bit-twist, open a terminal and type: bittwist -h
|
||||
|
||||
chaosreader
|
||||
url: http://chaosreader.sourceforge.net
|
||||
To run chaosreader, open a terminal and type: chaosreader -h
|
||||
|
||||
dnsiff
|
||||
url: https://www.monkey.org/~dugsong/dsniff/
|
||||
To run dsniff, open a terminal and type: dsniff -h
|
||||
|
||||
foremost
|
||||
url: http://foremost.sourceforge.net
|
||||
To run foremost, open a terminal and type: foremost -h
|
||||
|
||||
hping3
|
||||
url: http://www.hping.org/hping3.html
|
||||
To run hping3, open a terminal and type: hping3 -h
|
||||
|
||||
netsed
|
||||
url: http://silicone.homelinux.org/projects/netsed/
|
||||
To run netsed, open a terminal and type: netsed -h
|
||||
|
||||
ngrep
|
||||
url: https://github.com/jpr5/ngrep
|
||||
To run ngrep, open a terminal and type: ngrep -h
|
||||
|
||||
scapy
|
||||
url: http://www.secdev.org/projects/scapy/
|
||||
To run scapy, open a terminal and type: scapy
|
||||
|
||||
ssldump
|
||||
url: http://www.rtfm.com/ssldump/
|
||||
To run ssldump, open a terminal and type: ssldump -h
|
||||
|
||||
sslsplit
|
||||
url: https://github.com/droe/sslsplit
|
||||
To run sslsplit, open a terminal and type: sslsplit -h
|
||||
|
||||
tcpdump
|
||||
url: http://www.tcpdump.org
|
||||
To run tcpdump, open a terminal and type: tcpdump -h
|
||||
|
||||
tcpflow
|
||||
url: https://github.com/simsong/tcpflow
|
||||
To run tcpflow, open a terminal and type: tcpflow -h
|
||||
|
||||
tcpstat
|
||||
url: https://frenchfries.net/paul/tcpstat/
|
||||
To run tcpstat, open a terminal and type: tcpstat -h
|
||||
|
||||
tcptrace
|
||||
url: http://www.tcptrace.org
|
||||
To run tcptrace, open a terminal and type: tcptrace -h
|
||||
|
||||
tcpxtract
|
||||
url: http://tcpxtract.sourceforge.net/
|
||||
To run tcpxtract, open a terminal and type: tcpxtract -h
|
||||
|
||||
whois
|
||||
url: http://www.linux.it/~md/software/
|
||||
To run whois, open a terminal and type: whois -h
|
||||
|
Before Width: | Height: | Size: 269 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 319 KiB After Width: | Height: | Size: 319 KiB |
@@ -1,11 +1,11 @@
|
||||
{%- set DOCKERRANGE = salt['pillar.get']('docker:range', '172.17.0.0/24') %}
|
||||
{%- set DOCKERBIND = salt['pillar.get']('docker:bip', '172.17.0.1/24') %}
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://:5000"
|
||||
],
|
||||
"bip": "172.17.0.1/24",
|
||||
"registry-mirrors": [ "https://:5000" ],
|
||||
"bip": "{{ DOCKERBIND }}",
|
||||
"default-address-pools": [
|
||||
{
|
||||
"base": "172.17.0.0/24",
|
||||
"base" : "{{ DOCKERRANGE }}",
|
||||
"size" : 24
|
||||
}
|
||||
]
|
||||
|
||||
37
salt/common/files/log-rotate.conf
Normal file
@@ -0,0 +1,37 @@
|
||||
{%- set logrotate_conf = salt['pillar.get']('logrotate:conf') %}
|
||||
{%- set group_conf = salt['pillar.get']('logrotate:group_conf') %}
|
||||
|
||||
|
||||
/opt/so/log/aptcacher-ng/*.log
|
||||
/opt/so/log/idstools/*.log
|
||||
/opt/so/log/nginx/*.log
|
||||
/opt/so/log/soc/*.log
|
||||
/opt/so/log/kratos/*.log
|
||||
/opt/so/log/kibana/*.log
|
||||
/opt/so/log/influxdb/*.log
|
||||
/opt/so/log/elastalert/*.log
|
||||
/opt/so/log/soctopus/*.log
|
||||
/opt/so/log/curator/*.log
|
||||
/opt/so/log/fleet/*.log
|
||||
/opt/so/log/suricata/*.log
|
||||
/opt/so/log/mysql/*.log
|
||||
/opt/so/log/telegraf/*.log
|
||||
/opt/so/log/redis/*.log
|
||||
/opt/so/log/sensoroni/*.log
|
||||
/opt/so/log/stenographer/*.log
|
||||
/opt/so/log/salt/so-salt-minion-check
|
||||
/opt/so/log/salt/minion
|
||||
/opt/so/log/salt/master
|
||||
/opt/so/log/logscan/*.log
|
||||
/nsm/idh/*.log
|
||||
{
|
||||
{{ logrotate_conf | indent(width=4) }}
|
||||
}
|
||||
|
||||
# Playbook's log directory needs additional configuration
|
||||
# because Playbook requires a more permissive directory
|
||||
/opt/so/log/playbook/*.log
|
||||
{
|
||||
{{ logrotate_conf | indent(width=4) }}
|
||||
{{ group_conf | indent(width=4) }}
|
||||
}
|
||||
22
salt/common/files/sensor-rotate.conf
Normal file
@@ -0,0 +1,22 @@
|
||||
/opt/so/log/sensor_clean.log
|
||||
{
|
||||
daily
|
||||
rotate 2
|
||||
missingok
|
||||
nocompress
|
||||
create
|
||||
sharedscripts
|
||||
}
|
||||
|
||||
/nsm/strelka/log/strelka.log
|
||||
{
|
||||
daily
|
||||
rotate 14
|
||||
missingok
|
||||
copytruncate
|
||||
compress
|
||||
create
|
||||
extension .log
|
||||
dateext
|
||||
dateyesterday
|
||||
}
|
||||
@@ -3,3 +3,4 @@ filetype plugin indent on
|
||||
|
||||
" Sets .sls files to use YAML syntax highlighting
|
||||
autocmd BufNewFile,BufRead *.sls set syntax=yaml
|
||||
set number
|
||||
@@ -1,21 +0,0 @@
|
||||
# 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 %}
|
||||
@@ -1,30 +1,25 @@
|
||||
{% from 'allowed_states.map.jinja' import allowed_states %}
|
||||
{% if sls in allowed_states %}
|
||||
|
||||
{% from 'vars/globals.map.jinja' import GLOBALS %}
|
||||
{% set role = grains.id.split('_') | last %}
|
||||
{% from 'elasticsearch/auth.map.jinja' import ELASTICAUTH with context %}
|
||||
|
||||
include:
|
||||
- common.grains
|
||||
- common.packages
|
||||
{% if GLOBALS.role in GLOBALS.manager_roles %}
|
||||
- common.soup_scripts
|
||||
{% if grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch', 'so-import'] %}
|
||||
- manager.elasticsearch # needed for elastic_curl_config state
|
||||
- manager.kibana
|
||||
{% endif %}
|
||||
|
||||
net.core.wmem_default:
|
||||
sysctl.present:
|
||||
- value: 26214400
|
||||
|
||||
# Users are not a fan of console messages
|
||||
kernel.printk:
|
||||
sysctl.present:
|
||||
- value: "3 4 1 3"
|
||||
|
||||
# Remove variables.txt from /tmp - This is temp
|
||||
rmvariablesfile:
|
||||
file.absent:
|
||||
- name: /tmp/variables.txt
|
||||
|
||||
dockergroup:
|
||||
group.present:
|
||||
- name: docker
|
||||
- gid: 920
|
||||
|
||||
# Add socore Group
|
||||
socoregroup:
|
||||
group.present:
|
||||
@@ -43,15 +38,15 @@ socore:
|
||||
soconfperms:
|
||||
file.directory:
|
||||
- name: /opt/so/conf
|
||||
- user: 939
|
||||
- group: 939
|
||||
- uid: 939
|
||||
- gid: 939
|
||||
- dir_mode: 770
|
||||
|
||||
sostatusconf:
|
||||
file.directory:
|
||||
- name: /opt/so/conf/so-status
|
||||
- user: 939
|
||||
- group: 939
|
||||
- uid: 939
|
||||
- gid: 939
|
||||
- dir_mode: 770
|
||||
|
||||
so-status.conf:
|
||||
@@ -59,11 +54,12 @@ so-status.conf:
|
||||
- name: /opt/so/conf/so-status/so-status.conf
|
||||
- unless: ls /opt/so/conf/so-status/so-status.conf
|
||||
|
||||
socore_opso_perms:
|
||||
sosaltstackperms:
|
||||
file.directory:
|
||||
- name: /opt/so
|
||||
- user: 939
|
||||
- group: 939
|
||||
- name: /opt/so/saltstack
|
||||
- uid: 939
|
||||
- gid: 939
|
||||
- dir_mode: 770
|
||||
|
||||
so_log_perms:
|
||||
file.directory:
|
||||
@@ -92,6 +88,92 @@ vimconfig:
|
||||
- source: salt://common/files/vimrc
|
||||
- replace: False
|
||||
|
||||
# Install common packages
|
||||
{% if grains['os'] != 'CentOS' %}
|
||||
commonpkgs:
|
||||
pkg.installed:
|
||||
- skip_suggestions: True
|
||||
- pkgs:
|
||||
- apache2-utils
|
||||
- wget
|
||||
- ntpdate
|
||||
- jq
|
||||
- python3-docker
|
||||
- curl
|
||||
- ca-certificates
|
||||
- software-properties-common
|
||||
- apt-transport-https
|
||||
- openssl
|
||||
- netcat
|
||||
- python3-mysqldb
|
||||
- sqlite3
|
||||
- libssl-dev
|
||||
- python3-dateutil
|
||||
- python3-m2crypto
|
||||
- python3-mysqldb
|
||||
- python3-packaging
|
||||
- python3-lxml
|
||||
- git
|
||||
- vim
|
||||
|
||||
heldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
{% if grains['oscodename'] == 'bionic' %}
|
||||
- containerd.io: 1.4.4-1
|
||||
- docker-ce: 5:20.10.5~3-0~ubuntu-bionic
|
||||
- docker-ce-cli: 5:20.10.5~3-0~ubuntu-bionic
|
||||
- docker-ce-rootless-extras: 5:20.10.5~3-0~ubuntu-bionic
|
||||
{% elif grains['oscodename'] == 'focal' %}
|
||||
- containerd.io: 1.4.9-1
|
||||
- docker-ce: 5:20.10.8~3-0~ubuntu-focal
|
||||
- docker-ce-cli: 5:20.10.5~3-0~ubuntu-focal
|
||||
- docker-ce-rootless-extras: 5:20.10.5~3-0~ubuntu-focal
|
||||
{% endif %}
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
|
||||
{% else %}
|
||||
commonpkgs:
|
||||
pkg.installed:
|
||||
- skip_suggestions: True
|
||||
- pkgs:
|
||||
- wget
|
||||
- ntpdate
|
||||
- bind-utils
|
||||
- jq
|
||||
- tcpdump
|
||||
- httpd-tools
|
||||
- net-tools
|
||||
- curl
|
||||
- sqlite
|
||||
- mariadb-devel
|
||||
- nmap-ncat
|
||||
- python3
|
||||
- python36-docker
|
||||
- python36-dateutil
|
||||
- python36-m2crypto
|
||||
- python36-mysql
|
||||
- python36-packaging
|
||||
- python36-lxml
|
||||
- yum-utils
|
||||
- device-mapper-persistent-data
|
||||
- lvm2
|
||||
- openssl
|
||||
- git
|
||||
- vim-enhanced
|
||||
|
||||
heldpackages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- containerd.io: 1.4.4-3.1.el7
|
||||
- docker-ce: 3:20.10.5-3.el7
|
||||
- docker-ce-cli: 1:20.10.5-3.el7
|
||||
- docker-ce-rootless-extras: 20.10.5-3.el7
|
||||
- hold: True
|
||||
- update_holds: True
|
||||
{% endif %}
|
||||
|
||||
# Always keep these packages up to date
|
||||
|
||||
alwaysupdated:
|
||||
@@ -106,8 +188,7 @@ alwaysupdated:
|
||||
Etc/UTC:
|
||||
timezone.system
|
||||
|
||||
# Sync curl configuration for Elasticsearch authentication
|
||||
{% if GLOBALS.is_manager or GLOBALS.role in ['so-heavynode', 'so-searchnode'] %}
|
||||
{% if salt['pillar.get']('elasticsearch:auth:enabled', False) %}
|
||||
elastic_curl_config:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/elasticsearch/curl.config
|
||||
@@ -115,82 +196,87 @@ elastic_curl_config:
|
||||
- mode: 600
|
||||
- show_changes: False
|
||||
- makedirs: True
|
||||
{% if GLOBALS.role in GLOBALS.manager_roles %}
|
||||
{% if grains.role in ['so-eval', 'so-manager', 'so-standalone', 'so-managersearch', 'so-import'] %}
|
||||
- require:
|
||||
- file: elastic_curl_config_distributed
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
common_sbin:
|
||||
# Sync some Utilities
|
||||
utilsyncscripts:
|
||||
file.recurse:
|
||||
- name: /usr/sbin
|
||||
- source: salt://common/tools/sbin
|
||||
- user: 939
|
||||
- group: 939
|
||||
- file_mode: 755
|
||||
- show_changes: False
|
||||
{% if GLOBALS.role == 'so-heavynode' %}
|
||||
- exclude_pat:
|
||||
- so-pcap-import
|
||||
{% endif %}
|
||||
|
||||
common_sbin_jinja:
|
||||
file.recurse:
|
||||
- name: /usr/sbin
|
||||
- source: salt://common/tools/sbin_jinja
|
||||
- user: 939
|
||||
- group: 939
|
||||
- user: root
|
||||
- group: root
|
||||
- file_mode: 755
|
||||
- template: jinja
|
||||
- show_changes: False
|
||||
{% if GLOBALS.role == 'so-heavynode' %}
|
||||
- source: salt://common/tools/sbin
|
||||
- defaults:
|
||||
ELASTICCURL: 'curl'
|
||||
- context:
|
||||
ELASTICCURL: {{ ELASTICAUTH.elasticcurl }}
|
||||
- exclude_pat:
|
||||
- so-import-pcap
|
||||
{% endif %}
|
||||
- so-common
|
||||
- so-firewall
|
||||
- so-image-common
|
||||
- soup
|
||||
|
||||
{% if GLOBALS.role == 'so-heavynode' %}
|
||||
remove_so-pcap-import_heavynode:
|
||||
file.absent:
|
||||
- name: /usr/sbin/so-pcap-import
|
||||
|
||||
remove_so-import-pcap_heavynode:
|
||||
file.absent:
|
||||
- name: /usr/sbin/so-import-pcap
|
||||
{% endif %}
|
||||
|
||||
{% if not GLOBALS.is_manager%}
|
||||
# prior to 2.4.50 these scripts were in common/tools/sbin on the manager because of soup and distributed to non managers
|
||||
# these two states remove the scripts from non manager nodes
|
||||
remove_soup:
|
||||
file.absent:
|
||||
- name: /usr/sbin/soup
|
||||
|
||||
remove_so-firewall:
|
||||
file.absent:
|
||||
- name: /usr/sbin/so-firewall
|
||||
{% endif %}
|
||||
|
||||
so-status_script:
|
||||
file.managed:
|
||||
- name: /usr/sbin/so-status
|
||||
- source: salt://common/tools/sbin/so-status
|
||||
- mode: 755
|
||||
|
||||
{% if GLOBALS.role in GLOBALS.sensor_roles %}
|
||||
{% if role in ['eval', 'standalone', 'sensor', 'heavynode'] %}
|
||||
# Add sensor cleanup
|
||||
so-sensor-clean:
|
||||
/usr/sbin/so-sensor-clean:
|
||||
cron.present:
|
||||
- name: /usr/sbin/so-sensor-clean
|
||||
- identifier: so-sensor-clean
|
||||
- user: root
|
||||
- minute: '*'
|
||||
- hour: '*'
|
||||
- daymonth: '*'
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
|
||||
sensorrotatescript:
|
||||
file.managed:
|
||||
- name: /usr/local/bin/sensor-rotate
|
||||
- source: salt://common/cron/sensor-rotate
|
||||
- mode: 755
|
||||
|
||||
sensorrotateconf:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/sensor-rotate.conf
|
||||
- source: salt://common/files/sensor-rotate.conf
|
||||
- mode: 644
|
||||
|
||||
/usr/local/bin/sensor-rotate:
|
||||
cron.present:
|
||||
- user: root
|
||||
- minute: '1'
|
||||
- hour: '0'
|
||||
- daymonth: '*'
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
|
||||
{% endif %}
|
||||
|
||||
commonlogrotatescript:
|
||||
file.managed:
|
||||
- name: /usr/local/bin/common-rotate
|
||||
- source: salt://common/cron/common-rotate
|
||||
- mode: 755
|
||||
|
||||
commonlogrotateconf:
|
||||
file.managed:
|
||||
- name: /opt/so/conf/log-rotate.conf
|
||||
- source: salt://common/files/log-rotate.conf
|
||||
- template: jinja
|
||||
- mode: 644
|
||||
|
||||
/usr/local/bin/common-rotate:
|
||||
cron.present:
|
||||
- user: root
|
||||
- minute: '1'
|
||||
- hour: '0'
|
||||
- daymonth: '*'
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
|
||||
# Create the status directory
|
||||
sostatusdir:
|
||||
file.directory:
|
||||
@@ -203,13 +289,10 @@ sostatus_log:
|
||||
file.managed:
|
||||
- name: /opt/so/log/sostatus/status.log
|
||||
- mode: 644
|
||||
- replace: False
|
||||
|
||||
# Install sostatus check cron. This is used to populate Grid.
|
||||
so-status_check_cron:
|
||||
# Install sostatus check cron
|
||||
'/usr/sbin/so-status -q; echo $? > /opt/so/log/sostatus/status.log 2>&1':
|
||||
cron.present:
|
||||
- name: '/usr/sbin/so-status -j > /opt/so/log/sostatus/status.log 2>&1'
|
||||
- identifier: so-status_check_cron
|
||||
- user: root
|
||||
- minute: '*/1'
|
||||
- hour: '*'
|
||||
@@ -217,21 +300,36 @@ so-status_check_cron:
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
|
||||
# This cronjob/script runs a check if the node needs restarted, but should be used for future status checks as well
|
||||
common_status_check_cron:
|
||||
{% if role in ['eval', 'manager', 'managersearch', 'standalone'] %}
|
||||
# Install cron job to determine size of influxdb for telegraf
|
||||
'du -s -k /nsm/influxdb | cut -f1 > /opt/so/log/telegraf/influxdb_size.log 2>&1':
|
||||
cron.present:
|
||||
- name: '/usr/sbin/so-common-status-check > /dev/null 2>&1'
|
||||
- identifier: common_status_check
|
||||
- user: root
|
||||
- minute: '*/10'
|
||||
- minute: '*/1'
|
||||
- hour: '*'
|
||||
- daymonth: '*'
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
|
||||
remove_post_setup_cron:
|
||||
cron.absent:
|
||||
- name: 'PATH=$PATH:/usr/sbin salt-call state.highstate'
|
||||
- identifier: post_setup_cron
|
||||
|
||||
{% if GLOBALS.role not in ['eval', 'manager', 'managersearch', 'standalone'] %}
|
||||
# Lock permissions on the backup directory
|
||||
backupdir:
|
||||
file.directory:
|
||||
- name: /nsm/backup
|
||||
- user: 0
|
||||
- group: 0
|
||||
- makedirs: True
|
||||
- mode: 700
|
||||
|
||||
# Add config backup
|
||||
/usr/sbin/so-config-backup > /dev/null 2>&1:
|
||||
cron.present:
|
||||
- user: root
|
||||
- minute: '1'
|
||||
- hour: '0'
|
||||
- daymonth: '*'
|
||||
- month: '*'
|
||||
- dayweek: '*'
|
||||
{% else %}
|
||||
soversionfile:
|
||||
file.managed:
|
||||
- name: /etc/soversion
|
||||
@@ -241,8 +339,34 @@ soversionfile:
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if GLOBALS.so_model and GLOBALS.so_model not in ['SO2AMI01', 'SO2AZI01', 'SO2GCI01'] %}
|
||||
{% if GLOBALS.os == 'OEL' %}
|
||||
# Manager daemon.json
|
||||
docker_daemon:
|
||||
file.managed:
|
||||
- source: salt://common/files/daemon.json
|
||||
- name: /etc/docker/daemon.json
|
||||
- template: jinja
|
||||
|
||||
# Make sure Docker is always running
|
||||
docker:
|
||||
service.running:
|
||||
- enable: True
|
||||
- watch:
|
||||
- file: docker_daemon
|
||||
|
||||
# Reserve OS ports for Docker proxy in case boot settings are not already applied/present
|
||||
# 55000 = Wazuh, 57314 = Strelka, 47760-47860 = Zeek
|
||||
dockerapplyports:
|
||||
cmd.run:
|
||||
- name: if [ ! -s /etc/sysctl.d/99-reserved-ports.conf ]; then sysctl -w net.ipv4.ip_local_reserved_ports="55000,57314,47760-47860"; fi
|
||||
|
||||
# Reserve OS ports for Docker proxy
|
||||
dockerreserveports:
|
||||
file.managed:
|
||||
- source: salt://common/files/99-reserved-ports.conf
|
||||
- name: /etc/sysctl.d/99-reserved-ports.conf
|
||||
|
||||
{% if salt['grains.get']('sosmodel', '') %}
|
||||
{% if grains['os'] == 'CentOS' %}
|
||||
# Install Raid tools
|
||||
raidpkgs:
|
||||
pkg.installed:
|
||||
@@ -253,10 +377,8 @@ raidpkgs:
|
||||
{% endif %}
|
||||
|
||||
# Install raid check cron
|
||||
so-raid-status:
|
||||
/usr/sbin/so-raid-status > /dev/null 2>&1:
|
||||
cron.present:
|
||||
- name: '/usr/sbin/so-raid-status > /dev/null 2>&1'
|
||||
- identifier: so-raid-status
|
||||
- user: root
|
||||
- minute: '*/15'
|
||||
- hour: '*'
|
||||
@@ -265,6 +387,7 @@ so-raid-status:
|
||||
- dayweek: '*'
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{{sls}}_state_not_allowed:
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
# we cannot import GLOBALS from vars/globals.map.jinja in this state since it is called in setup.virt.init
|
||||
# since it is early in setup of a new VM, the pillars imported in GLOBALS are not yet defined
|
||||
{% if grains.os_family == 'Debian' %}
|
||||
commonpkgs:
|
||||
pkg.installed:
|
||||
- skip_suggestions: True
|
||||
- pkgs:
|
||||
- apache2-utils
|
||||
- wget
|
||||
- ntpdate
|
||||
- jq
|
||||
- curl
|
||||
- ca-certificates
|
||||
- software-properties-common
|
||||
- apt-transport-https
|
||||
- openssl
|
||||
- netcat-openbsd
|
||||
- sqlite3
|
||||
- libssl-dev
|
||||
- procps
|
||||
- python3-dateutil
|
||||
- python3-docker
|
||||
- python3-packaging
|
||||
- python3-lxml
|
||||
- git
|
||||
- rsync
|
||||
- vim
|
||||
- tar
|
||||
- unzip
|
||||
- bc
|
||||
{% if grains.oscodename != 'focal' %}
|
||||
- python3-rich
|
||||
{% endif %}
|
||||
|
||||
{% if grains.oscodename == 'focal' %}
|
||||
# since Ubuntu requires and internet connection we can use pip to install modules
|
||||
python3-pip:
|
||||
pkg.installed
|
||||
|
||||
python-rich:
|
||||
pip.installed:
|
||||
- name: rich
|
||||
- target: /usr/local/lib/python3.8/dist-packages/
|
||||
- require:
|
||||
- pkg: python3-pip
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if grains.os_family == 'RedHat' %}
|
||||
|
||||
remove_mariadb:
|
||||
pkg.removed:
|
||||
- name: mariadb-devel
|
||||
|
||||
commonpkgs:
|
||||
pkg.installed:
|
||||
- skip_suggestions: True
|
||||
- pkgs:
|
||||
- python3-dnf-plugin-versionlock
|
||||
- bc
|
||||
- curl
|
||||
- device-mapper-persistent-data
|
||||
- fuse
|
||||
- fuse-libs
|
||||
- fuse-overlayfs
|
||||
- fuse-common
|
||||
- fuse3
|
||||
- fuse3-libs
|
||||
- git
|
||||
- httpd-tools
|
||||
- jq
|
||||
- lvm2
|
||||
- net-tools
|
||||
- nmap-ncat
|
||||
- procps-ng
|
||||
- python3-docker
|
||||
- python3-m2crypto
|
||||
- python3-packaging
|
||||
- python3-pyyaml
|
||||
- python3-rich
|
||||
- rsync
|
||||
- sqlite
|
||||
- tcpdump
|
||||
- unzip
|
||||
- wget
|
||||
- yum-utils
|
||||
|
||||
{% endif %}
|
||||
@@ -1,142 +1,13 @@
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
{% if '2.4' in salt['cp.get_file_str']('/etc/soversion') %}
|
||||
|
||||
{% import_yaml '/opt/so/saltstack/local/pillar/global/soc_global.sls' as SOC_GLOBAL %}
|
||||
{% if SOC_GLOBAL.global.airgap %}
|
||||
{% set UPDATE_DIR='/tmp/soagupdate/SecurityOnion' %}
|
||||
{% else %}
|
||||
{% set UPDATE_DIR='/tmp/sogh/securityonion' %}
|
||||
{% endif %}
|
||||
{% set SOVERSION = salt['file.read']('/etc/soversion').strip() %}
|
||||
|
||||
remove_common_soup:
|
||||
file.absent:
|
||||
- name: /opt/so/saltstack/default/salt/common/tools/sbin/soup
|
||||
|
||||
remove_common_so-firewall:
|
||||
file.absent:
|
||||
- name: /opt/so/saltstack/default/salt/common/tools/sbin/so-firewall
|
||||
|
||||
# This section is used to put the scripts in place in the Salt file system
|
||||
# in case a state run tries to overwrite what we do in the next section.
|
||||
copy_so-common_common_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/common/tools/sbin/so-common
|
||||
- source: {{UPDATE_DIR}}/salt/common/tools/sbin/so-common
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-image-common_common_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/common/tools/sbin/so-image-common
|
||||
- source: {{UPDATE_DIR}}/salt/common/tools/sbin/so-image-common
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_soup_manager_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/manager/tools/sbin/soup
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/soup
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-firewall_manager_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/manager/tools/sbin/so-firewall
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-firewall
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-yaml_manager_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/manager/tools/sbin/so-yaml.py
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-yaml.py
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-repo-sync_manager_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/manager/tools/sbin/so-repo-sync
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-repo-sync
|
||||
- preserve: True
|
||||
|
||||
copy_bootstrap-salt_manager_tools_sbin:
|
||||
file.copy:
|
||||
- name: /opt/so/saltstack/default/salt/salt/scripts/bootstrap-salt.sh
|
||||
- source: {{UPDATE_DIR}}/salt/salt/scripts/bootstrap-salt.sh
|
||||
- preserve: True
|
||||
|
||||
# This section is used to put the new script in place so that it can be called during soup.
|
||||
# It is faster than calling the states that normally manage them to put them in place.
|
||||
copy_so-common_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/so-common
|
||||
- source: {{UPDATE_DIR}}/salt/common/tools/sbin/so-common
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-image-common_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/so-image-common
|
||||
- source: {{UPDATE_DIR}}/salt/common/tools/sbin/so-image-common
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_soup_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/soup
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/soup
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-firewall_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/so-firewall
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-firewall
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-yaml_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/so-yaml.py
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-yaml.py
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_so-repo-sync_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/so-repo-sync
|
||||
- source: {{UPDATE_DIR}}/salt/manager/tools/sbin/so-repo-sync
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
copy_bootstrap-salt_sbin:
|
||||
file.copy:
|
||||
- name: /usr/sbin/bootstrap-salt.sh
|
||||
- source: {{UPDATE_DIR}}/salt/salt/scripts/bootstrap-salt.sh
|
||||
- force: True
|
||||
- preserve: True
|
||||
|
||||
{# this is added in 2.4.120 to remove salt repo files pointing to saltproject.io to accomodate the move to broadcom and new bootstrap-salt script #}
|
||||
{% if salt['pkg.version_cmp'](SOVERSION, '2.4.120') == -1 %}
|
||||
{% set saltrepofile = '/etc/yum.repos.d/salt.repo' %}
|
||||
{% if grains.os_family == 'Debian' %}
|
||||
{% set saltrepofile = '/etc/apt/sources.list.d/salt.list' %}
|
||||
{% endif %}
|
||||
remove_saltproject_io_repo_manager:
|
||||
file.absent:
|
||||
- name: {{ saltrepofile }}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
fix_23_soup_sbin:
|
||||
cmd.run:
|
||||
- name: curl -s -f -o /usr/sbin/soup https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.3/main/salt/common/tools/sbin/soup
|
||||
fix_23_soup_salt:
|
||||
cmd.run:
|
||||
- name: curl -s -f -o /opt/so/saltstack/defalt/salt/common/tools/sbin/soup https://raw.githubusercontent.com/Security-Onion-Solutions/securityonion/2.3/main/salt/common/tools/sbin/soup
|
||||
{% endif %}
|
||||
# Sync some Utilities
|
||||
soup_scripts:
|
||||
file.recurse:
|
||||
- name: /usr/sbin
|
||||
- user: root
|
||||
- group: root
|
||||
- file_mode: 755
|
||||
- source: salt://common/tools/sbin
|
||||
- include_pat:
|
||||
- so-common
|
||||
- so-firewall
|
||||
- so-image-common
|
||||
- soup
|
||||
|
||||
207
salt/common/tools/sbin/so-allow
Executable file
@@ -0,0 +1,207 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ipaddress
|
||||
import textwrap
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import argparse
|
||||
import re
|
||||
from lxml import etree as ET
|
||||
from datetime import datetime as dt
|
||||
from datetime import timezone as tz
|
||||
|
||||
|
||||
LOCAL_SALT_DIR='/opt/so/saltstack/local'
|
||||
WAZUH_CONF='/nsm/wazuh/etc/ossec.conf'
|
||||
VALID_ROLES = {
|
||||
'a': { 'role': 'analyst','desc': 'Analyst - 80/tcp, 443/tcp' },
|
||||
'b': { 'role': 'beats_endpoint', 'desc': 'Logstash Beat - 5044/tcp' },
|
||||
'e': { 'role': 'elasticsearch_rest', 'desc': 'Elasticsearch REST API - 9200/tcp' },
|
||||
'f': { 'role': 'strelka_frontend', 'desc': 'Strelka frontend - 57314/tcp' },
|
||||
'o': { 'role': 'osquery_endpoint', 'desc': 'Osquery endpoint - 8090/tcp' },
|
||||
's': { 'role': 'syslog', 'desc': 'Syslog device - 514/tcp/udp' },
|
||||
'w': { 'role': 'wazuh_agent', 'desc': 'Wazuh agent - 1514/tcp/udp' },
|
||||
'p': { 'role': 'wazuh_api', 'desc': 'Wazuh API - 55000/tcp' },
|
||||
'r': { 'role': 'wazuh_authd', 'desc': 'Wazuh registration service - 1515/tcp' }
|
||||
}
|
||||
|
||||
|
||||
def validate_ip_cidr(ip_cidr: str) -> bool:
|
||||
try:
|
||||
ipaddress.ip_address(ip_cidr)
|
||||
except ValueError:
|
||||
try:
|
||||
ipaddress.ip_network(ip_cidr)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def role_prompt() -> str:
|
||||
print()
|
||||
print('Choose the role for the IP or Range you would like to allow')
|
||||
print()
|
||||
for role in VALID_ROLES:
|
||||
print(f'[{role}] - {VALID_ROLES[role]["desc"]}')
|
||||
print()
|
||||
role = input('Please enter your selection: ')
|
||||
if role in VALID_ROLES.keys():
|
||||
return VALID_ROLES[role]['role']
|
||||
else:
|
||||
print(f'Invalid role \'{role}\', please try again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def ip_prompt() -> str:
|
||||
ip = input('Enter a single ip address or range to allow (ex: 10.10.10.10 or 10.10.0.0/16): ')
|
||||
if validate_ip_cidr(ip):
|
||||
return ip
|
||||
else:
|
||||
print(f'Invalid IP address or CIDR block \'{ip}\', please try again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def wazuh_enabled() -> bool:
|
||||
file = f'{LOCAL_SALT_DIR}/pillar/global.sls'
|
||||
with open(file, 'r') as pillar:
|
||||
if 'wazuh: 1' in pillar.read():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def root_to_str(root: ET.ElementTree) -> str:
|
||||
return ET.tostring(root, encoding='unicode', method='xml', xml_declaration=False, pretty_print=True)
|
||||
|
||||
|
||||
def add_wl(ip):
|
||||
parser = ET.XMLParser(remove_blank_text=True)
|
||||
with open(WAZUH_CONF, 'rb') as wazuh_conf:
|
||||
tree = ET.parse(wazuh_conf, parser)
|
||||
root = tree.getroot()
|
||||
|
||||
source_comment = ET.Comment(f'Address {ip} added by /usr/sbin/so-allow on {dt.utcnow().replace(tzinfo=tz.utc).strftime("%a %b %e %H:%M:%S %Z %Y")}')
|
||||
new_global = ET.Element("global")
|
||||
new_wl = ET.SubElement(new_global, 'white_list')
|
||||
new_wl.text = ip
|
||||
|
||||
root.append(source_comment)
|
||||
root.append(new_global)
|
||||
|
||||
with open(WAZUH_CONF, 'w') as add_out:
|
||||
add_out.write(root_to_str(root))
|
||||
|
||||
|
||||
def apply(role: str, ip: str) -> int:
|
||||
firewall_cmd = ['so-firewall', 'includehost', role, ip]
|
||||
salt_cmd = ['salt-call', 'state.apply', '-l', 'quiet', 'firewall', 'queue=True']
|
||||
restart_wazuh_cmd = ['so-wazuh-restart']
|
||||
print(f'Adding {ip} to the {role} role. This can take a few seconds...')
|
||||
cmd = subprocess.run(firewall_cmd)
|
||||
if cmd.returncode == 0:
|
||||
cmd = subprocess.run(salt_cmd, stdout=subprocess.DEVNULL)
|
||||
else:
|
||||
return cmd.returncode
|
||||
if cmd.returncode == 0:
|
||||
if wazuh_enabled() and role=='analyst':
|
||||
try:
|
||||
add_wl(ip)
|
||||
print(f'Added whitelist entry for {ip} from {WAZUH_CONF}', file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f'Failed to add whitelist entry for {ip} from {WAZUH_CONF}', file=sys.stderr)
|
||||
print(e)
|
||||
return 1
|
||||
print('Restarting OSSEC Server...')
|
||||
cmd = subprocess.run(restart_wazuh_cmd)
|
||||
else:
|
||||
return cmd.returncode
|
||||
else:
|
||||
print(f'Commmand \'{" ".join(salt_cmd)}\' failed.', file=sys.stderr)
|
||||
return cmd.returncode
|
||||
if cmd.returncode != 0:
|
||||
print('Failed to restart OSSEC server.')
|
||||
return cmd.returncode
|
||||
|
||||
|
||||
def main():
|
||||
if os.geteuid() != 0:
|
||||
print('You must run this script as root', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
main_parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=textwrap.dedent(f'''\
|
||||
additional information:
|
||||
To use this script in interactive mode call it with no arguments
|
||||
'''
|
||||
))
|
||||
|
||||
group = main_parser.add_argument_group(title='roles')
|
||||
group.add_argument('-a', dest='roles', action='append_const', const=VALID_ROLES['a']['role'], help="Analyst - 80/tcp, 443/tcp")
|
||||
group.add_argument('-b', dest='roles', action='append_const', const=VALID_ROLES['b']['role'], help="Logstash Beat - 5044/tcp")
|
||||
group.add_argument('-e', dest='roles', action='append_const', const=VALID_ROLES['e']['role'], help="Elasticsearch REST API - 9200/tcp")
|
||||
group.add_argument('-f', dest='roles', action='append_const', const=VALID_ROLES['f']['role'], help="Strelka frontend - 57314/tcp")
|
||||
group.add_argument('-o', dest='roles', action='append_const', const=VALID_ROLES['o']['role'], help="Osquery endpoint - 8090/tcp")
|
||||
group.add_argument('-s', dest='roles', action='append_const', const=VALID_ROLES['s']['role'], help="Syslog device - 514/tcp/udp")
|
||||
group.add_argument('-w', dest='roles', action='append_const', const=VALID_ROLES['w']['role'], help="Wazuh agent - 1514/tcp/udp")
|
||||
group.add_argument('-p', dest='roles', action='append_const', const=VALID_ROLES['p']['role'], help="Wazuh API - 55000/tcp")
|
||||
group.add_argument('-r', dest='roles', action='append_const', const=VALID_ROLES['r']['role'], help="Wazuh registration service - 1515/tcp")
|
||||
|
||||
ip_g = main_parser.add_argument_group(title='allow')
|
||||
ip_g.add_argument('-i', help="IP or CIDR block to disallow connections from, requires at least one role argument", metavar='', dest='ip')
|
||||
|
||||
args = main_parser.parse_args(sys.argv[1:])
|
||||
|
||||
if args.roles is None:
|
||||
role = role_prompt()
|
||||
ip = ip_prompt()
|
||||
try:
|
||||
return_code = apply(role, ip)
|
||||
except Exception as e:
|
||||
print(f'Unexpected exception occurred: {e}', file=sys.stderr)
|
||||
return_code = e.errno
|
||||
sys.exit(return_code)
|
||||
elif args.roles is not None and args.ip is None:
|
||||
if os.environ.get('IP') is None:
|
||||
main_parser.print_help()
|
||||
sys.exit(1)
|
||||
else:
|
||||
args.ip = os.environ['IP']
|
||||
|
||||
if validate_ip_cidr(args.ip):
|
||||
try:
|
||||
for role in args.roles:
|
||||
return_code = apply(role, args.ip)
|
||||
if return_code > 0:
|
||||
break
|
||||
except Exception as e:
|
||||
print(f'Unexpected exception occurred: {e}', file=sys.stderr)
|
||||
return_code = e.errno
|
||||
else:
|
||||
print(f'Invalid IP address or CIDR block \'{args.ip}\', please try again.', file=sys.stderr)
|
||||
return_code = 1
|
||||
|
||||
sys.exit(return_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
|
||||
23
salt/common/tools/sbin/so-allow-view
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
echo ""
|
||||
echo "Hosts/Networks that have access to login to the Security Onion Console:"
|
||||
|
||||
so-firewall includedhosts analyst
|
||||
100
salt/common/tools/sbin/so-analyst-install
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
doc_workstation_url="https://docs.securityonion.net/en/2.3/analyst-vm.html"
|
||||
{# we only want the script to install the workstation if it is CentOS -#}
|
||||
{% if grains.os == 'CentOS' -%}
|
||||
{# if this is a manager -#}
|
||||
{% if grains.master == grains.id.split('_')|first -%}
|
||||
|
||||
source /usr/sbin/so-common
|
||||
pillar_file="/opt/so/saltstack/local/pillar/minions/{{grains.id}}.sls"
|
||||
|
||||
if [ -f "$pillar_file" ]; then
|
||||
if ! grep -q "^workstation:$" "$pillar_file"; then
|
||||
|
||||
FIRSTPASS=yes
|
||||
while [[ $INSTALL != "yes" ]] && [[ $INSTALL != "no" ]]; do
|
||||
if [[ "$FIRSTPASS" == "yes" ]]; then
|
||||
echo "###########################################"
|
||||
echo "## ** W A R N I N G ** ##"
|
||||
echo "## _______________________________ ##"
|
||||
echo "## ##"
|
||||
echo "## Installing the Security Onion ##"
|
||||
echo "## analyst node on this device will ##"
|
||||
echo "## make permanent changes to ##"
|
||||
echo "## the system. ##"
|
||||
echo "## A system reboot will be required ##"
|
||||
echo "## to complete the install. ##"
|
||||
echo "## ##"
|
||||
echo "###########################################"
|
||||
echo "Do you wish to continue? (Type the entire word 'yes' to proceed or 'no' to exit)"
|
||||
FIRSTPASS=no
|
||||
else
|
||||
echo "Please type 'yes' to continue or 'no' to exit."
|
||||
fi
|
||||
read INSTALL
|
||||
done
|
||||
|
||||
if [[ $INSTALL == "no" ]]; then
|
||||
echo "Exiting analyst node installation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Add workstation pillar to the minion's pillar file
|
||||
printf '%s\n'\
|
||||
"workstation:"\
|
||||
" gui:"\
|
||||
" enabled: true"\
|
||||
"" >> "$pillar_file"
|
||||
echo "Applying the workstation state. This could take some time since there are many packages that need to be installed."
|
||||
if salt-call state.apply workstation -linfo queue=True; then # make sure the state ran successfully
|
||||
echo ""
|
||||
echo "Analyst workstation has been installed!"
|
||||
echo "Press ENTER to reboot or Ctrl-C to cancel."
|
||||
read pause
|
||||
|
||||
reboot;
|
||||
else
|
||||
echo "There was an issue applying the workstation state. Please review the log above or at /opt/so/logs/salt/minion."
|
||||
fi
|
||||
else # workstation is already added
|
||||
echo "The workstation pillar already exists in $pillar_file."
|
||||
echo "To enable/disable the gui, set 'workstation:gui:enabled' to true or false in $pillar_file."
|
||||
echo "Additional documentation can be found at $doc_workstation_url."
|
||||
fi
|
||||
else # if the pillar file doesn't exist
|
||||
echo "Could not find $pillar_file and add the workstation pillar."
|
||||
fi
|
||||
|
||||
{#- if this is not a manager #}
|
||||
{% else -%}
|
||||
|
||||
echo "Since this is not a manager, the pillar values to enable analyst workstation must be set manually. Please view the documentation at $doc_workstation_url."
|
||||
|
||||
{#- endif if this is a manager #}
|
||||
{% endif -%}
|
||||
|
||||
{#- if not CentOS #}
|
||||
{%- else %}
|
||||
|
||||
echo "The Analyst Workstation can only be installed on CentOS. Please view the documentation at $doc_workstation_url."
|
||||
|
||||
{#- endif grains.os == CentOS #}
|
||||
{% endif -%}
|
||||
|
||||
exit 0
|
||||
@@ -1,17 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
cat << EOF
|
||||
|
||||
so-checkin will run a full salt highstate to apply all salt states. If a highstate is already running, this request will be queued and so it may pause for a few minutes before you see any more output. For more information about so-checkin and salt, please see:
|
||||
https://docs.securityonion.net/en/2.4/salt.html
|
||||
|
||||
EOF
|
||||
|
||||
salt-call state.highstate -l info queue=True
|
||||
salt-call state.highstate -l info
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||
# Elastic License 2.0.
|
||||
|
||||
# Elastic agent is not managed by salt. Because of this we must store this base information in a
|
||||
# script that accompanies the soup system. Since so-common is one of those special soup files,
|
||||
# and since this same logic is required during installation, it's included in this file.
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DEFAULT_SALT_DIR=/opt/so/saltstack/default
|
||||
DOC_BASE_URL="https://docs.securityonion.net/en/2.4"
|
||||
|
||||
if [ -z $NOROOT ]; then
|
||||
# Check for prerequisites
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run using sudo!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure /usr/sbin is in path
|
||||
if ! echo "$PATH" | grep -q "/usr/sbin"; then
|
||||
export PATH="$PATH:/usr/sbin"
|
||||
fi
|
||||
|
||||
# See if a proxy is set. If so use it.
|
||||
if [ -f /etc/profile.d/so-proxy.sh ]; then
|
||||
. /etc/profile.d/so-proxy.sh
|
||||
fi
|
||||
|
||||
# Define a banner to separate sections
|
||||
banner="========================================================================="
|
||||
@@ -63,8 +56,6 @@ add_interface_bond0() {
|
||||
ethtool -K "$BNIC" $i off &>/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
if ! [[ $is_cloud ]]; then
|
||||
# Check if the bond slave connection has already been created
|
||||
nmcli -f name,uuid -p con | grep -q "bond0-slave-$BNIC"
|
||||
local found_int=$?
|
||||
@@ -82,34 +73,21 @@ add_interface_bond0() {
|
||||
ethernet.mtu "$MTU" \
|
||||
connection.autoconnect "yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
ip link set dev "$BNIC" arp off multicast off allmulticast off promisc on
|
||||
|
||||
if ! [[ $is_cloud ]]; then
|
||||
# Bring the slave interface up
|
||||
if [[ $verbose == true ]]; then
|
||||
nmcli con up "bond0-slave-$BNIC"
|
||||
else
|
||||
nmcli con up "bond0-slave-$BNIC" &>/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$nic_error" != 0 ]; then
|
||||
return "$nic_error"
|
||||
fi
|
||||
}
|
||||
|
||||
airgap_playbooks() {
|
||||
SRC_DIR=$1
|
||||
# Copy playbooks if using airgap
|
||||
mkdir -p /nsm/airgap-resources
|
||||
# Purge old airgap playbooks to ensure SO only uses the latest released playbooks
|
||||
rm -fr /nsm/airgap-resources/playbooks
|
||||
tar xf $SRC_DIR/airgap-resources/playbooks.tgz -C /nsm/airgap-resources/
|
||||
chown -R socore:socore /nsm/airgap-resources/playbooks
|
||||
git config --global --add safe.directory /nsm/airgap-resources/playbooks
|
||||
}
|
||||
|
||||
check_container() {
|
||||
docker ps | grep "$1:" > /dev/null 2>&1
|
||||
return $?
|
||||
@@ -143,159 +121,56 @@ check_elastic_license() {
|
||||
}
|
||||
|
||||
check_salt_master_status() {
|
||||
local count=0
|
||||
local attempts="${1:- 10}"
|
||||
current_time="$(date '+%b %d %H:%M:%S')"
|
||||
echo "Checking if we can access the salt master and that it is ready at: ${current_time}"
|
||||
while ! salt-call state.show_top -l error concurrent=true 1> /dev/null; do
|
||||
current_time="$(date '+%b %d %H:%M:%S')"
|
||||
echo "Can't access salt master or it is not ready at: ${current_time}"
|
||||
((count+=1))
|
||||
if [[ $count -eq $attempts ]]; then
|
||||
# 10 attempts takes about 5.5 minutes
|
||||
echo "Gave up trying to access salt-master"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
current_time="$(date '+%b %d %H:%M:%S')"
|
||||
echo "Successfully accessed and salt master ready at: ${current_time}"
|
||||
return 0
|
||||
local timeout=$1
|
||||
echo "Checking if we can talk to the salt master"
|
||||
salt-call state.show_top concurrent=true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
# this is only intended to be used to check the status of the minion from a salt master
|
||||
check_salt_minion_status() {
|
||||
local minion="$1"
|
||||
local timeout="${2:-5}"
|
||||
local logfile="${3:-'/dev/stdout'}"
|
||||
echo "Checking if the salt minion: $minion will respond to jobs" >> "$logfile" 2>&1
|
||||
salt "$minion" test.ping -t $timeout > /dev/null 2>&1
|
||||
local timeout=$1
|
||||
echo "Checking if the salt minion will respond to jobs" >> "$setup_log" 2>&1
|
||||
salt "$MINION_ID" test.ping -t $timeout > /dev/null 2>&1
|
||||
local status=$?
|
||||
if [ $status -gt 0 ]; then
|
||||
echo " Minion did not respond" >> "$logfile" 2>&1
|
||||
echo " Minion did not respond" >> "$setup_log" 2>&1
|
||||
else
|
||||
echo " Received job response from salt minion" >> "$logfile" 2>&1
|
||||
echo " Received job response from salt minion" >> "$setup_log" 2>&1
|
||||
fi
|
||||
|
||||
return $status
|
||||
}
|
||||
|
||||
# Compare es versions and return the highest version
|
||||
compare_es_versions() {
|
||||
# Save the original IFS
|
||||
local OLD_IFS="$IFS"
|
||||
|
||||
IFS=.
|
||||
local i ver1=($1) ver2=($2)
|
||||
|
||||
# Restore the original IFS
|
||||
IFS="$OLD_IFS"
|
||||
|
||||
# Determine the maximum length between the two version arrays
|
||||
local max_len=${#ver1[@]}
|
||||
if [[ ${#ver2[@]} -gt $max_len ]]; then
|
||||
max_len=${#ver2[@]}
|
||||
fi
|
||||
|
||||
# Compare each segment of the versions
|
||||
for ((i=0; i<max_len; i++)); do
|
||||
# If a segment in ver1 or ver2 is missing, set it to 0
|
||||
if [[ -z ${ver1[i]} ]]; then
|
||||
ver1[i]=0
|
||||
fi
|
||||
if [[ -z ${ver2[i]} ]]; then
|
||||
ver2[i]=0
|
||||
fi
|
||||
if ((10#${ver1[i]} > 10#${ver2[i]})); then
|
||||
echo "$1"
|
||||
return 0
|
||||
fi
|
||||
if ((10#${ver1[i]} < 10#${ver2[i]})); then
|
||||
echo "$2"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "$1" # If versions are equal, return either
|
||||
return 0
|
||||
}
|
||||
|
||||
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 "${EXCLUDE_ARGS[@]}"
|
||||
rsync -a pillar $DEFAULT_SALT_DIR/ --delete "${EXCLUDE_ARGS[@]}"
|
||||
rsync -a salt $DEFAULT_SALT_DIR/
|
||||
rsync -a pillar $DEFAULT_SALT_DIR/
|
||||
chown -R socore:socore $DEFAULT_SALT_DIR/
|
||||
chmod 755 $DEFAULT_SALT_DIR/pillar/firewall/addfirewall.sh
|
||||
cd /tmp
|
||||
}
|
||||
|
||||
create_local_directories() {
|
||||
echo "Creating local pillar and salt directories if needed"
|
||||
PILLARSALTDIR=$1
|
||||
local_salt_dir="/opt/so/saltstack/local"
|
||||
for i in "pillar" "salt"; do
|
||||
for d in $(find $PILLARSALTDIR/$i -type d); do
|
||||
suffixdir=${d//$PILLARSALTDIR/}
|
||||
if [ ! -d "$local_salt_dir/$suffixdir" ]; then
|
||||
mkdir -p $local_salt_dir$suffixdir
|
||||
fi
|
||||
done
|
||||
chown -R socore:socore $local_salt_dir/$i
|
||||
done
|
||||
}
|
||||
|
||||
disable_fastestmirror() {
|
||||
sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf
|
||||
}
|
||||
|
||||
download_and_verify() {
|
||||
source_url=$1
|
||||
source_md5_url=$2
|
||||
dest_file=$3
|
||||
md5_file=$4
|
||||
expand_dir=$5
|
||||
|
||||
if [[ -n "$expand_dir" ]]; then
|
||||
mkdir -p "$expand_dir"
|
||||
fi
|
||||
|
||||
if ! verify_md5_checksum "$dest_file" "$md5_file"; then
|
||||
retry 15 10 "curl --fail --retry 5 --retry-delay 15 -L '$source_url' --output '$dest_file'" "" ""
|
||||
retry 15 10 "curl --fail --retry 5 --retry-delay 15 -L '$source_md5_url' --output '$md5_file'" "" ""
|
||||
|
||||
if verify_md5_checksum "$dest_file" "$md5_file"; then
|
||||
echo "Source file and checksum are good."
|
||||
else
|
||||
echo "Unable to download and verify the source file and checksum."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$expand_dir" ]]; then
|
||||
tar -xf "$dest_file" -C "$expand_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
elastic_license() {
|
||||
|
||||
read -r -d '' message <<- EOM
|
||||
\n
|
||||
Elastic Stack binaries and Security Onion components are only available under the Elastic License version 2 (ELv2):
|
||||
https://securityonion.net/license/
|
||||
Starting in Elastic Stack version 7.11, the Elastic Stack binaries are only available under the Elastic License:
|
||||
https://securityonion.net/elastic-license
|
||||
|
||||
Do you agree to the terms of ELv2?
|
||||
Please review the Elastic License:
|
||||
https://www.elastic.co/licensing/elastic-license
|
||||
|
||||
If so, type AGREE to accept ELv2 and continue. Otherwise, press Enter to exit this program without making any changes.
|
||||
Do you agree to the terms of the Elastic License?
|
||||
|
||||
If so, type AGREE to accept the Elastic License and continue. Otherwise, press Enter to exit this program without making any changes.
|
||||
EOM
|
||||
|
||||
AGREED=$(whiptail --title "$whiptail_title" --inputbox \
|
||||
@@ -318,51 +193,25 @@ fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
get_agent_count() {
|
||||
if [ -f /opt/so/log/agents/agentstatus.log ]; then
|
||||
AGENTCOUNT=$(cat /opt/so/log/agents/agentstatus.log | grep -wF active | awk '{print $2}' | sed 's/,//')
|
||||
[[ -z "$AGENTCOUNT" ]] && AGENTCOUNT="0"
|
||||
else
|
||||
AGENTCOUNT=0
|
||||
fi
|
||||
}
|
||||
|
||||
get_elastic_agent_vars() {
|
||||
local path="${1:-/opt/so/saltstack/default}"
|
||||
local defaultsfile="${path}/salt/elasticsearch/defaults.yaml"
|
||||
|
||||
if [ -f "$defaultsfile" ]; then
|
||||
ELASTIC_AGENT_TARBALL_VERSION=$(egrep " +version: " $defaultsfile | awk -F: '{print $2}' | tr -d '[:space:]')
|
||||
ELASTIC_AGENT_URL="https://repo.securityonion.net/file/so-repo/prod/2.4/elasticagent/elastic-agent_SO-$ELASTIC_AGENT_TARBALL_VERSION.tar.gz"
|
||||
ELASTIC_AGENT_MD5_URL="https://repo.securityonion.net/file/so-repo/prod/2.4/elasticagent/elastic-agent_SO-$ELASTIC_AGENT_TARBALL_VERSION.md5"
|
||||
ELASTIC_AGENT_FILE="/nsm/elastic-fleet/artifacts/elastic-agent_SO-$ELASTIC_AGENT_TARBALL_VERSION.tar.gz"
|
||||
ELASTIC_AGENT_MD5="/nsm/elastic-fleet/artifacts/elastic-agent_SO-$ELASTIC_AGENT_TARBALL_VERSION.md5"
|
||||
ELASTIC_AGENT_EXPANSION_DIR=/nsm/elastic-fleet/artifacts/beats/elastic-agent
|
||||
else
|
||||
fail "Could not find salt/elasticsearch/defaults.yaml"
|
||||
fi
|
||||
}
|
||||
|
||||
get_random_value() {
|
||||
length=${1:-20}
|
||||
head -c 5000 /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $length | head -n 1
|
||||
}
|
||||
|
||||
gpg_rpm_import() {
|
||||
if [[ $is_oracle ]]; then
|
||||
if [[ "$OS" == "centos" ]]; then
|
||||
if [[ "$WHATWOULDYOUSAYYAHDOHERE" == "setup" ]]; then
|
||||
local RPMKEYSLOC="../salt/repo/client/files/$OS/keys"
|
||||
local RPMKEYSLOC="../salt/repo/client/files/centos/keys"
|
||||
else
|
||||
local RPMKEYSLOC="$UPDATE_DIR/salt/repo/client/files/$OS/keys"
|
||||
local RPMKEYSLOC="$UPDATE_DIR/salt/repo/client/files/centos/keys"
|
||||
fi
|
||||
RPMKEYS=('RPM-GPG-KEY-oracle' 'RPM-GPG-KEY-EPEL-9' 'SALT-PROJECT-GPG-PUBKEY-2023.pub' 'docker.pub' 'securityonion.pub')
|
||||
|
||||
RPMKEYS=('RPM-GPG-KEY-EPEL-7' 'GPG-KEY-WAZUH' 'docker.pub' 'SALTSTACK-GPG-KEY.pub' 'securityonion.pub')
|
||||
|
||||
for RPMKEY in "${RPMKEYS[@]}"; do
|
||||
rpm --import $RPMKEYSLOC/$RPMKEY
|
||||
echo "Imported $RPMKEY"
|
||||
done
|
||||
elif [[ $is_rpm ]]; then
|
||||
echo "Importing the security onion GPG key"
|
||||
rpm --import ../salt/repo/client/files/oracle/keys/securityonion.pub
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -375,15 +224,12 @@ init_monitor() {
|
||||
|
||||
if [[ $MONITORNIC == "bond0" ]]; then
|
||||
BIFACES=$(lookup_bond_interfaces)
|
||||
for i in rx tx sg tso ufo gso gro lro rx-vlan-offload tx-vlan-offload generic-receive-offload generic-segmentation-offload tcp-segmentation-offload; do
|
||||
ethtool -K "$MONITORNIC" "$i" off;
|
||||
done
|
||||
else
|
||||
BIFACES=$MONITORNIC
|
||||
fi
|
||||
|
||||
for DEVICE_IFACE in $BIFACES; do
|
||||
for i in rx tx sg tso ufo gso gro lro rx-vlan-offload tx-vlan-offload generic-receive-offload generic-segmentation-offload tcp-segmentation-offload; do
|
||||
for i in rx tx sg tso ufo gso gro lro; do
|
||||
ethtool -K "$DEVICE_IFACE" "$i" off;
|
||||
done
|
||||
ip link set dev "$DEVICE_IFACE" arp off multicast off allmulticast off promisc on
|
||||
@@ -391,17 +237,31 @@ init_monitor() {
|
||||
}
|
||||
|
||||
is_manager_node() {
|
||||
grep "role: so-" /etc/salt/grains | grep -E "manager|eval|managersearch|standalone|import" &> /dev/null
|
||||
# Check to see if this is a manager node
|
||||
role=$(lookup_role)
|
||||
is_single_node_grid && return 0
|
||||
[ $role == 'manager' ] && return 0
|
||||
[ $role == 'managersearch' ] && return 0
|
||||
[ $role == 'helix' ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
is_sensor_node() {
|
||||
# Check to see if this is a sensor (forward) node
|
||||
role=$(lookup_role)
|
||||
is_single_node_grid && return 0
|
||||
grep "role: so-" /etc/salt/grains | grep -E "sensor|heavynode" &> /dev/null
|
||||
[ $role == 'sensor' ] && return 0
|
||||
[ $role == 'heavynode' ] && return 0
|
||||
[ $role == 'helix' ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
is_single_node_grid() {
|
||||
grep "role: so-" /etc/salt/grains | grep -E "eval|standalone|import" &> /dev/null
|
||||
role=$(lookup_role)
|
||||
[ $role == 'eval' ] && return 0
|
||||
[ $role == 'standalone' ] && return 0
|
||||
[ $role == 'import' ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
lookup_bond_interfaces() {
|
||||
@@ -429,7 +289,7 @@ lookup_salt_value() {
|
||||
local=""
|
||||
fi
|
||||
|
||||
salt-call -lerror --no-color ${kind}.get ${group}${key} --out=${output} ${local}
|
||||
salt-call --no-color ${kind}.get ${group}${key} --out=${output} ${local}
|
||||
}
|
||||
|
||||
lookup_pillar() {
|
||||
@@ -451,25 +311,8 @@ lookup_grain() {
|
||||
|
||||
lookup_role() {
|
||||
id=$(lookup_grain id)
|
||||
echo "${id##*_}"
|
||||
}
|
||||
|
||||
is_feature_enabled() {
|
||||
feature=$1
|
||||
enabled=$(lookup_salt_value features)
|
||||
for cur in $enabled; do
|
||||
if [[ "$feature" == "$cur" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
read_feat() {
|
||||
if [ -f /opt/so/log/sostatus/lks_enabled ]; then
|
||||
lic_id=$(cat /opt/so/saltstack/local/pillar/soc/license.sls | grep license_id: | awk '{print $2}')
|
||||
echo "$lic_id/$(cat /opt/so/log/sostatus/lks_enabled)/$(cat /opt/so/log/sostatus/fps_enabled)"
|
||||
fi
|
||||
pieces=($(echo $id | tr '_' ' '))
|
||||
echo ${pieces[1]}
|
||||
}
|
||||
|
||||
require_manager() {
|
||||
@@ -503,10 +346,6 @@ retry() {
|
||||
echo "<Start of output>"
|
||||
echo "$output"
|
||||
echo "<End of output>"
|
||||
if [[ $exitcode -eq 0 ]]; then
|
||||
echo "Forcing exit code to 1"
|
||||
exitcode=1
|
||||
fi
|
||||
fi
|
||||
elif [ -n "$failedOutput" ]; then
|
||||
if [[ "$output" =~ "$failedOutput" ]]; then
|
||||
@@ -515,7 +354,7 @@ retry() {
|
||||
echo "$output"
|
||||
echo "<End of output>"
|
||||
if [[ $exitcode -eq 0 ]]; then
|
||||
echo "Forcing exit code to 1"
|
||||
echo "The exitcode was 0, but we are setting to 1 since we found $failedOutput in the output."
|
||||
exitcode=1
|
||||
fi
|
||||
else
|
||||
@@ -553,82 +392,19 @@ run_check_net_err() {
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_salt_minion() {
|
||||
local minion="$1"
|
||||
local timeout="${2:-5}"
|
||||
local logfile="${3:-'/dev/stdout'}"
|
||||
retry 60 5 "journalctl -u salt-minion.service | grep 'Minion is ready to receive requests'" >> "$logfile" 2>&1 || fail
|
||||
local attempt=0
|
||||
# each attempts would take about 15 seconds
|
||||
local maxAttempts=20
|
||||
until check_salt_minion_status "$minion" "$timeout" "$logfile"; do
|
||||
attempt=$((attempt+1))
|
||||
if [[ $attempt -eq $maxAttempts ]]; then
|
||||
return 1
|
||||
set_cron_service_name() {
|
||||
if [[ "$OS" == "centos" ]]; then
|
||||
cron_service_name="crond"
|
||||
else
|
||||
cron_service_name="cron"
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
salt_minion_count() {
|
||||
local MINIONDIR="/opt/so/saltstack/local/pillar/minions"
|
||||
MINIONCOUNT=$(ls -la $MINIONDIR/*.sls | grep -v adv_ | wc -l)
|
||||
|
||||
}
|
||||
|
||||
set_os() {
|
||||
if [ -f /etc/redhat-release ]; then
|
||||
if grep -q "Rocky Linux release 9" /etc/redhat-release; then
|
||||
OS=rocky
|
||||
OSVER=9
|
||||
is_rocky=true
|
||||
is_rpm=true
|
||||
elif grep -q "CentOS Stream release 9" /etc/redhat-release; then
|
||||
OS=centos
|
||||
OSVER=9
|
||||
is_centos=true
|
||||
is_rpm=true
|
||||
elif grep -q "AlmaLinux release 9" /etc/redhat-release; then
|
||||
OS=alma
|
||||
OSVER=9
|
||||
is_alma=true
|
||||
is_rpm=true
|
||||
elif grep -q "Red Hat Enterprise Linux release 9" /etc/redhat-release; then
|
||||
if [ -f /etc/oracle-release ]; then
|
||||
OS=oracle
|
||||
OSVER=9
|
||||
is_oracle=true
|
||||
is_rpm=true
|
||||
else
|
||||
OS=rhel
|
||||
OSVER=9
|
||||
is_rhel=true
|
||||
is_rpm=true
|
||||
fi
|
||||
fi
|
||||
cron_service_name="crond"
|
||||
elif [ -f /etc/os-release ]; then
|
||||
if grep -q "UBUNTU_CODENAME=focal" /etc/os-release; then
|
||||
OSVER=focal
|
||||
UBVER=20.04
|
||||
OS=ubuntu
|
||||
is_ubuntu=true
|
||||
is_deb=true
|
||||
elif grep -q "UBUNTU_CODENAME=jammy" /etc/os-release; then
|
||||
OSVER=jammy
|
||||
UBVER=22.04
|
||||
OS=ubuntu
|
||||
is_ubuntu=true
|
||||
is_deb=true
|
||||
elif grep -q "VERSION_CODENAME=bookworm" /etc/os-release; then
|
||||
OSVER=bookworm
|
||||
DEBVER=12
|
||||
is_debian=true
|
||||
OS=debian
|
||||
is_deb=true
|
||||
fi
|
||||
cron_service_name="cron"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -637,7 +413,7 @@ set_minionid() {
|
||||
}
|
||||
|
||||
set_palette() {
|
||||
if [[ $is_deb ]]; then
|
||||
if [ "$OS" == ubuntu ]; then
|
||||
update-alternatives --set newt-palette /etc/newt/palette.original
|
||||
fi
|
||||
}
|
||||
@@ -661,19 +437,6 @@ set_version() {
|
||||
fi
|
||||
}
|
||||
|
||||
status () {
|
||||
printf "\n=========================================================================\n$(date) | $1\n=========================================================================\n"
|
||||
}
|
||||
|
||||
sync_options() {
|
||||
set_version
|
||||
set_os
|
||||
salt_minion_count
|
||||
get_agent_count
|
||||
|
||||
echo "$VERSION/$OS/$(uname -r)/$MINIONCOUNT:$AGENTCOUNT/$(read_feat)"
|
||||
}
|
||||
|
||||
systemctl_func() {
|
||||
local action=$1
|
||||
local echo_action=$1
|
||||
@@ -697,13 +460,6 @@ has_uppercase() {
|
||||
|| return 1
|
||||
}
|
||||
|
||||
update_elastic_agent() {
|
||||
local path="${1:-/opt/so/saltstack/default}"
|
||||
get_elastic_agent_vars "$path"
|
||||
echo "Checking if Elastic Agent update is necessary..."
|
||||
download_and_verify "$ELASTIC_AGENT_URL" "$ELASTIC_AGENT_MD5_URL" "$ELASTIC_AGENT_FILE" "$ELASTIC_AGENT_MD5" "$ELASTIC_AGENT_EXPANSION_DIR"
|
||||
}
|
||||
|
||||
valid_cidr() {
|
||||
# Verify there is a backslash in the string
|
||||
echo "$1" | grep -qP "^[^/]+/[^/]+$" || return 1
|
||||
@@ -762,18 +518,6 @@ valid_hostname() {
|
||||
[[ $hostname =~ ^[a-zA-Z0-9\-]+$ ]] && [[ $hostname != 'localhost' ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
verify_ip4() {
|
||||
local ip=$1
|
||||
# Is this an IP or CIDR?
|
||||
if grep -qP "^[^/]+/[^/]+$" <<< $ip; then
|
||||
# Looks like a CIDR
|
||||
valid_ip4_cidr_mask "$ip"
|
||||
else
|
||||
# We know this is not a CIDR - Is it an IP?
|
||||
valid_ip4 "$ip"
|
||||
fi
|
||||
}
|
||||
|
||||
valid_ip4() {
|
||||
local ip=$1
|
||||
|
||||
@@ -857,23 +601,6 @@ valid_username() {
|
||||
echo "$user" | grep -qP '^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$' && return 0 || return 1
|
||||
}
|
||||
|
||||
verify_md5_checksum() {
|
||||
data_file=$1
|
||||
md5_file=${2:-${data_file}.md5}
|
||||
|
||||
if [[ ! -f "$dest_file" || ! -f "$md5_file" ]]; then
|
||||
return 2
|
||||
fi
|
||||
|
||||
SOURCEHASH=$(md5sum "$data_file" | awk '{ print $1 }')
|
||||
HASH=$(cat "$md5_file")
|
||||
|
||||
if [[ "$HASH" == "$SOURCEHASH" ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_web_response() {
|
||||
url=$1
|
||||
expected=$2
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/usr/bin/env 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.
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
import json
|
||||
|
||||
sys.path.append('/opt/saltstack/salt/lib/python3.10/site-packages/')
|
||||
import salt.config
|
||||
import salt.loader
|
||||
|
||||
__opts__ = salt.config.minion_config('/etc/salt/minion')
|
||||
__grains__ = salt.loader.grains(__opts__)
|
||||
|
||||
def check_needs_restarted():
|
||||
osfam = __grains__['os_family']
|
||||
val = '0'
|
||||
outfile = "/opt/so/log/sostatus/needs_restarted"
|
||||
|
||||
if osfam == 'Debian':
|
||||
if os.path.exists('/var/run/reboot-required'):
|
||||
val = '1'
|
||||
elif osfam == 'RedHat':
|
||||
cmd = 'needs-restarting -r > /dev/null 2>&1'
|
||||
try:
|
||||
needs_restarting = subprocess.check_call(cmd, shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
val = '1'
|
||||
else:
|
||||
fail("Unsupported OS")
|
||||
|
||||
with open(outfile, 'w') as f:
|
||||
f.write(val)
|
||||
|
||||
def check_for_fps():
|
||||
feat = 'fps'
|
||||
feat_full = feat.replace('ps', 'ips')
|
||||
fps = 0
|
||||
try:
|
||||
result = subprocess.run([feat_full + '-mode-setup', '--is-enabled'], stdout=subprocess.PIPE)
|
||||
if result.returncode == 0:
|
||||
fps = 1
|
||||
except:
|
||||
fn = '/proc/sys/crypto/' + feat_full + '_enabled'
|
||||
try:
|
||||
with open(fn, 'r') as f:
|
||||
contents = f.read()
|
||||
if '1' in contents:
|
||||
fps = 1
|
||||
except:
|
||||
# Unknown, so assume 0
|
||||
fps = 0
|
||||
|
||||
with open('/opt/so/log/sostatus/fps_enabled', 'w') as f:
|
||||
f.write(str(fps))
|
||||
|
||||
def check_for_lks():
|
||||
feat = 'Lks'
|
||||
feat_full = feat.replace('ks', 'uks')
|
||||
lks = 0
|
||||
result = subprocess.run(['lsblk', '-p', '-J'], check=True, stdout=subprocess.PIPE)
|
||||
data = json.loads(result.stdout)
|
||||
for device in data['blockdevices']:
|
||||
if 'children' in device:
|
||||
for gc in device['children']:
|
||||
if 'children' in gc:
|
||||
try:
|
||||
arg = 'is' + feat_full
|
||||
result = subprocess.run(['cryptsetup', arg, gc['name']], stdout=subprocess.PIPE)
|
||||
if result.returncode == 0:
|
||||
lks = 1
|
||||
except FileNotFoundError:
|
||||
for ggc in gc['children']:
|
||||
if 'crypt' in ggc['type']:
|
||||
lks = 1
|
||||
if lks:
|
||||
break
|
||||
with open('/opt/so/log/sostatus/lks_enabled', 'w') as f:
|
||||
f.write(str(lks))
|
||||
|
||||
def fail(msg):
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
proc = subprocess.run(['id', '-u'], stdout=subprocess.PIPE, encoding="utf-8")
|
||||
if proc.stdout.strip() != "0":
|
||||
fail("This program must be run as root")
|
||||
# Ensure that umask is 0022 so that files created by this script have rw-r-r permissions
|
||||
org_umask = os.umask(0o022)
|
||||
check_needs_restarted()
|
||||
check_for_fps()
|
||||
check_for_lks()
|
||||
# Restore umask to whatever value was set before this script was run. SXIG sets to 0077 rw---
|
||||
os.umask(org_umask)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
48
salt/common/tools/sbin/so-config-backup
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.. /usr/sbin/so-common
|
||||
{% set BACKUPLOCATIONS = salt['pillar.get']('backup:locations', {}) %}
|
||||
|
||||
TODAY=$(date '+%Y_%m_%d')
|
||||
BACKUPFILE="/nsm/backup/so-config-backup-$TODAY.tar"
|
||||
MAXBACKUPS=7
|
||||
|
||||
# Create backup dir if it does not exist
|
||||
mkdir -p /nsm/backup
|
||||
|
||||
# If we haven't already written a backup file for today, let's do so
|
||||
if [ ! -f $BACKUPFILE ]; then
|
||||
|
||||
# Create empty backup file
|
||||
tar -cf $BACKUPFILE -T /dev/null
|
||||
|
||||
# Loop through all paths defined in global.sls, and append them to backup file
|
||||
{%- for LOCATION in BACKUPLOCATIONS %}
|
||||
tar -rf $BACKUPFILE {{ LOCATION }}
|
||||
{%- endfor %}
|
||||
tar -rf $BACKUPFILE /etc/pki
|
||||
tar -rf $BACKUPFILE /etc/salt
|
||||
tar -rf $BACKUPFILE /opt/so/conf/kratos
|
||||
|
||||
fi
|
||||
|
||||
# Find oldest backup files and remove them
|
||||
NUMBACKUPS=$(find /nsm/backup/ -type f -name "so-config-backup*" | wc -l)
|
||||
while [ "$NUMBACKUPS" -gt "$MAXBACKUPS" ]; do
|
||||
OLDESTBACKUP=$(find /nsm/backup/ -type f -name "so-config-backup*" -type f -printf '%T+ %p\n' | sort | head -n 1 | awk -F" " '{print $2}')
|
||||
rm -f $OLDESTBACKUP
|
||||
NUMBACKUPS=$(find /nsm/backup/ -type f -name "so-config-backup*" | wc -l)
|
||||
done
|
||||
20
salt/common/tools/sbin/so-cortex-restart
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
echo "TheHive and its components are no longer part of Security Onion"
|
||||
20
salt/common/tools/sbin/so-cortex-start
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
echo "TheHive and its components are no longer part of Security Onion"
|
||||
20
salt/common/tools/sbin/so-cortex-stop
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
echo "TheHive and its components are no longer part of Security Onion"
|
||||
20
salt/common/tools/sbin/so-cortex-user-add
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
echo "TheHive and its components are no longer part of Security Onion"
|
||||
20
salt/common/tools/sbin/so-cortex-user-enable
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
echo "TheHive and its components are no longer part of Security Onion"
|
||||
20
salt/common/tools/sbin/so-curator-restart
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
/usr/sbin/so-restart curator $1
|
||||
20
salt/common/tools/sbin/so-curator-start
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
/usr/sbin/so-start curator $1
|
||||
20
salt/common/tools/sbin/so-curator-stop
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
/usr/sbin/so-stop curator $1
|
||||
213
salt/common/tools/sbin/so-deny
Executable file
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ipaddress
|
||||
import textwrap
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import argparse
|
||||
import re
|
||||
from lxml import etree as ET
|
||||
from xml.dom import minidom
|
||||
|
||||
|
||||
LOCAL_SALT_DIR='/opt/so/saltstack/local'
|
||||
WAZUH_CONF='/nsm/wazuh/etc/ossec.conf'
|
||||
VALID_ROLES = {
|
||||
'a': { 'role': 'analyst','desc': 'Analyst - 80/tcp, 443/tcp' },
|
||||
'b': { 'role': 'beats_endpoint', 'desc': 'Logstash Beat - 5044/tcp' },
|
||||
'e': { 'role': 'elasticsearch_rest', 'desc': 'Elasticsearch REST API - 9200/tcp' },
|
||||
'f': { 'role': 'strelka_frontend', 'desc': 'Strelka frontend - 57314/tcp' },
|
||||
'o': { 'role': 'osquery_endpoint', 'desc': 'Osquery endpoint - 8090/tcp' },
|
||||
's': { 'role': 'syslog', 'desc': 'Syslog device - 514/tcp/udp' },
|
||||
'w': { 'role': 'wazuh_agent', 'desc': 'Wazuh agent - 1514/tcp/udp' },
|
||||
'p': { 'role': 'wazuh_api', 'desc': 'Wazuh API - 55000/tcp' },
|
||||
'r': { 'role': 'wazuh_authd', 'desc': 'Wazuh registration service - 1515/tcp' }
|
||||
}
|
||||
|
||||
|
||||
def validate_ip_cidr(ip_cidr: str) -> bool:
|
||||
try:
|
||||
ipaddress.ip_address(ip_cidr)
|
||||
except ValueError:
|
||||
try:
|
||||
ipaddress.ip_network(ip_cidr)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def role_prompt() -> str:
|
||||
print()
|
||||
print('Choose the role for the IP or Range you would like to deny')
|
||||
print()
|
||||
for role in VALID_ROLES:
|
||||
print(f'[{role}] - {VALID_ROLES[role]["desc"]}')
|
||||
print()
|
||||
role = input('Please enter your selection: ')
|
||||
if role in VALID_ROLES.keys():
|
||||
return VALID_ROLES[role]['role']
|
||||
else:
|
||||
print(f'Invalid role \'{role}\', please try again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def ip_prompt() -> str:
|
||||
ip = input('Enter a single ip address or range to deny (ex: 10.10.10.10 or 10.10.0.0/16): ')
|
||||
if validate_ip_cidr(ip):
|
||||
return ip
|
||||
else:
|
||||
print(f'Invalid IP address or CIDR block \'{ip}\', please try again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def wazuh_enabled() -> bool:
|
||||
for file in os.listdir(f'{LOCAL_SALT_DIR}/pillar'):
|
||||
with open(file, 'r') as pillar:
|
||||
if 'wazuh: 1' in pillar.read():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def root_to_str(root: ET.ElementTree) -> str:
|
||||
xml_str = ET.tostring(root, encoding='unicode', method='xml').replace('\n', '')
|
||||
xml_str = re.sub(r'(?:(?<=>) *)', '', xml_str)
|
||||
|
||||
# Remove specific substrings to better format comments on intial parse/write
|
||||
xml_str = re.sub(r' -', '', xml_str)
|
||||
xml_str = re.sub(r' -->', ' -->', xml_str)
|
||||
|
||||
dom = minidom.parseString(xml_str)
|
||||
return dom.toprettyxml(indent=" ")
|
||||
|
||||
|
||||
def rem_wl(ip):
|
||||
parser = ET.XMLParser(remove_blank_text=True)
|
||||
with open(WAZUH_CONF, 'rb') as wazuh_conf:
|
||||
tree = ET.parse(wazuh_conf, parser)
|
||||
root = tree.getroot()
|
||||
|
||||
global_elems = root.findall(f"global/white_list[. = '{ip}']/..")
|
||||
if len(global_elems) > 0:
|
||||
for g_elem in global_elems:
|
||||
ge_index = list(root).index(g_elem)
|
||||
if ge_index > 0 and root[list(root).index(g_elem) - 1].tag == ET.Comment:
|
||||
root.remove(root[ge_index - 1])
|
||||
root.remove(g_elem)
|
||||
|
||||
with open(WAZUH_CONF, 'w') as out:
|
||||
out.write(root_to_str(root))
|
||||
|
||||
|
||||
def apply(role: str, ip: str) -> int:
|
||||
firewall_cmd = ['so-firewall', 'excludehost', role, ip]
|
||||
salt_cmd = ['salt-call', 'state.apply', '-l', 'quiet', 'firewall', 'queue=True']
|
||||
restart_wazuh_cmd = ['so-wazuh-restart']
|
||||
print(f'Removing {ip} from the {role} role. This can take a few seconds...')
|
||||
cmd = subprocess.run(firewall_cmd)
|
||||
if cmd.returncode == 0:
|
||||
cmd = subprocess.run(salt_cmd, stdout=subprocess.DEVNULL)
|
||||
else:
|
||||
return cmd.returncode
|
||||
if cmd.returncode == 0:
|
||||
if wazuh_enabled and role=='analyst':
|
||||
try:
|
||||
rem_wl(ip)
|
||||
print(f'Removed whitelist entry for {ip} from {WAZUH_CONF}', file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f'Failed to remove whitelist entry for {ip} from {WAZUH_CONF}', file=sys.stderr)
|
||||
print(e)
|
||||
return 1
|
||||
print('Restarting OSSEC Server...')
|
||||
cmd = subprocess.run(restart_wazuh_cmd)
|
||||
else:
|
||||
return cmd.returncode
|
||||
else:
|
||||
print(f'Commmand \'{" ".join(salt_cmd)}\' failed.', file=sys.stderr)
|
||||
return cmd.returncode
|
||||
if cmd.returncode != 0:
|
||||
print('Failed to restart OSSEC server.')
|
||||
return cmd.returncode
|
||||
|
||||
|
||||
def main():
|
||||
if os.geteuid() != 0:
|
||||
print('You must run this script as root', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
main_parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=textwrap.dedent(f'''\
|
||||
additional information:
|
||||
To use this script in interactive mode call it with no arguments
|
||||
'''
|
||||
))
|
||||
|
||||
group = main_parser.add_argument_group(title='roles')
|
||||
group.add_argument('-a', dest='roles', action='append_const', const=VALID_ROLES['a']['role'], help="Analyst - 80/tcp, 443/tcp")
|
||||
group.add_argument('-b', dest='roles', action='append_const', const=VALID_ROLES['b']['role'], help="Logstash Beat - 5044/tcp")
|
||||
group.add_argument('-e', dest='roles', action='append_const', const=VALID_ROLES['e']['role'], help="Elasticsearch REST API - 9200/tcp")
|
||||
group.add_argument('-f', dest='roles', action='append_const', const=VALID_ROLES['f']['role'], help="Strelka frontend - 57314/tcp")
|
||||
group.add_argument('-o', dest='roles', action='append_const', const=VALID_ROLES['o']['role'], help="Osquery endpoint - 8090/tcp")
|
||||
group.add_argument('-s', dest='roles', action='append_const', const=VALID_ROLES['s']['role'], help="Syslog device - 514/tcp/udp")
|
||||
group.add_argument('-w', dest='roles', action='append_const', const=VALID_ROLES['w']['role'], help="Wazuh agent - 1514/tcp/udp")
|
||||
group.add_argument('-p', dest='roles', action='append_const', const=VALID_ROLES['p']['role'], help="Wazuh API - 55000/tcp")
|
||||
group.add_argument('-r', dest='roles', action='append_const', const=VALID_ROLES['r']['role'], help="Wazuh registration service - 1515/tcp")
|
||||
|
||||
ip_g = main_parser.add_argument_group(title='allow')
|
||||
ip_g.add_argument('-i', help="IP or CIDR block to disallow connections from, requires at least one role argument", metavar='', dest='ip')
|
||||
|
||||
args = main_parser.parse_args(sys.argv[1:])
|
||||
|
||||
if args.roles is None:
|
||||
role = role_prompt()
|
||||
ip = ip_prompt()
|
||||
try:
|
||||
return_code = apply(role, ip)
|
||||
except Exception as e:
|
||||
print(f'Unexpected exception occurred: {e}', file=sys.stderr)
|
||||
return_code = e.errno
|
||||
sys.exit(return_code)
|
||||
elif args.roles is not None and args.ip is None:
|
||||
if os.environ.get('IP') is None:
|
||||
main_parser.print_help()
|
||||
sys.exit(1)
|
||||
else:
|
||||
args.ip = os.environ['IP']
|
||||
|
||||
if validate_ip_cidr(args.ip):
|
||||
try:
|
||||
for role in args.roles:
|
||||
return_code = apply(role, args.ip)
|
||||
if return_code > 0:
|
||||
break
|
||||
except Exception as e:
|
||||
print(f'Unexpected exception occurred: {e}', file=sys.stderr)
|
||||
return_code = e.errno
|
||||
else:
|
||||
print(f'Invalid IP address or CIDR block \'{args.ip}\', please try again.', file=sys.stderr)
|
||||
return_code = 1
|
||||
|
||||
sys.exit(return_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
@@ -1,19 +1,33 @@
|
||||
#!/usr/bin/env 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.
|
||||
import sys, argparse, re, subprocess, json
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys, argparse, re, docker
|
||||
from packaging.version import Version, InvalidVersion
|
||||
from itertools import groupby, chain
|
||||
|
||||
|
||||
def get_image_name(string) -> str:
|
||||
return ':'.join(string.split(':')[:-1])
|
||||
|
||||
|
||||
def get_so_image_basename(string) -> str:
|
||||
return get_image_name(string).split('/so-')[-1]
|
||||
|
||||
|
||||
def get_image_version(string) -> str:
|
||||
ver = string.split(':')[-1]
|
||||
if ver == 'latest':
|
||||
@@ -29,49 +43,33 @@ def get_image_version(string) -> str:
|
||||
return '999999.9.9'
|
||||
return ver
|
||||
|
||||
def run_command(command):
|
||||
process = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
if process.returncode != 0:
|
||||
print(f"Error executing command: {command}", file=sys.stderr)
|
||||
print(f"Error message: {process.stderr}", file=sys.stderr)
|
||||
exit(1)
|
||||
return process.stdout
|
||||
|
||||
def main(quiet):
|
||||
try:
|
||||
# Prune old/stopped containers using docker CLI
|
||||
client = docker.from_env()
|
||||
|
||||
# Prune old/stopped containers
|
||||
if not quiet: print('Pruning old containers')
|
||||
run_command('docker container prune -f')
|
||||
client.containers.prune()
|
||||
|
||||
# Get list of images using docker CLI
|
||||
images_json = run_command('docker images --format "{{json .}}"')
|
||||
image_list = client.images.list(filters={ 'dangling': False })
|
||||
|
||||
# Parse the JSON output
|
||||
image_list = []
|
||||
for line in images_json.strip().split('\n'):
|
||||
if line: # Skip empty lines
|
||||
image_list.append(json.loads(line))
|
||||
# Map list of image objects to flattened list of tags (format: "name:version")
|
||||
tag_list = list(chain.from_iterable(list(map(lambda x: x.attrs.get('RepoTags'), image_list))))
|
||||
|
||||
# Extract tags in the format "name:version"
|
||||
tag_list = []
|
||||
for img in image_list:
|
||||
# Skip dangling images
|
||||
if img.get('Repository') != "<none>" and img.get('Tag') != "<none>":
|
||||
tag = f"{img.get('Repository')}:{img.get('Tag')}"
|
||||
# Filter to only SO images (base name begins with "so-")
|
||||
if re.match(r'^.*\/so-[^\/]*$', get_image_name(tag)):
|
||||
tag_list.append(tag)
|
||||
tag_list = list(filter(lambda x: re.match(r'^.*\/so-[^\/]*$', get_image_name(x)), tag_list))
|
||||
|
||||
# Group tags into lists by base name (sort by same projection first)
|
||||
tag_list.sort(key=lambda x: get_so_image_basename(x))
|
||||
grouped_tag_lists = [list(it) for k, it in groupby(tag_list, lambda x: get_so_image_basename(x))]
|
||||
grouped_tag_lists = [ list(it) for _, it in groupby(tag_list, lambda x: get_so_image_basename(x)) ]
|
||||
|
||||
no_prunable = True
|
||||
for t_list in grouped_tag_lists:
|
||||
try:
|
||||
# Group tags by version, in case multiple images exist with the same version string
|
||||
t_list.sort(key=lambda x: Version(get_image_version(x)), reverse=True)
|
||||
grouped_t_list = [list(it) for k, it in groupby(t_list, lambda x: get_image_version(x))]
|
||||
grouped_t_list = [ list(it) for _,it in groupby(t_list, lambda x: get_image_version(x)) ]
|
||||
|
||||
# Keep the 2 most current version groups
|
||||
if len(grouped_t_list) <= 2:
|
||||
continue
|
||||
@@ -81,10 +79,10 @@ def main(quiet):
|
||||
for tag in group:
|
||||
if not quiet: print(f'Removing image {tag}')
|
||||
try:
|
||||
run_command(f'docker rmi -f {tag}')
|
||||
except Exception as e:
|
||||
client.images.remove(tag, force=True)
|
||||
except docker.errors.ClientError as e:
|
||||
print(f'Could not remove image {tag}, continuing...')
|
||||
except (InvalidVersion) as e:
|
||||
except (docker.errors.APIError, InvalidVersion) as e:
|
||||
print(f'so-{get_so_image_basename(t_list[0])}: {e}', file=sys.stderr)
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
@@ -95,9 +93,6 @@ def main(quiet):
|
||||
if no_prunable and not quiet:
|
||||
print('No Security Onion images to prune')
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main_parser = argparse.ArgumentParser(add_help=False)
|
||||
|
||||
22
salt/common/tools/sbin/so-docker-refresh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
. /usr/sbin/so-image-common
|
||||
|
||||
require_manager
|
||||
update_docker_containers "refresh"
|
||||
20
salt/common/tools/sbin/so-elastalert-restart
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
/usr/sbin/so-restart elastalert $1
|
||||
20
salt/common/tools/sbin/so-elastalert-start
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
/usr/sbin/so-start elastalert $1
|
||||
20
salt/common/tools/sbin/so-elastalert-stop
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2014-2022 Security Onion Solutions, LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
/usr/sbin/so-stop elastalert $1
|
||||