Compare commits
2 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
487d2b3ba8
|
|||
|
97596fbcba
|
+51
-23
@@ -46,20 +46,24 @@ Verify_checksums() {
|
||||
|
||||
LIVE_VERIFY_CHECKSUMS_SIGNATURES="false"
|
||||
|
||||
_KEYFILE=""
|
||||
_CHECKSUM_LOG_DIR="${LIVE_VERIFY_CHECKSUMS_LOG_DIR:-/run}"
|
||||
|
||||
_MANIFEST_FOUND="false"
|
||||
_KEYFILE=""
|
||||
|
||||
_MP=""
|
||||
|
||||
_RETURN_PGP=""
|
||||
_MANIFEST_FOUND="false"
|
||||
|
||||
_RETURN_SHA=""
|
||||
_TOOL_FOUND="false"
|
||||
|
||||
_VERIFICATION_EXECUTED="false"
|
||||
|
||||
_VERIFICATION_SUCCEEDED="false"
|
||||
|
||||
_RETURN_PGP=""
|
||||
|
||||
_RETURN_SHA=""
|
||||
|
||||
### Parse commandline arguments ----------------------------------------------------------------------------------------------
|
||||
# shellcheck disable=SC2154
|
||||
for _PARAMETER in ${LIVE_BOOT_CMDLINE}; do
|
||||
@@ -118,6 +122,14 @@ Verify_checksums() {
|
||||
|
||||
done
|
||||
|
||||
if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ] && [ -z "${_KEYFILE}" ]; then
|
||||
|
||||
printf "\e[91m[FATAL] No pinned GPG key file found while checksum signature verification is enabled. \n\e[0m"
|
||||
sleep 8
|
||||
panic "No pinned GPG key file found while checksum signature verification is enabled."
|
||||
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2164
|
||||
cd "${_MOUNTPOINT}"
|
||||
|
||||
@@ -215,11 +227,13 @@ Verify_checksums() {
|
||||
if [ -e "${_CHECKSUM}" ]; then
|
||||
|
||||
_MANIFEST_FOUND="true"
|
||||
|
||||
printf "\e[95m[INFO] Found: [%s] ... \n\e[0m" "${_CHECKSUM}"
|
||||
|
||||
if [ -e "/usr/bin/${_DIGEST}sum" ]; then
|
||||
|
||||
_VERIFICATION_EXECUTED="true"
|
||||
_TOOL_FOUND="true"
|
||||
|
||||
printf "\e[95m[INFO] Found: [%s] ... \n\e[0m" "/usr/bin/${_DIGEST}sum"
|
||||
|
||||
if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then
|
||||
@@ -247,19 +261,34 @@ Verify_checksums() {
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
if grep -v '^#' "${_CHECKSUM}" | /usr/bin/"${_DIGEST}"sum -c > "${_TTY}"; then
|
||||
_VERIFICATION_EXECUTED="true"
|
||||
_CHECKSUM_LOG="${_CHECKSUM_LOG_DIR}/ciss-${_DIGEST}sum-check.log"
|
||||
if ! grep -v '^#' "${_CHECKSUM}" | grep -q '[^[:space:]]'; then
|
||||
|
||||
_RETURN_SHA="254"
|
||||
: > "${_CHECKSUM_LOG}"
|
||||
printf "\e[91m[FATAL] Checksum manifest has no checksum entries: [%s] \n\e[0m" "${_CHECKSUM}"
|
||||
|
||||
elif grep -v '^#' "${_CHECKSUM}" | /usr/bin/"${_DIGEST}"sum -c > "${_CHECKSUM_LOG}" 2>&1; then
|
||||
|
||||
_RETURN_SHA="${?}"
|
||||
_VERIFICATION_SUCCEEDED="true"
|
||||
cat "${_CHECKSUM_LOG}" > "${_TTY}"
|
||||
printf "\e[92m[INFO] Found: [%s] successful verified: [%s] \n\e[0m" "/usr/bin/${_DIGEST}sum" "${_CHECKSUM}"
|
||||
|
||||
else
|
||||
|
||||
_RETURN_SHA="${?}"
|
||||
cat "${_CHECKSUM_LOG}" > "${_TTY}"
|
||||
printf "\e[91m[FATAL] Found: [%s] unsuccessful verified: [%s] \n\e[0m" "/usr/bin/${_DIGEST}sum" "${_CHECKSUM}"
|
||||
|
||||
fi
|
||||
|
||||
if { [ "${_RETURN_PGP}" = "0" ] || [ "${_RETURN_PGP}" = "na" ]; } && [ "${_RETURN_SHA}" = "0" ]; then
|
||||
|
||||
_VERIFICATION_SUCCEEDED="true"
|
||||
|
||||
fi
|
||||
|
||||
# Stop after the first verification.
|
||||
break 2
|
||||
|
||||
@@ -281,28 +310,31 @@ Verify_checksums() {
|
||||
|
||||
if [ "${_MANIFEST_FOUND}" != "true" ]; then
|
||||
|
||||
printf "\e[91m[FATAL] No supported checksum manifest found. \n\e[0m"
|
||||
printf "\e[91m[FATAL] No supported checksum manifest found. Checksum verification is fail-closed. \n\e[0m"
|
||||
sleep 8
|
||||
panic "No supported checksum manifest found."
|
||||
return 1
|
||||
panic "No supported checksum manifest found. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_TOOL_FOUND}" != "true" ]; then
|
||||
|
||||
printf "\e[91m[FATAL] Checksum manifest found, but no supported checksum tool is available. Checksum verification is fail-closed. \n\e[0m"
|
||||
sleep 8
|
||||
panic "Checksum manifest found, but no supported checksum tool is available. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_VERIFICATION_EXECUTED}" != "true" ]; then
|
||||
|
||||
printf "\e[91m[FATAL] No supported checksum verification tool was available. \n\e[0m"
|
||||
printf "\e[91m[FATAL] Checksum verification was not executed. Checksum verification is fail-closed. \n\e[0m"
|
||||
sleep 8
|
||||
panic "No supported checksum verification tool was available."
|
||||
return 1
|
||||
panic "Checksum verification was not executed. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_VERIFICATION_SUCCEEDED}" != "true" ]; then
|
||||
|
||||
printf "\e[91m[FATAL] No supported checksum manifest was verified successfully. \n\e[0m"
|
||||
sleep 8
|
||||
panic "No supported checksum manifest was verified successfully."
|
||||
return 1
|
||||
printf "\e[91m[FATAL] Checksum verification did not complete successfully. Evaluating fail-closed failure state. \n\e[0m"
|
||||
|
||||
fi
|
||||
|
||||
@@ -329,7 +361,6 @@ Verify_checksums() {
|
||||
printf "\e[91m[FATAL] CDLB modified: [%s] done. \n\e[0m" "${CDLB_SCRIPT_FULL}"
|
||||
sleep 8
|
||||
panic "Verification of [GPG signature] file successful, while verification of [sha checksum] file failed."
|
||||
return 1
|
||||
;;
|
||||
|
||||
*",0")
|
||||
@@ -337,7 +368,6 @@ Verify_checksums() {
|
||||
printf "\e[91m[FATAL] CDLB modified: [%s] done. \n\e[0m" "${CDLB_SCRIPT_FULL}"
|
||||
sleep 8
|
||||
panic "Verification of [GPG signature] file failed, while verification of [sha checksum] file successful."
|
||||
return 1
|
||||
;;
|
||||
|
||||
"na,"*)
|
||||
@@ -345,14 +375,12 @@ Verify_checksums() {
|
||||
printf "\e[91m[FATAL] CDLB modified: [%s] done. \n\e[0m" "${CDLB_SCRIPT_FULL}"
|
||||
sleep 8
|
||||
panic "Verification of checksum file failed."
|
||||
return 1
|
||||
;;
|
||||
|
||||
*)
|
||||
printf "\e[91m[FATAL] Checksum verification ended in an unsupported state. \n\e[0m"
|
||||
printf "\e[91m[FATAL] Unknown checksum verification state: [%s,%s]. \n\e[0m" "${_RETURN_PGP:-unset}" "${_RETURN_SHA:-unset}"
|
||||
sleep 8
|
||||
panic "Checksum verification ended in an unsupported state."
|
||||
return 1
|
||||
panic "Unknown checksum verification state. Checksum verification is fail-closed."
|
||||
;;
|
||||
|
||||
esac
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 🔐 Generating a Private Live ISO TRIXIE.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 🔐 Generating a Private Live ISO TRIXIE.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 💙 Generating a PUBLIC Live ISO.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ body:
|
||||
attributes:
|
||||
label: "Version"
|
||||
description: "Which version are you running? Use `./ciss_live_builder.sh -v`."
|
||||
placeholder: "e.g., Master V9.14.022.2026.06.10"
|
||||
placeholder: "e.g., Master V9.14.024.2026.06.11"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
FROM debian:bookworm
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 🔁 Render README.md to README.html.
|
||||
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
|
||||
build:
|
||||
counter: 1023
|
||||
version: V9.14.022.2026.06.10
|
||||
version: V9.14.024.2026.06.11
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=yaml
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
|
||||
build:
|
||||
counter: 1023
|
||||
version: V9.14.022.2026.06.10
|
||||
version: V9.14.024.2026.06.11
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=yaml
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
|
||||
build:
|
||||
counter: 1023
|
||||
version: V9.14.022.2026.06.10
|
||||
version: V9.14.024.2026.06.11
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=yaml
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 🔐 Generating a Private Live ISO TRIXIE.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 🔐 Generating a Private Live ISO TRIXIE.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 💙 Generating a PUBLIC Live ISO.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
# Gitea Workflow: Shell-Script Linting
|
||||
#
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 🛡️ Retrieve DNSSEC status of coresecret.dev.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
name: 🔁 Render Graphviz Diagrams.
|
||||
|
||||
|
||||
+1
-1
@@ -15,5 +15,5 @@ properties_SPDX-License-Identifier="LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1 "
|
||||
properties_SPDX-LicenseComment="This file is part of the CISS.debian.installer.secure framework."
|
||||
properties_SPDX-PackageName="CISS.debian.live.builder"
|
||||
properties_SPDX-Security-Contact="security@coresecret.eu"
|
||||
properties_version="V9.14.022.2026.06.10"
|
||||
properties_version="V9.14.024.2026.06.11"
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf
|
||||
|
||||
@@ -6,7 +6,7 @@ Creator: Person: Marc S. Weidner (Centurion Intelligence Consulting Agency)
|
||||
Created: 2025-05-07T12:00:00Z
|
||||
Package: CISS.debian.live.builder
|
||||
PackageName: CISS.debian.live.builder
|
||||
PackageVersion: Master V9.14.022.2026.06.10
|
||||
PackageVersion: Master V9.14.024.2026.06.11
|
||||
PackageSupplier: Organization: Centurion Intelligence Consulting Agency
|
||||
PackageDownloadLocation: https://git.coresecret.dev/msw/CISS.debian.live.builder
|
||||
PackageHomePage: https://git.coresecret.dev/msw/CISS.debian.live.builder
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
[](https://git.coresecret.dev/msw/CISS.debian.live.builder)
|
||||
[](https://git.coresecret.dev/msw/CISS.debian.live.builder)
|
||||
|
||||
[](https://eupl.eu/1.2/en/)
|
||||
[](https://opensource.org/license/eupl-1-2)
|
||||
@@ -27,7 +27,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
**CISS.debian.live.builder — First of its own.**<br>
|
||||
**World-class CIA: Designed, handcrafted, and powered by Centurion Intelligence Consulting Agency.**
|
||||
@@ -46,9 +46,10 @@ Beyond a conventional live system, **CISS.debian.live.builder** assembles a **fu
|
||||
in a single, deterministic build step: a LUKS2 container backed by `dm-integrity` hosting the SquashFS root filesystem, combined
|
||||
with a hardened initramfs chain including a dedicated Dropbear build pipeline for remote LUKS unlock. The resulting ISO ships
|
||||
with a hardened kernel configuration, strict sysctl and network tuning, pre-configured SSH hardening and fail2ban, and a
|
||||
customised `verify-checksums` path providing both ISO-edge verification and runtime attestation of the live root. All components
|
||||
are aligned with the `CISS.debian.installer` baseline, ensuring a unified cryptographic and security posture from first boot to
|
||||
an installed system. For an overview of the entire build process, see:
|
||||
customised `verify-checksums` path providing fail-closed ISO-edge verification and runtime attestation of the exact final
|
||||
SquashFS payload bytes selected for the encrypted live root. All components are aligned with the `CISS.debian.installer`
|
||||
baseline, ensuring a unified cryptographic and security posture from first boot to an installed system. For an overview of the
|
||||
entire build process, see:
|
||||
**[MAN_CISS_ISO_BOOT_CHAIN.md](docs/MAN_CISS_ISO_BOOT_CHAIN.md)**
|
||||
|
||||
When built with the ``--dhcp-centurion`` profile, the live system ships with a strict network and resolver policy:
|
||||
@@ -60,12 +61,17 @@ and spoofing surfaces.
|
||||
|
||||
Internally, the builder employs a dedicated secret-handling pipeline backed by a tmpfs-only secrets directory
|
||||
(`/dev/shm/cdlb_secrets`). Sensitive material such as root passwords, SSH keys, and signing keys never appears on the command
|
||||
line, is guarded by strict `0400 root:root` permissions, and any symlink inside the secret path is treated as a hard failure
|
||||
that aborts the run. Critical code paths temporarily disable Bash xtrace so that credentials never leak into debug logs, and
|
||||
transient secret files are shredded (`shred -fzu`) as soon as they are no longer needed. GNUPG homes used for signing are
|
||||
wiped, unencrypted chroot artifacts and includes are removed after `lb build`, and the final artifact is reduced to the
|
||||
encrypted SquashFS inside the LUKS2 container. At runtime, LUKS passphrases in the live ISO and installer are transported via
|
||||
named pipes inside the initramfs instead of process arguments, further minimizing exposure in process listings.
|
||||
line. The secret root must be an existing `root:root` tmpfs or ramfs directory with `0700` permissions and no symlinks or
|
||||
special files below it. Filename-only secret arguments are rejected when they contain path separators, traversal names, shell
|
||||
metacharacters, or unsafe leading dashes. Critical code paths temporarily disable Bash xtrace so that credentials never leak
|
||||
into debug logs; the final exact-value sanitization pass runs only after xtrace has been stopped and its debug file descriptor
|
||||
has been closed, then redacts still-known secret values from debug, variable, and error logs as defense in depth. Transient
|
||||
secret files are shredded (`shred -fzu`) as soon as they are no longer needed; this is best-effort on SSD/NVMe media, so the
|
||||
architecture relies primarily on tmpfs for secret staging. GNUPG homes used for signing are wiped,
|
||||
unencrypted chroot artifacts and includes are removed after `lb build`, and cleanup is intentionally destructive only inside
|
||||
the exact build directory carrying the `.ciss-live-builder-owned` marker. At runtime, LUKS passphrases in the live ISO and
|
||||
installer are transported via named pipes inside the initramfs instead of process arguments, further minimizing exposure in
|
||||
process listings.
|
||||
|
||||
Check out more leading world-class services powered by Centurion Intelligence Consulting Agency:
|
||||
* [CenturionDNS Resolver](https://eddns.eu/)
|
||||
@@ -131,7 +137,7 @@ verification chain is documented separately in **[CISS ISO Boot Chain](docs/MAN_
|
||||
In compact form, my expectations for the system are:<br>
|
||||
|
||||
* Every bit that matters for boot and provisioning is covered by checksums that I control and that are signed with keys under my solely authoritative HSM.
|
||||
* The live root runs out of a LUKS2 dm-integrity container so that a tampered or bit-rotted SquashFS never becomes a trusted root.
|
||||
* The live root runs out of a LUKS2 dm-integrity container, and the final SquashFS byte stream copied into the decrypted mapper is verified against a signed rootfs attestation manifest, so a tampered or bit-rotted SquashFS never becomes a trusted root.
|
||||
* Verification steps are not advisory. Any anomaly causes a hard abort during boot.
|
||||
* After the live environment has reached a stable, verified state, it can hand off to ``CISS.debian.installer``. The installer operates from the same image, does not pull random payloads from the internet, and keeps the target system behind a hardened firewall until the entire provisioning process has completed.
|
||||
* For unattended, headless scenarios I also support builds where the target system is installed without ever exposing a shell over the console. After installation and reboot, the machine waits for a decryption passphrase via an embedded Dropbear SSH instance in the initramfs, limited to public key authentication and guarded by strict cryptographic policies. In such variants even ``/boot`` can be encrypted, with GRUB taking care of unlocking the boot partition.
|
||||
@@ -175,7 +181,7 @@ installer toolchain.
|
||||
|
||||
This project adheres strictly to a structured versioning scheme following the pattern x.y.z-Date.
|
||||
|
||||
Example: `V9.14.022.2026.06.10`
|
||||
Example: `V9.14.024.2026.06.11`
|
||||
|
||||
`x.y.z` represents major (x), minor (y), and patch (z) version increments.
|
||||
|
||||
@@ -369,7 +375,7 @@ For further details see: **[90-ciss-local.hardened.md](docs/documentation/90-cis
|
||||
`--primordial-ssh <port>` 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.
|
||||
hook still emits the requested labeled 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.
|
||||
|
||||
@@ -493,10 +499,13 @@ To use **``CISS.debian.live.builder``** as intended, the following baseline is e
|
||||
|
||||
2. Preparation:
|
||||
1. Ensure you are root.
|
||||
2. Create the build directory `mkdir /opt/cdlb` and the tmpfs secrets directory `mkdir /dev/shm/cdlb_secrets`.
|
||||
2. Create the build directory `install -d -m 0755 -o root -g root /opt/cdlb` and the tmpfs secrets directory
|
||||
`install -d -m 0700 -o root -g root /dev/shm/cdlb_secrets`.
|
||||
3. Place your desired SSH public key in the `authorized_keys` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||||
4. Place your desired Password in the `password.txt` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||||
5. Make any other changes you need to.
|
||||
5. Keep files below `/dev/shm/cdlb_secrets` as regular, non-symlink files owned by `root:root`; the builder normalizes them
|
||||
to `0400` before use.
|
||||
6. Make any other changes you need to.
|
||||
|
||||
3. Run the config builder script `./ciss_live_builder.sh` and the integrated `lb build` command (example):
|
||||
|
||||
@@ -505,7 +514,7 @@ To use **``CISS.debian.live.builder``** as intended, the following baseline is e
|
||||
timestamp=$(date -u +%Y-%m-%dT%H:%M:%S%z)
|
||||
./ciss_live_builder.sh \
|
||||
--architecture amd64 \
|
||||
--autobuild=6.16.3+deb13-amd64 \
|
||||
--autobuild=7.0.10+deb13-amd64 \
|
||||
--build-directory /opt/cdlb \
|
||||
--cdi \
|
||||
--change-splash hexagon \
|
||||
@@ -525,9 +534,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-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-key SSH-key-filename-for-Primordial-overlay-clone \
|
||||
--primordial-ssh SSH-port-for-Primordial-overlay-clone \
|
||||
--primordial-url URL-to-Primordial-overlay-clone \
|
||||
--ssh-pubkey /dev/shm/cdlb_secrets \
|
||||
--sshfp \
|
||||
--trixie
|
||||
@@ -559,10 +568,13 @@ preview it or run it.
|
||||
|
||||
2. Preparation:
|
||||
1. Ensure you are root.
|
||||
2. Create the build directory `mkdir /opt/cdlb` and the tmpfs secrets directory `mkdir /dev/shm/cdlb_secrets`.
|
||||
2. Create the build directory `install -d -m 0755 -o root -g root /opt/cdlb` and the tmpfs secrets directory
|
||||
`install -d -m 0700 -o root -g root /dev/shm/cdlb_secrets`.
|
||||
3. Place your desired SSH public key in the `authorized_keys` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||||
4. Place your desired Password in the `password.txt` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||||
5. Copy and edit the sample and set your options (no spaces around commas in lists):
|
||||
5. Keep files below `/dev/shm/cdlb_secrets` as regular, non-symlink files owned by `root:root`; the builder normalizes them
|
||||
to `0400` before use.
|
||||
6. Copy and edit the sample and set your options (no spaces around commas in lists):
|
||||
|
||||
````bash
|
||||
cp config.mk.sample config.mk
|
||||
@@ -577,9 +589,9 @@ preview it or run it.
|
||||
SSH_PUBKEY=/dev/shm/cdlb_secrets
|
||||
|
||||
# Optional
|
||||
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
|
||||
PRIMORDIAL_KEY=SSH-key-filename-for-Primordial-overlay-clone
|
||||
PRIMORDIAL_SSH_PORT=SSH-port-for-Primordial-overlay-clone
|
||||
PRIMORDIAL_URL=URL-to-Primordial-overlay-clone
|
||||
PROVIDER_NETCUP_IPV6=2001:cdb::1
|
||||
# comma-separated; IPv6 in [] is fine
|
||||
JUMP_HOSTS=[2001:db8::1],[2001:db8::2]
|
||||
|
||||
+2
-2
@@ -8,13 +8,13 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Repository Structure
|
||||
|
||||
**Project:** Centurion Intelligence Consulting Agency Information Security Standard (CISS) — Debian Live Builder
|
||||
**Branch:** `master`
|
||||
**Repository State:** Master Version **9.14**, Build **V9.14.022.2026.06.10** (as of 2025-10-11)
|
||||
**Repository State:** Master Version **9.14**, Build **V9.14.024.2026.06.11** (as of 2025-10-11)
|
||||
|
||||
## 3.1. Top-Level Layout
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. CISS Secure Boot Private Material
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. CISS Secure Boot Public Material
|
||||
|
||||
|
||||
+33
-20
@@ -41,6 +41,8 @@ declare -grx VAR_SETUP_PATH="$(cd "$(dirname "${0}")" && pwd)" # '/roo
|
||||
declare -grx VAR_TMP_SECRET="/dev/shm/cdlb_secrets" # Fixed tmpfs path to store securely build artifacts.
|
||||
declare -grx VAR_WORKDIR="$(dirname "${VAR_SETUP_FULL}")" # '/root/git/CISS.debian.live.builder'
|
||||
|
||||
clear
|
||||
|
||||
### PRELIMINARY CHECKS.
|
||||
### No ash, dash, ksh, sh.
|
||||
# shellcheck disable=SC2292
|
||||
@@ -116,7 +118,6 @@ for arg in "$@"; do case "${arg,,}" in -v|--version) . ./lib/lib_version.sh ;
|
||||
for arg in "$@"; do case "${arg,,}" in -d|--debug) . ./meta_sources_debug.sh; debugger "${@}";; esac; done
|
||||
|
||||
### ALL CHECKS DONE. READY TO START THE SCRIPT.
|
||||
clear
|
||||
printf '\033[95m'
|
||||
cat bootscreen.txt
|
||||
printf '\033[0m\n'
|
||||
@@ -143,6 +144,7 @@ find "${VAR_TMP_SECRET}" -type f -exec chown root:root {} +
|
||||
[[ "${VAR_SETUP}" == true ]] && {
|
||||
source_guard "./lib/lib_arg_parser.sh"
|
||||
source_guard "./lib/lib_arg_priority_check.sh"
|
||||
source_guard "./lib/lib_build_dir_safety.sh"
|
||||
source_guard "./lib/lib_boot_screen.sh"
|
||||
source_guard "./lib/lib_cdi.sh"
|
||||
source_guard "./lib/lib_change_splash.sh"
|
||||
@@ -160,6 +162,7 @@ find "${VAR_TMP_SECRET}" -type f -exec chown root:root {} +
|
||||
source_guard "./lib/lib_clean_screen.sh"
|
||||
source_guard "./lib/lib_clean_up.sh"
|
||||
source_guard "./lib/lib_copy_integrity.sh"
|
||||
source_guard "./lib/lib_debug_sanitize.sh"
|
||||
source_guard "./lib/lib_gnupg.sh"
|
||||
source_guard "./lib/lib_hardening_root_pw.sh"
|
||||
source_guard "./lib/lib_hardening_ssh_tcp.sh"
|
||||
@@ -173,6 +176,7 @@ find "${VAR_TMP_SECRET}" -type f -exec chown root:root {} +
|
||||
source_guard "./lib/lib_provider_netcup.sh"
|
||||
source_guard "./lib/lib_run_analysis.sh"
|
||||
source_guard "./lib/lib_sanitizer.sh"
|
||||
source_guard "./lib/lib_secret_validation.sh"
|
||||
source_guard "./lib/lib_secureboot_profile.sh"
|
||||
source_guard "./lib/lib_trap_on_err.sh"
|
||||
source_guard "./lib/lib_trap_on_exit.sh"
|
||||
@@ -180,22 +184,6 @@ find "${VAR_TMP_SECRET}" -type f -exec chown root:root {} +
|
||||
source_guard "./lib/lib_usage.sh"
|
||||
}
|
||||
|
||||
### PRE-SCAN SECURE BOOT PROFILE FOR BUILD-HOST PACKAGE CHECKS.
|
||||
### Formal validation still happens in arg_parser().
|
||||
for ((idx=0; idx<${#ARY_PARAM_ARRAY[@]}; idx++)); do
|
||||
case "${ARY_PARAM_ARRAY[idx],,}" in
|
||||
--secure-boot-profile=*)
|
||||
declare -gx VAR_CISS_SECUREBOOT_PROFILE="${ARY_PARAM_ARRAY[idx]#*=}"
|
||||
;;
|
||||
--secure-boot-profile)
|
||||
if [[ -n "${ARY_PARAM_ARRAY[idx + 1]:-}" ]]; then
|
||||
declare -gx VAR_CISS_SECUREBOOT_PROFILE="${ARY_PARAM_ARRAY[idx + 1]}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
unset idx
|
||||
|
||||
### CHECKING REQUIRED PACKAGES.
|
||||
check_pkgs
|
||||
|
||||
@@ -221,9 +209,30 @@ if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen; fi
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nInitialization done ... \nXXX\n15\n" >&3; fi
|
||||
|
||||
### Updating Status of Dialog Gauge Bar.
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nActivate traps ... \nXXX\n50\n" >&3; fi
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nValidating secrets artifacts ... \nXXX\n20\n" >&3; fi
|
||||
validate_secret_root "${VAR_TMP_SECRET}"
|
||||
harden_secret_root_files "${VAR_TMP_SECRET}"
|
||||
|
||||
### Following the CISS Bash naming and ordering scheme:
|
||||
### Updating Status of Dialog Gauge Bar.
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nPre-Scan Secure-Boot ... \nXXX\n30\n" >&3; fi
|
||||
### PRE-SCAN SECURE BOOT PROFILE FOR BUILD-HOST PACKAGE CHECKS.
|
||||
### Formal validation still happens in arg_parser().
|
||||
for ((idx=0; idx<${#ARY_PARAM_ARRAY[@]}; idx++)); do
|
||||
case "${ARY_PARAM_ARRAY[idx],,}" in
|
||||
--secure-boot-profile=*)
|
||||
declare -gx VAR_CISS_SECUREBOOT_PROFILE="${ARY_PARAM_ARRAY[idx]#*=}"
|
||||
;;
|
||||
--secure-boot-profile)
|
||||
if [[ -n "${ARY_PARAM_ARRAY[idx + 1]:-}" ]]; then
|
||||
declare -gx VAR_CISS_SECUREBOOT_PROFILE="${ARY_PARAM_ARRAY[idx + 1]}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
unset idx
|
||||
|
||||
### Updating Status of Dialog Gauge Bar.
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nTraps active ... \nXXX\n50\n" >&3; fi
|
||||
trap 'trap_on_exit "$?" "${BASH_SOURCE[0]}" "${LINENO}" "${FUNCNAME[0]:-main}" "${BASH_COMMAND}"' EXIT
|
||||
trap 'trap_on_err "$?" "${BASH_SOURCE[0]}" "${LINENO}" "${FUNCNAME[0]:-main}" "${BASH_COMMAND}"' ERR
|
||||
|
||||
@@ -234,9 +243,13 @@ declare -ar ARY_ARG_SANITIZED=("$@")
|
||||
declare -grx VAR_ARG_SANITIZED="${ARY_ARG_SANITIZED[*]}"
|
||||
|
||||
### Updating Status of Dialog Gauge Bar.
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nParsing Arguments ... \nXXX\n90\n" >&3; fi
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nParsing Arguments ... \nXXX\n80\n" >&3; fi
|
||||
arg_parser "$@"
|
||||
|
||||
### Updating Status of Dialog Gauge Bar.
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nValidate secret Arguments ... \nXXX\n95\n" >&3; fi
|
||||
validate_selected_secret_inputs
|
||||
|
||||
### Updating Status of Dialog Gauge Bar.
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nFinal checks ... \nXXX\n95\n" >&3; fi
|
||||
clean_ip
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
# shellcheck disable=SC2154
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-CreationInfo: 2025-10-11; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||||
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git
|
||||
@@ -9,90 +10,18 @@
|
||||
# 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
|
||||
# shellcheck disable=SC2154
|
||||
set -Ceuo pipefail
|
||||
|
||||
# Final live-build binary hook for encrypted root filesystem packaging. Preallocate a LUKS2 container, format it with the
|
||||
# generated build secret, copy the generated filesystem.squashfs into the opened encrypted mapping, generate and sign a
|
||||
# SHA-512 attestation manifest for the complete decrypted mapper, then close the container, shred the temporary LUKS secret,
|
||||
# and remove the plaintext SquashFS from the ISO payload.
|
||||
# Final live-build binary hook for encrypted root filesystem packaging. It creates and signs a deterministic attestation
|
||||
# manifest for the final filesystem.squashfs byte stream, preallocates a LUKS2 container, formats it with the generated build
|
||||
# secret, copies the generated filesystem.squashfs into the opened encrypted mapping, then closes the container, shreds the
|
||||
# temporary LUKS secret, and removes the plaintext SquashFS from the ISO payload.
|
||||
|
||||
printf "\e[95m🧪 '%s' starting ... \e[0m\n" "${0}"
|
||||
|
||||
__umask=$(umask)
|
||||
umask 0077
|
||||
|
||||
#######################################
|
||||
# Prints a fatal error message and terminates the hook.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Error message
|
||||
# Returns:
|
||||
# 42: always exits with failure
|
||||
#######################################
|
||||
die() {
|
||||
declare message="${1}"
|
||||
printf "\e[91m❌ %s \e[0m\n" "${message}" >&2
|
||||
exit 42
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Checks whether a required command exists.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Command name
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# 42: if the command is missing
|
||||
#######################################
|
||||
require_command() {
|
||||
declare command_name="${1}"
|
||||
|
||||
command -v "${command_name}" >/dev/null 2>&1 || die "Required command not found: '${command_name}'."
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Checks whether a required file exists and is readable.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: File path
|
||||
# 2: Human-readable file description
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# 42: if the file is missing or unreadable
|
||||
#######################################
|
||||
require_file() {
|
||||
declare file_path="${1}"
|
||||
declare description="${2}"
|
||||
|
||||
[[ -f "${file_path}" && -r "${file_path}" ]] || die "Missing or unreadable ${description}: '${file_path}'."
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Checks whether a required environment variable is non-empty.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Variable name
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# 42: if the variable is empty or unset
|
||||
#######################################
|
||||
require_variable() {
|
||||
declare variable_name="${1}"
|
||||
|
||||
[[ -n "${!variable_name:-}" ]] || die "Required environment variable is empty or unset: '${variable_name}'."
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Pre allocates space for LUKS container.
|
||||
# Globals:
|
||||
@@ -136,29 +65,120 @@ preallocate() {
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f preallocate
|
||||
|
||||
declare ROOTFS="${VAR_HANDLER_BUILD_DIR}/binary/live/filesystem.squashfs"
|
||||
#######################################
|
||||
# Create and sign the rootfs attestation manifest for the exact SquashFS payload copied into the LUKS mapper.
|
||||
# Globals:
|
||||
# VAR_SIGNING_KEY_FPR
|
||||
# VAR_SIGNING_KEY_PASSFILE
|
||||
# VAR_VERIFY_KEYRING
|
||||
# Arguments:
|
||||
# 1: Final SquashFS payload file
|
||||
# 2: Manifest path below binary/live
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# 42: on failure
|
||||
#######################################
|
||||
create_attestation() {
|
||||
declare rootfs_file="$1"
|
||||
declare rootfs_attestation="$2"
|
||||
declare rootfs_hash=""
|
||||
declare rootfs_size=""
|
||||
|
||||
|
||||
rootfs_size="$(stat -c%s -- "${rootfs_file}")"
|
||||
rootfs_hash="$(sha512sum "${rootfs_file}")"
|
||||
rootfs_hash="${rootfs_hash%% *}"
|
||||
|
||||
# The attested boundary is the final SquashFS byte stream before LUKS wrapping. The boot verifier reads exactly this many
|
||||
# bytes from the decrypted mapper and intentionally excludes the LUKS allocation slack after the SquashFS payload.
|
||||
cat << EOF >| "${rootfs_attestation}"
|
||||
# CISS rootfs attestation manifest v1
|
||||
# boundary: final filesystem.squashfs byte stream copied into /dev/mapper/crypt_liveiso
|
||||
# rootfs-size-bytes: ${rootfs_size}
|
||||
${rootfs_hash} ciss-rootfs.squashfs
|
||||
EOF
|
||||
chmod 0444 "${rootfs_attestation}"
|
||||
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase-file "${VAR_SIGNING_KEY_PASSFILE}" --local-user "${VAR_SIGNING_KEY_FPR}" \
|
||||
--detach-sign --output "${rootfs_attestation}.sig" "${rootfs_attestation}"
|
||||
chmod 0444 "${rootfs_attestation}.sig"
|
||||
|
||||
gpgv --keyring "${VAR_VERIFY_KEYRING}" "${rootfs_attestation}.sig" "${rootfs_attestation}"
|
||||
|
||||
printf "\e[92m[INFO] Rootfs attestation manifest created and verified: [%s]. \e[0m\n" "${rootfs_attestation}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f create_attestation
|
||||
|
||||
declare LUKSFS="${VAR_HANDLER_BUILD_DIR}/binary/live/ciss_rootfs.crypt"
|
||||
declare MAPPER_DEV="/dev/mapper/crypt_liveiso"
|
||||
declare ROOTFS_ATTESTATION="${LUKSFS}.decrypted.sha512sum.txt"
|
||||
declare ROOTFS_ATTESTATION_SIG="${ROOTFS_ATTESTATION}.sig"
|
||||
declare ROOTFS="${VAR_HANDLER_BUILD_DIR}/binary/live/filesystem.squashfs"
|
||||
declare ROOTFS_ATTESTATION="${VAR_HANDLER_BUILD_DIR}/binary/live/filesystem.squashfs.sha512sum.txt"
|
||||
declare DM_LAB="crypt_liveiso"
|
||||
declare DEVMAP="/dev/mapper/${DM_LAB}"
|
||||
declare LUKS_KEY_FILE="${VAR_TMP_SECRET}/${VAR_LUKS_KEY:-luks.txt}"
|
||||
declare KEYFD=""
|
||||
|
||||
require_command gpg
|
||||
require_command gpgv
|
||||
require_command sha512sum
|
||||
require_file "${ROOTFS}" "final SquashFS payload"
|
||||
require_variable VAR_SIGNING_KEY_FPR
|
||||
require_variable VAR_SIGNING_KEY_PASSFILE
|
||||
require_variable VAR_VERIFY_KEYRING
|
||||
require_file "${VAR_SIGNING_KEY_PASSFILE}" "GPG signing passphrase file"
|
||||
require_file "${VAR_VERIFY_KEYRING}" "GPG verification keyring"
|
||||
|
||||
[[ "${VAR_SIGNER:-false}" == "true" ]] || die "Rootfs attestation requires an enabled artifact signer."
|
||||
|
||||
rm -f -- "${ROOTFS_ATTESTATION}" "${ROOTFS_ATTESTATION_SIG}"
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
declare -i VAR_ROOTFS_SIZE=$(stat -c%s -- "${ROOTFS}")
|
||||
declare -i VAR_ROOTFS_SIZE="$(stat -c%s -- "${ROOTFS}")"
|
||||
# shellcheck disable=SC2155
|
||||
declare VAR_ROOTFS_HASH="$(LC_ALL=C sha512sum "${ROOTFS}")"
|
||||
declare VAR_ROOTFS_HASH="${VAR_ROOTFS_HASH%% *}"
|
||||
|
||||
### Attestation Boundary
|
||||
# - The attested boundary is the final SquashFS byte stream before LUKS wrapping.
|
||||
# - The boot verifier reads exactly this many bytes from the decrypted mapper and intentionally excludes the LUKS allocation
|
||||
# slack after the SquashFS payload.
|
||||
printf "\e[95m🧪 Attestation of filesystem.squashfs ... \e[0m\n"
|
||||
|
||||
cat << EOF >| "${ROOTFS_ATTESTATION}"
|
||||
# CISS.debian.live.builder Master ${VAR_VERSION}
|
||||
# Attestation file for filesystem.squashfs Version 1.0.0
|
||||
# Boundary : Final filesystem.squashfs byte stream copied into /dev/mapper/crypt_liveiso
|
||||
# Bytes : Final filesystem.squashfs ${VAR_ROOTFS_SIZE}
|
||||
${VAR_ROOTFS_HASH} filesystem.squashfs
|
||||
EOF
|
||||
|
||||
chmod 0444 "${ROOTFS_ATTESTATION}"
|
||||
|
||||
if gpg --batch --yes --pinentry-mode loopback --passphrase-file "${VAR_SIGNING_KEY_PASSFILE}" --local-user "${VAR_SIGNING_KEY_FPR}" \
|
||||
--detach-sign --output "${ROOTFS_ATTESTATION}.sig" "${ROOTFS_ATTESTATION}"; then
|
||||
|
||||
printf "\e[92m✅ [gpg of %s] successful. \e[0m\n" "${ROOTFS_ATTESTATION}"
|
||||
|
||||
else
|
||||
|
||||
printf "\e[91m❌ [gpg of %s] NOT successful. \e[0m\n" "${ROOTFS_ATTESTATION}"
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
chmod 0444 "${ROOTFS_ATTESTATION}.sig"
|
||||
|
||||
if gpgv --keyring "${VAR_VERIFY_KEYRING}" "${ROOTFS_ATTESTATION}.sig" "${ROOTFS_ATTESTATION}"; then
|
||||
|
||||
printf "\e[92m✅ [gpgv of %s] successful. \e[0m\n" "${ROOTFS_ATTESTATION}.sig"
|
||||
|
||||
else
|
||||
|
||||
printf "\e[91m❌ [gpgv of %s] NOT successful. \e[0m\n" "${ROOTFS_ATTESTATION}.sig"
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
if LC_ALL=C sha512sum -c --strict --quiet "${ROOTFS_ATTESTATION}"; then
|
||||
|
||||
printf "\e[92m✅ [LC_ALL=C sha512sum -c --strict --quiet of %s] successful. \e[0m\n" "${ROOTFS_ATTESTATION}"
|
||||
|
||||
else
|
||||
|
||||
printf "\e[91m❌ [LC_ALL=C sha512sum -c --strict --quiet of %s] NOT successful. \e[0m\n" "${ROOTFS_ATTESTATION}"
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
printf "\e[92m✅ Attestation of filesystem.squashfs successful. \e[0m\n"
|
||||
|
||||
### Safety margin:
|
||||
# - LUKS2-Header and Metadata
|
||||
@@ -172,7 +192,7 @@ declare -i VAR_LUKSFS_SIZE=$(( ( (BASE_SIZE + ALIGN_BYTES - 1) / ALIGN_BYTES ) *
|
||||
|
||||
preallocate "${LUKSFS}" "${VAR_LUKSFS_SIZE}"
|
||||
|
||||
exec {KEYFD}<"${VAR_TMP_SECRET}/luks.txt"
|
||||
exec {KEYFD}<"${LUKS_KEY_FILE}"
|
||||
|
||||
if [[ "${VAR_CDLB_INSIDE_RUNNER}" == "false" ]]; then
|
||||
|
||||
@@ -183,7 +203,7 @@ if [[ "${VAR_CDLB_INSIDE_RUNNER}" == "false" ]]; then
|
||||
--iter-time 1000 \
|
||||
--key-file "/proc/$$/fd/${KEYFD}" \
|
||||
--key-size 512 \
|
||||
--label crypt_liveiso \
|
||||
--label "${DM_LAB}" \
|
||||
--luks2-keyslots-size 16777216 \
|
||||
--luks2-metadata-size 4194304 \
|
||||
--pbkdf argon2id \
|
||||
@@ -198,10 +218,11 @@ elif [[ "${VAR_CDLB_INSIDE_RUNNER}" == "true" ]]; then
|
||||
cryptsetup luksFormat \
|
||||
--batch-mode \
|
||||
--cipher aes-xts-plain64 \
|
||||
--integrity hmac-sha512 \
|
||||
--iter-time 1000 \
|
||||
--key-file "/proc/$$/fd/${KEYFD}" \
|
||||
--key-size 512 \
|
||||
--label crypt_liveiso \
|
||||
--label "${DM_LAB}" \
|
||||
--luks2-keyslots-size 16777216 \
|
||||
--luks2-metadata-size 4194304 \
|
||||
--pbkdf argon2id \
|
||||
@@ -213,10 +234,10 @@ elif [[ "${VAR_CDLB_INSIDE_RUNNER}" == "true" ]]; then
|
||||
|
||||
fi
|
||||
|
||||
cryptsetup open --key-file "/proc/$$/fd/${KEYFD}" "${LUKSFS}" crypt_liveiso
|
||||
cryptsetup open --key-file "/proc/$$/fd/${KEYFD}" "${LUKSFS}" "${DM_LAB}"
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
declare -i LUKS_FREE=$(blockdev --getsize64 "${MAPPER_DEV}")
|
||||
declare -i LUKS_FREE=$(blockdev --getsize64 "${DEVMAP}")
|
||||
declare -i SQUASH_FS="${VAR_ROOTFS_SIZE}"
|
||||
|
||||
if (( LUKS_FREE >= SQUASH_FS )); then
|
||||
@@ -230,27 +251,13 @@ else
|
||||
|
||||
fi
|
||||
|
||||
dd if="${ROOTFS}" of="${MAPPER_DEV}" bs=8M status=progress conv=fsync
|
||||
dd if="${ROOTFS}" of="${DEVMAP}" bs=8M status=progress conv=fsync
|
||||
sync
|
||||
|
||||
# The selected boot root is the complete decrypted mapper. Hashing this exact block payload binds the signed manifest to the
|
||||
# bytes later mounted as SquashFS, including the mapper padding after the SquashFS image.
|
||||
LC_ALL=C sha512sum "${MAPPER_DEV}" >| "${ROOTFS_ATTESTATION}"
|
||||
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase-file "${VAR_SIGNING_KEY_PASSFILE}" --local-user "${VAR_SIGNING_KEY_FPR}" \
|
||||
--detach-sign --output "${ROOTFS_ATTESTATION_SIG}" "${ROOTFS_ATTESTATION}"
|
||||
|
||||
gpgv --keyring "${VAR_VERIFY_KEYRING}" "${ROOTFS_ATTESTATION_SIG}" "${ROOTFS_ATTESTATION}"
|
||||
|
||||
(cd / && LC_ALL=C sha512sum -c --strict --quiet "${ROOTFS_ATTESTATION}")
|
||||
|
||||
chmod 0444 "${ROOTFS_ATTESTATION}" "${ROOTFS_ATTESTATION_SIG}"
|
||||
|
||||
cryptsetup close crypt_liveiso
|
||||
cryptsetup close "${DM_LAB}"
|
||||
|
||||
exec {KEYFD}<&-
|
||||
|
||||
shred -fzu -n 5 -- "${VAR_TMP_SECRET}/luks.txt"
|
||||
shred -fzu -n 5 -- "${LUKS_KEY_FILE}"
|
||||
|
||||
rm -f -- "${ROOTFS}"
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
[git.coresecret.dev]:42842 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQA107AVmg1D/jnyXiqbPf38zQRl8s3c+PM1zbfpeQl
|
||||
[git.coresecret.dev]:42842 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDYD9ysmMWZlejUnxu0qOzeWcIYezoFLbYdo6ffGUL5kqOBAYb+5CF4bJLUpA93XFYVF+TbrcMV1yJh6JaHFL0VU5CvgAzruCeedx0c4qUV6lWcJUGNk5K0yb9n2Wosdy6F/zTOxL9KXBt/TV+cscsen2Dahvx0ctMKgNbu+vvUcWxHf9lOkbYoF/uA/nW5CVXy5XUPVUDFUhEeKXL85+6gid5AEMfYT8aRl5YDGvo1iMBmBYOljN4S7MnRe14qbAZG0GDGvF22eHbSU2pILcFIjc2Lo/S5Ox/MJpbLAqpFlLPTKgr6F7yVwfNMSNwl05ysUOZfrQKSXzCU6+lfqKYCwemLALyG/n1ernpp7/8W/2RYoz3fd+TQyfhW++rx3yUHpYCkTv9A4LRYZYGSAWKMHSBEYq3EcATQUxQi0xpwmcR+u0uC9F9eta5Bim+sBZD6F2hgPJ5xgYT8LFm880g1YadAwBoD4TAkqSvl+jYW0VA2GH9CknKHJ36gc/X4eeUHDC1Hf/E8M5RBj4D6NuHfeVRik/ahHmoCqKQUW7VU/EBsWFsngDiLEHcV71iMtWiUddWOHwoAPHIzn6p9HTeLCxTwsPMG5UDGK/S9HUozqDXxexRtqbcFa7DWuzRvZ1bcZ2VQsaafuzKCkkc4NjC7h1wssel7q9aeYPFg+1vS6Q==
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
### https://www.ssh-audit.com/
|
||||
### ssh -Q cipher | cipher-auth | compression | kex | kex-gss | key | key-cert | key-plain | key-sig | mac | protocol-version | sig
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Version Master V9.14.022.2026.06.10
|
||||
# Version Master V9.14.024.2026.06.11
|
||||
|
||||
### https://docs.kernel.org/
|
||||
### https://github.com/a13xp0p0v/kernel-hardening-checker/
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
declare -gr VERSION="Master V9.14.022.2026.06.10"
|
||||
declare -gr VERSION="Master V9.14.024.2026.06.11"
|
||||
|
||||
### VERY EARLY CHECK FOR DEBUGGING
|
||||
if [[ $* == *" --debug "* ]]; then
|
||||
|
||||
@@ -112,4 +112,4 @@ d-i preseed/late_command string sh /preseed/.ash/3_di_preseed_late_command.sh
|
||||
|
||||
# Please consider donating to my work at: https://coresecret.eu/spenden/
|
||||
###########################################################################################
|
||||
# Written by: ./preseed_hash_generator.sh Version: Master V9.14.022.2026.06.10 at: 10:18:37.9542
|
||||
# Written by: ./preseed_hash_generator.sh Version: Master V9.14.024.2026.06.11 at: 10:18:37.9542
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Module summary:
|
||||
# This live-boot component implements the verify-checksums mode for the mounted live medium.
|
||||
# It reads the live-boot command line to decide whether checksum verification is enabled and which digests to accept.
|
||||
# It locates the pinned CISS GPG key material on the live medium, optionally verifies this script's signed hash,
|
||||
# optionally verifies signed checksum files, and checks the first matching checksum manifest with the matching digest tool. It
|
||||
# writes detailed checksum output to the verification TTY. It panics instead of continuing boot when integrity or
|
||||
# authenticity verification fails.
|
||||
# This live-boot component implements verify-checksums mode for the mounted live medium.
|
||||
# It reads the live-boot command line to decide whether checksum verification is enabled, which digests to accept, and
|
||||
# whether checksum signature verification is required. When signature verification is enabled, it requires to be pinned CISS GPG
|
||||
# key material from the live medium, verifies this script's signed SHA-512 hash, and verifies the selected checksum manifest
|
||||
# signature before accepting checksum results. It checks the first supported checksum manifest with an available matching digest
|
||||
# tool and writes detailed checksum command output to the verification TTY when checksum execution runs. It fails closed by
|
||||
# panicking on missing manifests, missing digest tools, empty manifests, failed signatures, failed checksums, or unknown
|
||||
# verification states.
|
||||
|
||||
### Modified Version of the original file:
|
||||
### https://salsa.debian.org/live-team/live-boot 'components/0030-ciss-verify-checksums'
|
||||
@@ -87,6 +89,8 @@ Verify_checksums() {
|
||||
|
||||
_CHECKSUM_LOG=""
|
||||
|
||||
_CHECKSUM_LOG_DIR="${LIVE_VERIFY_CHECKSUMS_LOG_DIR:-/run}"
|
||||
|
||||
_KEYFILE=""
|
||||
|
||||
_MANIFEST_FOUND="false"
|
||||
@@ -97,6 +101,8 @@ Verify_checksums() {
|
||||
|
||||
_RETURN_SHA=""
|
||||
|
||||
_TOOL_FOUND="false"
|
||||
|
||||
_VERIFICATION_EXECUTED="false"
|
||||
|
||||
_VERIFICATION_SUCCEEDED="false"
|
||||
@@ -159,6 +165,14 @@ Verify_checksums() {
|
||||
|
||||
done
|
||||
|
||||
if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ] && [ -z "${_KEYFILE}" ]; then
|
||||
|
||||
log_er "No pinned GPG key file found while checksum signature verification is enabled."
|
||||
sleep 8
|
||||
panic "[FATAL] No pinned GPG key file found while checksum signature verification is enabled."
|
||||
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2164
|
||||
cd "${_MOUNTPOINT}"
|
||||
|
||||
@@ -256,11 +270,13 @@ Verify_checksums() {
|
||||
if [ -e "${_CHECKSUM}" ]; then
|
||||
|
||||
_MANIFEST_FOUND="true"
|
||||
|
||||
log_in "Found: [${_CHECKSUM}] ..."
|
||||
|
||||
if [ -e "/usr/bin/${_DIGEST}sum" ]; then
|
||||
|
||||
_VERIFICATION_EXECUTED="true"
|
||||
_TOOL_FOUND="true"
|
||||
|
||||
log_in "Found: [/usr/bin/${_DIGEST}sum] ..."
|
||||
|
||||
if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then
|
||||
@@ -288,11 +304,17 @@ Verify_checksums() {
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
_CHECKSUM_LOG="/run/ciss-${_DIGEST}sum-check.log"
|
||||
if grep -v '^#' "${_CHECKSUM}" | LC_ALL=C /usr/bin/"${_DIGEST}"sum -c > "${_CHECKSUM_LOG}" 2>&1; then
|
||||
_CHECKSUM_LOG="${_CHECKSUM_LOG_DIR}/ciss-${_DIGEST}sum-check.log"
|
||||
_VERIFICATION_EXECUTED="true"
|
||||
if ! grep -v '^#' "${_CHECKSUM}" | grep -q '[^[:space:]]'; then
|
||||
|
||||
_RETURN_SHA="254"
|
||||
: > "${_CHECKSUM_LOG}"
|
||||
log_er "Checksum manifest has no checksum entries: [${_CHECKSUM}]"
|
||||
|
||||
elif grep -v '^#' "${_CHECKSUM}" | LC_ALL=C /usr/bin/"${_DIGEST}"sum -c > "${_CHECKSUM_LOG}" 2>&1; then
|
||||
|
||||
_RETURN_SHA="${?}"
|
||||
_VERIFICATION_SUCCEEDED="true"
|
||||
cat "${_CHECKSUM_LOG}" > "${_TTY}"
|
||||
log_ok "Found: [/usr/bin/${_DIGEST}sum] successful verified: [${_CHECKSUM}]"
|
||||
|
||||
@@ -308,6 +330,12 @@ Verify_checksums() {
|
||||
|
||||
fi
|
||||
|
||||
if { [ "${_RETURN_PGP}" = "0" ] || [ "${_RETURN_PGP}" = "na" ]; } && [ "${_RETURN_SHA}" = "0" ]; then
|
||||
|
||||
_VERIFICATION_SUCCEEDED="true"
|
||||
|
||||
fi
|
||||
|
||||
# Stop after the first verification.
|
||||
break 2
|
||||
|
||||
@@ -329,28 +357,31 @@ Verify_checksums() {
|
||||
|
||||
if [ "${_MANIFEST_FOUND}" != "true" ]; then
|
||||
|
||||
log_er "No supported checksum manifest found."
|
||||
log_er "No supported checksum manifest found. Checksum verification is fail-closed."
|
||||
sleep 8
|
||||
panic "No supported checksum manifest found."
|
||||
return 1
|
||||
panic "[FATAL] No supported checksum manifest found. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_TOOL_FOUND}" != "true" ]; then
|
||||
|
||||
log_er "Checksum manifest found, but no supported checksum tool is available. Checksum verification is fail-closed."
|
||||
sleep 8
|
||||
panic "[FATAL] Checksum manifest found, but no supported checksum tool is available. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_VERIFICATION_EXECUTED}" != "true" ]; then
|
||||
|
||||
log_er "No supported checksum verification tool was available."
|
||||
log_er "Checksum verification was not executed. Checksum verification is fail-closed."
|
||||
sleep 8
|
||||
panic "No supported checksum verification tool was available."
|
||||
return 1
|
||||
panic "[FATAL] Checksum verification was not executed. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_VERIFICATION_SUCCEEDED}" != "true" ]; then
|
||||
|
||||
log_er "No supported checksum manifest was verified successfully."
|
||||
sleep 8
|
||||
panic "No supported checksum manifest was verified successfully."
|
||||
return 1
|
||||
log_er "[FATAL] Checksum verification did not complete successfully. Evaluating fail-closed failure state."
|
||||
|
||||
fi
|
||||
|
||||
@@ -372,28 +403,24 @@ Verify_checksums() {
|
||||
log_er "Verification of [GPG signature] file successful, while verification of [sha checksum] file failed."
|
||||
sleep 8
|
||||
panic "Verification of [GPG signature] file successful, while verification of [sha checksum] file failed."
|
||||
return 1
|
||||
;;
|
||||
|
||||
*",0")
|
||||
log_er "Verification of [GPG signature] file failed, while verification of [sha checksum] file successful."
|
||||
sleep 8
|
||||
panic "Verification of [GPG signature] file failed, while verification of [sha checksum] file successful."
|
||||
return 1
|
||||
;;
|
||||
|
||||
"na,"*)
|
||||
log_er "Verification of [sha checksum] file failed."
|
||||
sleep 8
|
||||
panic "Verification of [sha checksum] file failed."
|
||||
return 1
|
||||
;;
|
||||
|
||||
*)
|
||||
log_er "Checksum verification ended in an unsupported state."
|
||||
log_er "Unknown checksum verification state: [${_RETURN_PGP:-unset},${_RETURN_SHA:-unset}]."
|
||||
sleep 8
|
||||
panic "Checksum verification ended in an unsupported state."
|
||||
return 1
|
||||
panic "[FATAL] Unknown checksum verification state. Checksum verification is fail-closed."
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
@@ -15,16 +15,28 @@
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
|
||||
# Module summary:
|
||||
# - Runs after the encrypted live root filesystem has been decrypted and selected for the SquashFS root mount.
|
||||
# - Requires the pinned public key and the signed decrypted-mapper SHA-512 manifest from the mounted live medium.
|
||||
# - Verifies the manifest signature and pinned signer fingerprint, then verifies the complete selected decrypted mapper against
|
||||
# the manifest.
|
||||
# - Panics on missing, malformed, mismatched, or unverifiable evidence.
|
||||
# - Runs after the encrypted live root filesystem has been decrypted.
|
||||
# - Requires the pinned public key, external rootfs attestation manifest, and detached signature to exist as readable,
|
||||
# non-empty regular files.
|
||||
# - Verifies the attestation signature with gpgv against the pinned key material and expected signer fingerprint.
|
||||
# - Verifies the exact final SquashFS byte stream copied into the decrypted LUKS mapper. The signed manifest provides both the
|
||||
# SHA-512 digest and the exact byte length; allocation slack after that SquashFS payload is intentionally out of scope.
|
||||
# - Panics on missing, malformed, unauthentic, or mismatched evidence.
|
||||
|
||||
set -eu
|
||||
|
||||
printf "\e[95m[INFO] Starting : [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] \n\e[0m"
|
||||
|
||||
### Check panic command availability -------------------------------------------------------------------------------------------
|
||||
if ! command -v panic >/dev/null 2>&1; then
|
||||
|
||||
panic() {
|
||||
printf '\e[91m[FATAL] %s \n\e[0m' "${*}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
fi
|
||||
|
||||
### Declare variables ----------------------------------------------------------------------------------------------------------
|
||||
|
||||
### Will be replaced at build time:
|
||||
@@ -33,13 +45,13 @@ export CDLB_EXP_CA_FPR="@EXP_CA_FPR@"
|
||||
|
||||
### Name of the top-level dm-crypt mapping (e.g., cryptsetup --label): zzzz_ciss_crypt_squash.hook.binary ----------------------
|
||||
export CDLB_MAPPER_NAME="${CDLB_MAPPER_NAME:-crypt_liveiso}"
|
||||
export CDLB_MAPPER_DEV="${CDLB_MAPPER_DEV:-/dev/mapper/${CDLB_MAPPER_NAME}}"
|
||||
export CDLB_MNT_MEDIUM="${CDLB_MNT_MEDIUM:-/run/live/medium}"
|
||||
|
||||
### Rootfs selection and attestation file locations. ---------------------------------------------------------------------------
|
||||
CDLB_LUKS_FS="${CDLB_LUKS_FS:-/live/ciss_rootfs.crypt}"
|
||||
CDLB_MAPPER_DEV="${CDLB_MAPPER_DEV:-/dev/mapper/${CDLB_MAPPER_NAME}}"
|
||||
CDLB_MNT_MEDIUM="${CDLB_MNT_MEDIUM:-/run/live/medium}"
|
||||
CDLB_ATTEST_ROOTFS_SHA="${CDLB_ATTEST_ROOTFS_SHA:-${CDLB_MNT_MEDIUM}${CDLB_LUKS_FS}.decrypted.sha512sum.txt}"
|
||||
CDLB_ATTEST_ROOTFS_SIG="${CDLB_ATTEST_ROOTFS_SIG:-${CDLB_ATTEST_ROOTFS_SHA}.sig}"
|
||||
### Locations of the attestation file of filesystem.squashfs on the verified live medium. --------------------------------------
|
||||
CDLB_ROOTFS_ATTEST_MANIFEST="${CDLB_ROOTFS_ATTEST_MANIFEST:-${CDLB_MNT_MEDIUM}/live/filesystem.squashfs.sha512sum.txt}"
|
||||
CDLB_ROOTFS_ATTEST_SIGNATURE="${CDLB_ROOTFS_ATTEST_SIGNATURE:-${CDLB_ROOTFS_ATTEST_MANIFEST}.sig}"
|
||||
CDLB_ROOTFS_ATTEST_CHECK="${CDLB_ROOTFS_ATTEST_CHECK:-/run/ciss-rootfs-attestation.sha512sum}"
|
||||
CDLB_KEY_DIR="${CDLB_KEY_DIR:-/etc/ciss/keys}"
|
||||
|
||||
### Declare functions ----------------------------------------------------------------------------------------------------------
|
||||
@@ -91,13 +103,11 @@ require_attestation_file() {
|
||||
|
||||
log_er "0042() : ${artifact_label} is a broken symlink, not a regular file: [${artifact_path}]"
|
||||
panic "0042() : ${artifact_label} is a broken symlink, not a regular file: [${artifact_path}]"
|
||||
return 1
|
||||
|
||||
fi
|
||||
|
||||
log_er "0042() : ${artifact_label} missing: [${artifact_path}]"
|
||||
panic "0042() : ${artifact_label} missing: [${artifact_path}]"
|
||||
return 1
|
||||
|
||||
fi
|
||||
|
||||
@@ -105,7 +115,6 @@ require_attestation_file() {
|
||||
|
||||
log_er "0042() : ${artifact_label} is not a regular file: [${artifact_path}]"
|
||||
panic "0042() : ${artifact_label} is not a regular file: [${artifact_path}]"
|
||||
return 1
|
||||
|
||||
fi
|
||||
|
||||
@@ -113,7 +122,6 @@ require_attestation_file() {
|
||||
|
||||
log_er "0042() : ${artifact_label} is empty: [${artifact_path}]"
|
||||
panic "0042() : ${artifact_label} is empty: [${artifact_path}]"
|
||||
return 1
|
||||
|
||||
fi
|
||||
|
||||
@@ -121,7 +129,6 @@ require_attestation_file() {
|
||||
|
||||
log_er "0042() : ${artifact_label} is not readable: [${artifact_path}]"
|
||||
panic "0042() : ${artifact_label} is not readable: [${artifact_path}]"
|
||||
return 1
|
||||
|
||||
fi
|
||||
|
||||
@@ -129,48 +136,163 @@ require_attestation_file() {
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Validate the selected decrypted rootfs payload.
|
||||
# Validate the decrypted rootfs payload device.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Absolute payload path
|
||||
# 1: Absolute payload device path
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
require_rootfs_payload() {
|
||||
payload_path="${1}"
|
||||
require_rootfs_payload_device() {
|
||||
artifact_path="${1}"
|
||||
|
||||
if [ ! -b "${payload_path}" ]; then
|
||||
if [ ! -e "${artifact_path}" ]; then
|
||||
|
||||
log_er "0042() : Selected rootfs payload is not a block device: [${payload_path}]"
|
||||
panic "0042() : Selected rootfs payload is not a block device: [${payload_path}]"
|
||||
return 1
|
||||
log_er "0042() : Rootfs payload device missing: [${artifact_path}]"
|
||||
panic "0042() : Rootfs payload device missing: [${artifact_path}]"
|
||||
|
||||
fi
|
||||
|
||||
if [ ! -r "${payload_path}" ]; then
|
||||
if [ -L "${artifact_path}" ] || { [ ! -b "${artifact_path}" ] && [ ! -f "${artifact_path}" ]; }; then
|
||||
|
||||
log_er "0042() : Selected rootfs payload is not readable: [${payload_path}]"
|
||||
panic "0042() : Selected rootfs payload is not readable: [${payload_path}]"
|
||||
return 1
|
||||
log_er "0042() : Rootfs payload must be a block device or regular test fixture: [${artifact_path}]"
|
||||
panic "0042() : Rootfs payload must be a block device or regular test fixture: [${artifact_path}]"
|
||||
|
||||
fi
|
||||
|
||||
if [ ! -r "${artifact_path}" ]; then
|
||||
|
||||
log_er "0042() : Rootfs payload is not readable: [${artifact_path}]"
|
||||
panic "0042() : Rootfs payload is not readable: [${artifact_path}]"
|
||||
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
HASH_FILE="${CDLB_ATTEST_ROOTFS_SHA}"
|
||||
SIGN_FILE="${CDLB_ATTEST_ROOTFS_SIG}"
|
||||
#######################################
|
||||
# Stream exactly the attested SquashFS payload bytes from the decrypted mapper.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Payload device or regular test fixture
|
||||
# 2: Exact payload byte count
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
stream_rootfs_payload() {
|
||||
payload_device="${1}"
|
||||
payload_size="${2}"
|
||||
block_size=1048576
|
||||
full_blocks=$((payload_size / block_size))
|
||||
remainder=$((payload_size % block_size))
|
||||
remainder_offset=$((full_blocks * block_size))
|
||||
|
||||
if [ "${full_blocks}" -gt 0 ]; then
|
||||
|
||||
dd if="${payload_device}" bs="${block_size}" count="${full_blocks}" 2>/dev/null || return 1
|
||||
|
||||
fi
|
||||
|
||||
if [ "${remainder}" -gt 0 ]; then
|
||||
|
||||
dd if="${payload_device}" bs=1 skip="${remainder_offset}" count="${remainder}" 2>/dev/null || return 1
|
||||
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Verify the attested SquashFS payload hash against the decrypted mapper bytes.
|
||||
# Globals:
|
||||
# CDLB_ROOTFS_ATTEST_CHECK
|
||||
# Arguments:
|
||||
# 1: Manifest path
|
||||
# 2: Payload device path
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
verify_rootfs_payload() {
|
||||
manifest_path="${1}"
|
||||
payload_device="${2}"
|
||||
payload_size=""
|
||||
payload_hash=""
|
||||
|
||||
payload_size="$(awk -F': ' '/^# rootfs-size-bytes: /{print $2; exit}' "${manifest_path}")"
|
||||
payload_hash="$(awk '($0 !~ /^#/ && NF >= 2){print $1; exit}' "${manifest_path}")"
|
||||
|
||||
case "${payload_size}" in
|
||||
|
||||
""|*[!0-9]*)
|
||||
log_er "0042() : Rootfs attestation manifest has invalid payload size."
|
||||
panic "0042() : Rootfs attestation manifest has invalid payload size."
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
if [ "${payload_size}" -le 0 ]; then
|
||||
|
||||
log_er "0042() : Rootfs attestation manifest has empty payload size."
|
||||
panic "0042() : Rootfs attestation manifest has empty payload size."
|
||||
|
||||
fi
|
||||
|
||||
case "${payload_hash}" in
|
||||
|
||||
""|*[!0123456789abcdefABCDEF]*)
|
||||
log_er "0042() : Rootfs attestation manifest has invalid SHA-512 payload hash."
|
||||
panic "0042() : Rootfs attestation manifest has invalid SHA-512 payload hash."
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
if [ "${#payload_hash}" -ne 128 ]; then
|
||||
|
||||
log_er "0042() : Rootfs attestation manifest SHA-512 payload hash has invalid length."
|
||||
panic "0042() : Rootfs attestation manifest SHA-512 payload hash has invalid length."
|
||||
|
||||
fi
|
||||
|
||||
if ! printf '%s -\n' "${payload_hash}" > "${CDLB_ROOTFS_ATTEST_CHECK}"; then
|
||||
|
||||
log_er "0042() : Failed to prepare transient rootfs payload checksum file."
|
||||
panic "0042() : Failed to prepare transient rootfs payload checksum file."
|
||||
|
||||
fi
|
||||
|
||||
chmod 0600 "${CDLB_ROOTFS_ATTEST_CHECK}" 2>/dev/null || :
|
||||
|
||||
log_in "0042() : Verifying exact SquashFS payload bytes from: [${payload_device}]"
|
||||
|
||||
# stream_rootfs_payload may be evaluated in a pipeline here; sha512sum -c is the fail-closed authority for truncated or
|
||||
# tampered payload bytes.
|
||||
# shellcheck disable=SC2310
|
||||
if ! stream_rootfs_payload "${payload_device}" "${payload_size}" | /usr/bin/sha512sum -c "${CDLB_ROOTFS_ATTEST_CHECK}"; then
|
||||
|
||||
log_er "0042() : Rootfs payload SHA-512 verification failed."
|
||||
panic "0042() : Rootfs payload SHA-512 verification failed."
|
||||
|
||||
fi
|
||||
|
||||
log_ok "0042() : Rootfs payload SHA-512 verification successful."
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
HASH_FILE="${CDLB_ROOTFS_ATTEST_MANIFEST}"
|
||||
SIGN_FILE="${CDLB_ROOTFS_ATTEST_SIGNATURE}"
|
||||
KEYFILE="${CDLB_KEY_DIR}/${CDLB_EXP_FPR}.gpg"
|
||||
|
||||
require_attestation_file "Public key" "${KEYFILE}"
|
||||
require_attestation_file "Rootfs attestation manifest" "${HASH_FILE}"
|
||||
require_attestation_file "Rootfs attestation signature" "${SIGN_FILE}"
|
||||
require_rootfs_payload "${CDLB_MAPPER_DEV}"
|
||||
require_rootfs_payload_device "${CDLB_MAPPER_DEV}"
|
||||
|
||||
log_in "0042() : Verifying signed rootfs attestation manifest with pinned GPG FPR."
|
||||
log_in "0042() : Verifying rootfs attestation manifest with 'gpgv' and pinned GPG FPR."
|
||||
|
||||
if ! _STATUS="$(/usr/bin/gpgv --no-default-keyring --keyring "${KEYFILE}" --status-fd 1 "${SIGN_FILE}" "${HASH_FILE}" 2>&1)"; then
|
||||
if ! _STATUS="$(/usr/bin/gpgv --keyring "${KEYFILE}" --status-fd 1 "${SIGN_FILE}" "${HASH_FILE}" 2>&1)"; then
|
||||
|
||||
log_er "0042() : gpgv verification failed for signature: [${SIGN_FILE}]"
|
||||
|
||||
@@ -182,7 +304,6 @@ if ! _STATUS="$(/usr/bin/gpgv --no-default-keyring --keyring "${KEYFILE}" --stat
|
||||
|
||||
sleep 8
|
||||
panic "0042() : gpgv verification failed for signature: [${SIGN_FILE}]"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
@@ -198,51 +319,10 @@ else
|
||||
log_er "0042() : Signature FPR mismatch: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]"
|
||||
sleep 8
|
||||
panic "[FATAL] Signature FPR mismatch: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]."
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
_ATTEST_RECORD_COUNT="$(awk 'NF && $1 !~ /^#/ { count++ } END { print count + 0 }' "${HASH_FILE}")"
|
||||
|
||||
if [ "${_ATTEST_RECORD_COUNT}" -ne 1 ]; then
|
||||
|
||||
log_er "0042() : Rootfs attestation manifest must contain exactly one checksum record: [${HASH_FILE}]"
|
||||
sleep 8
|
||||
panic "0042() : Rootfs attestation manifest must contain exactly one checksum record: [${HASH_FILE}]"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
_ATTESTED_PAYLOAD="$(awk 'NF && $1 !~ /^#/ { print $2; exit }' "${HASH_FILE}")"
|
||||
|
||||
if [ "${_ATTESTED_PAYLOAD}" != "${CDLB_MAPPER_DEV}" ]; then
|
||||
|
||||
log_er "0042() : Rootfs attestation manifest targets [${_ATTESTED_PAYLOAD}], expected selected payload [${CDLB_MAPPER_DEV}]"
|
||||
sleep 8
|
||||
panic "0042() : Rootfs attestation manifest does not target the selected rootfs payload."
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
log_in "0042() : Verifying selected decrypted rootfs mapper content: [${CDLB_MAPPER_DEV}]"
|
||||
|
||||
if ! _CHECKSUM_STATUS="$(cd / && LC_ALL=C /usr/bin/sha512sum -c --strict --quiet "${HASH_FILE}" 2>&1)"; then
|
||||
|
||||
log_er "0042() : Rootfs payload checksum verification failed: [${CDLB_MAPPER_DEV}]"
|
||||
|
||||
if [ -n "${_CHECKSUM_STATUS}" ]; then
|
||||
|
||||
printf '%s\n' "${_CHECKSUM_STATUS}" >&2
|
||||
|
||||
fi
|
||||
|
||||
sleep 8
|
||||
panic "0042() : Rootfs payload checksum verification failed: [${CDLB_MAPPER_DEV}]"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
log_ok "0042() : Rootfs payload checksum verification successful: [${CDLB_MAPPER_DEV}]"
|
||||
verify_rootfs_payload "${HASH_FILE}" "${CDLB_MAPPER_DEV}"
|
||||
|
||||
printf "\e[92m[INFO] Successfully applied : [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] \n\e[0m"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. DNSSEC Status
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Haveged Audit on Netcup RS 2000 G11
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Lynis Audit:
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. SSH Audit by ssh-audit.com
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. TLS Audit:
|
||||
````text
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Hardened Kernel Boot Parameters
|
||||
|
||||
|
||||
+14
-2
@@ -8,10 +8,22 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Changelog
|
||||
|
||||
## V9.14.024.2026.06.11
|
||||
* **Added**: [lib_build_dir_safety.sh](../lib/lib_build_dir_safety.sh) Integrated Security Audit Finding A12
|
||||
* **Added**: [lib_debug_sanitize.sh](../lib/lib_debug_sanitize.sh) Integrated Security Audit Finding A11
|
||||
* **Added**: [lib_secret_validation.sh](../lib/lib_secret_validation.sh) Integrated Security Audit Finding A10
|
||||
* **Changed**: [zzzz_ciss_crypt_squash.hook.binary](../config/hooks/live/zzzz_ciss_crypt_squash.hook.binary) Integrated Security Audit Finding A01 + A10
|
||||
* **Changed**: [0030-ciss-verify-checksums](../config/includes.chroot/usr/lib/live/boot/0030-ciss-verify-checksums) Integrated Security Audit Finding A02
|
||||
* **Changed**: [0042_ciss_post_decrypt_attest](../config/includes.chroot/usr/lib/live/boot/0042_ciss_post_decrypt_attest) Integrated Security Audit Finding A01
|
||||
* **Changed**: [lib_ciss_upgrades_boot.sh](../lib/lib_ciss_upgrades_boot.sh) Integrated Security Audit Finding A01
|
||||
* **Changed**: [lib_lb_config_start.sh](../lib/lib_lb_config_start.sh) Integrated Security Audit Finding A12
|
||||
* **Changed**: [README.md](../README.md) Integrated Security Audit Finding A01 + A02
|
||||
* **Changed**: [lib_usage.sh](../lib/lib_usage.sh) Integrated Security Audit Finding A10
|
||||
|
||||
## 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™
|
||||
@@ -196,7 +208,7 @@ include_toc: true
|
||||
* **Added**: [lib_ciss_upgrades_build.sh](../lib/lib_ciss_upgrades_build.sh) Updates for CISS and PhysNet Primordial-Workflow™.
|
||||
* **Added**: [lib_gnupg.sh](../lib/lib_gnupg.sh) Updates for CISS and PhysNet Primordial-Workflow™.
|
||||
* **Added**: [lib_primordial.sh](../lib/lib_primordial.sh) Updates for CISS and PhysNet Primordial-Workflow™.
|
||||
* **Added**: [0030-ciss-verify-checksums](../scripts/usr/lib/live/boot/0030-ciss-verify-checksums) Unified handling via includes.chroot.
|
||||
* **Added**: [0030-ciss-verify-checksums](../.archive/0030-ciss-verify-checksums) Unified handling via includes.chroot.
|
||||
* **Bugfixes**: [linter_char_scripts.yaml](../.gitea/workflows/linter_char_scripts.yaml) - WORKFLOW_ID="${GITHUB_WORKFLOW:-linter_char_scripts.yaml}"
|
||||
* **Bugfixes**: [render-dnssec-status.yaml](../.gitea/workflows/render-dnssec-status.yaml) - WORKFLOW_ID="${GITHUB_WORKFLOW:-render-dnssec-status.yaml}"
|
||||
* **Bugfixes**: [render-dot-to-png.yaml](../.gitea/workflows/render-dot-to-png.yaml) - WORKFLOW_ID="${GITHUB_WORKFLOW:-render-dot-to-png.yaml}"
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Centurion Net - Developer Branch Overview
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Purpose
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Contributing / participating
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Credits
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Download the latest PUBLIC CISS.debian.live.ISO
|
||||
|
||||
|
||||
+11
-7
@@ -8,14 +8,14 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2.1. Usage
|
||||
````text
|
||||
CDLB(1) CISS.debian.live.builder CDLB(1)
|
||||
|
||||
CISS.debian.live.builder from https://git.coresecret.dev/msw
|
||||
Master V9.14.022.2026.06.10
|
||||
Master V9.14.024.2026.06.11
|
||||
A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
|
||||
(c) Marc S. Weidner, 2018 - 2026
|
||||
@@ -37,6 +37,7 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
|
||||
--build-directory </path/to/build_directory>
|
||||
Where the Debian Live Build Image should be generated. RECOMMENDED path: </opt/cdlb>
|
||||
Cleanup is destructive inside the exact builder-owned path and requires '.ciss-live-builder-owned'.
|
||||
MUST be provided.
|
||||
|
||||
--change-splash <STRING> one of <club | hexagon>
|
||||
@@ -57,6 +58,7 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
--debug, -d
|
||||
Enables debug logging for the main program routine. Detailed logging information are written to:
|
||||
</tmp/ciss_live_builder_1801049.log>
|
||||
After xtrace is stopped and its debug FD is closed, a final exact-value redaction pass sanitizes logs.
|
||||
|
||||
--dhcp-centurion
|
||||
If a DHCP lease is provided, the provider's name server will be overridden and the hardened, privacy-focused
|
||||
@@ -86,12 +88,12 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
|
||||
--key_age=*
|
||||
The SOPS AGE private keyring for decryption operations. Change '*' to your desired SOPS AGE key file.
|
||||
File MUST be placed in:
|
||||
This MUST be a filename only and MUST be placed in the root-owned tmpfs secret root:
|
||||
</dev/shm/cdlb_secrets>
|
||||
|
||||
--key_luks=*
|
||||
The LUKS encryption / decryption passphrase for '/'-fs-encryption. Change '*' to your desired passphrase file.
|
||||
File MUST be placed in:
|
||||
This MUST be a filename only and MUST be placed in the root-owned tmpfs secret root:
|
||||
</dev/shm/cdlb_secrets>
|
||||
|
||||
--log-statistics-only
|
||||
@@ -140,7 +142,8 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
--root-password-file </dev/shm/cdlb_secrets/password.txt>>
|
||||
Password file for 'root', if given, MUST be a string of 42 to 64 characters.
|
||||
If the argument is omitted, no further login authentication is required for the local console.
|
||||
MUST be placed in:
|
||||
The path MUST be absolute, regular, non-symlink, root-owned, and mode 0400 after normalization.
|
||||
RECOMMENDED path:
|
||||
</dev/shm/cdlb_secrets/password.txt>
|
||||
|
||||
--secure-boot-profile <STRING> one of <debian-shim | ciss-uki>
|
||||
@@ -156,7 +159,7 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
specified via '--signing_key=*'. If the keyring is protected, then provide the passphrase in its own file.
|
||||
Specify the fingerprint of the key to use via '--signing_key_fpr=*'.
|
||||
Optionally import an offline GPG CA signing public key via: '--signing_ca=*'.
|
||||
Change '*' to your desired files / fingerprint. Files MUST be placed in:
|
||||
Change '*' to your desired filename-only files / fingerprint. Files MUST be placed in:
|
||||
</dev/shm/cdlb_secrets>
|
||||
|
||||
--sshfp
|
||||
@@ -171,6 +174,7 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
|
||||
--ssh-pubkey </dev/shm/cdlb_secrets/>
|
||||
Imports the SSH Public Key from the file 'authorized_keys' into the Live ISO.
|
||||
Directory MUST be absolute, regular, non-symlink, root-owned, and not group/world-writable.
|
||||
Key file MUST be placed in:
|
||||
</dev/shm/cdlb_secrets/authorized_keys>
|
||||
|
||||
@@ -186,7 +190,7 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
💷 Please consider donating to my work at:
|
||||
🌐 https://coresecret.eu/spenden/
|
||||
|
||||
V9.14.022.2026.06.10 2026-05-17 CDLB(1)
|
||||
V9.14.024.2026.06.11 2026-05-17 CDLB(1)
|
||||
````
|
||||
|
||||
# 3. Booting
|
||||
|
||||
@@ -8,28 +8,23 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. CISS.debian.live.builder – Boot & Trust Chain (Technical Documentation)
|
||||
|
||||
**Status:** 2026-06-10<br>
|
||||
**Audience:** CICA CISO, CISS staff, technically proficient administrators<br>
|
||||
**Summary:** The **CISS.debian.live.builder** Live-ISO establishes a two-stage verification chain around the live root: an
|
||||
ISO-edge checksum-manifest check and a late attestation of the complete selected decrypted rootfs mapper. The late check verifies
|
||||
both the signed SHA-512 manifest and the mapper bytes before boot continues. `dm-integrity` separately provides sector-integrity
|
||||
protection and is not a substitute for origin-bound signature and checksum attestation. UEFI Secure Boot can use either the
|
||||
default Microsoft/Debian shim chain, or a CISS-signed UKI chain for systems that trust the CISS Secure Boot key material.<br>
|
||||
**Summary:** The **CISS.debian.live.builder** Live-ISO establishes a two-stage verification chain around the live root: an early ISO-edge check (signature and FPR pin) *before* LUKS unlock, and a late root-FS attestation *after* unlock that verifies the exact final SquashFS payload bytes copied into the decrypted LUKS mapper, reinforced by `dm-crypt (AES-XTS)` and `dm-integrity (HMAC-SHA-512)`. UEFI Secure Boot can use either the default Microsoft/Debian shim chain, or a CISS-signed UKI chain for systems that trust the CISS Secure Boot key material.<br>
|
||||
|
||||
# 3. Overview
|
||||
|
||||
* **Trust anchor:** Pinned fingerprint (FPR) of the signing key embedded at build time in initramfs hooks.
|
||||
* **Integrity & authenticity verification:**
|
||||
|
||||
1. **ISO edge:** Verify one supported checksum manifest using `gpgv`, FPR pinning, and the matching checksum tool.
|
||||
2. **Late rootfs attestation:** Verify the signed mapper manifest and the complete selected decrypted mapper
|
||||
`/dev/mapper/crypt_liveiso` with `sha512sum -c`.
|
||||
1. **Early:** Verify `sha512sum.txt` at the ISO edge using `gpgv` and FPR pin.
|
||||
2. **Late:** Verify the external rootfs attestation manifest using `gpgv` and FPR pin, then verify the exact SquashFS payload bytes from the decrypted mapper with `sha512sum -c`.
|
||||
|
||||
* **Storage-level protection:** `dm-crypt` (AES-XTS-512) and, outside runner builds, `dm-integrity` (HMAC-SHA-512, 4 KiB).
|
||||
* **Storage-level AEAD (functional):** `dm-crypt` (AES-XTS-512) and `dm-integrity` (HMAC-SHA-512, 4 KiB).
|
||||
* **Remotely unlock:** CISS hardened and build dropbear, modern primitives only, no passwords, no agent/forwarding.
|
||||
|
||||
# 3.1. Secure Boot Profiles
|
||||
@@ -57,11 +52,11 @@ private Secure Boot key names are detected in those paths before live-build chec
|
||||
| Component | Primitive / Parameter | Purpose |
|
||||
|--------------|-----------------------------------------------------------|--------------------------------------------------------|
|
||||
| LUKS2 | `aes-xts-plain64`, `--key-size 512`, `--sector-size 4096` | Confidentiality (2×256-bit XTS) |
|
||||
| dm-integrity | `hmac-sha512` (keyed), journal | Per-sector integrity inside the LUKS mapping; not origin attestation |
|
||||
| dm-integrity | `hmac-sha512` (keyed), journal | Adversary-resistant per-sector integrity, authenticity |
|
||||
| PBKDF | `argon2id`, `--iter-time 1000` ms | Key derivation, hardware-agnostic |
|
||||
| Signatures | Ed25519 or RSA-4096 (FPR pinned) | Public verifiability, non-repudiation |
|
||||
| Verification | `gpgv --no-default-keyring` | No agent dependency in initramfs |
|
||||
| Hash lists | `sha512sum` format | Deterministic ISO-edge and decrypted-mapper verification |
|
||||
| Hash lists | `sha512sum` format | Deterministic content verification |
|
||||
| Dropbear | Modern KEX/AEAD (per `localoptions.h`) | Minimal attack surface, remote unlock |
|
||||
|
||||
# 5. Diagram: CISS Live ISO Boot Flow
|
||||
@@ -102,7 +97,7 @@ flowchart TD
|
||||
LUKS e14@--> ROOT["Assemble RootFS OverlayFS"];
|
||||
ROOT e15@--> 0126["Executing 0026-ciss: Hardening early sysctls"];
|
||||
0126 e16@--> 0130["Executing 0030-ciss: Verification of authenticity and integrity via embedded and pinned GPG of ISO edge"];
|
||||
0130 e17@--> |SUCCESSFUL| 0142["Executing 0042-ciss: Attestation of RootFS"];
|
||||
0130 e17@--> |SUCCESSFUL| 0142["Executing 0042-ciss: Attestation of RootFS SquashFS payload"];
|
||||
0142 e18@--> 0145["init-bottom: stop CISS.hardened dropbear, tear down initramfs net"];
|
||||
0145 e19@--> 9050["Switching root (run-init / pivot_root)"];
|
||||
9050 e20@--> 9010["Starting /sbin/init -> systemd"];
|
||||
@@ -155,8 +150,7 @@ flowchart TD
|
||||
|
||||
```
|
||||
|
||||
**Note:** `dm-integrity` detects sector corruption within its keyed mapping. The signed SHA-512 mapper manifest independently
|
||||
binds the selected decrypted rootfs payload to the signing key. Neither property substitutes for the other.
|
||||
**Note:** Encrypt-then-MAC at the block layer (functionally AEAD-equivalent). Any manipulation ⇒ hard I/O error.
|
||||
|
||||
# 7. CISS Live ISO LUKS Build-Time Core Steps
|
||||
```sh
|
||||
@@ -178,62 +172,47 @@ cryptsetup luksFormat \
|
||||
"${LUKSFS}"
|
||||
```
|
||||
|
||||
After `filesystem.squashfs` is copied to `/dev/mapper/crypt_liveiso`, the binary hook hashes the complete decrypted mapper,
|
||||
including the mapper padding after the SquashFS image. It writes
|
||||
`binary/live/ciss_rootfs.crypt.decrypted.sha512sum.txt`, signs that manifest with the existing artifact-signing key, verifies the
|
||||
signature and checksum locally, and only then closes the mapping and removes the plaintext `filesystem.squashfs`.
|
||||
|
||||
**Signing keys:** Ed25519 and RSA-4096; **FPR pinned at build time** in hooks. Signing keys are **additionally** signed by an offline GPG Root-CA (out-of-band trust chain).
|
||||
|
||||
# 8. Early ISO-Edge Verification (CISS modified hook 0030-ciss-verify-checksums, live-bottom)
|
||||
|
||||
**Goal:** During live-boot, require one supported ISO-edge checksum manifest to be successfully verified:
|
||||
**Goal:** Before consuming any medium content, verify:
|
||||
|
||||
1. **Detached signature of `sha512sum.txt`** using `gpgv` against the embedded public key.
|
||||
2. **FPR pinning:** Parse `VALIDSIG` and require exact match with the build-time pinned FPR.
|
||||
3. **Optional:** *Script self-IA* – hash the executed hook and compare against the signed list (drift/bitrot detector).
|
||||
|
||||
Verification is fail-closed. Boot panics if no supported manifest is present, no matching checksum tool is available, checksum
|
||||
verification does not succeed, signature verification fails when requested, or verification ends in an unknown state.
|
||||
|
||||
**Core call (initramfs):**
|
||||
|
||||
```sh
|
||||
/usr/bin/gpgv --no-default-keyring --keyring "$KEYFILE" --status-fd 1 --verify sha512sum.txt.sig sha512sum.txt
|
||||
/usr/bin/gpgv --keyring "$KEYFILE" --status-fd 1 sha512sum.txt.sig sha512sum.txt
|
||||
# parse [GNUPG:] VALIDSIG ... <FPR> ...
|
||||
```
|
||||
|
||||
# 9. Late Root-FS Payload Attestation (CISS hook 0042_ciss_post_decrypt_attest, called by 9990-overlay.sh)
|
||||
# 9. Late Root-FS Attestation and dmsetup Health (CISS hook 0042_ciss_post_decrypt_attest, called by 9990-overlay.sh)
|
||||
|
||||
**Goal:** After LUKS unlock and rootfs selection, verify the authenticity of the manifest and the content of the actual selected
|
||||
decrypted rootfs payload.
|
||||
**Goal:** After LUKS unlocked, validate the **decrypted** rootfs payload selected at build time and the **actual** mapping topology.
|
||||
|
||||
* **Exact attested boundary:** the complete `/dev/mapper/crypt_liveiso` block payload selected by `9990-overlay.sh` and mounted
|
||||
read-only as SquashFS, including mapper padding after the SquashFS image.
|
||||
* **Attestation files:** `/run/live/medium/live/ciss_rootfs.crypt.decrypted.sha512sum.txt[.sig]`
|
||||
* **Key source:** `/etc/ciss/keys/<FPR>.gpg` in the initramfs; the valid signature FPR must equal the build-time pin.
|
||||
* **Manifest constraint:** exactly one checksum record is accepted, and it must target the selected mapper path.
|
||||
* **Attested boundary:** the final `binary/live/filesystem.squashfs` byte stream, immediately before it is copied into `/dev/mapper/crypt_liveiso` by `zzzz_ciss_crypt_squash.hook.binary`.
|
||||
* **Runtime verification boundary:** the first `rootfs-size-bytes` bytes read from the decrypted mapper. Any LUKS allocation slack after the SquashFS payload is intentionally excluded.
|
||||
* **Attestation files:** `/run/live/medium/live/ciss_rootfs.sha512sum.txt[.sig]`
|
||||
* **Key source:** `/etc/ciss/keys/*.gpg` (accepted only if FPR == build-pin)
|
||||
|
||||
**Core calls (initramfs):**
|
||||
|
||||
```sh
|
||||
# 1) Signature and FPR pin (no agent)
|
||||
/usr/bin/gpgv --no-default-keyring --keyring "$KEYFILE" --status-fd 1 --verify "$SIG" "$DATA"
|
||||
/usr/bin/gpgv --keyring "${KEYFILE}" --status-fd 1 "${SIG}" "${DATA}"
|
||||
|
||||
# 2) Required selected-payload verification
|
||||
( cd / && /usr/bin/sha512sum -c --strict --quiet "$DATA" )
|
||||
# 2) Mandatory content hash verification
|
||||
dd if="${CDLB_MAPPER_DEV}" ... | /usr/bin/sha512sum -c /run/ciss-rootfs-attestation.sha512sum
|
||||
```
|
||||
|
||||
The signed manifest alone is not sufficient: boot continues only after the selected decrypted mapper content matches it.
|
||||
This boundary does not individually attest the mounted file tree, OverlayFS upper layer, runtime mutations, or secrets after
|
||||
unlock.
|
||||
|
||||
# 10. Failure Policy (fail-closed, deterministic)
|
||||
|
||||
* **ISO-edge abort:** no supported manifest, no supported verification tool, failed checksum, failed requested signature, or
|
||||
unknown verification state.
|
||||
* **Rootfs-attestation abort:** missing or malformed manifest/signature/key, missing `VALIDSIG`, FPR mismatch, manifest target
|
||||
mismatch, unreadable or non-block selected mapper, or mapper checksum mismatch.
|
||||
* **Abort** on: missing checksum manifest, unsupported checksum manifest/tool state, failed checksum, empty checksum manifest, missing `VALIDSIG`, FPR mismatch, missing key/signature, malformed rootfs attestation manifest, or rootfs payload hash mismatch.
|
||||
* A signed rootfs manifest alone is not sufficient. Boot continues only after the manifest signature/FPR, and the decrypted SquashFS payload bytes both verify successfully.
|
||||
* `dm-integrity` protects the opened LUKS mapping against sector corruption or tampering under the LUKS key, but it is not treated as origin authenticity. Origin authenticity is provided by the signed rootfs attestation manifest and pinned signer fingerprint.
|
||||
|
||||
# 11. CISS hardened and built dropbear
|
||||
|
||||
@@ -276,20 +255,18 @@ flowchart TD
|
||||
subgraph ISO Build Time
|
||||
A["Embed and pin GPG FPR (into ISO & RootFS as needed)"] e00@--> B["Generate ISO-edge sha512sum.txt and .sig"];
|
||||
B e01@--> C["Build filesystem.squashfs and wrap it into ciss_rootfs.crypt"];
|
||||
C e01a@--> C2["Hash complete decrypted mapper and sign rootfs manifest"];
|
||||
e00@{ animation: fast }
|
||||
e01@{ animation: fast }
|
||||
e01a@{ animation: fast }
|
||||
end
|
||||
|
||||
subgraph ISO Boot Time
|
||||
C2 e02@--> D["0024 LUKS2, dm-integrity HMAC-SHA512"];
|
||||
C e02@--> D["0024 LUKS2, dm-integrity HMAC-SHA512"];
|
||||
D e03@-->|SUCCESSFUL| E["ciss_rootfs.crypt opened"];
|
||||
E e04@--> F["Mounting RootFS"];
|
||||
F e05@--> G["0030 verification of authenticity and integrity via embedded and pinned GPG of ISO edge"];
|
||||
G e06@-->|SUCCESSFUL| H["ISO edge verified"];
|
||||
H e07@--> I["0042 verifies signed manifest and selected decrypted mapper"];
|
||||
I e08@-->|SUCCESSFUL| J["RootFS attestation successful"];
|
||||
H e07@--> I["0042 post-decrypt-attestation of RootFS SquashFS payload"];
|
||||
I e08@-->|SUCCESSFUL| J["RootFS SquashFS payload attestation successful"];
|
||||
e02@{ animation: fast }
|
||||
e03@{ animation: fast }
|
||||
e04@{ animation: fast }
|
||||
@@ -312,9 +289,7 @@ I -- FAIL --> X;
|
||||
|
||||
# 14. Closing Remarks
|
||||
|
||||
The verification path is fail-closed and binds the selected decrypted rootfs mapper to the pinned signing key. `dm-integrity`
|
||||
adds a separate sector-integrity property where enabled. The implemented attestation does not cover runtime OverlayFS changes,
|
||||
post-unlock secrets, or an individually enumerated mounted file tree.
|
||||
This achieves a portable, self-contained trust chain without a Microsoft-db, providing strong protection against medium tampering, bitrot, and active attacks **both before and after decryption**. The dual-verification phases make the state transparent and deterministic.
|
||||
|
||||
---
|
||||
**[no tracking | no logging | no advertising | no profiling | no bullshit](https://coresecret.eu/)**
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. SSH Host Key Policy – CISS.debian.live.builder / CISS.debian.installer
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. Resources
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. ``30-ciss-hardening.conf``
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. ``90-ciss-local.hardened``
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ include_toc: true
|
||||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||||
**Master Version**: 9.14<br>
|
||||
**Build**: V9.14.022.2026.06.10<br>
|
||||
**Build**: V9.14.024.2026.06.11<br>
|
||||
|
||||
# 2. ``ciss_live_builder.sh``
|
||||
|
||||
|
||||
+59
-15
@@ -76,7 +76,6 @@ 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
|
||||
@@ -109,6 +108,16 @@ arg_parser() {
|
||||
shift 1
|
||||
;;
|
||||
|
||||
-l | --logo)
|
||||
if [[ -n "${2-}" && "${2}" != -* ]]; then
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
printf "\e[91m❌ Error: --logo 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
|
||||
@@ -134,10 +143,9 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--build-directory)
|
||||
declare -gx VAR_HANDLER_BUILD_DIR="${2}"
|
||||
if [[ ! "${VAR_HANDLER_BUILD_DIR}" =~ ^/ ]]; then
|
||||
declare -gx VAR_HANDLER_BUILD_DIR="${2-}"
|
||||
if ! validate_build_dir_argument "${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"
|
||||
@@ -266,18 +274,28 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--key_age=*)
|
||||
declare key_age="${1#*=}"
|
||||
if ! validate_secret_filename "--key_age" "${key_age}"; then
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
exit "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_AGE="true"
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_AGE_KEY="${1#*=}"
|
||||
declare -gx VAR_AGE_KEY="${key_age}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
--key_luks=*)
|
||||
declare key_luks="${1#*=}"
|
||||
if ! validate_secret_filename "--key_luks" "${key_luks}"; then
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
exit "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_LUKS="true"
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_LUKS_KEY="${1#*=}"
|
||||
declare -gx VAR_LUKS_KEY="${key_luks}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
@@ -296,7 +314,7 @@ arg_parser() {
|
||||
--primordial-key)
|
||||
declare primordial_key="${2-}"
|
||||
|
||||
if [[ -n "${primordial_key}" && "${primordial_key}" != -* && "${primordial_key}" != "." && "${primordial_key}" != ".." && "${primordial_key}" != */* && "${primordial_key}" =~ ${primordial_key_regex} ]]; then
|
||||
if validate_secret_filename "--primordial-key" "${primordial_key}"; then
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_PRIMORDIAL_KEY="${primordial_key}"
|
||||
@@ -315,7 +333,7 @@ arg_parser() {
|
||||
--primordial-key=*)
|
||||
declare primordial_key="${1#*=}"
|
||||
|
||||
if [[ -n "${primordial_key}" && "${primordial_key}" != "." && "${primordial_key}" != ".." && "${primordial_key}" != */* && "${primordial_key}" =~ ${primordial_key_regex} ]]; then
|
||||
if validate_secret_filename "--primordial-key" "${primordial_key}"; then
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_PRIMORDIAL_KEY="${primordial_key}"
|
||||
@@ -461,7 +479,7 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--root-password-file)
|
||||
declare pw_file="${2}"
|
||||
declare pw_file="${2-}"
|
||||
|
||||
if [[ -z "${pw_file}" ]]; then
|
||||
|
||||
@@ -473,10 +491,9 @@ arg_parser() {
|
||||
|
||||
fi
|
||||
|
||||
if [[ ! -f "${pw_file}" ]]; then
|
||||
if ! validate_secret_absolute_file_basics "--root-password-file" "${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}"
|
||||
@@ -507,6 +524,13 @@ arg_parser() {
|
||||
}
|
||||
fi
|
||||
|
||||
validate_secret_file_path "--root-password-file" "${pw_file}" || {
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
# shellcheck disable=SC2162
|
||||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||||
exit "${ERR_RGHT_PWD_F}"
|
||||
}
|
||||
|
||||
declare plaintext_pw
|
||||
### No tracing for security reasons ----------------------------------------------------------------------------------
|
||||
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x
|
||||
@@ -606,16 +630,26 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--signing_ca=*)
|
||||
declare signing_ca="${1#*=}"
|
||||
if ! validate_secret_filename "--signing_ca" "${signing_ca}"; then
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
exit "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_SIGNING_CA="${1#*=}"
|
||||
declare -gx VAR_SIGNING_CA="${signing_ca}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
--signing_key=*)
|
||||
declare signing_key="${1#*=}"
|
||||
if ! validate_secret_filename "--signing_key" "${signing_key}"; then
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
exit "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_SIGNER="true"
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_SIGNING_KEY="${1#*=}"
|
||||
declare -gx VAR_SIGNING_KEY="${signing_key}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
@@ -626,8 +660,13 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--signing_key_pass=*)
|
||||
declare signing_key_pass="${1#*=}"
|
||||
if ! validate_secret_filename "--signing_key_pass" "${signing_key_pass}"; then
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
exit "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_SIGNING_KEY_PASS="${1#*=}"
|
||||
declare -gx VAR_SIGNING_KEY_PASS="${signing_key_pass}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
@@ -694,8 +733,13 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--ssh-pubkey)
|
||||
declare ssh_pubkey="${2-}"
|
||||
if ! validate_ssh_pubkey_directory "${ssh_pubkey}"; then
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
exit "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
# shellcheck disable=SC2034
|
||||
declare -gx VAR_SSHPUBKEY="${2}"
|
||||
declare -gx VAR_SSHPUBKEY="${ssh_pubkey}"
|
||||
shift 2
|
||||
;;
|
||||
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
#!/bin/bash
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-CreationInfo: 2026-06-11; 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-2026; 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
|
||||
# shellcheck disable=SC2154
|
||||
|
||||
# Module overview:
|
||||
# This module centralizes build-directory safety checks for path validation, builder-ownership markers, and destructive cleanup
|
||||
# helpers. It keeps cleanup operations limited to canonical, explicitly validated build-directory paths.
|
||||
#
|
||||
# Function behavior:
|
||||
# build_dir_safety_error(): writes a scoped build-directory safety error message to stderr.
|
||||
# reject_broad_build_dir_path(): rejects the filesystem root and common top-level system directories as build targets.
|
||||
# validate_build_dir_argument(): validates a non-empty absolute build-directory argument before the path is created.
|
||||
# validate_existing_build_dir(): validates the argument and confirms that it resolves to an existing directory.
|
||||
# require_builder_owned_build_dir(): requires a validated directory with a safe root-owned builder marker.
|
||||
# ensure_builder_owned_build_dir(): creates the marker for a safe empty build directory or verifies an existing marker.
|
||||
# require_builder_owned_subpath(): confirms that a target exists strictly below a verified builder-owned directory.
|
||||
# safe_clean_build_dir_contents(): removes direct build-directory contents while preserving the builder marker.
|
||||
# safe_remove_builder_subpath(): removes one verified subpath below a builder-owned build directory.
|
||||
|
||||
guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
|
||||
#######################################
|
||||
# Print a cleanup/path safety error.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Error detail.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
build_dir_safety_error() {
|
||||
declare detail="${1}"
|
||||
|
||||
printf "\e[91m❌ build directory safety: %s \e[0m\n" "${detail}" >&2
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f build_dir_safety_error
|
||||
|
||||
#######################################
|
||||
# Reject broad parent directories as build-directory targets.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Canonical path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
reject_broad_build_dir_path() {
|
||||
declare canonical_path="${1}"
|
||||
|
||||
case "${canonical_path}" in
|
||||
"" | "/" | "/bin" | "/boot" | "/dev" | "/etc" | "/home" | "/lib" | "/lib64" | "/opt" | "/proc" | "/root" | "/run" | "/sbin" | "/sys" | "/tmp" | "/usr" | "/var")
|
||||
build_dir_safety_error "refusing broad path."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f reject_broad_build_dir_path
|
||||
|
||||
#######################################
|
||||
# Validate a build-directory argument before it is created.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Build directory path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_build_dir_argument() {
|
||||
declare build_dir="${1}" canonical_path=""
|
||||
|
||||
if [[ -z "${build_dir}" ]]; then
|
||||
build_dir_safety_error "path MUST NOT be empty."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ "${build_dir}" != /* ]]; then
|
||||
build_dir_safety_error "path MUST be absolute."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ -L "${build_dir}" ]]; then
|
||||
build_dir_safety_error "path MUST NOT be a symlink."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
canonical_path="$(realpath -m -- "${build_dir}")"
|
||||
reject_broad_build_dir_path "${canonical_path}" || return "${?}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_build_dir_argument
|
||||
|
||||
#######################################
|
||||
# Canonicalize and validate an existing build directory.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Build directory path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_existing_build_dir() {
|
||||
declare build_dir="${1}" canonical_path=""
|
||||
|
||||
validate_build_dir_argument "${build_dir}" || return "${?}"
|
||||
|
||||
if [[ ! -d "${build_dir}" ]]; then
|
||||
build_dir_safety_error "path MUST be an existing directory."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
canonical_path="$(realpath -e -- "${build_dir}")"
|
||||
reject_broad_build_dir_path "${canonical_path}" || return "$?"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_existing_build_dir
|
||||
|
||||
#######################################
|
||||
# Validate the builder-owned marker in a build directory.
|
||||
# Globals:
|
||||
# CISS_BUILD_DIR_MARKER
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Build directory path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
require_builder_owned_build_dir() {
|
||||
declare build_dir="${1}" canonical_path="" marker_path="" marker_owner="" marker_mode="" marker_mode_octal=""
|
||||
|
||||
validate_existing_build_dir "${build_dir}" || return "$?"
|
||||
|
||||
canonical_path="$(realpath -e -- "${build_dir}")"
|
||||
marker_path="${canonical_path}/${CISS_BUILD_DIR_MARKER}"
|
||||
|
||||
if [[ -L "${marker_path}" || ! -f "${marker_path}" ]]; then
|
||||
build_dir_safety_error "builder-owned marker is missing or unsafe."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
marker_owner="$(stat -c '%u:%g' -- "${marker_path}")"
|
||||
if [[ "${marker_owner}" != "0:0" ]]; then
|
||||
build_dir_safety_error "builder-owned marker MUST be owned by root:root."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
marker_mode="$(stat -c '%a' -- "${marker_path}")"
|
||||
marker_mode_octal=$((8#${marker_mode}))
|
||||
if (( (marker_mode_octal & 022) != 0 )); then
|
||||
build_dir_safety_error "builder-owned marker MUST NOT be group- or world-writable."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f require_builder_owned_build_dir
|
||||
|
||||
#######################################
|
||||
# Create or preserve the builder-owned marker.
|
||||
# Globals:
|
||||
# CISS_BUILD_DIR_MARKER
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Build directory path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
ensure_builder_owned_build_dir() {
|
||||
declare build_dir="${1}" canonical_path="" marker_path=""
|
||||
|
||||
validate_existing_build_dir "${build_dir}" || return "${?}"
|
||||
|
||||
canonical_path="$(realpath -e -- "${build_dir}")"
|
||||
marker_path="${canonical_path}/${CISS_BUILD_DIR_MARKER}"
|
||||
|
||||
if [[ -e "${marker_path}" || -L "${marker_path}" ]]; then
|
||||
require_builder_owned_build_dir "${canonical_path}" || return "${?}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -d "${canonical_path}/.build" ]]; then
|
||||
build_dir_safety_error "existing live-build state lacks the builder-owned marker."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
install -m 0600 -o root -g root /dev/null "${marker_path}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f ensure_builder_owned_build_dir
|
||||
|
||||
#######################################
|
||||
# Validate that a target path is strictly below a builder-owned build directory.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Build directory path.
|
||||
# 2: Target-path below the build directory.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
require_builder_owned_subpath() {
|
||||
declare build_dir="${1}" target_path="${2}" build_real="" target_real=""
|
||||
|
||||
require_builder_owned_build_dir "${build_dir}" || return "$?"
|
||||
|
||||
if [[ -z "${target_path}" || -L "${target_path}" || ! -e "${target_path}" ]]; then
|
||||
build_dir_safety_error "target subpath is empty, missing, or a symlink."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
build_real="$(realpath -e -- "${build_dir}")"
|
||||
target_real="$(realpath -e -- "${target_path}")"
|
||||
|
||||
if [[ "${target_real}" == "${build_real}" ]]; then
|
||||
build_dir_safety_error "target subpath MUST NOT be the build directory itself."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
case "${target_real}" in
|
||||
"${build_real}"/*)
|
||||
;;
|
||||
*)
|
||||
build_dir_safety_error "target subpath MUST stay below the build directory."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f require_builder_owned_subpath
|
||||
|
||||
#######################################
|
||||
# Remove all contents of the exact builder-owned build directory.
|
||||
# Globals:
|
||||
# CISS_BUILD_DIR_MARKER
|
||||
# Arguments:
|
||||
# 1: Build directory path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# Non-zero: on failure
|
||||
#######################################
|
||||
safe_clean_build_dir_contents() {
|
||||
declare build_dir="${1}" build_real=""
|
||||
|
||||
require_builder_owned_build_dir "${build_dir}" || return "${?}"
|
||||
|
||||
build_real="$(realpath -e -- "${build_dir}")"
|
||||
|
||||
find "${build_real}" -mindepth 1 -maxdepth 1 -xdev ! -name "${CISS_BUILD_DIR_MARKER}" -exec rm -rf --one-file-system -- {} +
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f safe_clean_build_dir_contents
|
||||
|
||||
#######################################
|
||||
# Remove one exact builder-owned subpath.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Build-directory-path.
|
||||
# 2: Target-path below build-directory.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# Non-zero: on failure
|
||||
#######################################
|
||||
safe_remove_builder_subpath() {
|
||||
declare build_dir="${1}" target_path="${2}" target_real=""
|
||||
|
||||
require_builder_owned_subpath "${build_dir}" "${target_path}" || return "${?}"
|
||||
|
||||
target_real="$(realpath -e -- "${target_path}")"
|
||||
rm -rf --one-file-system -- "${target_real}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f safe_remove_builder_subpath
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|
||||
@@ -33,12 +33,16 @@ x_remove() {
|
||||
|
||||
shopt -s nullglob dotglob
|
||||
|
||||
### Collect exact currently available secret values before removing their source files.
|
||||
### Log rewriting is intentionally deferred to the final trap after xtrace has been stopped.
|
||||
collect_debug_secret_values || true
|
||||
|
||||
if [[ "${VAR_SIGNER}" == "true" ]]; then
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
find "${VAR_TMP_SECRET}" -xdev -type f \
|
||||
! -path "${VAR_TMP_SECRET}/signing_key_pass.txt" \
|
||||
! -path "${VAR_TMP_SECRET}/luks.txt" \
|
||||
! -path "${VAR_TMP_SECRET}/${VAR_SIGNING_KEY_PASS:-signing_key_pass.txt}" \
|
||||
! -path "${VAR_TMP_SECRET}/${VAR_LUKS_KEY:-luks.txt}" \
|
||||
-print0 \
|
||||
| xargs -0 --no-run-if-empty shred -fzu -n 5 --
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
# shellcheck disable=SC2154
|
||||
# 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
|
||||
@@ -9,14 +10,15 @@
|
||||
# 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
|
||||
# shellcheck disable=SC2154
|
||||
|
||||
guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
|
||||
#######################################
|
||||
# Integrates and generates sha512sum and GPG signatures on CISS specific LIVE boot artifacts:
|
||||
# - /root/.ciss/attestation/VAR_SIGNING_KEY_FPR.* legacy-signed public-key copy, not rootfs content attestation
|
||||
# - /etc/initramfs-tools/files/unlock_wrapper.sh
|
||||
# - /usr/lib/live/boot/0030-ciss-verify-checksums
|
||||
# Rootfs content attestation for the final SquashFS payload is generated by zzzz_ciss_crypt_squash.hook.binary.
|
||||
# Globals:
|
||||
# BASH_SOURCE
|
||||
# VAR_HANDLER_BUILD_DIR
|
||||
@@ -31,7 +33,10 @@ guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
ciss_upgrades_boot() {
|
||||
printf "\e[95m🧪 %s starting ... \e[0m\n" "${BASH_SOURCE[0]}"
|
||||
|
||||
gpg --batch --yes --export "${VAR_SIGNING_KEY_FPR}" >| "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ciss/attestation/${VAR_SIGNING_KEY_FPR}.gpg"
|
||||
|
||||
declare -ar _ary_target=(
|
||||
"/root/.ciss/attestation/${VAR_SIGNING_KEY_FPR}.gpg"
|
||||
"/etc/initramfs-tools/files/unlock_wrapper.sh"
|
||||
"/usr/lib/live/boot/0030-ciss-verify-checksums"
|
||||
)
|
||||
|
||||
+27
-12
@@ -33,6 +33,7 @@ guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
#######################################
|
||||
clean_up() {
|
||||
declare clean_exit_code="$1" fs_type="" _old_nullglob="" _old_dotglob="" _old_failglob=""
|
||||
declare build_dir_valid="false" includes_chroot=""
|
||||
|
||||
### Enable nullglob/dotglob, disable failglob for safe globbing.
|
||||
_old_nullglob="$(shopt -p nullglob || true)"
|
||||
@@ -52,10 +53,10 @@ clean_up() {
|
||||
rm -f -- "${VAR_NOTES}"
|
||||
|
||||
### Release advisory lock on FD 127.
|
||||
flock -u 127
|
||||
flock -u 127 2>/dev/null || true
|
||||
|
||||
### Close file descriptor 127.
|
||||
exec 127>&-
|
||||
exec 127>&- 2>/dev/null || true
|
||||
|
||||
### Remove the lockfile artifact.
|
||||
rm -f /run/lock/ciss_live_builder.lock
|
||||
@@ -100,32 +101,46 @@ clean_up() {
|
||||
### No tracing for security reasons ------------------------------------------------------------------------------------------
|
||||
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x
|
||||
|
||||
### Stop xtrace before destructive cleanup and collect still-available exact secret values.
|
||||
### The final log rewrite runs later from the trap after no further debug/error log writes are expected.
|
||||
finalize_debug_xtrace_logging || true
|
||||
collect_debug_secret_values || true
|
||||
|
||||
### Removes secrets securely.
|
||||
if [[ -n "${VAR_TMP_SECRET:-}" && -d "${VAR_TMP_SECRET}" && ! -L "${VAR_TMP_SECRET}" ]]; then
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
find "${VAR_TMP_SECRET}" -xdev -type f -print0 | xargs -0 --no-run-if-empty shred -fzu -n 5 --
|
||||
find "${VAR_TMP_SECRET}" -xdev -depth -type d -empty -delete
|
||||
find "${VAR_TMP_SECRET}" -xdev -type f -print0 | xargs -0 --no-run-if-empty shred -fzu -n 5 -- || true
|
||||
find "${VAR_TMP_SECRET}" -xdev -depth -type d -empty -delete || true
|
||||
|
||||
fi
|
||||
|
||||
if [[ -n "${VAR_HANDLER_BUILD_DIR:-}" && -d "${VAR_HANDLER_BUILD_DIR}" ]]; then
|
||||
if require_builder_owned_build_dir "${VAR_HANDLER_BUILD_DIR}"; then
|
||||
build_dir_valid="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
### Securely shred all regular files below ./includes.chroot, then remove empty dirs.
|
||||
if [[ -d "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot" ]]; then
|
||||
includes_chroot="${VAR_HANDLER_BUILD_DIR:-}/config/includes.chroot"
|
||||
if [[ "${build_dir_valid}" == "true" && -d "${includes_chroot}" ]] \
|
||||
&& require_builder_owned_subpath "${VAR_HANDLER_BUILD_DIR}" "${includes_chroot}"; then
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
find "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot" -xdev -type f -print0 | xargs -0 --no-run-if-empty shred -fzu -n 5 --
|
||||
find "${includes_chroot}" -xdev -type f -print0 | xargs -0 --no-run-if-empty shred -fzu -n 5 -- || true
|
||||
|
||||
### Remove empty directories (bottom-up).
|
||||
find "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot" -depth -xdev -type d -empty -delete
|
||||
find "${includes_chroot}" -depth -xdev -type d -empty -delete || true
|
||||
|
||||
fi
|
||||
|
||||
### Delete all files and directories below ./chroot.
|
||||
if [[ -d "${VAR_HANDLER_BUILD_DIR}/chroot" ]]; then
|
||||
if [[ "${build_dir_valid}" == "true" && -d "${VAR_HANDLER_BUILD_DIR}/chroot" ]]; then
|
||||
|
||||
rm -rf "${VAR_HANDLER_BUILD_DIR}/chroot"
|
||||
safe_remove_builder_subpath "${VAR_HANDLER_BUILD_DIR}" "${VAR_HANDLER_BUILD_DIR}/chroot" || true
|
||||
|
||||
fi
|
||||
|
||||
### Turn on tracing again ----------------------------------------------------------------------------------------------------
|
||||
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x
|
||||
|
||||
eval "${_old_nullglob}" 2>/dev/null || true
|
||||
eval "${_old_dotglob}" 2>/dev/null || true
|
||||
eval "${_old_failglob}" 2>/dev/null || true
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
#!/bin/bash
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-CreationInfo: 2026-06-11; 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-2026; 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
|
||||
# shellcheck disable=SC2154
|
||||
|
||||
# Function behavior:
|
||||
# debug_sanitize_escape_glob(): Escape Bash glob metacharacters so exact values can be used safely in parameter-substitution matches.
|
||||
# debug_sanitize_read_file(): Read a text file into a named variable while preserving trailing newline bytes.
|
||||
# debug_sanitize_add_secret(): Add one non-empty exact secret value to the in-memory redaction list, avoiding duplicates.
|
||||
# debug_sanitize_add_secret_file(): Add the exact content of a regular, non-symlink secret file to the redaction list.
|
||||
# debug_sanitize_collect_secrets(): Add controlled secret variables and secret-file contents to the redaction list.
|
||||
# collect_debug_secret_values(): Collect exact secret values while xtrace is temporarily disabled.
|
||||
# finalize_debug_xtrace_logging(): Permanently disable xtrace and close the xtrace log FD before rewriting logs.
|
||||
# debug_sanitize_log_file(): Redact collected exact secret values from one log file and restrict its permissions.
|
||||
# sanitize_debug_logs(): Run the final exact-value sanitization pass across closed debug, variable, and error logs.
|
||||
|
||||
guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
|
||||
declare -ga _ARY_DEBUG_SECRET_VALUES=()
|
||||
declare -g _VAR_DEBUG_XTRACE_FINALIZED="false"
|
||||
|
||||
#######################################
|
||||
# Escape Bash glob metacharacters for exact parameter-substitution matching.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Raw value.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
debug_sanitize_escape_glob() {
|
||||
declare value="${1}"
|
||||
|
||||
value="${value//\\/\\\\}"
|
||||
value="${value//\*/\\*}"
|
||||
value="${value//\?/\\?}"
|
||||
value="${value//\[/\\[}"
|
||||
value="${value//\]/\\]}"
|
||||
|
||||
printf '%s' "${value}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f debug_sanitize_escape_glob
|
||||
|
||||
#######################################
|
||||
# Read a text file into a variable, preserving trailing newlines.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: File path.
|
||||
# 2: Output variable name.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
debug_sanitize_read_file() {
|
||||
declare file_path="${1}" output_var="${2}" content=""
|
||||
|
||||
IFS= read -r -d '' content < "${file_path}" || true
|
||||
printf -v "${output_var}" '%s' "${content}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f debug_sanitize_read_file
|
||||
|
||||
#######################################
|
||||
# Add one exact secret value to the redaction list.
|
||||
# Globals:
|
||||
# _ARY_DEBUG_SECRET_VALUES
|
||||
# Arguments:
|
||||
# 1: Secret value.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
debug_sanitize_add_secret() {
|
||||
declare secret_value="${1}" known_value=""
|
||||
|
||||
[[ -n "${secret_value}" ]] || return 0
|
||||
|
||||
for known_value in "${_ARY_DEBUG_SECRET_VALUES[@]:-}"; do
|
||||
[[ "${known_value}" == "${secret_value}" ]] && return 0
|
||||
done
|
||||
|
||||
_ARY_DEBUG_SECRET_VALUES+=("${secret_value}")
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f debug_sanitize_add_secret
|
||||
|
||||
#######################################
|
||||
# Add a secret file's exact content to the redaction list.
|
||||
# Globals:
|
||||
# _ARY_DEBUG_SECRET_VALUES
|
||||
# Arguments:
|
||||
# 1: File path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
debug_sanitize_add_secret_file() {
|
||||
declare file_path="${1}" secret_value=""
|
||||
|
||||
[[ -n "${file_path}" && -f "${file_path}" && ! -L "${file_path}" ]] || return 0
|
||||
[[ -s "${file_path}" ]] || return 0
|
||||
|
||||
debug_sanitize_read_file "${file_path}" secret_value
|
||||
debug_sanitize_add_secret "${secret_value}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f debug_sanitize_add_secret_file
|
||||
|
||||
#######################################
|
||||
# Gather exact values from controlled secret variables and files.
|
||||
# Globals:
|
||||
# _ARY_DEBUG_SECRET_VALUES
|
||||
# VAR_HASHED_PWD
|
||||
# VAR_SIGNING_KEY_PASSFILE
|
||||
# VAR_TMP_SECRET
|
||||
# Arguments:
|
||||
# None
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
debug_sanitize_collect_secrets() {
|
||||
declare secret_file=""
|
||||
|
||||
if [[ -n "${VAR_HASHED_PWD:-}" ]]; then
|
||||
debug_sanitize_add_secret "${VAR_HASHED_PWD}"
|
||||
fi
|
||||
|
||||
if [[ -n "${VAR_SIGNING_KEY_PASSFILE:-}" ]]; then
|
||||
debug_sanitize_add_secret_file "${VAR_SIGNING_KEY_PASSFILE}"
|
||||
fi
|
||||
|
||||
if [[ -n "${VAR_TMP_SECRET:-}" && -d "${VAR_TMP_SECRET}" && ! -L "${VAR_TMP_SECRET}" ]]; then
|
||||
while IFS= read -r -d '' secret_file; do
|
||||
debug_sanitize_add_secret_file "${secret_file}"
|
||||
done < <(find "${VAR_TMP_SECRET}" -xdev -type f -print0 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f debug_sanitize_collect_secrets
|
||||
|
||||
#######################################
|
||||
# Collect exact secret values without tracing secret-bearing operations.
|
||||
# Globals:
|
||||
# _VAR_DEBUG_XTRACE_FINALIZED
|
||||
# Arguments:
|
||||
# None
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
collect_debug_secret_values() {
|
||||
declare tracing_was_enabled="false"
|
||||
|
||||
case "$-" in
|
||||
*x*)
|
||||
tracing_was_enabled="true"
|
||||
set +x
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
debug_sanitize_collect_secrets || true
|
||||
|
||||
if [[ "${tracing_was_enabled}" == "true" && "${_VAR_DEBUG_XTRACE_FINALIZED}" != "true" ]]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f collect_debug_secret_values
|
||||
|
||||
#######################################
|
||||
# Permanently stop xtrace logging before the final log rewrite.
|
||||
# Globals:
|
||||
# BASH_XTRACEFD
|
||||
# _VAR_DEBUG_XTRACE_FINALIZED
|
||||
# Arguments:
|
||||
# None
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
finalize_debug_xtrace_logging() {
|
||||
declare xtrace_fd=""
|
||||
|
||||
set +x
|
||||
|
||||
if [[ "${BASH_XTRACEFD:-}" =~ ^[0-9]+$ ]]; then
|
||||
xtrace_fd="${BASH_XTRACEFD}"
|
||||
unset BASH_XTRACEFD
|
||||
|
||||
if (( xtrace_fd > 2 )); then
|
||||
exec {xtrace_fd}>&- 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
_VAR_DEBUG_XTRACE_FINALIZED="true"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f finalize_debug_xtrace_logging
|
||||
|
||||
#######################################
|
||||
# Redact exact secret values from one log file.
|
||||
# Globals:
|
||||
# _ARY_DEBUG_SECRET_VALUES
|
||||
# Arguments:
|
||||
# 1: Log file path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
debug_sanitize_log_file() {
|
||||
declare log_file="${1}" log_content="" secret_value="" secret_pattern="" redaction=""
|
||||
declare -i secret_len=0
|
||||
|
||||
[[ -n "${log_file}" && -f "${log_file}" && ! -L "${log_file}" ]] || return 0
|
||||
|
||||
debug_sanitize_read_file "${log_file}" log_content
|
||||
|
||||
for secret_value in "${_ARY_DEBUG_SECRET_VALUES[@]:-}"; do
|
||||
|
||||
[[ -n "${secret_value}" ]] || continue
|
||||
|
||||
secret_pattern="$(debug_sanitize_escape_glob "${secret_value}")"
|
||||
secret_len="${#secret_value}"
|
||||
printf -v redaction '%*s' "${secret_len}" ''
|
||||
redaction="${redaction// /*}"
|
||||
log_content="${log_content//${secret_pattern}/${redaction}}"
|
||||
|
||||
done
|
||||
|
||||
printf '%s' "${log_content}" >| "${log_file}"
|
||||
chmod 0600 "${log_file}" 2>/dev/null || true
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f debug_sanitize_log_file
|
||||
|
||||
#######################################
|
||||
# Final exact-value debug log sanitization pass.
|
||||
# Globals:
|
||||
# LOG_DEBUG
|
||||
# LOG_ERROR
|
||||
# LOG_VAR
|
||||
# Arguments:
|
||||
# None
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
sanitize_debug_logs() {
|
||||
declare old_lc_all="${LC_ALL:-}" log_file=""
|
||||
|
||||
finalize_debug_xtrace_logging || true
|
||||
|
||||
LC_ALL=C
|
||||
|
||||
debug_sanitize_collect_secrets || true
|
||||
|
||||
for log_file in "${LOG_DEBUG:-}" "${LOG_VAR:-}" "${LOG_ERROR:-}"; do
|
||||
debug_sanitize_log_file "${log_file}" || true
|
||||
done
|
||||
|
||||
LC_ALL="${old_lc_all}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f sanitize_debug_logs
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|
||||
+7
-1
@@ -72,9 +72,15 @@ EOF
|
||||
|
||||
fi
|
||||
|
||||
### Use pubring as verification keyring reference.
|
||||
### Use pubring as a verification keyring reference.
|
||||
declare -grx VAR_VERIFY_KEYRING="${GNUPGHOME}/pubring.kbx"
|
||||
|
||||
validate_secret_file_in_root "--signing_key" "${VAR_SIGNING_KEY}" || return "${?}"
|
||||
validate_secret_file_in_root "--signing_key_pass" "${VAR_SIGNING_KEY_PASS}" || return "${?}"
|
||||
if [[ -n "${VAR_SIGNING_CA}" ]]; then
|
||||
validate_secret_file_in_root "--signing_ca" "${VAR_SIGNING_CA}" || return "${?}"
|
||||
fi
|
||||
|
||||
declare -grx VAR_SIGNING_KEY_PASSFILE="${VAR_TMP_SECRET}/${VAR_SIGNING_KEY_PASS}"
|
||||
|
||||
### No tracing for security reasons ------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -182,6 +182,7 @@ hardening_ultra() {
|
||||
printf "\e[95m🧪 Updating SSH Keys, Ports ... \e[0m\n"
|
||||
|
||||
### ./config/includes.chroot/root/.ssh ---------------------------------------------------------------------------------------
|
||||
validate_ssh_pubkey_directory "${VAR_SSHPUBKEY}" || return "${?}"
|
||||
install -d -m 0700 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh"
|
||||
install -m 0600 -o root -g root "${VAR_SSHPUBKEY}/authorized_keys" "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh/"
|
||||
|
||||
|
||||
@@ -30,12 +30,14 @@ lb_config_start() {
|
||||
mkdir -p "${VAR_HANDLER_BUILD_DIR}"
|
||||
# shellcheck disable=SC2164
|
||||
cd "${VAR_HANDLER_BUILD_DIR}"
|
||||
ensure_builder_owned_build_dir "${VAR_HANDLER_BUILD_DIR}"
|
||||
printf "\e[92m✅ '%s' created. \e[0m\n" "${VAR_HANDLER_BUILD_DIR}"
|
||||
|
||||
else
|
||||
|
||||
# shellcheck disable=SC2164
|
||||
cd "${VAR_HANDLER_BUILD_DIR}"
|
||||
ensure_builder_owned_build_dir "${VAR_HANDLER_BUILD_DIR}"
|
||||
|
||||
fi
|
||||
|
||||
@@ -48,11 +50,7 @@ lb_config_start() {
|
||||
|
||||
lb clean --binary --cache --purge --source
|
||||
|
||||
if [[ "${PWD}" == "${VAR_HANDLER_BUILD_DIR}" && "${PWD}" != "/" && -n "${PWD}" ]]; then
|
||||
|
||||
rm -rf -- ./* ./.??*
|
||||
|
||||
fi
|
||||
safe_clean_build_dir_contents "${VAR_HANDLER_BUILD_DIR}"
|
||||
|
||||
printf "\e[92m✅ Deleting former config, binary and cache done.\e[0m\n"
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ init_primordial() {
|
||||
### Check for SOPS AGE key integration ---------------------------------------------------------------------------------------
|
||||
if [[ "${VAR_AGE,,}" == "true" ]]; then
|
||||
|
||||
validate_secret_file_in_root "--key_age" "${VAR_AGE_KEY}" || return "$?"
|
||||
|
||||
install -d -m 0700 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.config/sops/age"
|
||||
install -m 0400 "${VAR_TMP_SECRET}/${VAR_AGE_KEY}" "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.config/sops/age/keys.txt"
|
||||
shred -fzu -n 5 -- "${VAR_TMP_SECRET}/${VAR_AGE_KEY}" 2>/dev/null || rm -f "${VAR_TMP_SECRET}/${VAR_AGE_KEY}"
|
||||
@@ -93,6 +95,18 @@ init_primordial() {
|
||||
### Check for SSH CISS and PhysNet Primordial-Workflow™ integration -------------------------------------------------------
|
||||
if [[ "${VAR_SSHFP,,}" == "true" ]]; then
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
if find "${VAR_TMP_SECRET}" -xdev \( -name 'id*' -o -name 'ssh_host_*' \) -type l -print -quit | grep -q .; then
|
||||
printf "\e[91m❌ ERROR: SSH identity and host key inputs MUST NOT be symlinks. \e[0m\n" >&2
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
if find "${VAR_TMP_SECRET}" -xdev \( -name 'id*' -o -name 'ssh_host_*' \) ! -type f -print -quit | grep -q .; then
|
||||
printf "\e[91m❌ ERROR: SSH identity and host key inputs MUST be regular files. \e[0m\n" >&2
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
install -d -m 0700 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh"
|
||||
install -m 0600 "${VAR_TMP_SECRET}/id"* "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh/"
|
||||
normalize_ssh_keys_in_dir "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh"
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
#!/bin/bash
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-CreationInfo: 2026-06-11; 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-2026; 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
|
||||
# shellcheck disable=SC2154
|
||||
|
||||
guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
|
||||
#######################################
|
||||
# Print a validation error without echoing secret values.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Validation label.
|
||||
# 2: Error detail.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
secret_validation_error() {
|
||||
declare label="$1" detail="$2"
|
||||
|
||||
printf "\e[91mERROR: %s: %s\e[0m\n" "${label}" "${detail}" >&2
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f secret_validation_error
|
||||
|
||||
#######################################
|
||||
# Validate a filename-only secret argument.
|
||||
# Globals:
|
||||
# ERR_ARG_MSMTCH
|
||||
# Arguments:
|
||||
# 1: Validation label.
|
||||
# 2: Filename.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_ARG_MSMTCH: on failure
|
||||
#######################################
|
||||
validate_secret_filename() {
|
||||
declare label="$1" filename="$2"
|
||||
declare filename_regex='^[A-Za-z0-9._@%+=:,~-]+$'
|
||||
|
||||
if [[ -z "${filename}" ]]; then
|
||||
secret_validation_error "${label}" "filename MUST NOT be empty."
|
||||
return "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
|
||||
if [[ "${filename}" == "." || "${filename}" == ".." ]]; then
|
||||
secret_validation_error "${label}" "filename MUST NOT be '.' or '..'."
|
||||
return "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
|
||||
if [[ "${filename}" == -* ]]; then
|
||||
secret_validation_error "${label}" "filename MUST NOT start with '-'."
|
||||
return "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
|
||||
if [[ "${filename}" == */* || "${filename}" == *\\* ]]; then
|
||||
secret_validation_error "${label}" "filename MUST NOT contain path separators."
|
||||
return "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
|
||||
if [[ ! "${filename}" =~ ${filename_regex} ]]; then
|
||||
secret_validation_error "${label}" "filename contains unsupported characters."
|
||||
return "${ERR_ARG_MSMTCH}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_filename
|
||||
|
||||
#######################################
|
||||
# Validate the fixed tmpfs secret root.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Secret root path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_secret_root() {
|
||||
declare secret_root="$1" root_owner="" root_mode="" root_mode_octal="" root_fs=""
|
||||
|
||||
if [[ -z "${secret_root}" ]]; then
|
||||
secret_validation_error "secret root" "path MUST NOT be empty."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ -L "${secret_root}" ]]; then
|
||||
secret_validation_error "secret root" "path MUST NOT be a symlink."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ ! -d "${secret_root}" ]]; then
|
||||
secret_validation_error "secret root" "path MUST be an existing directory."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
root_owner="$(stat -c '%u:%g' -- "${secret_root}")"
|
||||
if [[ "${root_owner}" != "0:0" ]]; then
|
||||
secret_validation_error "secret root" "directory MUST be owned by root:root."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
root_mode="$(stat -c '%a' -- "${secret_root}")"
|
||||
root_mode_octal=$((8#${root_mode}))
|
||||
if (( (root_mode_octal & 077) != 0 || (root_mode_octal & 0700) != 0700 )); then
|
||||
secret_validation_error "secret root" "directory mode MUST be 0700 or stricter."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
root_fs="$(stat -f -c '%T' -- "${secret_root}")"
|
||||
if [[ "${root_fs}" != "tmpfs" && "${root_fs}" != "ramfs" ]]; then
|
||||
secret_validation_error "secret root" "directory MUST be backed by tmpfs or ramfs."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
if find "${secret_root}" -xdev -type l -print -quit | grep -q .; then
|
||||
secret_validation_error "secret root" "secret tree MUST NOT contain symlinks."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
if find "${secret_root}" -xdev \( -type b -o -type c -o -type p -o -type s \) -print -quit | grep -q .; then
|
||||
secret_validation_error "secret root" "secret tree MUST NOT contain special files."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_root
|
||||
|
||||
#######################################
|
||||
# Normalize ownership and mode of regular files in the secret root.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Secret root path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
harden_secret_root_files() {
|
||||
declare secret_root="$1"
|
||||
|
||||
validate_secret_root "${secret_root}" || return "$?"
|
||||
|
||||
find "${secret_root}" -xdev -type f -exec chown root:root -- {} +
|
||||
find "${secret_root}" -xdev -type f -exec chmod 0400 -- {} +
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f harden_secret_root_files
|
||||
|
||||
#######################################
|
||||
# Validate that an existing absolute path is a regular non-symlink file.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Validation label.
|
||||
# 2: File path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_secret_absolute_file_basics() {
|
||||
declare label="$1" file_path="$2"
|
||||
|
||||
if [[ -z "${file_path}" ]]; then
|
||||
secret_validation_error "${label}" "file path MUST NOT be empty."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ "${file_path}" != /* ]]; then
|
||||
secret_validation_error "${label}" "file path MUST be absolute."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ -L "${file_path}" ]]; then
|
||||
secret_validation_error "${label}" "file MUST NOT be a symlink."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ ! -f "${file_path}" ]]; then
|
||||
secret_validation_error "${label}" "file MUST be an existing regular file."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_absolute_file_basics
|
||||
|
||||
#######################################
|
||||
# Validate a strict secret file.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Validation label.
|
||||
# 2: File path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_secret_file_path() {
|
||||
declare label="$1" file_path="$2" file_owner="" file_mode="" file_mode_octal=""
|
||||
|
||||
validate_secret_absolute_file_basics "${label}" "${file_path}" || return "$?"
|
||||
|
||||
file_owner="$(stat -c '%u:%g' -- "${file_path}")"
|
||||
if [[ "${file_owner}" != "0:0" ]]; then
|
||||
secret_validation_error "${label}" "file MUST be owned by root:root."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
file_mode="$(stat -c '%a' -- "${file_path}")"
|
||||
file_mode_octal=$((8#${file_mode}))
|
||||
if (( (file_mode_octal & 077) != 0 || (file_mode_octal & 0400) != 0400 )); then
|
||||
secret_validation_error "${label}" "file mode MUST allow root read and no group/other access."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_file_path
|
||||
|
||||
#######################################
|
||||
# Validate a filename-only secret stored below the fixed secret root.
|
||||
# Globals:
|
||||
# VAR_TMP_SECRET
|
||||
# Arguments:
|
||||
# 1: Validation label.
|
||||
# 2: Filename.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# Non-zero: on failure
|
||||
#######################################
|
||||
validate_secret_file_in_root() {
|
||||
declare label="$1" filename="$2"
|
||||
|
||||
validate_secret_filename "${label}" "${filename}" || return "$?"
|
||||
validate_secret_file_path "${label}" "${VAR_TMP_SECRET}/${filename}" || return "$?"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_file_in_root
|
||||
|
||||
#######################################
|
||||
# Validate a public input file copied into the ISO.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Validation label.
|
||||
# 2: File path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_public_input_file() {
|
||||
declare label="$1" file_path="$2" file_owner="" file_mode="" file_mode_octal=""
|
||||
|
||||
validate_secret_absolute_file_basics "${label}" "${file_path}" || return "$?"
|
||||
|
||||
file_owner="$(stat -c '%u:%g' -- "${file_path}")"
|
||||
if [[ "${file_owner}" != "0:0" ]]; then
|
||||
secret_validation_error "${label}" "file MUST be owned by root:root."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
file_mode="$(stat -c '%a' -- "${file_path}")"
|
||||
file_mode_octal=$((8#${file_mode}))
|
||||
if (( (file_mode_octal & 022) != 0 )); then
|
||||
secret_validation_error "${label}" "file MUST NOT be group- or world-writable."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_public_input_file
|
||||
|
||||
#######################################
|
||||
# Validate the authorized_keys directory.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: Directory path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_ssh_pubkey_directory() {
|
||||
declare key_dir="$1" key_file="" dir_owner="" dir_mode="" dir_mode_octal=""
|
||||
|
||||
if [[ -z "${key_dir}" ]]; then
|
||||
secret_validation_error "--ssh-pubkey" "directory path MUST NOT be empty."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ "${key_dir}" != /* ]]; then
|
||||
secret_validation_error "--ssh-pubkey" "directory path MUST be absolute."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ -L "${key_dir}" ]]; then
|
||||
secret_validation_error "--ssh-pubkey" "directory MUST NOT be a symlink."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ ! -d "${key_dir}" ]]; then
|
||||
secret_validation_error "--ssh-pubkey" "directory MUST exist."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
dir_owner="$(stat -c '%u:%g' -- "${key_dir}")"
|
||||
if [[ "${dir_owner}" != "0:0" ]]; then
|
||||
secret_validation_error "--ssh-pubkey" "directory MUST be owned by root:root."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
dir_mode="$(stat -c '%a' -- "${key_dir}")"
|
||||
dir_mode_octal=$((8#${dir_mode}))
|
||||
if (( (dir_mode_octal & 022) != 0 )); then
|
||||
secret_validation_error "--ssh-pubkey" "directory MUST NOT be group- or world-writable."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
key_file="${key_dir}/authorized_keys"
|
||||
validate_public_input_file "--ssh-pubkey authorized_keys" "${key_file}" || return "$?"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_ssh_pubkey_directory
|
||||
|
||||
#######################################
|
||||
# Validate all selected secret inputs after argument parsing.
|
||||
# Globals:
|
||||
# VAR_AGE
|
||||
# VAR_AGE_KEY
|
||||
# VAR_LUKS
|
||||
# VAR_LUKS_KEY
|
||||
# VAR_SIGNER
|
||||
# VAR_SIGNING_CA
|
||||
# VAR_SIGNING_KEY
|
||||
# VAR_SIGNING_KEY_PASS
|
||||
# VAR_SSHPUBKEY
|
||||
# VAR_TMP_SECRET
|
||||
# Arguments:
|
||||
# None
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# Non-zero: on failure
|
||||
#######################################
|
||||
validate_selected_secret_inputs() {
|
||||
if [[ "${VAR_AGE,,}" == "true" ]]; then
|
||||
validate_secret_file_in_root "--key_age" "${VAR_AGE_KEY}" || return "$?"
|
||||
fi
|
||||
|
||||
if [[ "${VAR_LUKS,,}" == "true" ]]; then
|
||||
validate_secret_file_in_root "--key_luks" "${VAR_LUKS_KEY}" || return "$?"
|
||||
fi
|
||||
|
||||
if [[ "${VAR_SIGNER,,}" == "true" ]]; then
|
||||
validate_secret_file_in_root "--signing_key" "${VAR_SIGNING_KEY}" || return "$?"
|
||||
validate_secret_file_in_root "--signing_key_pass" "${VAR_SIGNING_KEY_PASS}" || return "$?"
|
||||
fi
|
||||
|
||||
if [[ -n "${VAR_SIGNING_CA:-}" ]]; then
|
||||
validate_secret_file_in_root "--signing_ca" "${VAR_SIGNING_CA}" || return "$?"
|
||||
fi
|
||||
|
||||
if [[ -n "${VAR_SSHPUBKEY:-}" ]]; then
|
||||
validate_ssh_pubkey_directory "${VAR_SSHPUBKEY}" || return "$?"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_selected_secret_inputs
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|
||||
@@ -49,6 +49,8 @@ trap_on_exit() {
|
||||
|
||||
print_scr_exit "${errcode}"
|
||||
|
||||
sanitize_debug_logs || true
|
||||
|
||||
exit "${errcode}"
|
||||
|
||||
else
|
||||
@@ -61,6 +63,8 @@ trap_on_exit() {
|
||||
|
||||
print_scr_exit_non_zero "${errcode}" "${errscrt}" "${errline}" "${errfunc}" "${errcmmd}"
|
||||
|
||||
sanitize_debug_logs || true
|
||||
|
||||
fi
|
||||
|
||||
exit "${errcode}"
|
||||
|
||||
+10
-6
@@ -39,13 +39,13 @@ usage() {
|
||||
# shellcheck disable=SC2155
|
||||
declare var_header=$(center "CDLB(1) CISS.debian.live.builder CDLB(1)" "${var_cols}")
|
||||
# shellcheck disable=SC2155
|
||||
declare var_footer=$(center "V9.14.022.2026.06.10 2026-06-10 CDLB(1)" "${var_cols}")
|
||||
declare var_footer=$(center "${VAR_VERSION} 2026-06-11 CDLB(1)" "${var_cols}")
|
||||
|
||||
{
|
||||
echo -e "\e[1;97m${var_header}\e[0m"
|
||||
echo
|
||||
echo -e "\e[92mCISS.debian.live.builder from https://git.coresecret.dev/msw \e[0m"
|
||||
echo -e "\e[92mMaster V9.14.022.2026.06.10\e[0m"
|
||||
echo -e "\e[92mMaster ${VAR_VERSION}\e[0m"
|
||||
echo -e "\e[92mA lightweight Shell Wrapper for building a hardened Debian Live ISO Image.\e[0m"
|
||||
echo
|
||||
echo -e "\e[97m(c) Marc S. Weidner, 2018 - 2026 \e[0m"
|
||||
@@ -67,6 +67,7 @@ usage() {
|
||||
echo
|
||||
echo -e "\e[97m --build-directory </path/to/build_directory> \e[0m"
|
||||
echo " Where the Debian Live Build Image should be generated. RECOMMENDED path: </opt/cdlb>"
|
||||
echo " Cleanup is destructive inside the exact builder-owned path and requires '.ciss-live-builder-owned'."
|
||||
echo " MUST be provided."
|
||||
echo
|
||||
echo -e "\e[97m --change-splash <STRING> one of <club | hexagon> \e[0m"
|
||||
@@ -87,6 +88,7 @@ usage() {
|
||||
echo -e "\e[97m --debug, -d \e[0m"
|
||||
echo " Enables debug logging for the main program routine. Detailed logging information are written to:"
|
||||
echo " </tmp/ciss_live_builder_$$.log>"
|
||||
echo " After xtrace is stopped and its debug FD is closed, a final exact-value redaction pass sanitizes logs."
|
||||
echo
|
||||
echo -e "\e[97m --dhcp-centurion \e[0m"
|
||||
echo " If a DHCP lease is provided, the provider's name server will be overridden and the hardened, privacy-focused "
|
||||
@@ -108,12 +110,12 @@ usage() {
|
||||
echo
|
||||
echo -e "\e[97m --key_age=* \e[0m"
|
||||
echo " The SOPS AGE private keyring for decryption operations. Change '*' to your desired SOPS AGE key file."
|
||||
echo " File MUST be placed in:"
|
||||
echo " This MUST be a filename only and MUST be placed in the root-owned tmpfs secret root:"
|
||||
echo " </dev/shm/cdlb_secrets>"
|
||||
echo
|
||||
echo -e "\e[97m --key_luks=* \e[0m"
|
||||
echo " The LUKS encryption / decryption passphrase for '/'-fs-encryption. Change '*' to your desired passphrase file."
|
||||
echo " File MUST be placed in:"
|
||||
echo " This MUST be a filename only and MUST be placed in the root-owned tmpfs secret root:"
|
||||
echo " </dev/shm/cdlb_secrets>"
|
||||
echo
|
||||
echo -e "\e[97m --log-statistics-only \e[0m"
|
||||
@@ -162,7 +164,8 @@ usage() {
|
||||
echo -e "\e[97m --root-password-file </dev/shm/cdlb_secrets/password.txt>> \e[0m"
|
||||
echo " Password file for 'root', if given, MUST be a string of 42 to 64 characters."
|
||||
echo " If the argument is omitted, no further login authentication is required for the local console."
|
||||
echo " MUST be placed in:"
|
||||
echo " The path MUST be absolute, regular, non-symlink, root-owned, and mode 0400 after normalization."
|
||||
echo " RECOMMENDED path:"
|
||||
echo " </dev/shm/cdlb_secrets/password.txt>"
|
||||
echo
|
||||
echo -e "\e[97m --secure-boot-profile <STRING> one of <debian-shim | ciss-uki> \e[0m"
|
||||
@@ -178,7 +181,7 @@ usage() {
|
||||
echo " specified via '--signing_key=*'. If the keyring is protected, then provide the passphrase in its own file."
|
||||
echo " Specify the fingerprint of the key to use via '--signing_key_fpr=*'."
|
||||
echo " Optionally import an offline GPG CA signing public key via: '--signing_ca=*'."
|
||||
echo " Change '*' to your desired files / fingerprint. Files MUST be placed in:"
|
||||
echo " Change '*' to your desired filename-only files / fingerprint. Files MUST be placed in:"
|
||||
echo " </dev/shm/cdlb_secrets>"
|
||||
echo
|
||||
echo -e "\e[97m --sops-version <STRING> \e[0m"
|
||||
@@ -201,6 +204,7 @@ usage() {
|
||||
echo
|
||||
echo -e "\e[97m --ssh-pubkey </dev/shm/cdlb_secrets/> \e[0m"
|
||||
echo " Imports the SSH Public Key from the file 'authorized_keys' into the Live ISO."
|
||||
echo " Directory MUST be absolute, regular, non-symlink, root-owned, and not group/world-writable."
|
||||
echo " Key file MUST be placed in:"
|
||||
echo " </dev/shm/cdlb_secrets/authorized_keys>"
|
||||
echo
|
||||
|
||||
@@ -601,7 +601,7 @@ 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}"
|
||||
printf "CISS.debian.live.builder V9.14.024.2026.06.11 calling CISS.debian.installer ... \n" >> "${var_log}"
|
||||
|
||||
### Sleep a moment to settle boot artifacts.
|
||||
sleep 8
|
||||
@@ -696,7 +696,7 @@ main() {
|
||||
|
||||
### Timeout reached without acceptable semaphore.
|
||||
logger -t cdi-watcher "No valid semaphore ${VAR_SEMAPHORE} (mode 0600) within ${VAR_TIMEOUT}s; exiting idle."
|
||||
printf "CISS.debian.live.builder V9.14.022.2026.06.10: No valid semaphore [%s] within [%s]s.\n" "${VAR_SEMAPHORE}" "${VAR_TIMEOUT}" >> "${var_log}"
|
||||
printf "CISS.debian.live.builder V9.14.024.2026.06.11: No valid semaphore [%s] within [%s]s.\n" "${VAR_SEMAPHORE}" "${VAR_TIMEOUT}" >> "${var_log}"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
#!/bin/bash
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-FileCopyrightText: 2026; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||||
# SPDX-FileType: SOURCE
|
||||
# SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
|
||||
set -Ceuo pipefail
|
||||
|
||||
declare ROOT_DIR=""
|
||||
declare SHA512SUM=""
|
||||
declare SHASUM=""
|
||||
declare TMP_BASE=""
|
||||
declare TMP_ROOT=""
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SHA512SUM="$(command -v sha512sum)"
|
||||
SHASUM="$(command -v shasum || true)"
|
||||
TMP_BASE="${TMPDIR:-/tmp}"
|
||||
TMP_BASE="${TMP_BASE%/}"
|
||||
TMP_ROOT="$(mktemp -d "${TMP_BASE}/ciss-boot-attestation.XXXXXXXX")"
|
||||
|
||||
cleanup() {
|
||||
case "${TMP_ROOT}" in
|
||||
"${TMP_BASE}/ciss-boot-attestation."*)
|
||||
rm -rf -- "${TMP_ROOT}"
|
||||
;;
|
||||
*)
|
||||
printf 'Refusing to clean unexpected test path: %s\n' "${TMP_ROOT}" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "${1}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
prepare_checksum_hook() {
|
||||
declare source_hook="${1}"
|
||||
declare target_hook="${2}"
|
||||
declare fault_mode="${3}"
|
||||
declare test_bin="${4}"
|
||||
declare test_tty="${5}"
|
||||
declare test_runtime=""
|
||||
|
||||
test_runtime="$(dirname "${test_tty}")"
|
||||
sed \
|
||||
-e "s|/usr/bin/|${test_bin}/|g" \
|
||||
-e "s|/run/ciss-|${test_runtime}/ciss-|g" \
|
||||
-e "s|_TTY=\"/dev/tty8\"|_TTY=\"${test_tty}\"|" \
|
||||
"${source_hook}" > "${target_hook}"
|
||||
|
||||
if [[ "${fault_mode}" == "unknown-state" ]]; then
|
||||
sed \
|
||||
-e 's/_RETURN_PGP="na"/_RETURN_PGP="unknown"/' \
|
||||
-e 's/_RETURN_SHA="${?}"/_RETURN_SHA="unknown"/g' \
|
||||
"${target_hook}" > "${target_hook}.new"
|
||||
mv "${target_hook}.new" "${target_hook}"
|
||||
fi
|
||||
}
|
||||
|
||||
install_test_sha512sum() {
|
||||
declare test_bin="${1}"
|
||||
|
||||
if printf '' | "${SHA512SUM}" -c >/dev/null 2>&1; then
|
||||
ln -s "${SHA512SUM}" "${test_bin}/sha512sum"
|
||||
elif [[ -n "${SHASUM}" ]]; then
|
||||
printf '#!/bin/sh\nexec "%s" -a 512 "$@"\n' "${SHASUM}" > "${test_bin}/sha512sum"
|
||||
chmod 0755 "${test_bin}/sha512sum"
|
||||
else
|
||||
fail "No SHA-512 tool with checksum verification support is available."
|
||||
fi
|
||||
}
|
||||
|
||||
run_checksum_case() {
|
||||
declare source_hook="${1}"
|
||||
declare case_name="${2}"
|
||||
declare expected_status="${3}"
|
||||
declare expected_message="${4}"
|
||||
declare case_dir=""
|
||||
declare hook_copy=""
|
||||
declare output_file=""
|
||||
declare test_bin=""
|
||||
declare test_tty=""
|
||||
declare source_id=""
|
||||
declare fault_mode=""
|
||||
declare panic_returns="false"
|
||||
declare status=0
|
||||
|
||||
source_id="$(printf '%s' "${source_hook#"${ROOT_DIR}"/}" | tr '/' '_')"
|
||||
case_dir="${TMP_ROOT}/${source_id}-${case_name}"
|
||||
hook_copy="${case_dir}/0030-ciss-verify-checksums"
|
||||
output_file="${case_dir}/output.log"
|
||||
test_bin="${case_dir}/bin"
|
||||
test_tty="${case_dir}/tty.log"
|
||||
|
||||
mkdir -p "${case_dir}" "${test_bin}"
|
||||
: >| "${test_tty}"
|
||||
|
||||
case "${case_name}" in
|
||||
valid)
|
||||
install_test_sha512sum "${test_bin}"
|
||||
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
||||
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
||||
;;
|
||||
missing-manifest)
|
||||
install_test_sha512sum "${test_bin}"
|
||||
;;
|
||||
unsupported-manifest)
|
||||
install_test_sha512sum "${test_bin}"
|
||||
printf 'unsupported\n' > "${case_dir}/md5sum.txt"
|
||||
;;
|
||||
failed-checksum)
|
||||
install_test_sha512sum "${test_bin}"
|
||||
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
||||
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
||||
printf 'tampered rootfs payload\n' >| "${case_dir}/payload"
|
||||
;;
|
||||
missing-tool)
|
||||
install_test_sha512sum "${test_bin}"
|
||||
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
||||
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
||||
rm -f "${test_bin}/sha512sum"
|
||||
;;
|
||||
unknown-state)
|
||||
fault_mode="unknown-state"
|
||||
panic_returns="true"
|
||||
install_test_sha512sum "${test_bin}"
|
||||
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
||||
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
||||
;;
|
||||
*)
|
||||
fail "Unknown checksum test case: ${case_name}"
|
||||
;;
|
||||
esac
|
||||
|
||||
prepare_checksum_hook "${source_hook}" "${hook_copy}" "${fault_mode}" "${test_bin}" "${test_tty}"
|
||||
|
||||
set +e
|
||||
# shellcheck disable=SC2034,SC2329
|
||||
(
|
||||
set +C
|
||||
CDLB_SCRIPT_FULL="${hook_copy}"
|
||||
LIVE_BOOT_CMDLINE="verify-checksums=sha512"
|
||||
LIVE_VERIFY_CHECKSUMS=""
|
||||
|
||||
log_begin_msg() { :; }
|
||||
log_end_msg() { :; }
|
||||
log_success_msg() { :; }
|
||||
panic() {
|
||||
printf 'PANIC: %s\n' "${1}" >&2
|
||||
if [[ "${panic_returns}" == "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
exit 97
|
||||
}
|
||||
sleep() { :; }
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "${hook_copy}"
|
||||
Verify_checksums "${case_dir}"
|
||||
) > "${output_file}" 2>&1
|
||||
status="${?}"
|
||||
set -e
|
||||
|
||||
[[ "${status}" -eq "${expected_status}" ]] || {
|
||||
cat "${output_file}" >&2
|
||||
fail "${source_hook} ${case_name}: expected status ${expected_status}, got ${status}"
|
||||
}
|
||||
|
||||
grep -Fq "${expected_message}" "${output_file}" || {
|
||||
cat "${output_file}" >&2
|
||||
fail "${source_hook} ${case_name}: missing expected message '${expected_message}'"
|
||||
}
|
||||
}
|
||||
|
||||
test_rootfs_payload_tamper() {
|
||||
declare case_dir="${TMP_ROOT}/rootfs-payload-tamper"
|
||||
declare test_bin="${case_dir}/bin"
|
||||
declare payload="${case_dir}/crypt_liveiso"
|
||||
declare manifest="${case_dir}/ciss_rootfs.crypt.decrypted.sha512sum.txt"
|
||||
declare build_hook="${ROOT_DIR}/config/hooks/live/zzzz_ciss_crypt_squash.hook.binary"
|
||||
declare boot_hook="${ROOT_DIR}/config/includes.chroot/usr/lib/live/boot/0042_ciss_post_decrypt_attest"
|
||||
|
||||
mkdir -p "${case_dir}" "${test_bin}"
|
||||
install_test_sha512sum "${test_bin}"
|
||||
|
||||
printf 'trusted selected rootfs mapper payload\n' > "${payload}"
|
||||
"${test_bin}/sha512sum" "${payload}" > "${manifest}"
|
||||
"${test_bin}/sha512sum" -c --strict --quiet "${manifest}"
|
||||
|
||||
printf 'tampered selected rootfs mapper payload\n' >> "${payload}"
|
||||
if "${test_bin}/sha512sum" -c --strict --quiet "${manifest}" >/dev/null 2>&1; then
|
||||
fail "Modified selected rootfs payload unexpectedly passed checksum verification."
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
grep -Fq 'sha512sum "${MAPPER_DEV}" >| "${ROOTFS_ATTESTATION}"' "${build_hook}" || \
|
||||
fail "Build hook does not generate the attestation from the decrypted mapper."
|
||||
# shellcheck disable=SC2016
|
||||
grep -Fq '/usr/bin/sha512sum -c --strict --quiet "${HASH_FILE}"' "${boot_hook}" || \
|
||||
fail "Boot hook does not verify the selected rootfs payload with sha512sum -c."
|
||||
# shellcheck disable=SC2016
|
||||
grep -Fq '[ "${_ATTESTED_PAYLOAD}" != "${CDLB_MAPPER_DEV}" ]' "${boot_hook}" || \
|
||||
fail "Boot hook does not require the manifest to target the selected rootfs payload."
|
||||
}
|
||||
|
||||
declare -a CHECKSUM_HOOKS=(
|
||||
"${ROOT_DIR}/config/includes.chroot/usr/lib/live/boot/0030-ciss-verify-checksums"
|
||||
"${ROOT_DIR}/scripts/usr/lib/live/boot/0030-ciss-verify-checksums"
|
||||
)
|
||||
declare checksum_hook=""
|
||||
|
||||
for checksum_hook in "${CHECKSUM_HOOKS[@]}"; do
|
||||
run_checksum_case "${checksum_hook}" "valid" 0 "Verification of [sha checksum] file successful"
|
||||
run_checksum_case "${checksum_hook}" "missing-manifest" 97 "No supported checksum manifest found."
|
||||
run_checksum_case "${checksum_hook}" "unsupported-manifest" 97 "No supported checksum manifest found."
|
||||
run_checksum_case "${checksum_hook}" "failed-checksum" 97 "No supported checksum manifest was verified successfully."
|
||||
run_checksum_case "${checksum_hook}" "missing-tool" 97 "No supported checksum verification tool was available."
|
||||
run_checksum_case "${checksum_hook}" "unknown-state" 1 "Checksum verification ended in an unsupported state."
|
||||
done
|
||||
|
||||
test_rootfs_payload_tamper
|
||||
|
||||
printf 'PASS: checksum verification fails closed and modified rootfs payloads fail attestation.\n'
|
||||
+1
-1
@@ -25,7 +25,7 @@ declare -grx VAR_GIT_HEAD_FULL="$(git rev-parse HEAD)"
|
||||
declare -grx VAR_HOST="$(uname -n)"
|
||||
declare -grx VAR_ISO8601="$(date -u -d "@${VAR_DATE_EPOCH}" '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
declare -grx VAR_SYSTEM="$(uname -mnosv)"
|
||||
declare -grx VAR_VERSION="Master V9.14.022.2026.06.10"
|
||||
declare -grx VAR_VERSION="Master V9.14.024.2026.06.11"
|
||||
declare -grx VAR_VER_BASH="$(bash --version | head -n1 | awk '{
|
||||
# Print $4 and $5; include $6 only if it exists
|
||||
out = $4
|
||||
|
||||
@@ -43,6 +43,7 @@ declare -g VAR_SSHFP="false"
|
||||
declare -g VAR_SSHPORT=""
|
||||
declare -g VAR_SSHPUBKEY=""
|
||||
declare -g VAR_SUITE="trixie"
|
||||
declare -ga ARY_DEBUG_SECRET_VALUES=()
|
||||
declare -ga ARY_HANDLER_JUMPHOST_UNIQUE=()
|
||||
declare -ga ARY_HANDLER_JUMPHOST=()
|
||||
declare -ga ARY_HANDLER_NETCUP_IPV6=()
|
||||
@@ -51,10 +52,12 @@ declare -gi VAR_HANDLER_PRIORITY=0
|
||||
declare -gi VAR_HANDLER_STA=0
|
||||
declare -gi VAR_REIONICE_CLASS=2
|
||||
declare -gi VAR_REIONICE_PRIORITY=4
|
||||
declare -gr CISS_BUILD_DIR_MARKER=".ciss_debian_live_builder_owned"
|
||||
declare -gr VAR_CHROOT_DIR="chroot"
|
||||
declare -gr VAR_PACKAGES_FILE="chroot.packages.live"
|
||||
declare -gx VAR_AGE_KEY=""
|
||||
declare -gx VAR_AGE="false"
|
||||
declare -gx VAR_ARG_SANITIZED=""
|
||||
declare -gx VAR_CDLB_INSIDE_RUNNER="${VAR_CDLB_INSIDE_RUNNER:-false}"
|
||||
declare -gx VAR_CISS_SECUREBOOT_PROFILE="debian-shim"
|
||||
declare -gx VAR_LUKS_KEY=""
|
||||
|
||||
Reference in New Issue
Block a user