diff --git a/scripts/gitea_backup.sh b/scripts/gitea_backup.sh new file mode 100755 index 00000000..b3820cb1 --- /dev/null +++ b/scripts/gitea_backup.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# Gitea backup script — run on the VPS before any hardening changes. +# Usage: sudo bash scripts/gitea_backup.sh [off-site-dest] +# +# off-site-dest: optional rsync/scp destination for off-site copy +# e.g. user@backup-host:/backups/gitea/ +# +# Refs: #971, #990 + +set -euo pipefail + +BACKUP_DIR="/opt/gitea/backups" +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +GITEA_CONF="/etc/gitea/app.ini" +GITEA_WORK_DIR="/var/lib/gitea" +OFFSITE_DEST="${1:-}" + +echo "=== Gitea Backup — $TIMESTAMP ===" + +# Ensure backup directory exists +mkdir -p "$BACKUP_DIR" +cd "$BACKUP_DIR" + +# Run the dump +echo "[1/4] Running gitea dump..." +gitea dump -c "$GITEA_CONF" + +# Find the newest zip (gitea dump names it gitea-dump-*.zip) +BACKUP_FILE=$(ls -t "$BACKUP_DIR"/gitea-dump-*.zip 2>/dev/null | head -1) + +if [ -z "$BACKUP_FILE" ]; then + echo "ERROR: No backup zip found in $BACKUP_DIR" + exit 1 +fi + +BACKUP_SIZE=$(stat -c%s "$BACKUP_FILE" 2>/dev/null || stat -f%z "$BACKUP_FILE") +echo "[2/4] Backup created: $BACKUP_FILE ($BACKUP_SIZE bytes)" + +if [ "$BACKUP_SIZE" -eq 0 ]; then + echo "ERROR: Backup file is 0 bytes" + exit 1 +fi + +# Lock down permissions +chmod 600 "$BACKUP_FILE" + +# Verify contents +echo "[3/4] Verifying backup contents..." +CONTENTS=$(unzip -l "$BACKUP_FILE" 2>/dev/null || true) + +check_component() { + if echo "$CONTENTS" | grep -q "$1"; then + echo " OK: $2" + else + echo " WARN: $2 not found in backup" + fi +} + +check_component "gitea-db.sql" "Database dump" +check_component "gitea-repo" "Repositories" +check_component "custom" "Custom config" +check_component "app.ini" "app.ini" + +# Off-site copy +if [ -n "$OFFSITE_DEST" ]; then + echo "[4/4] Copying to off-site: $OFFSITE_DEST" + rsync -avz "$BACKUP_FILE" "$OFFSITE_DEST" + echo " Off-site copy complete." +else + echo "[4/4] No off-site destination provided. Skipping." + echo " To copy later: scp $BACKUP_FILE user@backup-host:/backups/gitea/" +fi + +echo "" +echo "=== Backup complete ===" +echo "File: $BACKUP_FILE" +echo "Size: $BACKUP_SIZE bytes" +echo "" +echo "To verify restore on a clean instance:" +echo " 1. Copy zip to test machine" +echo " 2. unzip $BACKUP_FILE" +echo " 3. gitea restore --from -c /etc/gitea/app.ini" +echo " 4. Verify repos and DB are intact"