diff --git a/AppImage/ProxMenux-1.2.2.2-beta.AppImage b/AppImage/ProxMenux-1.2.2.2-beta.AppImage index e79afd9d..f3aeae8e 100755 Binary files a/AppImage/ProxMenux-1.2.2.2-beta.AppImage and b/AppImage/ProxMenux-1.2.2.2-beta.AppImage differ diff --git a/AppImage/ProxMenux-Monitor.AppImage.sha256 b/AppImage/ProxMenux-Monitor.AppImage.sha256 index 327b2627..14527d24 100644 --- a/AppImage/ProxMenux-Monitor.AppImage.sha256 +++ b/AppImage/ProxMenux-Monitor.AppImage.sha256 @@ -1 +1 @@ -4d158239777b3d544c613c42a8fcdc005ab201e9f69072975443192e4b54824a ProxMenux-1.2.2.2-beta.AppImage +aa99bbf26b42528f72807a8c9d116fd79d4dc20f8df64a993c7efc0c0d805143 diff --git a/AppImage/components/host-backup.tsx b/AppImage/components/host-backup.tsx index dba4bf77..f1856b09 100644 --- a/AppImage/components/host-backup.tsx +++ b/AppImage/components/host-backup.tsx @@ -1989,11 +1989,6 @@ function CreateJobDialog({ // Backend-specific fields const [pbsRepository, setPbsRepository] = useState("") const [pbsBackupId, setPbsBackupId] = useState("") - // Explicit acknowledgement to proceed with encryption but WITHOUT a - // recovery passphrase. Mirrors the shell's 3-way gate: without this - // flag (or a filled passphrase pair), submit is blocked when the - // operator ticks "Encrypt". Same rationale as ManualBackup below. - const [pbsAcceptedNoRecovery, setPbsAcceptedNoRecovery] = useState(false) // Client-side PBS encryption — sent as `pbs_encrypt` in the payload. // Backend resolves the shared keyfile + injects PBS_KEYFILE into // the job .env so the runner adds `--keyfile` to the backup call. @@ -2187,7 +2182,6 @@ function CreateJobDialog({ setPbsRecoveryPass("") setPbsRecoveryPass2("") setPbsRecoveryChange(false) - setPbsAcceptedNoRecovery(false) setLocalDestDir("") setBorgRepoSelected("") setBorgPassphrase("") @@ -2274,11 +2268,15 @@ function CreateJobDialog({ ? true : /* borg */ !!borgRepoSelected - // PBS encryption 3-way gate — see ManualBackup for full rationale. + // PBS encryption gate — choosing "Encrypt" makes the recovery + // passphrase mandatory. The button stays disabled until a matching + // passphrase pair is entered (or an already-saved escrow is reused). + // There is no opt-out: encrypting without recovery would leave the + // keyfile only on this host, and a reinstall would render every + // encrypted backup unrecoverable. const pbsRecoveryOk = !(backend === "pbs" && pbsEncrypt) || (pbsRecoveryStatus?.has_recovery && !pbsRecoveryChange) || - (pbsRecoveryPass !== "" && pbsRecoveryPass === pbsRecoveryPass2) || - pbsAcceptedNoRecovery + (pbsRecoveryPass !== "" && pbsRecoveryPass === pbsRecoveryPass2) const canSubmit = canAdvanceFrom1 && canAdvanceFrom2 && canAdvanceFrom3 && canAdvanceFrom4 && backendValid && pbsRecoveryOk @@ -2974,24 +2972,6 @@ function CreateJobDialog({

Passphrases don't match.

)} - {!pbsRecoveryPass && !pbsRecoveryPass2 && ( -
-
- -
- Without a recovery passphrase, the keyfile lives ONLY on this host. If this host is lost or reinstalled, every encrypted backup becomes unrecoverable. -
-
- -
- )} {pbsRecoveryStatus?.has_recovery && pbsRecoveryChange && ( )} - {!pbsRecoveryPass && !pbsRecoveryPass2 && ( -
-
- -
- Without a recovery passphrase, the keyfile lives ONLY on this host. If this host is lost or reinstalled, every encrypted backup becomes unrecoverable. -
-
- -
- )} )} diff --git a/scripts/backup_restore/backup_host.sh b/scripts/backup_restore/backup_host.sh index 7cdeb78f..8261ec34 100755 --- a/scripts/backup_restore/backup_host.sh +++ b/scripts/backup_restore/backup_host.sh @@ -65,7 +65,10 @@ _bk_pbs() { local backup_id epoch log_file staging_root t_start elapsed staged_size hb_select_pbs_repository || return 1 - hb_ask_pbs_encryption + # Return 1 propagates operator cancel from the encryption / passphrase + # dialogs so the outer menu shows the source picker again instead of + # proceeding with a half-configured backup. + hb_ask_pbs_encryption || return 1 hb_select_profile_paths "$profile_mode" paths || return 1 diff --git a/scripts/backup_restore/backup_scheduler.sh b/scripts/backup_restore/backup_scheduler.sh index c4c5ceb4..d1b29096 100755 --- a/scripts/backup_restore/backup_scheduler.sh +++ b/scripts/backup_restore/backup_scheduler.sh @@ -396,7 +396,10 @@ _create_job() { ;; pbs) hb_select_pbs_repository || return 1 - hb_ask_pbs_encryption + # Propagate the operator cancel from the encryption dialog so the + # wizard drops back to the previous step instead of saving a job + # spec with a half-configured encryption block. + hb_ask_pbs_encryption || return 1 local bid bid="hostcfg-$(hostname)" bid=$(dialog --backtitle "ProxMenux" --title "PBS" \ diff --git a/scripts/backup_restore/lib_host_backup_common.sh b/scripts/backup_restore/lib_host_backup_common.sh index ae6a91f8..ad7cfcc9 100755 --- a/scripts/backup_restore/lib_host_backup_common.sh +++ b/scripts/backup_restore/lib_host_backup_common.sh @@ -900,10 +900,10 @@ hb_pbs_setup_recovery() { local key_file="$HB_STATE_DIR/pbs-key.conf" local recovery_enc="$HB_STATE_DIR/pbs-key.recovery.enc" - dialog --backtitle "ProxMenux" --title "$(hb_translate "Keyfile recovery setup")" \ - --yesno "$(hb_translate "Set a recovery passphrase for this keyfile? (Strongly recommended)")"$'\n\n'"$(hb_translate "With a recovery passphrase, an encrypted copy of the keyfile is uploaded to PBS with every backup. If you lose this host, you can recover the keyfile on a fresh install using only the passphrase.")"$'\n\n'"$(hb_translate "Without a recovery passphrase, losing the keyfile means the encrypted backups become unrecoverable forever.")" \ - 17 80 || return 1 - + # Choosing "encrypt" already implies wanting to protect the keyfile. + # No second yes/no — go straight to the passphrase input. Cancel at + # any passphrase prompt propagates up as return 1 so the caller can + # drop the operator back to the previous menu. if ! command -v openssl >/dev/null 2>&1; then dialog --backtitle "ProxMenux" --title "$(hb_translate "Recovery setup failed")" \ --msgbox "$(hb_translate "openssl is not installed — cannot create recovery copy. Install openssl and retry.")" 9 70 @@ -1131,34 +1131,20 @@ hb_ask_pbs_encryption() { msg_ok "$(hb_translate "Encryption key created:") $key_file" HB_PBS_KEYFILE_OPT="--keyfile $key_file" - # Set up automatic PBS-based recovery for the keyfile. - # Cancelling the passphrase used to fall through with - # `|| true` and leave the backup encrypted with a keyfile - # that only lived on this host — one reinstall away from - # being unrecoverable. Now we force an explicit choice: - # set the passphrase, accept the risk, or cancel encryption. - while ! hb_pbs_setup_recovery; do - local _rec_choice - _rec_choice=$(dialog --backtitle "ProxMenux" \ - --title "$(hb_translate "Recovery passphrase required")" \ - --menu "$(hb_translate "You chose to encrypt this backup. A recovery passphrase protects the keyfile: an encrypted copy is uploaded to PBS with every backup, and the restore flow can retrieve it if this host is ever lost.")"$'\n\n'"$(hb_translate "Without a recovery passphrase, the keyfile lives ONLY on this host. Losing it (fresh install, disk failure, ...) makes every encrypted backup UNRECOVERABLE.")"$'\n\n'"$(hb_translate "What do you want to do?")" \ - 20 82 3 \ - "1" "$(hb_translate "Set a recovery passphrase (recommended)")" \ - "2" "$(hb_translate "Continue WITHOUT recovery — I accept the risk")" \ - "3" "$(hb_translate "Cancel encryption — backup will not be encrypted")" \ - 3>&1 1>&2 2>&3) - # ESC / empty → treat as "retry passphrase" (safest default) - [[ -z "$_rec_choice" ]] && _rec_choice="1" - case "$_rec_choice" in - 1) continue ;; - 2) msg_warn "$(hb_translate "Encryption without recovery — the keyfile lives only on this host. Export it manually to a safe location.")" - break ;; - 3) rm -f "$key_file" 2>/dev/null - HB_PBS_KEYFILE_OPT="" - msg_info "$(hb_translate "Encryption cancelled — backup will proceed unencrypted.")" - break ;; - esac - done + # Recovery passphrase is REQUIRED when the operator chose + # to encrypt. Encrypting without a recovery escrow lets the + # keyfile live only on this host — one reinstall or disk + # failure away from every encrypted backup being lost. If + # the operator cancels the passphrase dialog: wipe the + # freshly-created keyfile (avoid orphans in state-dir), + # unset the --keyfile flag, and return 1 so the caller can + # bail out and drop the operator back at the previous menu. + if ! hb_pbs_setup_recovery; then + rm -f "$key_file" 2>/dev/null + HB_PBS_KEYFILE_OPT="" + msg_warn "$(hb_translate "Encryption cancelled — a recovery passphrase is required to encrypt. Returning to the previous menu.")" + return 1 + fi else # Surface the actual error from proxmox-backup-client to the # operator — silent failures here were the reason the user