Files

88 lines
3.6 KiB
Bash

#!/usr/bin/env bash
# /home/spuser/PassKeeper/scripts/backup_db.sh
#
# Automated MySQL backup for PassKeeper.
# - Dumps the passkeeper database with mysqldump
# - Compresses with gzip
# - Retains the last 30 daily backups
# - Logs all actions with timestamps
# - Exits non-zero on any failure so cron can email on error
#
# Usage:
# chmod +x scripts/backup_db.sh
# # Test manually:
# bash scripts/backup_db.sh
# # Schedule via cron (see scripts/backup.cron)
set -euo pipefail
# ── Configuration ─────────────────────────────────────────────────────────────
ENV_FILE="$(dirname "$(realpath "$0")")/../.env"
# Load .env variables
if [[ -f "$ENV_FILE" ]]; then
set -o allexport
# shellcheck disable=SC1090
source "$ENV_FILE"
set +o allexport
else
echo "ERROR: .env file not found at $ENV_FILE" >&2
exit 1
fi
BACKUP_DIR="${BACKUP_DIR:-/home/spuser/backups/passkeeper}"
RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-30}"
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
BACKUP_FILE="${BACKUP_DIR}/passkeeper_${TIMESTAMP}.sql.gz"
LOG_FILE="${BACKUP_DIR}/backup.log"
# MySQL credentials from .env
DB_HOST="${MYSQL_HOST:-127.0.0.1}"
DB_PORT="${MYSQL_PORT:-3306}"
DB_USER="${MYSQL_USER:-passkeeper}"
DB_PASS="${MYSQL_PASSWORD}"
DB_NAME="${MYSQL_DB:-passkeeper}"
# ── Helpers ───────────────────────────────────────────────────────────────────
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
# ── Pre-flight ────────────────────────────────────────────────────────────────
mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"
log "INFO Starting backup → $BACKUP_FILE"
# ── Dump ─────────────────────────────────────────────────────────────────────
# --single-transaction: consistent snapshot without locking (InnoDB)
# --routines --events: include stored procedures/events if any
# --no-tablespaces: avoid PROCESS privilege requirement on MySQL 8+
#
# MYSQL_PWD is used instead of --password so the credential never appears in
# the process list (ps aux) where any user on the server could read it.
# mysqldump and the mysql client both honour MYSQL_PWD natively.
MYSQL_PWD="$DB_PASS" mysqldump \
--host="$DB_HOST" \
--port="$DB_PORT" \
--user="$DB_USER" \
--single-transaction \
--routines \
--events \
--no-tablespaces \
"$DB_NAME" \
| gzip -9 > "$BACKUP_FILE"
BACKUP_SIZE="$(du -sh "$BACKUP_FILE" | cut -f1)"
log "INFO Backup complete — size: $BACKUP_SIZE"
# ── Verify the file is non-empty ──────────────────────────────────────────────
if [[ ! -s "$BACKUP_FILE" ]]; then
log "ERROR Backup file is empty — aborting retention cleanup"
exit 1
fi
# ── Retention: delete backups older than RETENTION_DAYS ──────────────────────
DELETED=$(find "$BACKUP_DIR" -maxdepth 1 -name 'passkeeper_*.sql.gz' \
-mtime +"$RETENTION_DAYS" -print -delete | wc -l)
log "INFO Retention cleanup: removed $DELETED file(s) older than ${RETENTION_DAYS} days"
log "INFO Backup finished successfully"