Files
CISS.debian.installer/func/9999_coresecret.sh
Marc S. Weidner 5ebc9f9356
All checks were successful
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Successful in 1m29s
V8.00.000.2025.06.17
Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
2025-06-28 09:23:59 +02:00

378 lines
10 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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.installer.git
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
# SPDX-FileCopyrightText: 20242025; 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.hardened.installer framework.
# SPDX-PackageName: CISS.debian.installer
# SPDX-Security-Contact: security@coresecret.eu
# coresecret.sh to be executed after dropbear SSH login
set -C -e -u -o pipefail
IFS=$(printf ' \n\t')
# shellcheck disable=SC2155
declare -gr CURRENTDATE=$(date +"%F %T")
declare -gir MAX_RETRIES=2
#######################################
# Generates informative shell prompt.
# Globals:
# PS1
# Arguments:
# None
#######################################
prompt_string() {
declare -gx PS1="\
\[\033[1;91m\]\d\[\033[0m\]|\[\033[1;91m\]\u\[\033[0m\]@\
\[\033[1;95m\]\h\[\033[0m\]:\
\[\033[1;96m\]\w\[\033[0m\]/>>\
\$(if [[ \$? -eq 0 ]]; then \
# Show exit status in green if zero
echo -e \"\[\033[1;92m\]\$?\[\033[0m\]\"; \
else \
# Show exit status in red otherwise
echo -e \"\[\033[1;91m\]\$?\[\033[0m\]\"; \
fi)\
|~\$ "
}
#######################################
# Trap function to be called on 'ERR'.
# Arguments:
# $1: ${?}
# $2: ${BASH_SOURCE[0]}
# $3: ${LINENO}
# $4: ${FUNCNAME[0]:-main}
# $5: ${BASH_COMMAND}
#######################################
trap_on_err() {
declare -r errcode="$1"
declare -r errscrt="$2"
declare -r errline="$3"
declare -r errfunc="$4"
declare -r errcmmd="$5"
trap - ERR
stty echo
if [[ ${errcode} -eq 0 ]]; then
print_scr_scc
prompt_string
exit 0
else
print_scr_err "${errcode}" "${errscrt}" "${errline}" "${errfunc}" "${errcmmd}"
sleep 15
sync
set +C
echo 1 > /proc/sys/kernel/sysrq
echo o > /proc/sysrq-trigger
fi
}
#######################################
# Security Trap on 'INT' and 'TERM' to provide a deterministic way to not circumvent the Nuke Routine
# Globals:
# DEVICES_LUKS
# DEVICES_NUKE
# Arguments:
# None
#######################################
trap_on_term() {
trap - INT
stty echo
printf "\n"
printf "\e[0;91m✘ System caught a 'SIGINT'. System Power Off in 3 seconds. \e[0m\n" >&2
sync
sleep 3
set +C
echo 1 > /proc/sys/kernel/sysrq
echo o > /proc/sysrq-trigger
}
#######################################
# Print Error Message for 'non-0' Trap on 'EXIT' on Terminal.
# Arguments:
# $1: ${?}
# $2: ${BASH_SOURCE[0]}
# $3: ${LINENO}
# $4: ${FUNCNAME[0]:-main}
# $5: ${BASH_COMMAND}
#######################################
print_scr_err() {
declare -r scr_err_errcode="$1"
declare -r scr_err_errscrt="$2"
declare -r scr_err_errline="$3"
declare -r scr_err_errfunc="$4"
declare -r scr_err_errcmmd="$5"
printf "\n"
printf "\e[0;91m✘ System caught an 'ERROR'. System Power Off in 15 seconds. \e[0m\n" >&2
printf "\n"
printf "\e[0;91m✘ Error : %s \e[0m\n" "${scr_err_errcode}" >&2
printf "\e[0;91m✘ Line : %s \e[0m\n" "${scr_err_errline}" >&2
printf "\e[0;91m✘ Script : %s \e[0m\n" "${scr_err_errscrt}" >&2
printf "\e[0;91m✘ Function : %s \e[0m\n" "${scr_err_errfunc}" >&2
printf "\e[0;91m✘ Command : %s \e[0m\n" "${scr_err_errcmmd}" >&2
printf "\n"
}
#######################################
# Print Error Message for '0' Trap on 'ERR' on Terminal.
# Arguments:
# none
#######################################
print_scr_scc() {
printf "\e[0;92m✅ Script exited successfully. Proceeding with booting. \e[0m\n"
printf "\n"
}
#######################################
# Gather information of all LUKS Devices available on the system
# Arguments:
# None
#######################################
gather_luks_devices() {
declare prev=() curr=() dev="" tries=0
while ((tries < 10)); do
mapfile -t curr < <(blkid -t TYPE=crypto_LUKS -o device)
if [[ ${curr[*]} == "${prev[*]}" ]]; then
break
fi
prev=("${curr[@]}")
tries=$((tries + 1))
sleep 1
done
### Print one device per line for mapfile compatibility
for dev in "${curr[@]}"; do
printf '%s\n' "${dev}"
done
}
#######################################
# Gather information of all NUKE Devices available on the system
# Globals:
# DEVICES_LUKS
# Arguments:
# None
#######################################
gather_nuke_devices() {
### 'DEVICES_LUKS' must already be a bash array of device paths
declare dev=""
declare result=()
for dev in "${DEVICES_LUKS[@]}"; do
### Check slot 31 for 'luks2'
if cryptsetup luksDump "${dev}" 2> /dev/null | grep -qE '^[[:space:]]*31: luks2'; then
result+=("${dev}")
fi
done
### Print one device per line for mapfile compatibility
for dev in "${result[@]}"; do
printf '%s\n' "${dev}"
done
}
#######################################
# Read passphrase interactively.
# Arguments:
# None
#######################################
passphrase_ask() {
declare -g PASSPHRASE=""
printf "\n"
stty -echo
printf "\e[0;95m🔐 Enter passphrase for decryption: \e[0m\n"
read -r PASSPHRASE
stty echo
printf "\n"
}
#######################################
# Test the entered passphrase against the dedicated Nuke Keyslot #31.
# Arguments:
# $1: DEVICE
# $2: PASSWD
#######################################
passphrase_test() {
declare -r DEVICE="$1"
declare -r PASPHR="$2"
printf '%s' "${PASPHR}" | cryptsetup open --batch-mode --test-passphrase --key-slot 31 "${DEVICE}" > /dev/null 2>&1
}
#######################################
# Check the integrity and authenticity of this script itself.
# Arguments:
# $0: Script Name
#######################################
verify_coresecret() {
### Directory of this script
# shellcheck disable=SC2155
declare dir="$(dirname "$(readlink -f "${0}")")"
# shellcheck disable=SC2155
declare script="$(basename "${0}")"
declare algo
for algo in sha512 sha384; do
# shellcheck disable=SC2155
declare hashfile="${dir}/${script}.${algo}"
# shellcheck disable=SC2155
declare sigfile="${hashfile}.sig"
# shellcheck disable=SC2155
declare cmd="${algo}sum"
printf "\e[0;95m🔏 Verifying signature of: [%s] \e[0m\n" "${hashfile}"
gpgv --keyring /etc/keys/pubring.gpg "${sigfile}" "${hashfile}" || {
printf "\e[0;91m✘ Signature verification failed for: [%s] \e[0m\n" "${hashfile}" >&2
printf "\e[0;91m✘ System Power Off in 3 seconds. \e[0m\n" >&2
sync
sleep 3
set +C
echo 1 > /proc/sys/kernel/sysrq
echo o > /proc/sysrq-trigger
}
printf "\e[0;92m🔏 Verifying signature of: [%s] successful. \e[0m\n" "${hashfile}"
printf "\e[0;95m🔢 Recomputing Hash: [%s] \e[0m\n" "${algo}"
# shellcheck disable=SC2155
declare computed=$($cmd "${dir}/${script}" | awk '{print $1}')
# shellcheck disable=SC2155
declare expected=$(cat "${hashfile}")
if [[ ${computed} != "${expected}" ]]; then
printf "\e[0;91m✘ Hash mismatch for: [%s] \e[0m\n" "${algo}" >&2
printf "\e[0;91m✘ System Power Off in 3 seconds. \e[0m\n" >&2
sync
sleep 3
set +C
echo 1 > /proc/sys/kernel/sysrq
echo o > /proc/sysrq-trigger
fi
printf "\e[0;92m🔢 Recomputing Hash: [%s] successful. \e[0m\n" "${algo}"
done
printf "\e[0;92m🔏 All signatures and hashes verified successfully. Proceeding. \e[0m\n"
}
### Main Programm
trap 'trap_on_err "$?" "${BASH_SOURCE[0]}" "${LINENO}" "${FUNCNAME[0]:-main}" "${BASH_COMMAND}"' ERR
trap 'trap_on_term' INT TERM
printf "\e[0;91mCoresecret Connection established.\e[0m\n"
printf "\e[0;91mStarting Time: %s\e[0m\n" "${CURRENTDATE}"
printf "\n"
verify_coresecret
### Read newline-separated output into an array
mapfile -t DEVICES_LUKS < <(gather_luks_devices)
mapfile -t DEVICES_NUKE < <(gather_nuke_devices)
### Debug output: list each element with its index
#for idx in "${!DEVICES_LUKS[@]}"; do
# printf 'Luks[%d]: %s\n' "${idx}" "${DEVICES_LUKS[${idx}]}"
#done
### Debug output: list each element with its index
#for idx in "${!DEVICES_NUKE[@]}"; do
# printf 'Nuke[%d]: %s\n' "${idx}" "${DEVICES_NUKE[${idx}]}"
#done
### # If there are no LUKS devices at all, drop to bash
[[ -n ${DEVICES_LUKS[*]} ]] || {
printf "\e[0;92m✘ No LUKS Devices found. Dropping to bash ... \e[0m\n"
prompt_string
exec /bin/bash -i
}
### If there are LUKS devices but no Nuke devices, try unlocking flow
if [[ -n ${DEVICES_LUKS[*]} ]] && [[ -z ${DEVICES_NUKE[*]} ]]; then
### Attempt interactive unlock with cryptroot-unlock
if cryptroot-unlock; then
exit 0
else
printf "\n"
printf "\e[0;91m✘ Unsuccessful command 'cryptroot-unlock'. \e[0m\n"
printf "\e[0;92m✘ No LUKS operations performed. Dropping to bash ... \e[0m\n"
printf "\e[0;92m✘ To unlock 'root' partition, and maybe others like 'swap', run 'cryptroot-unlock'. \e[0m\n"
prompt_string
exec /bin/bash -i
fi
elif [[ -n ${DEVICES_LUKS[*]} ]] && [[ -n ${DEVICES_NUKE[*]} ]]; then
declare -i attempt=1
declare NUKED=false
declare TEST_DEV="${DEVICES_NUKE[0]}"
while ((attempt <= MAX_RETRIES)); do
printf "\e[0;95m🔐Attempt %s/%s: \e[0m\n" "${attempt}" "${MAX_RETRIES}"
passphrase_ask
declare -g PASSWD="${PASSPHRASE}"
if passphrase_test "${TEST_DEV}" "${PASSWD}"; then
for dev in "${DEVICES_NUKE[@]}"; do
cryptsetup erase --batch-mode "${dev}" > /dev/null 2>&1
printf "%s:\e[0;95m✘ LUKS Device Header malfunction. \e[0m\n" "${dev}"
done
declare -r NUKED=true
unset PASSWD
break
else
declare code="$?"
case "${code}" in
1) printf "\e[0;91m✘ No usable key slot is available. \e[0m\n" ;;
2) printf "\e[0;91m✘ No key available with this passphrase. \e[0m\n" ;;
3) printf "\e[0;93m✘ Out of memory. \e[0m\n" ;;
*) printf "\e[0;91m✘ Unexpected Return Code. \e[0m\n" ;;
esac
fi
attempt=$((attempt + 1))
done
if [[ ${NUKED} == true ]]; then
stty echo
sleep 3
sync
set +C
echo 1 > /proc/sys/kernel/sysrq
echo o > /proc/sysrq-trigger
fi
if cryptroot-unlock; then
exit 0
else
printf "\e[0;91m✘ Unsuccessful command 'cryptroot-unlock'. \e[0m\n"
printf "\e[0;92m✘ No LUKS operations performed. Dropping to bash ... \e[0m\n"
printf "\e[0;92m✘ To unlock 'root' partition, and maybe others like 'swap', run 'cryptroot-unlock'. \e[0m\n"
prompt_string
exec /bin/bash -i
fi
fi
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh: