From 19c76f0a3f6049b849aa1e002729fdcaba336e1d Mon Sep 17 00:00:00 2001 From: librelad Date: Sat, 23 May 2026 16:11:31 +0100 Subject: [PATCH] feat(backup): CLI + data plumbing for per-location SSH keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the existing location_ssh.sh key store through the backup CLI: 'backup location ssh-key-set|ssh-key-generate|ssh-key-public|ssh-key-delete ' (the WebUI runs these as tasks). The locations generator now emits ssh_key_exists + ssh_public_key (public key only — the private key never leaves the per-location ssh.key file), so the editor can show the key state. Also fix the stale SSH_AUTH label (~/.ssh/id_rsa -> managed per-location key). Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- configs/backup/locations/1/location.config | 2 +- scripts/backup/locations/location_add.sh | 2 +- .../cli/commands/backup/cli_backup_commands.sh | 16 ++++++++++++++++ .../generators/backup/webui_backup_locations.sh | 13 +++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/configs/backup/locations/1/location.config b/configs/backup/locations/1/location.config index 5f7107a..d14b9c2 100644 --- a/configs/backup/locations/1/location.config +++ b/configs/backup/locations/1/location.config @@ -12,7 +12,7 @@ CFG_BACKUP_LOC_1_SSH_USER= # SSH User - For CFG_BACKUP_LOC_1_SSH_HOST= # SSH Host - For sftp type CFG_BACKUP_LOC_1_SSH_PORT=22 # SSH Port - For sftp type **ADVANCED** CFG_BACKUP_LOC_1_SSH_PATH= # SSH Remote Path - Path on the remote host where the repo lives -CFG_BACKUP_LOC_1_SSH_AUTH=key # SSH Authentication - [key:SSH key (~/.ssh/id_rsa)|password:Password (via sshpass)] +CFG_BACKUP_LOC_1_SSH_AUTH=key # SSH Authentication - [key:SSH key (managed by LibrePortal)|password:Password (via sshpass)] CFG_BACKUP_LOC_1_SSH_PASS= # SSH Password - Used only when SSH Authentication is set to Password CFG_BACKUP_LOC_1_S3_ACCESS_KEY= # S3 Access Key - For s3 type CFG_BACKUP_LOC_1_S3_SECRET_KEY= # S3 Secret Key - For s3 type diff --git a/scripts/backup/locations/location_add.sh b/scripts/backup/locations/location_add.sh index 49deb71..a85ee47 100644 --- a/scripts/backup/locations/location_add.sh +++ b/scripts/backup/locations/location_add.sh @@ -42,7 +42,7 @@ locationAdd() echo "CFG_BACKUP_LOC_${idx}_SSH_HOST= # SSH Host - For sftp type" echo "CFG_BACKUP_LOC_${idx}_SSH_PORT=22 # SSH Port - For sftp type **ADVANCED**" echo "CFG_BACKUP_LOC_${idx}_SSH_PATH= # SSH Remote Path - Path on the remote host where the repo lives" - echo "CFG_BACKUP_LOC_${idx}_SSH_AUTH=key # SSH Authentication - [key:SSH key (~/.ssh/id_rsa)|password:Password (via sshpass)]" + echo "CFG_BACKUP_LOC_${idx}_SSH_AUTH=key # SSH Authentication - [key:SSH key (managed by LibrePortal)|password:Password (via sshpass)]" echo "CFG_BACKUP_LOC_${idx}_SSH_PASS= # SSH Password - Used only when SSH Authentication is set to Password" echo "CFG_BACKUP_LOC_${idx}_S3_ACCESS_KEY=" echo "CFG_BACKUP_LOC_${idx}_S3_SECRET_KEY=" diff --git a/scripts/cli/commands/backup/cli_backup_commands.sh b/scripts/cli/commands/backup/cli_backup_commands.sh index c93f5e2..20fb4eb 100755 --- a/scripts/cli/commands/backup/cli_backup_commands.sh +++ b/scripts/cli/commands/backup/cli_backup_commands.sh @@ -85,6 +85,22 @@ cliHandleBackupCommands() [[ -z "$name" ]] && name="1" engineLocationStats "$name" ;; + ssh-key-set) + [[ -z "$name" || -z "$extra" ]] && { isNotice "Usage: backup location ssh-key-set "; return; } + backupSshKeySet "$name" "$extra" + ;; + ssh-key-generate) + [[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-generate "; return; } + backupSshKeyGenerate "$name" + ;; + ssh-key-public) + [[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-public "; return; } + backupSshKeyPublic "$name" + ;; + ssh-key-delete) + [[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-delete "; return; } + backupSshKeyDelete "$name" + ;; *) isNotice "Invalid location action: $action"; cliShowBackupHelp ;; esac ;; diff --git a/scripts/webui/data/generators/backup/webui_backup_locations.sh b/scripts/webui/data/generators/backup/webui_backup_locations.sh index 4ad969c..d3dcb0c 100644 --- a/scripts/webui/data/generators/backup/webui_backup_locations.sh +++ b/scripts/webui/data/generators/backup/webui_backup_locations.sh @@ -52,6 +52,17 @@ webuiGenerateBackupLocations() fi fi + # SSH key state for sftp key auth. Only the PUBLIC key is exposed (safe + # to share — it's what goes in the remote's authorized_keys); the + # private key never leaves the location's ssh.key file. + local ssh_key_exists="false" ssh_public_key="" + if declare -f backupSshKeyExists >/dev/null 2>&1 && backupSshKeyExists "$idx"; then + ssh_key_exists="true" + ssh_public_key=$(backupSshKeyPublic "$idx" 2>/dev/null) + fi + local ssh_pub_esc + ssh_pub_esc=$(printf '%s' "$ssh_public_key" | sed 's/\\/\\\\/g; s/"/\\"/g') + local name_esc uri_esc name_esc=$(printf '%s' "$name" | sed 's/\\/\\\\/g; s/"/\\"/g') uri_esc=$(printf '%s' "$uri" | sed 's/\\/\\\\/g; s/"/\\"/g') @@ -77,6 +88,8 @@ webuiGenerateBackupLocations() content+="\"ssh_port\":\"$(resticLocationField "$idx" SSH_PORT)\"," content+="\"ssh_path\":\"$(printf '%s' "$(resticLocationField "$idx" SSH_PATH)" | sed 's/\\/\\\\/g; s/"/\\"/g')\"," content+="\"ssh_auth\":\"$(resticLocationField "$idx" SSH_AUTH)\"," + content+="\"ssh_key_exists\":$ssh_key_exists," + content+="\"ssh_public_key\":\"$ssh_pub_esc\"," content+="\"keep_last\":\"$(resticLocationField "$idx" KEEP_LAST)\"," content+="\"keep_daily\":\"$(resticLocationField "$idx" KEEP_DAILY)\"," content+="\"keep_weekly\":\"$(resticLocationField "$idx" KEEP_WEEKLY)\","