Files
securityonion/setup/so-setup
2025-03-12 21:05:04 -04:00

850 lines
23 KiB
Bash
Executable File

#!/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.
# Make sure you are root before doing anything
uid="$(id -u)"
if [ "$uid" -ne 0 ]; then
echo "This script must be run using sudo!"
fail_setup
fi
# Save the original argument array since we modify it
original_args=("$@")
cd "$(dirname "$0")" || fail_setup
echo "Getting started..."
# Source the generic function libraries that are also used by the product after
# setup. These functions are intended to be reusable outside of the setup process.
source ../salt/common/tools/sbin/so-common
source ../salt/common/tools/sbin/so-image-common
# Setup bash functionality is divided into functions and user-facing prompts.
# Do not attempt to re-use any of this functionality outside of setup. Instead,
# if needed, migrated generic functions into so-common.
source ./so-functions
source ./so-whiptail
# Finally, source the default variable definitions, which require availability of
# functions sourced above.
source ./so-variables
# Parse command line arguments
setup_type=$1
test_profile=$2
WHATWOULDYOUSAYYAHDOHERE=setup
while [[ $# -gt 0 ]]; do
arg="$1"
shift
case "$arg" in
"--turbo="* )
export TURBO="http://${arg#*=}";;
"--proxy="* )
export {http,https,ftp,rsync,all}_proxy="${arg#*=}";;
"--allow-cidr="* )
export ALLOW_CIDR="${arg#*=}";;
* )
if [[ "$arg" == "--"* ]]; then
echo "Invalid option"
fi
esac
done
set_timezone
# Preserve old setup/error logs
[ -f "$error_log" ] && mv "$error_log" "$error_log.$(date +%Y-%m-%dT%H:%M:%S)"
[ -f "$setup_log" ] && mv "$setup_log" "$setup_log.$(date +%Y-%m-%dT%H:%M:%S)"
# Let's see what OS we are dealing with here
detect_os
# Ubuntu/Debian whiptail pallete to make it look the same as CentOS and Rocky.
set_palette >> $setup_log 2>&1
if [[ $not_supported ]] && [ -z "$test_profile" ]; then
if [[ "$OSVER" == "focal" ]]; then
if (whiptail_focal_warning); then
true
else
info "User cancelled setup."
whiptail_cancel
fi
else
if (whiptail_unsupported_os_warning); then
true
else
info "User cancelled setup."
whiptail_cancel
fi
fi
fi
# we need to upgrade packages on debian prior to install and reboot if there are due to iptables-restore not running properly
# if packages are updated and the box isn't rebooted
if [[ $is_debian ]]; then
update_packages
if [[ -f "/var/run/reboot-required" ]] && [ -z "$test_profile" ]; then
whiptail_debian_reboot_required
reboot
fi
fi
# Check to see if this is the setup type of "desktop".
is_desktop=
if [ "$setup_type" = 'desktop' ]; then
is_desktop=true
# Check to see if this is an ISO. Usually this dir on exists on ISO installs.
if [ -d /root/SecurityOnion ]; then
is_desktop_iso=true
install_type='DESKTOP'
fi
fi
# Make sure if ISO is specified that we are dealing with CentOS or Rocky
title "Detecting if this is an ISO install"
if [[ "$setup_type" == 'iso' ]]; then
if [[ $is_rpm ]]; then
is_iso=true
else
echo "Only use 'so-setup iso' for an ISO install on Security Onion ISO images. Please run 'so-setup network' instead."
fail_setup
fi
fi
if ! [ -f $install_opt_file ] && [ -d /root/manager_setup/securityonion ] && [[ $(pwd) != /root/manager_setup/securityonion/setup ]]; then
exec bash /root/manager_setup/securityonion/setup/so-setup "${original_args[@]}"
fi
title "Checking to see if install has run before"
if [[ -f /root/accept_changes ]]; then
is_reinstall=true
whiptail_reinstall
info "Old setup detected. Preparing for reinstallation."
reinstall_init
reset_proxy
fi
title "Parsing Username for Install"
parse_install_username
if ! [ -f $install_opt_file ]; then
# Begin Installation pre-processing
title "Initializing Setup"
info "Installing as the $INSTALLUSERNAME user"
analyze_system
fi
# Set up handler for setup to exit early (use `kill -SIGUSR1 "$setup_proc"; exit 1` in child scripts)
trap 'catch $LINENO' SIGUSR1
setup_proc="$$"
catch() {
info "Fatal error occurred at $1 in so-setup, failing setup."
grep --color=never "ERROR" "$setup_log" > "$error_log"
whiptail_setup_failed
fail_setup
}
# Add the progress function for manager node type installs
progress() {
local msg=${1:-'Please wait while installing...'}
if [ -z "$TESTING" ]; then
whiptail --title "$whiptail_title" --gauge "$msg" 6 70 0 # append to text
else
cat >> $setup_log 2>&1
fi
}
if [ -n "$test_profile" ]; then
install_type="$(echo $test_profile |awk -F- '{print $1}')"
if [[ "$install_type" == "distributed" ]]; then
install_type=MANAGER
else
install_type=${install_type^^}
fi
# The below settings are hardcoded purely for automated testing purposes.
TESTING=true
if [[ "$test_profile" =~ "-sensor" ]]; then
install_type=SENSOR
HOSTNAME=sensor
MSRVIP_OFFSET=-2
elif [[ "$test_profile" =~ "-search" ]]; then
install_type=SEARCHNODE
HOSTNAME=search
MSRVIP_OFFSET=-1
elif [[ "$test_profile" =~ "-managersearch" ]]; then
install_type=MANAGERSEARCH
HOSTNAME=manager
elif [[ "$test_profile" =~ "-heavynode" ]]; then
install_type=HEAVYNODE
HOSTNAME=sensor
MSRVIP_OFFSET=-1
elif [[ "$test_profile" =~ "-desktop" ]]; then
install_type=DESKTOP
MSRVIP_OFFSET=-3
is_desktop_grid=true
elif [[ "$test_profile" =~ "-idh" ]]; then
install_type=IDH
HOSTNAME=idh
MSRVIP_OFFSET=-4
elif [[ "$test_profile" =~ "-receiver" ]]; then
install_type=RECEIVER
HOSTNAME=receiver
MSRVIP_OFFSET=-5
elif [[ "$test_profile" =~ "-fleet" ]]; then
install_type=FLEET
HOSTNAME=fleet
MSRVIP_OFFSET=-6
else
HOSTNAME=manager
fi
if [[ "$install_type" =~ "DESKTOP" ]]; then
is_desktop=true
HOSTNAME=desktop
if [[ -z "$is_desktop_grid" ]]; then
is_desktop_grid=false
fi
fi
info "Activating test profile; profile=$test_profile; install_type=$install_type"
MINION_CIDR=10.0.0.0/8
MSRV=manager
if [[ "$test_profile" =~ "-net" ]] || [[ "$test_profile" =~ "-iso" ]]; then
address_type=DHCP
elif [[ "$test_profile" =~ "-cloud" ]]; then
MSRVIP=10.99.1.20
elif [[ "$test_profile" =~ "-airgap" ]]; then
is_airgap=true
address_type=DHCP
fi
if [ -f "/root/public_ip" ]; then
REDIRECTHOST=$(cat /root/public_ip)
REDIRECTINFO=OTHER
else
REDIRECTINFO=IP
fi
ALLOW_CIDR=0.0.0.0/0
nic_list=$(ls -1 /sys/class/net | grep -v docker | grep -v lo)
MNIC=$(echo "$nic_list" | head -1)
BNICS=$(echo "$nic_list" | head -2 | tail -1)
WEBUSER=onionuser@somewhere.invalid
WEBPASSWD1=0n10nus3r
WEBPASSWD2=0n10nus3r
update_sudoers_for_testing
fi
# Make sure the setup type is suppoted.
case "$setup_type" in
iso | network | desktop) # Accepted values
info "Beginning Security Onion $setup_type install"
;;
*)
error "Invalid install type, must be 'iso', 'network' or 'desktop'."
fail_setup
;;
esac
# Allow execution of SO tools during setup
local_sbin="$(pwd)/../salt/common/tools/sbin"
manager_sbin="$(pwd)/../salt/manager/tools/sbin"
export PATH=$PATH:$local_sbin:$manager_sbin
# Kernel messages can overwrite whiptail screen #812
# https://github.com/Security-Onion-Solutions/securityonion/issues/812
dmesg -D
# Kernel consoleblank is causing whiptail progress screen to appear to hang #1084
# https://github.com/Security-Onion-Solutions/securityonion/issues/1084
if [ -z "$TESTING" ]; then
TTY=$(tty)
info "Setup is running on TTY $TTY"
if echo $TTY | grep -q "/dev/tty"; then
CONSOLEBLANK=$(cat /sys/module/kernel/parameters/consoleblank)
info "Kernel consoleblank value before: $CONSOLEBLANK"
if [ $CONSOLEBLANK -gt 0 ]; then
info "Running 'setterm -blank 0' for TTY $TTY"
TERM=linux setterm -blank 0 >$TTY <$TTY
CONSOLEBLANK=$(cat /sys/module/kernel/parameters/consoleblank)
info "Kernel consoleblank value after: $CONSOLEBLANK"
fi
fi
fi
# Begin prompting the user with whiptail.
if ! [[ -f $install_opt_file ]]; then
if (whiptail_you_sure); then
true
else
info "User cancelled setup."
whiptail_cancel
fi
# If this is an desktop install lets streamline the process.
if [[ $is_desktop ]]; then
# Prompt for hostname
collect_hostname
if [[ $is_desktop_iso ]]; then
# Prompt Network Setup
whiptail_management_nic
whiptail_dhcp_or_static
if [ "$address_type" != 'DHCP' ]; then
collect_int_ip_mask
collect_gateway
collect_dns
collect_dns_domain
fi
fi
if [[ ! $is_desktop_iso ]]; then
# This should be a network install
whiptail_network_notice
# Warn about the dangers of DHCP
whiptail_dhcp_warn
whiptail_management_nic
fi
# Initializing the network based on the previous information
network_init
printf '%s\n' \
"MNIC=$MNIC" \
"HOSTNAME=$HOSTNAME" > "$net_init_file"
set_main_ip
compare_main_nic_ip
fi
if [[ $setup_type == 'iso' ]] && [ -z "$TESTING" ]; then
whiptail_first_menu_iso
if [[ $option == "CONFIGURENETWORK" ]]; then
collect_hostname
network_init_whiptail
network_init
printf '%s\n' \
"MNIC=$MNIC" \
"HOSTNAME=$HOSTNAME" > "$net_init_file"
set_main_ip
compare_main_nic_ip
whiptail_net_setup_complete
else
true
fi
fi
if [[ ! $is_desktop ]]; then
whiptail_install_type
fi
else
source $install_opt_file
fi
# Process the install type
process_installtype
# Check to see if this is an desktop install. If it is let's run things differently
if [[ $is_desktop ]]; then
title "This is a desktop install"
# Make sure it's oracle
if [[ $is_oracle ]]; then
info "Security Onion Desktop is supported on this OS."
else
info "Security Onion Desktop is not supported on this OS."
exit 1
fi
whiptail_desktop_install
if ! $is_desktop_grid; then
if [[ $is_desktop_iso ]]; then
if whiptail_desktop_nongrid_iso; then
# Remove setup from auto launching
parse_install_username
sed -i '$ d' /home/$INSTALLUSERNAME/.bash_profile >> "$setup_log" 2>&1
securityonion_repo
info "Enabling graphical interface and setting it to load at boot"
systemctl set-default graphical.target
info "Setting desktop background"
set_desktop_background
echo "Desktop Install Complete!"
echo ""
echo "Please reboot to start graphical interface."
exit 0
else
# Abort!
exit 0
fi
else
if whiptail_desktop_nongrid_network; then
networking_needful
info ""
info ""
info "Kicking off the automated setup of the Security Onion Desktop. This can take a while depending on your network connection."
info ""
info ""
desktop_salt_local
else
# Abort!
exit 0
fi
fi
fi
# If you got this far then you want to join the grid
is_minion=true
fi
# If this is not an automated install prompt
if ! [[ -f $install_opt_file ]]; then
# If you are a manager ask ALL the manager things here. I know there is code re-use but this makes it easier to add new roles
if [[ $is_eval ]]; then
info "Setting up as node type eval"
# waitforstate means we will run the full salt state at the end. This is for only nodes running the salt-master service
waitforstate=true
# Does this role have monitoring interfaces?
monints=true
# Prompt the user to accept the elastic license
check_elastic_license
# If it is an install from ISO is this airgap?
[[ $is_iso ]] && whiptail_airgap
# Make sure minimum requirements are met
check_requirements
# Do networking things
networking_needful
# Do we need a proxy?
[[ ! $is_airgap ]] && collect_net_method
# Do we need to change the dockernet subnet?
collect_dockernet
# Are we in the clouds?
[[ ! $is_airgap ]] && detect_cloud
# Sets some minion info
set_minion_info
set_default_log_size >> $setup_log 2>&1
info "Verifying all network devices are managed by Network Manager that should be"
check_network_manager_conf
set_network_dev_status_list
# What NIC for watching network traffic?
whiptail_sensor_nics
# How many cores do we have?
calculate_useable_cores
# What is the web user?
collect_webuser_inputs
# How are we accessing the UI?
get_redirect
# Does the user want to allow access to the UI?
collect_so_allow
[[ ! $is_airgap ]] && whiptail_accept_telemetry
whiptail_end_settings
elif [[ $is_standalone ]]; then
waitforstate=true
monints=true
check_elastic_license
[[ $is_iso ]] && whiptail_airgap
check_requirements
networking_needful
[[ ! $is_airgap ]] && collect_net_method
collect_dockernet
[[ ! $is_airgap ]] && detect_cloud
set_minion_info
set_default_log_size >> $setup_log 2>&1
info "Verifying all network devices are managed by Network Manager that should be"
check_network_manager_conf
set_network_dev_status_list
whiptail_sensor_nics
calculate_useable_cores
collect_webuser_inputs
get_redirect
collect_so_allow
[[ ! $is_airgap ]] && whiptail_accept_telemetry
whiptail_end_settings
elif [[ $is_manager ]]; then
info "Setting up as node type manager"
check_elastic_license
waitforstate=true
[[ $is_iso ]] && whiptail_airgap
check_requirements
networking_needful
[[ ! $is_airgap ]] && collect_net_method
collect_dockernet
[[ ! $is_airgap ]] && detect_cloud
set_minion_info
set_default_log_size >> $setup_log 2>&1
info "Verifying all network devices are managed by Network Manager that should be"
check_network_manager_conf
set_network_dev_status_list
calculate_useable_cores
collect_webuser_inputs
get_redirect
collect_so_allow
[[ ! $is_airgap ]] && whiptail_accept_telemetry
whiptail_end_settings
elif [[ $is_managersearch ]]; then
info "Setting up as node type managersearch"
check_elastic_license
waitforstate=true
[[ $is_iso ]] && whiptail_airgap
check_requirements
networking_needful
[[ ! $is_airgap ]] && collect_net_method
collect_dockernet
[[ ! $is_airgap ]] && detect_cloud
set_minion_info
set_default_log_size >> $setup_log 2>&1
info "Verifying all network devices are managed by Network Manager that should be"
check_network_manager_conf
set_network_dev_status_list
calculate_useable_cores
collect_webuser_inputs
get_redirect
collect_so_allow
[[ ! $is_airgap ]] && whiptail_accept_telemetry
whiptail_end_settings
elif [[ $is_sensor ]]; then
info "Setting up as node type sensor"
monints=true
check_requirements
calculate_useable_cores
networking_needful
check_network_manager_conf
set_network_dev_status_list
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
detect_cloud
whiptail_sensor_nics
set_minion_info
whiptail_end_settings
elif [[ $is_fleet ]]; then
info "Setting up as node type fleet"
check_requirements
networking_needful
check_network_manager_conf
set_network_dev_status_list
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
detect_cloud
set_minion_info
whiptail_end_settings
elif [[ $is_searchnode ]]; then
info "Setting up as node type searchnode"
check_requirements
networking_needful
check_network_manager_conf
set_network_dev_status_list
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
detect_cloud
set_minion_info
whiptail_end_settings
elif [[ $is_heavynode ]]; then
info "Setting up as node type heavynode"
monints=true
check_requirements
calculate_useable_cores
networking_needful
check_network_manager_conf
set_network_dev_status_list
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
detect_cloud
whiptail_sensor_nics
set_minion_info
whiptail_end_settings
elif [[ $is_idh ]]; then
info "Setting up as node type idh"
check_requirements
networking_needful
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
collect_idh_preferences
set_minion_info
whiptail_end_settings
elif [[ $is_import ]]; then
info "Setting up as node type import"
waitforstate=true
[[ $is_iso ]] && whiptail_airgap
check_elastic_license
check_requirements
networking_needful
[[ ! $is_airgap ]] && detect_cloud
collect_dockernet
[[ ! $is_airgap ]] && collect_net_method
set_minion_info
set_default_log_size >> $setup_log 2>&1
info "Verifying all network devices are managed by Network Manager that should be"
check_network_manager_conf
set_network_dev_status_list
calculate_useable_cores
collect_webuser_inputs
get_redirect
collect_so_allow
[[ ! $is_airgap ]] && whiptail_accept_telemetry
whiptail_end_settings
elif [[ $is_receiver ]]; then
info "Setting up as node type receiver"
check_requirements
networking_needful
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
set_minion_info
whiptail_end_settings
# desktop install will only get this far if joining the grid
elif [[ $is_desktop ]]; then
info "Setting up as node type desktop"
networking_needful
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
set_minion_info
whiptail_end_settings
elif [[ $is_hypervisor ]]; then
info "Setting up as node type hypervisor"
check_requirements
networking_needful
configure_hyper_bridge
collect_mngr_hostname
add_mngr_ip_to_hosts
check_manager_connection
set_minion_info
whiptail_end_settings
elif [[ $is_managerhype ]]; then
info "Setting up as node type managerhype"
check_elastic_license
waitforstate=true
[[ $is_iso ]] && whiptail_airgap
check_requirements
networking_needful
configure_hyper_bridge
[[ ! $is_airgap ]] && collect_net_method
collect_dockernet
[[ ! $is_airgap ]] && detect_cloud
set_minion_info
set_default_log_size >> $setup_log 2>&1
info "Verifying all network devices are managed by Network Manager that should be"
check_network_manager_conf
set_network_dev_status_list
calculate_useable_cores
collect_webuser_inputs
get_redirect
collect_so_allow
[[ ! $is_airgap ]] && whiptail_accept_telemetry
whiptail_end_settings
fi
if [[ $waitforstate ]]; then
touch /root/accept_changes
touch /etc/sohotfix
make_some_dirs
percentage=0
es_heapsize
ls_heapsize
if [[ $needs_proxy ]]; then
set_proxy
fi
set_redirect
if [[ $monints ]]; then
# Generate Interface Vars
generate_interface_vars
configure_network_sensor
fi
info "Reserving ports"
reserve_ports
info "Setting Paths"
# Set the paths
set_path
echo "Disable auto start of setup"
# Disable the setup from prompting at login
disable_auto_start
info "Setting the version"
# Set the version
mark_version
info "Clearing the old manager"
# Remove old manager if re-install
clear_manager
info "Generating Secrets"
# Generate passwords
generate_passwords
info "Populating the secrets pillar"
# Create the secrets pillar
secrets_pillar
info "Add socore user"
# Add the socore user
add_socore_user_manager
create_local_directories ${SCRIPTDIR::-5}
setup_salt_master_dirs
create_manager_pillars
info "Generating the minion pillar"
# Create the minion defaults
export NODETYPE=$install_type
export MINION_ID=$MINION_ID
export ES_HEAP_SIZE=$ES_HEAP_SIZE
export MNIC=$MNIC
export NODE_DESCRIPTION=$NODE_DESCRIPTION
export MAINIP=$MAINIP
export PATCHSCHEDULENAME=$PATCHSCHEDULENAME
export INTERFACE=$INTERFACE
if [[ $low_mem == "true" ]]; then
export CORECOUNT=1
else
export CORECOUNT=$lb_procs
fi
export LSHOSTNAME=$HOSTNAME
export LSHEAP=$LS_HEAP_SIZE
export CPUCORES=$num_cpu_cores
logCmd "so-minion -o=setup"
title "Creating Global SLS"
if [[ $is_airgap ]]; then
# Airgap Rules
airgap_rules
fi
manager_pillar
# Set up the repo to point to local file https://access.redhat.com/solutions/1355683
# reposync down the files is network and createrepo if CentOS
# Import the GPG keys
gpg_rpm_import
# Create the local repo and point the box to use the local repo
securityonion_repo
# Update existing packages
update_packages
# Install salt
saltify
# Start the master service
copy_salt_master_config
configure_minion "$minion_type"
check_sos_appliance
logCmd "salt-key -yd $MINION_ID"
sleep 2 # Debug RSA Key format errors
logCmd "salt-call state.show_top"
sleep 2 # Debug RSA Key format errors
logCmd "salt-key -ya $MINION_ID"
logCmd "salt-call saltutil.sync_all"
logCmd "salt-call state.apply common.packages"
logCmd "salt-call state.apply common"
# this will apply the salt.minion state first since salt.master includes salt.minion
logCmd "salt-call state.apply salt.master"
# wait here until we get a response from the salt-master since it may have just restarted
# exit setup after 5-6 minutes of trying
check_salt_master_status || fail "Can't access salt master or it is not ready"
# apply the ca state to create the ca and put it in the mine early in the install
# the minion ip will already be in the mine from configure_minion function in so-functions
generate_ca
# this will also call the ssl state since docker requires the intca
# the salt-minion service will need to be up on the manager to sign requests
generate_ssl
logCmd "salt-call state.apply docker"
firewall_generate_templates
set_initial_firewall_policy
# Download Elastic Agent Artifacts
title "Downloading Elastic Agent Artifacts"
download_elastic_agent_artifacts
logCmd "salt-call state.apply -l info firewall"
# create these so the registry state can add so-registry to /opt/so/conf/so-status/so-status.conf
logCmd "mkdir -p /opt/so/conf/so-status/ "
logCmd "touch /opt/so/conf/so-status/so-status.conf"
title "Importing Registry Docker"
import_registry_docker
title "Applying the registry state"
logCmd "salt-call state.apply -l info registry"
title "Seeding the docker registry"
docker_seed_registry
title "Applying the manager state"
logCmd "salt-call state.apply -l info manager"
logCmd "salt-call state.apply influxdb -l info"
logCmd "salt-call state.highstate -l info"
logCmd "salt-call schedule.disable -linfo --local"
if [[ ! $is_airgap ]]; then
title "Downloading IDS Rules"
logCmd "so-rule-update"
if [[ $monints || $is_import ]]; then
title "Applying the Suricata state to load the new rules"
logCmd "salt-call state.apply suricata -l info"
fi
fi
if [[ $is_airgap ]]; then
title "Syncing AI-Generated Detection Summaries"
airgap_detection_summaries
fi
title "Setting up Kibana Default Space"
logCmd "so-kibana-space-defaults"
add_web_user
info "Restarting SOC to pick up initial user"
logCmd "so-soc-restart"
title "Setting up Elastic Fleet"
logCmd "salt-call state.apply elasticfleet.config"
if ! logCmd so-elastic-fleet-setup; then
error "Failed to run so-elastic-fleet-setup"
fail_setup
fi
checkin_at_boot
set_initial_firewall_access
logCmd "salt-call schedule.enable -linfo --local"
verify_setup
else
touch /root/accept_changes
mkdir -p /opt/so
es_heapsize
ls_heapsize
generate_interface_vars
if [[ $monints ]]; then
configure_network_sensor
fi
reserve_ports
# Set the version
mark_version
# Disable the setup from prompting at login
disable_auto_start
info "Clearing the old manager"
# Remove old manager if re-install
clear_manager
gpg_rpm_import
securityonion_repo
update_packages
saltify
configure_minion "$minion_type"
check_sos_appliance
drop_install_options
verify_setup
fi
# Need to make sure the latest install is located on the web server of the manager to check the versions and download the code if required
fi