Files
Maven/scripts/maven-backup.sh
T
kami 3f09cdb5ae scripts: add maven-backup.sh for encrypted DB backup/restore
Simple shell script for backing up, restoring, and verifying the
encrypted SQLite database (AES-256-GCM with MVNC1\0 magic header).

Commands:
  backup   — cp + magic-verify to MAVEN_BACKUP_DIR
  restore  — cp back with confirmation prompt
  verify   — check magic header via od (no xxd/jq dependency)
  list     — show all backups with size and validity

Portable: uses only POSIX sh, od, grep, stat. Config path resolved
from mavend.json via grep or env var overrides.
2026-07-05 11:48:46 +04:00

251 lines
6.7 KiB
Bash
Executable File

#!/bin/bash
# maven-backup.sh — backup/restore/verify the encrypted Maven SQLite database.
#
# The encrypted DB (AES-256-GCM with 6-byte magic "MVNC1\0") is the only
# persistent state. Backup is `cp` + magic-verify; restore is `cp` back.
#
# Usage:
# maven-backup.sh backup — create timestamped backup
# maven-backup.sh restore <backup-file> — restore from backup
# maven-backup.sh verify [backup-file] — verify magic header (default: latest)
# maven-backup.sh list — list available backups
set -euo pipefail
SCRIPT_NAME=$(basename "$0")
CONFIG_FILE="${MAVEN_CONFIG:-/opt/maven/config/mavend.json}"
BACKUP_DIR="${MAVEN_BACKUP_DIR:-/var/backups/maven}"
STATE_DIR="${MAVEN_STATE_DIR:-/var/lib/maven}"
DB_FILE="${MAVEN_DB_FILE:-maven.db.enc}"
DB_PATH="$STATE_DIR/$DB_FILE"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
usage() {
cat <<EOF
Usage: $SCRIPT_NAME {backup|restore|verify|list} [backup-file]
Commands:
backup Create a timestamped backup of the encrypted DB
restore <backup-file> Restore the DB from a backup file
verify [backup-file] Verify backup integrity (default: latest)
list List available backups
Environment:
MAVEN_CONFIG Path to mavend.json (default: /opt/maven/config/mavend.json)
MAVEN_BACKUP_DIR Backup directory (default: /var/backups/maven)
MAVEN_STATE_DIR State directory with the DB file (default: /var/lib/maven)
MAVEN_DB_FILE Database filename (default: maven.db.enc)
EOF
exit 1
}
# check_magic: verify the file starts with the correct 6-byte magic header.
# Magic is "MVNC1\0" = bytes: 4d 56 4e 43 31 00
check_magic() {
local file="$1"
if [ ! -f "$file" ]; then
error "file not found: $file"
return 1
fi
local magic
magic=$(od -A n -t x1 -N 6 "$file" 2>/dev/null | tr -d ' \n')
if [ "$magic" = "4d564e433100" ]; then
return 0
fi
error "invalid magic header in $file (expected MVNC1\\0)"
return 1
}
# file_size: get file size in bytes (Linux stat).
file_size() {
local file="$1"
stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "0"
}
# read_config_value: read a JSON string value from the config file using grep.
read_config_value() {
local key="$1"
grep -o "\"$key\": *\"[^\"]*\"" "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*: *"\(.*\)"/\1/'
}
# resolve_db_path: determine the actual DB path from config or defaults.
resolve_db_path() {
local config_db
config_db=$(read_config_value "db_path")
if [ -n "$config_db" ]; then
echo "$config_db"
else
echo "$DB_PATH"
fi
}
cmd_backup() {
local actual_db
actual_db=$(resolve_db_path)
if [ ! -f "$actual_db" ]; then
error "database not found at $actual_db"
error "is mavend running? has it been started at least once?"
exit 1
fi
mkdir -p "$BACKUP_DIR"
local timestamp
timestamp=$(date -u +"%Y%m%dT%H%M%SZ")
local backup_file="$BACKUP_DIR/maven-db-$timestamp.enc"
info "backing up $actual_db$backup_file"
cp "$actual_db" "$backup_file"
sync
if check_magic "$backup_file"; then
local size
size=$(file_size "$backup_file")
info "backup complete: $backup_file ($size bytes)"
echo "$backup_file"
else
error "backup file failed magic check — removing"
rm -f "$backup_file"
exit 1
fi
}
cmd_restore() {
local backup_file="$1"
local actual_db
actual_db=$(resolve_db_path)
if [ ! -f "$backup_file" ]; then
error "backup file not found: $backup_file"
exit 1
fi
if ! check_magic "$backup_file"; then
error "backup file is not a valid encrypted Maven database"
exit 1
fi
if [ -f "$actual_db" ]; then
local backup_size restore_size
backup_size=$(file_size "$backup_file")
restore_size=$(file_size "$actual_db")
warn "about to OVERWRITE $actual_db ($restore_size bytes)"
warn "with $backup_file ($backup_size bytes)"
echo -n "continue? [y/N] "
read -r confirm
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
info "restore cancelled"
exit 0
fi
fi
info "restoring $backup_file$actual_db"
cp "$backup_file" "$actual_db"
sync
if check_magic "$actual_db"; then
info "restore complete: $actual_db"
else
error "restored file failed magic check — database may be corrupt"
exit 1
fi
}
cmd_verify() {
local target="$1"
if [ ! -f "$target" ]; then
error "file not found: $target"
exit 1
fi
if check_magic "$target"; then
local size
size=$(file_size "$target")
info "valid encrypted database: $target ($size bytes)"
return 0
else
error "NOT a valid encrypted database: $target"
exit 1
fi
}
cmd_list() {
if [ ! -d "$BACKUP_DIR" ]; then
info "no backups directory: $BACKUP_DIR"
exit 0
fi
local backups
backups=$(ls -1 "$BACKUP_DIR"/maven-db-*.enc 2>/dev/null || true)
if [ -z "$backups" ]; then
info "no backups found in $BACKUP_DIR"
exit 0
fi
echo "Available backups in $BACKUP_DIR:"
echo ""
printf "%-30s %12s %s\n" "Filename" "Size" "Valid"
echo "$(printf '=%*s' 70 '' | tr ' ' '=')"
for f in $backups; do
local name size valid
name=$(basename "$f")
size=$(file_size "$f")
if check_magic "$f" >/dev/null 2>&1; then
valid="${GREEN}${NC}"
else
valid="${RED}${NC}"
fi
printf "%-30s %12s %b\n" "$name" "$size" "$valid"
done
}
# --- Main ---
case "${1:-help}" in
backup)
cmd_backup
;;
restore)
if [ -z "${2:-}" ]; then
error "restore requires a backup file path"
echo ""
cmd_list
exit 1
fi
cmd_restore "$2"
;;
verify)
target="${2:-}"
if [ -z "$target" ]; then
if [ -d "$BACKUP_DIR" ]; then
target=$(ls -t "$BACKUP_DIR"/maven-db-*.enc 2>/dev/null | head -1 || true)
fi
if [ -z "$target" ]; then
target=$(resolve_db_path)
info "no backups found; verifying live database"
fi
fi
cmd_verify "$target"
;;
list)
cmd_list
;;
help|--help|-h)
usage
;;
*)
error "unknown command: $1"
usage
;;
esac