fix(scripts): migrate to rclone from aws-cli and add cleanup actions
This commit is contained in:
@@ -4,9 +4,9 @@ POSTGRES_PASSWORD=postgres
|
|||||||
|
|
||||||
S3_BACKUP_BUCKET=
|
S3_BACKUP_BUCKET=
|
||||||
S3_BACKUP_PREFIX=qlockify
|
S3_BACKUP_PREFIX=qlockify
|
||||||
S3_BACKUP_REGION=us-east-1
|
|
||||||
S3_BACKUP_ENDPOINT_URL=
|
S3_BACKUP_ENDPOINT_URL=
|
||||||
S3_BACKUP_ACCESS_KEY_ID=
|
S3_BACKUP_ACCESS_KEY_ID=
|
||||||
S3_BACKUP_SECRET_ACCESS_KEY=
|
S3_BACKUP_SECRET_ACCESS_KEY=
|
||||||
BACKUP_ENCRYPTION_PASSPHRASE=
|
BACKUP_ENCRYPTION_PASSPHRASE=
|
||||||
BACKUP_LOCAL_KEEP_LATEST=1
|
BACKUP_LOCAL_KEEP_LATEST=3
|
||||||
|
BACKUP_REMOTE_KEEP_LATEST=7
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -267,7 +267,7 @@ Install backup upload prerequisites:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y awscli openssl
|
sudo apt install -y rclone openssl
|
||||||
```
|
```
|
||||||
|
|
||||||
Configure encrypted S3-compatible backup uploads in `./.env`:
|
Configure encrypted S3-compatible backup uploads in `./.env`:
|
||||||
@@ -275,21 +275,23 @@ Configure encrypted S3-compatible backup uploads in `./.env`:
|
|||||||
```bash
|
```bash
|
||||||
S3_BACKUP_BUCKET=qlockify-backups
|
S3_BACKUP_BUCKET=qlockify-backups
|
||||||
S3_BACKUP_PREFIX=qlockify
|
S3_BACKUP_PREFIX=qlockify
|
||||||
S3_BACKUP_REGION=us-east-1
|
|
||||||
S3_BACKUP_ENDPOINT_URL=https://c284984.parspack.net
|
S3_BACKUP_ENDPOINT_URL=https://c284984.parspack.net
|
||||||
S3_BACKUP_ACCESS_KEY_ID=
|
S3_BACKUP_ACCESS_KEY_ID=
|
||||||
S3_BACKUP_SECRET_ACCESS_KEY=
|
S3_BACKUP_SECRET_ACCESS_KEY=
|
||||||
BACKUP_ENCRYPTION_PASSPHRASE=
|
BACKUP_ENCRYPTION_PASSPHRASE=
|
||||||
BACKUP_LOCAL_KEEP_LATEST=1
|
BACKUP_LOCAL_KEEP_LATEST=3
|
||||||
|
BACKUP_REMOTE_KEEP_LATEST=7
|
||||||
```
|
```
|
||||||
|
|
||||||
Upload an encrypted backup to S3:
|
Upload an encrypted backup to S3-compatible object storage with rclone:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod +x ./scripts/backup-upload-s3.sh
|
chmod +x ./scripts/backup-upload-s3.sh
|
||||||
./scripts/backup-upload-s3.sh
|
./scripts/backup-upload-s3.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
After a successful upload, the remote storage keeps only the latest `BACKUP_REMOTE_KEEP_LATEST` encrypted `.tar.gz.enc` files. The server keeps only the latest `BACKUP_LOCAL_KEEP_LATEST` plaintext `.tar.gz` files under `./backups/latest/`; encrypted backup files are not kept locally.
|
||||||
|
|
||||||
Restore the latest encrypted backup from S3:
|
Restore the latest encrypted backup from S3:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
161
scripts/backup-upload-s3.sh
Normal file → Executable file
161
scripts/backup-upload-s3.sh
Normal file → Executable file
@@ -8,23 +8,25 @@ Usage:
|
|||||||
|
|
||||||
Environment:
|
Environment:
|
||||||
DEPLOY_ROOT Deployment directory. Defaults to ~/qlockify-deployment.
|
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 endpoint URL.
|
||||||
S3_BACKUP_REGION S3 region. Defaults to us-east-1.
|
|
||||||
S3_BACKUP_ENDPOINT_URL Optional S3-compatible endpoint URL.
|
|
||||||
S3_BACKUP_ACCESS_KEY_ID S3 access key.
|
S3_BACKUP_ACCESS_KEY_ID S3 access key.
|
||||||
S3_BACKUP_SECRET_ACCESS_KEY S3 secret key.
|
S3_BACKUP_SECRET_ACCESS_KEY S3 secret key.
|
||||||
BACKUP_ENCRYPTION_PASSPHRASE Passphrase used to encrypt backup archives.
|
S3_BACKUP_BUCKET Bucket name.
|
||||||
BACKUP_LOCAL_KEEP_LATEST Set to 1 to keep latest encrypted archive locally.
|
S3_BACKUP_PREFIX Object prefix. Defaults to qlockify.
|
||||||
|
|
||||||
|
BACKUP_ENCRYPTION_PASSPHRASE Encryption passphrase.
|
||||||
|
BACKUP_LOCAL_KEEP_LATEST Number of latest local plaintext backups to keep. Defaults to 3.
|
||||||
|
BACKUP_REMOTE_KEEP_LATEST Number of latest remote encrypted backups to keep. Defaults to 7.
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
printf '[backup-s3] %s\n' "$*"
|
printf '[backup-rclone] %s\n' "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
printf '[backup-s3] %s\n' "$*" >&2
|
printf '[backup-rclone] %s\n' "$*" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,26 +37,93 @@ require_var() {
|
|||||||
|
|
||||||
load_env() {
|
load_env() {
|
||||||
local env_path="$1"
|
local env_path="$1"
|
||||||
|
|
||||||
[[ -f "$env_path" ]] || fail "Deployment env file not found: $env_path"
|
[[ -f "$env_path" ]] || fail "Deployment env file not found: $env_path"
|
||||||
|
|
||||||
set -a
|
set -a
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
. "$env_path"
|
. "$env_path"
|
||||||
set +a
|
set +a
|
||||||
}
|
}
|
||||||
|
|
||||||
aws_s3() {
|
normalize_prefix() {
|
||||||
if [[ -n "${S3_BACKUP_ENDPOINT_URL:-}" ]]; then
|
local value="${1:-qlockify}"
|
||||||
aws --endpoint-url "$S3_BACKUP_ENDPOINT_URL" s3 "$@"
|
|
||||||
|
value="${value#/}"
|
||||||
|
value="${value%/}"
|
||||||
|
|
||||||
|
printf '%s' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
positive_integer_or_default() {
|
||||||
|
local value="${1:-}"
|
||||||
|
local fallback="$2"
|
||||||
|
|
||||||
|
if [[ "$value" =~ ^[0-9]+$ ]]; then
|
||||||
|
printf '%s' "$value"
|
||||||
else
|
else
|
||||||
aws s3 "$@"
|
printf '%s' "$fallback"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
normalize_prefix() {
|
rclone_remote_path() {
|
||||||
local value="${1:-qlockify}"
|
printf 'parspack:%s/%s' "$S3_BACKUP_BUCKET" "$S3_BACKUP_PREFIX"
|
||||||
value="${value#/}"
|
}
|
||||||
value="${value%/}"
|
|
||||||
printf '%s' "$value"
|
cleanup_local_backups() {
|
||||||
|
local keep_count="$1"
|
||||||
|
local latest_dir="$2"
|
||||||
|
|
||||||
|
[[ "$keep_count" -gt 0 ]] || {
|
||||||
|
rm -rf "$latest_dir"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "$latest_dir"
|
||||||
|
find "$latest_dir" -maxdepth 1 -type f -name '*.tar.gz.enc' -delete
|
||||||
|
cp "$ARCHIVE_PATH" "$latest_dir/$ARCHIVE_NAME"
|
||||||
|
|
||||||
|
find "$latest_dir" -maxdepth 1 -type f -name '*.tar.gz' -printf '%T@ %p\n' \
|
||||||
|
| sort -nr \
|
||||||
|
| awk -v keep="$keep_count" 'NR > keep {print $2}' \
|
||||||
|
| xargs -r rm -f
|
||||||
|
|
||||||
|
log "Kept latest $keep_count plaintext backup(s) locally in: $latest_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_remote_backups() {
|
||||||
|
local keep_count="$1"
|
||||||
|
local remote_path="$2"
|
||||||
|
local stale_files
|
||||||
|
|
||||||
|
[[ "$keep_count" -gt 0 ]] || {
|
||||||
|
log "Skipping remote cleanup because BACKUP_REMOTE_KEEP_LATEST is $keep_count"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stale_files="$(
|
||||||
|
rclone lsf "$remote_path" \
|
||||||
|
--config "$RCLONE_CONFIG" \
|
||||||
|
--s3-no-check-bucket \
|
||||||
|
--files-only \
|
||||||
|
| grep '\.tar\.gz\.enc$' \
|
||||||
|
| sort -r \
|
||||||
|
| awk -v keep="$keep_count" 'NR > keep' \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "$stale_files" ]]; then
|
||||||
|
log "No remote backups need cleanup"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r stale_file; do
|
||||||
|
[[ -n "$stale_file" ]] || continue
|
||||||
|
log "Deleting old remote backup: $remote_path/$stale_file"
|
||||||
|
rclone deletefile "$remote_path/$stale_file" \
|
||||||
|
--config "$RCLONE_CONFIG" \
|
||||||
|
--s3-no-check-bucket
|
||||||
|
done <<< "$stale_files"
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||||
@@ -62,29 +131,28 @@ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
command -v aws >/dev/null 2>&1 || fail "aws CLI is required"
|
command -v rclone >/dev/null 2>&1 || fail "rclone is required"
|
||||||
command -v openssl >/dev/null 2>&1 || fail "openssl is required"
|
command -v openssl >/dev/null 2>&1 || fail "openssl is required"
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
DEPLOY_ROOT="${DEPLOY_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
DEPLOY_ROOT="${DEPLOY_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
||||||
|
|
||||||
DEPLOY_ENV="$DEPLOY_ROOT/.env"
|
DEPLOY_ENV="$DEPLOY_ROOT/.env"
|
||||||
BACKUP_SCRIPT="$SCRIPT_DIR/backup.sh"
|
BACKUP_SCRIPT="$SCRIPT_DIR/backup.sh"
|
||||||
|
|
||||||
[[ -x "$BACKUP_SCRIPT" ]] || fail "Backup script is not executable: $BACKUP_SCRIPT"
|
[[ -x "$BACKUP_SCRIPT" ]] || fail "Backup script is not executable: $BACKUP_SCRIPT"
|
||||||
|
|
||||||
load_env "$DEPLOY_ENV"
|
load_env "$DEPLOY_ENV"
|
||||||
|
|
||||||
S3_BACKUP_PREFIX="$(normalize_prefix "${S3_BACKUP_PREFIX:-qlockify}")"
|
require_var S3_BACKUP_ENDPOINT_URL
|
||||||
S3_BACKUP_REGION="${S3_BACKUP_REGION:-us-east-1}"
|
|
||||||
|
|
||||||
require_var S3_BACKUP_BUCKET
|
|
||||||
require_var S3_BACKUP_ACCESS_KEY_ID
|
require_var S3_BACKUP_ACCESS_KEY_ID
|
||||||
require_var S3_BACKUP_SECRET_ACCESS_KEY
|
require_var S3_BACKUP_SECRET_ACCESS_KEY
|
||||||
|
require_var S3_BACKUP_BUCKET
|
||||||
require_var BACKUP_ENCRYPTION_PASSPHRASE
|
require_var BACKUP_ENCRYPTION_PASSPHRASE
|
||||||
|
|
||||||
export AWS_ACCESS_KEY_ID="$S3_BACKUP_ACCESS_KEY_ID"
|
S3_BACKUP_PREFIX="$(normalize_prefix "${S3_BACKUP_PREFIX:-qlockify}")"
|
||||||
export AWS_SECRET_ACCESS_KEY="$S3_BACKUP_SECRET_ACCESS_KEY"
|
BACKUP_LOCAL_KEEP_LATEST="$(positive_integer_or_default "${BACKUP_LOCAL_KEEP_LATEST:-3}" 3)"
|
||||||
export AWS_DEFAULT_REGION="$S3_BACKUP_REGION"
|
BACKUP_REMOTE_KEEP_LATEST="$(positive_integer_or_default "${BACKUP_REMOTE_KEEP_LATEST:-7}" 7)"
|
||||||
export AWS_EC2_METADATA_DISABLED=true
|
|
||||||
|
|
||||||
WORK_DIR="$(mktemp -d)"
|
WORK_DIR="$(mktemp -d)"
|
||||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||||
@@ -93,36 +161,53 @@ PLAIN_DIR="$WORK_DIR/plain"
|
|||||||
mkdir -p "$PLAIN_DIR"
|
mkdir -p "$PLAIN_DIR"
|
||||||
|
|
||||||
log "Creating local backup archive"
|
log "Creating local backup archive"
|
||||||
|
|
||||||
DEPLOY_ROOT="$DEPLOY_ROOT" "$BACKUP_SCRIPT" "$PLAIN_DIR"
|
DEPLOY_ROOT="$DEPLOY_ROOT" "$BACKUP_SCRIPT" "$PLAIN_DIR"
|
||||||
|
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
archives=("$PLAIN_DIR"/*.tar.gz)
|
archives=("$PLAIN_DIR"/*.tar.gz)
|
||||||
shopt -u nullglob
|
shopt -u nullglob
|
||||||
|
|
||||||
[[ "${#archives[@]}" -eq 1 ]] || fail "Expected exactly one backup archive, found ${#archives[@]}"
|
[[ "${#archives[@]}" -eq 1 ]] || fail "Expected exactly one backup archive, found ${#archives[@]}"
|
||||||
|
|
||||||
ARCHIVE_PATH="${archives[0]}"
|
ARCHIVE_PATH="${archives[0]}"
|
||||||
ARCHIVE_NAME="$(basename "$ARCHIVE_PATH")"
|
ARCHIVE_NAME="$(basename "$ARCHIVE_PATH")"
|
||||||
|
|
||||||
ENCRYPTED_NAME="$ARCHIVE_NAME.enc"
|
ENCRYPTED_NAME="$ARCHIVE_NAME.enc"
|
||||||
ENCRYPTED_PATH="$WORK_DIR/$ENCRYPTED_NAME"
|
ENCRYPTED_PATH="$WORK_DIR/$ENCRYPTED_NAME"
|
||||||
S3_URI="s3://$S3_BACKUP_BUCKET/$S3_BACKUP_PREFIX/$ENCRYPTED_NAME"
|
|
||||||
|
|
||||||
log "Encrypting backup archive"
|
log "Encrypting backup archive"
|
||||||
|
|
||||||
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 200000 \
|
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 200000 \
|
||||||
-in "$ARCHIVE_PATH" \
|
-in "$ARCHIVE_PATH" \
|
||||||
-out "$ENCRYPTED_PATH" \
|
-out "$ENCRYPTED_PATH" \
|
||||||
-pass env:BACKUP_ENCRYPTION_PASSPHRASE
|
-pass env:BACKUP_ENCRYPTION_PASSPHRASE
|
||||||
|
|
||||||
rm -f "$ARCHIVE_PATH"
|
RCLONE_CONFIG="$WORK_DIR/rclone.conf"
|
||||||
|
|
||||||
log "Uploading encrypted backup to $S3_URI"
|
cat > "$RCLONE_CONFIG" <<EOF
|
||||||
aws_s3 cp "$ENCRYPTED_PATH" "$S3_URI" --only-show-errors
|
[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
|
||||||
|
|
||||||
if [[ "${BACKUP_LOCAL_KEEP_LATEST:-1}" == "1" ]]; then
|
REMOTE_PATH="$(rclone_remote_path)"
|
||||||
LATEST_DIR="$DEPLOY_ROOT/backups/latest"
|
|
||||||
mkdir -p "$LATEST_DIR"
|
|
||||||
find "$LATEST_DIR" -type f -name '*.tar.gz.enc' -delete
|
|
||||||
cp "$ENCRYPTED_PATH" "$LATEST_DIR/$ENCRYPTED_NAME"
|
|
||||||
log "Kept latest encrypted backup locally: $LATEST_DIR/$ENCRYPTED_NAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "S3 backup upload completed: $S3_URI"
|
log "Uploading encrypted backup to $REMOTE_PATH/$ENCRYPTED_NAME"
|
||||||
|
|
||||||
|
rclone copy \
|
||||||
|
"$ENCRYPTED_PATH" \
|
||||||
|
"$REMOTE_PATH" \
|
||||||
|
--config "$RCLONE_CONFIG" \
|
||||||
|
--s3-no-check-bucket \
|
||||||
|
--progress
|
||||||
|
|
||||||
|
cleanup_local_backups "$BACKUP_LOCAL_KEEP_LATEST" "$DEPLOY_ROOT/backups/latest"
|
||||||
|
cleanup_remote_backups "$BACKUP_REMOTE_KEEP_LATEST" "$REMOTE_PATH"
|
||||||
|
|
||||||
|
log "Backup upload completed successfully"
|
||||||
|
|||||||
81
scripts/restore-from-s3.sh
Normal file → Executable file
81
scripts/restore-from-s3.sh
Normal file → Executable file
@@ -15,8 +15,7 @@ Environment:
|
|||||||
DEPLOY_ROOT Deployment directory. Defaults to ~/qlockify-deployment.
|
DEPLOY_ROOT Deployment directory. Defaults to ~/qlockify-deployment.
|
||||||
S3_BACKUP_BUCKET S3 bucket name.
|
S3_BACKUP_BUCKET S3 bucket name.
|
||||||
S3_BACKUP_PREFIX S3 object prefix. Defaults to qlockify.
|
S3_BACKUP_PREFIX S3 object prefix. Defaults to qlockify.
|
||||||
S3_BACKUP_REGION S3 region. Defaults to us-east-1.
|
S3_BACKUP_ENDPOINT_URL S3-compatible endpoint URL.
|
||||||
S3_BACKUP_ENDPOINT_URL Optional S3-compatible endpoint URL.
|
|
||||||
S3_BACKUP_ACCESS_KEY_ID S3 access key.
|
S3_BACKUP_ACCESS_KEY_ID S3 access key.
|
||||||
S3_BACKUP_SECRET_ACCESS_KEY S3 secret key.
|
S3_BACKUP_SECRET_ACCESS_KEY S3 secret key.
|
||||||
BACKUP_ENCRYPTION_PASSPHRASE Passphrase used to decrypt backup archives.
|
BACKUP_ENCRYPTION_PASSPHRASE Passphrase used to decrypt backup archives.
|
||||||
@@ -27,11 +26,11 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
printf '[restore-s3] %s\n' "$*"
|
printf '[restore-rclone] %s\n' "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
printf '[restore-s3] %s\n' "$*" >&2
|
printf '[restore-rclone] %s\n' "$*" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,14 +48,6 @@ load_env() {
|
|||||||
set +a
|
set +a
|
||||||
}
|
}
|
||||||
|
|
||||||
aws_s3() {
|
|
||||||
if [[ -n "${S3_BACKUP_ENDPOINT_URL:-}" ]]; then
|
|
||||||
aws --endpoint-url "$S3_BACKUP_ENDPOINT_URL" s3 "$@"
|
|
||||||
else
|
|
||||||
aws s3 "$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
normalize_prefix() {
|
normalize_prefix() {
|
||||||
local value="${1:-qlockify}"
|
local value="${1:-qlockify}"
|
||||||
value="${value#/}"
|
value="${value#/}"
|
||||||
@@ -64,12 +55,19 @@ normalize_prefix() {
|
|||||||
printf '%s' "$value"
|
printf '%s' "$value"
|
||||||
}
|
}
|
||||||
|
|
||||||
latest_object_key() {
|
rclone_remote_path() {
|
||||||
aws_s3 ls "s3://$S3_BACKUP_BUCKET/$S3_BACKUP_PREFIX/" --recursive \
|
printf 'parspack:%s/%s' "$S3_BACKUP_BUCKET" "$S3_BACKUP_PREFIX"
|
||||||
| awk '{print $4}' \
|
}
|
||||||
|
|
||||||
|
latest_object_name() {
|
||||||
|
rclone lsf "$REMOTE_PATH" \
|
||||||
|
--config "$RCLONE_CONFIG" \
|
||||||
|
--s3-no-check-bucket \
|
||||||
|
--files-only \
|
||||||
| grep '\.tar\.gz\.enc$' \
|
| grep '\.tar\.gz\.enc$' \
|
||||||
| sort \
|
| sort \
|
||||||
| tail -n 1
|
| tail -n 1 \
|
||||||
|
|| true
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||||
@@ -83,7 +81,7 @@ REQUESTED_KEY="${1:-}"
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
command -v aws >/dev/null 2>&1 || fail "aws CLI is required"
|
command -v rclone >/dev/null 2>&1 || fail "rclone is required"
|
||||||
command -v openssl >/dev/null 2>&1 || fail "openssl is required"
|
command -v openssl >/dev/null 2>&1 || fail "openssl is required"
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
@@ -95,35 +93,48 @@ RESTORE_SCRIPT="$SCRIPT_DIR/restore.sh"
|
|||||||
load_env "$DEPLOY_ENV"
|
load_env "$DEPLOY_ENV"
|
||||||
|
|
||||||
S3_BACKUP_PREFIX="$(normalize_prefix "${S3_BACKUP_PREFIX:-qlockify}")"
|
S3_BACKUP_PREFIX="$(normalize_prefix "${S3_BACKUP_PREFIX:-qlockify}")"
|
||||||
S3_BACKUP_REGION="${S3_BACKUP_REGION:-us-east-1}"
|
|
||||||
|
|
||||||
require_var S3_BACKUP_BUCKET
|
require_var S3_BACKUP_BUCKET
|
||||||
|
require_var S3_BACKUP_ENDPOINT_URL
|
||||||
require_var S3_BACKUP_ACCESS_KEY_ID
|
require_var S3_BACKUP_ACCESS_KEY_ID
|
||||||
require_var S3_BACKUP_SECRET_ACCESS_KEY
|
require_var S3_BACKUP_SECRET_ACCESS_KEY
|
||||||
require_var BACKUP_ENCRYPTION_PASSPHRASE
|
require_var BACKUP_ENCRYPTION_PASSPHRASE
|
||||||
|
|
||||||
export AWS_ACCESS_KEY_ID="$S3_BACKUP_ACCESS_KEY_ID"
|
|
||||||
export AWS_SECRET_ACCESS_KEY="$S3_BACKUP_SECRET_ACCESS_KEY"
|
|
||||||
export AWS_DEFAULT_REGION="$S3_BACKUP_REGION"
|
|
||||||
export AWS_EC2_METADATA_DISABLED=true
|
|
||||||
|
|
||||||
if [[ "$REQUESTED_KEY" == "latest" ]]; then
|
|
||||||
log "Resolving latest encrypted backup object"
|
|
||||||
OBJECT_KEY="$(latest_object_key)"
|
|
||||||
[[ -n "$OBJECT_KEY" ]] || fail "No encrypted backups found in s3://$S3_BACKUP_BUCKET/$S3_BACKUP_PREFIX/"
|
|
||||||
else
|
|
||||||
OBJECT_KEY="${REQUESTED_KEY#s3://$S3_BACKUP_BUCKET/}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
WORK_DIR="$(mktemp -d)"
|
WORK_DIR="$(mktemp -d)"
|
||||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||||
|
|
||||||
ENCRYPTED_PATH="$WORK_DIR/$(basename "$OBJECT_KEY")"
|
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="$WORK_DIR/${ENCRYPTED_PATH##*/}"
|
||||||
DECRYPTED_PATH="${DECRYPTED_PATH%.enc}"
|
DECRYPTED_PATH="${DECRYPTED_PATH%.enc}"
|
||||||
|
|
||||||
log "Downloading encrypted backup from s3://$S3_BACKUP_BUCKET/$OBJECT_KEY"
|
log "Downloading encrypted backup from $REMOTE_PATH/$OBJECT_NAME"
|
||||||
aws_s3 cp "s3://$S3_BACKUP_BUCKET/$OBJECT_KEY" "$ENCRYPTED_PATH" --only-show-errors
|
rclone copyto "$REMOTE_PATH/$OBJECT_NAME" "$ENCRYPTED_PATH" \
|
||||||
|
--config "$RCLONE_CONFIG" \
|
||||||
|
--s3-no-check-bucket \
|
||||||
|
--progress
|
||||||
|
|
||||||
log "Decrypting backup archive"
|
log "Decrypting backup archive"
|
||||||
openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \
|
openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \
|
||||||
@@ -134,4 +145,4 @@ openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \
|
|||||||
log "Restoring decrypted backup archive"
|
log "Restoring decrypted backup archive"
|
||||||
DEPLOY_ROOT="$DEPLOY_ROOT" "$RESTORE_SCRIPT" "$DECRYPTED_PATH"
|
DEPLOY_ROOT="$DEPLOY_ROOT" "$RESTORE_SCRIPT" "$DECRYPTED_PATH"
|
||||||
|
|
||||||
log "S3 restore completed from: s3://$S3_BACKUP_BUCKET/$OBJECT_KEY"
|
log "S3 restore completed from: $REMOTE_PATH/$OBJECT_NAME"
|
||||||
|
|||||||
Reference in New Issue
Block a user