Fix: Roll back failed SSH hardening

This commit is contained in:
Gabriel Brown
2026-08-27 03:44:57 -04:00
parent 20b41c3976
commit d267da58ad
2 changed files with 363 additions and 18 deletions
+123 -12
View File
@@ -68,23 +68,131 @@ safe_root_authorized_keys() {
grep -qEv '^[[:space:]]*(#|$)' "$keys"
}
harden_server_ssh() {
local username="$1" user_home="$2" sshd_dir sshd_dropin harden
sshd_dir="$(system_path /etc/ssh/sshd_config.d)" || return 1
sshd_dropin="$sshd_dir/90-panama.conf"
detect_ssh_unit() {
local unit
for unit in sshd.service ssh.service; do
systemctl cat "$unit" >/dev/null 2>&1 && {
printf '%s\n' "$unit"
return 0
}
done
return 1
}
if [[ -f "$sshd_dropin" ]]; then
echo "sshd is already hardened ($sshd_dropin)"
return 0
restore_ssh_dropin() {
local restore
if [[ -n "${ssh_backup:-}" && -e "$ssh_backup" ]]; then
restore="$(mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.restore)" || return 1
if ! cp -a -- "$ssh_backup" "$restore"; then
rm -f -- "$restore"
return 1
fi
if ! mv -f -- "$restore" "$ssh_dropin"; then
rm -f -- "$restore"
return 1
fi
else
rm -f -- "$ssh_dropin"
fi
}
restore_ssh_transaction_traps() {
trap - EXIT INT TERM
[[ -n "${ssh_saved_exit_trap:-}" ]] && eval "$ssh_saved_exit_trap"
[[ -n "${ssh_saved_int_trap:-}" ]] && eval "$ssh_saved_int_trap"
[[ -n "${ssh_saved_term_trap:-}" ]] && eval "$ssh_saved_term_trap"
return 0
}
harden_server_ssh() {
local username="$1" user_home="$2" sshd_dir ssh_dropin harden ssh_unit
local ssh_candidate="" ssh_backup="" rollback_failed=0
local ssh_transaction_active=0
local ssh_saved_exit_trap ssh_saved_int_trap ssh_saved_term_trap
sshd_dir="$(system_path /etc/ssh/sshd_config.d)" || return 1
ssh_dropin="$sshd_dir/90-panama.conf"
printf 'Harden sshd (disable root login and password auth)? [Y/n]: '
read -r harden </dev/tty || harden=""
if [[ ! "$harden" =~ ^[Nn] ]]; then
printf 'PermitRootLogin no\nPasswordAuthentication no\n' >"$sshd_dropin"
systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null || true
echo "Wrote $sshd_dropin; make sure your key works before logging out."
if [[ "$harden" =~ ^[Nn] ]]; then
return 0
fi
if ! ssh_unit="$(detect_ssh_unit)"; then
echo "SSH hardening failed: neither sshd.service nor ssh.service exists" >&2
return 1
fi
ssh_candidate="$(umask 077; mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.tmp)" || return 1
if ! printf 'PermitRootLogin no\nPasswordAuthentication no\n' >"$ssh_candidate"; then
rm -f -- "$ssh_candidate"
return 1
fi
if [[ -e "$ssh_dropin" ]]; then
ssh_backup="$(umask 077; mktemp --tmpdir="$sshd_dir" .90-panama.XXXXXX.backup)" || {
rm -f -- "$ssh_candidate"
return 1
}
if ! cat -- "$ssh_dropin" >"$ssh_backup"; then
rm -f -- "$ssh_candidate" "$ssh_backup"
return 1
fi
fi
ssh_saved_exit_trap="$(trap -p EXIT)"
ssh_saved_int_trap="$(trap -p INT)"
ssh_saved_term_trap="$(trap -p TERM)"
trap 'if [[ "${ssh_transaction_active:-0}" == 1 ]]; then restore_ssh_dropin || true; fi' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
ssh_transaction_active=1
if ! mv -f -- "$ssh_candidate" "$ssh_dropin"; then
ssh_transaction_active=0
restore_ssh_transaction_traps
rm -f -- "$ssh_candidate" "$ssh_backup"
return 1
fi
ssh_candidate=""
if ! sshd -t; then
restore_ssh_dropin || rollback_failed=1
sshd -t || rollback_failed=1
ssh_transaction_active=0
restore_ssh_transaction_traps
if (( rollback_failed )); then
printf 'SSH rollback needs manual recovery. Backup: %s\n' "$ssh_backup" >&2
printf ' cp -a -- %q %q\n' "$ssh_backup" "$ssh_dropin" >&2
printf ' sshd -t\n' >&2
printf ' systemctl reload %s\n' "$ssh_unit" >&2
else
[[ -z "$ssh_backup" ]] || rm -f -- "$ssh_backup"
fi
return 1
fi
if ! systemctl reload "$ssh_unit"; then
restore_ssh_dropin || rollback_failed=1
sshd -t || rollback_failed=1
systemctl reload "$ssh_unit" || rollback_failed=1
ssh_transaction_active=0
restore_ssh_transaction_traps
if (( rollback_failed )); then
printf 'SSH rollback needs manual recovery. Backup: %s\n' "$ssh_backup" >&2
printf ' cp -a -- %q %q\n' "$ssh_backup" "$ssh_dropin" >&2
printf ' sshd -t\n' >&2
printf ' systemctl reload %s\n' "$ssh_unit" >&2
else
[[ -z "$ssh_backup" ]] || rm -f -- "$ssh_backup"
fi
return 1
fi
ssh_transaction_active=0
restore_ssh_transaction_traps
[[ -z "$ssh_backup" ]] || rm -f -- "$ssh_backup"
echo "Wrote $ssh_dropin; make sure your key works before logging out."
}
# Panama assumes Fedora's repositories and package names.
@@ -175,7 +283,10 @@ if [[ "$(id -u)" -eq 0 ]]; then
fi
if safe_authorized_keys "$username" "$user_home"; then
harden_server_ssh "$username" "$user_home"
if ! harden_server_ssh "$username" "$user_home"; then
echo "SSH hardening failed; stopping before install handoff." >&2
exit 1
fi
else
echo "SSH hardening unavailable: $username has no safe authorized_keys" >&2
fi