#!/bin/bash

# Copyright 2014-2020 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/>.

# Make sure you are root before doing anything
uid="$(id -u)"
if [ "$uid" -ne 0 ]; then
	echo "This script must be run using sudo!"
	exit 1
fi

# Save the original argument array since we modify it
original_args=("$@")

cd "$(dirname "$0")" || exit 255

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
automation=$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-role="* )
			export ALLOW_ROLE="${arg#*=}";;
		"--allow-cidr="* )
			export ALLOW_CIDR="${arg#*=}";;
		"--skip-reboot" )
			export SKIP_REBOOT=1;;
		* )
			if [[ "$arg" == "--"* ]]; then
				echo "Invalid option"
			fi
	esac
done

if [[ "$setup_type" == 'iso' ]]; then
  is_iso=true
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

if [[ -f /root/accept_changes ]]; then
	is_reinstall=true

	# Move last setup log to backup
	mv "$setup_log" "$setup_log.bak"
	[ -f "$error_log" ] && mv "$error_log" "$error_log.bak"
fi

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
	exit 1
}

automated=no
progress() {
	local msg=${1:-'Please wait while installing...'}

	if [ $automated == no ]; then
		whiptail --title "$whiptail_title" --gauge "$msg" 6 70 0 # append to text
	else
		cat >> $setup_log 2>&1
	fi		
}

detect_os 

if [[ -f automation/$automation && $(basename $automation) == $automation ]]; then
	echo "Preselecting variable values based on automated setup: $automation" >> $setup_log 2>&1
	source automation/$automation
	automated=yes

	attempt=1
	attempts=60
	ip a | grep "$MNIC:" | grep "state UP" >> $setup_log 2>&1
	while [ $? -ne 0 ]; do
		ip a >> $setup_log 2>&1
		if [ $attempt -gt $attempts ]; then
			echo "Network unavailable - setup cannot continue" >> $setup_log 2>&1
			exit 1
		fi
		echo "Waiting for network to come up (attempt $attempt of $attempts)" >> $setup_log 2>&1
		attempt=$((attempt + 1))
		sleep 10;
		ip a | grep "$MNIC:" | grep "state UP" >> $setup_log 2>&1
	done
	echo "Network is up on $MNIC" >> $setup_log 2>&1

	if [[ ! $is_iso ]]; then
		echo "Installing sshpass for automated testing." >> $setup_log 2>&1
		if [ "$OS" == ubuntu ]; then
			retry 50 10 "apt-get -y install sshpass" >> $setup_log 2>&1 || exit 1
		else
			yum -y install sshpass >> $setup_log 2>&1
		fi
	fi
fi

case "$setup_type" in
	iso | network) # Accepted values
		echo "Beginning Security Onion $setup_type install" >> $setup_log 2>&1
		;;
	*)
		echo "Invalid install type, must be 'iso' or 'network'" | tee -a $setup_log
		exit 1
		;;
esac

#set ssh commands that will be used based on if this is an automated test install or not
set_ssh_cmds $automated

# Allow execution of SO tools during setup
local_sbin="$(pwd)/../salt/common/tools/sbin"
export PATH=$PATH:$local_sbin

set_palette >> $setup_log 2>&1

# 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 [ "$automated" == no ]; then
	TTY=$(tty)
	echo "Setup is running on TTY $TTY" >> $setup_log 2>&1
	if echo $TTY | grep -q "/dev/tty"; then 
		CONSOLEBLANK=$(cat /sys/module/kernel/parameters/consoleblank)
		echo "Kernel consoleblank value before: $CONSOLEBLANK" >> $setup_log 2>&1
		if [ $CONSOLEBLANK -gt 0 ]; then 
			echo "Running 'setterm -blank 0' for TTY $TTY" >> $setup_log 2>&1
			TERM=linux setterm -blank 0 >$TTY <$TTY
			CONSOLEBLANK=$(cat /sys/module/kernel/parameters/consoleblank)
			echo "Kernel consoleblank value after: $CONSOLEBLANK" >> $setup_log 2>&1
		fi
	fi
fi

if ! [[ -f $install_opt_file ]]; then
	if (whiptail_you_sure); then
		true
	else
		echo "User cancelled setup." | tee -a "$setup_log"
		whiptail_cancel
	fi
	if [[ $setup_type == 'iso' ]] && [ "$automated" == no ]; then
		whiptail_first_menu_iso
		if [[ $option == "CONFIGURENETWORK" ]]; then
			collect_hostname
			network_init_whiptail
			whiptail_network_init_notice
			network_init
			printf '%s\n' \
				"MNIC=$MNIC" \
				"HOSTNAME=$HOSTNAME" > "$net_init_file"
			set_main_ip >> $setup_log 2>&1
			compare_main_nic_ip
			whiptail_net_setup_complete
		else
			true
		fi
	fi
	whiptail_install_type
else
	source $install_opt_file
fi

if [ "$install_type" = 'EVAL' ]; then
	is_node=true
	is_manager=true
	is_sensor=true
	is_eval=true
	STRELKARULES=1
elif [ "$install_type" = 'STANDALONE' ]; then
	is_manager=true
	is_distmanager=true
	is_node=true
	is_sensor=true
elif [ "$install_type" = 'MANAGERSEARCH' ]; then
	is_manager=true
	is_distmanager=true
	is_node=true
elif [ "$install_type" = 'MANAGER' ]; then
	is_manager=true
	is_distmanager=true
elif [ "$install_type" = 'SENSOR' ]; then
	is_sensor=true
	is_minion=true
elif [[ "$install_type" =~ ^('SEARCHNODE'|'HOTNODE'|'WARMNODE')$ ]]; then
	is_node=true
	is_minion=true
elif [ "$install_type" = 'HEAVYNODE' ]; then
	is_node=true
	is_minion=true
	is_sensor=true
elif [ "$install_type" = 'FLEET' ]; then
	is_minion=true
	is_fleet_standalone=true
	OSQUERY=1
elif [ "$install_type" = 'HELIXSENSOR' ]; then
	is_helix=true
elif [ "$install_type" = 'IMPORT' ]; then
	is_import=true
elif [ "$install_type" = 'ANALYST' ]; then
	is_analyst=true
fi

if [[ $is_manager || $is_import ]]; then
	check_elastic_license
fi

if ! [[ -f $install_opt_file ]]; then
	# Check if this is an airgap install
	if [[ ( $is_manager || $is_import || $is_minion ) && $is_iso ]]; then
		whiptail_airgap
		if [[ "$INTERWEBS" == 'AIRGAP' ]]; then
			is_airgap=true
		fi
	fi

	if [[ $is_manager && $is_sensor ]]; then
		check_requirements "standalone"
	elif [[ $is_fleet_standalone ]]; then
		check_requirements "dist" "fleet"
	elif [[ $is_sensor && ! $is_eval ]]; then
		check_requirements "dist" "sensor"
	elif [[ $is_distmanager || $is_minion ]] && [[ ! $is_import ]]; then
		check_requirements "dist"
	elif [[ $is_import ]]; then
		check_requirements "import"
	fi

	[[ -f $net_init_file ]] && whiptail_net_reinit && reinit_networking=true

	if [[ $reinit_networking ]] || ! [[ -f $net_init_file ]]; then
		collect_hostname
	fi

	[[ ! ( $is_eval || $is_import ) ]] && whiptail_node_description

	if [[ $reinit_networking ]] || ! [[ -f $net_init_file ]]; then
		network_init_whiptail
	else
		source "$net_init_file"
	fi

	if [[ $reinit_networking ]] || ! [[ -f $net_init_file ]]; then
		whiptail_network_init_notice
		network_init
	fi

	set_main_ip >> $setup_log 2>&1
	compare_main_nic_ip

	if [[ $is_minion ]]; then
		collect_mngr_hostname
		add_mngr_ip_to_hosts
	fi

	reset_proxy
	if [[ -z $is_airgap ]]; then
		collect_net_method
		[[ -n "$so_proxy" ]] && set_proxy >> $setup_log 2>&1
	fi

	if [[ $is_minion ]]; then
		whiptail_ssh_key_copy_notice
		copy_ssh_key >> $setup_log 2>&1
	fi

	if [[ $is_minion ]] && ! (compare_versions); then
		info "Installer version mismatch, downloading correct version from manager"
		printf '%s\n' \
			"install_type=$install_type" \
			"MNIC=$MNIC" \
			"HOSTNAME=$HOSTNAME" \
			"MSRV=$MSRV" \
			"MSRVIP=$MSRVIP" \
			"is_airgap=$is_airgap" \
			"NODE_DESCRIPTION=\"$NODE_DESCRIPTION\"" > "$install_opt_file"
		[[ -n $so_proxy ]] && echo "so_proxy=$so_proxy" >> "$install_opt_file"
		download_repo_tarball
		exec bash /root/manager_setup/securityonion/setup/so-setup "${original_args[@]}"
	fi

	if [[ $is_analyst ]]; then
		cd .. || exit 255
		exec bash so-analyst-install
	fi

else
	rm -rf $install_opt_file >> "$setup_log" 2>&1
fi

percentage=0
{
	installer_progress_loop & # Run progress bar to 98 in ~8 minutes while waiting for package installs
	progress_bg_proc=$!
	installer_prereq_packages
	install_success=$?
	kill -9 "$progress_bg_proc"
	wait "$progress_bg_proc" &> /dev/null # Kill just sends signal, redirect output of wait to catch stdout
	if [[ $install_success -gt 0 ]]; then
		echo "Could not install packages required for setup, exiting now." >> "$setup_log" 2>&1
		kill -SIGUSR1 "$setup_proc"; exit 1
	fi
} | progress '...'

detect_cloud

short_name=$(echo "$HOSTNAME" | awk -F. '{print $1}')

MINION_ID=$(echo "${short_name}_${install_type}" | tr '[:upper:]' '[:lower:]')
export MINION_ID

echo "MINION_ID = $MINION_ID" >> $setup_log 2>&1

minion_type=$(get_minion_type)

# Set any variables needed
set_default_log_size >> $setup_log 2>&1

if [[ $is_helix ]]; then
	RULESETUP=${RULESETUP:-ETOPEN}
	NSMSETUP=${NSMSETUP:-BASIC}
	HNSENSOR=${HNSENSOR:-inherit}
	MANAGERUPDATES=${MANAGERUPDATES:-0}
fi

if [[ $is_helix || ( $is_manager && $is_node ) ]]; then
	RULESETUP=${RULESETUP:-ETOPEN}
	NSMSETUP=${NSMSETUP:-BASIC}
fi

if [[ $is_manager && $is_node ]]; then
	LSPIPELINEWORKERS=${LSPIPELINEWORKERS:-1}
	LSPIPELINEBATCH=${LSPIPELINEBATCH:-125}
	LSINPUTTHREADS=${LSINPUTTHREADS:-1}
	LSPIPELINEWORKERS=${LSPIPELINEBATCH:-125}
	NIDS=${NIDS:-Suricata}
	ZEEKVERSION=${ZEEKVERSION:-ZEEK}
fi

if [[ $is_node ]]; then
	CURCLOSEDAYS=${CURCLOSEDAYS:-30}
fi

if [[ $is_import ]]; then
	PATCHSCHEDULENAME=${PATCHSCHEDULENAME:-auto}
	MTU=${MTU:-1500}
	RULESETUP=${RULESETUP:-ETOPEN}
	NSMSETUP=${NSMSETUP:-BASIC}
	HNSENSOR=${HNSENSOR:-inherit}
	MANAGERUPDATES=${MANAGERUPDATES:-0}
	MANAGERADV=${MANAGERADV:-BASIC}
	INTERFACE=${INTERFACE:-bond0}
	ZEEKVERSION=${ZEEKVERSION:-ZEEK}
	NIDS=${NIDS:-Suricata}
	RULESETUP=${RULESETUP:-ETOPEN}
	GRAFANA=${GRAFANA:-0}
	OSQUERY=${OSQUERY:-0}
	WAZUH=${WAZUH:-0}
	THEHIVE=${THEHIVE:-0}
	PLAYBOOK=${PLAYBOOK:-0}
fi

if [[ $is_airgap ]]; then
	PATCHSCHEDULENAME=${PATCHSCHEDULENAME:-manual}
	[[ ! $is_minion ]] && MANAGERUPDATES=${MANAGERUPDATES:-0} || MANAGERUPDATES=${MANAGERUPDATES:-1}
fi

# Start user prompts

if [[ $is_helix ]]; then
	collect_helix_key
fi

if [[ $is_helix || $is_sensor ]]; then
	set_network_dev_status_list
	whiptail_sensor_nics
fi

if [[ $is_helix || $is_sensor || $is_import ]]; then
	calculate_useable_cores
fi

if [[ ! $is_airgap && ! $is_import ]]; then
	collect_patch_schedule
fi

if [[ $is_helix ||  $is_manager || $is_import ]]; then
	collect_homenet_mngr
fi

if [[ $is_helix || $is_manager || $is_node || $is_import ]]; then
	set_base_heapsizes
fi

if [[ $is_manager && ! $is_eval ]]; then
	whiptail_manager_adv
	if [ "$MANAGERADV" = 'ADVANCED' ]; then
	  if [ "$install_type" = 'MANAGER' ] || [ "$install_type" = 'MANAGERSEARCH' ]; then
	  	collect_es_cluster_name
	  fi
	fi

	whiptail_metadata_tool
	
	[[ $MANAGERADV == "ADVANCED" ]] && [[ $ZEEKVERSION == "ZEEK" ]] && whiptail_manager_adv_service_zeeklogs

	# Don't run this function for now since Snort is not yet supported
	# whiptail_nids
	NIDS=Suricata
	whiptail_rule_setup

	if [ "$RULESETUP" != 'ETOPEN' ]; then
		collect_oinkcode
	fi
fi

if [[ $is_manager ]]; then
	whiptail_components_adv_warning
	whiptail_enable_components

	if [[ "$STRELKA" = 1 ]]; then
		info "Enabling Strelka rules"
		STRELKARULES=1
	else
		info "Disabling Strelka rules: STRELKA='$STRELKA'"
	fi

	collect_dockernet
fi

if [[ $is_manager || $is_import ]]; then
	collect_webuser_inputs
	get_redirect
fi

if [[ $is_distmanager ]]; then
	collect_soremote_inputs
fi

if [[ $is_sensor && ! $is_eval ]]; then
	[[ $is_manager ]] || collect_homenet_snsr
	whiptail_sensor_config
	if [ $NSMSETUP == 'ADVANCED' ]; then
		if [[ $is_manager ]]; then
			[[ $ZEEKVERSION == "ZEEK" ]] && whiptail_zeek_pins
		else
			whiptail_zeek_pins
		fi
		
		whiptail_suricata_pins
		collect_mtu
	else
		if [[ $is_node && $is_sensor && ! $is_eval ]]; then
			PROCS=$(( lb_procs / 2 ))
			if [ "$PROCS" -lt 1 ]; then PROCS=1; else PROCS=$PROCS; fi
		else
			PROCS=$lb_procs
		fi

		if [[ $is_manager ]]; then
			[[ $ZEEKVERSION == "ZEEK" ]] && collect_zeek
		else
			collect_zeek
		fi

		collect_suri
	fi
fi

[[ $is_iso ]] && collect_ntp_servers

if [[ $is_node && ! $is_eval ]]; then
	whiptail_node_advanced
	if [ "$NODESETUP" == 'NODEADVANCED' ]; then
		collect_node_es_heap
		collect_node_ls_heap
		collect_node_ls_pipeline_worker_count
		collect_node_ls_pipeline_batch_size
		collect_node_ls_input
		collect_cur_close_days
		collect_es_space_limit
	else
		NODE_ES_HEAP_SIZE=$ES_HEAP_SIZE
		NODE_LS_HEAP_SIZE=$LS_HEAP_SIZE
		LSPIPELINEWORKERS=$num_cpu_cores
		LSPIPELINEBATCH=125
		LSINPUTTHREADS=1
	fi
fi

if [ "$install_type" == 'FLEET' ]; then
	collect_fleetuser_inputs
	collect_fleet_custom_hostname_inputs
else
	FLEETNODEUSER=$WEBUSER
	FLEETNODEPASSWD1=$WEBPASSWD1
