Files
CISS.debian.live.builder/lib/lib_arg_parser.sh
Marc S. Weidner e3dc26858d
All checks were successful
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Successful in 58s
V8.13.404.2025.11.10
Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
2025-11-10 18:59:00 +01:00

505 lines
18 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
guard_sourcing || return "${ERR_GUARD_SRCE}"
#######################################
# Argument Parser.
# Globals:
# ARY_HANDLER_JUMPHOST
# ARY_HANDLER_NETCUP_IPV6
# VAR_AGE
# VAR_AGE_KEY
# 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_LUKS
# VAR_LUKS_KEY
# 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
;;
--key_age=*)
# shellcheck disable=SC2034
declare -gx VAR_AGE="true"
# shellcheck disable=SC2034
declare -gx VAR_AGE_KEY="${1#*=}"
shift 1
;;
--key_luks=*)
# shellcheck disable=SC2034
declare -gx VAR_LUKS="true"
# shellcheck disable=SC2034
declare -gx VAR_LUKS_KEY="${1#*=}"
shift 1
;;
--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 0400 "${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/random | head -c 16)
[[ ${#salt} -eq 16 ]] && break
done
set -o pipefail
declare hash_temp
### No tracing for security reasons ----------------------------------------------------------------------------------
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x
hash_temp=$(mkpasswd --method=sha-512 --salt="${salt}" --rounds=8388608 "${plaintext_pw}")
### Turn on tracing again --------------------------------------------------------------------------------------------
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x
# shellcheck disable=SC2034
declare -g VAR_HASHED_PWD="${hash_temp}"
unset hash_temp plaintext_pw
sync
if shred -fzu -n 5 "${pw_file}" > /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