387 lines
14 KiB
Bash
387 lines
14 KiB
Bash
#!/bin/bash
|
||
# SPDX-Version: 3.0
|
||
# SPDX-CreationInfo: 2025-05-05; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git
|
||
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
|
||
# SPDX-FileCopyrightText: 2024–2025; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||
# SPDX-FileType: SOURCE
|
||
# SPDX-License-Identifier: EUPL-1.2 OR LicenseRef-CCLA-1.0
|
||
# SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework.
|
||
# SPDX-PackageName: CISS.debian.live.builder
|
||
# SPDX-Security-Contact: security@coresecret.eu
|
||
|
||
#######################################
|
||
# Argument Parser
|
||
# Globals:
|
||
# BUILD_LOG
|
||
# DEBUG
|
||
# EARLY_DEBUG
|
||
# ERR_CONTROL_CT
|
||
# ERR_MISS_PWD_F
|
||
# ERR_MISS_PWD_P
|
||
# ERR_OWNS_PWD_F
|
||
# ERR_PASS_LENGH
|
||
# ERR_PASS_PLICY
|
||
# ERR_REIONICE_P
|
||
# ERR_REIO_C_VAL
|
||
# ERR_REIO_P_VAL
|
||
# ERR_RENICE_PRI
|
||
# ERR_RGHT_PWD_F
|
||
# ERR_SPLASH_PNG
|
||
# ERR_UNCRITICAL
|
||
# ERR__SSH__PORT
|
||
# HANDLER_ARCHITECTURE
|
||
# HANDLER_BUILD_DIR
|
||
# HANDLER_CDI
|
||
# HANDLER_DHCP
|
||
# HANDLER_ISO_COUNTER
|
||
# HANDLER_PRIORITY
|
||
# HANDLER_SPLASH
|
||
# HANDLER_SSHPORT
|
||
# HANDLER_SSHPUBKEY
|
||
# HANDLER_STA
|
||
# HASHED_PWD
|
||
# ISO8601
|
||
# REIONICE_CLASS
|
||
# REIONICE_PRIORITY
|
||
# VERSION
|
||
# handler_jumphost
|
||
# Arguments:
|
||
# None
|
||
#######################################
|
||
arg_parser() {
|
||
while [[ $# -gt 0 ]]; do
|
||
declare argument="${1}"
|
||
case "${argument,,}" in
|
||
|
||
-c | --contact)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --contact MUST NOT be followed by an argument.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
shift 1
|
||
;;
|
||
|
||
-h | --help)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --help MUST NOT be followed by an argument.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
shift 1
|
||
;;
|
||
|
||
-v | --version)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --version MUST NOT be followed by an argument.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
shift 1
|
||
;;
|
||
|
||
--architecture)
|
||
if [[ "${2}" == "amd64" || "${2}" == "arm64" ]]; then
|
||
declare -gx HANDLER_ARCHITECTURE="$2"
|
||
shift 2
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --architecture MUST be 'amd64' or 'arm64'.\e[0m\n" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_UNCRITICAL}"
|
||
fi
|
||
;;
|
||
|
||
--build-directory)
|
||
declare -gx HANDLER_BUILD_DIR="${2}"
|
||
declare -gx BUILD_LOG="${HANDLER_BUILD_DIR}/${ISO8601}_build.log"
|
||
shift 2
|
||
;;
|
||
|
||
--cdi)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --cdi MUST NOT be followed by an argument.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
declare -g HANDLER_CDI=true
|
||
shift 1
|
||
;;
|
||
|
||
--change-splash )
|
||
if [[ "${2}" == "club" || "${2}" == "hexagon" ]]; then
|
||
declare -g HANDLER_SPLASH="${2}"
|
||
shift 2
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --change-splash MUST be 'club' or 'hexagon'.\e[0m\n" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_SPLASH_PNG}"
|
||
fi
|
||
;;
|
||
|
||
--control)
|
||
if [[ -n "${2}" && "${2}" =~ ^-?[0-9]+$ && "${2}" -ge 1 && "${2}" -le 65536 ]]; then
|
||
declare -gi HANDLER_ISO_COUNTER="$2"
|
||
shift 2
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --control MUST be an integer between '1' and '65535'.\e[0m\n" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_CONTROL_CT}"
|
||
fi
|
||
;;
|
||
|
||
--debug)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --debug MUST NOT be followed by an argument.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
shift 1
|
||
;;
|
||
|
||
--dhcp-centurion)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --dhcp-centurion MUST NOT be followed by an argument.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
declare -gi HANDLER_DHCP=1
|
||
shift 1
|
||
;;
|
||
|
||
--jump-host)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
declare -i count=0
|
||
shift
|
||
while [[ "${#}" -gt 0 && "${1}" != -* && count -lt 10 ]]; do
|
||
declare -g handler_jumphost+=("$1")
|
||
count=$((count + 1))
|
||
shift
|
||
done
|
||
while [[ "${#}" -gt 0 && "${1}" != -* ]]; do
|
||
shift
|
||
done
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --jump-host MUST contain one or up to ten IPs.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
;;
|
||
|
||
--log-statistics-only)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --log-statistics-only MUST NOT be followed by an argument.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
declare -gi HANDLER_STA=1
|
||
shift 1
|
||
;;
|
||
|
||
--provider-netcup-ipv6)
|
||
if [[ -n "${2}" && "${2}" != -* ]]; then
|
||
declare -i count=0
|
||
declare -g handler_netcup_ipv6=true
|
||
shift
|
||
while [[ "${#}" -gt 0 && "${1}" != -* && count -lt 1 ]]; do
|
||
declare cleaned="${1//[\[\]]/}"
|
||
declare -g handler_netcup_ipv6_array+=("${cleaned}")
|
||
count=$((count + 1))
|
||
shift
|
||
done
|
||
while [[ "${#}" -gt 0 && "${1}" != -* ]]; do
|
||
shift
|
||
done
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --provider-netcup-ipv6 MUST provide one IPv6.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_ARG_MSMTCH}"
|
||
fi
|
||
;;
|
||
|
||
--renice-priority)
|
||
if [[ -n ${2} && ${2} =~ ^-?[0-9]+$ && ${2} -ge -19 && ${2} -le 19 ]]; then
|
||
declare -gi HANDLER_PRIORITY="$2"
|
||
shift 2
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --renice-priority MUST an integer between '-19' and '19'.\e[0m\n" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_RENICE_PRI}"
|
||
fi
|
||
;;
|
||
|
||
--reionice-priority)
|
||
if [[ -z "${2}" ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --reionice-priority no values provided.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_REIONICE_P}"
|
||
else
|
||
if [[ "${2}" =~ ^[1-3]$ ]]; then
|
||
declare -gi REIONICE_CLASS="${2}"
|
||
if [[ -z "${3}" ]]; then
|
||
:
|
||
else
|
||
if [[ "${3}" =~ ^[0-7]$ ]]; then
|
||
declare -gi REIONICE_PRIORITY="${3}"
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --reionice-priority PRIORITY MUST be an integer between '0' and '7'.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_REIO_P_VAL}"
|
||
fi
|
||
fi
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --reionice-priority CLASS MUST be an integer between '1' and '3'.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_REIO_C_VAL}"
|
||
fi
|
||
fi
|
||
if [[ -n ${REIONICE_PRIORITY} ]]; then
|
||
shift 3
|
||
else
|
||
shift 2
|
||
fi
|
||
;;
|
||
|
||
--root-password-file)
|
||
declare pw_file="${2}"
|
||
if [[ -z "${pw_file}" ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --root-password-file missing password file path argument.\e[0m\n" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_MISS_PWD_P}"
|
||
fi
|
||
|
||
if [[ ! -f "${pw_file}" ]]; then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --root-password-file password file '%s' does not exist.\e[0m\n" "${pw_file}" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_MISS_PWD_F}"
|
||
fi
|
||
|
||
declare owner
|
||
owner=$(stat -c '%U:%G' "${pw_file}")
|
||
if [[ "${owner}" != "root:root" ]]; then
|
||
chown root:root "${pw_file}" || {
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --root-password-file failed to set owner root:root on '%s'.\e[0m\n" "${pw_file}" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_OWNS_PWD_F}"
|
||
}
|
||
fi
|
||
|
||
declare perms
|
||
perms=$(stat -c '%a' "${pw_file}")
|
||
if [[ "${perms}" -ne 400 ]]; then
|
||
chmod 400 "${pw_file}" || {
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --root-password-file failed to set permissions 0400 on '%s'.\e[0m\n" "${pw_file}" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_RGHT_PWD_F}"
|
||
}
|
||
fi
|
||
|
||
declare plaintext_pw
|
||
[[ "${EARLY_DEBUG}" == "true" ]] && set +x # No tracing for security reasons
|
||
if ! IFS= read -r plaintext_pw < "${pw_file}"; then
|
||
:
|
||
fi
|
||
[[ "${EARLY_DEBUG}" == "true" ]] && set -x # Turn on tracing again
|
||
|
||
declare pw_length
|
||
pw_length=${#plaintext_pw}
|
||
if (( pw_length < 20 || pw_length > 64 )); then
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --root-password-file password MUST be between 20 and 64 characters (got %d).\e[0m\n" "${pw_length}" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_PASS_LENGH}"
|
||
fi
|
||
|
||
[[ "${EARLY_DEBUG}" == "true" ]] && set +x # No tracing for security reasons
|
||
if [[ "${plaintext_pw}" == *\"* ]]; then
|
||
[[ "${EARLY_DEBUG}" == "true" ]] && set -x # Turn on tracing again
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --root-password-file password MUST NOT contain double quotes (\").\e[0m\n" >&2
|
||
# shellcheck disable=SC2162
|
||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR_PASS_PLICY}"
|
||
fi
|
||
[[ "${EARLY_DEBUG}" == "true" ]] && set -x # Turn on tracing again
|
||
|
||
declare salt
|
||
set +o pipefail
|
||
while :; do
|
||
salt=$(tr -dc 'A-Za-z0-9' </dev/random | head -c 16)
|
||
[[ ${#salt} -eq 16 ]] && break
|
||
done
|
||
set -o pipefail
|
||
|
||
declare hash_temp
|
||
[[ "${EARLY_DEBUG}" == "true" ]] && set +x # No tracing for security reasons
|
||
hash_temp=$(mkpasswd --method=sha-512 --salt="${salt}" --rounds=8388608 "${plaintext_pw}")
|
||
[[ "${EARLY_DEBUG}" == "true" ]] && set -x # Turn on tracing again
|
||
|
||
declare -g HASHED_PWD="${hash_temp}"
|
||
unset hash_temp plaintext_pw
|
||
|
||
sync
|
||
if shred -vfzu -n 5 "${pw_file}" > /dev/null 2>&1; then
|
||
printf "\e[92m✅ Password file '%s': shred -vfzu -n 5 >> done. \e[0m\n" "${pw_file}" > /dev/null 2>&1
|
||
else
|
||
printf "\e[91m❌ Password file '%s': shred -vfzu -n 5 >> NOT successful. \e[0m\n" "${pw_file}" > /dev/null 2>&1
|
||
fi
|
||
sync
|
||
|
||
shift 2
|
||
;;
|
||
|
||
--ssh-port)
|
||
if [[ -n "${2}" && "${2}" =~ ^-?[0-9]+$ && "${2}" -ge 1 && "${2}" -le 65535 ]]; then
|
||
declare -gi HANDLER_SSHPORT="${2}"
|
||
shift 2
|
||
else
|
||
boot_screen_cleaner
|
||
printf "\e[91m❌ Error: --ssh-port MUST be an integer between '1' and '65535'.\e[0m\n" >&2
|
||
read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||
exit "${ERR__SSH__PORT}"
|
||
fi
|
||
;;
|
||
|
||
--ssh-pubkey)
|
||
declare -g HANDLER_SSHPUBKEY="${2}"
|
||
shift 2
|
||
;;
|
||
|
||
*)
|
||
boot_screen_cleaner
|
||
usage
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|