From 9ef535554a868ab5443e1a04fab1b1e8592ae4c7623235fe5544c24ff893e2dc Mon Sep 17 00:00:00 2001 From: "Marc S. Weidner" Date: Wed, 10 Jun 2026 18:57:46 +0100 Subject: [PATCH] V9.14.022.2026.06.10 Signed-off-by: Marc S. Weidner --- README.md | 17 +- centurion.txt | 37 +++ ciss_live_builder.sh | 1 + config.mk.sample | 4 +- docs/CHANGELOG.md | 1 + docs/DOCUMENTATION.md | 14 +- lib/lib_arg_parser.sh | 81 +++++++ lib/lib_cdi.sh | 18 +- lib/lib_logo.sh | 33 +++ lib/lib_sanitizer.sh | 16 +- lib/lib_usage.sh | 12 + makefile | 2 + scripts/usr/local/sbin/9999_cdi_starter.sh | 249 ++++++++++++++++++++- var/global.var.sh | 2 + 14 files changed, 468 insertions(+), 19 deletions(-) create mode 100644 centurion.txt create mode 100644 lib/lib_logo.sh diff --git a/README.md b/README.md index 6b87f79..02cf7a7 100644 --- a/README.md +++ b/README.md @@ -365,10 +365,11 @@ For further details see: **[90-ciss-local.hardened.md](docs/documentation/90-cis ## 2.9. UFW Hardening * **Description**: Defaults to `deny incoming` and (optionally) `deny outgoing`; automatically opens only whitelisted ports. -* **Primordial SSH exception**: `--primordial-ssh ` adds an outgoing-only UFW TCP exception for a bootstrap/recovery SSH - port when the live system's UFW outgoing policy is `deny`. It adds no incoming firewall rule and does not replace - `--ssh-port`. If the requested port already matches an existing outgoing SSH exception, the current hook still emits the - requested labelled rule because this repository has no separate UFW rule deduplication layer. +* **Primordial SSH exception**: `--primordial-url `, `--primordial-key ` and + `--primordial-ssh ` configure the CDI Primordial overlay clone. `--primordial-ssh` also adds an outgoing-only UFW TCP + exception for a bootstrap/recovery SSH port when the live system's UFW outgoing policy is `deny`. It adds no incoming firewall + rule and does not replace `--ssh-port`. If the requested port already matches an existing outgoing SSH exception, the current + hook still emits the requested labelled rule because this repository has no separate UFW rule deduplication layer. * **Rationale**: Implements a default-deny firewall, reducing lateral movement and data exfiltration risks immediately after deployment. @@ -524,7 +525,9 @@ To use **``CISS.debian.live.builder``** as intended, the following baseline is e --signing_key_pass=signing_key_pass.txt \ --signing_key=signing_key.asc \ --ssh-port 4242 \ - --primordial-ssh 2222 \ + --primordial-url https://git.coresecret.dev/ahz/PhysNet.primordial.git \ + --primordial-key id--git.coresecret.dev--PhysNet.primordial_deploy--ed25519--newton--2025-10 \ + --primordial-ssh 42842 \ --ssh-pubkey /dev/shm/cdlb_secrets \ --sshfp \ --trixie @@ -574,7 +577,9 @@ preview it or run it. SSH_PUBKEY=/dev/shm/cdlb_secrets # Optional - PRIMORDIAL_SSH_PORT=2222 + PRIMORDIAL_URL=https://git.coresecret.dev/ahz/PhysNet.primordial.git + PRIMORDIAL_KEY=id--git.coresecret.dev--PhysNet.primordial_deploy--ed25519--newton--2025-10 + PRIMORDIAL_SSH_PORT=42842 PROVIDER_NETCUP_IPV6=2001:cdb::1 # comma-separated; IPv6 in [] is fine JUMP_HOSTS=[2001:db8::1],[2001:db8::2] diff --git a/centurion.txt b/centurion.txt new file mode 100644 index 0000000..5df5b69 --- /dev/null +++ b/centurion.txt @@ -0,0 +1,37 @@ + .:-=++***#####***+==-:. + .-=*#%%@@@@@@@@@@@@@@@@@@@@@%%#*=-. + .=*#@@@@@@@%%%%%%%%%%%%%%%%%%%%%@@@@@@@%*=: + :+#@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@@%*=. + .+#@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@#=: + :*%@@%%%%%%%%%%%%%%%%@@@@@@@@@@@@@%%%%%%%%%%%%%%%%@@@@%%%*= + :*@@%%%%%%%%%%%%%%@@@@@%%#*******#%%@@@@%%%%%%%%%@@%#+-:. + .+@@%%%%%%%%%%%%%%@@%#+-. .-+#%@@%%%%@@#=. + -%@%%%%%%%%%%%%%@@%*-. :-+**####**+-: .-*%@@@*: + +@@%%%%%%%%%%%%%@%+. :+#%@@@@@@@@@@@@@@%#+: .+#: + *@%%%%%%%%%%%%%%@*. =#@@@@%%%%%%%%%%%%%%@@@@#- + *@%%%%%%%%%%%%%%@- -%@@%%%%%%%%%%%%%%%%%%%%%%@@#- + +@%%%%%%%%%%%%%%@- +@@%%%%%%%%%%%%%%%%%%%%%%%%%%@@+-*# + -@%%%%%%%%%%%%%%@+ +@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@@- + %%%%%%%%%%%%%%%%% :@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + -@%%%%%%%%%%%%%%@* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@= + #%%%%%%%%%%%%%%%@= *@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +.%%%%%%%%%%%%%%%%@+ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@%%%%%%%= +-@%%%%%%%%%%%%%%%@* :@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@@@@@@. +=@%%%%%%%%%%%%%%%%%. #@%%%%%%%%%%%%%%%%%%%%%%%%%%%*..:--==+*- +=@%%%%%%%%%%%%%%%%@= :@%%%%%%%%%%%%%%%%%%%%%%%%%%%@#: +=@%%%%%%%%%%%%%%%%%%. +@%%%%%%%%%%%%%%%%%%%%%%%%%%%@@+ +:@%%%%%%%%%%%%%%%%%@# #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@#::::. + %@%%%%%%%%%%%%%%%%%@= :@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@@@@%#: + *%%%%%%%%%%%%%%%%%%- *@%%%%%%%%%%%%%%%@@@@%%%%%%%%%%%%%%%@@@. + :@%%%%%%%%%%%%%%%@- -@%%%%%%%%%%%%@@@%%%%%@@%%%%%%%%%%%%%%%. + *@%%%%%%%%%%%%%@+ .%%%%%%%%%%%@@*=:. .-*@%%%%%%%%%%%%@= + .%%%%%%%%%%%%%%%. .%%%%%%%%%@@*: :%%%%%%%%%%%@+ + =@%%%%%%%%%%%@* -@%%%%%%%@#: =@%%%%%%%%@* + +@%%%%%%%%%%@. *@%%%%%@@+ .@%%%%%%%%%. + *@%%%%%%%%@+ -@%%%%%@%- .@%%%%%%%@= + +@%%%%%@@* :%%%%%@@*. -@%%%%%%%% + =@@@@@#- :%%%%@@%- #%%%%%%%@+ + :#*+: :%%%@@%+ -@@@%%%%%@: + =@@@@#=. :+#@@@@%%. + .*%#*=. .=*%@% + ::. .-+ diff --git a/ciss_live_builder.sh b/ciss_live_builder.sh index 53a8a31..49a5fea 100644 --- a/ciss_live_builder.sh +++ b/ciss_live_builder.sh @@ -111,6 +111,7 @@ source_guard "./var/bash.var.sh" ### CHECK FOR CONTACT, HELP, VERSION STRING, AND XTRACE DEBUG. for arg in "$@"; do case "${arg,,}" in -c|--contact) . ./lib/lib_contact.sh ; contact; exit 0;; esac; done for arg in "$@"; do case "${arg,,}" in -h|--help) . ./lib/lib_usage.sh ; usage ; exit 0;; esac; done +for arg in "$@"; do case "${arg,,}" in -l|--logo) . ./lib/lib_logo.sh ; logo ; exit 0;; esac; done for arg in "$@"; do case "${arg,,}" in -v|--version) . ./lib/lib_version.sh ; version; exit 0;; esac; done for arg in "$@"; do case "${arg,,}" in -d|--debug) . ./meta_sources_debug.sh; debugger "${@}";; esac; done diff --git a/config.mk.sample b/config.mk.sample index 3875af3..45f1ccc 100644 --- a/config.mk.sample +++ b/config.mk.sample @@ -15,7 +15,9 @@ BUILD_DIR ?= DROPBEAR_VERSION ?= ### Optional SOPS release override; empty uses VAR_SOPS_VERSION from var/global.var.sh: SOPS_VERSION ?= -### Optional outgoing bootstrap/recovery SSH port; empty disables the extra UFW rule: +### Optional Primordial CDI overlay settings; all three values are required for automatic overlay bootstrap: +PRIMORDIAL_URL ?= +PRIMORDIAL_KEY ?= PRIMORDIAL_SSH_PORT ?= PROVIDER_NETCUP_IPV6 ?= ROOT_PASSWORD_FILE ?= diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 86c71d6..8b1970f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,7 @@ include_toc: true # 2. Changelog ## V9.14.022.2026.06.10 +* **Added**: [lib_logo.sh](../lib/lib_logo.sh) * **Added**: [9999_cdi_starter.sh](../scripts/usr/local/sbin/9999_cdi_starter.sh) Retrieve rdns for Primordial-Workflow™ * **Added**: [0900_ufw_setup.chroot](../config/hooks/live/0900_ufw_setup.chroot) SSH ufw out exception for Primordial-Workflow™ diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md index ba07909..05e1146 100644 --- a/docs/DOCUMENTATION.md +++ b/docs/DOCUMENTATION.md @@ -98,13 +98,23 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image. Provides statistic only after successful building a CISS.debian.live-ISO. While enabling '--log-statistics-only' the argument '--build-directory' MUST be provided. + --primordial-key + SSH identity filename for the Primordial overlay clone. This MUST be a filename only; the runtime path is derived as + '/root/.ssh/'. + Example fragment: + ./ciss_live_builder.sh --primordial-url https://git.coresecret.dev/ahz/PhysNet.primordial.git \ + --primordial-key id--git.coresecret.dev--PhysNet.primordial_deploy--ed25519--newton--2025-10 \ + --primordial-ssh 42842 + --primordial-ssh Adds one outgoing UFW TCP exception for a bootstrap/recovery SSH port. Outgoing only: no incoming firewall rule is added, and this option does not replace '--ssh-port'. Effective only when the Live System's UFW outgoing policy is 'deny'. Port MUST be a decimal integer between '1' and '65535'. - Example fragment: - ./ciss_live_builder.sh --ssh-port 42842 --primordial-ssh 2222 + + --primordial-url + HTTPS Git repository URL for the Primordial CDI overlay. MUST start with 'https://', include a host and path, and end in + '.git'. The CDI starter converts this URL to an SSH clone URL at runtime. --provider-netcup-ipv6 Activates IPv6 support for Netcup Root Server. One unique IPv6 address MUST be provided in this case and MUST be diff --git a/lib/lib_arg_parser.sh b/lib/lib_arg_parser.sh index bed97ff..680957c 100644 --- a/lib/lib_arg_parser.sh +++ b/lib/lib_arg_parser.sh @@ -38,7 +38,9 @@ guard_sourcing || return "${ERR_GUARD_SRCE}" # VAR_ISO8601 # VAR_LUKS # VAR_LUKS_KEY +# VAR_PRIMORDIAL_KEY # VAR_PRIMORDIAL_SSH_PORT +# VAR_PRIMORDIAL_URL # VAR_REIONICE_CLASS # VAR_REIONICE_PRIORITY # VAR_SIGNER @@ -74,6 +76,9 @@ guard_sourcing || return "${ERR_GUARD_SRCE}" # ERR__SSH__PORT: on failure ####################################### arg_parser() { + declare primordial_key_regex='^[A-Za-z0-9._@%+=:,~-]+$' + declare primordial_url_regex='^https://[A-Za-z0-9.-]+/[A-Za-z0-9._~/%+=:@,-]+\.git$' + while [[ $# -gt 0 ]]; do declare argument="${1}" @@ -288,6 +293,44 @@ arg_parser() { shift 1 ;; + --primordial-key) + declare primordial_key="${2-}" + + if [[ -n "${primordial_key}" && "${primordial_key}" != -* && "${primordial_key}" != "." && "${primordial_key}" != ".." && "${primordial_key}" != */* && "${primordial_key}" =~ ${primordial_key_regex} ]]; then + + # shellcheck disable=SC2034 + declare -gx VAR_PRIMORDIAL_KEY="${primordial_key}" + shift 2 + + else + + if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi + printf "\e[91m❌ Error: --primordial-key MUST be a filename matching '^[A-Za-z0-9._@%%+=:,~-]+$' and MUST NOT be '.', '..', or contain '/'.\e[0m\n" >&2 + read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m' + exit "${ERR_ARG_MSMTCH}" + + fi + ;; + + --primordial-key=*) + declare primordial_key="${1#*=}" + + if [[ -n "${primordial_key}" && "${primordial_key}" != "." && "${primordial_key}" != ".." && "${primordial_key}" != */* && "${primordial_key}" =~ ${primordial_key_regex} ]]; then + + # shellcheck disable=SC2034 + declare -gx VAR_PRIMORDIAL_KEY="${primordial_key}" + shift 1 + + else + + if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi + printf "\e[91m❌ Error: --primordial-key MUST be a filename matching '^[A-Za-z0-9._@%%+=:,~-]+$' and MUST NOT be '.', '..', or contain '/'.\e[0m\n" >&2 + read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m' + exit "${ERR_ARG_MSMTCH}" + + fi + ;; + --primordial-ssh) if [[ -n "${2-}" && "${2}" =~ ^-?[0-9]+$ && "${2}" -ge 1 && "${2}" -le 65535 ]]; then @@ -305,6 +348,44 @@ arg_parser() { fi ;; + --primordial-url) + declare primordial_url="${2-}" + + if [[ -n "${primordial_url}" && "${primordial_url}" != -* && "${primordial_url}" =~ ${primordial_url_regex} ]]; then + + # shellcheck disable=SC2034 + declare -gx VAR_PRIMORDIAL_URL="${primordial_url}" + shift 2 + + else + + if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi + printf "\e[91m❌ Error: --primordial-url MUST be an HTTPS Git URL with non-empty host, non-empty path, and '.git' suffix.\e[0m\n" >&2 + read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m' + exit "${ERR_ARG_MSMTCH}" + + fi + ;; + + --primordial-url=*) + declare primordial_url="${1#*=}" + + if [[ -n "${primordial_url}" && "${primordial_url}" =~ ${primordial_url_regex} ]]; then + + # shellcheck disable=SC2034 + declare -gx VAR_PRIMORDIAL_URL="${primordial_url}" + shift 1 + + else + + if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi + printf "\e[91m❌ Error: --primordial-url MUST be an HTTPS Git URL with non-empty host, non-empty path, and '.git' suffix.\e[0m\n" >&2 + read -p -r $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m' + exit "${ERR_ARG_MSMTCH}" + + fi + ;; + --provider-netcup-ipv6) if [[ -n "${2-}" && "${2}" != -* ]]; then declare -i count=0 diff --git a/lib/lib_cdi.sh b/lib/lib_cdi.sh index 061ccc5..fd2246c 100644 --- a/lib/lib_cdi.sh +++ b/lib/lib_cdi.sh @@ -19,6 +19,9 @@ guard_sourcing || return "${ERR_GUARD_SRCE}" # VAR_HANDLER_BUILD_DIR # VAR_HANDLER_CDI # VAR_KERNEL +# VAR_PRIMORDIAL_KEY +# VAR_PRIMORDIAL_SSH_PORT +# VAR_PRIMORDIAL_URL # VAR_WORKDIR # Arguments: # None @@ -38,7 +41,20 @@ cdi() { fi - install -m 0755 -o root -g root "${VAR_WORKDIR}/scripts/usr/local/sbin/9999_cdi_starter.sh" "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/usr/local/sbin/9999_cdi_starter.sh" + declare var_cdi_starter="${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/usr/local/sbin/9999_cdi_starter.sh" + declare var_primordial_key_q="" var_primordial_ssh_port_q="" var_primordial_url_q="" + + install -m 0755 -o root -g root "${VAR_WORKDIR}/scripts/usr/local/sbin/9999_cdi_starter.sh" "${var_cdi_starter}" + + printf -v var_primordial_key_q '%q' "${VAR_PRIMORDIAL_KEY:-}" + printf -v var_primordial_ssh_port_q '%q' "${VAR_PRIMORDIAL_SSH_PORT:-}" + printf -v var_primordial_url_q '%q' "${VAR_PRIMORDIAL_URL:-}" + + sed -i \ + -e "s|^declare -gx VAR_PRIMORDIAL_KEY=.*$|declare -gx VAR_PRIMORDIAL_KEY=${var_primordial_key_q} # Primordial SSH identity filename.|" \ + -e "s|^declare -gx VAR_PRIMORDIAL_SSH_PORT=.*$|declare -gx VAR_PRIMORDIAL_SSH_PORT=${var_primordial_ssh_port_q} # Primordial SSH port.|" \ + -e "s|^declare -gx VAR_PRIMORDIAL_URL=.*$|declare -gx VAR_PRIMORDIAL_URL=${var_primordial_url_q} # Primordial HTTPS Git URL.|" \ + "${var_cdi_starter}" declare tmp_entry tmp_entry="$(mktemp)" diff --git a/lib/lib_logo.sh b/lib/lib_logo.sh new file mode 100644 index 0000000..0ca9782 --- /dev/null +++ b/lib/lib_logo.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# SPDX-Version: 3.0 +# SPDX-CreationInfo: 2026-06-10; 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-2026; WEIDNER, Marc S.; +# SPDX-FileType: SOURCE +# SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1 +# 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 + +####################################### +# Logo Wrapper CISS.debian.live.builder +# Globals: +# None +# Arguments: +# None +# Returns: +# 0: on success +####################################### +logo() { + clear + printf '\033[95m' + cat centurion.txt + printf '\033[0m\n' + sleep 4 + return 0 +} +### Prevents accidental 'unset -f'. +# shellcheck disable=SC2034 +readonly -f logo +# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh diff --git a/lib/lib_sanitizer.sh b/lib/lib_sanitizer.sh index 185aa67..0b17dec 100644 --- a/lib/lib_sanitizer.sh +++ b/lib/lib_sanitizer.sh @@ -55,7 +55,7 @@ sanitize_arg() { { printf "❌ Control character : '%s'. \n" "${disallowed_ctrl}" printf "❌ in argument : '%s'. \n" "${input}" - printf "❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" - + space' \n" + printf "❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" @ %% , ~ - + space' \n" printf "\n" } >> "${LOG_ERROR}" @@ -63,7 +63,7 @@ sanitize_arg() { printf "\e[91m❌ Control character : '%s'. \e[0m\n" "${disallowed_ctrl}" >&2 printf "\e[91m❌ in argument : '%s'. \e[0m\n" "${input}" >&2 - printf "\e[91m❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" - + space' \e[0m\n" >&2 + printf "\e[91m❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" @ %% , ~ - + space' \e[0m\n" >&2 # shellcheck disable=SC2162 read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m' @@ -73,8 +73,8 @@ sanitize_arg() { fi ### Step 2: Define allowed characters: - ### letters, digits, dot, underscore, slash, equals, [, ], colon, double-quote, hyphen, space. - declare allowed='a-zA-Z0-9._/=\[\]:"\-+ ' + ### letters, digits, dot, underscore, slash, equals, [, ], colon, double-quote, @, %, comma, tilde, hyphen, plus, space. + declare allowed='a-zA-Z0-9._/=\[\]:"@%,~\-+ ' declare disallowed disallowed=$(printf '%s' "${input}" | tr -d "${allowed}") @@ -82,7 +82,7 @@ sanitize_arg() { { printf "❌ Invalid character : '%s'. \n" "${disallowed//?/& }" printf "❌ in argument : '%s'. \n" "${input}" - printf "❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" - + space' \n" + printf "❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" @ %% , ~ - + space' \n" printf "\n" } >> "${LOG_ERROR}" @@ -90,7 +90,7 @@ sanitize_arg() { printf "\e[91m❌ Invalid character : '%s'. \e[0m\n" "${disallowed//?/& }" >&2 printf "\e[91m❌ in argument : '%s'. \e[0m\n" "${input}" >&2 - printf "\e[91m❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" - + space' \e[0m\n" >&2 + printf "\e[91m❌ Allowed Characters : 'a-z A-Z 0-9 . _ / = [ ] : \" @ %% , ~ - + space' \e[0m\n" >&2 # shellcheck disable=SC2162 read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m' @@ -119,8 +119,8 @@ readonly -f sanitize_arg sanitize_string() { declare input="$1" ### Define allowed characters: - ### letters, digits, dot, underscore, slash, equals, [, ], colon, double-quote, hyphen, space. - declare allowed='a-zA-Z0-9._/=\[\]:"\-+ ' + ### letters, digits, dot, underscore, slash, equals, [, ], colon, double-quote, @, %, comma, tilde, hyphen, plus, space. + declare allowed='a-zA-Z0-9._/=\[\]:"@%,~\-+ ' printf '%s' "${input}" | tr -cd "${allowed}" } ### Prevents accidental 'unset -f'. diff --git a/lib/lib_usage.sh b/lib/lib_usage.sh index 3cdd26c..bef358a 100644 --- a/lib/lib_usage.sh +++ b/lib/lib_usage.sh @@ -120,12 +120,24 @@ usage() { echo " Provides statistic only after successful building a CISS.debian.live-ISO. While enabling '--log-statistics-only'" echo " the argument '--build-directory' MUST be provided." echo + echo -e "\e[97m --primordial-key \e[0m" + echo " SSH identity filename for the Primordial overlay clone. This MUST be a filename only; the runtime path is" + echo " derived as '/root/.ssh/'." + echo " Example fragment:" + echo " ./ciss_live_builder.sh --primordial-url https://git.coresecret.dev/ahz/PhysNet.primordial.git \\" + echo " --primordial-key id--git.coresecret.dev--PhysNet.primordial_deploy--ed25519--newton--2025-10 \\" + echo " --primordial-ssh 42842" + echo echo -e "\e[97m --primordial-ssh \e[0m" echo " Adds one outgoing UFW TCP exception for a bootstrap SSH port." echo " Outgoing only: no incoming firewall rule is added, and this option does not replace '--ssh-port'." echo " Effective only when the Live System's UFW outgoing policy is 'deny'." echo " Port MUST be a decimal integer between '1' and '65535'." echo + echo -e "\e[97m --primordial-url \e[0m" + echo " HTTPS Git repository URL for the Primordial CDI overlay. MUST start with 'https://', include a host and" + echo " path, and end in '.git'. The CDI starter converts this URL to an SSH clone URL at runtime." + echo echo -e "\e[97m --provider-netcup-ipv6 \e[0m" echo " Activates IPv6 support for Netcup Root Server. One unique IPv6 address MUST be provided in this case and MUST be" echo " encapsulated with [], e.g., [1234::abcd]." diff --git a/makefile b/makefile index 1ecda49..25b14da 100644 --- a/makefile +++ b/makefile @@ -63,6 +63,8 @@ define COMPOSE_AND [[ -n '$(FLAG_DEBUG)' ]] && cmd+=( --debug ) [[ -n '$(FLAG_DHCP_CENTURION)' ]] && cmd+=( --dhcp-centurion ) [[ -n '$(FLAG_TRIXIE)' ]] && cmd+=( --trixie ) + [[ -n '$(PRIMORDIAL_URL)' ]] && cmd+=( --primordial-url '$(PRIMORDIAL_URL)' ) + [[ -n '$(PRIMORDIAL_KEY)' ]] && cmd+=( --primordial-key '$(PRIMORDIAL_KEY)' ) [[ -n '$(PRIMORDIAL_SSH_PORT)' ]] && cmd+=( --primordial-ssh '$(PRIMORDIAL_SSH_PORT)' ) [[ -n '$(PROVIDER_NETCUP_IPV6)' ]] && cmd+=( --provider-netcup-ipv6 '$(PROVIDER_NETCUP_IPV6)' ) [[ -n '$(RENICE)' ]] && cmd+=( --renice-priority '$(RENICE)' ) diff --git a/scripts/usr/local/sbin/9999_cdi_starter.sh b/scripts/usr/local/sbin/9999_cdi_starter.sh index 280f23f..9f2dbcb 100644 --- a/scripts/usr/local/sbin/9999_cdi_starter.sh +++ b/scripts/usr/local/sbin/9999_cdi_starter.sh @@ -15,6 +15,10 @@ umask 0077 declare -gx VAR_RDNS_DOMAIN="" # Forward-confirmed reverse DNS domain. declare -gx VAR_RDNS_IPV4="" # IPv4 address used for RDNS verification. +declare -gx VAR_RDNS_NORMALIZED="" # RDNS domain normalized for Primordial branch names. +declare -gx VAR_PRIMORDIAL_KEY="" # Primordial SSH identity filename. +declare -gx VAR_PRIMORDIAL_SSH_PORT="" # Primordial SSH port. +declare -gx VAR_PRIMORDIAL_URL="" # Primordial HTTPS Git URL. declare -grx VAR_SEMAPHORE="/root/cdi.ciss" # Semaphore to appear. declare -girx VAR_TIMEOUT=3600 # Semaphore timer in seconds. @@ -324,6 +328,240 @@ retrieve_rdns() { # shellcheck disable=SC2034 readonly -f retrieve_rdns +####################################### +# Normalize a DNS domain into a Primordial branch name. +# Globals: +# None +# Arguments: +# $1: DNS domain name +# Returns: +# 0: on success +# 1: on invalid DNS domain +####################################### +normalize_rdns_domain() { + ### Declare Arrays, HashMaps, and Variables. + declare var_domain="${1:-}" + + var_domain="${var_domain%.}" + var_domain="${var_domain,,}" + + # shellcheck disable=SC2310 + is_dns_name "${var_domain}" || return 1 + + printf '%s\n' "${var_domain//./_}" + + return 0 +} +### Prevents accidental 'unset -f'. +# shellcheck disable=SC2034 +readonly -f normalize_rdns_domain + +####################################### +# Convert an HTTPS Git URL into the SSH URL used for Primordial clone. +# Globals: +# None +# Arguments: +# $1: HTTPS Git URL +# $2: SSH port +# Returns: +# 0: on success +# 1: on invalid URL or port +####################################### +derive_ssh_git_url() { + ### Declare Arrays, HashMaps, and Variables. + declare -r var_https_url="${1:-}" + declare -r var_ssh_port="${2:-}" + declare var_host="" var_path="" + + if [[ ! "${var_https_url}" =~ ^https://([A-Za-z0-9.-]+)/([A-Za-z0-9._~/%+=:@,-]+\.git)$ ]]; then + + return 1 + + fi + + var_host="${BASH_REMATCH[1]}" + var_path="${BASH_REMATCH[2]}" + + if [[ -z "${var_host}" || -z "${var_path}" || ! "${var_ssh_port}" =~ ^[0-9]+$ ]] \ + || ((10#${var_ssh_port} < 1 || 10#${var_ssh_port} > 65535)); then + + return 1 + + fi + + printf 'ssh://git@%s:%s/%s\n' "${var_host}" "${var_ssh_port}" "${var_path}" + + return 0 +} +### Prevents accidental 'unset -f'. +# shellcheck disable=SC2034 +readonly -f derive_ssh_git_url + +####################################### +# Apply the Primordial overlay and create the CDI semaphore only on success. +# Globals: +# VAR_PRIMORDIAL_KEY +# VAR_PRIMORDIAL_SSH_PORT +# VAR_PRIMORDIAL_URL +# VAR_RDNS_DOMAIN +# VAR_RDNS_NORMALIZED +# VAR_SEMAPHORE +# Arguments: +# $1: module log file +# $2: CISS.debian.installer directory +# Returns: +# 0: on success or optional skip +# 1: on failed configured Primordial overlay +####################################### +apply_primordial_overlay() { + ### Declare Arrays, HashMaps, and Variables. + declare -r var_log="${1:-}" + declare -r var_repo_dir="${2:-}" + declare -r var_overlay_dir="/root/git/overlay" + declare var_identity="" var_ssh_url="" var_rdns_normalized="" + + if [[ -z "${VAR_PRIMORDIAL_URL}" && -z "${VAR_PRIMORDIAL_KEY}" && -z "${VAR_PRIMORDIAL_SSH_PORT}" ]]; then + + logger -t cdi-watcher "Primordial overlay not configured; continuing with existing semaphore polling." + printf "Command: [apply_primordial_overlay] skipped; Primordial overlay not configured.\n" >> "${var_log}" + return 0 + + fi + + if ! rm -f -- "${VAR_SEMAPHORE}"; then + + logger -t cdi-watcher "Failed to remove existing CDI semaphore; aborting CDI autostart." + printf "Command: [rm -f -- %s] failed; aborting CDI autostart.\n" "${VAR_SEMAPHORE}" >> "${var_log}" + return 1 + + fi + + if [[ -z "${VAR_PRIMORDIAL_URL}" || -z "${VAR_PRIMORDIAL_KEY}" || -z "${VAR_PRIMORDIAL_SSH_PORT}" || -z "${VAR_RDNS_DOMAIN}" ]]; then + + logger -t cdi-watcher "Primordial overlay configuration incomplete; aborting CDI autostart." + printf "Command: [apply_primordial_overlay] failed; Primordial URL, key, SSH port, and RDNS domain are required.\n" >> "${var_log}" + return 1 + + fi + + # shellcheck disable=SC2310 + if ! var_rdns_normalized="$(normalize_rdns_domain "${VAR_RDNS_DOMAIN}")"; then + + logger -t cdi-watcher "Primordial overlay RDNS branch derivation failed; aborting CDI autostart." + printf "Command: [normalize_rdns_domain %s] failed; aborting CDI autostart.\n" "${VAR_RDNS_DOMAIN}" >> "${var_log}" + return 1 + + fi + + declare -gx VAR_RDNS_NORMALIZED="${var_rdns_normalized}" + + # shellcheck disable=SC2310 + if ! var_ssh_url="$(derive_ssh_git_url "${VAR_PRIMORDIAL_URL}" "${VAR_PRIMORDIAL_SSH_PORT}")"; then + + logger -t cdi-watcher "Primordial HTTPS Git URL conversion failed; aborting CDI autostart." + printf "Command: [derive_ssh_git_url] failed for configured Primordial URL; aborting CDI autostart.\n" >> "${var_log}" + return 1 + + fi + + var_identity="/root/.ssh/${VAR_PRIMORDIAL_KEY}" + + if [[ ! -e "${var_identity}" ]]; then + + logger -t cdi-watcher "Primordial SSH identity file is missing; aborting CDI autostart." + printf "Command: [test -e /root/.ssh/] failed; aborting CDI autostart.\n" >> "${var_log}" + return 1 + + fi + + if [[ ! -f "${var_identity}" ]]; then + + logger -t cdi-watcher "Primordial SSH identity path is not a regular file; aborting CDI autostart." + printf "Command: [test -f /root/.ssh/] failed; aborting CDI autostart.\n" >> "${var_log}" + return 1 + + fi + + if [[ ! -r "${var_identity}" ]]; then + + logger -t cdi-watcher "Primordial SSH identity file is not readable by root; aborting CDI autostart." + printf "Command: [test -r /root/.ssh/] failed; aborting CDI autostart.\n" >> "${var_log}" + return 1 + + fi + + if ! rm -rf -- "${var_overlay_dir}"; then + + logger -t cdi-watcher "Failed to remove existing Primordial overlay directory; aborting CDI autostart." + printf "Command: [rm -rf -- %s] failed; aborting CDI autostart.\n" "${var_overlay_dir}" >> "${var_log}" + return 1 + + fi + + if ! GIT_SSH_COMMAND="ssh -i ${var_identity} -p ${VAR_PRIMORDIAL_SSH_PORT}" \ + git clone --branch "${VAR_RDNS_NORMALIZED}" "${var_ssh_url}" "${var_overlay_dir}"; then + + logger -t cdi-watcher "Primordial overlay clone failed; aborting CDI autostart." + printf "Command: [git clone --branch %s %s] failed; aborting CDI autostart.\n" \ + "${VAR_RDNS_NORMALIZED}" "${var_overlay_dir}" >> "${var_log}" + rm -rf -- "${var_overlay_dir}" || true + return 1 + + fi + + if [[ ! -d "${var_overlay_dir}/.preseed" ]]; then + + logger -t cdi-watcher "Primordial overlay .preseed directory is missing; aborting CDI autostart." + printf "Command: [test -d %s/.preseed] failed; aborting CDI autostart.\n" "${var_overlay_dir}" >> "${var_log}" + return 1 + + fi + + if [[ ! -d "${var_overlay_dir}/includes" ]]; then + + logger -t cdi-watcher "Primordial overlay includes directory is missing; aborting CDI autostart." + printf "Command: [test -d %s/includes] failed; aborting CDI autostart.\n" "${var_overlay_dir}" >> "${var_log}" + return 1 + + fi + + install -d -m 0700 "${var_repo_dir}/.preseed" "${var_repo_dir}/includes" + + if ! rsync -av "${var_overlay_dir}/.preseed/" "${var_repo_dir}/.preseed/"; then + + logger -t cdi-watcher "Primordial overlay .preseed rsync failed; aborting CDI autostart." + printf "Command: [rsync -av %s/.preseed/ %s/.preseed/] failed; aborting CDI autostart.\n" \ + "${var_overlay_dir}" "${var_repo_dir}" >> "${var_log}" + return 1 + + fi + + if ! rsync -av "${var_overlay_dir}/includes/" "${var_repo_dir}/includes"; then + + logger -t cdi-watcher "Primordial overlay includes rsync failed; aborting CDI autostart." + printf "Command: [rsync -av %s/includes/ %s/includes] failed; aborting CDI autostart.\n" \ + "${var_overlay_dir}" "${var_repo_dir}" >> "${var_log}" + return 1 + + fi + + if ! install -m 0600 /dev/null "${VAR_SEMAPHORE}"; then + + logger -t cdi-watcher "Primordial overlay applied but semaphore creation failed; aborting CDI autostart." + printf "Command: [install -m 0600 /dev/null %s] failed; aborting CDI autostart.\n" "${VAR_SEMAPHORE}" >> "${var_log}" + return 1 + + fi + + logger -t cdi-watcher "Primordial overlay applied for branch ${VAR_RDNS_NORMALIZED}; CDI semaphore created." + printf "Command: [apply_primordial_overlay] executed for branch [%s].\n" "${VAR_RDNS_NORMALIZED}" >> "${var_log}" + + return 0 +} +### Prevents accidental 'unset -f'. +# shellcheck disable=SC2034 +readonly -f apply_primordial_overlay + ####################################### # Wrapper for loading CISS hardened Kernel Parameters. # Globals: @@ -363,7 +601,6 @@ main() { var_log="/root/.ciss/cdi/log/9999-cdi-starter_$(date +"%Y-%m-%d_%H-%M-%S").log" touch "${var_log}" - printf "CISS.debian.live.builder V9.14.022.2026.06.10 calling CISS.debian.installer ... \n" >> "${var_log}" ### Sleep a moment to settle boot artifacts. @@ -419,6 +656,16 @@ main() { cd "${var_repo_dir}" printf "Command: [git clone %s %s] executed.\n" "${var_repo_url}" "${var_repo_dir}" >> "${var_log}" + ### Apply Primordial overlay before allowing CDI autostart. + # shellcheck disable=SC2310 + if ! apply_primordial_overlay "${var_log}" "${var_repo_dir}"; then + + logger -t cdi-watcher "Primordial overlay failed; CDI autostart aborted before semaphore polling." + printf "Command: [apply_primordial_overlay] failed; CDI autostart aborted before semaphore polling.\n" >> "${var_log}" + exit 0 + + fi + ### Poll up to VAR_TIMEOUT seconds for the semaphore to appear and be mode 0600. for ((i=0; i