V8.00.000.2025.06.17
All checks were successful
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Successful in 59s

Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
This commit is contained in:
2025-09-14 13:49:38 +02:00
parent d9a1c926de
commit 175cfd0bff
2 changed files with 95 additions and 56 deletions

View File

@@ -154,7 +154,7 @@ chroot_script() {
}
#######################################
# Run the installer-desired code via stdin inside the chroot with bash -s.
# Run the installer-desired code incl. positional arguments via stdin (HEREDEC) inside the chroot with bash -s.
# Globals:
# BASH_SOURCE
# TERM
@@ -163,9 +163,9 @@ chroot_script() {
# VAR_DEBUG_TRAP
# VAR_IN_DIALOG_WR
# Arguments:
# 1: Target of the chroot environment
# 2: Command string to execute inside a shell (quoted)
# 3: Log level of command pipeline to be executed.
# 1: Target of chroot environment
# 2: Command string to execute inside a shell (HEREDOC):
# chroot_stdin "${TARGET}" "__payload__" -- "${ARG1}" "${ARG2}" ... <<'EOF' ... EOF
# Returns:
# 0: on success
# ERR_CHRT_COMMAND: on failure
@@ -193,7 +193,7 @@ chroot_stdin() {
DEBIAN_FRONTEND="noninteractive" \
APT_LISTCHANGES_FRONTEND="none" \
/bin/bash -o errexit -o errtrace -o functrace -o nounset -o pipefail \
-O inherit_errexit -O failglob -O lastpipe -s
-O inherit_errexit -O failglob -O lastpipe -s -- "$@"
then

View File

@@ -401,10 +401,18 @@ generate_totp_secret() {
guard_trace on
### Derive 20 bytes via HKDF-SHA256 using OpenSSL 3 kdf, output as raw, then base32 (uppercase, no padding).
### NOTE: 'key' must be provided via '-kdfopt key:hex:<STRING>'; expects a hexstring (no spaces).
# 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:]'
openssl kdf -keylen 20 \
-kdfopt digest:SHA256 \
-kdfopt key:hex:"${VAR_TEMP_PLAIN_MFA_SEED}" \
-kdfopt salt:"${var_salt}" \
-kdfopt info:"${var_info}" \
-binary HKDF \
| base32 \
| tr -d '=' \
| tr '[:lower:]' '[:upper:]'
)"
printf '%s\n' "${var_secret}"
@@ -427,31 +435,39 @@ hardening_su() {
### Declare Arrays, HashMaps, and Variables.
declare -r pam_su="/etc/pam.d/su"
[[ -f "${pam_su}" ]] || return 0
[[ -f "${TARGET}${pam_su}" ]] || return 0
### If the pam_wheel line already exists with the group=sudo and use_uid, then do nothing.
if grep -Eq '^[[:space:]]*auth[[:space:]]+required[[:space:]]+pam_wheel\.so([[:space:]].*)?\bgroup=sudo\b([[:space:]].*)?\buse_uid\b' "${pam_su}"; then
return 0
fi
chroot_stdin "${TARGET}" "__payload__" -- "${pam_su}" <<'EOF'
export LC_ALL=C
pam="$1"
### Insert 'auth required pam_wheel.so use_uid group=sudo' before pam_unix/rootok (fail early).
if grep -Eq '^[[:space:]]*auth[[:space:]]+required[[:space:]]+pam_wheel[.]so([[:space:]].*)?group=sudo([[:space:]].*)?use_uid' "${pam}"; then
:
else
tmp="$(mktemp "${pam}.XXXXXX")"
### 1) Insert rule before pam_unix.so or pam_rootok.so (fail early). Fallback: append.
awk '
BEGIN{ins=0}
BEGIN { ins=0 }
{
### Insert just before the first pam_unix or pam_rootok auth line.
if (!ins && $0 ~ /^[[:space:]]*auth[[:space:]]+.*pam_(unix|rootok)\.so/) {
if (!ins && $0 ~ /^[[:space:]]*auth[[:space:]]+.*pam_(unix|rootok)[.]so/ ) {
print "auth required pam_wheel.so use_uid group=sudo"
ins=1
}
print
}
END{
END {
if (!ins) {
### Fallback: append if no anchor found
print "auth required pam_wheel.so use_uid group=sudo"
}
}
' "${pam_su}" > "${pam_su}.new" && mv -f "${pam_su}.new" "${pam_su}"
' "${pam}" >| "${tmp}"
test -s "${tmp}"
mv -f "${tmp}" "${pam}"
rm -f -- "${tmp}" || :
fi
:
EOF
return 0
}
@@ -502,7 +518,7 @@ EOF
if [[ ! -f "${var_sudoers_winscp_global}" ]]; then
cat << EOF > "${var_sudoers_winscp_global}"
cat << EOF >| "${var_sudoers_winscp_global}"
### Added by CISS.debian.installer. WinSCP SFTP-as-root (least privilege).
### Allow exactly the sftp-server binary, optionally with -e (stderr logging).
Cmnd_Alias CISS_SFTPROOT = ${var_sftp_bin}, ${var_sftp_bin} -e
@@ -668,11 +684,12 @@ pam_access_totp_enable() {
declare var_module="$2"
declare var_pam_file="/etc/pam.d/${var_module}"
declare var_users_file="${TARGET}/etc/ciss/2fa.users"
declare var_allowlist="/etc/ciss/2fa.users"
### Basic sanitation; module must be a safe 'pam.d' filename.
[[ -n "${var_user:-}" && -n "${var_module:-}" ]] || return 0
[[ "${var_module}" =~ ^[A-Za-z0-9._+-]+$ ]] || return 0
[[ -f "${var_pam_file}" ]] || return 0
[[ -f "${TARGET}${var_pam_file}" ]] || return 0
### 0) Ensure the allowlist file contains the user (deduplicated).
if ! grep -Fxq "${var_user}" "${var_users_file}"; then
@@ -682,44 +699,66 @@ pam_access_totp_enable() {
### 1) Ensure a single CISS TOTP framework block is present in the PAM file.
### The block gates GA by pam_listfile over '/etc/ciss/2fa.users'.
### We place it right after pam_unix.so or @include common-auth; fallback: append.
if ! grep -q '^# CISS TOTP START$' "${var_pam_file}"; then
awk -v START='# CISS TOTP START' -v END='# CISS TOTP END' '
BEGIN{ins=0}
chroot_stdin "${TARGET}" "__payload__" -- "${var_pam_file}" "${var_allowlist}" <<'EOF'
export LC_ALL=C
pam="$1"
allowlist="$2"
tmp="$(mktemp "${pam}.XXXXXX")"
awk -v START='# CISS TOTP START' -v END='# CISS TOTP END' -v allowlist="${allowlist}" '
BEGIN { ins=0 }
{
print
if (!ins && ($0 ~ /^[[:space:]]*auth[[:space:]]+.*pam_unix\.so/ || $0 ~ /^[[:space:]]*@include[[:space:]]+common-auth/)) {
if (!ins && ($0 ~ /^[[:space:]]*auth[[:space:]]+.*pam_unix[.]so/ \
|| $0 ~ /^[[:space:]]*@include[[:space:]]+common-auth/)) {
print START
print "auth [success=1 default=ignore] pam_listfile.so item=user sense=deny file=/etc/ciss/2fa.users onerr=ignore"
# Only users in allowlist must pass GA:
# pam_listfile sense=deny succeeds for non-listed → skip next line (GA)
print "auth [success=1 default=ignore] pam_listfile.so item=user sense=deny file=" allowlist " onerr=ignore"
print "auth required pam_google_authenticator.so"
print END
ins=1
}
}
END{
END {
if (!ins) {
print START
print "auth [success=1 default=ignore] pam_listfile.so item=user sense=allow file=/etc/ciss/2fa.users onerr=ignore"
print "auth [success=1 default=ignore] pam_listfile.so item=user sense=deny file=" allowlist " onerr=ignore"
print "auth required pam_google_authenticator.so"
print END
}
}
' "${var_pam_file}" > "${var_pam_file}.new" && mv -f "${var_pam_file}.new" "${var_pam_file}"
fi
' "${pam}" >| "${tmp}"
test -s "${tmp}"
mv -f "${tmp}" "${pam}"
rm -f -- "${tmp}" || :
:
EOF
### 2) Comment out any other active GA lines to avoid double prompts.
### We keep the CISS block intact (recognized by the START/END markers).
awk '
BEGIN{in_ciss=0}
chroot_stdin "${TARGET}" "__payload__" -- "${var_pam_file}" <<'EOF'
export LC_ALL=C
pam="$1"
tmp="$(mktemp "${pam}.XXXXXX")"
awk '
BEGIN { in_ciss=0 }
/^# CISS TOTP START$/ { in_ciss=1; print; next }
/^# CISS TOTP END$/ { in_ciss=0; print; next }
{
if (!in_ciss && $0 ~ /^[[:space:]]*auth[[:space:]]+.*pam_google_authenticator\.so/ && $0 !~ /^[[:space:]]*#/) {
if (!in_ciss && $0 ~ /^[[:space:]]*auth[[:space:]]+.*pam_google_authenticator[.]so/ && $0 !~ /^[[:space:]]*#/) {
print "# " $0
} else {
print
}
}
' "${var_pam_file}" > "${var_pam_file}.new" && mv -f "${var_pam_file}.new" "${var_pam_file}"
' "${pam}" >| "${tmp}"
test -s "${tmp}"
mv -f "${tmp}" "${pam}"
rm -f -- "${tmp}" || :
:
EOF
return 0
}