feat(backup): CLI + data plumbing for per-location SSH keys

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 <idx>'
(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 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-23 16:11:31 +01:00
parent 520595bc67
commit 19c76f0a3f
4 changed files with 31 additions and 2 deletions

View File

@ -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_HOST= # SSH Host - For sftp type
CFG_BACKUP_LOC_1_SSH_PORT=22 # SSH Port - For sftp type **ADVANCED** 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_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_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_ACCESS_KEY= # S3 Access Key - For s3 type
CFG_BACKUP_LOC_1_S3_SECRET_KEY= # S3 Secret Key - For s3 type CFG_BACKUP_LOC_1_S3_SECRET_KEY= # S3 Secret Key - For s3 type

View File

@ -42,7 +42,7 @@ locationAdd()
echo "CFG_BACKUP_LOC_${idx}_SSH_HOST= # SSH Host - For sftp type" 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_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_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}_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_ACCESS_KEY="
echo "CFG_BACKUP_LOC_${idx}_S3_SECRET_KEY=" echo "CFG_BACKUP_LOC_${idx}_S3_SECRET_KEY="

View File

@ -85,6 +85,22 @@ cliHandleBackupCommands()
[[ -z "$name" ]] && name="1" [[ -z "$name" ]] && name="1"
engineLocationStats "$name" engineLocationStats "$name"
;; ;;
ssh-key-set)
[[ -z "$name" || -z "$extra" ]] && { isNotice "Usage: backup location ssh-key-set <idx> <base64-key>"; return; }
backupSshKeySet "$name" "$extra"
;;
ssh-key-generate)
[[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-generate <idx>"; return; }
backupSshKeyGenerate "$name"
;;
ssh-key-public)
[[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-public <idx>"; return; }
backupSshKeyPublic "$name"
;;
ssh-key-delete)
[[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-delete <idx>"; return; }
backupSshKeyDelete "$name"
;;
*) isNotice "Invalid location action: $action"; cliShowBackupHelp ;; *) isNotice "Invalid location action: $action"; cliShowBackupHelp ;;
esac esac
;; ;;

View File

@ -52,6 +52,17 @@ webuiGenerateBackupLocations()
fi fi
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 local name_esc uri_esc
name_esc=$(printf '%s' "$name" | sed 's/\\/\\\\/g; s/"/\\"/g') name_esc=$(printf '%s' "$name" | sed 's/\\/\\\\/g; s/"/\\"/g')
uri_esc=$(printf '%s' "$uri" | 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_port\":\"$(resticLocationField "$idx" SSH_PORT)\","
content+="\"ssh_path\":\"$(printf '%s' "$(resticLocationField "$idx" SSH_PATH)" | sed 's/\\/\\\\/g; s/"/\\"/g')\"," content+="\"ssh_path\":\"$(printf '%s' "$(resticLocationField "$idx" SSH_PATH)" | sed 's/\\/\\\\/g; s/"/\\"/g')\","
content+="\"ssh_auth\":\"$(resticLocationField "$idx" SSH_AUTH)\"," 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_last\":\"$(resticLocationField "$idx" KEEP_LAST)\","
content+="\"keep_daily\":\"$(resticLocationField "$idx" KEEP_DAILY)\"," content+="\"keep_daily\":\"$(resticLocationField "$idx" KEEP_DAILY)\","
content+="\"keep_weekly\":\"$(resticLocationField "$idx" KEEP_WEEKLY)\"," content+="\"keep_weekly\":\"$(resticLocationField "$idx" KEEP_WEEKLY)\","