#!/bin/bash # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-06-17; WEIDNER, Marc S.; # SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.installer.git # SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency # SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: EUPL-1.2 OR LicenseRef-CCLA-1.0 # SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework. # SPDX-PackageName: CISS.debian.installer # SPDX-Security-Contact: security@coresecret.eu guard_sourcing ####################################### # Configure the target system for chroot. # Globals: # TARGET # VAR_CHROOT_ACTIVATED # VAR_CHROOT_SYS_MASK_HELPER # VAR_NEED_RUN_IN_TARGET # Arguments: # None # Returns: # 0: on success # ERR_CHRT_MOUNTS: on failure ####################################### prepare_mounts() { ### Notes # This function mounts all necessary pseudo filesystems into the target root environment to enable chroot operations. # --rbind: recursive binding. # --make-rslave: In this case, the mount point is marked as 'slave'. # This means changes to the source mount (e.g., /proc) are propagated to the target mount (e.g., "${TARGET}/proc"). # Conversely, changes to the target mount are not propagated back to the source mount. # This mode is necessary to avoid problems with double or erroneous propagation effects in chroot or container environments. # # Some subdirectories (such as /dev/pts, /dev/shm, /sys/fs/cgroup) are remounted with more restrictive options # like 'noexec', 'nosuid', and 'nodev' to enhance security. This ensures they override the inherited bind-mounts and # enforce proper runtime behavior in the chroot. ### Declare Arrays, HashMaps, and Variables. declare -A HMP_SPECIAL_MOUNTS=( ["/dev"]="devtmpfs devtmpfs mode=0755,nosuid" # Base device node FS ["/dev/pts"]="devpts devpts noexec,nosuid" # Pseudoterminals ["/dev/shm"]="tmpfs tmpfs rw,nosuid,nodev" # Shared memory ["/dev/mqueue"]="mqueue mqueue rw,nosuid,nodev,noexec" # POSIX message queues ["/dev/hugepages"]="hugetlbfs hugetlbfs rw,nosuid,nodev" # Huge pages ["/proc"]="proc proc nosuid,noexec,nodev" # procfs ["/sys"]="sysfs sysfs nosuid,noexec,nodev" # sysfs ["/sys/fs/cgroup"]="cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime" # Unified cgroup2 ) declare var_path="" var_fs="" var_src="" var_opts="" # shellcheck disable=SC2034 declare -g VAR_CHROOT_SYS_MASK_HELPER="" # shellcheck disable=SC2034 VAR_CHROOT_SYS_MASK_HELPER=$(cat <<'EOF' #------------------------------------------------------------------------------- # Helpers: mount detection and conditional umount (idempotent). cdi_is_mounted() { declare path="${1:?target path required}" # Prefer findmnt, fall back to mountpoint, then /proc/self/mountinfo. if command -v findmnt >/dev/null 2>&1; then if findmnt -rn --target "${path}" >/dev/null 2>&1; then return 0 else return 1 fi fi if command -v mountpoint >/dev/null 2>&1; then if mountpoint -q -- "${path}"; then return 0 else return 1 fi fi # Last resort: grep mountinfo. if grep -Fq " $(readlink -f -- "${path}") " /proc/self/mountinfo 2>/dev/null; then return 0 fi return 1 } cdi_umount_if_mounted() { declare path="${1:?target path required}" if cdi_is_mounted "${path}"; then # Lazy umount to avoid EBUSY in edge cases. if ! umount -l -- "${path}"; then printf 'ERROR: cannot umount %s\n' "${path}" >&2 return 128 fi fi return 0 } # ENTER: mask '/sys' without a host hardware view. cdi_sys_mask_enter() { declare state="/run/.ciss_sysmask" mkdir -p /run # Remember the pre-state, so we can restore exactly what was present. declare had_sys="0" declare had_cg="0" if cdi_is_mounted /sys; then had_sys="1" fi if cdi_is_mounted /sys/fs/cgroup; then had_cg="1" fi printf 'HAD_SYS=%s\nHAD_CG=%s\n' "${had_sys}" "${had_cg}" >| "${state}" # Unmount existing mounts if present; do not error if they were absent. cdi_umount_if_mounted /sys/fs/cgroup || return 129 cdi_umount_if_mounted /sys || return 130 # Mask '/sys' with a tiny read-only tmpfs; directory must exist. mkdir -p /sys if ! mount -t tmpfs -o ro,nosuid,nodev,noexec,mode=0555,size=1M tmpfs /sys; then printf 'ERROR: cannot mount tmpfs on /sys\n' >&2 return 131 fi return 0 } # LEAVE: restore pre-enter state exactly as it was. cdi_sys_mask_leave() { declare state="/run/.ciss_sysmask" declare had_sys="0" declare had_cg="0" if [[ -f "${state}" ]]; then # shellcheck disable=SC2155 declare had_sys="$(grep -Eo '^HAD_SYS=[01]' "${state}" 2>/dev/null | cut -d= -f2 || printf '0')" # shellcheck disable=SC2155 declare had_cg="$(grep -Eo '^HAD_CG=[01]' "${state}" 2>/dev/null | cut -d= -f2 || printf '0')" fi # Drop the mask (tmpfs on /sys) if still mounted. cdi_umount_if_mounted /sys || return 132 # Restore '/sys' only if it was mounted before entering. if [[ "${had_sys}" == "1" ]]; then if ! mount -t sysfs -o nosuid,nodev,noexec sysfs /sys; then printf 'ERROR: cannot mount sysfs on /sys\n' >&2 return 133 fi fi # Restore 'cgroup2' only if it was mounted before entering. if [[ "${had_cg}" == "1" ]]; then mkdir -p /sys/fs/cgroup if ! mount -t cgroup2 -o rw,nosuid,nodev,noexec,relatime cgroup2 /sys/fs/cgroup; then printf 'ERROR: cannot mount cgroup2 on /sys/fs/cgroup\n' >&2 return 134 fi fi rm -f -- "${state}" return 0 } #------------------------------------------------------------------------------- EOF ) for var_path in "${!HMP_SPECIAL_MOUNTS[@]}"; do mkdir -p "${TARGET}${var_path}" done for var_path in "${!HMP_SPECIAL_MOUNTS[@]}"; do IFS=" " read -r var_fs var_src var_opts <<< "${HMP_SPECIAL_MOUNTS[${var_path}]}" if mountpoint -q "${TARGET}${var_path}"; then do_log "info" "file_only" "4010() Skipped: '${TARGET}${var_path}' is already a mountpoint." continue fi if ! mount -t "${var_fs}" "${var_src}" "${TARGET}${var_path}" -o "${var_opts}"; then do_log "emergency" "file_only" "4010() Command: [mount -t ${var_fs} ${var_src} ${TARGET}${var_path} -o ${var_opts}] failed." return "${ERR_CHRT_MOUNTS}" fi do_log "info" "file_only" "4010() Command: [mount -t ${var_fs} ${var_src} ${TARGET}${var_path} -o ${var_opts}] successful." done if [[ "${VAR_NEED_RUN_IN_TARGET:-false}" == "true" ]]; then mkdir -p "${TARGET}/run" if ! mount --make-rslave --rbind /run "${TARGET}/run"; then do_log "emergency" "file_only" "4010() Command: [mount --make-rslave --rbind /run ${TARGET}/run] failed." return "${ERR_CHRT_MOUNTS}" fi do_log "info" "file_only" "4010() Command: [mount --make-rslave --rbind /run ${TARGET}/run] successful." fi if ! chroot_exec "${TARGET}" mkdir -p /etc/systemd/system/multi-user.target.wants; then do_log "emergency" "file_only" "4010() Command: [chroot_exec ${TARGET} mkdir -p /etc/systemd/system/multi-user.target.wants] failed." return "${ERR_CHRT_MOUNTS}" fi do_log "info" "file_only" "4010() Command: [chroot_exec ${TARGET} mkdir -p /etc/systemd/system/multi-user.target.wants] successful." mkdir -p "${TARGET}/media/cdrom0" # shellcheck disable=SC2034 declare -gx VAR_CHROOT_ACTIVATED="system" do_log "info" "file_only" "4010() Command: [declare -gx VAR_CHROOT_ACTIVATED=system]" guard_dir && return 0 } ### Prevents accidental 'unset -f'. # shellcheck disable=SC2034 readonly -f prepare_mounts # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh