#!/bin/bash # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-10-11; WEIDNER, Marc S.; # SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git # SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency # SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1 # SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework. # SPDX-PackageName: CISS.debian.live.builder # SPDX-Security-Contact: security@coresecret.eu set -Ceuo pipefail printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ 🧪 '%s' starting ... \e[0m\n" "${0}" [[ -r /root/ciss_xdg_tmp.sh ]] && . /root/ciss_xdg_tmp.sh export DEBIAN_FRONTEND="noninteractive" export INITRD="No" apt-get install -y --no-install-recommends kexec-tools install -d -m 0755 /boot/ciss-memwipe install -d -m 0755 /usr/local/sbin install -d -m 0755 /etc/systemd/system install -d -m 0755 /etc/default ### Pick a kernel to kexec into: use the latest installed vmlinuz. ------------------------------------------------------------- # shellcheck disable=SC2012,SC2155 declare _KERNEL="$(cd /boot && ls -1 vmlinuz-* | sed 's|vmlinuz-||' | sort -V | tail -n1)" cp -f "/boot/vmlinuz-${_KERNEL}" /boot/ciss-memwipe/vmlinuz ### Build minimal initramfs with a busybox and a tiny '/init'. ----------------------------------------------------------------- declare _TMP_DIR; _TMP_DIR="$(mktemp -d)" trap 'rm -rf "${_TMP_DIR}"' EXIT mkdir -p "${_TMP_DIR}"/{bin,dev,proc,sys,wipe} ### Locate the current busybox binary. ----------------------------------------------------------------------------------------- declare _BUSYBOX_BIN; _BUSYBOX_BIN="$(command -v busybox || true)" if [[ -z "${_BUSYBOX_BIN}" ]]; then echo "ERROR: busybox not found after installation attempt." >&2 exit 42 fi cp -f "${_BUSYBOX_BIN}" "${_TMP_DIR}/bin/busybox" ####################################### # Copy required shared libs into the initramfs (if the busybox is dynamic). # Globals: # _TMP_DIR # Arguments: # 1: _BUSYBOX_BIN # Returns: # 0: on success ####################################### copy_libs() { declare bin="$1" if ldd "${bin}" 2>&1 | grep -q 'not a dynamic executable'; then return 0 fi ldd "${bin}" | awk ' /=> \// {print $3} # some libs are printed as absolute path without "=>" /^\// {print $1} ' | while read -r lib; do [[ -n "${lib}" ]] || continue dest="${_TMP_DIR}$(dirname "${lib}")" install -d -m 0755 "${dest}" cp -f "${lib}" "${dest}" done } copy_libs "${_BUSYBOX_BIN}" ### Generate '/init' script ---------------------------------------------------------------------------------------------------- cat << 'EOF' >| "${_TMP_DIR}/init" #!/bin/busybox sh # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-10-11; WEIDNER, Marc S.; # SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git # SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency # SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1 # SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework. # SPDX-PackageName: CISS.debian.live.builder # SPDX-Security-Contact: security@coresecret.eu # Minimal init to wipe RAM, then power off. # Parses cmdline: ciss_wipe_passes=2 ciss_wipe_mode=zero+random ciss_dd_bs=64M ciss_tmpfs_pct=95 set -eu ####################################### # Helper # Globals: # None # Arguments: # 1: key # Returns: # 0: on success ####################################### get_arg() { # $1=key ; echoes value or empty for tok in $(cat /proc/cmdline); do case "$tok" in $1=*) echo "${tok#*=}"; return 0;; esac done echo "" } mount -t devtmpfs devtmpfs /dev 2>/dev/null || true [ -e /dev/console ] || mknod -m 600 /dev/console c 5 1 [ -e /dev/null ] || mknod -m 666 /dev/null c 1 3 [ -e /dev/urandom ] || mknod -m 444 /dev/urandom c 1 9 mount -t proc proc /proc mount -t sysfs sysfs /sys PASSES="$(get_arg ciss_wipe_passes)"; [ -n "${PASSES}" ] || PASSES=2 MODE="$(get_arg ciss_wipe_mode)"; [ -n "${MODE}" ] || MODE="zero+random" BS="$(get_arg ciss_dd_bs)"; [ -n "${BS}" ] || BS=64M PCT="$(get_arg ciss_tmpfs_pct)"; [ -n "${PCT}" ] || PCT=95 echo 1 >| /proc/sys/kernel/printk 2>/dev/null || true MEM_KB="$(awk '/MemTotal:/ {print $2}' /proc/meminfo)" SIZE_KB=$(( MEM_KB * PCT / 100 )) mount -t tmpfs -o "size=${SIZE_KB}k,nodev,nosuid,noexec,mode=0700" tmpfs /wipe ####################################### # Wipe helper # Globals: # None # Arguments: # 1: pattern # Returns: # 0: on success ####################################### wipe_pass() { pattern="$1" # zero or random if [ "$pattern" = "zero" ]; then src="/dev/zero" else src="/dev/urandom" fi i=0 while :; do # Use busybox dd explicitly to avoid surprises busybox dd if="$src" of="/wipe/block_$i" bs="$BS" status=none || break i=$((i+1)) done sync echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true rm -f /wipe/block_* 2>/dev/null || true sync return 0 } DO_ZERO=0; DO_RANDOM=0 case "$MODE" in zero) DO_ZERO=1 ;; random) DO_RANDOM=1 ;; zero+random|random+zero) DO_ZERO=1; DO_RANDOM=1 ;; *) DO_ZERO=1 ;; esac p=1 while [ $p -le "$PASSES" ]; do [ $DO_ZERO -eq 1 ] && wipe_pass zero [ $DO_RANDOM -eq 1 ] && wipe_pass random p=$((p+1)) done sync busybox poweroff -f || echo o >| /proc/sysrq-trigger # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh EOF chmod +x "${_TMP_DIR}/init" ### Create the initramfs archive ----------------------------------------------------------------------------------------------- ( cd "${_TMP_DIR}" && find . -print0 | cpio --null -ov --format=newc ) | gzip -9 > /boot/ciss-memwipe/initrd.img ### Default configuration. cat << 'EOF' >| /etc/default/ciss-memwipe # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-10-11; WEIDNER, Marc S.; # SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git # SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency # SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1 # SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework. # SPDX-PackageName: CISS.debian.live.builder # SPDX-Security-Contact: security@coresecret.eu # CISS Memory Wipe defaults: CISS_WIPE_PASSES=2 # number of passes CISS_WIPE_MODE="zero+random" # zero | random | zero+random CISS_WIPE_DD_BS="64M" # dd block size CISS_WIPE_TMPFS_PCT=95 # percentage of MemTotal to allocate # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf EOF ### Helper script -------------------------------------------------------------------------------------------------------------- cat << 'EOF' >| /usr/local/sbin/ciss-memwipe #!/bin/bash # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-10-11; WEIDNER, Marc S.; # SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git # SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency # SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1 # SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework. # SPDX-PackageName: CISS.debian.live.builder # SPDX-Security-Contact: security@coresecret.eu set -euo pipefail . /etc/default/ciss-memwipe || true KERNEL="/boot/ciss-memwipe/vmlinuz" INITRD="/boot/ciss-memwipe/initrd.img" append_common="quiet loglevel=1 ciss_wipe_passes=${CISS_WIPE_PASSES:-2} ciss_wipe_mode=${CISS_WIPE_MODE:-zero+random} ciss_dd_bs=${CISS_WIPE_DD_BS:-64M} ciss_tmpfs_pct=${CISS_WIPE_TMPFS_PCT:-95}" prepare() { if [ -w /proc/sys/kernel/kexec_load_disabled ] && [ "$(cat /proc/sys/kernel/kexec_load_disabled)" = "1" ]; then echo 0 > /proc/sys/kernel/kexec_load_disabled || true fi if command -v kexec >/dev/null 2>&1 && [ -s "$KERNEL" ] && [ -s "$INITRD" ]; then kexec -l "$KERNEL" --initrd="$INITRD" --append="$append_common" || true fi } fallback_inplace() { mount -t tmpfs -o "size=95%,nodev,nosuid,noexec,mode=0700" tmpfs /run/wipe 2>/dev/null || mkdir -p /run/wipe i=0 while :; do dd if=/dev/zero of="/run/wipe/blk_$i" bs="${CISS_WIPE_DD_BS:-64M}" status=none || break i=$((i+1)) done sync; echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true rm -f /run/wipe/blk_* 2>/dev/null || true sync systemctl poweroff -f || poweroff -f || echo o > /proc/sysrq-trigger } execute() { sync; echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true if command -v systemctl >/dev/null 2>&1 && systemctl --quiet is-system-running; then systemctl kexec || kexec -e || fallback_inplace else kexec -e || fallback_inplace fi } case "${1:-}" in prepare) prepare ;; execute) execute ;; *) echo "Usage: $0 {prepare|execute}" >&2; exit 2 ;; esac # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh EOF chmod 0755 /usr/local/sbin/ciss-memwipe ### Systemd service: load at boot, execute on shutdown. ------------------------------------------------------------------------ cat << 'EOF' >| /etc/systemd/system/ciss-memwipe.service [Unit] Description=CISS: preload and execute kexec-based RAM wipe on shutdown DefaultDependencies=no Before=shutdown.target After=local-fs.target network.target multi-user.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/local/sbin/ciss-memwipe prepare ExecStop=/usr/local/sbin/ciss-memwipe execute TimeoutStartSec=20s TimeoutStopSec=infinity [Install] WantedBy=multi-user.target EOF install -d -m 0755 /etc/systemd/system/multi-user.target.wants ln -sf /etc/systemd/system/ciss-memwipe.service /etc/systemd/system/multi-user.target.wants/ciss-memwipe.service systemctl enable ciss-memwipe.service printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ ✅ '%s' applied successfully. \e[0m\n" "${0}" exit 0 # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh