Files
CISS.debian.live.builder/config/includes.chroot/usr/lib/live/boot/0024-ciss-crypt-squash
T
msw 0a091fb9a6
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Has been cancelled
V9.14.018.2026.06.07
Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
2026-06-07 10:54:44 +01:00

643 lines
17 KiB
Bash

#!/bin/sh
# bashsupport disable=BP5007
# shellcheck disable=SC2249
# shellcheck shell=sh
# SPDX-Version: 3.0
# SPDX-CreationInfo: 2025-11-12; 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: 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
# Module summary:
# - Read CISS boot parameters for the encrypted root path and live ISO label.
# - Mount the live medium read-only and locate the encrypted SquashFS container.
# - Attach the encrypted container through a read-only loop device.
# - Accept a LUKS passphrase from the local console or remotely unlock FIFO.
# - Open the decrypted root mapper and expose the handoff state for later live-boot overlay processing.
_SAVED_SET_OPTS="$(set +o)"
set -eu
#######################################
# Ensure the minimal device nodes required by this early boot script exist.
# Globals:
# None
# Arguments:
# None
# Returns:
# 0: always, device-node setup is best-effort only
#######################################
ensure_minimal_dev_nodes() {
mknod_cmd=""
busybox_cmd=""
[ -d /dev ] || mkdir -p /dev || return 0
if [ -c /dev/null ] && [ -c /dev/console ]; then
return 0
fi
mknod_cmd="$(command -v mknod 2>&- || printf '')"
if [ -z "${mknod_cmd}" ]; then
busybox_cmd="$(command -v busybox 2>&- || printf '')"
fi
if [ ! -c /dev/null ]; then
rm -f /dev/null || true
if [ -n "${mknod_cmd}" ]; then
"${mknod_cmd}" -m 666 /dev/null c 1 3 || true
elif [ -n "${busybox_cmd}" ]; then
"${busybox_cmd}" mknod -m 666 /dev/null c 1 3 || true
fi
fi
if [ ! -c /dev/console ]; then
rm -f /dev/console || true
if [ -n "${mknod_cmd}" ]; then
"${mknod_cmd}" -m 600 /dev/console c 5 1 || true
elif [ -n "${busybox_cmd}" ]; then
"${busybox_cmd}" mknod -m 600 /dev/console c 5 1 || true
fi
fi
return 0
}
#######################################
# Console logging helper that does not assume /dev/console is always present.
# Globals:
# None
# Arguments:
# 1: printf format
# *: printf arguments
# Returns:
# 0: always, logging failure is not fatal
#######################################
console_printf() {
console_format="$1"
shift
if [ -c /dev/console ]; then
# shellcheck disable=SC2059
printf "${console_format}" "$@" > /dev/console || :
elif [ -e /proc/1/fd/1 ]; then
# shellcheck disable=SC2059
printf "${console_format}" "$@" > /proc/1/fd/1 || :
fi
return 0
}
#######################################
# Clear the current console line without making cleanup fatal.
# Globals:
# None
# Arguments:
# None
# Returns:
# 0: always, console cleanup failure is not fatal
#######################################
console_clear_line() {
console_printf '\r\033[K'
return 0
}
#######################################
# Wait for an explicit local-console unlock activation.
# Globals:
# None
# Arguments:
# None
# Returns:
# 0: local console activation was requested
# 1: console unavailable or activation read failed
#######################################
wait_for_local_unlock_activation() {
LOCAL_UNLOCK_SAVED_STTY=""
LOCAL_UNLOCK_READ_STATUS=0
ensure_minimal_dev_nodes
[ -c /dev/console ] || return 1
exec 9<>/dev/console || return 1
LOCAL_UNLOCK_SAVED_STTY=$(stty -g <&9 2>&- || printf '')
trap 'if [ -n "${LOCAL_UNLOCK_SAVED_STTY}" ]; then stty "${LOCAL_UNLOCK_SAVED_STTY}" <&9 2>&- || :; fi; printf "\r\033[K" >&9 2>&- || :; exec 9>&-; exit 143' TERM INT HUP
if [ -n "${LOCAL_UNLOCK_SAVED_STTY}" ]; then
stty -echo <&9 2>&- || :
fi
printf '\e[93m[INFO] Press Enter for local unlock: \e[0m' >&9 || :
IFS= read -r _ <&9
LOCAL_UNLOCK_READ_STATUS="$?"
if [ -n "${LOCAL_UNLOCK_SAVED_STTY}" ]; then
stty "${LOCAL_UNLOCK_SAVED_STTY}" <&9 2>&- || :
fi
printf '\r\033[K' >&9 || :
exec 9>&-
trap - TERM INT HUP
[ "${LOCAL_UNLOCK_READ_STATUS}" -eq 0 ] || return 1
return 0
}
ensure_minimal_dev_nodes
printf "\e[95m[INFO] Starting : [/usr/lib/live/boot/0024-ciss-crypt-squash] \n\e[0m"
#######################################
# Ask for a passphrase on /dev/console, mask input with '*'.
# Globals:
# None
# Arguments:
# None
# Returns:
# 0: on success
# 1: on failure / empty
#######################################
ask_pass_console() {
PASSPHRASE=""
SAVED_STTY=""
ensure_minimal_dev_nodes
[ -c /dev/console ] || return 1
exec 8<>/dev/console || return 1
### Save current console settings.
SAVED_STTY=$(stty -g <&8 2>&- || printf '')
### Non-canonical mode, no echo, 1 byte at a time.
if ! stty -echo -icanon time 0 min 1 <&8 2>&-; then
exec 8>&-
return 1
fi
cr=$(printf '\r')
bs=$(printf '\b')
del=$(printf '\177')
while :; do
### Read exactly one byte from the console.
c=$(dd bs=1 count=1 2>&- <&8)
if [ -z "${c}" ]; then
printf '\n' >&8
break
fi
### If nothing read (race), loop again.
[ -z "${c}" ] && continue
case "${c}" in
"${cr}")
### Enter: finish input.
printf '\n' >&8
break
;;
"${bs}"|"${del}")
### Backspace, delete: delete one character, if available.
if [ -n "${PASSPHRASE}" ]; then
PASSPHRASE=${PASSPHRASE%?}
printf '\b \b' >&8
fi
;;
*)
### Normal character: append and mask output.
PASSPHRASE="${PASSPHRASE}${c}"
printf '*' >&8
;;
esac
done
if [ -n "${SAVED_STTY}" ]; then
stty "${SAVED_STTY}" <&8 2>&- || :
fi
exec 8>&-
printf '%s' "${PASSPHRASE}"
return 0
}
#######################################
# Premount logging helper.
# Globals:
# None
# Arguments:
# *: String to log.
#######################################
log() {
msg="$*"
if [ -w /dev/kmsg ]; then
printf '<6>%s: %s\n' '0024-ciss-crypt-squash' "${msg}" > /dev/kmsg
else
printf '%s: %s\n' '0024-ciss-crypt-squash' "${msg}"
fi
}
### Declare variables. ---------------------------------------------------------------------------------------------------------
export CDLB_ISO_LABEL="CISS.debian.live"
export CDLB_LUKS_FS="/live/ciss_rootfs.crypt"
export CDLB_LUKS_ROOTFS_MNT="/run/live/ciss-rootfs"
export CDLB_MAPPER_NAME="crypt_liveiso"
export CDLB_MAPPER_DEV="/dev/mapper/${CDLB_MAPPER_NAME}"
export CDLB_MNT_MEDIUM="/run/live/medium"
export CDLB_MNT_ROOTFS="/run/live/rootfs"
export CDLB_REMOTE_WAIT_SECS="${CDLB_REMOTE_WAIT_SECS:-3600}"
_PARAMETER=""
_dev=""
### Read the kernel cmdline once. ----------------------------------------------------------------------------------------------
CMDLINE="$(cat /proc/cmdline 2>&- || printf '')"
for _PARAMETER in ${CMDLINE}; do
case "${_PARAMETER}" in
ciss_crypt_path=*) export CDLB_LUKS_FS="${_PARAMETER#ciss_crypt_path=}";;
ciss_iso_label=* ) export CDLB_ISO_LABEL="${_PARAMETER#ciss_iso_label=}";;
esac
done
printf "\e[92m[INFO] CDLB_LUKS_FS : [%s] \n\e[0m" "${CDLB_LUKS_FS}"
printf "\e[92m[INFO] CDLB_ISO_LABEL : [%s] \n\e[0m" "${CDLB_ISO_LABEL}"
mkdir -p /conf "${CDLB_MNT_MEDIUM}" "${CDLB_MNT_ROOTFS}"
### Mount the live medium (ISO) read-only, unless already mounted. -------------------------------------------------------------
if ! mountpoint -q "${CDLB_MNT_MEDIUM}"; then
if [ -n "${CDLB_ISO_LABEL}" ] && [ -e "/dev/disk/by-label/${CDLB_ISO_LABEL}" ]; then
mount -r -t iso9660 "/dev/disk/by-label/${CDLB_ISO_LABEL}" "${CDLB_MNT_MEDIUM}" 2>&- \
|| mount -r -t udf "/dev/disk/by-label/${CDLB_ISO_LABEL}" "${CDLB_MNT_MEDIUM}" 2>&- \
|| log "could not mount label=${CDLB_ISO_LABEL} (iso9660/udf)"
fi
fi
if ! mountpoint -q "${CDLB_MNT_MEDIUM}"; then
### Fallback scan (covers SR drives and loop-mounted ISOs that udev exposed).
for _dev in /dev/sr* /dev/cdrom /dev/disk/by-label/*; do
### Skip non-block entries early.
[ -b "${_dev}" ] || continue
### Try ISO9660 first, then UDF; only unmount on failure.
if mount -r -t iso9660 "${_dev}" "${CDLB_MNT_MEDIUM}" 2>&- || mount -r -t udf "${_dev}" "${CDLB_MNT_MEDIUM}" 2>&-; then
mountpoint -q "${CDLB_MNT_MEDIUM}" 2>&- && break
else
umount "${CDLB_MNT_MEDIUM}" 2>&- || true
fi
done
fi
if ! mountpoint -q "${CDLB_MNT_MEDIUM}"; then
printf "\e[91m[FATAL] Boot failure : No live medium mounted, defer to default live-boot path. \n\e[0m"
sleep 60
log "[FATAL] Boot failure : No live medium mounted, defer to default live-boot path."
panic "[FATAL] Boot failure : No live medium mounted, defer to default live-boot path."
fi
printf "\e[92m[INFO] MNT_MEDIUM : [%s] \n\e[0m" "${CDLB_MNT_MEDIUM}"
### Locate the encrypted root container on the medium. -------------------------------------------------------------------------
if [ ! -f "${CDLB_MNT_MEDIUM}${CDLB_LUKS_FS}" ]; then
printf "\e[91m[FATAL] Boot failure : Encrypted root not found at: [%s%s] \n\e[0m" "${CDLB_MNT_MEDIUM}" "${CDLB_LUKS_FS}"
sleep 60
log "[FATAL] Boot failure : Encrypted root not found at: [${CDLB_MNT_MEDIUM}${CDLB_LUKS_FS}]"
panic "[FATAL] Boot failure : Encrypted root not found at: [${CDLB_MNT_MEDIUM}${CDLB_LUKS_FS}]"
fi
printf "\e[92m[INFO] CISS LUKS FS : [%s%s] \n\e[0m" "${CDLB_MNT_MEDIUM}" "${CDLB_LUKS_FS}"
### Attach a loop device read-only to the encrypted file. ----------------------------------------------------------------------
if ! LOOP="$(losetup -f --show -r "${CDLB_MNT_MEDIUM}${CDLB_LUKS_FS}")"; then
printf "\e[91m[FATAL] Boot failure : losetup failed \n\e[0m"
sleep 60
log "[FATAL] Boot failure : losetup failed "
panic "[FATAL] Boot failure : losetup failed "
fi
printf "\e[92m[INFO] Loop device : [%s] \n\e[0m" "${LOOP}"
### Expose the loop device for unlock-wrapper.sh, dropbear forced-command. -----------------------------------------------------
mkdir -p /run 2>&- || true
echo "${LOOP}" > /run/ciss-loopdev 2>&- || true
chmod 0600 /run/ciss-loopdev 2>&- || true
printf "\e[92m[INFO] Exposed LOOP : [/run/ciss-loopdev] -> [%s]\n\e[0m" "${LOOP}"
### Prepare fifo for passphrase. -----------------------------------------------------------------------------------------------
mkdir -p /lib/cryptsetup 2>&- || true
if [ -p /lib/cryptsetup/passfifo ]; then
rm -f /lib/cryptsetup/passfifo 2>&- || true
fi
if ! mkfifo /lib/cryptsetup/passfifo 2>&-; then
printf "\e[92m[WARN] Boot failure : Failed to create [/lib/cryptsetup/passfifo] \n\e[0m"
sleep 60
log "[WARN] Boot failure : Failed to create [/lib/cryptsetup/passfifo]"
panic "[WARN] Boot failure : Failed to create [/lib/cryptsetup/passfifo]"
fi
chmod 0600 /lib/cryptsetup/passfifo 2>&- || true
### Background broker: read FIFO, try cryptsetup per line. ---------------------------------------------------------------------
(
set +e
PASS=""
while :; do
if [ -b "${CDLB_MAPPER_DEV}" ]; then
break
fi
if ! IFS= read -r PASS < /lib/cryptsetup/passfifo; then
sleep 1
continue
fi
[ -n "${PASS}" ] || continue
console_printf "\e[93m[INFO] CISS LUKS decryption : LUKS mapper [%s] trying to unlock via cryptsetup ... \n\e[0m" "${CDLB_MAPPER_DEV}"
KEYLEN=${#PASS}
if [ -c /dev/console ]; then
printf '%s' "${PASS}" | cryptsetup open --tries 1 \
--type luks \
--keyfile-size="${KEYLEN}" \
--readonly "${LOOP}" "${CDLB_MAPPER_NAME}" --key-file - 2>/dev/console
else
printf '%s' "${PASS}" | cryptsetup open --tries 1 \
--type luks \
--keyfile-size="${KEYLEN}" \
--readonly "${LOOP}" "${CDLB_MAPPER_NAME}" --key-file - 2>&-
fi
if [ -b "${CDLB_MAPPER_DEV}" ]; then
console_printf "\e[92m[INFO] CISS LUKS decryption : LUKS mapper [%s] successfully opened. \n\e[0m" "${CDLB_MAPPER_DEV}"
break
fi
done
) &
PID_BROKER="$!"
### Background process console-prompt feed passphrases into FIFO. --------------------------------------------------------------
(
set +e
PASS=""
PASS_SENT=0
WAIT_LOOP=0
while :; do
if [ -b "${CDLB_MAPPER_DEV}" ]; then
break
fi
if [ "${PASS_SENT}" -eq 0 ]; then
# shellcheck disable=SC2310
wait_for_local_unlock_activation || continue
console_printf '\e[93m[INFO] Enter LUKS passphrase: \n\e[0m'
# shellcheck disable=SC2310
PASS="$(ask_pass_console)" || continue
printf '%s\n' "${PASS}" >| /lib/cryptsetup/passfifo 2>&- || :
PASS_SENT=1
WAIT_LOOP=0
else
WAIT_LOOP=$((WAIT_LOOP + 1))
if [ "${WAIT_LOOP}" -ge 160 ]; then
console_printf '\e[91m[WARN] Please try again : \n\e[0m'
PASS_SENT=0
WAIT_LOOP=0
fi
fi
sleep 0.1
done
return 0
) &
PID_PROMPT="$!"
### Main process: wait bounded time for the mapper to appear. ------------------------------------------------------------------
REMAINING="${CDLB_REMOTE_WAIT_SECS}"
if [ ! -b "${CDLB_MAPPER_DEV}" ]; then
printf "\e[93m[INFO] CISS LUKS decryption : Waiting up to %s seconds for [%s] to be unlocked ... \n\e[0m" "${REMAINING}" "${CDLB_MAPPER_DEV}"
fi
while [ "${REMAINING}" -gt 0 ]; do
if [ -b "${CDLB_MAPPER_DEV}" ]; then
break
fi
sleep 1
REMAINING=$((REMAINING - 1))
done
if [ ! -b "${CDLB_MAPPER_DEV}" ]; then
printf "\e[91m[WARN] CISS LUKS decryption : Timeout LUKS mapper [%s] not present after %s seconds. \n\e[0m" "${CDLB_MAPPER_DEV}" "${CDLB_REMOTE_WAIT_SECS}"
kill "${PID_PROMPT}" 2>&- || true
kill "${PID_BROKER}" 2>&- || true
wait "${PID_PROMPT}" 2>&- || true
wait "${PID_BROKER}" 2>&- || true
console_clear_line
rm -f /lib/cryptsetup/passfifo 2>&- || true
sleep 60
log "[WARN] CISS LUKS decryption : Timeout LUKS mapper [${CDLB_MAPPER_DEV}] not present after ${CDLB_REMOTE_WAIT_SECS} seconds."
panic "[WARN] CISS LUKS decryption : Timeout LUKS mapper [${CDLB_MAPPER_DEV}] not present after ${CDLB_REMOTE_WAIT_SECS} seconds."
fi
kill "${PID_PROMPT}" 2>&- || true
kill "${PID_BROKER}" 2>&- || true
wait "${PID_PROMPT}" 2>&- || true
wait "${PID_BROKER}" 2>&- || true
console_clear_line
rm -f /lib/cryptsetup/passfifo 2>&- || true
printf "\e[92m[INFO] CISS LUKS decryption : [%s] is now present.\n\e[0m" "${CDLB_MAPPER_DEV}"
### Expose the decrypted root device for live-boot overlay. The live-boot components will pick this up in '9990-overlay.sh'. ---
cat << EOF >| /run/ciss-rootdev
export CDLB_ISO_LABEL=${CDLB_ISO_LABEL}
export CDLB_LUKS_FS=${CDLB_LUKS_FS}
export CDLB_LUKS_ROOTFS_MNT=${CDLB_LUKS_ROOTFS_MNT}
export CDLB_MAPPER_NAME=${CDLB_MAPPER_NAME}
export CDLB_MAPPER_DEV=${CDLB_MAPPER_DEV}
export CDLB_MNT_MEDIUM=${CDLB_MNT_MEDIUM}
export CDLB_MNT_ROOTFS=${CDLB_MNT_ROOTFS}
export CDLB_REMOTE_WAIT_SECS=${CDLB_REMOTE_WAIT_SECS}
EOF
chmod 0444 /run/ciss-rootdev 2>&- || true
### Override '9990-main.sh' behavior to ensure 'Verify_checksums()' functions properly. ----------------------------------------
if [ ! -e /conf/param.conf ]; then
printf "\e[92m[INFO] CISS LUKS decryption : Not existing [/conf/param.conf] \n\e[0m"
: >| /conf/param.conf
fi
if ! grep -q '^PLAIN_ROOT=' /conf/param.conf 2>&-; then
printf 'PLAIN_ROOT=1\n' >> /conf/param.conf
fi
if ! grep -q '^livefs_root=' /conf/param.conf 2>&-; then
printf 'livefs_root=%s\n' "/run/live/medium" >> /conf/param.conf
fi
printf "\e[92m[INFO] CISS LUKS decryption : Final state [/conf/param.conf] \n\e[0m"
if [ -c /dev/console ]; then
cat /conf/param.conf >/dev/console 2>&1 || :
elif [ -e /proc/1/fd/1 ]; then
cat /conf/param.conf >/proc/1/fd/1 2>&1 || :
fi
log "Decrypted root device exposed at [/run/ciss-rootdev] -> [${CDLB_MAPPER_DEV}]"
printf "\e[92m[INFO] CISS LUKS decryption : Decrypted root device exposed at: [/run/ciss-rootdev] -> [%s] \n\e[0m" "${CDLB_MAPPER_DEV}"
### Final sanity check. --------------------------------------------------------------------------------------------------------
if [ ! -b "${CDLB_MAPPER_DEV}" ]; then
printf "\e[91m[WARN] Failed unlock : [%s] via dropbear and console. \n\e[0m" "${CDLB_LUKS_FS}"
sleep 60
log "[WARN] Failed unlock : [${CDLB_LUKS_FS}] via dropbear and console."
panic "[WARN] Failed unlock : [${CDLB_LUKS_FS}] via dropbear and console."
fi
eval "${_SAVED_SET_OPTS}"
printf "\e[92m[INFO] Successfully applied : [/usr/lib/live/boot/0024-ciss-crypt-squash] \n\e[0m"
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh