From fd1aeb1eadb67475f64aa58c211db5979427e863 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Sun, 5 Jul 2026 23:38:12 +0200 Subject: [PATCH] Update run_scheduled_backup.sh --- .../backup_restore/run_scheduled_backup.sh | 90 ++++++++++++++++++- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/scripts/backup_restore/run_scheduled_backup.sh b/scripts/backup_restore/run_scheduled_backup.sh index 7888fa02..a7b68a19 100755 --- a/scripts/backup_restore/run_scheduled_backup.sh +++ b/scripts/backup_restore/run_scheduled_backup.sh @@ -275,6 +275,70 @@ _sb_pbs_resolve_password() { cat "$pw_file" } +# Return the certificate fingerprint of the PBS storage entry whose +# `@:` matches $1. Mirrors the walker in +# `_sb_pbs_resolve_password` but reads the `fingerprint ` line +# instead. Needed because attached scheduled jobs are created without +# PBS_FINGERPRINT in their .env (the wizard inherits it from +# `/etc/pve/storage.cfg`), and without a fingerprint the client falls +# back to an interactive `(y/n)` prompt at connect time — which never +# gets an answer under systemd, hanging the whole job. Empty result +# is a valid outcome (PBS behind a CA-signed cert doesn't need one); +# a missing storage entry returns non-zero so the caller can log it. +_sb_pbs_resolve_fingerprint() { + local repo="$1" + local cfg=/etc/pve/storage.cfg + [[ -f "$cfg" && -n "$repo" ]] || return 1 + + local current_type="" current_name="" server="" datastore="" username="" + local current_fp="" + local found_fp="" found_any=0 + + local line key val + while IFS= read -r line; do + if [[ "$line" =~ ^([a-z]+):[[:space:]]+(.+)$ ]]; then + if [[ "$current_type" == "pbs" && -n "$current_name" && -n "$server" && -n "$datastore" ]]; then + local u="${username:-root@pam}" + local built="${u}@${server}:${datastore}" + if [[ "$built" == "$repo" ]]; then + found_fp="$current_fp" + found_any=1 + break + fi + fi + current_type="${BASH_REMATCH[1]}" + current_name="${BASH_REMATCH[2]}" + server=""; datastore=""; username=""; current_fp="" + continue + fi + if [[ "$line" =~ ^[[:space:]]+([a-zA-Z_]+)[[:space:]]+(.+)$ ]]; then + key="${BASH_REMATCH[1]}" + val="${BASH_REMATCH[2]}" + case "$key" in + server) server="$val" ;; + datastore) datastore="$val" ;; + username) username="$val" ;; + fingerprint) current_fp="$val" ;; + esac + fi + done < "$cfg" + # Final block may not have triggered the start-of-next-block check. + if (( ! found_any )); then + if [[ "$current_type" == "pbs" && -n "$current_name" && -n "$server" && -n "$datastore" ]]; then + local u="${username:-root@pam}" + local built="${u}@${server}:${datastore}" + if [[ "$built" == "$repo" ]]; then + found_fp="$current_fp" + found_any=1 + fi + fi + fi + + (( found_any )) || return 1 + # Empty fingerprint is a legit outcome for CA-signed PBS setups. + printf '%s' "$found_fp" +} + _sb_run_pbs() { local stage_root="$1" local backup_id="$2" @@ -301,15 +365,33 @@ _sb_run_pbs() { PBS_PASSWORD=$(_sb_pbs_resolve_password "$PBS_REPOSITORY" 2>/dev/null || true) fi + # Same treatment for PBS_FINGERPRINT. Attached scheduled jobs are + # created without one (the wizard delegates trust to PVE's storage + # config), and running proxmox-backup-client with an empty fingerprint + # against a self-signed PBS drops into an interactive `(y/n)` prompt — + # which under systemd never gets an answer and hangs the timer. + # Resolving on-demand from storage.cfg matches the runtime behavior + # PVE itself has for its own vzdump jobs. + if [[ -z "${PBS_FINGERPRINT:-}" && -n "${PBS_REPOSITORY:-}" ]]; then + PBS_FINGERPRINT=$(_sb_pbs_resolve_fingerprint "$PBS_REPOSITORY" 2>/dev/null || true) + fi + [[ -z "${PBS_REPOSITORY:-}" || -z "${PBS_PASSWORD:-}" ]] && return 1 if [[ -n "${PBS_KEYFILE:-}" ]]; then cmd+=(--keyfile "$PBS_KEYFILE") fi + # `&1 || return 1 + "${cmd[@]}" &1 || return 1 # Best effort prune for PBS group. env PBS_PASSWORD="$PBS_PASSWORD" \ @@ -321,7 +403,7 @@ _sb_run_pbs() { ${KEEP_WEEKLY:+--keep-weekly "$KEEP_WEEKLY"} \ ${KEEP_MONTHLY:+--keep-monthly "$KEEP_MONTHLY"} \ ${KEEP_YEARLY:+--keep-yearly "$KEEP_YEARLY"} \ - 2>&1 || true + &1 || true # Upload the keyfile recovery blob alongside the main backup so the # operator can rebuild the keyfile on a fresh host. Only fires when: @@ -344,7 +426,7 @@ _sb_run_pbs() { --backup-type host \ --backup-id "$keyrec_id" \ --backup-time "$epoch" \ - 2>&1 || true + &1 || true # Prune the paired keyrecovery group with the SAME retention # values as the main backup. Without this the keyrecovery snapshots @@ -361,7 +443,7 @@ _sb_run_pbs() { ${KEEP_WEEKLY:+--keep-weekly "$KEEP_WEEKLY"} \ ${KEEP_MONTHLY:+--keep-monthly "$KEEP_MONTHLY"} \ ${KEEP_YEARLY:+--keep-yearly "$KEEP_YEARLY"} \ - 2>&1 || true + &1 || true fi echo "PBS_SNAPSHOT=host/${backup_id}/${epoch}"