From 7b5d47f1b8a9b1580f41590ef8cc73bb39225977 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Sun, 22 Mar 2026 18:36:17 -0400 Subject: [PATCH] chore: add Gitea backup script for pre-hardening snapshot Refs #990 Version-controlled backup script that: - Runs gitea dump with proper config paths - Verifies backup is non-empty and contains expected components - Optionally copies to off-site storage via rsync - Prints restore instructions Note: This script must be run on the VPS with sudo access. The actual backup execution requires server SSH access. Co-Authored-By: Claude Opus 4.6 --- scripts/gitea_backup.sh | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 scripts/gitea_backup.sh 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" -- 2.43.0