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

Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
This commit is contained in:
2025-10-17 06:42:14 +01:00
parent bedd0d3fa5
commit 7d599e8463
71 changed files with 2203 additions and 548 deletions

View File

@@ -12,12 +12,6 @@
guard_sourcing
# TODO: sudo TOTP deactivation
# TODO: PAM Module
# TODO: Check Password activation
# TODO: Check expiration and other dates
# TODO: Logic
#######################################
# Updating user accounts.
# Globals:
@@ -59,6 +53,12 @@ accounts_setup() {
chroot_logger "${var_target}${var_logfile}"
### Update pam modules for 2fa.
write_pam_login "${var_target}"
write_pam_sshd "${var_target}"
write_pam_su "${var_target}"
write_pam_sudo "${var_target}"
### Prepare the '2fa'-seed variable.
read_totp_seed
do_log "debug" "file_only" "4520() Command: [read_totp_seed]"
@@ -66,56 +66,17 @@ accounts_setup() {
### 0) The 'root' account is generated via debootstrap by default.
### 1) Prepare the 'root' account.
install -d -m 0700 -o root -g root "${var_target}/root/.ssh"
install -m 0600 -o root -g root /dev/null "${var_target}/root/.ssh/authorized_keys"
install -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/etc/skel/.bashrc" "${var_target}/root/"
install -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/etc/skel/.ciss/theme_eza_ciss.yml" "${var_target}/root/.ciss/"
install -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/alias" "${var_target}/root/.ciss/"
install -m 0700 -o root -g root "${VAR_SETUP_PATH}/includes/target/etc/skel/.ciss/check_chrony.sh" "${var_target}/root/.ciss/"
install -m 0700 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/clean_logout.sh" "${var_target}/root/.ciss/"
install -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/f2bchk" "${var_target}/root/.ciss/"
install -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/scan_libwrap" "${var_target}/root/.ciss/"
install -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/root/.ciss/shortcuts" "${var_target}/root/.ciss/"
case "${VAR_USER_ROOT_SPECIFIC}" in
if [[ "${user_root_shell}" == "/bin/zsh" ]]; then
"ciss" ) accounts_setup_ciss_root ;;
if [[ -x "${var_target}${user_root_shell}" ]]; then
"physnet") accounts_setup_physnet_root ;;
case "${VAR_USER_ROOT_SPECIFIC,,}" in
"none" ) do_log "info" "file_only" "4520() Account preparation [none] selected." ;;
"ciss")
zsh_omz_installer "root" "${var_target}"
mv "${var_target}/root/.zshrc" "${var_target}/root/.zshrc.bak"
install -m 0600 -o root -g root "${VAR_SETUP_PATH}/includes/target/etc/skel/.zshrc" "${var_target}/root/"
;;
* ) do_log "warn" "file_only" "4520() Account preparation nothing selected. Keeping defaults." ;;
"physnet")
:
;;
"none"|*)
:
;;
esac
chroot_exec "${var_target}" chsh -s "${user_root_shell}" root
do_log "info" "file_only" "4520() Shell: '${user_root_shell}' used for: 'root'."
else
chroot_exec "${var_target}" chsh -s /bin/bash root
do_log "info" "file_only" "4520() Shell: '${user_root_shell}' not found for: 'root'. Using '/bin/bash' instead."
fi
fi
### To be able to copy/paste from vim, one needs to create a '.vimrc' with the following content:
echo 'set clipboard=unnamed' >| "${var_target}/root/.vimrc"
chmod 0600 "${var_target}/root/.vimrc"
do_log "info" "file_only" "4520() Skeleton: 'root' successfully generated."
esac
### 2) Check SSH access capabilities.
case "${user_root_authentication_access_ssh,,}" in
@@ -126,6 +87,7 @@ accounts_setup() {
;;
true)
### SSH Public Key per default, only.
sed -i -E "s|^[[:space:]]*PermitRootLogin[[:space:]]+.*$|$(printf '%-29s%s' 'PermitRootLogin' 'prohibit-password')|" "${var_target}/etc/ssh/sshd_config"
do_log "info" "file_only" "4520() User: 'root' SSH access: [PermitRootLogin prohibit-password]"
;;
@@ -141,54 +103,26 @@ accounts_setup() {
case "${user_root_authentication_access_tty,,}" in
false)
### 3) A) 1) Ensure the 'pam_access' line is not activated in '/etc/pam.d/login' and '/etc/pam.d/sshd' in parallel.
pam_access_sync_login_sshd
### 3) A) 2) Ensure 'pam_securetty' in the auth phase; requisite causes immediate fail for disallowed ttys.
chroot_stdin "${var_target}" "__payload__" <<'EOF'
export LC_ALL=C
if ! grep -Eq '^[[:space:]]*auth[[:space:]]+requisite[[:space:]]+pam_securetty[.]so([[:space:]]|$)' /etc/pam.d/login; then
tmp="$(mktemp /etc/pam.d/login.XXXXXX)"
awk '
BEGIN { ins=0 }
{
if (!ins && $0 ~ /^[[:space:]]*auth[[:space:]]+.*pam_unix[.]so/) {
print "auth requisite pam_securetty.so"
ins=1
}
print
}
END {
if (!ins) print "auth requisite pam_securetty.so"
}
' /etc/pam.d/login >| "${tmp}"
test -s "${tmp}"
mv -f "${tmp}" /etc/pam.d/login
rm -f -- "${tmp}"
fi
:
EOF
### 3) A) 3) Disallow all local access for root in '/etc/security/access.conf'.
### Disallow all local access for root in '/etc/security/access.conf'.
printf -- '-: root:ALL\n' >> "${var_target}/etc/security/access.conf"
do_log "info" "file_only" "4520() User: 'root' [disallow all local access in '/etc/security/access.conf']"
### 3) A) 4) Empty "/etc/securetty".
### Empty "/etc/securetty".
cat << 'EOF' >| "${var_target}/etc/securetty"
EOF
do_log "info" "file_only" "4520() User: 'root' tty access: [false]"
do_log "info" "file_only" "4520() User: 'root' [empty '/etc/securetty']"
;;
true)
### 3) B) 1) Allow local access for 'root' only on 'tty1' in '/etc/security/access.conf'.
### Allow local access for 'root' only on 'tty1' in '/etc/security/access.conf'.
printf -- "+: root:tty1 \n" >> "${var_target}/etc/security/access.conf"
do_log "info" "file_only" "4520() User: 'root' [allow local access on tty1 in '/etc/security/access.conf']"
### 3) B) 2) Allow local access for 'root' only on 'tty1' in '/etc/securetty'.
### Allow local access for 'root' only on 'tty1' in '/etc/securetty'.
cat << 'EOF' >| "${var_target}/etc/securetty"
tty1
EOF
do_log "info" "file_only" "4520() User: 'root' tty access: [true]"
do_log "info" "file_only" "4520() User: 'root' [tty1 in '/etc/securetty']."
;;
*)
@@ -199,6 +133,8 @@ EOF
esac
### 4) Check the password policy for the 'root' account.
chroot_script "${var_target}" "printf '%s:%s\n' 'root' '${user_root_password}' | /usr/sbin/chpasswd -e"
case "${user_root_authentication_password,,}" in
false)
@@ -207,8 +143,7 @@ EOF
;;
true)
chroot_script "${var_target}" "printf '%s:%s\n' 'root' '${var_password}' | /usr/sbin/chpasswd -e"
#chroot_script "${var_target}" "/usr/sbin/usermod -p '${user_root_password}' root"
chroot_script "${var_target}" "passwd -u root"
do_log "info" "file_only" "4520() User: 'root' password access: [true]"
;;
@@ -227,17 +162,16 @@ EOF
fi
### 6) Update the 'root' 'totp'-policy and write the '.google_authenticator'-file.
### 6) Update the 'root' 'totp'-policy-user file and write the '.google_authenticator'-file.
if [[ "${user_root_authentication_2fa_ssh}" == "true" || "${user_root_authentication_2fa_tty}" == "true" ]]; then
write_google_authenticator_file "root" "0" "0" "${var_target}"
printf '%s\n' "root" >> "${var_target}/etc/ciss/2fa.users"
fi
if [[ "${user_root_authentication_2fa_ssh}" == "true" ]]; then
pam_access_totp_enable "root" "sshd" "${var_target}"
var_ssh_totp_update="true"
cat << EOF >> "${var_target}/etc/ssh/sshd_config"
Match User root
@@ -246,8 +180,6 @@ Match User root
EOF
fi
[[ "${user_root_authentication_2fa_tty}" == "true" ]] && pam_access_totp_enable "root" "login" "${var_target}"
### 7) Install eza themes.
eza_installer "root" "${var_target}"
@@ -844,172 +776,6 @@ EOF
# shellcheck disable=SC2034
readonly -f hardening_sudo
#######################################
# Ensure the 'pam_access' line is not activated in '/etc/pam.d/login' and '/etc/pam.d/sshd' in parallel.
# MUST be executed inside chroot.
# Globals:
# None
# Arguments:
# None
# Returns:
# 0: on success
#######################################
pam_access_sync_login_sshd() {
### Declare Arrays, HashMaps, and Variables.
declare var_file_login="/etc/pam.d/login"
declare var_file_sshd="/etc/pam.d/sshd"
### Guard: The file must exist, no-op otherwise.
if [[ ! -f "${var_target}${var_file_login}" ]]; then
return 0
fi
### 1) If the 'pam_access' line is commented in '/etc/pam.d/login', uncomment exactly one occurrence.
chroot_stdin "${var_target}" "__payload__" <<'EOF'
tmp="$(mktemp /etc/pam.d/login.XXXXXX)"
export LC_ALL=C
awk '
BEGIN { done=0 }
{
if (!done) {
line=$0
sub(/^[[:space:]]*#+[[:space:]]*/, "", line)
if (line ~ /^[[:space:]]*account[[:space:]]+required[[:space:]]+pam_access[.]so([[:space:]]|$)/) {
print line; done=1; next
}
}
print
}
' /etc/pam.d/login >| "${tmp}"
test -s "${tmp}"
mv -f "${tmp}" /etc/pam.d/login
rm -f "${tmp}" || :
:
EOF
### 2) If '/etc/pam.d/login' now has an active pam_access line, ensure '/etc/pam.d/sshd' pam_access line(s) are commented out.
### No-op if '/etc/pam.d/sshd' is absent.
[[ -f "${var_target}${var_file_sshd}" ]] || return 0
chroot_stdin "${var_target}" "__payload__" <<'EOF'
export LC_ALL=C
if grep -Eq '^[[:space:]]*account[[:space:]]+required[[:space:]]+pam_access[.]so([[:space:]]|$)' /etc/pam.d/login; then
tmp="$(mktemp /etc/pam.d/sshd.XXXXXX)"
awk '
/^[[:space:]]*account[[:space:]]+required[[:space:]]+pam_access[.]so([[:space:]]|$)/ { print "# " $0; next }
{ print }
' /etc/pam.d/sshd >| "${tmp}"
test -s "${tmp}"
mv -f "${tmp}" /etc/pam.d/sshd
rm -f "${tmp}" || :
fi
:
EOF
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f pam_access_sync_login_sshd
#######################################
# Enable per-user TOTP in a given PAM service (login, sshd, su, sudo).
# Globals:
# None
# Arguments:
# 1: username
# 2: pam_module
# 3: target
# Returns:
# 0: on success
#######################################
pam_access_totp_enable() {
### Declare Arrays, HashMaps, and Variables.
declare -r var_user="${1}" var_module="${2}" var_target="${3}"
declare -r var_pam_file="/etc/pam.d/${var_module}"
declare -r var_users_file="${var_target}/etc/ciss/2fa.users"
declare -r 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_target}${var_pam_file}" ]] || return 0
### 0) Ensure the allowlist file contains the user (deduplicated).
if ! grep -Fxq "${var_user}" "${var_users_file}"; then
printf '%s\n' "${var_user}" >> "${var_users_file}"
fi
### 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.
chroot_stdin "${var_target}" "__payload__" -- "${var_pam_file}" "${var_allowlist}" <<'EOF'
export LC_ALL=C
pam="$1"
allowlist="$2"
tmp="$(mktemp "${pam}.XXXXXX")"
awk -v MARK_S="# CISS TOTP START" -v MARK_E="# 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/)) {
print MARK_S
### 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 MARK_E
ins=1
}
}
END {
if (!ins) {
print MARK_S
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 MARK_E
}
}
' "${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.
chroot_stdin "${var_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:]]*#/) {
print "# " $0
} else {
print
}
}
' "${pam}" >| "${tmp}"
test -s "${tmp}"
mv -f "${tmp}" "${pam}"
rm -f -- "${tmp}" || :
:
EOF
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f pam_access_totp_enable
#######################################
# Reads a 256-bit seed from '${DIR_CNF}/mfa_master.txt' (64 hex chars) into VAR_TEMP_PLAIN_MFA_SEED.
# Globals:
@@ -1113,6 +879,379 @@ write_google_authenticator_file() {
# shellcheck disable=SC2034
readonly -f write_google_authenticator_file
#######################################
# Writes CISS Header for '/etc/pam.d/login'.
# Globals:
# None
# Arguments:
# 1: TARGET
# Returns:
# 0: on success
#######################################
write_pam_login() {
### Declare Arrays, HashMaps, and Variables.
declare -r var_target="$1"
mv "${var_target}/etc/pam.d/login" "${var_target}/root/.ciss/cdi/backup/etc/pam.d/login"
insert_header "${var_target}/etc/pam.d/login"
insert_comments "${var_target}/etc/pam.d/login"
cat << EOF >> "${var_target}/etc/pam.d/login"
#
# The PAM configuration file for the Shadow 'login' service
#
# --- AUTH phase ---------------------------------------------------------------------------------------------------------------
# Root only on secure ttys listed in '/etc/securetty' (fail fast, no prompts).
auth requisite pam_securetty.so
# Enforce a minimal delay in case of failure (in microseconds). (Replaces the 'FAIL_DELAY' setting from login.defs).
# Note that other modules may require another minimal delay. (For example, to disable any delay, you should add the 'nodelay'
# option to pam_unix).
auth optional pam_faildelay.so delay=3200000
# Outputs an issue file prior to each login prompt (Replaces the ISSUE_FILE option from login.defs). Uncomment for use.
#auth required pam_issue.so issue=/etc/issue
# Disallows other than root logins when /etc/nologin exists. (Replaces the 'NOLOGINS_FILE' option from login.defs).
auth requisite pam_nologin.so
# SELinux needs to be the first session rule. This ensures that any lingering context has been cleared. Without this it is
# possible that a module could execute code in the wrong domain. When the module is present, "required" would be sufficient
# (When SELinux is disabled, this returns success.)
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Sets the loginuid process attribute
session required pam_loginuid.so
# Prints the message of the day upon successful login. (Replaces the 'MOTD_FILE' option in login.defs). This includes a
# dynamically generated part from /run/motd.dynamic, and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# SELinux needs to intervene at login time to ensure that the process starts in the proper default security context. Only
# sessions which are intended to run in the user's context should be run after this. The module pam_selinux.so changes the
# SELinux context of the used TTY and configures SELinux in order to transition to the user context with the next execve()
# call.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# When the module is present, "required" would be sufficient (When SELinux is disabled, this returns success.)
# This module parses environment configuration file(s) and also allows you to use an extended config file
# /etc/security/pam_env.conf. Parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# Locale variables can also be set in /etc/default/locale reading this file *in addition to /etc/environment* does not hurt.
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Standard Un*x authentication.
@include common-auth
# ===== CISS 2FA block ========
# If user is NOT listed -> succeed and SKIP next two lines (no TOTP prompt).
auth [success=2 default=ignore] pam_listfile.so item=user sense=deny file=/etc/ciss/2fa.users onerr=ignore
# For listed users: enforce that the secret file exists, else deny without prompting.
# pam_google_authenticator will itself fail if the file is absent; we add a clear hint before it.
# No 'nullok' here: listed users MUST have a secret; missing -> hard fail.
auth required pam_echo.so file=/etc/ciss/pam_login_totp.prompt
auth required pam_google_authenticator.so disallow-reuse
# ===== CISS 2FA block end =====
# This allows certain extra groups to be granted to a user based on things like time of day, tty, service, and user. Please
# edit /etc/security/group.conf to fit your needs (Replaces the 'CONSOLE_GROUPS' option in login.defs).
auth optional pam_group.so
# Uncomment and edit /etc/security/time.conf if you need to set time restraint on logins. (Replaces the 'PORTTIME_CHECKS_ENAB'
# option from login.defs as well as /etc/porttime).
#account requisite pam_time.so
# Uncomment and edit /etc/security/access.conf if you need to set access limits. (Replaces /etc/login.access file).
#account required pam_access.so
# Sets up user limits according to /etc/security/limits.conf. (Replaces the use of /etc/limits in old login).
session required pam_limits.so
# Prints the status of the user's mailbox upon successful login (Replaces the 'MAIL_CHECK_ENAB' option from login.defs).
# This also defines the MAIL environment variable. However, userdel also needs MAIL_DIR and MAIL_FILE variables in
# /etc/login.defs to make sure that removing a user also removes the user's mail spool file. See comments in /etc/login.defs.
session optional pam_mail.so standard
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Console-only access control for this service (do NOT also enable in common-account).
account requisite pam_access.so
# Standard Un*x account and session
@include common-account
@include common-session
@include common-password
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf
EOF
do_log "info" "file_only" "4520() Written: [/etc/pam.d/login]."
cat << 'EOF' >| "${var_target}/etc/ciss/pam_login_totp.prompt"
Please enter your 6-digit TOTP or 8-digit Backup code:
EOF
chmod 0444 "${var_target}/etc/ciss/pam_login_totp.prompt"
do_log "info" "file_only" "4520() Written: [/etc/ciss/pam_login_totp.prompt]."
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f write_pam_login
#######################################
# Writes CISS Header for '/etc/pam.d/sshd'.
# Globals:
# None
# Arguments:
# 1: TARGET
# Returns:
# 0: on success
#######################################
write_pam_sshd() {
### Declare Arrays, HashMaps, and Variables.
declare -r var_target="$1"
mv "${var_target}/etc/pam.d/sshd" "${var_target}/root/.ciss/cdi/backup/etc/pam.d/sshd"
insert_header "${var_target}/etc/pam.d/sshd"
insert_comments "${var_target}/etc/pam.d/sshd"
cat << EOF >> "${var_target}/etc/pam.d/sshd"
#
# PAM configuration for the Secure Shell service
#
# ===== CISS 2FA block ========
# If user is NOT listed -> succeed and SKIP next two lines (silent Keyboard-Interactive (KI) success).
auth [success=2 default=ignore] pam_listfile.so item=user sense=deny file=/etc/ciss/2fa.users onerr=ignore
# For listed users: enforce that the secret file exists, else deny without prompting.
# pam_google_authenticator will itself fail if the file is absent; we add a clear hint before it.
# No 'nullok' here: listed users MUST have a secret; missing -> hard fail.
auth required pam_echo.so file=/etc/ciss/pam_ssh_totp.prompt
auth required pam_google_authenticator.so disallow-reuse
# For non-2FA users KI must be a silent success to satisfy AuthenticationMethods.
auth sufficient pam_permit.so
# ===== CISS 2FA block end =====
# Keep the rest as shipped by Debian. It will be short-circuited by pam_permit for KI and never reached for 2FA users after
# successful GA.
# Standard Un*x authentication.
@include common-auth
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex access limits that are hard to express in sshd_config.
#account required pam_access.so
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any lingering context has been cleared. Without this it is
# possible that a module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login. This includes a dynamically generated part from /run/motd.dynamic and a
# static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and /etc/security/pam_env.conf.
session required pam_env.so
# In Debian 4.0 (etch), locale-related environment variables were moved to /etc/default/locale, so read that as well.
session required pam_env.so envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts in the proper default security context. Only
# sessions which are intended to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf
EOF
do_log "info" "file_only" "4520() Written: [/etc/pam.d/sshd]."
cat << 'EOF' >| "${var_target}/etc/ciss/pam_ssh_totp.prompt"
Please enter your 6-digit TOTP or 8-digit Backup code:
EOF
chmod 0444 "${var_target}/etc/ciss/pam_ssh_totp.prompt"
do_log "info" "file_only" "4520() Written: [/etc/ciss/pam_ssh_totp.prompt]."
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f write_pam_sshd
#######################################
# Writes CISS Header for '/etc/pam.d/su'.
# Globals:
# None
# Arguments:
# 1: TARGET
# Returns:
# 0: on success
#######################################
write_pam_sudo() {
### Declare Arrays, HashMaps, and Variables.
declare -r var_target="$1"
mv "${var_target}/etc/pam.d/su" "${var_target}/root/.ciss/cdi/backup/etc/pam.d/su"
insert_header "${var_target}/etc/pam.d/su"
insert_comments "${var_target}/etc/pam.d/su"
cat << EOF >> "${var_target}/etc/pam.d/su"
#
# PAM configuration for the su service
#
# If caller is already root, allow quickly without further auth:
auth sufficient pam_rootok.so
# Reuse a recent successful su-auth within the TTL:
auth sufficient pam_timestamp.so
# Standard password for the target account (root or other):
@include common-auth
# ===== CISS 2FA block ========
# If user is NOT listed -> succeed and SKIP next two lines (no TOTP prompt).
auth [success=2 default=ignore] pam_listfile.so item=user sense=deny file=/etc/ciss/2fa.users onerr=ignore
# For listed users: enforce that the secret file exists, else deny without prompting.
# pam_google_authenticator will itself fail if the file is absent; we add a clear hint before it.
# No 'nullok' here: listed users MUST have a secret; missing -> hard fail.
auth required pam_echo.so file=/etc/ciss/pam_su_totp.prompt
auth required pam_google_authenticator.so disallow-reuse
# ===== CISS 2FA block end =====
@include common-account
@include common-session
# Keep a ticket to avoid re-prompts during this shell session:
session optional pam_timestamp.so
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf
EOF
do_log "info" "file_only" "4520() Written: [/etc/pam.d/su]."
cat << 'EOF' >| "${var_target}/etc/ciss/pam_su_totp.prompt"
Please enter the 6-digit TOTP or 8-digit Backup code of the target user:
EOF
chmod 0444 "${var_target}/etc/ciss/pam_su_totp.prompt"
do_log "info" "file_only" "4520() Written: [/etc/ciss/pam_su_totp.prompt]."
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f write_pam_su
#######################################
# Writes CISS Header for '/etc/pam.d/sudo'.
# Globals:
# None
# Arguments:
# 1: TARGET
# Returns:
# 0: on success
#######################################
write_pam_sudo() {
### Declare Arrays, HashMaps, and Variables.
declare -r var_target="$1"
mv "${var_target}/etc/pam.d/sudo" "${var_target}/root/.ciss/cdi/backup/etc/pam.d/sudo"
insert_header "${var_target}/etc/pam.d/sudo"
insert_comments "${var_target}/etc/pam.d/sudo"
cat << EOF >> "${var_target}/etc/pam.d/sudo"
#
# PAM configuration for the sudo service
#
# Reuse a recent successful auth to avoid re-prompting within the TTL.
auth sufficient pam_timestamp.so
# Standard UNIX password:
@include common-auth
# ===== CISS 2FA block ========
# If user is NOT listed -> succeed and SKIP next two lines (no TOTP prompt).
auth [success=2 default=ignore] pam_listfile.so item=user sense=deny file=/etc/ciss/2fa.users onerr=ignore
# For listed users: enforce that the secret file exists, else deny without prompting.
# pam_google_authenticator will itself fail if the file is absent; we add a clear hint before it.
# No 'nullok' here: listed users MUST have a secret; missing -> hard fail.
auth required pam_echo.so file=/etc/ciss/pam_sudo_totp.prompt
auth required pam_google_authenticator.so disallow-reuse
# ===== CISS 2FA block end =====
# Accounts, sessions:
@include common-account
@include common-session-noninteractive
# Maintain a pam_timestamp ticket on successful sudo to suppress re-prompts.
session optional pam_timestamp.so
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf
EOF
do_log "info" "file_only" "4520() Written: [/etc/pam.d/sudo]."
cat << 'EOF' >| "${var_target}/etc/ciss/pam_sudo_totp.prompt"
Please enter your 6-digit TOTP or 8-digit Backup code:
EOF
chmod 0444 "${var_target}/etc/ciss/pam_sudo_totp.prompt"
do_log "info" "file_only" "4520() Written: [/etc/ciss/pam_sudo_totp.prompt]."
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f write_pam_sudo
#######################################
# Use the official ohmyzsh-installer but force non-interactive behavior; do not run zsh; do not chsh.
# Globals:
@@ -1240,228 +1379,4 @@ EOF
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f zsh_omz_installer
#######################################
# Writes CISS Header for '/etc/pam.d/login'.
# Globals:
# None
# Arguments:
# 1: TARGET
# Returns:
# 0: on success
#######################################
write_pam_login() {
### Declare Arrays, HashMaps, and Variables.
declare -r var_target="$1"
mv "${var_target}/etc/pam.d/sshd" "${var_target}/root/.ciss/cdi/backup/etc/pam.d/login"
insert_header "${var_target}/etc/pam.d/login"
insert_comments "${var_target}/etc/pam.d/login"
cat << EOF >> "${var_target}/etc/pam.d/login"
#
# The PAM configuration file for the Shadow 'login' service
#
# Enforce a minimal delay in case of failure (in microseconds). (Replaces the 'FAIL_DELAY' setting from login.defs).
# Note that other modules may require another minimal delay. (For example, to disable any delay, you should add the 'nodelay'
# option to pam_unix).
auth optional pam_faildelay.so delay=3000000
# Outputs an issue file prior to each login prompt (Replaces the ISSUE_FILE option from login.defs). Uncomment for use.
# auth required pam_issue.so issue=/etc/issue
# Disallows other than root logins when /etc/nologin exists. (Replaces the 'NOLOGINS_FILE' option from login.defs).
auth requisite pam_nologin.so
# SELinux needs to be the first session rule. This ensures that any lingering context has been cleared. Without this it is
# possible that a module could execute code in the wrong domain. When the module is present, "required" would be sufficient
# (When SELinux is disabled, this returns success.)
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Sets the loginuid process attribute
session required pam_loginuid.so
# Prints the message of the day upon successful login. (Replaces the 'MOTD_FILE' option in login.defs). This includes a
# dynamically generated part from /run/motd.dynamic, and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# SELinux needs to intervene at login time to ensure that the process starts in the proper default security context. Only
# sessions which are intended to run in the user's context should be run after this. The module pam_selinux.so changes the
# SELinux context of the used TTY and configures SELinux in order to transition to the user context with the next execve()
# call.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# When the module is present, "required" would be sufficient (When SELinux is disabled, this returns success.)
# This module parses environment configuration file(s) and also allows you to use an extended config file
# /etc/security/pam_env.conf. Parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# Locale variables can also be set in /etc/default/locale reading this file *in addition to /etc/environment* does not hurt.
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Standard Un*x authentication.
@include common-auth
# ===== CISS 2FA block =====
# If user is NOT listed -> succeed and SKIP next two lines (no TOTP prompt).
auth [success=2 default=ignore] pam_listfile.so item=user sense=deny file=/etc/ciss/2fa onerr=ignore
# Listed users: show a clear hint and then require GA. No 'nullok': missing secret -> fail.
auth required pam_echo.so file=/etc/ciss/login_totp.prompt
auth required pam_google_authenticator.so
# ===== CISS 2FA block end =====
# This allows certain extra groups to be granted to a user based on things like time of day, tty, service, and user. Please
# edit /etc/security/group.conf to fit your needs (Replaces the 'CONSOLE_GROUPS' option in login.defs).
auth optional pam_group.so
# Uncomment and edit /etc/security/time.conf if you need to set time restraint on logins. (Replaces the 'PORTTIME_CHECKS_ENAB'
# option from login.defs as well as /etc/porttime).
# account requisite am_time.so
# Uncomment and edit /etc/security/access.conf if you need to set access limits. (Replaces /etc/login.access file).
# account required pam_access.so
# Sets up user limits according to /etc/security/limits.conf. (Replaces the use of /etc/limits in old login).
session required pam_limits.so
# Prints the status of the user's mailbox upon successful login (Replaces the 'MAIL_CHECK_ENAB' option from login.defs).
# This also defines the MAIL environment variable. However, userdel also needs MAIL_DIR and MAIL_FILE variables in
# /etc/login.defs to make sure that removing a user also removes the user's mail spool file. See comments in /etc/login.defs.
session optional pam_mail.so standard
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x account and session
@include common-account
@include common-session
@include common-password
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf
EOF
do_log "info" "file_only" "4520() Written: [/etc/pam.d/login]."
cat << 'EOF' >| "${var_target}/etc/ciss/login_totp.prompt"
After your UNIX password, please enter your 6-digit TOTP code.
EOF
chmod 0444 "${var_target}/etc/ciss/login_totp.prompt"
do_log "info" "file_only" "4520() Written: [/etc/ciss/login_totp.prompt]."
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f write_pam_login
#######################################
# Writes CISS Header for '/etc/pam.d/sshd'.
# Globals:
# None
# Arguments:
# 1: TARGET
# Returns:
# 0: on success
#######################################
write_pam_sshd() {
### Declare Arrays, HashMaps, and Variables.
declare -r var_target="$1"
mv "${var_target}/etc/pam.d/sshd" "${var_target}/root/.ciss/cdi/backup/etc/pam.d/sshd"
insert_header "${var_target}/etc/pam.d/sshd"
insert_comments "${var_target}/etc/pam.d/sshd"
cat << EOF >> "${var_target}/etc/pam.d/sshd"
#
# PAM configuration for the Secure Shell service
#
# ===== CISS 2FA block =====
# If user is NOT listed -> succeed and SKIP next two lines (silent Keyboard-Interactive (KI) success).
auth [success=2 default=ignore] pam_listfile.so item=user sense=deny file=/etc/ciss/2fa onerr=ignore
# For listed users: enforce that the secret file exists, else deny without prompting.
# pam_google_authenticator will itself fail if the file is absent; we add a clear hint before it.
auth required pam_echo.so file=/etc/ciss/ssh_totp.prompt
auth required pam_google_authenticator.so
# No 'nullok' here: listed users MUST have a secret; missing -> hard fail.
# For non-2FA users KI must be a silent success to satisfy AuthenticationMethods.
auth sufficient pam_permit.so
# ===== CISS 2FA block end =====
# Keep the rest as shipped by Debian. It will be short-circuited by pam_permit for KI and never reached for 2FA users after
# successful GA.
# Standard Un*x authentication.
@include common-auth
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex access limits that are hard to express in sshd_config.
# account required pam_access.so
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any lingering context has been cleared. Without this it is
# possible that a module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login. This includes a dynamically generated part from /run/motd.dynamic and a
# static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and /etc/security/pam_env.conf.
session required pam_env.so # [1]
# In Debian 4.0 (etch), locale-related environment variables were moved to /etc/default/locale, so read that as well.
session required pam_env.so envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts in the proper default security context. Only
# sessions which are intended to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=conf
EOF
do_log "info" "file_only" "4520() Written: [/etc/pam.d/sshd]."
cat << 'EOF' >| "${var_target}/etc/ciss/ssh_totp.prompt"
Please enter your 6-digit TOTP code for %u@%H.
EOF
chmod 0444 "${var_target}/etc/ciss/ssh_totp.prompt"
do_log "info" "file_only" "4520() Written: [/etc/ciss/ssh_totp.prompt]."
return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f write_pam_sshd
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh