feat: Phase 13 — off-site backup with rclone + crypt encryption
Adds cloud sync extension to the daily backup script using rclone with client-side AES-256 encryption. Includes interactive setup script for configuring OneDrive, Google Drive, or any rclone provider. - scripts/setup-rclone.sh: interactive NAS rclone installer + config - scripts/cloud-backup.sh: sourced by backup.sh when CLOUD_ENABLED=true - backup.sh: cloud sync step runs after local backup Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# Off-site Backup — rclone cloud sync extension for backup.sh
|
||||
#
|
||||
# This script is sourced by backup.sh when CLOUD_ENABLED=true.
|
||||
# It syncs local backups to a cloud provider via rclone with encryption.
|
||||
#
|
||||
# Prerequisites (handled by setup-rclone.sh):
|
||||
# 1. rclone installed (Docker image: rclone/rclone)
|
||||
# 2. Cloud remote configured (e.g., `onedrive`, `gdrive`)
|
||||
# 3. Crypt remote configured (e.g., `nas-backup-crypt`)
|
||||
#
|
||||
# Env vars (set in .env or exported before running backup.sh):
|
||||
# CLOUD_ENABLED=false # Set to true to enable cloud sync
|
||||
# RCLONE_REMOTE=nas-backup # Base rclone remote name
|
||||
# RCLONE_CRYPT_REMOTE="" # If set, uses encrypted remote (e.g., nas-backup-crypt:)
|
||||
# CLOUD_RETENTION_DAYS=30 # Keep cloud copies for this many days
|
||||
# RCLONE_EXTRA_FLAGS="" # Extra flags for rclone sync (e.g., "--bwlimit 2M")
|
||||
|
||||
CLOUD_ENABLED="${CLOUD_ENABLED:-false}"
|
||||
RCLONE_REMOTE="${RCLONE_REMOTE:-nas-backup}"
|
||||
RCLONE_CRYPT_REMOTE="${RCLONE_CRYPT_REMOTE:-}"
|
||||
CLOUD_RETENTION_DAYS="${CLOUD_RETENTION_DAYS:-30}"
|
||||
CLOUD_ERRORS=0
|
||||
|
||||
# Resolve the target remote (plain or crypt)
|
||||
if [ -n "$RCLONE_CRYPT_REMOTE" ]; then
|
||||
_cloud_target="${RCLONE_CRYPT_REMOTE}"
|
||||
else
|
||||
_cloud_target="${RCLONE_REMOTE}:nas-tools"
|
||||
fi
|
||||
|
||||
_rclone() {
|
||||
docker run --rm \
|
||||
-v "$RCLONE_CONFIG_DIR:/config/rclone" \
|
||||
-v "$BACKUP_DIR:/data:ro" \
|
||||
rclone/rclone:latest \
|
||||
"$@" --config /config/rclone/rclone.conf
|
||||
}
|
||||
|
||||
cloud_backup() {
|
||||
log "=== Cloud backup started ==="
|
||||
|
||||
# 1. Sync backup files to cloud
|
||||
log "Syncing $BACKUP_DIR to $_cloud_target ..."
|
||||
if _rclone sync /data/ "$_cloud_target" \
|
||||
--verbose \
|
||||
--progress \
|
||||
--copy-links \
|
||||
$RCLONE_EXTRA_FLAGS; then
|
||||
log "Cloud sync OK"
|
||||
else
|
||||
log "FAILED: cloud sync"
|
||||
CLOUD_ERRORS=$((CLOUD_ERRORS + 1))
|
||||
fi
|
||||
|
||||
# 2. Cloud retention — delete files older than CLOUD_RETENTION_DAYS
|
||||
log "Applying cloud retention: ${CLOUD_RETENTION_DAYS} days ..."
|
||||
if _rclone delete "$_cloud_target" \
|
||||
--min-age "${CLOUD_RETENTION_DAYS}d" \
|
||||
--verbose; then
|
||||
log "Cloud retention OK"
|
||||
else
|
||||
log "FAILED: cloud retention cleanup"
|
||||
CLOUD_ERRORS=$((CLOUD_ERRORS + 1))
|
||||
fi
|
||||
|
||||
# 3. Show cloud usage summary
|
||||
log "Cloud storage summary:"
|
||||
_rclone size "$_cloud_target" --json 2>/dev/null | python3 -c "
|
||||
import json, sys
|
||||
try:
|
||||
d = json.load(sys.stdin)
|
||||
print(f' {d.get(\"count\",0)} files, {d.get(\"bytes\",0)/1024/1024:.1f} MB')
|
||||
except: pass
|
||||
" || true
|
||||
|
||||
log "=== Cloud backup finished ($CLOUD_ERRORS errors) ==="
|
||||
return $CLOUD_ERRORS
|
||||
}
|
||||
Reference in New Issue
Block a user