Compare commits
19 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
a8454eeadf
|
|||
|
f31ac3503f
|
|||
|
0f28dad6c2
|
|||
|
784c088c0e
|
|||
|
1d130a7027
|
|||
|
7fb6ca2cd2
|
|||
|
009f92aea1
|
|||
|
e11b6285ca
|
|||
|
b59bca727e
|
|||
|
7bb871e3f7
|
|||
|
4633ff5ea7
|
|||
|
bd5c7729a2
|
|||
|
666111df0e
|
|||
|
5cc2110ecb
|
|||
|
f6ca83fb26
|
|||
|
ab827e9c05
|
|||
|
b81b9bf836
|
|||
|
487d2b3ba8
|
|||
|
97596fbcba
|
+80
-1
@@ -46,11 +46,26 @@ Verify_checksums() {
|
||||
|
||||
LIVE_VERIFY_CHECKSUMS_SIGNATURES="false"
|
||||
|
||||
_CHECKSUM_LOG_DIR="${LIVE_VERIFY_CHECKSUMS_LOG_DIR:-/run}"
|
||||
|
||||
_KEYFILE=""
|
||||
|
||||
_MP=""
|
||||
|
||||
_MANIFEST_FOUND="false"
|
||||
|
||||
_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
|
||||
|
||||
case "${_PARAMETER}" in
|
||||
@@ -107,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}"
|
||||
|
||||
@@ -203,10 +226,14 @@ 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
|
||||
|
||||
_TOOL_FOUND="true"
|
||||
|
||||
printf "\e[95m[INFO] Found: [%s] ... \n\e[0m" "/usr/bin/${_DIGEST}sum"
|
||||
|
||||
if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then
|
||||
@@ -234,18 +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="${?}"
|
||||
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
|
||||
|
||||
@@ -265,6 +308,36 @@ Verify_checksums() {
|
||||
log_end_msg
|
||||
printf "\n"
|
||||
|
||||
if [ "${_MANIFEST_FOUND}" != "true" ]; then
|
||||
|
||||
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. 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] Checksum verification was not executed. Checksum verification is fail-closed. \n\e[0m"
|
||||
sleep 8
|
||||
panic "Checksum verification was not executed. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_VERIFICATION_SUCCEEDED}" != "true" ]; then
|
||||
|
||||
printf "\e[91m[FATAL] Checksum verification did not complete successfully. Evaluating fail-closed failure state. \n\e[0m"
|
||||
|
||||
fi
|
||||
|
||||
case "${_RETURN_PGP},${_RETURN_SHA}" in
|
||||
|
||||
"0,0")
|
||||
@@ -304,6 +377,12 @@ Verify_checksums() {
|
||||
panic "Verification of checksum file failed."
|
||||
;;
|
||||
|
||||
*)
|
||||
printf "\e[91m[FATAL] Unknown checksum verification state: [%s,%s]. \n\e[0m" "${_RETURN_PGP:-unset}" "${_RETURN_SHA:-unset}"
|
||||
sleep 8
|
||||
panic "Unknown checksum verification state. Checksum verification is fail-closed."
|
||||
;;
|
||||
|
||||
esac
|
||||
}
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|
||||
@@ -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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18"
|
||||
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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18
|
||||
|
||||
name: 🔁 Render README.md to README.html.
|
||||
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
|
||||
build:
|
||||
counter: 1023
|
||||
version: V9.14.022.2026.06.10
|
||||
version: V9.14.028.2026.06.18
|
||||
# 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.028.2026.06.18
|
||||
# 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.028.2026.06.18
|
||||
# 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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18
|
||||
|
||||
# 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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18
|
||||
|
||||
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.028.2026.06.18"
|
||||
# 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.028.2026.06.18
|
||||
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.028.2026.06.18<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 mounted-medium verification plus 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,15 +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 a `0700 root:root` secret root and single-link regular `0400` or `0600` root-owned files, and any symlink
|
||||
inside the secret path is treated as a hard failure that aborts the run. Filename-only secret arguments reject slashes and
|
||||
traversal.
|
||||
Critical code paths temporarily disable Bash xtrace, and a final exact-value debug-log sanitisation pass provides additional
|
||||
defence in depth. Transient secret files are shredded (`shred -fzu`) as soon as they are no longer needed, but this is only a
|
||||
best-effort cleanup on SSD, NVMe, copy-on-write, journaled, and virtualised storage. Use tmpfs for secrets and encrypted storage
|
||||
for build workspaces. Destructive build cleanup is restricted to the exact canonical directory carrying the
|
||||
`.ciss-live-builder-owned` marker. This private operator workflow still requires strict local path validation; it does not
|
||||
define public ISO release policy.
|
||||
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/)
|
||||
@@ -134,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. During boot, `0024-ciss-crypt-squash` copies `/live/filesystem.squashfs.sha512sum.txt[.sig]` from the real ISO medium to `/run/ciss-rootfs-attestation/`; `0042_ciss_post_decrypt_attest` then verifies that cached manifest/signature pair against `/etc/ciss/keys/<FPR>.gpg` and the exact bytes read from `/dev/mapper/crypt_liveiso`.
|
||||
* 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.
|
||||
@@ -178,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.028.2026.06.18`
|
||||
|
||||
`x.y.z` represents major (x), minor (y), and patch (z) version increments.
|
||||
|
||||
@@ -372,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.
|
||||
|
||||
@@ -496,14 +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 empty build directory with `install -d -m 0700 -o root -g root /opt/cdlb`.
|
||||
3. Create the tmpfs secret root with `install -d -m 0700 -o root -g root /dev/shm/cdlb_secrets`.
|
||||
4. Place required secret files in the secret root as single-link regular, non-symlink, root-owned files with mode `0400`
|
||||
or `0600`.
|
||||
5. Place your desired SSH public key in `/dev/shm/cdlb_secrets/authorized_keys`.
|
||||
6. Place your desired root password in `/dev/shm/cdlb_secrets/password.txt`.
|
||||
7. Use filename-only values without slashes, `.` or `..` for `--key_age`, `--key_luks`, and signing-file arguments.
|
||||
8. Make any other changes you need to.
|
||||
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. 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):
|
||||
|
||||
@@ -512,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 \
|
||||
@@ -532,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
|
||||
@@ -545,10 +547,6 @@ To use **``CISS.debian.live.builder``** as intended, the following baseline is e
|
||||
both the newer Sigstore bundle asset, and the legacy-split certificate/signature assets before checking the downloaded
|
||||
SOPS binary with `sha256sum -c --ignore-missing`.
|
||||
|
||||
On the first run, the builder creates `.ciss-live-builder-owned` in a new or empty build directory whose canonical parent
|
||||
already exists. A populated directory without that marker is rejected and is never adopted automatically. Cleanup remains
|
||||
intentionally destructive inside the exact validated marker-owned directory.
|
||||
|
||||
4. Locate your ISO in the `--build-directory`.
|
||||
5. Boot from the ISO and login to the live image via the console, or the multi-layer secured **coresecret** SSH tunnel.
|
||||
6. Type `sysp` for the final kernel hardening features.
|
||||
@@ -570,11 +568,13 @@ preview it or run it.
|
||||
|
||||
2. Preparation:
|
||||
1. Ensure you are root.
|
||||
2. Create the empty build directory and tmpfs secret root with restrictive ownership and permissions:
|
||||
`install -d -m 0700 -o root -g root /opt/cdlb /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
|
||||
@@ -589,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]
|
||||
@@ -668,10 +668,10 @@ The private directory is ignored by Git. The hooks fail if the CISS EFI image si
|
||||
#...
|
||||
- name: Preparing the build environment.
|
||||
run: |
|
||||
install -d -m 0700 -o root -g root /opt/livebuild /dev/shm/cdlb_secrets
|
||||
umask 0077
|
||||
printf '%s\n' "${{ secrets.CHANGE_ME }}" >| /dev/shm/cdlb_secrets/password.txt
|
||||
printf '%s\n' "${{ secrets.CHANGE_ME }}" >| /dev/shm/cdlb_secrets/authorized_keys
|
||||
mkdir -p /opt/config
|
||||
mkdir -p /opt/livebuild
|
||||
echo "${{ secrets.CHANGE_ME }}" >| /opt/config/password.txt
|
||||
echo "${{ secrets.CHANGE_ME }}" >| /opt/config/authorized_keys
|
||||
#...
|
||||
- name: Starting CISS.debian.live.builder. This may take a while ...
|
||||
run: |
|
||||
@@ -684,9 +684,9 @@ The private directory is ignored by Git. The hooks fail if the CISS EFI image si
|
||||
--build-directory /opt/livebuild \
|
||||
--control "${timestamp}" \
|
||||
--jump-host "${{ secrets.CHANGE_ME }}" \
|
||||
--root-password-file /dev/shm/cdlb_secrets/password.txt \
|
||||
--root-password-file /opt/config/password.txt \
|
||||
--ssh-port CHANGE_ME \
|
||||
--ssh-pubkey /dev/shm/cdlb_secrets
|
||||
--ssh-pubkey /opt/config
|
||||
#...
|
||||
### SKIP OR CHANGE ALL REMAINING STEPS
|
||||
```
|
||||
|
||||
+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.028.2026.06.18<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.028.2026.06.18** (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.028.2026.06.18<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.028.2026.06.18<br>
|
||||
|
||||
# 2. CISS Secure Boot Public Material
|
||||
|
||||
|
||||
+49
-38
@@ -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'
|
||||
@@ -124,31 +125,26 @@ sleep 4
|
||||
printf "\e[95m🧪 %s starting ... \e[0m\n" "${BASH_SOURCE[0]}"
|
||||
declare -grx VAR_SETUP="true"
|
||||
|
||||
### SECURING SECRETS ARTIFACTS.
|
||||
test ! -L "${VAR_TMP_SECRET}" || {
|
||||
. ./var/global.var.sh
|
||||
printf "\e[91m❌ Refusing symlink: '%s'! Bye... \e[0m\n" "${VAR_TMP_SECRET}" >&2
|
||||
exit "${ERR_SECRETSSYM}"
|
||||
}
|
||||
find "${VAR_TMP_SECRET}" -type f -exec chmod 0400 {} +
|
||||
find "${VAR_TMP_SECRET}" -type f -exec chown root:root {} +
|
||||
|
||||
### SOURCING VARIABLES.
|
||||
[[ "${VAR_SETUP}" == true ]] && {
|
||||
source_guard "./var/color.var.sh"
|
||||
source_guard "./var/global.var.sh"
|
||||
}
|
||||
|
||||
### SOURCE THE MINIMUM REQUIRED FOR EARLY EXIT CLEANUP COVERAGE.
|
||||
[[ "${VAR_SETUP}" == true ]] && {
|
||||
source_guard "./lib/lib_secret_validation.sh"
|
||||
source_guard "./lib/lib_build_directory.sh"
|
||||
source_guard "./lib/lib_debug_sanitizer.sh"
|
||||
source_guard "./lib/lib_clean_up.sh"
|
||||
source_guard "./lib/lib_trap_on_err.sh"
|
||||
source_guard "./lib/lib_trap_on_exit.sh"
|
||||
}
|
||||
|
||||
trap 'trap_on_exit "$?" "${BASH_SOURCE[0]}" "${LINENO}" "${FUNCNAME[0]:-main}" "${BASH_COMMAND}"' EXIT
|
||||
|
||||
### Validate the fixed tmpfs secret staging area without modifying operator-provided files.
|
||||
validate_secret_staging_area
|
||||
|
||||
### SOURCING REMAINING LIBRARIES.
|
||||
### SOURCING LIBRARIES.
|
||||
[[ "${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"
|
||||
@@ -164,7 +160,9 @@ validate_secret_staging_area
|
||||
source_guard "./lib/lib_ciss_upgrades_boot.sh"
|
||||
source_guard "./lib/lib_ciss_upgrades_build.sh"
|
||||
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"
|
||||
@@ -178,30 +176,14 @@ validate_secret_staging_area
|
||||
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"
|
||||
source_guard "./lib/lib_update_microcode.sh"
|
||||
source_guard "./lib/lib_usage.sh"
|
||||
}
|
||||
|
||||
### Add ERR handling after all remaining libraries are available.
|
||||
trap 'trap_on_err "$?" "${BASH_SOURCE[0]}" "${LINENO}" "${FUNCNAME[0]:-main}" "${BASH_COMMAND}"' ERR
|
||||
|
||||
### 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
|
||||
|
||||
@@ -227,7 +209,32 @@ 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}"
|
||||
|
||||
### 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
|
||||
|
||||
### Updating Status of Dialog Gauge Bar.
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then printf "XXX\nSanitizing Arguments ... \nXXX\n75\n" >&3; fi
|
||||
@@ -236,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
|
||||
@@ -11,9 +12,10 @@
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
set -Ceuo pipefail
|
||||
|
||||
# Final live-build binary hook for encrypted root filesystem packaging. Preallocate 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.
|
||||
# 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}"
|
||||
|
||||
@@ -63,55 +65,163 @@ preallocate() {
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f preallocate
|
||||
|
||||
declare ROOTFS="${VAR_HANDLER_BUILD_DIR}/binary/live/filesystem.squashfs"
|
||||
declare LUKSFS="${VAR_HANDLER_BUILD_DIR}/binary/live/ciss_rootfs.crypt"
|
||||
#######################################
|
||||
# Validate that the rootfs attestation artifacts exist in the final ISO payload tree.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Rootfs attestation manifest path
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# 42: on failure
|
||||
#######################################
|
||||
require_rootfs_attestation_artifacts() {
|
||||
declare manifest="${1}"
|
||||
declare signature="${manifest}.sig"
|
||||
declare artifact=""
|
||||
|
||||
for artifact in "${manifest}" "${signature}"; do
|
||||
|
||||
if [[ ! -e "${artifact}" ]]; then
|
||||
|
||||
printf "\e[91m❌ Required rootfs attestation artifact missing: [%s]. \e[0m\n" "${artifact}" >&2
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
if [[ -L "${artifact}" || ! -f "${artifact}" ]]; then
|
||||
|
||||
printf "\e[91m❌ Required rootfs attestation artifact is not a regular file: [%s]. \e[0m\n" "${artifact}" >&2
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
if [[ ! -s "${artifact}" ]]; then
|
||||
|
||||
printf "\e[91m❌ Required rootfs attestation artifact is empty: [%s]. \e[0m\n" "${artifact}" >&2
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
if [[ ! -r "${artifact}" ]]; then
|
||||
|
||||
printf "\e[91m❌ Required rootfs attestation artifact is not readable: [%s]. \e[0m\n" "${artifact}" >&2
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f require_rootfs_attestation_artifacts
|
||||
|
||||
#######################################
|
||||
# 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="$(LC_ALL=C sha512sum "${rootfs_file}")"
|
||||
rootfs_hash="${rootfs_hash%% *}"
|
||||
|
||||
if printf '%s %s\n' "${rootfs_hash}" "${rootfs_file}" | LC_ALL=C sha512sum -c --strict --quiet; then
|
||||
|
||||
printf "\e[92m✅ [LC_ALL=C sha512sum -c --strict --quiet of %s] successful. \e[0m\n" "${rootfs_file}"
|
||||
|
||||
else
|
||||
|
||||
printf "\e[91m❌ [LC_ALL=C sha512sum -c --strict --quiet of %s] NOT successful. \e[0m\n" "${rootfs_file}"
|
||||
return 42
|
||||
|
||||
fi
|
||||
|
||||
# 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.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 ${rootfs_size}
|
||||
${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
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f create_attestation
|
||||
|
||||
declare LIVE_PAYLOAD_DIR="${VAR_HANDLER_BUILD_DIR}/binary/live"
|
||||
declare ROOTFS_ATTESTATION_NAME="filesystem.squashfs.sha512sum.txt"
|
||||
declare ROOTFS_ATTESTATION_REL="live/${ROOTFS_ATTESTATION_NAME}"
|
||||
declare LUKSFS="${LIVE_PAYLOAD_DIR}/ciss_rootfs.crypt"
|
||||
declare ROOTFS="${LIVE_PAYLOAD_DIR}/filesystem.squashfs"
|
||||
declare ROOTFS_ATTESTATION="${VAR_HANDLER_BUILD_DIR}/binary/${ROOTFS_ATTESTATION_REL}"
|
||||
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=""
|
||||
declare LUKS_KEY_FILE=""
|
||||
declare LUKS_KEY_FILENAME="${VAR_LUKS_KEY:-luks.txt}"
|
||||
declare LUKS_KEY_LINK_COUNT=""
|
||||
declare LUKS_KEY_MODE=""
|
||||
declare LUKS_KEY_OWNER=""
|
||||
declare SECRET_ROOT_FS=""
|
||||
declare SECRET_ROOT_MODE=""
|
||||
declare SECRET_ROOT_OWNER=""
|
||||
|
||||
if [[ -L "${VAR_TMP_SECRET}" || ! -d "${VAR_TMP_SECRET}" ]]; then
|
||||
printf "\e[91m❌ Unsafe secret root rejected. \e[0m\n" >&2
|
||||
exit 42
|
||||
fi
|
||||
|
||||
SECRET_ROOT_OWNER="$(stat -c '%u' "${VAR_TMP_SECRET}")"
|
||||
SECRET_ROOT_MODE="$(stat -c '%a' "${VAR_TMP_SECRET}")"
|
||||
SECRET_ROOT_FS="$(stat -f -c '%T' "${VAR_TMP_SECRET}")"
|
||||
if [[ "${SECRET_ROOT_OWNER}" != "${EUID}" || "${SECRET_ROOT_MODE}" != "700" \
|
||||
|| ( "${SECRET_ROOT_FS}" != "tmpfs" && "${SECRET_ROOT_FS}" != "ramfs" ) ]]; then
|
||||
printf "\e[91m❌ Unsafe secret-root ownership, permissions, or filesystem rejected. \e[0m\n" >&2
|
||||
exit 42
|
||||
fi
|
||||
|
||||
if [[ -z "${LUKS_KEY_FILENAME}" || "${LUKS_KEY_FILENAME}" == "." || "${LUKS_KEY_FILENAME}" == ".." \
|
||||
|| "${LUKS_KEY_FILENAME}" == */* || ! "${LUKS_KEY_FILENAME}" =~ ^[A-Za-z0-9._@%+=:,~-]+$ ]]; then
|
||||
printf "\e[91m❌ Unsafe LUKS key filename rejected. \e[0m\n" >&2
|
||||
exit 42
|
||||
fi
|
||||
|
||||
LUKS_KEY_FILE="${VAR_TMP_SECRET}/${LUKS_KEY_FILENAME}"
|
||||
if [[ -L "${LUKS_KEY_FILE}" || ! -f "${LUKS_KEY_FILE}" ]]; then
|
||||
printf "\e[91m❌ Unsafe LUKS key file rejected. \e[0m\n" >&2
|
||||
exit 42
|
||||
fi
|
||||
|
||||
LUKS_KEY_OWNER="$(stat -c '%u' "${LUKS_KEY_FILE}")"
|
||||
LUKS_KEY_MODE="$(stat -c '%a' "${LUKS_KEY_FILE}")"
|
||||
LUKS_KEY_LINK_COUNT="$(stat -c '%h' "${LUKS_KEY_FILE}")"
|
||||
if [[ "${LUKS_KEY_OWNER}" != "${EUID}" || "${LUKS_KEY_LINK_COUNT}" != "1" \
|
||||
|| ( "${LUKS_KEY_MODE}" != "400" && "${LUKS_KEY_MODE}" != "600" ) ]]; then
|
||||
printf "\e[91m❌ Unsafe LUKS key ownership, permissions, or link count rejected. \e[0m\n" >&2
|
||||
exit 42
|
||||
fi
|
||||
|
||||
# Keep Argon2 keyslot memory and parallel costs bounded for later initramfs unlocks on smaller systems.
|
||||
declare -i LUKS_PBKDF_MEMORY_KIB=262144
|
||||
declare -i LUKS_PBKDF_PARALLEL=1
|
||||
# shellcheck disable=SC2155
|
||||
declare -i VAR_ROOTFS_SIZE=$(stat -c%s -- "${ROOTFS}")
|
||||
declare -i VAR_ROOTFS_SIZE="$(stat -c%s -- "${ROOTFS}")"
|
||||
|
||||
### 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"
|
||||
|
||||
create_attestation "${ROOTFS}" "${ROOTFS_ATTESTATION}"
|
||||
require_rootfs_attestation_artifacts "${ROOTFS_ATTESTATION}"
|
||||
|
||||
printf "\e[92m✅ Attestation of filesystem.squashfs successful: ISO paths [/%s] and [/%s.sig]. \e[0m\n" \
|
||||
"${ROOTFS_ATTESTATION_REL}" "${ROOTFS_ATTESTATION_REL}"
|
||||
|
||||
### Safety margin:
|
||||
# - LUKS2-Header and Metadata
|
||||
@@ -136,10 +246,12 @@ 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 \
|
||||
--pbkdf-memory "${LUKS_PBKDF_MEMORY_KIB}" \
|
||||
--pbkdf-parallel "${LUKS_PBKDF_PARALLEL}" \
|
||||
--sector-size 4096 \
|
||||
--type luks2 \
|
||||
--use-random \
|
||||
@@ -151,13 +263,16 @@ 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 \
|
||||
--pbkdf-memory "${LUKS_PBKDF_MEMORY_KIB}" \
|
||||
--pbkdf-parallel "${LUKS_PBKDF_PARALLEL}" \
|
||||
--sector-size 4096 \
|
||||
--type luks2 \
|
||||
--use-random \
|
||||
@@ -166,10 +281,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 /dev/mapper/crypt_liveiso)
|
||||
declare -i LUKS_FREE=$(blockdev --getsize64 "${DEVMAP}")
|
||||
declare -i SQUASH_FS="${VAR_ROOTFS_SIZE}"
|
||||
|
||||
if (( LUKS_FREE >= SQUASH_FS )); then
|
||||
@@ -183,9 +298,9 @@ else
|
||||
|
||||
fi
|
||||
|
||||
dd if="${ROOTFS}" of=/dev/mapper/crypt_liveiso bs=8M status=progress conv=fsync
|
||||
dd if="${ROOTFS}" of="${DEVMAP}" bs=8M status=progress conv=fsync
|
||||
sync
|
||||
cryptsetup close crypt_liveiso
|
||||
cryptsetup close "${DM_LAB}"
|
||||
|
||||
exec {KEYFD}<&-
|
||||
|
||||
@@ -193,6 +308,8 @@ shred -fzu -n 5 -- "${LUKS_KEY_FILE}"
|
||||
|
||||
rm -f -- "${ROOTFS}"
|
||||
|
||||
require_rootfs_attestation_artifacts "${ROOTFS_ATTESTATION}"
|
||||
|
||||
umask "${__umask}"
|
||||
__umask=""
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ install -d -m 0755 "${DESTDIR}/usr/sbin"
|
||||
|
||||
|
||||
### Include binaries -----------------------------------------------------------------------------------------------------------
|
||||
for bin in bash blkid busybox dd dmsetup gpgv losetup lsblk mkpasswd mountpoint sha384sum sha512sum sort stty timeout tr tree udevadm whois; do
|
||||
for bin in awk bash blkid busybox dd dmsetup gawk gpgv losetup lsblk mkpasswd mountpoint sha384sum sha512sum sort stty timeout tr tree udevadm whois; do
|
||||
|
||||
path="$(command -v "${bin}" 2>/dev/null || true)"
|
||||
|
||||
@@ -123,7 +123,6 @@ if [ -d "${src_dir}" ]; then
|
||||
|
||||
fi
|
||||
|
||||
|
||||
### Install Dropbear configuration ---------------------------------------------------------------------------------------------
|
||||
install -m 0444 /etc/dropbear/initramfs/dropbear.conf "${DESTDIR}/etc/dropbear/dropbear.conf"
|
||||
printf "\e[92mSuccessfully executed: [install -m 0444 /etc/dropbear/initramfs/dropbear.conf %s/etc/dropbear/dropbear.conf] \n\e[0m" "${DESTDIR}"
|
||||
@@ -146,6 +145,13 @@ printf "\e[92mSuccessfully executed: [install -m 0444 /etc/dropbear/initramfs/ba
|
||||
install -m 0444 /etc/banner "${DESTDIR}/etc/dropbear/banner"
|
||||
printf "\e[92mSuccessfully executed: [install -m 0444 /etc/dropbear/initramfs/banner %s/etc/dropbear/banner] \n\e[0m" "${DESTDIR}"
|
||||
|
||||
### Ensure live-boot runtime scripts in the initramfs are executable -----------------------------------------------------------
|
||||
if [ -d "${DESTDIR}/usr/lib/live/boot" ]; then
|
||||
|
||||
find "${DESTDIR}/usr/lib/live/boot" -type f -exec chmod +x -- {} +
|
||||
printf "\e[92mSuccessfully executed: [find %s/usr/lib/live/boot -type f -exec chmod +x -- {} +] \n\e[0m" "${DESTDIR}"
|
||||
|
||||
fi
|
||||
### EOS
|
||||
|
||||
printf "\e[92mSuccessfully executed: [/etc/initramfs-tools/hooks/9999_ciss_debian_live_builder.sh] \n\e[0m"
|
||||
|
||||
@@ -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.028.2026.06.18
|
||||
|
||||
[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.028.2026.06.18
|
||||
|
||||
### 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.028.2026.06.18
|
||||
|
||||
### 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.028.2026.06.18"
|
||||
|
||||
### 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.028.2026.06.18 at: 10:18:37.9542
|
||||
|
||||
@@ -295,6 +295,10 @@ export CDLB_MAPPER_NAME="crypt_liveiso"
|
||||
export CDLB_MAPPER_DEV="/dev/mapper/${CDLB_MAPPER_NAME}"
|
||||
export CDLB_MNT_MEDIUM="/run/live/medium"
|
||||
export CDLB_MNT_ROOTFS="/run/live/rootfs"
|
||||
export CDLB_ROOTFS_ATTEST_NAME="filesystem.squashfs.sha512sum.txt"
|
||||
export CDLB_ROOTFS_ATTEST_CACHE_DIR="/run/ciss-rootfs-attestation"
|
||||
export CDLB_ROOTFS_ATTEST_MANIFEST="${CDLB_ROOTFS_ATTEST_CACHE_DIR}/${CDLB_ROOTFS_ATTEST_NAME}"
|
||||
export CDLB_ROOTFS_ATTEST_SIGNATURE="${CDLB_ROOTFS_ATTEST_MANIFEST}.sig"
|
||||
export CDLB_REMOTE_WAIT_SECS="${CDLB_REMOTE_WAIT_SECS:-3600}"
|
||||
_PARAMETER=""
|
||||
_dev=""
|
||||
@@ -377,6 +381,66 @@ fi
|
||||
|
||||
printf "\e[92m[INFO] CISS LUKS FS : [%s%s] \n\e[0m" "${CDLB_MNT_MEDIUM}" "${CDLB_LUKS_FS}"
|
||||
|
||||
### Preserve rootfs attestation evidence before live-boot may replace or unmount the medium view. -----------------------------
|
||||
CDLB_ROOTFS_ATTEST_SOURCE="${CDLB_MNT_MEDIUM}/live/${CDLB_ROOTFS_ATTEST_NAME}"
|
||||
CDLB_ROOTFS_ATTEST_SOURCE_SIG="${CDLB_ROOTFS_ATTEST_SOURCE}.sig"
|
||||
|
||||
if [ ! -f "${CDLB_ROOTFS_ATTEST_SOURCE}" ] || [ ! -f "${CDLB_ROOTFS_ATTEST_SOURCE_SIG}" ]; then
|
||||
|
||||
printf "\e[91m[FATAL] Boot failure : Rootfs attestation artifacts not found on live medium: [%s] [%s] \n\e[0m" \
|
||||
"${CDLB_ROOTFS_ATTEST_SOURCE}" "${CDLB_ROOTFS_ATTEST_SOURCE_SIG}"
|
||||
sleep 8
|
||||
log "[FATAL] Boot failure : Rootfs attestation artifacts not found on live medium: [${CDLB_ROOTFS_ATTEST_SOURCE}] [${CDLB_ROOTFS_ATTEST_SOURCE_SIG}]"
|
||||
panic "[FATAL] Boot failure : Rootfs attestation artifacts not found on live medium: [${CDLB_ROOTFS_ATTEST_SOURCE}] [${CDLB_ROOTFS_ATTEST_SOURCE_SIG}]"
|
||||
|
||||
fi
|
||||
|
||||
if ! mkdir -p "${CDLB_ROOTFS_ATTEST_CACHE_DIR}"; then
|
||||
|
||||
printf "\e[91m[FATAL] Boot failure : Failed to create rootfs attestation cache directory: [%s] \n\e[0m" \
|
||||
"${CDLB_ROOTFS_ATTEST_CACHE_DIR}"
|
||||
sleep 8
|
||||
log "[FATAL] Boot failure : Failed to create rootfs attestation cache directory: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
panic "[FATAL] Boot failure : Failed to create rootfs attestation cache directory: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
|
||||
fi
|
||||
|
||||
if ! chmod 0755 "${CDLB_ROOTFS_ATTEST_CACHE_DIR}"; then
|
||||
|
||||
printf "\e[91m[FATAL] Boot failure : Failed to permission rootfs attestation cache directory: [%s] \n\e[0m" \
|
||||
"${CDLB_ROOTFS_ATTEST_CACHE_DIR}"
|
||||
sleep 8
|
||||
log "[FATAL] Boot failure : Failed to permission rootfs attestation cache directory: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
panic "[FATAL] Boot failure : Failed to permission rootfs attestation cache directory: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
|
||||
fi
|
||||
|
||||
if ! cp "${CDLB_ROOTFS_ATTEST_SOURCE}" "${CDLB_ROOTFS_ATTEST_MANIFEST}" || \
|
||||
! cp "${CDLB_ROOTFS_ATTEST_SOURCE_SIG}" "${CDLB_ROOTFS_ATTEST_SIGNATURE}"; then
|
||||
|
||||
printf "\e[91m[FATAL] Boot failure : Failed to preserve rootfs attestation artifacts in: [%s] \n\e[0m" \
|
||||
"${CDLB_ROOTFS_ATTEST_CACHE_DIR}"
|
||||
sleep 8
|
||||
log "[FATAL] Boot failure : Failed to preserve rootfs attestation artifacts in: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
panic "[FATAL] Boot failure : Failed to preserve rootfs attestation artifacts in: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
|
||||
fi
|
||||
|
||||
if ! chmod 0444 "${CDLB_ROOTFS_ATTEST_MANIFEST}" "${CDLB_ROOTFS_ATTEST_SIGNATURE}"; then
|
||||
|
||||
printf "\e[91m[FATAL] Boot failure : Failed to make rootfs attestation cache read-only: [%s] \n\e[0m" \
|
||||
"${CDLB_ROOTFS_ATTEST_CACHE_DIR}"
|
||||
sleep 8
|
||||
log "[FATAL] Boot failure : Failed to make rootfs attestation cache read-only: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
panic "[FATAL] Boot failure : Failed to make rootfs attestation cache read-only: [${CDLB_ROOTFS_ATTEST_CACHE_DIR}]"
|
||||
|
||||
fi
|
||||
|
||||
chmod 0555 "${CDLB_ROOTFS_ATTEST_CACHE_DIR}" 2>&- || true
|
||||
|
||||
printf "\e[92m[INFO] Rootfs attestation : Preserved [%s] and [%s] \n\e[0m" \
|
||||
"${CDLB_ROOTFS_ATTEST_MANIFEST}" "${CDLB_ROOTFS_ATTEST_SIGNATURE}"
|
||||
|
||||
### Attach a loop device read-only to the encrypted file. ----------------------------------------------------------------------
|
||||
if ! LOOP="$(losetup -f --show -r "${CDLB_MNT_MEDIUM}${CDLB_LUKS_FS}")"; then
|
||||
|
||||
@@ -587,6 +651,10 @@ export CDLB_MAPPER_NAME=${CDLB_MAPPER_NAME}
|
||||
export CDLB_MAPPER_DEV=${CDLB_MAPPER_DEV}
|
||||
export CDLB_MNT_MEDIUM=${CDLB_MNT_MEDIUM}
|
||||
export CDLB_MNT_ROOTFS=${CDLB_MNT_ROOTFS}
|
||||
export CDLB_ROOTFS_ATTEST_NAME=${CDLB_ROOTFS_ATTEST_NAME}
|
||||
export CDLB_ROOTFS_ATTEST_CACHE_DIR=${CDLB_ROOTFS_ATTEST_CACHE_DIR}
|
||||
export CDLB_ROOTFS_ATTEST_MANIFEST=${CDLB_ROOTFS_ATTEST_MANIFEST}
|
||||
export CDLB_ROOTFS_ATTEST_SIGNATURE=${CDLB_ROOTFS_ATTEST_SIGNATURE}
|
||||
export CDLB_REMOTE_WAIT_SECS=${CDLB_REMOTE_WAIT_SECS}
|
||||
EOF
|
||||
chmod 0444 /run/ciss-rootdev 2>&- || true
|
||||
|
||||
@@ -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,11 +89,26 @@ Verify_checksums() {
|
||||
|
||||
_CHECKSUM_LOG=""
|
||||
|
||||
_CHECKSUM_LOG_DIR="${LIVE_VERIFY_CHECKSUMS_LOG_DIR:-/run}"
|
||||
|
||||
_KEYFILE=""
|
||||
|
||||
_MANIFEST_FOUND="false"
|
||||
|
||||
_MP=""
|
||||
|
||||
_RETURN_PGP=""
|
||||
|
||||
_RETURN_SHA=""
|
||||
|
||||
_TOOL_FOUND="false"
|
||||
|
||||
_VERIFICATION_EXECUTED="false"
|
||||
|
||||
_VERIFICATION_SUCCEEDED="false"
|
||||
|
||||
### Parse commandline arguments ----------------------------------------------------------------------------------------------
|
||||
# shellcheck disable=SC2154
|
||||
for _PARAMETER in ${LIVE_BOOT_CMDLINE}; do
|
||||
|
||||
case "${_PARAMETER}" in
|
||||
@@ -148,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}"
|
||||
|
||||
@@ -244,10 +269,14 @@ Verify_checksums() {
|
||||
|
||||
if [ -e "${_CHECKSUM}" ]; then
|
||||
|
||||
_MANIFEST_FOUND="true"
|
||||
|
||||
log_in "Found: [${_CHECKSUM}] ..."
|
||||
|
||||
if [ -e "/usr/bin/${_DIGEST}sum" ]; then
|
||||
|
||||
_TOOL_FOUND="true"
|
||||
|
||||
log_in "Found: [/usr/bin/${_DIGEST}sum] ..."
|
||||
|
||||
if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then
|
||||
@@ -275,8 +304,15 @@ 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="${?}"
|
||||
cat "${_CHECKSUM_LOG}" > "${_TTY}"
|
||||
@@ -294,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
|
||||
|
||||
@@ -313,6 +355,36 @@ Verify_checksums() {
|
||||
log_end_msg
|
||||
printf "\n"
|
||||
|
||||
if [ "${_MANIFEST_FOUND}" != "true" ]; then
|
||||
|
||||
log_er "No supported checksum manifest found. Checksum verification is fail-closed."
|
||||
sleep 8
|
||||
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 "Checksum verification was not executed. Checksum verification is fail-closed."
|
||||
sleep 8
|
||||
panic "[FATAL] Checksum verification was not executed. Checksum verification is fail-closed."
|
||||
|
||||
fi
|
||||
|
||||
if [ "${_VERIFICATION_SUCCEEDED}" != "true" ]; then
|
||||
|
||||
log_er "[FATAL] Checksum verification did not complete successfully. Evaluating fail-closed failure state."
|
||||
|
||||
fi
|
||||
|
||||
case "${_RETURN_PGP},${_RETURN_SHA}" in
|
||||
|
||||
"0,0")
|
||||
@@ -345,6 +417,12 @@ Verify_checksums() {
|
||||
panic "Verification of [sha checksum] file failed."
|
||||
;;
|
||||
|
||||
*)
|
||||
log_er "Unknown checksum verification state: [${_RETURN_PGP:-unset},${_RETURN_SHA:-unset}]."
|
||||
sleep 8
|
||||
panic "[FATAL] Unknown checksum verification state. Checksum verification is fail-closed."
|
||||
;;
|
||||
|
||||
esac
|
||||
}
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
# Module summary:
|
||||
# - Runs after the encrypted live root filesystem has been decrypted.
|
||||
# - Requires the pinned public key, attestation hash file, and detached signature to exist as readable, non-empty regular files
|
||||
# inside the decrypted rootfs.
|
||||
# - Verifies the attestation signature with gpgv against the pinned key material.
|
||||
# - Confirms that the signature fingerprint matches the build-time expected rootfs fingerprint and panics on missing, malformed,
|
||||
# or mismatched evidence.
|
||||
|
||||
_SAVED_SET_OPTS="$(set +o)"
|
||||
# - 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
|
||||
|
||||
@@ -36,10 +35,15 @@ 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}"
|
||||
|
||||
### Attestation file locations inside decrypted rootfs. ------------------------------------------------------------------------
|
||||
CDLB_ATTEST_FPR_SHA="${CDLB_ATTEST_FPR_SHA:-/root/root/.ciss/attestation/${CDLB_EXP_FPR}.gpg.sha512sum.txt}"
|
||||
CDLB_ATTEST_FPR_SIG="${CDLB_ATTEST_FPR_SIG:-/root/root/.ciss/attestation/${CDLB_EXP_FPR}.gpg.sha512sum.txt.sig}"
|
||||
### Locations of the attestation file of filesystem.squashfs on the verified live medium. --------------------------------------
|
||||
CDLB_ROOTFS_ATTEST_NAME="${CDLB_ROOTFS_ATTEST_NAME:-filesystem.squashfs.sha512sum.txt}"
|
||||
CDLB_ROOTFS_ATTEST_MANIFEST="${CDLB_ROOTFS_ATTEST_MANIFEST:-${CDLB_MNT_MEDIUM}/live/${CDLB_ROOTFS_ATTEST_NAME}}"
|
||||
CDLB_ROOTFS_ATTEST_SIGNATURE="${CDLB_ROOTFS_ATTEST_SIGNATURE:-${CDLB_ROOTFS_ATTEST_MANIFEST}.sig}"
|
||||
CDLB_ROOTFS_ATTEST_CACHE_DIR="${CDLB_ROOTFS_ATTEST_CACHE_DIR:-/run/ciss-rootfs-attestation}"
|
||||
CDLB_ROOTFS_ATTEST_CHECK="${CDLB_ROOTFS_ATTEST_CHECK:-/run/ciss-rootfs-attestation.sha512sum}"
|
||||
CDLB_KEY_DIR="${CDLB_KEY_DIR:-/etc/ciss/keys}"
|
||||
|
||||
### Declare functions ----------------------------------------------------------------------------------------------------------
|
||||
@@ -71,6 +75,17 @@ log_ok() { printf '\e[92m[INFO] %s \n\e[0m' "$*"; }
|
||||
#######################################
|
||||
log_er() { printf '\e[91m[FATAL] %s \n\e[0m' "$*"; }
|
||||
|
||||
### Provide a local fail-closed fallback when this file is executed as a subprocess outside the live-boot shell context. --------
|
||||
if ! command -v panic >/dev/null 2>&1; then
|
||||
|
||||
panic() {
|
||||
log_er "${*}"
|
||||
printf '%s\n' "0042 FATAL: ${*}" >/dev/console 2>/dev/null || :
|
||||
exit 1
|
||||
}
|
||||
|
||||
fi
|
||||
|
||||
#######################################
|
||||
# Validate a boot-time attestation input file.
|
||||
# Globals:
|
||||
@@ -123,15 +138,230 @@ require_attestation_file() {
|
||||
return 0
|
||||
}
|
||||
|
||||
HASH_FILE="${CDLB_ATTEST_FPR_SHA}"
|
||||
SIGN_FILE="${CDLB_ATTEST_FPR_SIG}"
|
||||
#######################################
|
||||
# Resolve rootfs attestation paths on known live medium mountpoints.
|
||||
# Globals:
|
||||
# CDLB_MNT_MEDIUM
|
||||
# CDLB_ROOTFS_ATTEST_MANIFEST
|
||||
# CDLB_ROOTFS_ATTEST_NAME
|
||||
# CDLB_ROOTFS_ATTEST_SIGNATURE
|
||||
# Arguments:
|
||||
# None
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
resolve_rootfs_attestation_artifacts() {
|
||||
medium_path=""
|
||||
manifest_path=""
|
||||
signature_path=""
|
||||
|
||||
if [ -f "${CDLB_ROOTFS_ATTEST_MANIFEST}" ] && [ -f "${CDLB_ROOTFS_ATTEST_SIGNATURE}" ]; then
|
||||
|
||||
return 0
|
||||
|
||||
fi
|
||||
|
||||
manifest_path="${CDLB_ROOTFS_ATTEST_CACHE_DIR}/${CDLB_ROOTFS_ATTEST_NAME}"
|
||||
signature_path="${manifest_path}.sig"
|
||||
|
||||
if [ -f "${manifest_path}" ] && [ -f "${signature_path}" ]; then
|
||||
|
||||
CDLB_ROOTFS_ATTEST_MANIFEST="${manifest_path}"
|
||||
CDLB_ROOTFS_ATTEST_SIGNATURE="${signature_path}"
|
||||
return 0
|
||||
|
||||
fi
|
||||
|
||||
for medium_path in "${CDLB_MNT_MEDIUM}" /run/live/medium /lib/live/mount/medium /cdrom; do
|
||||
|
||||
[ -n "${medium_path}" ] || continue
|
||||
|
||||
manifest_path="${medium_path}/live/${CDLB_ROOTFS_ATTEST_NAME}"
|
||||
signature_path="${manifest_path}.sig"
|
||||
|
||||
if [ -f "${manifest_path}" ] && [ -f "${signature_path}" ]; then
|
||||
|
||||
CDLB_ROOTFS_ATTEST_MANIFEST="${manifest_path}"
|
||||
CDLB_ROOTFS_ATTEST_SIGNATURE="${signature_path}"
|
||||
return 0
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
log_er "0042() : Rootfs attestation artifacts not found. Expected manifest/signature: [${CDLB_ROOTFS_ATTEST_MANIFEST}] [${CDLB_ROOTFS_ATTEST_SIGNATURE}]"
|
||||
panic "0042() : Rootfs attestation artifacts not found. Expected manifest/signature: [${CDLB_ROOTFS_ATTEST_MANIFEST}] [${CDLB_ROOTFS_ATTEST_SIGNATURE}]"
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Validate the decrypted rootfs payload device.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# 1: Absolute payload device path
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
require_rootfs_payload_device() {
|
||||
artifact_path="${1}"
|
||||
|
||||
if [ ! -e "${artifact_path}" ]; then
|
||||
|
||||
log_er "0042() : Rootfs payload device missing: [${artifact_path}]"
|
||||
panic "0042() : Rootfs payload device missing: [${artifact_path}]"
|
||||
|
||||
fi
|
||||
|
||||
if [ -b "${artifact_path}" ]; then
|
||||
|
||||
:
|
||||
|
||||
elif [ -L "${artifact_path}" ] || [ ! -f "${artifact_path}" ]; then
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#######################################
|
||||
# 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))
|
||||
|
||||
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="${block_size}" skip="${full_blocks}" count=1 2>/dev/null | dd bs=1 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 '/^# Bytes[[:space:]]*:[[:space:]]Final filesystem[.]squashfs[[:space:]]+[0-9]+[[:space:]]*$/ {print $NF; 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
|
||||
}
|
||||
|
||||
resolve_rootfs_attestation_artifacts
|
||||
|
||||
HASH_FILE="${CDLB_ROOTFS_ATTEST_MANIFEST}"
|
||||
SIGN_FILE="${CDLB_ROOTFS_ATTEST_SIGNATURE}"
|
||||
KEYFILE="${CDLB_KEY_DIR}/${CDLB_EXP_FPR}.gpg"
|
||||
|
||||
log_in "0042() : Validating [${KEYFILE}]"
|
||||
require_attestation_file "Public key" "${KEYFILE}"
|
||||
require_attestation_file "Attestation data" "${HASH_FILE}"
|
||||
require_attestation_file "Attestation signature" "${SIGN_FILE}"
|
||||
log_in "0042() : Validating [${HASH_FILE}]"
|
||||
require_attestation_file "Rootfs attestation manifest" "${HASH_FILE}"
|
||||
log_in "0042() : Validating [${SIGN_FILE}]"
|
||||
require_attestation_file "Rootfs attestation signature" "${SIGN_FILE}"
|
||||
log_in "0042() : Validating [${CDLB_MAPPER_DEV}]"
|
||||
require_rootfs_payload_device "${CDLB_MAPPER_DEV}"
|
||||
|
||||
log_in "0042() : Verifying rootfs attestation with 'gpgv' and inside LUKS encrypted rootfs pinned GPG FPR."
|
||||
log_ok "0042() : Rootfs attestation inputs are present and readable."
|
||||
|
||||
log_in "0042() : Verifying rootfs attestation manifest with 'gpgv' and pinned GPG FPR."
|
||||
|
||||
if ! _STATUS="$(/usr/bin/gpgv --keyring "${KEYFILE}" --status-fd 1 "${SIGN_FILE}" "${HASH_FILE}" 2>&1)"; then
|
||||
|
||||
@@ -163,7 +393,7 @@ else
|
||||
|
||||
fi
|
||||
|
||||
eval "${_SAVED_SET_OPTS}"
|
||||
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"
|
||||
|
||||
|
||||
@@ -497,7 +497,23 @@ setup_unionfs ()
|
||||
|
||||
### CISS override for /usr/lib/live/boot/0042_ciss_post_decrypt_attest -------------------------------------------------------
|
||||
printf "\e[95m[INFO] Calling : [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] ... \n\e[0m"
|
||||
[ -x /usr/lib/live/boot/0042_ciss_post_decrypt_attest ] && /usr/lib/live/boot/0042_ciss_post_decrypt_attest
|
||||
|
||||
chmod +x /usr/lib/live/boot/0042_ciss_post_decrypt_attest
|
||||
|
||||
if [ -x /usr/lib/live/boot/0042_ciss_post_decrypt_attest ]; then
|
||||
|
||||
if ! /usr/lib/live/boot/0042_ciss_post_decrypt_attest; then
|
||||
|
||||
panic "[FATAL] [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] failed."
|
||||
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
panic "[FATAL] [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] missing or not executable."
|
||||
|
||||
fi
|
||||
|
||||
printf "\e[92m[INFO] Calling : [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] done. \n\e[0m"
|
||||
### CISS override for /usr/lib/live/boot/0042_ciss_post_decrypt_attest -------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<br>
|
||||
|
||||
# 2. SSH Audit by ssh-audit.com
|
||||
|
||||
|
||||
+51
-53
@@ -8,15 +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.028.2026.06.18<br>
|
||||
|
||||
# 2. TLS Audit:
|
||||
````text
|
||||
./testssl.sh --show-each --wide --phone-out --full https://git.coresecret.dev/
|
||||
|
||||
#####################################################################
|
||||
testssl.sh version 3.2.2 from https://testssl.sh/
|
||||
(2e77f5e 2025-09-22 19:35:27)
|
||||
testssl.sh version 3.2.3 from https://testssl.sh/
|
||||
(0ff7a34 2026-06-01 09:45:44)
|
||||
|
||||
This program is free software. Distribution and modification under
|
||||
GPLv2 permitted. USAGE w/o ANY WARRANTY. USE IT AT YOUR OWN RISK!
|
||||
@@ -27,7 +26,7 @@ include_toc: true
|
||||
Using OpenSSL 1.0.2-bad (Mar 28 2025) [~179 ciphers]
|
||||
on kali:./bin/openssl.Linux.x86_64
|
||||
|
||||
Start 2025-09-28 16:12:17 -->> 152.53.110.40:443 (git.coresecret.dev) <<--
|
||||
Start 2026-06-17 14:44:50 -->> 152.53.110.40:443 (git.coresecret.dev) <<--
|
||||
|
||||
Further IP addresses: 2a0a:4cc0:80:330f:152:53:110:40
|
||||
rDNS (152.53.110.40): git.coresecret.dev.
|
||||
@@ -73,11 +72,11 @@ TLSv1
|
||||
TLSv1.1
|
||||
-
|
||||
TLSv1.2 (server order)
|
||||
xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 448 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 448 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 448 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||
xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 448 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
TLSv1.3 (server order)
|
||||
x1302 TLS_AES_256_GCM_SHA384 ECDH 448 AESGCM 256 TLS_AES_256_GCM_SHA384
|
||||
x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 448 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256
|
||||
x1302 TLS_AES_256_GCM_SHA384 MLKEM1024 AESGCM 256 TLS_AES_256_GCM_SHA384
|
||||
x1303 TLS_CHACHA20_POLY1305_SHA256 MLKEM1024 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256
|
||||
|
||||
Has server cipher order? yes (OK) -- TLS 1.3 and below
|
||||
|
||||
@@ -88,21 +87,21 @@ TLSv1.3 (server order)
|
||||
|
||||
Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC)
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
x1302 TLS_AES_256_GCM_SHA384 ECDH 448 AESGCM 256 TLS_AES_256_GCM_SHA384 available
|
||||
x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 448 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256 available
|
||||
x1302 TLS_AES_256_GCM_SHA384 MLKEM1024 AESGCM 256 TLS_AES_256_GCM_SHA384 available
|
||||
x1303 TLS_CHACHA20_POLY1305_SHA256 MLKEM1024 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256 available
|
||||
xcc14 ECDHE-ECDSA-CHACHA20-POLY1305-OLD ECDH ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD not a/v
|
||||
xcc13 ECDHE-RSA-CHACHA20-POLY1305-OLD ECDH ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD not a/v
|
||||
xcc15 DHE-RSA-CHACHA20-POLY1305-OLD DH ChaCha20 256 TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD not a/v
|
||||
xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 521 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 available
|
||||
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 not a/v
|
||||
xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 not a/v
|
||||
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 521 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 available
|
||||
xc028 ECDHE-RSA-AES256-SHA384 ECDH AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 not a/v
|
||||
xc024 ECDHE-ECDSA-AES256-SHA384 ECDH AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 not a/v
|
||||
xc014 ECDHE-RSA-AES256-SHA ECDH AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA not a/v
|
||||
xc00a ECDHE-ECDSA-AES256-SHA ECDH AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA not a/v
|
||||
xa3 DHE-DSS-AES256-GCM-SHA384 DH AESGCM 256 TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 not a/v
|
||||
x9f DHE-RSA-AES256-GCM-SHA384 DH AESGCM 256 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 not a/v
|
||||
xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 not a/v
|
||||
xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 448 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 available
|
||||
xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 448 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 available
|
||||
xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 not a/v
|
||||
xccaa DHE-RSA-CHACHA20-POLY1305 DH ChaCha20 256 TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 not a/v
|
||||
xc0af ECDHE-ECDSA-AES256-CCM8 ECDH AESCCM8 256 TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 not a/v
|
||||
xc0ad ECDHE-ECDSA-AES256-CCM ECDH AESCCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_CCM not a/v
|
||||
@@ -170,9 +169,10 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
xc086 - ECDH CamelliaGCM 128 TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 not a/v
|
||||
xc08a - ECDH CamelliaGCM 128 TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 not a/v
|
||||
|
||||
KEMs offered MLKEM1024 X25519MLKEM768 SecP384r1MLKEM1024
|
||||
Elliptic curves offered: secp384r1 secp521r1 X448
|
||||
TLS 1.2 sig_algs offered: RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512 RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA+SHA224
|
||||
TLS 1.3 sig_algs offered: RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512
|
||||
TLS 1.2 sig_algs offered: ECDSA+SHA256 ECDSA+SHA384 ECDSA+SHA512 ECDSA+SHA224
|
||||
TLS 1.3 sig_algs offered: ECDSA+SHA384
|
||||
|
||||
Testing server defaults (Server Hello)
|
||||
|
||||
@@ -185,33 +185,33 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
TLS clock skew Random values, no fingerprinting possible
|
||||
Certificate Compression none
|
||||
Client Authentication none
|
||||
Signature Algorithm SHA256 with RSA
|
||||
Server key size RSA 4096 bits (exponent is 65537)
|
||||
Server key usage Digital Signature, Key Encipherment
|
||||
Server extended key usage TLS Web Server Authentication, TLS Web Client Authentication
|
||||
Serial 13292523EB168BD226CE46 (OK: length 11)
|
||||
Fingerprints SHA1 1CCF67686A5FFF33D163EFC9E67AB5C70D1122B8
|
||||
SHA256 565271C2C74AF9EF5F0DCA16453A643C13E43CBD5B87AB82A622E929C48C8B7B
|
||||
Signature Algorithm ECDSA with SHA256
|
||||
Server key size EC 384 bits (curve P-384)
|
||||
Server key usage Digital Signature
|
||||
Server extended key usage TLS Web Server Authentication
|
||||
Serial 85135AE9A772A9778768548CDED9F483 (OK: length 16)
|
||||
Fingerprints SHA1 7745E895B49A44DA786509D124F7BAEF5BCDE21A
|
||||
SHA256 EBAC1DAD82CFAF97644D2F9A03082DE9ABC2B44AD4C86FE6FA202D3EF7243FE4
|
||||
Common Name (CN) coresecret.dev
|
||||
subjectAltName (SAN) coresecret.dev git.coresecret.dev lab.coresecret.dev run.coresecret.dev www.coresecret.dev
|
||||
subjectAltName (SAN) coresecret.dev badges.coresecret.dev cendev.eu git.coresecret.dev lab.coresecret.dev phpmyadmin.git.coresecret.dev
|
||||
run.coresecret.dev uml.coresecret.dev www.coresecret.dev
|
||||
Trust (hostname) Ok via SAN (same w/o SNI)
|
||||
Chain of trust Ok
|
||||
EV cert (experimental) no
|
||||
Certificate Validity (UTC) 178 >= 60 days (2025-09-27 18:27 --> 2026-03-25 22:59)
|
||||
Certificate Validity (UTC) 89 >= 60 days (2026-06-16 00:00 --> 2026-09-14 23:59)
|
||||
ETS/"eTLS", visibility info not present
|
||||
In pwnedkeys.com DB not in database Certificate Revocation List http://crl.buypass.no/crl/BPClass2CA5.crl, not revoked
|
||||
OCSP URI http://ocsp.buypass.com, not revoked
|
||||
In pwnedkeys.com DB not in database
|
||||
Certificate Revocation List --
|
||||
OCSP URI http://ocsp.sectigo.com, not revoked
|
||||
OCSP stapling offered, not revoked
|
||||
OCSP must staple extension --
|
||||
OCSP must staple extension supported
|
||||
DNS CAA RR (experimental) available - please check for match with "Issuer" below
|
||||
communications=error, iodef=mailto:dns@coresecret.eu, issue=;, issue=buypass.no, issue=certum.pl,
|
||||
issue=letsencrypt.org;, issue=quantumsign.eu;, issue=sectigo.com, issuect=quantumsign.eu;, issuect=quantumsign.eu;,
|
||||
issuect=quantumsign.eu;, issuect=quantumsign.eu;, issuect=quantumsign.eu;, issuect=quantumsign.eu;,
|
||||
issuect=quantumsign.eu;, issuect=quantumsign.eu;, issuemail=buypass.no, issuemail=certum.pl, issuewild=;
|
||||
contactemail=caa@coresecret.eu, iodef=mailto:caa@coresecret.eu, issue=;, issue=certum.pl, issue=letsencrypt.org;,
|
||||
issue=quantumsign.eu;, issue=sectigo.com, issuemail=certum.pl, issuevmc=sectigo.com, issuewild=;
|
||||
Certificate Transparency yes (certificate extension)
|
||||
Certificates provided 2
|
||||
Issuer Buypass Class 2 CA 5 (Buypass AS-983163327 from NO)
|
||||
Intermediate cert validity #1: ok > 40 days (2027-05-23 12:57). Buypass Class 2 CA 5 <-- Buypass Class 2 Root CA
|
||||
Issuer ZeroSSL ECC DV SSL CA 2 (ZeroSSL GmbH from AT)
|
||||
Intermediate cert validity #1: ok > 40 days (2035-09-23 23:59). ZeroSSL ECC DV SSL CA 2 <-- Sectigo Public Server Authentication Root E46
|
||||
Intermediate Bad OCSP (exp.) Ok
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
Public Key Pinning --
|
||||
Server banner nginx
|
||||
Application banner --
|
||||
Cookie(s) 2 issued: 2/2 secure, 2/2 HttpOnly
|
||||
Cookie(s) 1 issued: 1/1 secure, 1/1 HttpOnly
|
||||
Security headers X-Frame-Options: SAMEORIGIN
|
||||
X-Content-Type-Options: nosniff
|
||||
Content-Security-Policy: default-src 'self'; connect-src 'self'; font-src 'self' data:; form-action 'self'
|
||||
@@ -236,7 +236,6 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
Cross-Origin-Resource-Policy: cross-origin
|
||||
Cross-Origin-Embedder-Policy: unsafe-none
|
||||
X-XSS-Protection: 1; mode=block
|
||||
Permissions-Policy: interest-cohort=()
|
||||
Referrer-Policy: no-referrer
|
||||
Cache-Control: no-cache
|
||||
Reverse Proxy banner --
|
||||
@@ -257,8 +256,7 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
SWEET32 (CVE-2016-2183, CVE-2016-6329) not vulnerable (OK)
|
||||
FREAK (CVE-2015-0204) not vulnerable (OK)
|
||||
DROWN (CVE-2016-0800, CVE-2016-0703) not vulnerable on this host and port (OK)
|
||||
make sure you don't use this certificate elsewhere with SSLv2 enabled services, see
|
||||
https://search.censys.io/search?resource=hosts&virtual_hosts=INCLUDE&q=565271C2C74AF9EF5F0DCA16453A643C13E43CBD5B87AB82A622E929C48C8B7B
|
||||
no RSA certificate, thus certificate can't be used with SSLv2 elsewhere
|
||||
LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected with <= TLS 1.2
|
||||
BEAST (CVE-2011-3389) not vulnerable (OK), no SSL3 or TLS1
|
||||
LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)
|
||||
@@ -271,24 +269,24 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
Browser Protocol Cipher Suite Name (OpenSSL) Forward Secrecy
|
||||
------------------------------------------------------------------------------------------------
|
||||
Android 7.0 (native) No connection
|
||||
Android 8.1 (native) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
Android 8.1 (native) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
Android 9.0 (native) TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Android 10.0 (native) TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Android 11/12 (native) TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Android 13/14 (native) TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Android 15 (native) TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Android 15 (native) TLSv1.3 TLS_AES_256_GCM_SHA384 X25519MLKEM768
|
||||
Chrome 101 (Win 10) TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Chromium 137 (Win 11) TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Chromium 137 (Win 11) TLSv1.3 TLS_AES_256_GCM_SHA384 X25519MLKEM768
|
||||
Firefox 100 (Win 10) TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
Firefox 137 (Win 11) TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
Firefox 137 (Win 11) TLSv1.3 TLS_AES_256_GCM_SHA384 X25519MLKEM768
|
||||
IE 8 Win 7 No connection
|
||||
IE 11 Win 7 No connection
|
||||
IE 11 Win 8.1 No connection
|
||||
IE 11 Win Phone 8.1 No connection
|
||||
IE 11 Win 10 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
Edge 15 Win 10 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
IE 11 Win 7 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
IE 11 Win 8.1 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
IE 11 Win Phone 8.1 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
IE 11 Win 10 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
Edge 15 Win 10 TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 384 bit ECDH (P-384)
|
||||
Edge 101 Win 10 21H2 TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Edge 133 Win 11 23H2 TLSv1.3 TLS_AES_256_GCM_SHA384 384 bit ECDH (P-384)
|
||||
Edge 133 Win 11 23H2 TLSv1.3 TLS_AES_256_GCM_SHA384 X25519MLKEM768
|
||||
Safari 18.4 (iOS 18.4) TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
Safari 15.4 (macOS 12.3.1) TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
Safari 18.4 (macOS 15.4) TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
@@ -299,11 +297,11 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
Java 21.0.6 (OpenJDK) TLSv1.3 TLS_AES_256_GCM_SHA384 448 bit ECDH (X448)
|
||||
go 1.17.8 TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
LibreSSL 3.3.6 (macOS) TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
OpenSSL 1.0.2e TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 521 bit ECDH (P-521)
|
||||
OpenSSL 1.0.2e TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 521 bit ECDH (P-521)
|
||||
OpenSSL 1.1.1d (Debian) TLSv1.3 TLS_AES_256_GCM_SHA384 448 bit ECDH (X448)
|
||||
OpenSSL 3.0.15 (Debian) TLSv1.3 TLS_AES_256_GCM_SHA384 448 bit ECDH (X448)
|
||||
OpenSSL 3.5.0 (git) TLSv1.3 TLS_AES_256_GCM_SHA384 448 bit ECDH (X448)
|
||||
Apple Mail (16.0) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 521 bit ECDH (P-521)
|
||||
OpenSSL 3.5.0 (git) TLSv1.3 TLS_AES_256_GCM_SHA384 X25519MLKEM768
|
||||
Apple Mail (16.0) TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 521 bit ECDH (P-521)
|
||||
Thunderbird (91.9) TLSv1.3 TLS_AES_256_GCM_SHA384 521 bit ECDH (P-521)
|
||||
|
||||
|
||||
@@ -317,7 +315,7 @@ Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Ciphe
|
||||
Final Score 100
|
||||
Overall Grade A+
|
||||
|
||||
Done 2025-09-28 16:13:50 [ 95s] -->> 152.53.110.40:443 (git.coresecret.dev) <<--
|
||||
Done 2026-06-17 14:46:05 [ 78s] -->> 152.53.110.40:443 (git.coresecret.dev) <<--
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
+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.028.2026.06.18<br>
|
||||
|
||||
# 2. Hardened Kernel Boot Parameters
|
||||
|
||||
|
||||
+25
-2
@@ -8,10 +8,33 @@ 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.028.2026.06.18<br>
|
||||
|
||||
# 2. Changelog
|
||||
|
||||
## V9.14.028.2026.06.18
|
||||
* **Changed**: [0024-ciss-crypt-squash](../config/includes.chroot/usr/lib/live/boot/0024-ciss-crypt-squash) Explicitly permissions the runtime rootfs attestation cache and fails closed on cache creation or chmod errors.
|
||||
* **Changed**: [MAN_CISS_ISO_BOOT_CHAIN.md](MAN_CISS_ISO_BOOT_CHAIN.md) Documents the rootfs attestation artifact custody path from build-time `binary/live` creation through the `0024` runtime cache and `0042` verification.
|
||||
* **Changed**: [README.md](../README.md) Documents the runtime rootfs attestation cache handoff.
|
||||
|
||||
## V9.14.026.2026.06.17
|
||||
* **Updated**: git.coresecret.dev nginx Mainline 1.31.1 custom build with OpenSSL 4.0.1 to support PQC KEX algorithms:
|
||||
* * ``MLKEM1024`` ``SecP384r1MLKEM1024`` ``X25519MLKEM768``
|
||||
* * ECDH: ``X448`` ``secp521r1`` ``secp384r1``
|
||||
* **Updated**: [AUDIT_TLS.md](AUDIT_TLS.md)
|
||||
|
||||
## 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 +219,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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<br>
|
||||
|
||||
# 2. Download the latest PUBLIC CISS.debian.live.ISO
|
||||
|
||||
|
||||
+11
-18
@@ -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.028.2026.06.18<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.028.2026.06.18
|
||||
A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
|
||||
(c) Marc S. Weidner, 2018 - 2026
|
||||
@@ -37,10 +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>
|
||||
The path MUST be canonical and dedicated to the builder; a new directory's canonical parent MUST already exist.
|
||||
New or empty directories receive the
|
||||
'.ciss-live-builder-owned' marker; populated unmarked directories are rejected. Cleanup is intentionally destructive
|
||||
only inside the exact validated marker-owned directory.
|
||||
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>
|
||||
@@ -61,7 +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>
|
||||
A final exact-value sanitisation pass is defence in depth and does not replace careful tracing discipline.
|
||||
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
|
||||
@@ -91,14 +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.
|
||||
'*' MUST be a filename only without slashes, '.' or '..' traversal.
|
||||
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.
|
||||
'*' MUST be a filename only without slashes, '.' or '..' traversal.
|
||||
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
|
||||
@@ -147,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.
|
||||
Safe absolute paths remain supported and are validated separately. RECOMMENDED path:
|
||||
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>
|
||||
@@ -163,8 +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 filename-only files / fingerprint. Filename-only values MUST NOT contain slashes or traversal.
|
||||
Files MUST be placed in:
|
||||
Change '*' to your desired filename-only files / fingerprint. Files MUST be placed in:
|
||||
</dev/shm/cdlb_secrets>
|
||||
|
||||
--sshfp
|
||||
@@ -179,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>
|
||||
|
||||
@@ -190,14 +186,11 @@ A lightweight Shell Wrapper for building a hardened Debian Live ISO Image.
|
||||
|
||||
💡 Notes:
|
||||
🔵 You MUST be 'root' to run this script.
|
||||
🔵 Private operator control does not remove the requirement for strict local secret path validation.
|
||||
🔵 '/dev/shm/cdlb_secrets' MUST be tmpfs-backed, root-owned, mode 0700, and contain only single-link regular non-symlink files
|
||||
with mode 0400 or 0600. Secure deletion with shred is best-effort only on modern storage.
|
||||
|
||||
💷 Please consider donating to my work at:
|
||||
🌐 https://coresecret.eu/spenden/
|
||||
|
||||
V9.14.022.2026.06.10 2026-05-17 CDLB(1)
|
||||
V9.14.028.2026.06.18 2026-05-17 CDLB(1)
|
||||
````
|
||||
|
||||
# 3. Booting
|
||||
|
||||
+164
-57
@@ -8,23 +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.028.2026.06.18<br>
|
||||
|
||||
# 2. CISS.debian.live.builder – Boot & Trust Chain (Technical Documentation)
|
||||
|
||||
**Status:** 2025-11-12<br>
|
||||
**Status:** 2026-06-18<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 early ISO-edge check (signature and FPR pin) *before* LUKS unlock, and a late root-FS attestation *after* unlock, 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>
|
||||
**Summary:** The **CISS.debian.live.builder** Live-ISO establishes a two-stage verification chain around the live root: after the CISS LUKS/dm-integrity container has been opened, and the live medium context has been exposed, `0030-ciss-verify-checksums` verifies the mounted live-medium checksum manifest, detached signature, and signer fingerprint; `0024-ciss-crypt-squash` preserves the rootfs attestation artifacts from the real ISO medium into a stable initramfs runtime cache; later, `0042_ciss_post_decrypt_attest` verifies the signed rootfs attestation manifest, and the exact final SquashFS payload bytes copied into the decrypted LUKS mapper. 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:**
|
||||
* **Integrity and authenticity verification:**
|
||||
|
||||
1. **Early:** Verify `sha512sum.txt` at the ISO edge using `gpgv` and FPR pin.
|
||||
2. **Late:** Verify an attestation hash list inside the decrypted root FS using `gpgv` and FPR pin.
|
||||
1. **Mounted live medium:** After `0024-ciss-crypt-squash` has opened the encrypted container and exposed `/run/live/medium`, verify `sha512sum.txt` using `gpgv`, FPR pinning, and checksum execution.
|
||||
2. **Decrypted rootfs payload:** Preserve the external rootfs attestation manifest and detached signature before live-boot may replace or unmount the medium view, verify the cached manifest using `gpgv` and FPR pinning, then verify the exact SquashFS payload bytes from the decrypted mapper with `sha512sum -c`.
|
||||
|
||||
* **Storage-level AEAD (functional):** `dm-crypt` (AES-XTS-512) and `dm-integrity` (HMAC-SHA-512, 4 KiB).
|
||||
* **Storage-level confidentiality and keyed sector integrity:** `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
|
||||
@@ -50,12 +50,12 @@ private Secure Boot key names are detected in those paths before live-build chec
|
||||
# 4. Primitives & Parameters
|
||||
|
||||
| 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 | Adversary-resistant per-sector integrity, authenticity |
|
||||
| PBKDF | `argon2id`, `--iter-time 1000` ms | Key derivation, hardware-agnostic |
|
||||
| dm-integrity | `hmac-sha512` (keyed), journal | Keyed per-sector integrity for the opened mapping; not origin authenticity |
|
||||
| PBKDF | `argon2id`, `--iter-time 1000` ms, `--pbkdf-memory 262144`, `--pbkdf-parallel 1` | Bounded key derivation cost for initramfs unlock |
|
||||
| Signatures | Ed25519 or RSA-4096 (FPR pinned) | Public verifiability, non-repudiation |
|
||||
| Verification | `gpgv --no-default-keyring` | No agent dependency in initramfs |
|
||||
| Verification | `gpgv --keyring <pinned-keyring>` | Explicit keyring selection and no agent dependency in initramfs |
|
||||
| Hash lists | `sha512sum` format | Deterministic content verification |
|
||||
| Dropbear | Modern KEX/AEAD (per `localoptions.h`) | Minimal attack surface, remote unlock |
|
||||
|
||||
@@ -92,12 +92,14 @@ flowchart TD
|
||||
0090 e09@--> 0100["Starting CISS.hardened dropbear"];
|
||||
0100 e10@--> 0110["Executing live-boot, mounting ISO FS"];
|
||||
0110 e11@--> 0122["Executing 0022-ciss: Hardening tmpfs for OverlayFS upper/work"];
|
||||
0122 e12@--> 0124["Executing 0024-ciss: LUKS open (dm-crypt & integrity)"];
|
||||
0124 e13@--> |SUCCESSFUL| LUKS["Unlocking LUKS2 Argon2id PBKDF → XTS + HMAC-SHA512"];
|
||||
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"];
|
||||
0122 e12@--> 0124["Executing 0024-ciss: Mount ISO medium and locate /live/ciss_rootfs.crypt"];
|
||||
0124 e13@--> CACHE["0024-ciss: Preserve rootfs attestation artifacts in /run/ciss-rootfs-attestation"];
|
||||
CACHE e13b@--> LUKSOPEN["0024-ciss: LUKS open (dm-crypt & integrity)"];
|
||||
LUKSOPEN e13c@--> |SUCCESSFUL| LUKS["Decrypted mapper exposed; livefs_root=/run/live/medium set"];
|
||||
LUKS e14@--> 0126["Executing 0026-ciss: Hardening early sysctls"];
|
||||
0126 e15@--> 0130["Executing 0030-ciss: Mounted live-medium checksum and signature verification"];
|
||||
0130 e16@--> |SUCCESSFUL| ROOT["9990-overlay: Mount SquashFS / OverlayFS"];
|
||||
ROOT e17@--> 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"];
|
||||
@@ -111,6 +113,8 @@ flowchart TD
|
||||
e11@{ animation: fast }
|
||||
e12@{ animation: fast }
|
||||
e13@{ animation: fast }
|
||||
e13b@{ animation: fast }
|
||||
e13c@{ animation: fast }
|
||||
e14@{ animation: fast }
|
||||
e15@{ animation: fast }
|
||||
e16@{ animation: fast }
|
||||
@@ -130,27 +134,52 @@ flowchart TD
|
||||
0030 -- FAIL --> X;
|
||||
0040 -- FAIL --> X;
|
||||
0124 -- FAIL --> X;
|
||||
CACHE -- FAIL --> X;
|
||||
LUKSOPEN -- FAIL --> X;
|
||||
0130 -- FAIL --> X;
|
||||
0142 -- FAIL --> X;
|
||||
```
|
||||
|
||||
# 6. Diagram: CISS Live ISO LUKS and dm-integrity layering
|
||||
|
||||
```text
|
||||
ISO medium
|
||||
└── /live/ciss_rootfs.crypt
|
||||
└── LUKS2 / dm-crypt / dm-integrity
|
||||
└── /dev/mapper/crypt_liveiso
|
||||
└── SquashFS rootfs [SHA-512 over exact SquashFS byte stream]
|
||||
└── OverlayFS / running root filesystem
|
||||
```
|
||||
|
||||
Rootfs attestation evidence follows a separate side path:
|
||||
|
||||
```text
|
||||
ISO medium
|
||||
├── /live/filesystem.squashfs.sha512sum.txt
|
||||
└── /live/filesystem.squashfs.sha512sum.txt.sig
|
||||
└── copied by 0024-ciss-crypt-squash to:
|
||||
├── /run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt
|
||||
└── /run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt.sig
|
||||
```
|
||||
|
||||
The `/run/ciss-rootfs-attestation/` cache is only a stable initramfs runtime location. It is not a trust anchor. `0042_ciss_post_decrypt_attest` still requires the cached manifest to verify against the detached signature, the pinned GPG fingerprint, and the actual decrypted mapper bytes.
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
theme: forest
|
||||
---
|
||||
flowchart TD
|
||||
0{{"Plain device: CD-ROM / USB"}} --> 1["ISO image (ISO9660 + ESP)"];
|
||||
1 --> 2["Mount ISO9660 FS → /run/live/medium"];
|
||||
2 --> 3["Container file /run/live/medium/live/ciss_rootfs.crypt"];
|
||||
3 --> 4["dm-integrity layer (HMAC-SHA-512, 4 KiB)"];
|
||||
4 --> 5["dm-crypt LUKS2 (AES-XTS-512) → /dev/mapper/crypt_liveiso"];
|
||||
5 --> 6["Mount SquashFS from /dev/mapper/crypt_liveiso → /run/live/rootfs"];
|
||||
0{{"Plain device: CD-ROM / USB"}} --> 1["ISO medium (ISO9660 + ESP)"];
|
||||
1 --> 2["/live/ciss_rootfs.crypt"];
|
||||
2 --> 3["LUKS2 / dm-crypt / dm-integrity"];
|
||||
3 --> 4["/dev/mapper/crypt_liveiso"];
|
||||
4 --> 5["SquashFS rootfs byte stream"];
|
||||
5 --> 6["OverlayFS / running root filesystem"];
|
||||
|
||||
```
|
||||
|
||||
**Note:** Encrypt-then-MAC at the block layer (functionally AEAD-equivalent). Any manipulation ⇒ hard I/O error.
|
||||
**Note:** `dm-integrity` provides keyed sector integrity for the opened LUKS mapping. It is not treated as origin authenticity; origin authenticity is provided by the signed checksum and rootfs attestation manifests plus pinned signer fingerprints.
|
||||
|
||||
# 7. CISS Live ISO LUKS Build-Time Core Steps
|
||||
```sh
|
||||
@@ -165,6 +194,8 @@ cryptsetup luksFormat \
|
||||
--luks2-keyslots-size 16777216 \
|
||||
--luks2-metadata-size 4194304 \
|
||||
--pbkdf argon2id \
|
||||
--pbkdf-memory 262144 \
|
||||
--pbkdf-parallel 1 \
|
||||
--sector-size 4096 \
|
||||
--type luks2 \
|
||||
--use-random \
|
||||
@@ -174,9 +205,41 @@ cryptsetup luksFormat \
|
||||
|
||||
**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)
|
||||
## 7.1. Rootfs Attestation Artifacts Created at Build Time
|
||||
|
||||
**Goal:** Before consuming any medium content, verify:
|
||||
`config/hooks/live/zzzz_ciss_crypt_squash.hook.binary` runs in the live-build binary phase after `binary/live/filesystem.squashfs` exists and before the final ISO image is emitted.
|
||||
|
||||
The hook expects:
|
||||
|
||||
| Artifact | Build-time path | Purpose |
|
||||
|-----------------------------|------------------------------------------------------------|-----------------------------------------------------------------------------------------|
|
||||
| Final plaintext SquashFS | `${VAR_HANDLER_BUILD_DIR}/binary/live/filesystem.squashfs` | Source byte stream that will be attested and copied into the encrypted mapper. |
|
||||
| Signing key passphrase file | `${VAR_SIGNING_KEY_PASSFILE}` | Unlocks the configured signing key without exposing the passphrase on the command line. |
|
||||
| Verification keyring | `${VAR_VERIFY_KEYRING}` | Build-time self-check for the detached signature before the ISO is accepted. |
|
||||
|
||||
The hook creates:
|
||||
|
||||
| Artifact | Build-time path | ISO path |
|
||||
|---------------------------------------|------------------------------------------------------------------------------|-----------------------------------------------|
|
||||
| Encrypted live root container | `${VAR_HANDLER_BUILD_DIR}/binary/live/ciss_rootfs.crypt` | `/live/ciss_rootfs.crypt` |
|
||||
| Rootfs attestation manifest | `${VAR_HANDLER_BUILD_DIR}/binary/live/filesystem.squashfs.sha512sum.txt` | `/live/filesystem.squashfs.sha512sum.txt` |
|
||||
| Rootfs attestation detached signature | `${VAR_HANDLER_BUILD_DIR}/binary/live/filesystem.squashfs.sha512sum.txt.sig` | `/live/filesystem.squashfs.sha512sum.txt.sig` |
|
||||
|
||||
The manifest format is intentionally small and deterministic:
|
||||
|
||||
```text
|
||||
# CISS.debian.live.builder Master <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 <exact-byte-count>
|
||||
<sha512-of-final-filesystem.squashfs> filesystem.squashfs
|
||||
```
|
||||
|
||||
The signed boundary is the final SquashFS byte stream before LUKS wrapping. The hook writes that byte stream into `/dev/mapper/crypt_liveiso`, closes the mapper, shreds the transient LUKS key file, removes `binary/live/filesystem.squashfs`, and keeps only `/live/ciss_rootfs.crypt` plus the manifest/signature pair in the final ISO payload tree.
|
||||
|
||||
# 8. Mounted Live-Medium Checksum Verification (CISS modified hook 0030-ciss-verify-checksums, live-bottom)
|
||||
|
||||
**Goal:** After `0024-ciss-crypt-squash` has opened the encrypted container and exposed the live medium context, but before the final live root is accepted, 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.
|
||||
@@ -185,30 +248,56 @@ cryptsetup luksFormat \
|
||||
**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 Attestation and dmsetup Health (CISS hook 0042_ciss_post_decrypt_attest, called by 9990-overlay.sh)
|
||||
|
||||
**Goal:** After LUKS unlock, validate the **decrypted** contents and the **actual** mapping topology.
|
||||
**Goal:** After LUKS unlocked, and the live root has been mounted by `9990-overlay.sh`, validate the **decrypted** rootfs payload selected at build time and the **actual** mapping topology.
|
||||
|
||||
* **Attestation files:** `/root/.ciss/attestation/<FPR>.sha512sum.txt[.sig]`
|
||||
* **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 byte count declared by `# Bytes : Final filesystem.squashfs <bytes>` in the signed manifest, read from the decrypted mapper. Any LUKS allocation slack after the SquashFS payload is intentionally excluded.
|
||||
* **ISO attestation files:** `/run/live/medium/live/filesystem.squashfs.sha512sum.txt[.sig]` while the original ISO medium is mounted by `0024-ciss-crypt-squash`.
|
||||
* **Runtime attestation cache:** `/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt[.sig]`, copied by `0024-ciss-crypt-squash` before live-boot may replace or unmount the medium view during `toram` handling.
|
||||
* **Key source:** `/etc/ciss/keys/*.gpg` (accepted only if FPR == build-pin)
|
||||
|
||||
## 9.1. Runtime Artifact Custody and Expectations
|
||||
|
||||
| Step | Actor | Requires | Copies / writes | Later consumer |
|
||||
|------|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
|
||||
| 1 | `0024-ciss-crypt-squash` | Mounted ISO medium at `/run/live/medium`; `/run/live/medium/live/ciss_rootfs.crypt`; `/run/live/medium/live/filesystem.squashfs.sha512sum.txt`; `/run/live/medium/live/filesystem.squashfs.sha512sum.txt.sig` | Copies the manifest to `/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt` and the detached signature to `/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt.sig`; sets the cache directory to `0755` before copy, cached files to `0444`, and best-effort final directory mode to `0555` | `0042_ciss_post_decrypt_attest` |
|
||||
| 2 | `0024-ciss-crypt-squash` | `/run/live/medium/live/ciss_rootfs.crypt`; unlock passphrase from console or Dropbear path | Opens the encrypted container as `/dev/mapper/crypt_liveiso`; writes `/run/ciss-rootdev` with mapper, medium, and attestation-cache paths | `9990-overlay.sh` |
|
||||
| 3 | `9990-main.sh` | `/conf/param.conf` with `PLAIN_ROOT=1` and `livefs_root=/run/live/medium`; optional `toram` boot parameter | May copy live media to RAM and may leave `/run/live/medium` busy, replaced, or otherwise unsuitable as the only attestation source | `9990-overlay.sh` and `0042_ciss_post_decrypt_attest` |
|
||||
| 4 | `9990-overlay.sh` | `/run/ciss-rootdev`; `/dev/mapper/crypt_liveiso` | Sources `/run/ciss-rootdev`, overrides the image directory to `/dev/mapper/crypt_liveiso`, mounts the decrypted SquashFS read-only, and invokes `/usr/lib/live/boot/0042_ciss_post_decrypt_attest` | `0042_ciss_post_decrypt_attest` |
|
||||
| 5 | `0042_ciss_post_decrypt_attest` | `/etc/ciss/keys/<pinned-FPR>.gpg`; `/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt`; `/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt.sig`; `/dev/mapper/crypt_liveiso` | Creates transient `/run/ciss-rootfs-attestation.sha512sum` for `sha512sum -c`; does not create trusted evidence | Boot continues only after signature, FPR, and exact payload bytes all verify |
|
||||
|
||||
`0042_ciss_post_decrypt_attest` resolves artifacts in this order:
|
||||
|
||||
1. The explicit manifest/signature paths exported through `/run/ciss-rootdev`.
|
||||
2. The default runtime cache under `/run/ciss-rootfs-attestation/`.
|
||||
3. Compatibility fallback mountpoints: `${CDLB_MNT_MEDIUM}`, `/run/live/medium`, `/lib/live/mount/medium`, and `/cdrom`.
|
||||
|
||||
The fallback mountpoints are diagnostic and compatibility paths. The intended normal path for current CISS ISOs is the runtime cache copied by `0024-ciss-crypt-squash`.
|
||||
|
||||
**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"
|
||||
DATA="/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt"
|
||||
SIG="${DATA}.sig"
|
||||
KEYFILE="/etc/ciss/keys/<pinned-FPR>.gpg"
|
||||
/usr/bin/gpgv --keyring "${KEYFILE}" --status-fd 1 "${SIG}" "${DATA}"
|
||||
|
||||
# 2) Optional: Content hash verification
|
||||
( cd "$ROOTMP" && /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
|
||||
```
|
||||
|
||||
# 10. Failure Policy (fail-closed, deterministic)
|
||||
|
||||
* **Abort** on: missing `VALIDSIG`, FPR mismatch, missing key / signature.
|
||||
* **Abort** on: missing checksum manifest, unsupported checksum manifest/tool state, failed checksum, empty checksum manifest, missing rootfs attestation artifacts on the real ISO medium during `0024`, failed preservation of the runtime attestation cache, missing cached rootfs manifest/signature during `0042`, 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
|
||||
|
||||
@@ -233,59 +322,77 @@ cryptsetup luksFormat \
|
||||
* [9990-main.sh](../config/includes.chroot/usr/lib/live/boot/9990-main.sh),
|
||||
* [9990-networking.sh](../config/includes.chroot/usr/lib/live/boot/9990-networking.sh),
|
||||
* [9990-overlay.sh](../config/includes.chroot/usr/lib/live/boot/9990-overlay.sh).
|
||||
* **Hooks (boot view):**
|
||||
* `/scripts/live-premount/0022-ciss-overlay-tmpfs`,
|
||||
* `/scripts/live-premount/0024-ciss-crypt-squash`,
|
||||
* `/scripts/live-premount/0026-ciss-early-sysctl`,
|
||||
* `/scripts/live-bottom/0030-ciss-verify-checksums`,
|
||||
* `/scripts/live-bottom/0042-ciss-post-decrypt-attest`
|
||||
* **Hooks (initramfs boot view):**
|
||||
* `/usr/lib/live/boot/0022-ciss-overlay-tmpfs`,
|
||||
* `/usr/lib/live/boot/0024-ciss-crypt-squash`,
|
||||
* `/usr/lib/live/boot/0026-ciss-early-sysctl`,
|
||||
* `/usr/lib/live/boot/0030-ciss-verify-checksums`,
|
||||
* `/usr/lib/live/boot/0042_ciss_post_decrypt_attest`,
|
||||
* `/usr/lib/live/boot/9990-main.sh`,
|
||||
* `/usr/lib/live/boot/9990-overlay.sh`
|
||||
* **Key files:**
|
||||
* ISO edge (for 0030): embedded public key blob (project-specific FPR)
|
||||
* Mounted live medium (for 0030): embedded public key blob (project-specific FPR)
|
||||
* Root FS (for 0042): `/etc/ciss/keys/<FPR>.gpg`
|
||||
* **Mounts (typical):** `/run/live/rootfs`, `/run/live/overlay`
|
||||
* **Rootfs attestation artifacts:**
|
||||
* ISO payload paths: `/live/filesystem.squashfs.sha512sum.txt`, `/live/filesystem.squashfs.sha512sum.txt.sig`
|
||||
* Runtime cache paths: `/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt`, `/run/ciss-rootfs-attestation/filesystem.squashfs.sha512sum.txt.sig`
|
||||
* Transient checksum file for exact mapper-byte verification: `/run/ciss-rootfs-attestation.sha512sum`
|
||||
* **Runtime handoff state:** `/run/ciss-rootdev`
|
||||
* **Mounts (typical):** `/run/live/medium`, `/run/live/rootfs`, `/run/live/overlay`
|
||||
|
||||
# 13. Diagram: CISS Live ISO Build, Boot, and Run Time Trust Chain & Verification Paths
|
||||
```mermaid
|
||||
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"];
|
||||
A["Embed and pin GPG FPR (into ISO & RootFS as needed)"] e00@--> B["Generate mounted-medium sha512sum.txt and .sig"];
|
||||
B e01@--> C["Build filesystem.squashfs"];
|
||||
C e01b@--> C2["Generate rootfs attestation manifest and detached signature in binary/live"];
|
||||
C2 e01c@--> C3["Copy filesystem.squashfs into ciss_rootfs.crypt and remove plaintext filesystem.squashfs"];
|
||||
e00@{ animation: fast }
|
||||
e01@{ animation: fast }
|
||||
e01b@{ animation: fast }
|
||||
e01c@{ animation: fast }
|
||||
end
|
||||
|
||||
subgraph ISO Boot Time
|
||||
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 post-decrypt-attestation of RootFS"];
|
||||
I e08@-->|SUCCESSFUL| J["RootFS attestation successful"];
|
||||
C3 e02@--> D["0024 mounts real ISO medium and expects ciss_rootfs.crypt plus rootfs attestation files under /live"];
|
||||
D e02b@--> DCACHE["0024 copies rootfs attestation files to /run/ciss-rootfs-attestation"];
|
||||
DCACHE e03@--> E["0024 opens ciss_rootfs.crypt with LUKS2/dm-integrity and exposes /dev/mapper/crypt_liveiso"];
|
||||
E e04@--> F["0030 verifies mounted live-medium manifest, signature, FPR, and checksums"];
|
||||
F e05@-->|SUCCESSFUL| G["Mounted live medium verified"];
|
||||
G e06@--> H["9990-overlay mounts SquashFS / OverlayFS"];
|
||||
H e07@--> I["0042 verifies cached rootfs attestation manifest and FPR"];
|
||||
I e08@--> J["0042 verifies exact SquashFS bytes from /dev/mapper/crypt_liveiso"];
|
||||
J e09@-->|SUCCESSFUL| K["RootFS SquashFS payload attestation successful"];
|
||||
e02@{ animation: fast }
|
||||
e02b@{ animation: fast }
|
||||
e03@{ animation: fast }
|
||||
e04@{ animation: fast }
|
||||
e05@{ animation: fast }
|
||||
e06@{ animation: fast }
|
||||
e07@{ animation: fast }
|
||||
e08@{ animation: fast }
|
||||
end
|
||||
|
||||
subgraph ISO Run Time
|
||||
J e09@--> K{{"CISS.debian.live.builder ISO running"}};
|
||||
X{{"CISS.debian.live.builder Boot process halted"}};
|
||||
e09@{ animation: fast }
|
||||
end
|
||||
|
||||
subgraph ISO Run Time
|
||||
K e10@--> L{{"CISS.debian.live.builder ISO running"}};
|
||||
X{{"CISS.debian.live.builder Boot process halted"}};
|
||||
e10@{ animation: fast }
|
||||
end
|
||||
|
||||
D -- FAIL --> X;
|
||||
G -- FAIL --> X;
|
||||
DCACHE -- FAIL --> X;
|
||||
E -- FAIL --> X;
|
||||
F -- FAIL --> X;
|
||||
I -- FAIL --> X;
|
||||
J -- FAIL --> X;
|
||||
```
|
||||
|
||||
# 14. Closing Remarks
|
||||
|
||||
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.
|
||||
This achieves a portable, self-contained trust chain without a Microsoft-db, providing strong protection at the mounted-medium and decrypted-rootfs-payload boundaries. The dual-verification phases make the state transparent and deterministic without treating `dm-integrity`, LUKS, or private infrastructure as substitutes for origin authenticity.
|
||||
|
||||
---
|
||||
**[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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<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.028.2026.06.18<br>
|
||||
|
||||
# 2. ``ciss_live_builder.sh``
|
||||
|
||||
|
||||
+91
-24
@@ -57,23 +57,24 @@ guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
# None
|
||||
# Returns:
|
||||
# ERR_ARG_MSMTCH: on failure
|
||||
# ERR_BUILD_PATH: on failure
|
||||
# ERR_CONTROL_CT: on failure
|
||||
# ERR_DROPBEAR_V: on failure
|
||||
# ERR_MISS_PWD_F: on failure
|
||||
# ERR_MISS_PWD_P: on failure
|
||||
# ERR_NOTABSPATH: on failure
|
||||
# ERR_OWNS_PWD_F: on failure
|
||||
# ERR_PASS_LENGH: on failure
|
||||
# ERR_PASS_PLICY: on failure
|
||||
# ERR_REIONICE_P: on failure
|
||||
# ERR_REIO_C_VAL: on failure
|
||||
# ERR_REIO_P_VAL: on failure
|
||||
# ERR_RENICE_PRI: on failure
|
||||
# ERR_SECRET_PATH: on failure
|
||||
# ERR_RGHT_PWD_F: on failure
|
||||
# ERR_SPLASH_PNG: on failure
|
||||
# ERR__SOPS__VER: on failure
|
||||
# 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
|
||||
@@ -106,6 +107,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
|
||||
@@ -131,9 +142,11 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--build-directory)
|
||||
declare build_directory="${2-}"
|
||||
validate_build_directory_path "${build_directory}" build_directory || exit "${ERR_BUILD_PATH}"
|
||||
declare -gx VAR_HANDLER_BUILD_DIR="${build_directory}"
|
||||
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
|
||||
exit "${ERR_NOTABSPATH}"
|
||||
fi
|
||||
declare -gx VAR_BUILD_LOG="${VAR_HANDLER_BUILD_DIR}/cdlb_${VAR_ISO8601}_build.log"
|
||||
shift 2
|
||||
;;
|
||||
@@ -260,22 +273,28 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--key_age=*)
|
||||
declare age_key="${1#*=}"
|
||||
validate_secret_file_in_root "${age_key}" "SOPS Age key" || exit "${ERR_SECRET_PATH}"
|
||||
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="${age_key}"
|
||||
declare -gx VAR_AGE_KEY="${key_age}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
--key_luks=*)
|
||||
declare luks_key="${1#*=}"
|
||||
validate_secret_file_in_root "${luks_key}" "LUKS key file" || exit "${ERR_SECRET_PATH}"
|
||||
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="${luks_key}"
|
||||
declare -gx VAR_LUKS_KEY="${key_luks}"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
@@ -294,7 +313,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}"
|
||||
@@ -313,7 +332,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}"
|
||||
@@ -459,7 +478,7 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--root-password-file)
|
||||
declare pw_file="${2}"
|
||||
declare pw_file="${2-}"
|
||||
|
||||
if [[ -z "${pw_file}" ]]; then
|
||||
|
||||
@@ -471,7 +490,45 @@ arg_parser() {
|
||||
|
||||
fi
|
||||
|
||||
validate_secret_absolute_file "${pw_file}" "root password file" || exit "${ERR_SECRET_PATH}"
|
||||
if ! validate_secret_absolute_file_basics "--root-password-file" "${pw_file}"; then
|
||||
|
||||
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_MISS_PWD_F}"
|
||||
|
||||
fi
|
||||
|
||||
declare owner
|
||||
owner=$(stat -c '%U:%G' "${pw_file}")
|
||||
if [[ "${owner}" != "root:root" ]]; then
|
||||
chown root:root "${pw_file}" || {
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
printf "\e[91m❌ Error: --root-password-file failed to set owner root:root on '%s'.\e[0m\n" "${pw_file}" >&2
|
||||
# shellcheck disable=SC2162
|
||||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||||
exit "${ERR_OWNS_PWD_F}"
|
||||
}
|
||||
fi
|
||||
|
||||
declare perms
|
||||
perms=$(stat -c '%a' "${pw_file}")
|
||||
if [[ "${perms}" -ne 400 ]]; then
|
||||
chmod 0400 "${pw_file}" || {
|
||||
if ! ${VAR_HANDLER_AUTOBUILD}; then boot_screen_cleaner; fi
|
||||
printf "\e[91m❌ Error: --root-password-file failed to set permissions 0400 on '%s'.\e[0m\n" "${pw_file}" >&2
|
||||
# shellcheck disable=SC2162
|
||||
read -p $'\e[92m✅ Press \'ENTER\' to exit the script ... \e[0m'
|
||||
exit "${ERR_RGHT_PWD_F}"
|
||||
}
|
||||
fi
|
||||
|
||||
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 ----------------------------------------------------------------------------------
|
||||
@@ -524,7 +581,6 @@ arg_parser() {
|
||||
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x
|
||||
|
||||
hash_temp=$(mkpasswd --method=sha-512 --salt="${salt}" --rounds=8388608 "${plaintext_pw}")
|
||||
register_secret_value "${hash_temp}"
|
||||
|
||||
### Turn on tracing again --------------------------------------------------------------------------------------------
|
||||
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set -x
|
||||
@@ -574,7 +630,10 @@ arg_parser() {
|
||||
|
||||
--signing_ca=*)
|
||||
declare signing_ca="${1#*=}"
|
||||
validate_secret_file_in_root "${signing_ca}" "signing CA file" || exit "${ERR_SECRET_PATH}"
|
||||
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="${signing_ca}"
|
||||
shift 1
|
||||
@@ -582,7 +641,10 @@ arg_parser() {
|
||||
|
||||
--signing_key=*)
|
||||
declare signing_key="${1#*=}"
|
||||
validate_secret_file_in_root "${signing_key}" "signing key file" || exit "${ERR_SECRET_PATH}"
|
||||
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
|
||||
@@ -598,7 +660,10 @@ arg_parser() {
|
||||
|
||||
--signing_key_pass=*)
|
||||
declare signing_key_pass="${1#*=}"
|
||||
validate_secret_file_in_root "${signing_key_pass}" "signing passphrase file" || exit "${ERR_SECRET_PATH}"
|
||||
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="${signing_key_pass}"
|
||||
shift 1
|
||||
@@ -667,11 +732,13 @@ arg_parser() {
|
||||
;;
|
||||
|
||||
--ssh-pubkey)
|
||||
declare ssh_pubkey_dir="${2-}"
|
||||
validate_secret_absolute_directory "${ssh_pubkey_dir}" "SSH public-key directory" || exit "${ERR_SECRET_PATH}"
|
||||
validate_secret_file "${ssh_pubkey_dir}/authorized_keys" "SSH authorized_keys file" || exit "${ERR_SECRET_PATH}"
|
||||
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="${ssh_pubkey_dir}"
|
||||
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
|
||||
@@ -1,340 +0,0 @@
|
||||
#!/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
|
||||
|
||||
guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
|
||||
#######################################
|
||||
# Prints a generic build-directory validation error without disclosing a path.
|
||||
# Arguments:
|
||||
# 1: unsafe input class
|
||||
# 2: quiet flag
|
||||
# Returns:
|
||||
# ERR_BUILD_PATH
|
||||
#######################################
|
||||
build_directory_validation_error() {
|
||||
declare error_class="$1" quiet="${2:-false}"
|
||||
|
||||
if [[ "${quiet}" != "true" ]]; then
|
||||
printf "\e[91m❌ Unsafe build-directory input rejected: %s. \e[0m\n" "${error_class}" >&2
|
||||
fi
|
||||
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f build_directory_validation_error
|
||||
|
||||
#######################################
|
||||
# Canonicalises an existing path or a path whose parent exists.
|
||||
# Arguments:
|
||||
# 1: candidate path
|
||||
# 2: output variable name
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_BUILD_PATH: on failure
|
||||
#######################################
|
||||
canonicalize_build_directory() {
|
||||
declare candidate="$1" output_variable="$2" basename="" parent="" resolved_path=""
|
||||
|
||||
[[ -n "${candidate}" && "${candidate}" == /* ]] || return "${ERR_BUILD_PATH:-217}"
|
||||
[[ "${candidate}" == "/" ]] || candidate="${candidate%/}"
|
||||
[[ ! -L "${candidate}" ]] || return "${ERR_BUILD_PATH:-217}"
|
||||
|
||||
if [[ -e "${candidate}" ]]; then
|
||||
resolved_path="$(realpath "${candidate}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
else
|
||||
basename="${candidate##*/}"
|
||||
parent="${candidate%/*}"
|
||||
[[ -n "${parent}" ]] || parent="/"
|
||||
[[ -n "${basename}" && "${basename}" != "." && "${basename}" != ".." && -d "${parent}" ]] || return "${ERR_BUILD_PATH:-217}"
|
||||
resolved_path="$(realpath "${parent}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
resolved_path="${resolved_path%/}/${basename}"
|
||||
fi
|
||||
|
||||
[[ "${candidate}" == "${resolved_path}" ]] || return "${ERR_BUILD_PATH:-217}"
|
||||
printf -v "${output_variable}" '%s' "${resolved_path}"
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f canonicalize_build_directory
|
||||
|
||||
#######################################
|
||||
# Validates a build directory against the repository cleanup policy.
|
||||
# Globals:
|
||||
# VAR_TMP_SECRET
|
||||
# VAR_WORKDIR
|
||||
# Arguments:
|
||||
# 1: candidate path
|
||||
# 2: output variable name
|
||||
# 3: quiet flag
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_BUILD_PATH: on failure
|
||||
#######################################
|
||||
validate_build_directory_path() {
|
||||
declare candidate="$1" output_variable="${2:-}" quiet="${3:-false}" secret_root="" validated_path="" workdir=""
|
||||
declare -a rejected_paths=(
|
||||
"/" "/bin" "/boot" "/dev" "/etc" "/home" "/lib" "/lib64" "/media" "/mnt" "/opt" "/proc" "/root" "/run" "/sbin" "/srv"
|
||||
"/sys" "/tmp" "/usr" "/usr/local" "/var" "/var/lib" "/var/tmp"
|
||||
)
|
||||
declare rejected_path=""
|
||||
|
||||
canonicalize_build_directory "${candidate}" validated_path || {
|
||||
build_directory_validation_error \
|
||||
"path is empty, non-absolute, non-canonical, missing its parent, or is a symlink" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
}
|
||||
|
||||
for rejected_path in "${rejected_paths[@]}"; do
|
||||
if [[ "${validated_path}" == "${rejected_path}" ]]; then
|
||||
build_directory_validation_error "broad or system parent directory" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
done
|
||||
|
||||
workdir="$(realpath "${VAR_WORKDIR}" 2>/dev/null)" || {
|
||||
build_directory_validation_error "repository work directory cannot be resolved" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
}
|
||||
secret_root="$(realpath "${VAR_TMP_SECRET}" 2>/dev/null)" || {
|
||||
build_directory_validation_error "secret root cannot be resolved" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
}
|
||||
|
||||
if [[ "${workdir}" == "${validated_path}" || "${workdir}" == "${validated_path}/"* || "${validated_path}" == "${workdir}/"* \
|
||||
|| "${validated_path}" == "${secret_root}" || "${validated_path}" == "${secret_root}/"* ]]; then
|
||||
build_directory_validation_error "path is outside the dedicated build-directory policy" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
if [[ -n "${output_variable}" ]]; then
|
||||
printf -v "${output_variable}" '%s' "${validated_path}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_build_directory_path
|
||||
|
||||
#######################################
|
||||
# Validates the builder-owned marker for an exact build directory.
|
||||
# Globals:
|
||||
# EUID
|
||||
# Arguments:
|
||||
# 1: candidate path
|
||||
# 2: quiet flag
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_BUILD_PATH: on failure
|
||||
#######################################
|
||||
validate_build_directory_marker() {
|
||||
declare candidate="$1" quiet="${2:-false}" directory_mode="" directory_owner="" marker="" marker_build_dir=""
|
||||
declare expected_marker_value="" marker_link_count="" marker_value="" mode="" owner=""
|
||||
|
||||
validate_build_directory_path "${candidate}" marker_build_dir "${quiet}" || return "${ERR_BUILD_PATH:-217}"
|
||||
[[ -d "${marker_build_dir}" ]] || {
|
||||
build_directory_validation_error "build directory does not exist" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
}
|
||||
|
||||
directory_owner="$(secure_stat -c '%u' "${marker_build_dir}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
directory_mode="$(secure_stat -c '%a' "${marker_build_dir}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
if [[ "${directory_owner}" != "${EUID}" ]] || (( (8#${directory_mode} & 022) != 0 )); then
|
||||
build_directory_validation_error "build directory ownership or permissions are unsafe" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
marker="${marker_build_dir}/.ciss-live-builder-owned"
|
||||
if [[ -L "${marker}" || ! -f "${marker}" ]]; then
|
||||
build_directory_validation_error "builder-owned marker is missing or unsafe" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
owner="$(secure_stat -c '%u' "${marker}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
mode="$(secure_stat -c '%a' "${marker}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
marker_link_count="$(secure_stat -c '%h' "${marker}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
marker_value="$(cat "${marker}" || exit $?; printf '.')" || return "${ERR_BUILD_PATH:-217}"
|
||||
marker_value="${marker_value%.}"
|
||||
expected_marker_value="${marker_build_dir}"$'\n'
|
||||
|
||||
if [[ "${owner}" != "${EUID}" || "${mode}" != "400" || "${marker_link_count}" != "1" \
|
||||
|| "${marker_value}" != "${expected_marker_value}" ]]; then
|
||||
build_directory_validation_error "builder-owned marker does not match the exact directory" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_build_directory_marker
|
||||
|
||||
#######################################
|
||||
# Validates an existing exact subpath below a marker-owned build directory.
|
||||
# Arguments:
|
||||
# 1: build directory
|
||||
# 2: relative subpath
|
||||
# 3: output variable name
|
||||
# 4: quiet flag
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_BUILD_PATH: on failure
|
||||
#######################################
|
||||
validate_build_directory_subpath() {
|
||||
declare build_directory="$1" relative_path="$2" output_variable="$3" quiet="${4:-false}"
|
||||
declare candidate_subpath="" resolved_subpath=""
|
||||
|
||||
validate_build_directory_marker "${build_directory}" "${quiet}" || return "${ERR_BUILD_PATH:-217}"
|
||||
|
||||
if [[ -z "${relative_path}" || "${relative_path}" == /* || "${relative_path}" == "." || "${relative_path}" == ".." \
|
||||
|| "${relative_path}" == ../* || "${relative_path}" == */../* || "${relative_path}" == */.. \
|
||||
|| "${relative_path}" == ./* || "${relative_path}" == */./* || "${relative_path}" == */. ]]; then
|
||||
build_directory_validation_error "unsafe relative cleanup subpath" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
candidate_subpath="${build_directory}/${relative_path}"
|
||||
if [[ -L "${candidate_subpath}" || ! -e "${candidate_subpath}" ]]; then
|
||||
build_directory_validation_error "cleanup subpath is missing or is a symlink" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
resolved_subpath="$(realpath "${candidate_subpath}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
if [[ "${resolved_subpath}" != "${candidate_subpath}" || "${resolved_subpath}" != "${build_directory}/"* ]]; then
|
||||
build_directory_validation_error "cleanup subpath escapes the exact build directory" "${quiet}"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
printf -v "${output_variable}" '%s' "${resolved_subpath}"
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_build_directory_subpath
|
||||
|
||||
#######################################
|
||||
# Initialises a new or empty build directory and its ownership marker.
|
||||
# Arguments:
|
||||
# 1: candidate path
|
||||
# 2: output variable name
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_BUILD_PATH: on failure
|
||||
#######################################
|
||||
initialize_build_directory() {
|
||||
declare candidate="$1" output_variable="$2" directory_mode="" directory_owner="" existing_entry=""
|
||||
declare initialized_build_dir="" marker=""
|
||||
|
||||
validate_build_directory_path "${candidate}" initialized_build_dir || return "${ERR_BUILD_PATH:-217}"
|
||||
|
||||
if [[ ! -e "${initialized_build_dir}" ]]; then
|
||||
mkdir -m 0700 "${initialized_build_dir}" || return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
validate_build_directory_path "${initialized_build_dir}" initialized_build_dir || return "${ERR_BUILD_PATH:-217}"
|
||||
[[ -d "${initialized_build_dir}" && ! -L "${initialized_build_dir}" ]] || return "${ERR_BUILD_PATH:-217}"
|
||||
|
||||
directory_owner="$(secure_stat -c '%u' "${initialized_build_dir}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
directory_mode="$(secure_stat -c '%a' "${initialized_build_dir}" 2>/dev/null)" || return "${ERR_BUILD_PATH:-217}"
|
||||
if [[ "${directory_owner}" != "${EUID}" ]] || (( (8#${directory_mode} & 022) != 0 )); then
|
||||
build_directory_validation_error "build directory ownership or permissions are unsafe"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
marker="${initialized_build_dir}/.ciss-live-builder-owned"
|
||||
|
||||
if [[ -e "${marker}" || -L "${marker}" ]]; then
|
||||
validate_build_directory_marker "${initialized_build_dir}" || return "${ERR_BUILD_PATH:-217}"
|
||||
else
|
||||
existing_entry="$(find "${initialized_build_dir}" -mindepth 1 -maxdepth 1 -print -quit)" || return "${ERR_BUILD_PATH:-217}"
|
||||
if [[ -n "${existing_entry}" ]]; then
|
||||
build_directory_validation_error "non-empty directory has no builder-owned marker"
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
(umask 077; printf '%s\n' "${initialized_build_dir}" >| "${marker}") || return "${ERR_BUILD_PATH:-217}"
|
||||
chmod 0400 "${marker}" || return "${ERR_BUILD_PATH:-217}"
|
||||
validate_build_directory_marker "${initialized_build_dir}" || return "${ERR_BUILD_PATH:-217}"
|
||||
fi
|
||||
|
||||
printf -v "${output_variable}" '%s' "${initialized_build_dir}"
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f initialize_build_directory
|
||||
|
||||
#######################################
|
||||
# Removes paths with a one-filesystem boundary where supported.
|
||||
# Arguments:
|
||||
# paths to remove
|
||||
# Returns:
|
||||
# rm exit status
|
||||
#######################################
|
||||
remove_build_paths() {
|
||||
# shellcheck disable=SC2312
|
||||
if rm --help 2>&1 | grep -q -- '--one-file-system'; then
|
||||
rm -rf --one-file-system -- "$@"
|
||||
else
|
||||
rm -rf -- "$@"
|
||||
fi
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f remove_build_paths
|
||||
|
||||
#######################################
|
||||
# Deletes all content except the ownership marker from an exact build directory.
|
||||
# Arguments:
|
||||
# 1: candidate path
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_BUILD_PATH: on failure
|
||||
#######################################
|
||||
clean_build_directory_contents() {
|
||||
declare candidate="$1" build_entry=""
|
||||
declare -a build_entries=()
|
||||
declare -i old_dotglob=0 old_failglob=0 old_nullglob=0
|
||||
|
||||
validate_build_directory_marker "${candidate}" || return "${ERR_BUILD_PATH:-217}"
|
||||
|
||||
shopt -q dotglob && old_dotglob=1
|
||||
shopt -q failglob && old_failglob=1
|
||||
shopt -q nullglob && old_nullglob=1
|
||||
shopt -s dotglob nullglob
|
||||
shopt -u failglob
|
||||
|
||||
build_entries=("${candidate}"/*)
|
||||
for build_entry in "${build_entries[@]}"; do
|
||||
[[ "${build_entry}" == "${candidate}/.ciss-live-builder-owned" ]] && continue
|
||||
remove_build_paths "${build_entry}" || {
|
||||
if (( old_dotglob )); then shopt -s dotglob; else shopt -u dotglob; fi
|
||||
if (( old_failglob )); then shopt -s failglob; else shopt -u failglob; fi
|
||||
if (( old_nullglob )); then shopt -s nullglob; else shopt -u nullglob; fi
|
||||
return "${ERR_BUILD_PATH:-217}"
|
||||
}
|
||||
done
|
||||
|
||||
if (( old_dotglob )); then shopt -s dotglob; else shopt -u dotglob; fi
|
||||
if (( old_failglob )); then shopt -s failglob; else shopt -u failglob; fi
|
||||
if (( old_nullglob )); then shopt -s nullglob; else shopt -u nullglob; fi
|
||||
|
||||
validate_build_directory_marker "${candidate}" || return "${ERR_BUILD_PATH:-217}"
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f clean_build_directory_contents
|
||||
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|
||||
+27
-13
@@ -23,27 +23,41 @@ guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
# 0: on success
|
||||
#######################################
|
||||
x_remove() {
|
||||
declare luks_key_filename="${VAR_LUKS_KEY:-luks.txt}" luks_key_path="" signing_pass_path=""
|
||||
declare -a find_args=("${VAR_TMP_SECRET}" -xdev -type f)
|
||||
|
||||
printf "\e[95m🧪 %s starting ... \e[0m\n" "${BASH_SOURCE[0]}"
|
||||
|
||||
validate_secret_staging_area || return "${ERR_SECRET_PATH}"
|
||||
declare _old_nullglob="" _old_dotglob=""
|
||||
|
||||
### Enable nullglob/dotglob, disable failglob for safe globbing.
|
||||
_old_nullglob="$(shopt -p nullglob || true)"
|
||||
_old_dotglob="$( shopt -p dotglob || true)"
|
||||
|
||||
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
|
||||
validate_secret_file_in_root "${VAR_SIGNING_KEY_PASS}" "signing passphrase file" || return "${ERR_SECRET_PATH}"
|
||||
signing_pass_path="${VAR_TMP_SECRET}/${VAR_SIGNING_KEY_PASS}"
|
||||
find_args+=(! -path "${signing_pass_path}")
|
||||
fi
|
||||
|
||||
validate_secret_file_in_root "${luks_key_filename}" "LUKS key file" || return "${ERR_SECRET_PATH}"
|
||||
luks_key_path="${VAR_TMP_SECRET}/${luks_key_filename}"
|
||||
find_args+=(! -path "${luks_key_path}")
|
||||
|
||||
# shellcheck disable=SC2312
|
||||
find "${find_args[@]}" -print0 | xargs -0 --no-run-if-empty shred -fzu -n 5 --
|
||||
find "${VAR_TMP_SECRET}" -xdev -type f \
|
||||
! -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 --
|
||||
|
||||
else
|
||||
|
||||
### Removes secrets securely.
|
||||
# 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
|
||||
|
||||
fi
|
||||
|
||||
eval "${_old_nullglob}" 2>/dev/null || true
|
||||
eval "${_old_dotglob}" 2>/dev/null || true
|
||||
|
||||
printf "\e[92m✅ %s successfully applied. \e[0m\n" "${BASH_SOURCE[0]}"
|
||||
|
||||
return 0
|
||||
|
||||
@@ -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
|
||||
@@ -14,9 +15,10 @@ 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.*
|
||||
# - /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
|
||||
|
||||
+43
-25
@@ -32,7 +32,16 @@ guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
# 0: on success
|
||||
#######################################
|
||||
clean_up() {
|
||||
declare chroot_directory="" clean_exit_code="$1" fs_type="" includes_directory=""
|
||||
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)"
|
||||
_old_dotglob="$( shopt -p dotglob || true)"
|
||||
_old_failglob="$(shopt -p failglob || true)"
|
||||
|
||||
shopt -s nullglob dotglob
|
||||
shopt -u failglob
|
||||
|
||||
if [[ -e /dev/mapper/crypt_liveiso ]]; then
|
||||
cryptsetup close crypt_liveiso || true
|
||||
@@ -92,40 +101,49 @@ clean_up() {
|
||||
### No tracing for security reasons ------------------------------------------------------------------------------------------
|
||||
[[ "${VAR_EARLY_DEBUG}" == "true" ]] && set +x
|
||||
|
||||
### Removes secrets securely only after re-validating the fixed tmpfs staging area.
|
||||
if validate_secret_staging_area "true"; then
|
||||
### 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 -- || true
|
||||
find "${VAR_TMP_SECRET}" -xdev -depth -type d -empty -delete || true
|
||||
else
|
||||
printf "\e[93m⚠ Secret cleanup skipped because the staging area failed validation. \e[0m\n" >&2
|
||||
|
||||
fi
|
||||
|
||||
### Destructive build cleanup requires the exact builder-owned directory marker.
|
||||
if [[ -n "${VAR_HANDLER_BUILD_DIR}" ]] && validate_build_directory_marker "${VAR_HANDLER_BUILD_DIR}" "true"; then
|
||||
if [[ -e "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot" || -L "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot" ]]; then
|
||||
if validate_build_directory_subpath "${VAR_HANDLER_BUILD_DIR}" "config/includes.chroot" includes_directory "true"; then
|
||||
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.
|
||||
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 "${includes_directory}" -xdev -type f -print0 | xargs -0 --no-run-if-empty shred -fzu -n 5 -- || true
|
||||
find "${includes_directory}" -depth -xdev -type d -empty -delete || true
|
||||
else
|
||||
printf "\e[93m⚠ Build includes cleanup skipped because the exact subpath failed validation. \e[0m\n" >&2
|
||||
fi
|
||||
find "${includes_chroot}" -xdev -type f -print0 | xargs -0 --no-run-if-empty shred -fzu -n 5 -- || true
|
||||
|
||||
### Remove empty directories (bottom-up).
|
||||
find "${includes_chroot}" -depth -xdev -type d -empty -delete || true
|
||||
|
||||
fi
|
||||
|
||||
if [[ -e "${VAR_HANDLER_BUILD_DIR}/chroot" || -L "${VAR_HANDLER_BUILD_DIR}/chroot" ]]; then
|
||||
if validate_build_directory_subpath "${VAR_HANDLER_BUILD_DIR}" "chroot" chroot_directory "true"; then
|
||||
remove_build_paths "${chroot_directory}" || true
|
||||
else
|
||||
printf "\e[93m⚠ Build chroot cleanup skipped because the exact subpath failed validation. \e[0m\n" >&2
|
||||
fi
|
||||
fi
|
||||
elif [[ -n "${VAR_HANDLER_BUILD_DIR}" ]]; then
|
||||
printf "\e[93m⚠ Build-directory cleanup skipped because the exact builder-owned marker failed validation. \e[0m\n" >&2
|
||||
### Delete all files and directories below ./chroot.
|
||||
if [[ "${build_dir_valid}" == "true" && -d "${VAR_HANDLER_BUILD_DIR}/chroot" ]]; then
|
||||
|
||||
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
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/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
|
||||
|
||||
guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
|
||||
#######################################
|
||||
# Replaces exact registered secret values in one controlled log file.
|
||||
# Globals:
|
||||
# _ARY_SECRET_REDACTION_VALUES
|
||||
# Arguments:
|
||||
# 1: log file
|
||||
# Returns:
|
||||
# 0: on success or missing log
|
||||
# ERR_SANITIZING: on failure
|
||||
#######################################
|
||||
sanitize_debug_log() {
|
||||
declare log_file="$1" log_text="" replacement="" secret_value="" tmp_file=""
|
||||
|
||||
[[ -n "${log_file}" && -f "${log_file}" ]] || return 0
|
||||
[[ ! -L "${log_file}" ]] || return "${ERR_SANITIZING:-133}"
|
||||
|
||||
log_text="$(cat "${log_file}" || exit $?; printf '.')" || return "${ERR_SANITIZING:-133}"
|
||||
log_text="${log_text%.}"
|
||||
|
||||
for secret_value in "${_ARY_SECRET_REDACTION_VALUES[@]}"; do
|
||||
[[ -n "${secret_value}" ]] || continue
|
||||
printf -v replacement '%*s' "${#secret_value}" ''
|
||||
replacement="${replacement// /*}"
|
||||
log_text="${log_text//"${secret_value}"/"${replacement}"}"
|
||||
done
|
||||
|
||||
tmp_file="$(mktemp "${log_file}.sanitize.XXXXXX")" || return "${ERR_SANITIZING:-133}"
|
||||
chmod 0600 "${tmp_file}" || {
|
||||
rm -f "${tmp_file}"
|
||||
return "${ERR_SANITIZING:-133}"
|
||||
}
|
||||
printf '%s' "${log_text}" >| "${tmp_file}" || {
|
||||
rm -f "${tmp_file}"
|
||||
return "${ERR_SANITIZING:-133}"
|
||||
}
|
||||
mv -f "${tmp_file}" "${log_file}" || {
|
||||
rm -f "${tmp_file}"
|
||||
return "${ERR_SANITIZING:-133}"
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f sanitize_debug_log
|
||||
|
||||
#######################################
|
||||
# Runs the final exact-value sanitisation pass for controlled logs.
|
||||
# Globals:
|
||||
# LOG_DEBUG
|
||||
# LOG_ERROR
|
||||
# LOG_VAR
|
||||
# Arguments:
|
||||
# None
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SANITIZING: on failure
|
||||
#######################################
|
||||
sanitize_debug_logs() {
|
||||
declare log_file=""
|
||||
declare -a log_files=("${LOG_DEBUG:-}" "${LOG_VAR:-}" "${LOG_ERROR:-}")
|
||||
|
||||
set +x
|
||||
if [[ -e "/proc/$$/fd/42" || -e "/dev/fd/42" ]]; then
|
||||
exec 42>&-
|
||||
fi
|
||||
|
||||
for log_file in "${log_files[@]}"; do
|
||||
sanitize_debug_log "${log_file}" || return "${ERR_SANITIZING:-133}"
|
||||
done
|
||||
|
||||
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
-8
@@ -41,12 +41,6 @@ init_gnupg() {
|
||||
|
||||
if [[ "${VAR_SIGNER}" == "true" ]]; then
|
||||
|
||||
validate_secret_file_in_root "${VAR_SIGNING_KEY}" "signing key file" || return "${ERR_SECRET_PATH}"
|
||||
validate_secret_file_in_root "${VAR_SIGNING_KEY_PASS}" "signing passphrase file" || return "${ERR_SECRET_PATH}"
|
||||
if [[ -n "${VAR_SIGNING_CA}" ]]; then
|
||||
validate_secret_file_in_root "${VAR_SIGNING_CA}" "signing CA file" || return "${ERR_SECRET_PATH}"
|
||||
fi
|
||||
|
||||
__umask=$(umask)
|
||||
umask 0077
|
||||
|
||||
@@ -78,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 ------------------------------------------------------------------------------------------
|
||||
@@ -88,7 +88,6 @@ EOF
|
||||
|
||||
declare __pw=""
|
||||
__pw="$(<"${VAR_SIGNING_KEY_PASSFILE}")"; __pw="${__pw%$'\r'}"; printf '%s' "${__pw}" >| "${VAR_SIGNING_KEY_PASSFILE}"
|
||||
register_secret_value "${__pw}"
|
||||
__pw="" && unset __pw
|
||||
|
||||
### Turn on tracing again ----------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -182,8 +182,7 @@ hardening_ultra() {
|
||||
printf "\e[95m🧪 Updating SSH Keys, Ports ... \e[0m\n"
|
||||
|
||||
### ./config/includes.chroot/root/.ssh ---------------------------------------------------------------------------------------
|
||||
validate_secret_absolute_directory "${VAR_SSHPUBKEY}" "SSH public-key directory" || return "${ERR_SECRET_PATH}"
|
||||
validate_secret_file "${VAR_SSHPUBKEY}/authorized_keys" "SSH authorized_keys file" || return "${ERR_SECRET_PATH}"
|
||||
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/"
|
||||
|
||||
|
||||
@@ -23,22 +23,34 @@ guard_sourcing || return "${ERR_GUARD_SRCE}"
|
||||
# 0: on success
|
||||
#######################################
|
||||
lb_config_start() {
|
||||
declare canonical_build_dir=""
|
||||
|
||||
printf "\e[95m🧪 %s starting ... \e[0m\n" "${BASH_SOURCE[0]}"
|
||||
|
||||
initialize_build_directory "${VAR_HANDLER_BUILD_DIR}" canonical_build_dir || return "${ERR_BUILD_PATH}"
|
||||
VAR_HANDLER_BUILD_DIR="${canonical_build_dir}"
|
||||
cd "${VAR_HANDLER_BUILD_DIR}" || return "${ERR_BUILD_PATH}"
|
||||
if [[ ! -d ${VAR_HANDLER_BUILD_DIR} ]]; then
|
||||
|
||||
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
|
||||
|
||||
if [[ -d "${VAR_HANDLER_BUILD_DIR}/.build" ]]; then
|
||||
|
||||
validate_build_directory_marker "${VAR_HANDLER_BUILD_DIR}" || return "${ERR_BUILD_PATH}"
|
||||
# shellcheck disable=SC2164
|
||||
cd "${VAR_HANDLER_BUILD_DIR}"
|
||||
|
||||
printf "\e[95m🧪 Deleting former config, binary and cache ... \e[0m\n"
|
||||
|
||||
lb clean --binary --cache --purge --source
|
||||
clean_build_directory_contents "${VAR_HANDLER_BUILD_DIR}" || return "${ERR_BUILD_PATH}"
|
||||
|
||||
safe_clean_build_dir_contents "${VAR_HANDLER_BUILD_DIR}"
|
||||
|
||||
printf "\e[92m✅ Deleting former config, binary and cache done.\e[0m\n"
|
||||
|
||||
|
||||
+16
-22
@@ -84,7 +84,8 @@ init_primordial() {
|
||||
### Check for SOPS AGE key integration ---------------------------------------------------------------------------------------
|
||||
if [[ "${VAR_AGE,,}" == "true" ]]; then
|
||||
|
||||
validate_secret_file_in_root "${VAR_AGE_KEY}" "SOPS Age key" || return "${ERR_SECRET_PATH}"
|
||||
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,35 +94,28 @@ init_primordial() {
|
||||
|
||||
### Check for SSH CISS and PhysNet Primordial-Workflow™ integration -------------------------------------------------------
|
||||
if [[ "${VAR_SSHFP,,}" == "true" ]]; then
|
||||
declare secret_key_file=""
|
||||
declare -a identity_files=() host_key_files=()
|
||||
|
||||
validate_secret_directory "${VAR_TMP_SECRET}" "secret root" "true" || return "${ERR_SECRET_PATH}"
|
||||
# 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
|
||||
|
||||
while IFS= read -r -d '' secret_key_file; do
|
||||
validate_secret_file "${secret_key_file}" "primordial SSH identity file" || return "${ERR_SECRET_PATH}"
|
||||
identity_files+=("${secret_key_file}")
|
||||
done < <(find "${VAR_TMP_SECRET}" -maxdepth 1 -type f -name 'id*' -print0)
|
||||
|
||||
while IFS= read -r -d '' secret_key_file; do
|
||||
validate_secret_file "${secret_key_file}" "primordial SSH host-key file" || return "${ERR_SECRET_PATH}"
|
||||
host_key_files+=("${secret_key_file}")
|
||||
done < <(find "${VAR_TMP_SECRET}" -maxdepth 1 -type f -name 'ssh_host_*' -print0)
|
||||
|
||||
(( ${#identity_files[@]} > 0 && ${#host_key_files[@]} > 0 )) || {
|
||||
secret_validation_error "required primordial SSH key files are missing"
|
||||
return "${ERR_SECRET_PATH}"
|
||||
}
|
||||
# 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 "${identity_files[@]}" "${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"
|
||||
shred -fzu -n 5 -- "${identity_files[@]}" 2>/dev/null || rm -f "${identity_files[@]}"
|
||||
shred -fzu -n 5 -- "${VAR_TMP_SECRET}/id"* 2>/dev/null || rm -f "${VAR_TMP_SECRET}/id"*
|
||||
|
||||
install -d -m 0700 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/ssh"
|
||||
install -m 0600 "${host_key_files[@]}" "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/ssh/"
|
||||
install -m 0600 "${VAR_TMP_SECRET}/ssh_host_"* "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/ssh/"
|
||||
normalize_ssh_keys_in_dir "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/ssh/"
|
||||
shred -fzu -n 5 -- "${host_key_files[@]}" 2>/dev/null || rm -f "${host_key_files[@]}"
|
||||
shred -fzu -n 5 -- "${VAR_TMP_SECRET}/ssh_host_"* 2>/dev/null || rm -f "${VAR_TMP_SECRET}/ssh_host_"*
|
||||
|
||||
fi
|
||||
|
||||
|
||||
+279
-257
@@ -9,134 +9,69 @@
|
||||
# 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}"
|
||||
|
||||
if ! declare -p _ARY_SECRET_REDACTION_VALUES >/dev/null 2>&1; then
|
||||
declare -ga _ARY_SECRET_REDACTION_VALUES=()
|
||||
fi
|
||||
|
||||
#######################################
|
||||
# Runs GNU stat on Debian and gstat on macOS development hosts.
|
||||
# Print a validation error without echoing secret values.
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# stat arguments
|
||||
# 1: Validation label.
|
||||
# 2: Error detail.
|
||||
# Returns:
|
||||
# stat exit status
|
||||
#######################################
|
||||
secure_stat() {
|
||||
if command -v gstat >/dev/null 2>&1; then
|
||||
gstat "$@"
|
||||
else
|
||||
stat "$@"
|
||||
fi
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f secure_stat
|
||||
|
||||
#######################################
|
||||
# Prints a generic secret validation error without disclosing a path or value.
|
||||
# Arguments:
|
||||
# 1: unsafe input class
|
||||
# 2: quiet flag
|
||||
# Returns:
|
||||
# ERR_SECRET_PATH
|
||||
# 0: on success
|
||||
#######################################
|
||||
secret_validation_error() {
|
||||
declare error_class="$1" quiet="${2:-false}"
|
||||
declare label="$1" detail="$2"
|
||||
|
||||
if [[ "${quiet}" != "true" ]]; then
|
||||
printf "\e[91m❌ Unsafe secret input rejected: %s. \e[0m\n" "${error_class}" >&2
|
||||
fi
|
||||
printf "\e[91mERROR: %s: %s\e[0m\n" "${label}" "${detail}" >&2
|
||||
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f secret_validation_error
|
||||
|
||||
#######################################
|
||||
# Registers an exact known secret value for final log redaction.
|
||||
# Validate a filename-only secret argument.
|
||||
# Globals:
|
||||
# _ARY_SECRET_REDACTION_VALUES
|
||||
# ERR_ARG_MSMTCH
|
||||
# Arguments:
|
||||
# 1: secret value
|
||||
# 1: Validation label.
|
||||
# 2: Filename.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
register_secret_value() {
|
||||
declare secret_value="$1" registered_value="" was_traced="false"
|
||||
|
||||
[[ $- == *x* ]] && was_traced="true"
|
||||
set +x
|
||||
|
||||
if [[ -n "${secret_value}" ]]; then
|
||||
for registered_value in "${_ARY_SECRET_REDACTION_VALUES[@]}"; do
|
||||
if [[ "${registered_value}" == "${secret_value}" ]]; then
|
||||
[[ "${was_traced}" == "true" ]] && set -x
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
_ARY_SECRET_REDACTION_VALUES+=("${secret_value}")
|
||||
fi
|
||||
|
||||
[[ "${was_traced}" == "true" ]] && set -x
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f register_secret_value
|
||||
|
||||
#######################################
|
||||
# Registers exact text values from a controlled secret file.
|
||||
# Globals:
|
||||
# _ARY_SECRET_REDACTION_VALUES
|
||||
# Arguments:
|
||||
# 1: secret file
|
||||
# Returns:
|
||||
# 0: on success
|
||||
#######################################
|
||||
register_secret_file_for_redaction() {
|
||||
declare secret_file="$1" secret_line="" secret_text="" was_traced="false"
|
||||
|
||||
[[ $- == *x* ]] && was_traced="true"
|
||||
set +x
|
||||
|
||||
secret_text="$(cat "${secret_file}" || exit $?; printf '.')" || {
|
||||
[[ "${was_traced}" == "true" ]] && set -x
|
||||
return "${ERR_SANITIZING:-133}"
|
||||
}
|
||||
secret_text="${secret_text%.}"
|
||||
register_secret_value "${secret_text}"
|
||||
|
||||
while IFS= read -r secret_line || [[ -n "${secret_line}" ]]; do
|
||||
register_secret_value "${secret_line}"
|
||||
done < "${secret_file}"
|
||||
|
||||
[[ "${was_traced}" == "true" ]] && set -x
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f register_secret_file_for_redaction
|
||||
|
||||
#######################################
|
||||
# Validates a filename-only secret argument.
|
||||
# Arguments:
|
||||
# 1: filename
|
||||
# 2: input class
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SECRET_PATH: on failure
|
||||
# ERR_ARG_MSMTCH: on failure
|
||||
#######################################
|
||||
validate_secret_filename() {
|
||||
declare filename="$1" input_class="${2:-filename-only secret argument}"
|
||||
declare label="$1" filename="$2"
|
||||
declare filename_regex='^[A-Za-z0-9._@%+=:,~-]+$'
|
||||
|
||||
if [[ -z "${filename}" || "${filename}" == "." || "${filename}" == ".." || "${filename}" == */* \
|
||||
|| ! "${filename}" =~ ${filename_regex} ]]; then
|
||||
secret_validation_error "${input_class}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
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
|
||||
@@ -146,188 +81,185 @@ validate_secret_filename() {
|
||||
readonly -f validate_secret_filename
|
||||
|
||||
#######################################
|
||||
# Validates a restrictively permissioned secret directory.
|
||||
# Validate the fixed tmpfs secret root.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: directory path
|
||||
# 2: input class
|
||||
# 3: require tmpfs
|
||||
# 4: quiet flag
|
||||
# 1: Secret root path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SECRET_PATH: on failure
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_secret_directory() {
|
||||
declare directory="$1" input_class="${2:-secret directory}" require_tmpfs="${3:-false}" quiet="${4:-false}"
|
||||
declare fs_type="" mode="" owner=""
|
||||
validate_secret_root() {
|
||||
declare secret_root="$1" root_owner="" root_mode="" root_mode_octal="" root_fs=""
|
||||
|
||||
if [[ -z "${directory}" || -L "${directory}" || ! -d "${directory}" ]]; then
|
||||
secret_validation_error "${input_class}" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
if [[ -z "${secret_root}" ]]; then
|
||||
secret_validation_error "secret root" "path MUST NOT be empty."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
owner="$(secure_stat -c '%u' "${directory}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} ownership" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
mode="$(secure_stat -c '%a' "${directory}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} permissions" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
|
||||
if [[ "${owner}" != "${EUID}" || "${mode}" != "700" ]]; then
|
||||
secret_validation_error "${input_class} ownership or permissions" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
if [[ -L "${secret_root}" ]]; then
|
||||
secret_validation_error "secret root" "path MUST NOT be a symlink."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ "${require_tmpfs}" == "true" ]]; then
|
||||
fs_type="$(secure_stat -f -c '%T' "${directory}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} filesystem" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
if [[ "${fs_type}" != "tmpfs" && "${fs_type}" != "ramfs" ]]; then
|
||||
secret_validation_error "${input_class} is not tmpfs-backed" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
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_directory
|
||||
readonly -f validate_secret_root
|
||||
|
||||
#######################################
|
||||
# Validates and registers a secret file.
|
||||
# Normalize ownership and mode of regular files in the secret root.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: file path
|
||||
# 2: input class
|
||||
# 3: register for redaction
|
||||
# 4: quiet flag
|
||||
# 1: Secret root path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SECRET_PATH: on failure
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_secret_file() {
|
||||
declare secret_file="$1" input_class="${2:-secret file}" register_value="${3:-true}" quiet="${4:-false}"
|
||||
declare link_count="" mode="" owner=""
|
||||
harden_secret_root_files() {
|
||||
declare secret_root="$1"
|
||||
|
||||
if [[ -z "${secret_file}" || -L "${secret_file}" || ! -f "${secret_file}" ]]; then
|
||||
secret_validation_error "${input_class}" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
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
|
||||
|
||||
owner="$(secure_stat -c '%u' "${secret_file}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} ownership" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
mode="$(secure_stat -c '%a' "${secret_file}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} permissions" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
link_count="$(secure_stat -c '%h' "${secret_file}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} link count" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
|
||||
if [[ "${owner}" != "${EUID}" || "${link_count}" != "1" || ( "${mode}" != "400" && "${mode}" != "600" ) ]]; then
|
||||
secret_validation_error "${input_class} ownership, permissions, or link count" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
if [[ "${file_path}" != /* ]]; then
|
||||
secret_validation_error "${label}" "file path MUST be absolute."
|
||||
return "${ERR_INVLD_CHAR}"
|
||||
fi
|
||||
|
||||
if [[ "${register_value}" == "true" ]]; then
|
||||
register_secret_file_for_redaction "${secret_file}"
|
||||
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_file
|
||||
readonly -f validate_secret_absolute_file_basics
|
||||
|
||||
#######################################
|
||||
# Validates an explicitly supported absolute secret file path.
|
||||
# Validate a strict secret file.
|
||||
# Globals:
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: file path
|
||||
# 2: input class
|
||||
# 1: Validation label.
|
||||
# 2: File path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SECRET_PATH: on failure
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_secret_absolute_file() {
|
||||
declare secret_file="$1" input_class="${2:-absolute secret file}" resolved_file=""
|
||||
validate_secret_file_path() {
|
||||
declare label="$1" file_path="$2" file_owner="" file_mode="" file_mode_octal=""
|
||||
|
||||
if [[ -z "${secret_file}" || "${secret_file}" != /* ]]; then
|
||||
secret_validation_error "${input_class} must be an absolute path"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
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
|
||||
|
||||
resolved_file="$(realpath "${secret_file}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} cannot be resolved"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
if [[ "${resolved_file}" != "${secret_file}" ]]; then
|
||||
secret_validation_error "${input_class} must be canonical and must not traverse symlinked parents"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
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
|
||||
|
||||
validate_secret_file "${secret_file}" "${input_class}" || return "${ERR_SECRET_PATH:-216}"
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_absolute_file
|
||||
readonly -f validate_secret_file_path
|
||||
|
||||
#######################################
|
||||
# Validates an explicitly supported absolute secret directory path.
|
||||
# Arguments:
|
||||
# 1: directory path
|
||||
# 2: input class
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SECRET_PATH: on failure
|
||||
#######################################
|
||||
validate_secret_absolute_directory() {
|
||||
declare directory="$1" input_class="${2:-absolute secret directory}" resolved_directory=""
|
||||
|
||||
if [[ -z "${directory}" || "${directory}" != /* ]]; then
|
||||
secret_validation_error "${input_class} must be an absolute path"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
fi
|
||||
|
||||
resolved_directory="$(realpath "${directory}" 2>/dev/null)" || {
|
||||
secret_validation_error "${input_class} cannot be resolved"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
}
|
||||
if [[ "${resolved_directory}" != "${directory}" ]]; then
|
||||
secret_validation_error "${input_class} must be canonical and must not traverse symlinked parents"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
fi
|
||||
|
||||
validate_secret_directory "${directory}" "${input_class}" || return "${ERR_SECRET_PATH:-216}"
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_absolute_directory
|
||||
|
||||
#######################################
|
||||
# Validates a filename-only secret file below the fixed secret root.
|
||||
# Validate a filename-only secret stored below the fixed secret root.
|
||||
# Globals:
|
||||
# VAR_TMP_SECRET
|
||||
# Arguments:
|
||||
# 1: filename
|
||||
# 2: input class
|
||||
# 3: register for redaction
|
||||
# 1: Validation label.
|
||||
# 2: Filename.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SECRET_PATH: on failure
|
||||
# Non-zero: on failure
|
||||
#######################################
|
||||
validate_secret_file_in_root() {
|
||||
declare filename="$1" input_class="${2:-secret file}" register_value="${3:-true}"
|
||||
declare label="$1" filename="$2"
|
||||
|
||||
validate_secret_directory "${VAR_TMP_SECRET}" "secret root" "true" || return "${ERR_SECRET_PATH:-216}"
|
||||
validate_secret_filename "${filename}" "${input_class} filename" || return "${ERR_SECRET_PATH:-216}"
|
||||
validate_secret_file "${VAR_TMP_SECRET}/${filename}" "${input_class}" "${register_value}" || return "${ERR_SECRET_PATH:-216}"
|
||||
validate_secret_filename "${label}" "${filename}" || return "$?"
|
||||
validate_secret_file_path "${label}" "${VAR_TMP_SECRET}/${filename}" || return "$?"
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -336,49 +268,139 @@ validate_secret_file_in_root() {
|
||||
readonly -f validate_secret_file_in_root
|
||||
|
||||
#######################################
|
||||
# Validates the fixed tmpfs secret staging area and all entries below it.
|
||||
# Validate a public input file copied into the ISO.
|
||||
# Globals:
|
||||
# VAR_TMP_SECRET
|
||||
# ERR_INVLD_CHAR
|
||||
# Arguments:
|
||||
# 1: quiet flag
|
||||
# 1: Validation label.
|
||||
# 2: File path.
|
||||
# Returns:
|
||||
# 0: on success
|
||||
# ERR_SECRET_PATH: on failure
|
||||
# ERR_INVLD_CHAR: on failure
|
||||
#######################################
|
||||
validate_secret_staging_area() {
|
||||
declare quiet="${1:-false}" secret_entries_file="" secret_entry=""
|
||||
declare -a secret_entries=()
|
||||
validate_public_input_file() {
|
||||
declare label="$1" file_path="$2" file_owner="" file_mode="" file_mode_octal=""
|
||||
|
||||
validate_secret_directory "${VAR_TMP_SECRET}" "secret root" "true" "${quiet}" || return "${ERR_SECRET_PATH:-216}"
|
||||
validate_secret_absolute_file_basics "${label}" "${file_path}" || return "$?"
|
||||
|
||||
secret_entries_file="$(mktemp)" || return "${ERR_SECRET_PATH:-216}"
|
||||
if ! find "${VAR_TMP_SECRET}" -xdev -mindepth 1 -print0 >| "${secret_entries_file}"; then
|
||||
rm -f "${secret_entries_file}"
|
||||
secret_validation_error "secret-root enumeration failed" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
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
|
||||
mapfile -d '' -t secret_entries < "${secret_entries_file}"
|
||||
rm -f "${secret_entries_file}"
|
||||
|
||||
for secret_entry in "${secret_entries[@]}"; do
|
||||
if [[ -L "${secret_entry}" ]]; then
|
||||
secret_validation_error "symlink below secret root" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
elif [[ -d "${secret_entry}" ]]; then
|
||||
validate_secret_directory "${secret_entry}" "directory below secret root" "false" "${quiet}" \
|
||||
|| return "${ERR_SECRET_PATH:-216}"
|
||||
elif [[ -f "${secret_entry}" ]]; then
|
||||
validate_secret_file "${secret_entry}" "file below secret root" "true" "${quiet}" || return "${ERR_SECRET_PATH:-216}"
|
||||
else
|
||||
secret_validation_error "non-regular entry below secret root" "${quiet}"
|
||||
return "${ERR_SECRET_PATH:-216}"
|
||||
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
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
readonly -f validate_secret_staging_area
|
||||
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,10 @@ trap_on_exit() {
|
||||
|
||||
print_scr_exit "${errcode}"
|
||||
|
||||
sanitize_debug_logs || true
|
||||
|
||||
exit "${errcode}"
|
||||
|
||||
else
|
||||
|
||||
if [[ "${ERRTRAP}" != "true" ]]; then
|
||||
@@ -61,16 +65,11 @@ trap_on_exit() {
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if ! sanitize_debug_logs; then
|
||||
printf "\e[93m⚠ Final debug-log sanitisation failed; preserving original exit status %s. \e[0m\n" "${errcode}" >&2
|
||||
if [[ -n "${LOG_ERROR:-}" && -f "${LOG_ERROR}" && ! -L "${LOG_ERROR}" ]]; then
|
||||
printf "⚠ Final debug-log sanitisation failed; original exit status: %s.\n" "${errcode}" >> "${LOG_ERROR}" || true
|
||||
fi
|
||||
fi
|
||||
sanitize_debug_logs || true
|
||||
|
||||
exit "${errcode}"
|
||||
|
||||
fi
|
||||
}
|
||||
### Prevents accidental 'unset -f'.
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
+16
-21
@@ -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,10 +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 " The path MUST be canonical and dedicated to the builder; a new directory's canonical parent MUST already exist."
|
||||
echo " New or empty directories receive the"
|
||||
echo " '.ciss-live-builder-owned' marker; populated unmarked directories are rejected. Cleanup is intentionally"
|
||||
echo " destructive only inside the exact validated marker-owned directory."
|
||||
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"
|
||||
@@ -81,6 +78,9 @@ usage() {
|
||||
echo " This option creates a boot menu entry that starts the forthcoming 'CISS.debian.installer', which is executed"
|
||||
echo " once the system has successfully booted up."
|
||||
echo
|
||||
echo -e "\e[97m --cicd \e[0m"
|
||||
echo " Only for CISS internal Gitea Action Runners"
|
||||
echo
|
||||
echo -e "\e[97m --contact, -c \e[0m"
|
||||
echo " Show author contact information."
|
||||
echo
|
||||
@@ -91,7 +91,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 " A final exact-value sanitisation pass is defence in depth and does not replace careful tracing discipline."
|
||||
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 "
|
||||
@@ -113,16 +113,17 @@ 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 " '*' MUST be a filename only without slashes, '.' or '..' traversal."
|
||||
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 " '*' MUST be a filename only without slashes, '.' or '..' traversal."
|
||||
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 --logo, -l \e[0m"
|
||||
echo " Shows the Centurion Head."
|
||||
echo
|
||||
echo -e "\e[97m --log-statistics-only \e[0m"
|
||||
echo " Provides statistic only after successful building a CISS.debian.live-ISO. While enabling '--log-statistics-only'"
|
||||
echo " the argument '--build-directory' MUST be provided."
|
||||
@@ -130,10 +131,6 @@ usage() {
|
||||
echo -e "\e[97m --primordial-key <ssh-identity-filename> \e[0m"
|
||||
echo " SSH identity filename for the Primordial overlay clone. This MUST be a filename only; the runtime path is"
|
||||
echo " derived as '/root/.ssh/<ssh-identity-filename>'."
|
||||
echo " Example fragment:"
|
||||
echo " ./ciss_live_builder.sh --primordial-url https://git.coresecret.dev/ahz/PhysNet.primordial.git \\"
|
||||
echo " --primordial-key id--git.coresecret.dev--PhysNet.primordial_deploy--ed25519--newton--2025-10 \\"
|
||||
echo " --primordial-ssh 42842"
|
||||
echo
|
||||
echo -e "\e[97m --primordial-ssh <INTEGER> \e[0m"
|
||||
echo " Adds one outgoing UFW TCP exception for a bootstrap SSH port."
|
||||
@@ -169,7 +166,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 " Safe absolute paths remain supported and are validated separately. RECOMMENDED path:"
|
||||
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"
|
||||
@@ -185,8 +183,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 filename-only files / fingerprint. Filename-only values MUST NOT contain slashes"
|
||||
echo " or traversal. 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"
|
||||
@@ -209,6 +206,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
|
||||
@@ -220,9 +218,6 @@ usage() {
|
||||
echo
|
||||
echo -e "\e[93m💡 Notes: \e[0m"
|
||||
echo -e "\e[93m🔵 You MUST be 'root' to run this script. \e[0m"
|
||||
echo -e "\e[93m🔵 Private operator control does not remove the requirement for strict local secret path validation. \e[0m"
|
||||
echo -e "\e[93m🔵 '/dev/shm/cdlb_secrets' MUST be tmpfs-backed, root-owned, mode 0700, and contain only \e[0m"
|
||||
echo -e "\e[93m single-link regular secret files with mode 0400 or 0600. Secure deletion with shred is best-effort only. \e[0m"
|
||||
echo
|
||||
echo -e "\e[95m💷 Please consider donating to my work at: \e[0m"
|
||||
echo -e "\e[95m🌐 https://coresecret.eu/spenden/ \e[0m"
|
||||
|
||||
@@ -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.028.2026.06.18 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.028.2026.06.18: No valid semaphore [%s] within [%s]s.\n" "${VAR_SEMAPHORE}" "${VAR_TIMEOUT}" >> "${var_log}"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
#!/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
|
||||
set -Ceuo pipefail
|
||||
# shellcheck disable=SC1091,SC2034
|
||||
|
||||
declare TEST_ROOT=""
|
||||
declare TEST_TMP=""
|
||||
TEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TEST_TMP="$(realpath "$(mktemp -d)")"
|
||||
readonly TEST_ROOT TEST_TMP
|
||||
declare -r ERR_BUILD_PATH=217
|
||||
declare -r ERR_GUARD_SRCE=131
|
||||
declare -r ERR_SANITIZING=133
|
||||
declare -r ERR_SECRET_PATH=216
|
||||
declare -r VAR_WORKDIR="${TEST_ROOT}"
|
||||
declare VAR_TMP_SECRET="${TEST_TMP}/secret-root"
|
||||
declare LOG_DEBUG=""
|
||||
declare LOG_ERROR=""
|
||||
declare LOG_VAR=""
|
||||
declare VAR_EARLY_DEBUG="false"
|
||||
declare ERRTRAP="true"
|
||||
|
||||
cleanup_test() {
|
||||
chmod -R u+rwX "${TEST_TMP}" 2>/dev/null || true
|
||||
rm -rf "${TEST_TMP}"
|
||||
}
|
||||
trap cleanup_test EXIT
|
||||
|
||||
guard_sourcing() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# shellcheck source=../lib/lib_secret_validation.sh
|
||||
. "${TEST_ROOT}/lib/lib_secret_validation.sh"
|
||||
# shellcheck source=../lib/lib_build_directory.sh
|
||||
. "${TEST_ROOT}/lib/lib_build_directory.sh"
|
||||
# shellcheck source=../lib/lib_debug_sanitizer.sh
|
||||
. "${TEST_ROOT}/lib/lib_debug_sanitizer.sh"
|
||||
# shellcheck source=../lib/lib_trap_on_exit.sh
|
||||
. "${TEST_ROOT}/lib/lib_trap_on_exit.sh"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
expect_failure() {
|
||||
declare description="$1"
|
||||
shift
|
||||
if "$@" >/dev/null 2>&1; then
|
||||
fail "${description}"
|
||||
fi
|
||||
}
|
||||
|
||||
mkdir -m 0700 "${VAR_TMP_SECRET}"
|
||||
printf 'safe-private-value\n' > "${VAR_TMP_SECRET}/safe.txt"
|
||||
chmod 0400 "${VAR_TMP_SECRET}/safe.txt"
|
||||
|
||||
validate_secret_directory "${VAR_TMP_SECRET}" "test secret root" "false" || fail "safe secret root rejected"
|
||||
validate_secret_filename "safe.txt" "test secret filename" || fail "safe secret filename rejected"
|
||||
validate_secret_file "${VAR_TMP_SECRET}/safe.txt" "test secret file" || fail "safe secret file rejected"
|
||||
validate_secret_absolute_file "${VAR_TMP_SECRET}/safe.txt" "test absolute secret file" \
|
||||
|| fail "safe absolute secret file rejected"
|
||||
expect_failure "relative external secret path accepted" \
|
||||
validate_secret_absolute_file "secret-root/safe.txt" "test absolute secret file"
|
||||
mkdir -m 0700 "${TEST_TMP}/external-secret-dir"
|
||||
printf 'external-private-value\n' > "${TEST_TMP}/external-secret-dir/external.txt"
|
||||
chmod 0400 "${TEST_TMP}/external-secret-dir/external.txt"
|
||||
ln -s "${TEST_TMP}/external-secret-dir" "${TEST_TMP}/external-secret-dir-link"
|
||||
expect_failure "external secret file through a symlinked parent accepted" \
|
||||
validate_secret_absolute_file "${TEST_TMP}/external-secret-dir-link/external.txt" "test absolute secret file"
|
||||
expect_failure "external secret directory through a symlinked parent accepted" \
|
||||
validate_secret_absolute_directory "${TEST_TMP}/external-secret-dir-link" "test absolute secret directory"
|
||||
declare secret_root_fs=""
|
||||
secret_root_fs="$(secure_stat -f -c '%T' "${VAR_TMP_SECRET}")"
|
||||
if [[ "${secret_root_fs}" != "tmpfs" && "${secret_root_fs}" != "ramfs" ]]; then
|
||||
expect_failure "persistent secret staging area accepted" validate_secret_staging_area
|
||||
fi
|
||||
expect_failure "secret filename traversal accepted" validate_secret_filename "../safe.txt" "test secret filename"
|
||||
expect_failure "absolute filename-only secret accepted" validate_secret_filename "/tmp/safe.txt" "test secret filename"
|
||||
expect_failure "slash in filename-only secret accepted" validate_secret_filename "subdir/safe.txt" "test secret filename"
|
||||
|
||||
ln -s "${VAR_TMP_SECRET}" "${TEST_TMP}/secret-root-link"
|
||||
expect_failure "secret-root symlink accepted" \
|
||||
validate_secret_directory "${TEST_TMP}/secret-root-link" "test secret root" "false"
|
||||
mkdir -m 0700 "${TEST_TMP}/unsafe-secret-root-mode"
|
||||
chmod 0755 "${TEST_TMP}/unsafe-secret-root-mode"
|
||||
expect_failure "broad secret-root permissions accepted" \
|
||||
validate_secret_directory "${TEST_TMP}/unsafe-secret-root-mode" "test secret root" "false"
|
||||
ln -s "${VAR_TMP_SECRET}/safe.txt" "${VAR_TMP_SECRET}/unsafe-link"
|
||||
expect_failure "secret-file symlink accepted" validate_secret_file "${VAR_TMP_SECRET}/unsafe-link" "test secret file"
|
||||
rm "${VAR_TMP_SECRET}/unsafe-link"
|
||||
ln "${VAR_TMP_SECRET}/safe.txt" "${VAR_TMP_SECRET}/unsafe-hardlink"
|
||||
expect_failure "hardlinked secret file accepted" validate_secret_file "${VAR_TMP_SECRET}/safe.txt" "test secret file"
|
||||
rm "${VAR_TMP_SECRET}/unsafe-hardlink"
|
||||
|
||||
declare fake_secret='CISS-CANARY-[exact]-value'
|
||||
declare expected_redaction=""
|
||||
declare sanitisation_status=0
|
||||
printf -v expected_redaction '%*s' "${#fake_secret}" ''
|
||||
expected_redaction="${expected_redaction// /*}"
|
||||
register_secret_value "${fake_secret}"
|
||||
|
||||
LOG_DEBUG="${TEST_TMP}/debug.log"
|
||||
LOG_VAR="${TEST_TMP}/var.log"
|
||||
LOG_ERROR="${TEST_TMP}/error.log"
|
||||
printf 'before %s after\nunrelated line\n' "${fake_secret}" > "${LOG_DEBUG}"
|
||||
printf 'unrelated vars\n' > "${LOG_VAR}"
|
||||
printf 'unrelated error\n' > "${LOG_ERROR}"
|
||||
chmod 0600 "${LOG_DEBUG}" "${LOG_VAR}" "${LOG_ERROR}"
|
||||
|
||||
sanitize_debug_logs || fail "debug-log sanitisation failed"
|
||||
grep -Fq "${fake_secret}" "${LOG_DEBUG}" && fail "debug-log canary remained"
|
||||
grep -Fq "${expected_redaction}" "${LOG_DEBUG}" || fail "expected exact-value redaction missing"
|
||||
grep -Fq 'unrelated line' "${LOG_DEBUG}" || fail "unrelated debug content changed"
|
||||
|
||||
ln -s "${LOG_DEBUG}" "${TEST_TMP}/unsafe-debug-link"
|
||||
(
|
||||
LOG_DEBUG="${TEST_TMP}/unsafe-debug-link"
|
||||
trap_on_exit 73 "test" 1 "test" "false"
|
||||
) 2>/dev/null || sanitisation_status=$?
|
||||
[[ ${sanitisation_status} -eq 73 ]] || fail "sanitisation failure masked the original exit status"
|
||||
|
||||
expect_failure "empty build-directory path accepted" validate_build_directory_path ""
|
||||
expect_failure "root build-directory path accepted" validate_build_directory_path "/"
|
||||
expect_failure "broad parent build-directory path accepted" validate_build_directory_path "/tmp"
|
||||
expect_failure "secret root accepted as build directory" validate_build_directory_path "${VAR_TMP_SECRET}"
|
||||
mkdir -m 0700 "${VAR_TMP_SECRET}/unsafe-build-child"
|
||||
expect_failure "secret-root descendant accepted as build directory" \
|
||||
validate_build_directory_path "${VAR_TMP_SECRET}/unsafe-build-child"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/unmarked"
|
||||
expect_failure "build directory without marker accepted" validate_build_directory_marker "${TEST_TMP}/unmarked"
|
||||
printf 'do-not-adopt\n' > "${TEST_TMP}/unmarked/content"
|
||||
expect_failure "non-empty unmarked build directory adopted" initialize_build_directory "${TEST_TMP}/unmarked" unsafe_result
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/unsafe-build-mode"
|
||||
chmod 0777 "${TEST_TMP}/unsafe-build-mode"
|
||||
expect_failure "unsafe build-directory permissions accepted" \
|
||||
initialize_build_directory "${TEST_TMP}/unsafe-build-mode" unsafe_result
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/marker-link-dir"
|
||||
printf '%s\n' "${TEST_TMP}/marker-link-dir" > "${TEST_TMP}/marker-target"
|
||||
chmod 0400 "${TEST_TMP}/marker-target"
|
||||
ln -s "${TEST_TMP}/marker-target" "${TEST_TMP}/marker-link-dir/.ciss-live-builder-owned"
|
||||
expect_failure "symlinked builder-owned marker accepted" validate_build_directory_marker "${TEST_TMP}/marker-link-dir"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/marker-hardlink-dir"
|
||||
printf '%s\n' "${TEST_TMP}/marker-hardlink-dir" > "${TEST_TMP}/marker-hardlink-target"
|
||||
chmod 0400 "${TEST_TMP}/marker-hardlink-target"
|
||||
ln "${TEST_TMP}/marker-hardlink-target" "${TEST_TMP}/marker-hardlink-dir/.ciss-live-builder-owned"
|
||||
expect_failure "hardlinked builder-owned marker accepted" validate_build_directory_marker "${TEST_TMP}/marker-hardlink-dir"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/marker-extra-content-dir"
|
||||
printf '%s\nunexpected\n' "${TEST_TMP}/marker-extra-content-dir" \
|
||||
> "${TEST_TMP}/marker-extra-content-dir/.ciss-live-builder-owned"
|
||||
chmod 0400 "${TEST_TMP}/marker-extra-content-dir/.ciss-live-builder-owned"
|
||||
expect_failure "builder-owned marker with extra content accepted" \
|
||||
validate_build_directory_marker "${TEST_TMP}/marker-extra-content-dir"
|
||||
|
||||
ln -s "${TEST_TMP}/unmarked" "${TEST_TMP}/build-link"
|
||||
expect_failure "build-directory symlink accepted" validate_build_directory_path "${TEST_TMP}/build-link"
|
||||
|
||||
declare validated_build_dir=""
|
||||
initialize_build_directory "${TEST_TMP}/owned-build" validated_build_dir || fail "safe builder-owned directory rejected"
|
||||
validate_build_directory_marker "${validated_build_dir}" || fail "builder marker rejected"
|
||||
mkdir "${validated_build_dir}/subdir"
|
||||
printf 'remove me\n' > "${validated_build_dir}/artifact"
|
||||
printf 'remove me too\n' > "${validated_build_dir}/.hidden-artifact"
|
||||
printf 'nested\n' > "${validated_build_dir}/subdir/nested"
|
||||
clean_build_directory_contents "${validated_build_dir}" || fail "safe builder-owned cleanup failed"
|
||||
validate_build_directory_marker "${validated_build_dir}" || fail "builder marker removed by cleanup"
|
||||
[[ -z "$(find "${validated_build_dir}" -mindepth 1 ! -name '.ciss-live-builder-owned' -print -quit)" ]] \
|
||||
|| fail "builder-owned cleanup left unexpected content"
|
||||
|
||||
mkdir -m 0700 "${TEST_TMP}/outside-build"
|
||||
mkdir "${TEST_TMP}/outside-build/includes.chroot"
|
||||
ln -s "${TEST_TMP}/outside-build" "${validated_build_dir}/config"
|
||||
expect_failure "cleanup subpath through a symlinked parent accepted" \
|
||||
validate_build_directory_subpath "${validated_build_dir}" "config/includes.chroot" unsafe_subpath
|
||||
|
||||
printf 'PASS: secret validation, debug sanitisation, and build cleanup guards\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.028.2026.06.18"
|
||||
declare -grx VAR_VER_BASH="$(bash --version | head -n1 | awk '{
|
||||
# Print $4 and $5; include $6 only if it exists
|
||||
out = $4
|
||||
|
||||
+3
-3
@@ -28,7 +28,6 @@ touch "${LOG_ERROR}" && chmod 0600 "${LOG_ERROR}"
|
||||
|
||||
declare -g __umask=""
|
||||
declare -g VAR_ARCHITECTURE=""
|
||||
declare -g VAR_ARG_SANITIZED=""
|
||||
declare -g VAR_DROPBEAR_VERSION="2026.91"
|
||||
declare -g VAR_HANDLER_BUILD_DIR=""
|
||||
declare -g VAR_HANDLER_CDI="false"
|
||||
@@ -44,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=()
|
||||
@@ -52,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=""
|
||||
@@ -92,8 +94,6 @@ declare -gir ERR__SSH__PORT=212 # --ssh-port MUST be an integer between '1' and
|
||||
declare -gir ERR_ARG_MSMTCH=213 # Wrong Number of optional Arguments provided
|
||||
declare -gir ERR_DROPBEAR_V=214 # --dropbear-version MUST match the bundled Dropbear tarball version format
|
||||
declare -gir ERR__SOPS__VER=215 # --sops-version MUST match the upstream SOPS semantic version format
|
||||
declare -gir ERR_SECRET_PATH=216 # Unsafe secret root, filename, or file.
|
||||
declare -gir ERR_BUILD_PATH=217 # Unsafe build-directory path or marker.
|
||||
declare -gir ERR_SECRETSSYM=251 # VAR_TMP_SECRET is a symlink.
|
||||
declare -gir ERR_NOTABSPATH=252 # Not an absolute path
|
||||
declare -gir ERR_INVLD_CHAR=253 # Invalid Character
|
||||
|
||||
Reference in New Issue
Block a user