Apply critical security fixes and major improvements to all scripts
Security (CRITICAL): - Add .env.example with strong password generation instructions - Fix path traversal validation in restore.sh (now detects all .. patterns) - Secure .env loading with set -a/set +a in all scripts - Add logs/ to .gitignore to prevent credential leaks Backup & Restore (IMPORTANT): - Add file locking system to prevent concurrent backups - Add disk space verification before backup operations - Generate SHA256 checksums for all backups - Verify checksums before restoration - Create safety database backup before restore - Implement comprehensive logging to ./logs/ directory - Fix BACKUP_RETENTION_DAYS inconsistency - Replace dangerous find -delete with safe iteration Update & Recovery: - Backup docker-compose.yml before updates with auto-rollback - Add version display before/after updates - Increase timeouts to 120s for slow containers - Dynamic backup suggestion in recover.sh Compatibility: - Add Docker Compose v2 support with v1 fallback in all scripts - Standardized log() function across all scripts New Features: - Add check-health.sh: comprehensive system health monitoring - Add SECURITY.md: complete security documentation - Update Makefile with check-health and recover commands - Centralized logging with timestamps and levels 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,14 +3,40 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Variables globales
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
LOCK_FILE="/tmp/nextcloud_backup.lock"
|
||||
LOG_DIR="./logs"
|
||||
LOG_FILE="$LOG_DIR/backup_$(date +%Y%m%d_%H%M%S).log"
|
||||
MAINTENANCE_ENABLED=false
|
||||
|
||||
# Créer le dossier de logs
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# Fonction de logging
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp
|
||||
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Charger les variables d'environnement
|
||||
if [ ! -f .env ]; then
|
||||
echo "[ERR] Erreur: Fichier .env introuvable"
|
||||
log "ERROR" "Fichier .env introuvable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Charger .env de manière sécurisée
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
|
||||
# Vérifier les variables requises
|
||||
: "${MYSQL_USER:?Variable MYSQL_USER non définie}"
|
||||
@@ -18,108 +44,194 @@ source .env
|
||||
: "${MYSQL_DATABASE:?Variable MYSQL_DATABASE non définie}"
|
||||
|
||||
BACKUP_DIR="${BACKUP_DESTINATION:-./backups}"
|
||||
BACKUP_RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_NAME="nextcloud_backup_$DATE"
|
||||
BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
|
||||
MAINTENANCE_ENABLED=false
|
||||
BACKUP_RETENTION_DAYS=7
|
||||
|
||||
# Fonction de nettoyage en cas d'erreur
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
|
||||
if [ "$exit_code" -ne 0 ]; then
|
||||
echo "[ERR] Erreur détectée (code: $exit_code), nettoyage..."
|
||||
log "ERROR" "Erreur détectée (code: $exit_code), nettoyage..."
|
||||
fi
|
||||
|
||||
# Désactiver le mode maintenance si activé
|
||||
if [ "$MAINTENANCE_ENABLED" = true ]; then
|
||||
echo "▶️ Désactivation du mode maintenance..."
|
||||
docker-compose exec -T -u www-data nextcloud php occ maintenance:mode --off || true
|
||||
log "INFO" "Désactivation du mode maintenance..."
|
||||
docker-compose exec -T -u www-data nextcloud php occ maintenance:mode --off 2>>"$LOG_FILE" || true
|
||||
fi
|
||||
|
||||
# Nettoyer le backup partiel
|
||||
if [ -d "$BACKUP_PATH" ]; then
|
||||
echo "🧹 Nettoyage du backup partiel..."
|
||||
log "INFO" "Nettoyage du backup partiel..."
|
||||
rm -rf "${BACKUP_PATH:?}"
|
||||
fi
|
||||
|
||||
# Supprimer le lock
|
||||
rm -f "$LOCK_FILE"
|
||||
|
||||
if [ "$exit_code" -eq 0 ]; then
|
||||
log "SUCCESS" "Backup terminé avec succès"
|
||||
else
|
||||
log "ERROR" "Backup échoué avec code: $exit_code"
|
||||
fi
|
||||
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
echo "[*] Démarrage du backup: $BACKUP_NAME"
|
||||
# Vérifier qu'un backup n'est pas déjà en cours
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
log "ERROR" "Un backup est déjà en cours (lock file: $LOCK_FILE)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Créer le lock file
|
||||
echo $$ > "$LOCK_FILE"
|
||||
|
||||
log "INFO" "=== Démarrage du backup: $BACKUP_NAME ==="
|
||||
log "INFO" "Log file: $LOG_FILE"
|
||||
|
||||
# Vérifier l'espace disque disponible
|
||||
log "INFO" "Vérification de l'espace disque..."
|
||||
REQUIRED_SPACE=$(du -sb ./data ./db 2>/dev/null | awk '{sum+=$1} END {print int(sum*1.2)}' || echo "0")
|
||||
AVAILABLE_SPACE=$(df -B1 "$BACKUP_DIR" | awk 'NR==2 {print $4}')
|
||||
|
||||
log "INFO" "Espace requis (estimé): $(numfmt --to=iec-i --suffix=B "$REQUIRED_SPACE" 2>/dev/null || echo "$REQUIRED_SPACE bytes")"
|
||||
log "INFO" "Espace disponible: $(numfmt --to=iec-i --suffix=B "$AVAILABLE_SPACE" 2>/dev/null || echo "$AVAILABLE_SPACE bytes")"
|
||||
|
||||
if [ "$AVAILABLE_SPACE" -lt "$REQUIRED_SPACE" ]; then
|
||||
log "ERROR" "Espace disque insuffisant"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Créer le dossier de backup
|
||||
mkdir -p "$BACKUP_PATH"
|
||||
|
||||
# 1. Activer le mode maintenance
|
||||
echo "[-] Activation du mode maintenance..."
|
||||
if docker-compose exec -T -u www-data nextcloud php occ maintenance:mode --on; then
|
||||
log "INFO" "Activation du mode maintenance..."
|
||||
if docker-compose exec -T -u www-data nextcloud php occ maintenance:mode --on 2>>"$LOG_FILE"; then
|
||||
MAINTENANCE_ENABLED=true
|
||||
log "INFO" "Mode maintenance activé"
|
||||
else
|
||||
echo "[ERR] Impossible d'activer le mode maintenance"
|
||||
log "ERROR" "Impossible d'activer le mode maintenance"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Backup de la base de données
|
||||
echo "💾 Backup de la base de données..."
|
||||
# Utiliser des variables d'environnement pour éviter le mot de passe dans la ligne de commande
|
||||
log "INFO" "Backup de la base de données..."
|
||||
START_TIME=$(date +%s)
|
||||
if ! docker-compose exec -T db sh -c "MYSQL_PWD=\"\$MYSQL_PASSWORD\" mysqldump \
|
||||
-u\"\$MYSQL_USER\" \
|
||||
\"\$MYSQL_DATABASE\" \
|
||||
--single-transaction \
|
||||
--quick \
|
||||
--lock-tables=false" >"$BACKUP_PATH/database.sql"; then
|
||||
echo "[ERR] Erreur lors du backup de la base de données"
|
||||
--lock-tables=false" >"$BACKUP_PATH/database.sql" 2>>"$LOG_FILE"; then
|
||||
log "ERROR" "Erreur lors du backup de la base de données"
|
||||
exit 1
|
||||
fi
|
||||
END_TIME=$(date +%s)
|
||||
DB_SIZE=$(du -h "$BACKUP_PATH/database.sql" | cut -f1)
|
||||
log "INFO" "Base de données sauvegardée: $DB_SIZE ($(( END_TIME - START_TIME ))s)"
|
||||
|
||||
# 3. Backup des fichiers de config
|
||||
echo "⚙️ Backup de la configuration..."
|
||||
docker-compose exec -T -u www-data nextcloud tar -czf - -C /var/www/html/config . > "$BACKUP_PATH/config.tar.gz"
|
||||
log "INFO" "Backup de la configuration..."
|
||||
START_TIME=$(date +%s)
|
||||
if ! docker-compose exec -T -u www-data nextcloud tar -czf - -C /var/www/html/config . > "$BACKUP_PATH/config.tar.gz" 2>>"$LOG_FILE"; then
|
||||
log "ERROR" "Erreur lors du backup de la configuration"
|
||||
exit 1
|
||||
fi
|
||||
END_TIME=$(date +%s)
|
||||
CONFIG_SIZE=$(du -h "$BACKUP_PATH/config.tar.gz" | cut -f1)
|
||||
log "INFO" "Configuration sauvegardée: $CONFIG_SIZE ($(( END_TIME - START_TIME ))s)"
|
||||
|
||||
# 4. Backup des données utilisateurs (peut être volumineux)
|
||||
echo "📁 Backup des données utilisateurs..."
|
||||
# Utiliser tar avec compression et exclusions depuis le container
|
||||
docker-compose exec -T -u www-data nextcloud tar -czf - \
|
||||
# 4. Backup des données utilisateurs
|
||||
log "INFO" "Backup des données utilisateurs..."
|
||||
START_TIME=$(date +%s)
|
||||
if ! docker-compose exec -T -u www-data nextcloud tar -czf - \
|
||||
-C /var/www/html/data \
|
||||
--exclude='appdata_*/preview' \
|
||||
--exclude='*/cache' \
|
||||
--exclude='*/thumbnails' \
|
||||
. > "$BACKUP_PATH/data.tar.gz"
|
||||
. > "$BACKUP_PATH/data.tar.gz" 2>>"$LOG_FILE"; then
|
||||
log "ERROR" "Erreur lors du backup des données"
|
||||
exit 1
|
||||
fi
|
||||
END_TIME=$(date +%s)
|
||||
DATA_SIZE=$(du -h "$BACKUP_PATH/data.tar.gz" | cut -f1)
|
||||
log "INFO" "Données sauvegardées: $DATA_SIZE ($(( END_TIME - START_TIME ))s)"
|
||||
|
||||
# 5. Backup des apps personnalisées
|
||||
echo "📦 Backup des apps personnalisées..."
|
||||
if docker-compose exec -T nextcloud [ -d /var/www/html/custom_apps ]; then
|
||||
docker-compose exec -T -u www-data nextcloud tar -czf - \
|
||||
-C /var/www/html/custom_apps . > "$BACKUP_PATH/apps.tar.gz"
|
||||
log "INFO" "Backup des apps personnalisées..."
|
||||
if docker-compose exec -T nextcloud [ -d /var/www/html/custom_apps ] 2>>"$LOG_FILE"; then
|
||||
if docker-compose exec -T -u www-data nextcloud tar -czf - \
|
||||
-C /var/www/html/custom_apps . > "$BACKUP_PATH/apps.tar.gz" 2>>"$LOG_FILE"; then
|
||||
APPS_SIZE=$(du -h "$BACKUP_PATH/apps.tar.gz" | cut -f1)
|
||||
log "INFO" "Apps sauvegardées: $APPS_SIZE"
|
||||
else
|
||||
log "WARN" "Erreur lors du backup des apps personnalisées"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ Pas d'apps personnalisées à sauvegarder"
|
||||
log "INFO" "Pas d'apps personnalisées à sauvegarder"
|
||||
fi
|
||||
|
||||
# 6. Désactiver le mode maintenance
|
||||
echo "▶️ Désactivation du mode maintenance..."
|
||||
if docker-compose exec -T -u www-data nextcloud php occ maintenance:mode --off; then
|
||||
log "INFO" "Désactivation du mode maintenance..."
|
||||
if docker-compose exec -T -u www-data nextcloud php occ maintenance:mode --off 2>>"$LOG_FILE"; then
|
||||
MAINTENANCE_ENABLED=false
|
||||
log "INFO" "Mode maintenance désactivé"
|
||||
else
|
||||
echo "[WARN] Attention: Impossible de désactiver le mode maintenance"
|
||||
log "WARN" "Impossible de désactiver le mode maintenance"
|
||||
fi
|
||||
|
||||
# 7. Créer une archive complète
|
||||
echo "🗜️ Compression finale..."
|
||||
if ! tar -czf "$BACKUP_DIR/$BACKUP_NAME.tar.gz" -C "$BACKUP_DIR" "$BACKUP_NAME/"; then
|
||||
echo "[ERR] Erreur lors de la compression"
|
||||
log "INFO" "Compression finale..."
|
||||
START_TIME=$(date +%s)
|
||||
if ! tar -czf "$BACKUP_DIR/$BACKUP_NAME.tar.gz" -C "$BACKUP_DIR" "$BACKUP_NAME/" 2>>"$LOG_FILE"; then
|
||||
log "ERROR" "Erreur lors de la compression"
|
||||
exit 1
|
||||
fi
|
||||
END_TIME=$(date +%s)
|
||||
ARCHIVE_SIZE=$(du -h "$BACKUP_DIR/$BACKUP_NAME.tar.gz" | cut -f1)
|
||||
log "INFO" "Archive créée: $ARCHIVE_SIZE ($(( END_TIME - START_TIME ))s)"
|
||||
|
||||
# 8. Générer le checksum SHA256
|
||||
log "INFO" "Génération du checksum SHA256..."
|
||||
if cd "$BACKUP_DIR" && sha256sum "$BACKUP_NAME.tar.gz" > "$BACKUP_NAME.tar.gz.sha256"; then
|
||||
cd "$PROJECT_ROOT"
|
||||
CHECKSUM=$(cut -d' ' -f1 "$BACKUP_DIR/$BACKUP_NAME.tar.gz.sha256")
|
||||
log "INFO" "Checksum: $CHECKSUM"
|
||||
else
|
||||
cd "$PROJECT_ROOT"
|
||||
log "WARN" "Impossible de générer le checksum"
|
||||
fi
|
||||
|
||||
# Supprimer le dossier temporaire après compression réussie
|
||||
rm -rf "${BACKUP_PATH:?}"
|
||||
|
||||
# 8. Nettoyer les vieux backups
|
||||
RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-30}"
|
||||
echo "🧹 Nettoyage des backups > $RETENTION_DAYS jours..."
|
||||
find "$BACKUP_DIR" -name "nextcloud_backup_*.tar.gz" -type f -mtime +"$RETENTION_DAYS" -delete
|
||||
# 9. Nettoyer les vieux backups
|
||||
log "INFO" "Nettoyage des backups > $BACKUP_RETENTION_DAYS jours..."
|
||||
DELETED_COUNT=0
|
||||
while IFS= read -r -d '' file; do
|
||||
CHECKSUM_FILE="${file}.sha256"
|
||||
log "INFO" "Suppression: $(basename "$file")"
|
||||
rm -f "$file" "$CHECKSUM_FILE" 2>>"$LOG_FILE" || true
|
||||
DELETED_COUNT=$((DELETED_COUNT + 1))
|
||||
done < <(find "$BACKUP_DIR" -name "nextcloud_backup_*.tar.gz" -type f -mtime +"$BACKUP_RETENTION_DAYS" -print0 2>/dev/null || true)
|
||||
|
||||
echo "✅ Backup terminé: $BACKUP_DIR/$BACKUP_NAME.tar.gz"
|
||||
du -h "$BACKUP_DIR/$BACKUP_NAME.tar.gz"
|
||||
if [ "$DELETED_COUNT" -gt 0 ]; then
|
||||
log "INFO" "$DELETED_COUNT ancien(s) backup(s) supprimé(s)"
|
||||
else
|
||||
log "INFO" "Aucun ancien backup à supprimer"
|
||||
fi
|
||||
|
||||
# 10. Statistiques finales
|
||||
log "INFO" "=== Statistiques du backup ==="
|
||||
log "INFO" "Archive: $BACKUP_DIR/$BACKUP_NAME.tar.gz"
|
||||
log "INFO" "Taille: $ARCHIVE_SIZE"
|
||||
log "INFO" "Checksum: ${CHECKSUM:-N/A}"
|
||||
log "INFO" "Rétention: $BACKUP_RETENTION_DAYS jours"
|
||||
log "INFO" "Backups disponibles: $(find "$BACKUP_DIR" -name "nextcloud_backup_*.tar.gz" -type f 2>/dev/null | wc -l)"
|
||||
|
||||
Reference in New Issue
Block a user