149 lines
3.7 KiB
Bash
Executable File
149 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
restore-from-s3.sh latest
|
|
restore-from-s3.sh <s3-object-key>
|
|
|
|
Examples:
|
|
restore-from-s3.sh latest
|
|
restore-from-s3.sh qlockify/qlockify-backup-YYYYMMDD-HHMMSS.tar.gz.enc
|
|
|
|
Environment:
|
|
DEPLOY_ROOT Deployment directory. Defaults to ~/qlockify-deployment.
|
|
S3_BACKUP_BUCKET S3 bucket name.
|
|
S3_BACKUP_PREFIX S3 object prefix. Defaults to qlockify.
|
|
S3_BACKUP_ENDPOINT_URL S3-compatible endpoint URL.
|
|
S3_BACKUP_ACCESS_KEY_ID S3 access key.
|
|
S3_BACKUP_SECRET_ACCESS_KEY S3 secret key.
|
|
BACKUP_ENCRYPTION_PASSPHRASE Passphrase used to decrypt backup archives.
|
|
|
|
Existing RESTORE_SKIP_DB, RESTORE_SKIP_MEDIA, and RESTORE_SKIP_ENV flags are
|
|
passed through to restore.sh.
|
|
EOF
|
|
}
|
|
|
|
log() {
|
|
printf '[restore-rclone] %s\n' "$*"
|
|
}
|
|
|
|
fail() {
|
|
printf '[restore-rclone] %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_var() {
|
|
local name="$1"
|
|
[[ -n "${!name:-}" ]] || fail "$name is required"
|
|
}
|
|
|
|
load_env() {
|
|
local env_path="$1"
|
|
[[ -f "$env_path" ]] || fail "Deployment env file not found: $env_path"
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
. "$env_path"
|
|
set +a
|
|
}
|
|
|
|
normalize_prefix() {
|
|
local value="${1:-qlockify}"
|
|
value="${value#/}"
|
|
value="${value%/}"
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
rclone_remote_path() {
|
|
printf 'parspack:%s/%s' "$S3_BACKUP_BUCKET" "$S3_BACKUP_PREFIX"
|
|
}
|
|
|
|
latest_object_name() {
|
|
rclone lsf "$REMOTE_PATH" \
|
|
--config "$RCLONE_CONFIG" \
|
|
--s3-no-check-bucket \
|
|
--files-only \
|
|
| grep '\.tar\.gz\.enc$' \
|
|
| sort \
|
|
| tail -n 1 \
|
|
|| true
|
|
}
|
|
|
|
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
|
|
REQUESTED_KEY="${1:-}"
|
|
[[ -n "$REQUESTED_KEY" ]] || {
|
|
usage
|
|
exit 1
|
|
}
|
|
|
|
command -v rclone >/dev/null 2>&1 || fail "rclone is required"
|
|
command -v openssl >/dev/null 2>&1 || fail "openssl is required"
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DEPLOY_ROOT="${DEPLOY_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
DEPLOY_ENV="$DEPLOY_ROOT/.env"
|
|
RESTORE_SCRIPT="$SCRIPT_DIR/restore.sh"
|
|
|
|
[[ -x "$RESTORE_SCRIPT" ]] || fail "Restore script is not executable: $RESTORE_SCRIPT"
|
|
load_env "$DEPLOY_ENV"
|
|
|
|
S3_BACKUP_PREFIX="$(normalize_prefix "${S3_BACKUP_PREFIX:-qlockify}")"
|
|
|
|
require_var S3_BACKUP_BUCKET
|
|
require_var S3_BACKUP_ENDPOINT_URL
|
|
require_var S3_BACKUP_ACCESS_KEY_ID
|
|
require_var S3_BACKUP_SECRET_ACCESS_KEY
|
|
require_var BACKUP_ENCRYPTION_PASSPHRASE
|
|
|
|
WORK_DIR="$(mktemp -d)"
|
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
|
|
|
RCLONE_CONFIG="$WORK_DIR/rclone.conf"
|
|
|
|
cat > "$RCLONE_CONFIG" <<EOF
|
|
[parspack]
|
|
type = s3
|
|
provider = Other
|
|
access_key_id = $S3_BACKUP_ACCESS_KEY_ID
|
|
secret_access_key = $S3_BACKUP_SECRET_ACCESS_KEY
|
|
endpoint = $S3_BACKUP_ENDPOINT_URL
|
|
acl = private
|
|
force_path_style = true
|
|
EOF
|
|
|
|
REMOTE_PATH="$(rclone_remote_path)"
|
|
|
|
if [[ "$REQUESTED_KEY" == "latest" ]]; then
|
|
log "Resolving latest encrypted backup object"
|
|
OBJECT_NAME="$(latest_object_name)"
|
|
[[ -n "$OBJECT_NAME" ]] || fail "No encrypted backups found in $REMOTE_PATH"
|
|
else
|
|
OBJECT_NAME="${REQUESTED_KEY##*/}"
|
|
fi
|
|
|
|
ENCRYPTED_PATH="$WORK_DIR/$OBJECT_NAME"
|
|
DECRYPTED_PATH="$WORK_DIR/${ENCRYPTED_PATH##*/}"
|
|
DECRYPTED_PATH="${DECRYPTED_PATH%.enc}"
|
|
|
|
log "Downloading encrypted backup from $REMOTE_PATH/$OBJECT_NAME"
|
|
rclone copyto "$REMOTE_PATH/$OBJECT_NAME" "$ENCRYPTED_PATH" \
|
|
--config "$RCLONE_CONFIG" \
|
|
--s3-no-check-bucket \
|
|
--progress
|
|
|
|
log "Decrypting backup archive"
|
|
openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \
|
|
-in "$ENCRYPTED_PATH" \
|
|
-out "$DECRYPTED_PATH" \
|
|
-pass env:BACKUP_ENCRYPTION_PASSPHRASE
|
|
|
|
log "Restoring decrypted backup archive"
|
|
DEPLOY_ROOT="$DEPLOY_ROOT" "$RESTORE_SCRIPT" "$DECRYPTED_PATH"
|
|
|
|
log "S3 restore completed from: $REMOTE_PATH/$OBJECT_NAME"
|