Merge branch '2.4/dev' into funstuff

This commit is contained in:
Mike Reeves
2022-09-19 09:40:27 -04:00
7 changed files with 181 additions and 335 deletions

View File

@@ -123,6 +123,13 @@ utilsyncscripts:
- so-firewall
- so-image-common
- soup
- so-status
so-status_script:
file.managed:
- name: /usr/sbin/so-status
- source: salt://common/tools/sbin/so-status
- mode: 755
{% if role in ['eval', 'standalone', 'sensor', 'heavynode'] %}
# Add sensor cleanup
@@ -192,9 +199,16 @@ sostatus_log:
file.managed:
- name: /opt/so/log/sostatus/status.log
- mode: 644
common_pip_dependencies:
pip.installed:
- user: root
- pkgs:
- rich
- target: /usr/lib64/python3.6/site-packages
# Install sostatus check cron
'/usr/sbin/so-status -q; echo $? > /opt/so/log/sostatus/status.log 2>&1':
'/usr/sbin/so-status -j > /opt/so/log/sostatus/status.log 2>&1':
cron.present:
- user: root
- minute: '*/1'

View File

@@ -1,301 +1,165 @@
#!/bin/bash
#!/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.
if ! [ "$(id -u)" = 0 ]; then
echo "This command must be run as root"
exit 1
fi
import json
import os
from rich import box
from rich.console import Console
from rich.table import Table
import subprocess
import sys
import time
display_help() {
cat <<HELP_USAGE
EXPECTED_CONTAINERS_FILE = "/opt/so/conf/so-status/so-status.conf"
HIGHSTATE_COMPLETE_FILE = "/opt/so/log/salt/lasthighstate"
UPTIME_FILE = "/proc/uptime"
$0 [-h] [-q|--quiet]
-h Show this message.
-q|--quiet Suppress the output and only return a
single status code for overall status
0:Ok, 1:Error, 2:Starting/Pending, 99:Installing SO
HELP_USAGE
}
# Constants
QUIET=false
EXITCODE=0
SYSTEM_START_TIME=$(date -d "$(</proc/uptime awk '{print $1}') seconds ago" +%s)
# file populated by salt.lasthighstate state at end of successful highstate run
LAST_HIGHSTATE_END=$([ -e "/opt/so/log/salt/lasthighstate" ] && date -r /opt/so/log/salt/lasthighstate +%s || echo 0)
LAST_SOSETUP_LOG=$([ -e "/root/sosetup.log" ] && date -r /root/sosetup.log +%s || echo 0)
HIGHSTATE_RUNNING=$(salt-call --local saltutil.running --out=json | jq -r '.local[].fun' | grep -q 'state.highstate' && echo $?)
ERROR_STRING="ERROR"
SUCCESS_STRING="OK"
PENDING_STRING="PENDING"
MISSING_STRING='MISSING'
DISABLED_STRING='DISABLED'
WAIT_START_STRING='WAIT_START'
STARTING_STRING='STARTING'
CALLER=$(ps -o comm= $PPID)
declare -a BAD_STATUSES=("removing" "paused" "exited" "dead")
declare -a PENDING_STATUSES=("paused" "created" "restarting")
declare -a GOOD_STATUSES=("running")
declare -a DISABLED_CONTAINERS=()
mapfile -t DISABLED_CONTAINERS < <(sort -u /opt/so/conf/so-status/so-status.conf | grep "^\s*#" | tr -d "#")
def showUsage(options, args):
print('Usage: {} [OPTIONS]'.format(sys.argv[0]))
print(' Options:')
print(' -h - Prints this usage information')
print(' -q - Suppress output; useful for automation of exit code value')
print(' -j - Output in JSON format')
print('')
print(' Exit codes:')
print(' 0 - Success, system appears to be running correctly')
print(' 1 - Error, one or more subsystems are not running')
print(' 2 - System is starting')
print(' 99 - Installation in progress')
sys.exit(1)
declare -a temp_container_name_list=()
declare -a temp_container_state_list=()
def fail(msg):
print(msg, file=sys.stderr)
sys.exit(1)
declare -a container_name_list=()
declare -a container_state_list=()
declare -a expected_container_list=()
def check_system_status(options, console):
code = 0
highstate_end_time = 0
try:
highstate_end_time = os.path.getmtime(HIGHSTATE_COMPLETE_FILE)
uptime_file = open(UPTIME_FILE, "r")
uptime_contents = uptime_file.readline()
uptime = uptime_contents.split()
if len(uptime) != 2:
fail("Unable to determine system uptime")
system_start_time = time.time() - float(uptime[0])
if highstate_end_time < system_start_time:
code = 2
except OSError:
code = 99
# {% raw %}
return code
compare_lists() {
local found=0
create_expected_container_list
def output(options, console, code, data):
if "-j" in options:
summary = { "status_code": code, "containers": data }
print(json.dumps(summary))
elif "-q" not in options:
if code == 2:
console.print(" [bright_yellow]:hourglass: [bold white]System appears to be starting. No highstate has completed since the system was restarted.")
elif code == 99:
console.print(" [bright_red]:exclamation: [bold white]Installation does not appear to be complete. A highstate has not fully completed.")
else:
table = Table(title = "Security Onion Status", row_styles = ["dim", ""], show_edge = False, safe_box = True, box = box.MINIMAL)
table.add_column("Container", justify="right", style="white", no_wrap=True)
table.add_column("Status", justify="left", style="green", no_wrap=True)
table.add_column("Details", justify="right", style="cyan", no_wrap=True)
data.sort(key = lambda x: x['Name'])
for container in data:
color = "[green]"
if container['Status'] != "running":
color = "[bright_red]"
table.add_row(container['Name'], color + container['Status'], container['Details'])
console.print()
console.print(table)
console.print()
if code == 0:
console.print(" [green]:heavy_check_mark: [bold white]This onion is ready to make your adversaries cry!")
elif code == 1:
console.print(" [bright_red]:exclamation: [bold white]Check container logs and /opt/so/log for more details.")
console.print()
if [[ ${#expected_container_list[@]} = 0 ]]; then
container_name_list="${temp_container_name_list[*]}"
container_state_list="${temp_container_state_list[*]}"
return 1
fi
def check_container_status(options, console):
code = 0
cli = "docker"
proc = subprocess.run([cli, 'ps', '--format', '{{json .}}'], stdout=subprocess.PIPE, encoding="utf-8")
if proc.returncode != 0:
fail("Container system error; unable to obtain container process statuses")
for intended_item in "${expected_container_list[@]}"; do
found=0
for i in "${!temp_container_name_list[@]}"; do
[[ ${temp_container_name_list[$i]} = "$intended_item" ]] \
&& found=1 \
&& container_name_list+=("${temp_container_name_list[$i]}") \
&& container_state_list+=("${temp_container_state_list[$i]}") \
&& break
done
if [[ $found = 0 ]]; then
container_name_list+=("$intended_item")
container_state_list+=("missing")
fi
done
}
container_list = []
expected_file = open(EXPECTED_CONTAINERS_FILE, "r")
for container in expected_file:
container = container.strip()
exists = False
details = { "Name": container, "Status": "missing", "Details": ""}
# {% endraw %}
try:
# Podman format
containers_data = json.loads(proc.stdout)
for item in containers_data:
if item['Names'][0] == container:
details['Status'] = item['State']
details['Details'] = item['Status']
container_list.append(details)
exists = True
if item['State'] != "running":
code = 1
break
create_expected_container_list() {
except:
# Docker format
for line in proc.stdout.splitlines():
if len(line) > 2:
item = json.loads(line)
if item['Names'] == container:
details['Status'] = item['State']
details['Details'] = item['Status']
container_list.append(details)
exists = True
if item['State'] != "running":
code = 1
break
mapfile -t expected_container_list < <(sort -u /opt/so/conf/so-status/so-status.conf | tr -d "#")
if not exists:
container_list.append(details)
code = 1
return code, container_list
}
populate_container_lists() {
systemctl is-active --quiet docker
def check_status(options, console):
container_list = []
code = check_system_status(options, console)
if code == 0:
code, container_list = check_container_status(options, console)
output(options, console, code, container_list)
return code
if [[ $? = 0 ]]; then
mapfile -t docker_raw_list < <(curl -s --unix-socket /var/run/docker.sock http:/v1.40/containers/json?all=1 \
| jq -c '.[] | { Name: .Names[0], State: .State }' \
| tr -d '/{"}')
else
exit 1
fi
local container_name=""
local container_state=""
def main():
options = []
args = sys.argv[1:]
for option in args:
if option.startswith("-"):
options.append(option)
args.remove(option)
for line in "${docker_raw_list[@]}"; do
container_name="$( echo $line | sed -e 's/Name:\(.*\),State:\(.*\)/\1/' )" # Get value in the first search group (container names)
container_state="$( echo $line | sed -e 's/Name:\(.*\),State:\(.*\)/\2/' )" # Get value in the second search group (container states)
if len(args) != 0 or "-h" in options:
showUsage(options, None)
temp_container_name_list+=( "${container_name}" )
temp_container_state_list+=( "${container_state}" )
done
if os.environ["USER"] != "root":
fail("This program must be run as root")
console = Console()
sys.exit(check_status(options, console))
compare_lists
}
parse_status() {
local service_name=${1}
local container_state=${2}
if __name__ == "__main__":
main()
for state in "${GOOD_STATUSES[@]}"; do
[[ $container_state = "$state" ]] && [[ $QUIET = "false" ]] && printf $SUCCESS_STRING && return 0 || [[ $container_state = "$state" ]] && return 0
done
for state in "${BAD_STATUSES[@]}"; do
[[ " ${DISABLED_CONTAINERS[@]} " =~ " ${service_name} " ]] && [[ $QUIET = "false" ]] && printf $DISABLED_STRING && return 0 || [[ " ${DISABLED_CONTAINERS[@]} " =~ " ${service_name} " ]] && return 0
done
# if a highstate has finished running since the system has started
# then the containers should be running so let's check the status
if [ $LAST_HIGHSTATE_END -ge $SYSTEM_START_TIME ]; then
[[ $container_state = "missing" ]] && [[ $QUIET = "false" ]] && printf $MISSING_STRING && return 1 || [[ $container_state = "missing" ]] && [[ "$EXITCODE" -lt 2 ]] && EXITCODE=1 && return 1
for state in "${PENDING_STATUSES[@]}"; do
[[ $container_state = "$state" ]] && [[ $QUIET = "false" ]] && printf $PENDING_STRING && return 0
done
# This is technically not needed since the default is error state
for state in "${BAD_STATUSES[@]}"; do
[[ $container_state = "$state" ]] && [[ $QUIET = "false" ]] && printf $ERROR_STRING && return 1 || [[ $container_state = "$state" ]] && [[ "$EXITCODE" -lt 2 ]] && EXITCODE=1 && return 1
done
[[ $QUIET = "false" ]] && printf $ERROR_STRING && return 1 || [[ "$EXITCODE" -lt 2 ]] && EXITCODE=1 && return 1
# if a highstate has not run since system start time, but a highstate is currently running
# then show that the containers are STARTING
elif [[ "$HIGHSTATE_RUNNING" == 0 ]]; then
[[ $QUIET = "false" ]] && printf $STARTING_STRING && return 2 || EXITCODE=2 && return 2
# if a highstate has not finished running since system startup and isn't currently running
# then just show that the containers are WAIT_START; waiting to be started
else
[[ $QUIET = "false" ]] && printf $WAIT_START_STRING && return 2 || EXITCODE=2 && return 2
fi
}
# {% raw %}
print_line() {
local service_name=${1}
local service_state="$( parse_status ${1} ${2} )"
local columns=$(tput cols)
local state_color="\e[0m"
local PADDING_CONSTANT=15
if [[ $service_state = "$ERROR_STRING" ]] || [[ $service_state = "$MISSING_STRING" ]]; then
state_color="\e[1;31m"
if [[ "$EXITCODE" -eq 0 ]]; then
EXITCODE=1
fi
elif [[ $service_state = "$SUCCESS_STRING" ]]; then
state_color="\e[1;32m"
elif [[ $service_state = "$PENDING_STRING" ]] || [[ $service_state = "$DISABLED_STRING" ]] || [[ $service_state = "$STARTING_STRING" ]] || [[ $service_state = "$WAIT_START_STRING" ]]; then
state_color="\e[1;33m"
EXITCODE=2
fi
printf " $service_name "
for i in $(seq 0 $(( $columns - $PADDING_CONSTANT - ${#service_name} - ${#service_state} ))); do
printf "${state_color}%b\e[0m" "-"
done
printf " [ "
printf "${state_color}%b\e[0m" "$service_state"
printf "%s \n" " ]"
}
non_term_print_line() {
local service_name=${1}
local service_state="$( parse_status ${1} ${2} )"
if [[ $service_state = "$ERROR_STRING" ]] || [[ $service_state = "$MISSING_STRING" ]]; then
if [[ "$EXITCODE" -eq 0 ]]; then
EXITCODE=1
fi
elif [[ $service_state = "$PENDING_STRING" ]] || [[ $service_state = "$DISABLED_STRING" ]] || [[ $service_state = "$STARTING_STRING" ]] || [[ $service_state = "$WAIT_START_STRING" ]]; then
EXITCODE=2
fi
printf " $service_name "
for i in $(seq 0 $(( 35 - ${#service_name} - ${#service_state} ))); do
printf "-"
done
printf " [ "
printf "$service_state"
printf "%s \n" " ]"
}
main() {
# if running from salt
if [ "$CALLER" == 'salt-call' ] || [ "$CALLER" == 'salt-minion' ]; then
printf "\n"
printf "Checking Docker status\n\n"
systemctl is-active --quiet docker
if [[ $? = 0 ]]; then
non_term_print_line "Docker" "running"
else
non_term_print_line "Docker" "exited"
fi
populate_container_lists
printf "\n"
printf "Checking container statuses\n\n"
local num_containers=${#container_name_list[@]}
for i in $(seq 0 $(($num_containers - 1 ))); do
non_term_print_line ${container_name_list[$i]} ${container_state_list[$i]}
done
printf "\n"
# else if running from a terminal
else
if [ "$QUIET" = true ]; then
if [ $SYSTEM_START_TIME -lt $LAST_SOSETUP_LOG ]; then
exit 99
fi
print_or_parse="parse_status"
else
print_or_parse="print_line"
local focus_color="\e[1;34m"
printf "\n"
printf "${focus_color}%b\e[0m" "Checking Docker status\n\n"
fi
systemctl is-active --quiet docker
if [[ $? = 0 ]]; then
${print_or_parse} "Docker" "running"
else
${print_or_parse} "Docker" "exited"
fi
populate_container_lists
if [ "$QUIET" = false ]; then
printf "\n"
printf "${focus_color}%b\e[0m" "Checking container statuses\n\n"
fi
local num_containers=${#container_name_list[@]}
for i in $(seq 0 $(($num_containers - 1 ))); do
${print_or_parse} ${container_name_list[$i]} ${container_state_list[$i]}
done
if [ "$QUIET" = false ]; then
printf "\n"
fi
fi
}
# {% endraw %}
while getopts ':hq' OPTION; do
case "$OPTION" in
h)
display_help
exit 0
;;
q)
QUIET=true
;;
\?)
display_help
exit 0
;;
esac
done
main
exit $EXITCODE

View File

@@ -75,7 +75,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.apm_server@package"
@@ -86,7 +86,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -109,7 +109,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.auditbeat@package"
@@ -120,7 +120,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -143,7 +143,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.cloudbeat@package"
@@ -154,7 +154,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -177,7 +177,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.endpoint_security@package"
@@ -188,7 +188,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -211,7 +211,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.filebeat@package"
@@ -222,7 +222,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -245,7 +245,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.fleet_server@package"
@@ -256,7 +256,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -279,7 +279,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.heartbeat@package"
@@ -290,7 +290,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -313,7 +313,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent@package"
@@ -324,7 +324,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -347,7 +347,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.metricbeat@package"
@@ -358,7 +358,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -381,7 +381,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.osquerybeat@package"
@@ -392,7 +392,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false
@@ -415,7 +415,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
composed_of:
- "so-logs-elastic_agent.packetbeat@package"
@@ -426,7 +426,7 @@ elasticsearch:
_meta:
package:
name: elastic_agent
managed_by: fleet
managed_by: security_onion
managed: true
data_stream:
hidden: false

View File

@@ -1,37 +0,0 @@
{%- set ES = salt['grains.get']('master') -%}
{%- set ES_USER = salt['pillar.get']('elasticsearch:auth:users:so_elastic_user:user', '') %}
{%- set ES_PASS = salt['pillar.get']('elasticsearch:auth:users:so_elastic_user:pass', '') %}
filter {
if [type] =~ "live_query" {
mutate {
rename => {
"[host][hostname]" => "computer_name"
}
}
prune {
blacklist_names => ["host"]
}
split {
field => "rows"
}
}
}
output {
if [type] =~ "live_query" {
elasticsearch {
pipeline => "osquery.live_query"
hosts => "{{ ES }}"
user => "{{ ES_USER }}"
password => "{{ ES_PASS }}"
index => "so-osquery"
ssl => true
ssl_certificate_verification => false
}
}
}

View File

@@ -55,6 +55,7 @@ http {
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_ecdh_curve secp521r1:secp384r1;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1.2;
}

View File

@@ -6,11 +6,13 @@ soc:
description: Customize the login page with a specific markdown-formatted message.
file: True
global: True
syntax: md
motd__md:
title: Overview Page
description: Customize the overview page with specific markdown-formatted content. Images can be used but must be hosted from another host that is accessible by the users' browser.
file: True
global: True
syntax: md
custom__js:
title: Custom Javascript
description: Customize SOC UI behavior with custom Javascript code. Custom Javascript not provided by Security Onion Solutions is unsupported, and should be removed prior to requesting support and prior to performing upgrades.

View File

@@ -11,10 +11,12 @@
if [[ ! "`pidof -x $(basename $0) -o %PPID`" ]]; then
SOSTATUSLOG=/var/log/sostatus/status.log
SOSTATUSSTATUS=$(cat /var/log/sostatus/status.log)
SOSTATUSCODE=$(jq -r .status_code /var/log/sostatus/status.log)
SOSTATUSJSON=$(cat /var/log/sostatus/status.log)
if [ -f "$SOSTATUSLOG" ]; then
echo "sostatus status=$SOSTATUSSTATUS"
echo "sostatus status=$SOSTATUSCODE"
echo "sostatus json=$SOSTATUSJSON"
else
exit 0
fi