#!/bin/bash # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-05-05; WEIDNER, Marc S.; # 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.; # 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: # ARY_HANDLER_JUMPHOST # ARY_HANDLER_NETCUP_IPV6 # ERR_ARG_MSMTCH # ERR_CONTROL_CT # ERR_MISS_PWD_F # ERR_MISS_PWD_P # ERR_NOTABSPATH # 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 # VAR_ARCHITECTURE # VAR_BUILD_LOG # VAR_EARLY_DEBUG # VAR_HANDLER_AUTOBUILD # VAR_HANDLER_BUILD_DIR # VAR_HANDLER_CDI # VAR_HANDLER_DHCP # VAR_HANDLER_ISO_COUNTER # VAR_HANDLER_NETCUP_IPV6 # VAR_HANDLER_PRIORITY # VAR_HANDLER_SPLASH # VAR_HANDLER_STA # VAR_HASHED_PWD # VAR_ISO8601 # VAR_REIONICE_CLASS # VAR_REIONICE_PRIORITY # VAR_SSHPORT # VAR_SSHPUBKEY # Arguments: # None ####################################### arg_parser() { while [[ $# -gt 0 ]]; do declare argument="${1}" case "${argument,,}" in -a=* | --autobuild=*) shift 1 ;; -c | --contact) if [[ -n "${2}" && "${2}" != -* ]]; then if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_ARCHITECTURE="${2}" shift 2 else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_HANDLER_BUILD_DIR="${2}" if [[ ! "${VAR_HANDLER_BUILD_DIR}" =~ ^/ ]]; then if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi printf "\e[91m❌ Error: --build-directory MUST be an absolute path. Got: '%s'\n" "${VAR_HANDLER_BUILD_DIR}" >&2 exit "${ERR_NOTABSPATH}" fi declare -gx VAR_BUILD_LOG="${VAR_HANDLER_BUILD_DIR}/${VAR_ISO8601}_build.log" shift 2 ;; --cdi) if [[ -n "${2}" && "${2}" != -* ]]; then if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_HANDLER_CDI=true shift 1 ;; --change-splash ) if [[ "${2}" == "club" || "${2}" == "hexagon" ]]; then declare -g VAR_HANDLER_SPLASH="${2}" shift 2 else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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}" ]]; then declare -g VAR_HANDLER_ISO_COUNTER="${2}" shift 2 else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi printf "\e[91m❌ Error: --control MUST be provided with a Parameter.\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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_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 ARY_HANDLER_JUMPHOST+=("$1") count=$((count + 1)) shift done while [[ "${#}" -gt 0 && "${1}" != -* ]]; do shift done else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_HANDLER_STA=1 shift 1 ;; --provider-netcup-ipv6) if [[ -n "${2}" && "${2}" != -* ]]; then declare -i count=0 declare -g VAR_HANDLER_NETCUP_IPV6=true shift while [[ "${#}" -gt 0 && "${1}" != -* && count -lt 1 ]]; do declare cleaned="${1//[\[\]]/}" declare -g ARY_HANDLER_NETCUP_IPV6+=("${cleaned}") count=$((count + 1)) shift done while [[ "${#}" -gt 0 && "${1}" != -* ]]; do shift done else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_HANDLER_PRIORITY="$2" shift 2 else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_REIONICE_CLASS="${2}" if [[ -z "${3}" ]]; then : else if [[ "${3}" =~ ^[0-7]$ ]]; then declare -gi VAR_REIONICE_PRIORITY="${3}" else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 ${VAR_REIONICE_PRIORITY} ]]; then shift 3 else shift 2 fi ;; --root-password-file) declare pw_file="${2}" if [[ -z "${pw_file}" ]]; then if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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}" || { if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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}" || { if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x # No tracing for security reasons if ! IFS= read -r plaintext_pw < "${pw_file}"; then : fi [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x # Turn on tracing again declare pw_length pw_length=${#plaintext_pw} if (( pw_length < 20 || pw_length > 64 )); then if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x # No tracing for security reasons if [[ "${plaintext_pw}" == *\"* ]]; then [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x # Turn on tracing again if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x # Turn on tracing again declare salt set +o pipefail while :; do salt=$(tr -dc 'A-Za-z0-9' /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 VAR_SSHPORT="${2}" shift 2 else if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi 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 VAR_SSHPUBKEY="${2}" shift 2 ;; *) if ! $VAR_HANDLER_AUTOBUILD; then boot_screen_cleaner; fi usage ;; esac done } # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh