#!/usr/bin/env bash set -Eeuo pipefail IFS=$'\n\t' umask 027 STEP="STEP034_2156_DVM_SUITE_PORTAL_GENERATOR_SSH_SERVER_AUTH_INSTALL_PATCH_VERIFIED_20260730" EXPECTED_HOST="${DVM_STEP_EXPECTED_HOST:-ax-accessi}" EXPECTED_IP="${DVM_STEP_EXPECTED_IP:-192.168.215.6}" EXPECTED_CLIENT_IP="${DVM_STEP_EXPECTED_CLIENT_IP:-192.168.215.5}" REMOTE_USER="${DVM_STEP_REMOTE_USER:-dvm-portal-generator}" REMOTE_HOME="${DVM_STEP_REMOTE_HOME:-/var/lib/dvm-portal-generator}" REMOTE_SHELL="${DVM_STEP_REMOTE_SHELL:-/bin/bash}" WORKER="${DVM_STEP_WORKER:-/usr/local/sbin/dvm-presenze-portal-generator}" DISPATCHER="${DVM_STEP_DISPATCHER:-/usr/local/sbin/dvm-presenze-portal-ssh-dispatch}" SUDOERS_FILE="${DVM_STEP_SUDOERS_FILE:-/etc/sudoers.d/dvm-presenze-portal-generator}" AUTHORIZED_KEYS="${DVM_STEP_AUTHORIZED_KEYS:-${REMOTE_HOME}/.ssh/authorized_keys}" LOCK_FILE="${DVM_STEP_LOCK_FILE:-/var/lock/dvm-presenze-portal-generator-ssh-server.lock}" SSHD_BIN="${DVM_STEP_SSHD_BIN:-/usr/sbin/sshd}" SSH_KEYGEN_BIN="${DVM_STEP_SSH_KEYGEN_BIN:-/usr/bin/ssh-keygen}" RUNUSER_BIN="${DVM_STEP_RUNUSER_BIN:-/usr/sbin/runuser}" VISUDO_BIN="${DVM_STEP_VISUDO_BIN:-/usr/sbin/visudo}" TEST_MODE="${DVM_STEP_TEST_MODE:-0}" PUBLIC_KEY='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFNsNsMB84fN0tIySyKgtXXgdwq+q6STQdOBYZdD1RMb dvm-suite-portal-generator@ax-presenze' EXPECTED_FINGERPRINT='SHA256:zz+SPo4s3AB2H/7w7LNXdhLCQP1t604i4u3VUAyYTL0' KEY_OPTIONS="from=\"${EXPECTED_CLIENT_IP}\",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding,command=\"${DISPATCHER}\"" AUTHORIZED_LINE="${KEY_OPTIONS} ${PUBLIC_KEY}" START_DIR="$PWD" STAMP="$(date +%Y%m%d_%H%M%S)" OUTPUT_ZIP="${START_DIR}/${STEP}_${STAMP}.zip" PARTIAL_ZIP="${OUTPUT_ZIP}.partial" WORKDIR="" REPORT_DIR="" FINISHED=0 USER_CREATED=0 DISPATCHER_INSTALLED=0 SUDOERS_INSTALLED=0 AUTHORIZED_INSTALLED=0 HAD_DISPATCHER=0 HAD_SUDOERS=0 HAD_AUTHORIZED=0 HAD_SSH_DIR=0 HAD_HOME=0 HOME_META="" SSH_DIR_META="" fail() { echo "ERRORE: $*" >&2 exit 1 } restore_meta() { local path="$1" meta="$2" [[ -n "$meta" && -e "$path" ]] || return 0 local uid gid mode IFS=: read -r uid gid mode <<< "$meta" chown "$uid:$gid" "$path" 2>/dev/null || true chmod "$mode" "$path" 2>/dev/null || true } rollback() { local rc="$1" trap - ERR if [[ $USER_CREATED -eq 1 ]]; then userdel -r "$REMOTE_USER" >/dev/null 2>&1 || true else if [[ $AUTHORIZED_INSTALLED -eq 1 ]]; then if [[ $HAD_AUTHORIZED -eq 1 ]]; then cp -a -- "$WORKDIR/authorized_keys.previous" "$AUTHORIZED_KEYS" || true else rm -f -- "$AUTHORIZED_KEYS" || true fi fi if [[ $HAD_SSH_DIR -eq 0 ]]; then rmdir -- "${REMOTE_HOME}/.ssh" >/dev/null 2>&1 || true else restore_meta "${REMOTE_HOME}/.ssh" "$SSH_DIR_META" fi if [[ $HAD_HOME -eq 1 ]]; then restore_meta "$REMOTE_HOME" "$HOME_META" fi fi if [[ $SUDOERS_INSTALLED -eq 1 ]]; then if [[ $HAD_SUDOERS -eq 1 ]]; then cp -a -- "$WORKDIR/sudoers.previous" "$SUDOERS_FILE" || true else rm -f -- "$SUDOERS_FILE" || true fi fi if [[ $DISPATCHER_INSTALLED -eq 1 ]]; then if [[ $HAD_DISPATCHER -eq 1 ]]; then cp -a -- "$WORKDIR/dispatcher.previous" "$DISPATCHER" || true else rm -f -- "$DISPATCHER" || true fi fi } cleanup() { local rc=$? trap - EXIT if [[ $rc -ne 0 && $FINISHED -eq 0 && -n "$WORKDIR" && -d "$WORKDIR" ]]; then rollback "$rc" fi [[ -n "$WORKDIR" && -d "$WORKDIR" ]] && rm -rf -- "$WORKDIR" [[ -e "$PARTIAL_ZIP" ]] && rm -f -- "$PARTIAL_ZIP" exit "$rc" } trap cleanup EXIT trap 'echo "ERRORE linea ${LINENO}: ${BASH_COMMAND}" >&2' ERR require_command() { command -v "$1" >/dev/null 2>&1 || fail "comando richiesto assente: $1" } validate_server() { [[ "$TEST_MODE" == "1" ]] && return 0 local host_now ips_now host_now="$(hostname -s 2>/dev/null || hostname)" ips_now="$(hostname -I 2>/dev/null || true)" [[ "$host_now" == "$EXPECTED_HOST" ]] || fail "server errato: rilevato ${host_now}, atteso ${EXPECTED_HOST}" [[ " $ips_now " == *" $EXPECTED_IP "* ]] || fail "IP atteso ${EXPECTED_IP} non presente: ${ips_now}" } write_dispatcher() { local destination="$1" cat > "$destination" <&2 exit 64 } case "\$ORIGINAL" in inspect) COMMAND='inspect' ;; plan\ *) COMMAND='plan' SLUG="\${ORIGINAL#plan }" ;; create\ *) COMMAND='create' SLUG="\${ORIGINAL#create }" ;; *) reject ;; esac if [[ "\$COMMAND" == 'plan' || "\$COMMAND" == 'create' ]]; then [[ "\$SLUG" =~ ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?\$ ]] || reject exec "\$SUDO_BIN" -n -- "\$WORKER" "\$COMMAND" "\$SLUG" fi exec "\$SUDO_BIN" -n -- "\$WORKER" inspect DISPATCHER_SCRIPT bash -n "$destination" } write_sudoers() { local destination="$1" cat > "$destination" </dev/null } run_remote_user() { "$RUNUSER_BIN" -u "$REMOTE_USER" -- "$@" } [[ ${EUID} -eq 0 ]] || fail "eseguire con sudo/root" validate_server for cmd in useradd userdel getent install stat chown chmod mkdir cp rm rmdir flock zip awk grep sed sha256sum wc sudo mktemp; do require_command "$cmd" done [[ -x "$SSH_KEYGEN_BIN" ]] || fail "ssh-keygen assente: $SSH_KEYGEN_BIN" [[ -x "$VISUDO_BIN" ]] || fail "visudo assente: $VISUDO_BIN" [[ -x "$RUNUSER_BIN" ]] || fail "runuser assente: $RUNUSER_BIN" [[ -x "$SSHD_BIN" ]] || fail "sshd assente: $SSHD_BIN" [[ -x "$WORKER" ]] || fail "worker locale assente o non eseguibile: $WORKER" bash -n "$WORKER" mkdir -p "$(dirname "$LOCK_FILE")" exec 9>"$LOCK_FILE" flock -n 9 || fail "un'altra installazione STEP034 è già in corso" WORKDIR="$(mktemp -d "/tmp/${STEP}.XXXXXX")" REPORT_DIR="${WORKDIR}/report" mkdir -p "$REPORT_DIR" chmod 0750 "$WORKDIR" "$REPORT_DIR" if [[ -e "$DISPATCHER" ]]; then cp -a -- "$DISPATCHER" "$WORKDIR/dispatcher.previous" HAD_DISPATCHER=1 fi if [[ -e "$SUDOERS_FILE" ]]; then cp -a -- "$SUDOERS_FILE" "$WORKDIR/sudoers.previous" HAD_SUDOERS=1 fi if [[ -e "$AUTHORIZED_KEYS" ]]; then cp -a -- "$AUTHORIZED_KEYS" "$WORKDIR/authorized_keys.previous" HAD_AUTHORIZED=1 fi if [[ -d "$REMOTE_HOME" ]]; then HAD_HOME=1 HOME_META="$(stat -c '%u:%g:%a' "$REMOTE_HOME")" fi if [[ -d "${REMOTE_HOME}/.ssh" ]]; then HAD_SSH_DIR=1 SSH_DIR_META="$(stat -c '%u:%g:%a' "${REMOTE_HOME}/.ssh")" fi if getent passwd "$REMOTE_USER" >/dev/null; then USER_ACTION='PRESERVED' IFS=: read -r _ _ REMOTE_UID REMOTE_GID _ ACCOUNT_HOME ACCOUNT_SHELL < <(getent passwd "$REMOTE_USER") [[ "$ACCOUNT_HOME" == "$REMOTE_HOME" ]] || fail "utente esistente con home inattesa: $ACCOUNT_HOME" [[ "$ACCOUNT_SHELL" == "$REMOTE_SHELL" ]] || fail "utente esistente con shell inattesa: $ACCOUNT_SHELL" [[ -d "$REMOTE_HOME" ]] || fail "utente esistente ma home assente: $REMOTE_HOME" SHADOW_FIELD="$(getent shadow "$REMOTE_USER" | awk -F: '{print $2}')" [[ "$SHADOW_FIELD" == '!'* || "$SHADOW_FIELD" == '*'* ]] || fail "account ${REMOTE_USER} non risulta bloccato da password" else [[ ! -e "$REMOTE_HOME" ]] || fail "home già esistente senza account dedicato: $REMOTE_HOME" useradd --system --user-group --create-home --home-dir "$REMOTE_HOME" --shell "$REMOTE_SHELL" --comment 'DVM portal generator SSH account' "$REMOTE_USER" USER_CREATED=1 USER_ACTION='CREATED' IFS=: read -r _ _ REMOTE_UID REMOTE_GID _ ACCOUNT_HOME ACCOUNT_SHELL < <(getent passwd "$REMOTE_USER") fi REMOTE_GROUP="$(getent group "$REMOTE_GID" | awk -F: '{print $1}')" [[ -n "$REMOTE_GROUP" ]] || fail "gruppo primario non trovato per ${REMOTE_USER}" install -d -o "$REMOTE_USER" -g "$REMOTE_GROUP" -m 0750 "$REMOTE_HOME" install -d -o "$REMOTE_USER" -g "$REMOTE_GROUP" -m 0700 "${REMOTE_HOME}/.ssh" AUTH_TMP="$WORKDIR/authorized_keys.new" printf '%s\n' "$AUTHORIZED_LINE" > "$AUTH_TMP" install -o "$REMOTE_USER" -g "$REMOTE_GROUP" -m 0600 "$AUTH_TMP" "$AUTHORIZED_KEYS" AUTHORIZED_INSTALLED=1 KEY_FINGERPRINT="$("$SSH_KEYGEN_BIN" -lf "$AUTHORIZED_KEYS" | awk 'NR==1 {print $2}')" [[ "$KEY_FINGERPRINT" == "$EXPECTED_FINGERPRINT" ]] || fail "fingerprint chiave inatteso: ${KEY_FINGERPRINT}" [[ "$(wc -l < "$AUTHORIZED_KEYS")" -eq 1 ]] || fail "authorized_keys deve contenere una sola riga gestita" grep -Fq "from=\"${EXPECTED_CLIENT_IP}\"" "$AUTHORIZED_KEYS" || fail "vincolo IP sorgente assente" grep -Fq "command=\"${DISPATCHER}\"" "$AUTHORIZED_KEYS" || fail "forced-command assente" grep -Fq 'no-port-forwarding' "$AUTHORIZED_KEYS" || fail "blocco port forwarding assente" grep -Fq 'no-pty' "$AUTHORIZED_KEYS" || fail "blocco PTY assente" DISPATCHER_TMP="$WORKDIR/dispatcher.new" write_dispatcher "$DISPATCHER_TMP" install -o root -g root -m 0755 "$DISPATCHER_TMP" "$DISPATCHER" DISPATCHER_INSTALLED=1 bash -n "$DISPATCHER" SUDOERS_TMP="$WORKDIR/sudoers.new" write_sudoers "$SUDOERS_TMP" install -o root -g root -m 0440 "$SUDOERS_TMP" "$SUDOERS_FILE" SUDOERS_INSTALLED=1 "$VISUDO_BIN" -cf "$SUDOERS_FILE" >/dev/null "$VISUDO_BIN" -c >/dev/null "$SSHD_BIN" -t "$SSHD_BIN" -T -C "user=${REMOTE_USER},host=${EXPECTED_HOST},addr=${EXPECTED_CLIENT_IP}" > "$REPORT_DIR/06_SSHD_EFFECTIVE_FULL.txt" grep -E '^(pubkeyauthentication|authorizedkeysfile|passwordauthentication|kbdinteractiveauthentication|permittty|allowtcpforwarding|x11forwarding|allowusers|denyusers) ' \ "$REPORT_DIR/06_SSHD_EFFECTIVE_FULL.txt" > "$REPORT_DIR/06_SSHD_EFFECTIVE.txt" || true grep -Eq '^pubkeyauthentication yes$' "$REPORT_DIR/06_SSHD_EFFECTIVE_FULL.txt" || fail "PubkeyAuthentication non attiva" grep -Eq '^authorizedkeysfile .*\.ssh/authorized_keys' "$REPORT_DIR/06_SSHD_EFFECTIVE_FULL.txt" || fail "AuthorizedKeysFile non include .ssh/authorized_keys" run_remote_user env SSH_ORIGINAL_COMMAND=inspect "$DISPATCHER" > "$REPORT_DIR/04_LOCAL_INSPECT_TEST.txt" grep -Eq '^status=OK$' "$REPORT_DIR/04_LOCAL_INSPECT_TEST.txt" || fail "test dispatcher inspect non valido" run_remote_user env SSH_ORIGINAL_COMMAND='plan generator-verifica' "$DISPATCHER" > "$REPORT_DIR/05_LOCAL_PLAN_TEST.txt" grep -Eq '^status=READY$' "$REPORT_DIR/05_LOCAL_PLAN_TEST.txt" || fail "test dispatcher plan non valido" : > "$REPORT_DIR/07_DENIED_TESTS.txt" for denied in '' 'bash' 'inspect extra' 'plan bad slug' 'create bad/slug' 'create valido extra'; do if run_remote_user env SSH_ORIGINAL_COMMAND="$denied" "$DISPATCHER" > "$WORKDIR/denied.stdout" 2> "$WORKDIR/denied.stderr"; then fail "comando vietato accettato: ${denied:-}" else denied_rc=$? fi printf 'command=%q exit=%s result=REJECTED\n' "$denied" "$denied_rc" >> "$REPORT_DIR/07_DENIED_TESTS.txt" done sudo -l -U "$REMOTE_USER" > "$REPORT_DIR/08_SUDO_RULES.txt" { echo "STEP: $STEP" echo "Server: $(hostname -s 2>/dev/null || hostname)" echo "IP: $(hostname -I 2>/dev/null || true)" echo "Account: $REMOTE_USER" echo "Account action: $USER_ACTION" echo "Home: $REMOTE_HOME" echo "Client IP consentito: $EXPECTED_CLIENT_IP" echo "Worker: $WORKER" echo "Dispatcher: $DISPATCHER" echo "Sudoers: $SUDOERS_FILE" echo "Authorized keys: $AUTHORIZED_KEYS" } > "$REPORT_DIR/01_INSTALLATION.txt" { getent passwd "$REMOTE_USER" getent shadow "$REMOTE_USER" | awk -F: '{print $1 "::" $3 ":" $4 ":" $5 ":" $6 ":" $7 ":" $8 ":" $9}' stat -c 'home=%U:%G %a %n' "$REMOTE_HOME" stat -c 'ssh_dir=%U:%G %a %n' "${REMOTE_HOME}/.ssh" stat -c 'authorized_keys=%U:%G %a %n' "$AUTHORIZED_KEYS" stat -c 'dispatcher=%U:%G %a %n' "$DISPATCHER" stat -c 'sudoers=%U:%G %a %n' "$SUDOERS_FILE" } > "$REPORT_DIR/02_ACCOUNT_AND_PERMISSIONS.txt" { echo "fingerprint=$KEY_FINGERPRINT" echo "source_restriction=$EXPECTED_CLIENT_IP" echo "forced_command=$DISPATCHER" echo "agent_forwarding=DENIED" echo "port_forwarding=DENIED" echo "pty=DENIED" echo "user_rc=DENIED" echo "x11_forwarding=DENIED" echo "public_key=$PUBLIC_KEY" } > "$REPORT_DIR/03_AUTHORIZATION.txt" cp -- "$DISPATCHER" "$REPORT_DIR/dvm-presenze-portal-ssh-dispatch.txt" cp -- "$SUDOERS_FILE" "$REPORT_DIR/dvm-presenze-portal-generator.sudoers.txt" { echo "Verifica sintassi STEP034: OK" echo "Verifica sintassi dispatcher: OK" echo "Verifica sudoers completa: OK" echo "Verifica configurazione sshd: OK" echo "Account dedicato e password bloccata: OK" echo "Chiave STEP033 autorizzata esclusivamente da ${EXPECTED_CLIENT_IP}: OK" echo "Forced-command senza PTY/forwarding: OK" echo "Test locale inspect come ${REMOTE_USER}: OK" echo "Test locale plan come ${REMOTE_USER}: OK" echo "Comandi non consentiti respinti: OK" echo "Comando create eseguito: NO" echo "Portali creati: 0" echo "Apache modificato/ricaricato: NO" echo "DVM-SUITE modificato: NO" echo "Prossimo passo: test SSH reale dal 215.5 tramite /usr/local/bin/dvm-presenze-portal-remote." } > "$REPORT_DIR/99_SUMMARY.txt" rm -f -- "$PARTIAL_ZIP" "$OUTPUT_ZIP" ( cd "$REPORT_DIR" zip -q -9 "$PARTIAL_ZIP" \ 01_INSTALLATION.txt \ 02_ACCOUNT_AND_PERMISSIONS.txt \ 03_AUTHORIZATION.txt \ 04_LOCAL_INSPECT_TEST.txt \ 05_LOCAL_PLAN_TEST.txt \ 06_SSHD_EFFECTIVE.txt \ 06_SSHD_EFFECTIVE_FULL.txt \ 07_DENIED_TESTS.txt \ 08_SUDO_RULES.txt \ 99_SUMMARY.txt \ dvm-presenze-portal-ssh-dispatch.txt \ dvm-presenze-portal-generator.sudoers.txt ) zip -T "$PARTIAL_ZIP" >/dev/null mv -- "$PARTIAL_ZIP" "$OUTPUT_ZIP" FINISHED=1 printf '%s\n' "$OUTPUT_ZIP"