Files
soteria/scripts/setup_wizard.sh
John Lancaster 4fbf229c0d more file checks
2025-12-30 00:25:38 -06:00

193 lines
5.8 KiB
Bash
Executable File

#!/bin/bash
set -e
# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${YELLOW}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# -----------------------------------------------------------------------------
# Input Framework
# -----------------------------------------------------------------------------
# Ensure we have a tty for input
if [ ! -e /dev/tty ]; then
echo "Error: Script must be run in an interactive terminal (cannot find /dev/tty)." >&2
exit 1
fi
# Function to prompt for user input
# Usage: get_input "VARIABLE_NAME" "Prompt Text" "Default Value" "is_secret(true/false)"
get_input() {
local var_name="$1"
local prompt_text="$2"
local default_value="$3"
local is_secret="$4"
local input_val=""
local prompt_full="${GREEN}${prompt_text}${NC}"
if [ -n "$default_value" ]; then
prompt_full+=" [$default_value]"
fi
prompt_full+=": "
while true; do
# Print prompt to stderr so it shows up even if stdout is redirected
if [ "$is_secret" == "true" ]; then
echo -ne "$prompt_full" >&2
read -s input_val < /dev/tty
echo "" >&2 # Newline after secret input
else
echo -ne "$prompt_full" >&2
read input_val < /dev/tty
fi
# Use default if input is empty
if [ -z "$input_val" ] && [ -n "$default_value" ]; then
input_val="$default_value"
fi
# Validation: Require input if no default exists
if [ -z "$input_val" ]; then
echo -e "${RED}Error: This value is required.${NC}" >&2
else
break
fi
done
# Set the variable dynamically in the parent scope
printf -v "$var_name" "%s" "$input_val"
export "$var_name=$input_val"
}
# Function to confirm collected inputs
# Usage: confirm_inputs "VAR1" "VAR2" "VAR3" ...
confirm_inputs() {
echo "" >&2
echo -e "${GREEN}=== Configuration Summary ===${NC}" >&2
for var in "$@"; do
local val="${!var}"
# Mask secrets in summary if needed, or just show length
# For now, just printing value.
# To improve: pass a list of secret vars to mask them.
echo -e "${YELLOW}$var:${NC} $val" >&2
done
echo "" >&2
get_input "CONFIRM" "Is this correct? (y/n)" "y" "false"
if [[ "${CONFIRM,,}" != "y" ]]; then
echo -e "${RED}Aborted by user.${NC}" >&2
exit 1
fi
}
install_unit() {
local template_url=$1
local filename=$(basename "$template_url")
local dest_path=/etc/systemd/system/"$filename"
if [ -e "$dest_path" ]; then
get_input "CONFIRM_OVERWRITE" "Overwrite $dest_path? (y/n)" "y" "false"
if [[ "${CONFIRM_OVERWRITE,,}" != "y" ]]; then
echo "Skipping overwrite of ${dest_path}."
return
fi
fi
log_info "Installing $filename..."
curl -sL $template_url | envsubst > "$dest_path"
log_success "$filename installed to $dest_path"
}
# -----------------------------------------------------------------------------
# Script Logic
# -----------------------------------------------------------------------------
echo "Starting Interactive Setup..."
echo "-----------------------------"
# Verify required external binaries
if ! command -v step >/dev/null 2>&1; then
# Prompt the user to install the step CLI
get_input "INSTALL_STEP" "The 'step' CLI was not found. Install now? (y/n)" "y" "false"
if [[ "${INSTALL_STEP,,}" == "y" ]]; then
apt-get update && apt-get install -y --no-install-recommends curl vim gpg ca-certificates
curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg -o /etc/apt/trusted.gpg.d/smallstep.asc && \
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/smallstep.asc] https://packages.smallstep.com/stable/debian debs main' \
| tee /etc/apt/sources.list.d/smallstep.list
apt-get update && apt-get -y install step-cli step-ca
else
log_error "Cannot continue without 'step'. Aborting." >&2
exit 1
fi
else
log_success "Step CA installed\n"
fi
# 1. Collect Inputs
# Example:
get_input "CERT_DIR" "Enter directory for certificates" "/var/lib/tls" "false"
get_input "CERT_FILENAME" "Name for cert file" "cert.pem" "false"
get_input "KEY_FILENAME" "Name for private key" "key.pem" "false"
# These need to get set so that they get filled into the service correctly.
export CERT_LOCATION=$(readlink -f ${CERT_DIR}/$CERT_FILENAME)
export KEY_LOCATION=$(readlink -f ${CERT_DIR}/$KEY_FILENAME)
# 2. Confirm
confirm_inputs "CERT_LOCATION" "KEY_LOCATION"
if [ ! -e "$CERT_DIR" ]; then
(umask 077; mkdir -p "${CERT_DIR}")
log_info "Created ${CERT_DIR}"
fi
if [ ! -e "${CERT_DIR}/root_ca.crt" ]; then
step ca root "${CERT_DIR}/root_ca.crt"
fi
if [ ! -f "$CERT_LOCATION" ] || [ ! -f "$KEY_LOCATION" ]; then
hostname=$(hostname -s)
ip_address=$(ip -4 addr show dev eth0 | awk '/inet /{print $2}' | cut -d/ -f1)
step ca certificate "$hostname" \
"${CERT_DIR}/cert.pem" "${CERT_DIR}/key.pem" \
--san "$hostname" \
--san "$hostname.john-stream.com" \
--san "$ip_address" \
--provisioner admin
fi
# 3. Configure
SERVICE_FILE="cert-renewer.service"
TIMER_FILE="cert-renewer.timer"
REPO_URL_BASE=https://gitea.john-stream.com/john/soteria/raw/branch/main/
SERVICE_TEMPLATE_URL="${REPO_URL_BASE}systemd/${SERVICE_FILE}"
TIMER_TEMPLATE_URL="${REPO_URL_BASE}systemd/${TIMER_FILE}"
# 3. Execute
# echo "Configuring $HOST_NAME..."
install_unit ${SERVICE_TEMPLATE_URL}
install_unit ${TIMER_TEMPLATE_URL}
systemctl daemon-reload
systemctl enable --now "${TIMER_FILE}" "${SERVICE_FILE}"
systemctl list-unit-files $SERVICE_FILE $TIMER_FILE