#!/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 guard_sourcing ####################################### # Argument Parser. # Globals: # ARY_HANDLER_JUMPHOST # ARY_HANDLER_NETCUP_IPV6 # 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_SIGNER # VAR_SIGNING_KEY # VAR_SIGNING_KEY_FPR # VAR_SIGNING_KEY_PASS # VAR_SSHFP # VAR_SSHPORT # VAR_SSHPUBKEY # VAR_SUITE # Arguments: # None # Returns: # 0: on success # ERR_ARG_MSMTCH: on failure # ERR_CONTROL_CT: on failure # ERR_MISS_PWD_F: on failure # ERR_MISS_PWD_P: on failure # ERR_NOTABSPATH: on failure # ERR_OWNS_PWD_F: on failure # ERR_PASS_LENGH: on failure # ERR_PASS_PLICY: on failure # ERR_REIONICE_P: on failure # ERR_REIO_C_VAL: on failure # ERR_REIO_P_VAL: on failure # ERR_RENICE_PRI: on failure # ERR_RGHT_PWD_F: on failure # ERR_SPLASH_PNG: on failure # ERR__SSH__PORT: on failure ####################################### 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 # shellcheck disable=SC2034 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_ARG_MSMTCH}" 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}/cdlb_${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 # shellcheck disable=SC2034 declare -g VAR_HANDLER_CDI="true" shift 1 ;; --change-splash ) if [[ "${2}" == "club" || "${2}" == "hexagon" ]]; then # shellcheck disable=SC2034 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 # shellcheck disable=SC2034 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 # shellcheck disable=SC2034 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 # shellcheck disable=SC2034 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 # shellcheck disable=SC2034 declare -gi VAR_HANDLER_STA=1 shift 1 ;; --provider-netcup-ipv6) if [[ -n "${2-}" && "${2}" != -* ]]; then declare -i count=0 # shellcheck disable=SC2034 declare -g VAR_HANDLER_NETCUP_IPV6="true" shift while [[ "${#}" -gt 0 && "${1}" != -* && count -lt 1 ]]; do declare cleaned="${1//[\[\]]/}" # shellcheck disable=SC2034 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 # shellcheck disable=SC2034 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 # shellcheck disable=SC2034 VAR_REIONICE_CLASS="${2}" if [[ -z "${3-}" ]]; then : else if [[ "${3}" =~ ^[0-7]$ ]]; then 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 ### No tracing for security reasons ---------------------------------------------------------------------------------- [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x if ! IFS= read -r plaintext_pw < "${pw_file}"; then : fi ### Turn on tracing again -------------------------------------------------------------------------------------------- [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x declare pw_length pw_length=${#plaintext_pw} if (( pw_length < 42 || pw_length > 64 )); then if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi printf "\e[91m❌ Error: --root-password-file password MUST be between 42 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 ### No tracing for security reasons ---------------------------------------------------------------------------------- [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x if [[ "${plaintext_pw}" == *\"* ]]; then 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 ### Turn on tracing again -------------------------------------------------------------------------------------------- [[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x 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 -fzu -n 5 >> done. \e[0m\n" "${pw_file}" > /dev/null 2>&1 else printf "\e[91m❌ Password file '%s': shred -fzu -n 5 >> NOT successful. \e[0m\n" "${pw_file}" > /dev/null 2>&1 fi sync shift 2 ;; --signing_key=*) # shellcheck disable=SC2034 declare -gx VAR_SIGNER="true" # shellcheck disable=SC2034 declare -gx VAR_SIGNING_KEY="${1#*=}" shift 1 ;; --signing_key_fpr=*) # shellcheck disable=SC2034 declare -gx VAR_SIGNING_KEY_FPR="${1#*=}" shift 1 ;; --signing_key_pass=*) # shellcheck disable=SC2034 declare -gx VAR_SIGNING_KEY_PASS="${1#*=}" shift 1 ;; --ssh-port) if [[ -n "${2-}" && "${2}" =~ ^-?[0-9]+$ && "${2}" -ge 1 && "${2}" -le 65535 ]]; then # shellcheck disable=SC2034 declare -gix 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 ;; --sshfp) # shellcheck disable=SC2034 declare -gx VAR_SSHFP="true" shift 1 ;; --ssh-pubkey) # shellcheck disable=SC2034 declare -gx VAR_SSHPUBKEY="${2}" shift 2 ;; --trixie) # shellcheck disable=SC2034 declare -gx VAR_SUITE="trixie" shift 1 ;; *) if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi usage ;; esac done return 0 } ### Prevents accidental 'unset -f'. # shellcheck disable=SC2034 readonly -f arg_parser # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh