Files
agence66-gitea/scripts/backup.sh
BeauTroll eff651e720 Enhance backup/restore/update scripts with security and best practices
- Security improvements: set -euo pipefail, secure .env loading, safe PGPASSWORD handling
- Add comprehensive logging to ./logs/ directory for all operations
- Implement SHA256 checksums for backup integrity verification
- Add lock file mechanism to prevent concurrent backups
- Improve error handling with detailed exit codes and cleanup functions
- Add safety backup of current DB before restore operations
- Backup docker-compose.yml before updates with auto-restore on failure
- Replace wget with curl for better reliability in health checks
- Use find -mindepth for safer data directory cleanup
- Add progress indicators with file sizes and operation statistics
- Validate paths and checksums before restore operations
- All operations now log to timestamped files with full traceability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-17 05:42:54 +01:00

200 lines
6.0 KiB
Bash
Executable File

#!/bin/bash
# ============================================
# Script de sauvegarde Gitea
# ============================================
# Ce script crée une sauvegarde complète de:
# - Base de données PostgreSQL
# - Dépôts Git
# - Configuration Gitea
# - Données utilisateur
set -euo pipefail # Arrêter en cas d'erreur, variables non définies, erreurs dans pipes
# Couleurs pour l'affichage
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Charger les variables d'environnement de manière sécurisée
if [ -f .env ]; then
set -a
source .env
set +a
else
echo -e "${RED}Erreur: Fichier .env non trouvé${NC}"
exit 1
fi
# Variables
BACKUP_DIR="./backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="gitea_backup_${TIMESTAMP}"
TEMP_DIR="${BACKUP_DIR}/${BACKUP_NAME}"
RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-7}
LOCK_FILE="/tmp/gitea_backup.lock"
LOG_DIR="./logs"
LOG_FILE="${LOG_DIR}/backup_${TIMESTAMP}.log"
# Vérifier que les variables nécessaires sont définies
if [ -z "${POSTGRES_DATABASE:-}" ] || [ -z "${POSTGRES_USER:-}" ] || [ -z "${POSTGRES_PASSWORD:-}" ]; then
echo -e "${RED}Erreur: Variables PostgreSQL non définies dans .env${NC}"
exit 1
fi
# Vérifier qu'une sauvegarde n'est pas déjà en cours
if [ -f "$LOCK_FILE" ]; then
echo -e "${RED}Erreur: Une sauvegarde est déjà en cours${NC}"
echo "Si aucune sauvegarde n'est en cours, supprimez le fichier: $LOCK_FILE"
exit 1
fi
# Créer le fichier de lock
touch "$LOCK_FILE"
# Fonction de nettoyage en cas d'erreur
cleanup() {
local exit_code=$?
if [ -d "$TEMP_DIR" ]; then
echo -e "${YELLOW}Nettoyage des fichiers temporaires...${NC}"
rm -rf "$TEMP_DIR"
fi
# Supprimer le fichier de lock
rm -f "$LOCK_FILE"
if [ $exit_code -ne 0 ]; then
echo -e "${RED}Sauvegarde échouée avec le code: $exit_code${NC}"
fi
}
# Piège pour nettoyer en cas d'erreur ou de fin
trap cleanup EXIT INT TERM
# Fonction de logging
log() {
echo -e "$@" | tee -a "$LOG_FILE"
}
echo -e "${GREEN}=== Début de la sauvegarde Gitea ===${NC}"
echo "Timestamp: $TIMESTAMP"
# Créer les répertoires si nécessaire
mkdir -p "$BACKUP_DIR"
mkdir -p "$TEMP_DIR"
mkdir -p "$LOG_DIR"
# Initialiser le fichier de log
log "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] Début de la sauvegarde${NC}"
log "Backup name: $BACKUP_NAME"
# Vérifier que les conteneurs sont en cours d'exécution
if ! docker compose ps | grep -q 'gitea.*running'; then
log "${RED}Erreur: Le conteneur Gitea n'est pas en cours d'exécution${NC}"
exit 1
fi
log "${YELLOW}[1/5] Sauvegarde de la base de données PostgreSQL...${NC}"
# Exporter la base de données (PGPASSWORD passé de manière sécurisée)
if ! docker compose exec -T -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_dump \
-U "$POSTGRES_USER" \
-d "$POSTGRES_DATABASE" \
--format=custom \
--compress=9 \
--no-owner \
--no-acl \
> "${TEMP_DIR}/database.dump" 2>> "$LOG_FILE"; then
log "${RED}Erreur: pg_dump a échoué${NC}"
exit 1
fi
if [ ! -s "${TEMP_DIR}/database.dump" ]; then
log "${RED}Erreur: Le dump de la base de données est vide${NC}"
exit 1
fi
DB_SIZE=$(du -h "${TEMP_DIR}/database.dump" | cut -f1)
log "${GREEN}✓ Base de données sauvegardée (${DB_SIZE})${NC}"
log "${YELLOW}[2/5] Sauvegarde des dépôts Git et données...${NC}"
# Créer une archive des dépôts et données
if ! docker compose exec -T gitea tar czf - \
-C /data \
--exclude='./log' \
--exclude='./cache' \
--exclude='./tmp' \
--exclude='./sessions' \
. > "${TEMP_DIR}/gitea_data.tar.gz" 2>> "$LOG_FILE"; then
log "${RED}Erreur: Création de l'archive des données a échoué${NC}"
exit 1
fi
if [ ! -s "${TEMP_DIR}/gitea_data.tar.gz" ]; then
log "${RED}Erreur: L'archive des données est vide${NC}"
exit 1
fi
DATA_SIZE=$(du -h "${TEMP_DIR}/gitea_data.tar.gz" | cut -f1)
log "${GREEN}✓ Dépôts et données sauvegardés (${DATA_SIZE})${NC}"
log "${YELLOW}[3/5] Création de l'archive finale...${NC}"
# Créer l'archive finale
if ! cd "$BACKUP_DIR"; then
log "${RED}Erreur: Impossible d'accéder au répertoire $BACKUP_DIR${NC}"
exit 1
fi
if ! tar czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}/"; then
log "${RED}Erreur: Création de l'archive finale a échoué${NC}"
cd - > /dev/null
exit 1
fi
if [ ! -s "${BACKUP_NAME}.tar.gz" ]; then
log "${RED}Erreur: L'archive finale est vide${NC}"
cd - > /dev/null
exit 1
fi
# Supprimer le répertoire temporaire
rm -rf "${BACKUP_NAME}"
cd - > /dev/null
BACKUP_SIZE=$(du -h "${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" | cut -f1)
log "${GREEN}✓ Archive créée: ${BACKUP_NAME}.tar.gz (${BACKUP_SIZE})${NC}"
log "${YELLOW}[4/5] Génération du checksum de sécurité...${NC}"
# Générer le checksum SHA256
if ! cd "$BACKUP_DIR"; then
log "${RED}Erreur: Impossible d'accéder au répertoire $BACKUP_DIR${NC}"
exit 1
fi
sha256sum "${BACKUP_NAME}.tar.gz" > "${BACKUP_NAME}.tar.gz.sha256"
CHECKSUM=$(cut -d' ' -f1 "${BACKUP_NAME}.tar.gz.sha256")
cd - > /dev/null
log "${GREEN}✓ Checksum: ${CHECKSUM:0:16}...${NC}"
log "${YELLOW}[5/5] Nettoyage des anciennes sauvegardes (>$RETENTION_DAYS jours)...${NC}"
# Supprimer les sauvegardes plus anciennes que RETENTION_DAYS (fichiers .tar.gz et .sha256)
DELETED_COUNT=0
while IFS= read -r -d '' old_backup; do
log " Suppression: $(basename "$old_backup")"
rm -f "$old_backup" "${old_backup}.sha256"
DELETED_COUNT=$((DELETED_COUNT + 1))
done < <(find "$BACKUP_DIR" -name "gitea_backup_*.tar.gz" -type f -mtime +"$RETENTION_DAYS" -print0)
REMAINING_BACKUPS=$(find "$BACKUP_DIR" -name "gitea_backup_*.tar.gz" -type f | wc -l)
log "${GREEN}✓ Sauvegardes supprimées: $DELETED_COUNT | Restantes: $REMAINING_BACKUPS${NC}"
# Statistiques finales
END_TIME=$(date '+%Y-%m-%d %H:%M:%S')
log ""
log "${GREEN}=== Sauvegarde terminée avec succès ===${NC}"
log "Fichier: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz"
log "Taille: $BACKUP_SIZE"
log "Checksum: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz.sha256"
log "Log: $LOG_FILE"
log "Terminé: $END_TIME"