mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-07-26 18:38:30 +00:00
update 1.2.2.2 beta
This commit is contained in:
Binary file not shown.
@@ -1 +1 @@
|
|||||||
4d158239777b3d544c613c42a8fcdc005ab201e9f69072975443192e4b54824a ProxMenux-1.2.2.2-beta.AppImage
|
aa99bbf26b42528f72807a8c9d116fd79d4dc20f8df64a993c7efc0c0d805143
|
||||||
|
|||||||
@@ -1989,11 +1989,6 @@ function CreateJobDialog({
|
|||||||
// Backend-specific fields
|
// Backend-specific fields
|
||||||
const [pbsRepository, setPbsRepository] = useState<string>("")
|
const [pbsRepository, setPbsRepository] = useState<string>("")
|
||||||
const [pbsBackupId, setPbsBackupId] = useState<string>("")
|
const [pbsBackupId, setPbsBackupId] = useState<string>("")
|
||||||
// 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<boolean>(false)
|
|
||||||
// Client-side PBS encryption — sent as `pbs_encrypt` in the payload.
|
// Client-side PBS encryption — sent as `pbs_encrypt` in the payload.
|
||||||
// Backend resolves the shared keyfile + injects PBS_KEYFILE into
|
// Backend resolves the shared keyfile + injects PBS_KEYFILE into
|
||||||
// the job .env so the runner adds `--keyfile` to the backup call.
|
// the job .env so the runner adds `--keyfile` to the backup call.
|
||||||
@@ -2187,7 +2182,6 @@ function CreateJobDialog({
|
|||||||
setPbsRecoveryPass("")
|
setPbsRecoveryPass("")
|
||||||
setPbsRecoveryPass2("")
|
setPbsRecoveryPass2("")
|
||||||
setPbsRecoveryChange(false)
|
setPbsRecoveryChange(false)
|
||||||
setPbsAcceptedNoRecovery(false)
|
|
||||||
setLocalDestDir("")
|
setLocalDestDir("")
|
||||||
setBorgRepoSelected("")
|
setBorgRepoSelected("")
|
||||||
setBorgPassphrase("")
|
setBorgPassphrase("")
|
||||||
@@ -2274,11 +2268,15 @@ function CreateJobDialog({
|
|||||||
? true
|
? true
|
||||||
: /* borg */ !!borgRepoSelected
|
: /* 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) ||
|
const pbsRecoveryOk = !(backend === "pbs" && pbsEncrypt) ||
|
||||||
(pbsRecoveryStatus?.has_recovery && !pbsRecoveryChange) ||
|
(pbsRecoveryStatus?.has_recovery && !pbsRecoveryChange) ||
|
||||||
(pbsRecoveryPass !== "" && pbsRecoveryPass === pbsRecoveryPass2) ||
|
(pbsRecoveryPass !== "" && pbsRecoveryPass === pbsRecoveryPass2)
|
||||||
pbsAcceptedNoRecovery
|
|
||||||
|
|
||||||
const canSubmit =
|
const canSubmit =
|
||||||
canAdvanceFrom1 && canAdvanceFrom2 && canAdvanceFrom3 && canAdvanceFrom4 && backendValid && pbsRecoveryOk
|
canAdvanceFrom1 && canAdvanceFrom2 && canAdvanceFrom3 && canAdvanceFrom4 && backendValid && pbsRecoveryOk
|
||||||
@@ -2974,24 +2972,6 @@ function CreateJobDialog({
|
|||||||
<p className="text-[11px] text-red-400 mt-1">Passphrases don't match.</p>
|
<p className="text-[11px] text-red-400 mt-1">Passphrases don't match.</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!pbsRecoveryPass && !pbsRecoveryPass2 && (
|
|
||||||
<div className="mt-2 rounded-md border border-red-500/40 bg-red-500/10 p-2.5 space-y-1.5">
|
|
||||||
<div className="flex items-start gap-1.5 text-[11px] text-red-300">
|
|
||||||
<AlertTriangle className="h-3.5 w-3.5 shrink-0 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<span className="font-medium">Without a recovery passphrase, the keyfile lives ONLY on this host.</span> If this host is lost or reinstalled, every encrypted backup becomes unrecoverable.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Label className="flex items-center gap-2 text-[11px] cursor-pointer">
|
|
||||||
<Checkbox
|
|
||||||
id="pbsSchedAcceptNoRec"
|
|
||||||
checked={pbsAcceptedNoRecovery}
|
|
||||||
onCheckedChange={(v) => setPbsAcceptedNoRecovery(!!v)}
|
|
||||||
/>
|
|
||||||
<span>I understand the risk and want to encrypt without recovery.</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{pbsRecoveryStatus?.has_recovery && pbsRecoveryChange && (
|
{pbsRecoveryStatus?.has_recovery && pbsRecoveryChange && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -3342,11 +3322,6 @@ function ManualBackupDialog({
|
|||||||
// passphrase if one is already saved — only show the inputs when
|
// passphrase if one is already saved — only show the inputs when
|
||||||
// there's no escrow yet OR the operator explicitly asks to change.
|
// there's no escrow yet OR the operator explicitly asks to change.
|
||||||
const [pbsRecoveryChange, setPbsRecoveryChange] = useState<boolean>(false)
|
const [pbsRecoveryChange, setPbsRecoveryChange] = useState<boolean>(false)
|
||||||
// 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".
|
|
||||||
const [pbsAcceptedNoRecovery, setPbsAcceptedNoRecovery] = useState<boolean>(false)
|
|
||||||
// Whether the host already has an escrow blob configured. When
|
// Whether the host already has an escrow blob configured. When
|
||||||
// present, the passphrase input becomes optional ("leave blank to
|
// present, the passphrase input becomes optional ("leave blank to
|
||||||
// keep saved"). Refreshed after a successful setup call.
|
// keep saved"). Refreshed after a successful setup call.
|
||||||
@@ -3406,7 +3381,6 @@ function ManualBackupDialog({
|
|||||||
setPbsRecoveryPass("")
|
setPbsRecoveryPass("")
|
||||||
setPbsRecoveryPass2("")
|
setPbsRecoveryPass2("")
|
||||||
setPbsRecoveryChange(false)
|
setPbsRecoveryChange(false)
|
||||||
setPbsAcceptedNoRecovery(false)
|
|
||||||
setLocalDestDir("")
|
setLocalDestDir("")
|
||||||
setBorgRepoSelected("")
|
setBorgRepoSelected("")
|
||||||
setBorgPassphrase("")
|
setBorgPassphrase("")
|
||||||
@@ -3444,15 +3418,16 @@ function ManualBackupDialog({
|
|||||||
// Borg destination carries its own passphrase + encryption.
|
// Borg destination carries its own passphrase + encryption.
|
||||||
// No need to gate on local state — the wizard no longer asks.
|
// No need to gate on local state — the wizard no longer asks.
|
||||||
: !!borgRepoSelected
|
: !!borgRepoSelected
|
||||||
// Recovery gate for PBS encryption. When "Encrypt" is ticked, either
|
// Recovery gate for PBS encryption. Choosing "Encrypt" makes the
|
||||||
// a matching passphrase pair OR an explicit accept-risk checkbox is
|
// recovery passphrase mandatory — same as the CLI. The "Run backup"
|
||||||
// required — otherwise submit stays blocked. Mirrors the shell 3-way
|
// button stays disabled until a matching passphrase pair is entered
|
||||||
// gate so the operator can never silently create an encrypted backup
|
// (or an already-saved escrow is reused). No accept-risk escape:
|
||||||
// whose keyfile only lives on this host.
|
// 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) ||
|
const pbsRecoveryOk = !(backend === "pbs" && pbsEncrypt) ||
|
||||||
(pbsRecoveryStatus?.has_recovery && !pbsRecoveryChange) ||
|
(pbsRecoveryStatus?.has_recovery && !pbsRecoveryChange) ||
|
||||||
(pbsRecoveryPass !== "" && pbsRecoveryPass === pbsRecoveryPass2) ||
|
(pbsRecoveryPass !== "" && pbsRecoveryPass === pbsRecoveryPass2)
|
||||||
pbsAcceptedNoRecovery
|
|
||||||
const canSubmit = canAdvanceFrom1 && backendValid && pbsRecoveryOk
|
const canSubmit = canAdvanceFrom1 && backendValid && pbsRecoveryOk
|
||||||
|
|
||||||
async function handleRun() {
|
async function handleRun() {
|
||||||
@@ -3768,24 +3743,6 @@ function ManualBackupDialog({
|
|||||||
Cancel change — keep the saved passphrase
|
Cancel change — keep the saved passphrase
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{!pbsRecoveryPass && !pbsRecoveryPass2 && (
|
|
||||||
<div className="mt-2 rounded-md border border-red-500/40 bg-red-500/10 p-2.5 space-y-1.5">
|
|
||||||
<div className="flex items-start gap-1.5 text-[11px] text-red-300">
|
|
||||||
<AlertTriangle className="h-3.5 w-3.5 shrink-0 mt-0.5" />
|
|
||||||
<div>
|
|
||||||
<span className="font-medium">Without a recovery passphrase, the keyfile lives ONLY on this host.</span> If this host is lost or reinstalled, every encrypted backup becomes unrecoverable.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Label className="flex items-center gap-2 text-[11px] cursor-pointer">
|
|
||||||
<Checkbox
|
|
||||||
id="pbsManualAcceptNoRec"
|
|
||||||
checked={pbsAcceptedNoRecovery}
|
|
||||||
onCheckedChange={(v) => setPbsAcceptedNoRecovery(!!v)}
|
|
||||||
/>
|
|
||||||
<span>I understand the risk and want to encrypt without recovery.</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ _bk_pbs() {
|
|||||||
local backup_id epoch log_file staging_root t_start elapsed staged_size
|
local backup_id epoch log_file staging_root t_start elapsed staged_size
|
||||||
|
|
||||||
hb_select_pbs_repository || return 1
|
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
|
hb_select_profile_paths "$profile_mode" paths || return 1
|
||||||
|
|
||||||
|
|||||||
@@ -396,7 +396,10 @@ _create_job() {
|
|||||||
;;
|
;;
|
||||||
pbs)
|
pbs)
|
||||||
hb_select_pbs_repository || return 1
|
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
|
local bid
|
||||||
bid="hostcfg-$(hostname)"
|
bid="hostcfg-$(hostname)"
|
||||||
bid=$(dialog --backtitle "ProxMenux" --title "PBS" \
|
bid=$(dialog --backtitle "ProxMenux" --title "PBS" \
|
||||||
|
|||||||
@@ -900,10 +900,10 @@ hb_pbs_setup_recovery() {
|
|||||||
local key_file="$HB_STATE_DIR/pbs-key.conf"
|
local key_file="$HB_STATE_DIR/pbs-key.conf"
|
||||||
local recovery_enc="$HB_STATE_DIR/pbs-key.recovery.enc"
|
local recovery_enc="$HB_STATE_DIR/pbs-key.recovery.enc"
|
||||||
|
|
||||||
dialog --backtitle "ProxMenux" --title "$(hb_translate "Keyfile recovery setup")" \
|
# Choosing "encrypt" already implies wanting to protect the keyfile.
|
||||||
--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.")" \
|
# No second yes/no — go straight to the passphrase input. Cancel at
|
||||||
17 80 || return 1
|
# 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
|
if ! command -v openssl >/dev/null 2>&1; then
|
||||||
dialog --backtitle "ProxMenux" --title "$(hb_translate "Recovery setup failed")" \
|
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
|
--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"
|
msg_ok "$(hb_translate "Encryption key created:") $key_file"
|
||||||
HB_PBS_KEYFILE_OPT="--keyfile $key_file"
|
HB_PBS_KEYFILE_OPT="--keyfile $key_file"
|
||||||
|
|
||||||
# Set up automatic PBS-based recovery for the keyfile.
|
# Recovery passphrase is REQUIRED when the operator chose
|
||||||
# Cancelling the passphrase used to fall through with
|
# to encrypt. Encrypting without a recovery escrow lets the
|
||||||
# `|| true` and leave the backup encrypted with a keyfile
|
# keyfile live only on this host — one reinstall or disk
|
||||||
# that only lived on this host — one reinstall away from
|
# failure away from every encrypted backup being lost. If
|
||||||
# being unrecoverable. Now we force an explicit choice:
|
# the operator cancels the passphrase dialog: wipe the
|
||||||
# set the passphrase, accept the risk, or cancel encryption.
|
# freshly-created keyfile (avoid orphans in state-dir),
|
||||||
while ! hb_pbs_setup_recovery; do
|
# unset the --keyfile flag, and return 1 so the caller can
|
||||||
local _rec_choice
|
# bail out and drop the operator back at the previous menu.
|
||||||
_rec_choice=$(dialog --backtitle "ProxMenux" \
|
if ! hb_pbs_setup_recovery; then
|
||||||
--title "$(hb_translate "Recovery passphrase required")" \
|
rm -f "$key_file" 2>/dev/null
|
||||||
--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?")" \
|
HB_PBS_KEYFILE_OPT=""
|
||||||
20 82 3 \
|
msg_warn "$(hb_translate "Encryption cancelled — a recovery passphrase is required to encrypt. Returning to the previous menu.")"
|
||||||
"1" "$(hb_translate "Set a recovery passphrase (recommended)")" \
|
return 1
|
||||||
"2" "$(hb_translate "Continue WITHOUT recovery — I accept the risk")" \
|
fi
|
||||||
"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
|
|
||||||
else
|
else
|
||||||
# Surface the actual error from proxmox-backup-client to the
|
# Surface the actual error from proxmox-backup-client to the
|
||||||
# operator — silent failures here were the reason the user
|
# operator — silent failures here were the reason the user
|
||||||
|
|||||||
Reference in New Issue
Block a user