fi

if [[ $is_manager || $is_import ]]; then collect_so_allow; fi

# This block sets REDIRECTIT which is used by a function outside the below subshell
set_redirect >> $setup_log 2>&1

if [[ $is_minion ]] && ! check_manager_state; then
	echo "Manager was not in a good state" >> "$setup_log" 2>&1
	whiptail_manager_error
fi

whiptail_end_settings

# From here on changes will be made.
echo "1" > /root/accept_changes


# Begin install
{
	# Set initial percentage to 0
	export percentage=0

	# Show initial progress message
	set_progress_str 0 'Running initial configuration steps'

	[[ ${#ntp_servers[@]} -gt 0 ]] && configure_ntp >> $setup_log 2>&1

	reserve_ports
	
	set_path

	if [[ $is_reinstall ]]; then
		reinstall_init
	fi

	disable_auto_start

	{
		mark_version;
		clear_manager;
	} >> $setup_log 2>&1


	if [[ $is_manager || $is_import ]]; then
		{
			generate_passwords;
			secrets_pillar;
		} >> $setup_log 2>&1	
	fi

	if [[ $is_manager || $is_import || $is_helix ]]; then
		add_socore_user_manager >> $setup_log 2>&1
	fi

	if [[ $is_manager && ! $is_eval ]]; then
		add_soremote_user_manager >> $setup_log 2>&1
	fi

	host_pillar >> $setup_log 2>&1
	ntp_pillar >> $setup_log 2>&1


	if [[ $is_minion || $is_import ]]; then
		set_updates >> $setup_log 2>&1
	fi

	if [[ ( $is_manager || $is_import ) && $is_airgap ]]; then
		info "Creating airgap repo"
		create_repo >> $setup_log 2>&1
		airgap_rules >> $setup_log 2>&1
	fi

	if [[ $is_minion ]]; then
		set_progress_str 1 'Configuring firewall'
		set_initial_firewall_policy >> $setup_log 2>&1
	fi

	set_progress_str 2 'Updating packages'
	# Import the gpg keys
	gpg_rpm_import >> $setup_log 2>&1
	info "Disabling fastestmirror"
	[[ $OS == 'centos' ]] && disable_fastestmirror
	if [[ ! $is_airgap ]]; then
	    securityonion_repo >> $setup_log 2>&1
	    update_packages >> $setup_log 2>&1
	else
	    airgap_repo >> $setup_log 2>&1
	fi

	if [[ $is_sensor || $is_helix || $is_import ]]; then
		set_progress_str 3 'Generating sensor pillar'
		generate_sensor_vars
		sensor_pillar >> $setup_log 2>&1
		if [[ $is_sensor || $is_helix ]]; then
			steno_pillar >> $setup_log
		fi
	fi

	if [[ $is_sensor || $is_helix ]]; then
		set_progress_str 4 'Configuring sensor interface'
		configure_network_sensor >> $setup_log 2>&1
	fi

	set_progress_str 5 'Installing Salt and dependencies'
	saltify 2>> $setup_log

	set_progress_str 6 'Installing Docker and dependencies'
	docker_install >> $setup_log 2>&1
	
	set_progress_str 7 'Generating patch pillar'
	patch_pillar >> $setup_log 2>&1

	set_progress_str 8 'Initializing Salt minion'
	configure_minion "$minion_type" >> $setup_log 2>&1
	check_sos_appliance >> $setup_log 2>&1

	update_sudoers_for_testing >> $setup_log 2>&1

	if [[ $is_manager || $is_helix || $is_import ]]; then
		set_progress_str 9 'Configuring Salt master'
		{
			create_local_directories;
			addtotab_generate_templates;
			copy_salt_master_config;
			setup_salt_master_dirs;
			firewall_generate_templates;
		} >> $setup_log 2>&1
		
		set_progress_str 10 'Updating sudoers file for soremote user'
		update_sudoers >> $setup_log 2>&1
		
		set_progress_str 11 'Generating manager global pillar'
		#minio_generate_keys
		manager_global >> $setup_log 2>&1
		
		set_progress_str 12 'Generating manager pillar'
		manager_pillar >> $setup_log 2>&1
		zeek_logs_enabled >> $setup_log 2>&1
	fi
	
	set_progress_str 16 'Running first Salt checkin'
	salt_firstcheckin >> $setup_log 2>&1

	if [[ $is_helix ]]; then
		set_progress_str 17 'Generating the FireEye pillar'
		fireeye_pillar >> $setup_log 2>&1
	fi
	
	if [[ $is_node ]]; then
		set_progress_str 18 'Setting node type'
		set_node_type >> $setup_log 2>&1

		if ! [[ $is_manager || $is_helix ]]; then
			set_progress_str 19 'Generating search node pillar'
			elasticsearch_pillar >> $setup_log 2>&1
		fi
	fi

	if [[ $is_minion ]]; then
		set_progress_str 20 'Accepting Salt key on manager'
		retry 20 10	accept_salt_key_remote "going to be accepted" >> $setup_log 2>&1
	fi

	if [[ $is_manager || $is_import || $is_helix ]]; then
		set_progress_str 20 'Accepting Salt key'
		retry 20 10 "salt-key -ya $MINION_ID" "going to be accepted" >> $setup_log 2>&1
	fi

	set_progress_str 21 'Copying minion pillars to manager'
	copy_minion_tmp_files >> $setup_log 2>&1

	if [[ $is_minion ]]; then
		set_progress_str 22 'Checking if the Salt Minion needs to be updated'
		salt-call state.apply -l info salt.minion >> $setup_log 2>&1
	fi

	set_progress_str 23 'Generating CA and checking in'
	salt_checkin >> $setup_log 2>&1

	if [[ $is_manager || $is_helix || $is_import ]]; then
		set_progress_str 25 'Configuring firewall'
		set_initial_firewall_policy >> $setup_log 2>&1
		
		# create these so the registry state can add so-registry to /opt/so/conf/so-status/so-status.conf
		mkdir -p /opt/so/conf/so-status/ >> $setup_log 2>&1
		touch /opt/so/conf/so-status/so-status.conf >> $setup_log 2>&1

		if [[ "$setup_type" == 'iso' ]]; then
			set_progress_str 26 'Copying containers from iso'
		else
			set_progress_str 26 'Downloading containers from the internet'
		fi
		import_registry_docker >> $setup_log 2>&1
		salt-call state.apply -l info registry >> $setup_log 2>&1
		docker_seed_registry # ~ 60% when finished
		
		set_progress_str 60 "$(print_salt_state_apply 'manager')"
		salt-call state.apply -l info manager >> $setup_log 2>&1
	fi

	set_progress_str 61 "$(print_salt_state_apply 'firewall')"
	salt-call state.apply -l info firewall >> $setup_log 2>&1

	if [ $OS = 'centos' ]; then
		set_progress_str 61 'Installing Yum utilities'
		salt-call state.apply -l info yum.packages >> $setup_log 2>&1
	fi

	set_progress_str 62 "$(print_salt_state_apply 'common')"
	salt-call state.apply -l info common >> $setup_log 2>&1

	if [[ ! $is_helix ]]; then
		set_progress_str 62 "$(print_salt_state_apply 'nginx')"
		salt-call state.apply -l info nginx >> $setup_log 2>&1
	fi

	if [[ $is_manager || $is_helix || $is_import ]]; then
		set_progress_str 63 "$(print_salt_state_apply 'idstools')"
		create_local_nids_rules >> $setup_log 2>&1
		salt-call state.apply -l info idstools >> $setup_log 2>&1

		set_progress_str 63 "$(print_salt_state_apply 'suricata.manager')"
		salt-call state.apply -l info suricata.manager >> $setup_log 2>&1
	fi

	if [[ $is_manager || $is_node || $is_import || $is_helix ]]; then
		set_progress_str 64 "$(print_salt_state_apply 'elasticsearch')"
		salt-call state.apply -l info elasticsearch >> $setup_log 2>&1
	fi

	if [[ $is_sensor || $is_import ]]; then
		set_progress_str 65 "$(print_salt_state_apply 'pcap')"
		salt-call state.apply -l info pcap >> $setup_log 2>&1
	fi

	if [[ $is_sensor || $is_import || $is_helix ]]; then
		set_progress_str 66 "$(print_salt_state_apply 'suricata')"
		salt-call state.apply -l info suricata >> $setup_log 2>&1

		if [[ $(lookup_pillar "mdengine") == 'ZEEK' ]]; then
			set_progress_str 67 "$(print_salt_state_apply 'zeek')"
			salt-call state.apply -l info zeek >> $setup_log 2>&1
		fi
	fi

	if [[ $is_node ]]; then
		set_progress_str 68 "$(print_salt_state_apply 'curator')"
		salt-call state.apply -l info curator >> $setup_log 2>&1
	fi

	if [[ $is_manager || $is_import ]]; then
		set_progress_str 69 "$(print_salt_state_apply 'soc')"
		salt-call state.apply -l info soc >> $setup_log 2>&1

		set_progress_str 70 "$(print_salt_state_apply 'kibana')"
		salt-call state.apply -l info kibana >> $setup_log 2>&1

		set_progress_str 70 "Setting up default Space in Kibana"
		so-kibana-space-defaults >> $setup_log 2>&1
	fi

	if [[ "$PLAYBOOK" = 1 ]]; then
		set_progress_str 71 "$(print_salt_state_apply 'playbook.db_init')"
		salt-call state.apply -l info playbook.db_init >> $setup_log 2>&1

		set_progress_str 71 "$(print_salt_state_apply 'playbook')"
		salt-call state.apply -l info playbook >> $setup_log 2>&1

		set_progress_str 71 "$(print_salt_state_apply 'playbook.automation_user_create')"
		salt-call state.apply -l info playbook.automation_user_create >> $setup_log 2>&1
	fi

	if [[ $is_manager ]]; then
		set_progress_str 72 "$(print_salt_state_apply 'elastalert')"
		salt-call state.apply -l info elastalert >> $setup_log 2>&1

		set_progress_str 73 "$(print_salt_state_apply 'soctopus')"
		salt-call state.apply -l info soctopus >> $setup_log 2>&1

		if [[ "$PLAYBOOK" = 1 ]]; then
			set_progress_str 73 "Update playbook rules"
			so-playbook-ruleupdate >> /root/setup_playbook_rule_update.log 2>&1 &
		fi

		if [[ "$GRAFANA" = 1 ]]; then
			set_progress_str 74 "Installing InfluxDB and Grafana"
			salt-call state.apply -l info influxdb >> $setup_log 2>&1
			salt-call state.apply -l info grafana >> $setup_log 2>&1
		fi

	fi
	
	if [[ "$OSQUERY" = 1 ]]; then

		set_progress_str 75 "$(print_salt_state_apply 'fleet.event_enable-fleet')"
		salt-call state.apply -l info fleet.event_enable-fleet >> $setup_log 2>&1

		set_progress_str 75 "$(print_salt_state_apply 'fleet')"
		salt-call state.apply -l info fleet >> $setup_log 2>&1

		set_progress_str 76 "$(print_salt_state_apply 'redis')"
		salt-call state.apply -l info redis >> $setup_log 2>&1

		if [[ $is_fleet_standalone && $FLEETCUSTOMHOSTNAME != '' ]]; then
			set_progress_str 77 "$(print_salt_state_apply 'fleet.event_update-custom-hostname')"
			pillar_override="{\"global\":{\"fleet_custom_hostname\": \"$FLEETCUSTOMHOSTNAME\"}}"
			salt-call state.apply -l info fleet.event_update-custom-hostname pillar="$pillar_override" >> $setup_log 2>&1
		fi
	
		set_progress_str 78 "$(print_salt_state_apply 'so-fleet-setup')"	
		so-fleet-setup "$FLEETNODEUSER" "$FLEETNODEPASSWD1" >> $setup_log 2>&1

	fi

	if [[ "$WAZUH" = 1 ]]; then
		set_progress_str 79 "$(print_salt_state_apply 'wazuh')"
		salt-call state.apply -l info wazuh >> $setup_log 2>&1
	fi

	if [[ "$THEHIVE" = 1 ]]; then
		set_progress_str 80 "$(print_salt_state_apply 'thehive')"
		salt-call state.apply -l info thehive >> $setup_log 2>&1
	fi

	if [[ "$STRELKA" = 1 ]]; then
		if [[ $is_sensor ]]; then
			set_progress_str 81 "$(print_salt_state_apply 'strelka')"
			salt-call state.apply -l info strelka >> $setup_log 2>&1
		fi
		if [[ "$STRELKARULES" = 1 ]]; then
			logCmd /usr/sbin/so-yara-update
		else
			info "Skipping running yara update: STRELKARULES='$STRELKARULES'"
		fi
	fi

	if [[ $is_manager || $is_import ]]; then		
		set_progress_str 82 "$(print_salt_state_apply 'utility')"
		salt-call state.apply -l info utility >> $setup_log 2>&1
	fi

	if [[ ( $is_helix || $is_manager || $is_node ) && ! $is_eval ]]; then
		set_progress_str 83 "$(print_salt_state_apply 'logstash')"
		salt-call state.apply -l info logstash >> $setup_log 2>&1

		set_progress_str 84 "$(print_salt_state_apply 'filebeat')"
		salt-call state.apply -l info filebeat >> $setup_log 2>&1
	fi

	set_progress_str 85 'Applying finishing touches'
	filter_unused_nics >> $setup_log 2>&1
	network_setup >> $setup_log 2>&1
	so-ssh-harden >> $setup_log 2>&1

	if [[ $is_manager || $is_import ]]; then
		set_progress_str 87 'Adding user to SOC'
		add_web_user >> $setup_log 2>&1
	fi

	set_progress_str 90 'Enabling checkin at boot'
	checkin_at_boot >> $setup_log 2>&1

	set_progress_str 95 'Verifying setup'
	salt-call -l info state.highstate >> $setup_log 2>&1

} | progress

success=$(tail -10 $setup_log | grep Failed | awk '{ print $2}')
if [[ $success != 0 ]]; then SO_ERROR=1; fi

# Check entire setup log for errors or unexpected salt states and ensure cron jobs are not reporting errors to root's mailbox
# Ignore "Status .* was not found" due to output from salt http.query or http.wait_for_successful_query states used with retry
if grep -E "ERROR|Result: False" $setup_log | grep -qvE "Status .* was not found" || [[ -s /var/spool/mail/root && "$setup_type" == "iso" ]]; then 
	SO_ERROR=1

	grep --color=never "ERROR" "$setup_log" | grep -qvE "Status .* was not found" > "$error_log"
fi

if [[ -n $SO_ERROR ]]; then 
	echo "Errors detected during setup; skipping post-setup steps to allow for analysis of failures." >> $setup_log 2>&1

	SKIP_REBOOT=1
	whiptail_setup_failed

else 
	echo "Successfully completed setup! Continuing with post-installation steps" >> $setup_log 2>&1
	{
		export percentage=95 # set to last percentage used in previous subshell
		if [[ -n $ALLOW_ROLE && -n $ALLOW_CIDR ]]; then 
			set_progress_str 96 "Stopping SOC prior to adjusting firewall rules"
			so-soc-stop # Stop SOC so it doesn't accept external requests prior to the reboot
			
			set_progress_str 97 "Running so-allow -${ALLOW_ROLE} for ${ALLOW_CIDR}"
			IP=$ALLOW_CIDR so-allow -$ALLOW_ROLE >> $setup_log 2>&1
		fi

		if [[ $is_manager ]]; then
			set_progress_str 98 "Generating archive for setup directory"
			generate_repo_tarball >> "$setup_log" 2>&1
		fi

		if [[ $THEHIVE == 1 ]]; then 
			set_progress_str 99 'Waiting for TheHive to start up'
			check_hive_init >> $setup_log 2>&1
		fi
	} | whiptail_gauge_post_setup "Running post-installation steps..."

	whiptail_setup_complete
	[[ $setup_type != 'iso' ]] && whitpail_ssh_warning
	echo "Post-installation steps have completed." >> $setup_log 2>&1
fi

install_cleanup >> "$setup_log" 2>&1

if [[ -z $SKIP_REBOOT ]]; then shutdown -r now; else exit; fi
