#!/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 ####################################### # Updating user accounts. # Globals: # TARGET # VAR_SETUP_PATH # VAR_USER_MAX # user_root_authentication_access_ssh # user_root_password # user_root_shell # user_root_sshpubkey # Arguments: # None # Returns: # 0: on success ####################################### installation_accounts() { ### Declare Arrays, HashMaps, and Variables. declare -i i declare tmp_username="" tmp_fullname="" tmp_uid="" tmp_gid="" tmp_shell="" tmp_password="" tmp_sshpubkey="" tmp_sudo="" \ tmp_restricted="" declare var_username="" var_fullname="" var_uid="" var_gid="" var_shell="" var_password="" var_sshpubkey="" var_sudo="" \ var_restricted="" var_chpasswd="" var_sshdir="" ### Hardening '/etc/login.defs' rm -f "${TARGET}/etc/login.defs" insert_header "${TARGET}/etc/login.defs" insert_comments "${TARGET}/etc/login.defs" cat "${VAR_SETUP_PATH}/includes/target/etc/login.defs" >> "${TARGET}/etc/login.defs" ### Hardening '/etc/security/pwquality.conf' rm -f "${TARGET}/etc/security/pwquality.conf" insert_header "${TARGET}/etc/security/pwquality.conf" insert_comments "${TARGET}/etc/security/pwquality.conf" cat "${VAR_SETUP_PATH}/includes/target/etc/security/pwquality.cnf" >> "${TARGET}/etc/security/pwquality.conf" ### Preparing the root account chown root:root "${TARGET}/etc/passwd" "${TARGET}/etc/shadow" "${TARGET}/etc/group" "${TARGET}/etc/gshadow" chmod 0644 "${TARGET}/etc/passwd" "${TARGET}/etc/group" chmod 0600 "${TARGET}/etc/shadow" "${TARGET}/etc/gshadow" if [[ -x "${TARGET}${user_root_shell}" ]]; then chroot_exec "${TARGET}" chsh -s "${user_root_shell}" root else do_log "warn" "file_only" "4500() Shell: '${user_root_shell}' not found for: 'root'. Using '/bin/bash' instead." fi var_chpasswd="root:${user_root_password}" chroot_script "${TARGET}" "echo \"${var_chpasswd}\" | chpasswd -e" var_chpasswd="" install -d -m 0700 -o root -g root "${TARGET}/root/.ssh" install -m 0600 -o root -g root /dev/null "${TARGET}/root/.ssh/authorized_keys" grep -qxF "${user_root_sshpubkey}" "${TARGET}/root/.ssh/authorized_keys" || \ printf "%s\n" "${user_root_sshpubkey}" >> "${TARGET}/root/.ssh/authorized_keys" if [[ "${user_root_authentication_access_ssh}" == "false" ]]; then if grep -q '^\s*PermitRootLogin' "${TARGET}/etc/ssh/sshd_config"; then sed -i 's/^\s*PermitRootLogin\s\+.*/PermitRootLogin no/' "${TARGET}/etc/ssh/sshd_config" else echo 'PermitRootLogin no' >> "${TARGET}/etc/ssh/sshd_config" fi fi install -D -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/etc/skel/.bashrc" "${TARGET}/root/" install -D -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/etc/skel/.zshrc" "${TARGET}/root/" install -D -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/alias" "${TARGET}/root/.ciss/" install -D -m 0700 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/clean_logout.sh" "${TARGET}/root/.ciss/" install -D -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/shortcuts" "${TARGET}/root/.ciss/" # To be able to copy/paste from vim, one needs to create a '.vimrc' with the following content: echo 'set clipboard=unnamed' >| "${TARGET}/root/.vimrc" chmod 0600 "${TARGET}/root/.vimrc" do_log "info" "file_only" "User: 'root' updated." ### Install all user accounts. for ((i = 0; i <= VAR_USER_MAX; i++)); do tmp_username="user_user${i}_name" tmp_fullname="user_user${i}_fullname" tmp_uid="user_user${i}_uid" tmp_gid="user_user${i}_gid" tmp_shell="user_user${i}_shell" tmp_password="user_user${i}_password" tmp_sshpubkey="user_user${i}_sshpubkey" tmp_sudo="user_user${i}_privileges_sudo" tmp_restricted="user_user${i}_privileges_restricted" var_username="${!tmp_username}" var_fullname="${!tmp_fullname}" var_uid="${!tmp_uid}" var_gid="${!tmp_gid}" var_shell="${!tmp_shell}" var_password="${!tmp_password}" var_sshpubkey="${!tmp_sshpubkey}" var_sudo="${!tmp_sudo}" var_restricted="${!tmp_restricted}" chroot_exec "${TARGET}" getent group "${var_username}" >/dev/null || \ chroot_exec "${TARGET}" groupadd --gid "${var_gid}" "${var_username}" if [[ "${var_restricted}" == "false" ]]; then chroot_exec "${TARGET}" useradd \ --comment "${var_fullname}" \ --create-home \ --expiredate 2102-12-31 \ --gid "${var_gid}" \ --home-dir /home/"${var_username}" \ --inactive 0 \ --shell "${var_shell}" \ --uid "${var_uid}" \ "${var_username}" else chroot_exec "${TARGET}" useradd \ --comment "${var_fullname}" \ --expiredate 2102-12-31 \ --gid "${var_gid}" \ --home-dir /home/"${var_username}" \ --inactive 0 \ --no-create-home \ --shell "${var_shell}" \ --uid "${var_uid}" \ "${var_username}" fi var_chpasswd="${var_username}:${var_password}" chroot_script "${TARGET}" "echo \"${var_chpasswd}\" | chpasswd -e" var_chpasswd="" if [[ "${var_sudo}" == "true" ]]; then chroot_exec "${TARGET}" usermod -aG sudo "${var_username}" fi if [[ -n "${var_sshpubkey}" ]]; then var_sshdir="${TARGET}/home/${var_username}/.ssh" install -d -m 0700 -o "${var_username}" -g "${var_username}" "${var_sshdir}" install -m 0600 -o "${var_username}" -g "${var_username}" /dev/null "${var_sshdir}/authorized_keys" grep -qxF "${var_sshpubkey}" "${var_sshdir}/authorized_keys" || \ printf "%s\n" "${var_sshpubkey}" >> "${var_sshdir}/authorized_keys" fi do_log "info" "file_only" "Created user: [${var_username}] UID: [${var_uid}], GID: [${var_gid}]" done unset VAR_TEMP_PLAIN_MFA_SEED guard_dir && return 0 } ####################################### # Writes '.google_authenticator'-file for the respective user. # Globals: # RANDOM # TARGET # Arguments: # 1: Username # Returns: # 0: on success ####################################### write_google_authenticator_file() { ### Declare Arrays, HashMaps, and Variables. declare var_user="${1}" var_secret="" case "${1}" in root) declare var_base="${TARGET}/root" ;; *) declare var_base="${TARGET}/home/${var_user}" ;; esac declare -i i=0 ### TODO: PASSWORD REMINDER START:NOT ACTIVE #guard_trace on var_secret="$(generate_totp_secret "${var_user}")" umask 0077 { printf '%s\n' "${var_secret}" printf '"RATE_LIMIT 3 30"\n' printf '"WINDOW 10"\n' printf '"DISALLOW_REUSE"\n' printf '"TOTP_AUTH"\n' ### Emergency Codes: for i in {0..7}; do printf '%08d\n' "$(( RANDOM % 100000000 ))"; done } >| "${var_base}/.google_authenticator" ### TODO: PASSWORD REMINDER STOP:NOT ACTIVE #guard_trace off chown "${var_user}:${var_user}" "${var_base}/.google_authenticator" chmod 0600 "${var_base}/.google_authenticator" umask 0022 return 0 } ####################################### # Generates a deterministic TOTP secret based on: # Username, FQDN, MFA salt, MFA master seed # Globals: # VAR_FINAL_FQDN # VAR_TEMP_PLAIN_MFA_SEED # user_mfa_info # user_mfa_salt # Arguments: # 1: Username # Returns: # 0: on success ####################################### generate_totp_secret() { ### Declare Arrays, HashMaps, and Variables. declare var_user="${1}" declare var_host_id="${VAR_FINAL_FQDN}" declare var_salt="${user_mfa_salt}:${var_host_id}:${var_user}" declare var_info="${user_mfa_info}" declare var_secret="" ### TODO: PASSWORD REMINDER START:NOT ACTIVE #guard_trace on ### Derive 20 bytes via HKDF-SHA256 using OpenSSL 3 kdf, output as raw, then base32 (uppercase, no padding). # shellcheck disable=SC2312 var_secret="$( printf '%s' "${VAR_TEMP_PLAIN_MFA_SEED}" | xxd -r -p | openssl kdf -keylen 20 -kdfopt digest:SHA256 \ -kdfopt salt:"${var_salt}" -kdfopt info:"${var_info}" -binary HKDF | base32 | tr -d '=' | tr '[:lower:]' '[:upper:]' )" ### TODO: PASSWORD REMINDER STOP:NOT ACTIVE #guard_trace off printf '%s\n' "${var_secret}" return 0 } ####################################### # Reads a 256-bit seed from '${DIR_CNF}/mfa_master.txt' (64 hex chars) into VAR_TEMP_PLAIN_MFA_SEED. # Globals: # DIR_CNF # VAR_TEMP_PLAIN_MFA_SEED # Arguments: # None # Returns: # 0: on success # ERR_READ_SEED_FILE ####################################### read_totp_seed(){ ### Declare Arrays, HashMaps, and Variables. declare -r var_mfa_seed_file="${DIR_CNF}/mfa_master.txt" declare -g VAR_TEMP_PLAIN_MFA_SEED="" ### TODO: PASSWORD REMINDER START:NOT ACTIVE #guard_trace on if ! read_password_file "${var_mfa_seed_file}" VAR_TEMP_PLAIN_MFA_SEED; then return "${ERR_READ_SEED_FILE}" fi ### Validate: exactly 64 hex. [[ "${VAR_TEMP_PLAIN_MFA_SEED}" =~ ^[0-9a-fA-F]{64}$ ]] || return "${ERR_READ_SEED_FILE}" ### TODO: PASSWORD REMINDER STOP:NOT ACTIVE #guard_trace off return 0 } # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh