diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1801e9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Configuration files with sensitive data +backup.env +config/restic.conf + +# Logs +*.log + +# Temporary files +*.tmp +*.bak +*~ + +# Cache directories +cache/ +.cache/ \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..cde7816 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,118 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Critical Convention + +**ALL scripts must be executed in an environment that has sourced the configuration:** + +```bash +# Always start by sourcing configuration +source backup.env + +# Then execute any script +./backup/manage list +./backup/gen-conf.sh +sudo ./backup/install-service paperless +``` + +This is the core architectural pattern of this codebase. Scripts do NOT auto-source configuration files. + +## Common Commands + +### Initial Setup +```bash +cp backup.env.sample backup.env # Copy configuration template +# Edit backup.env to match environment +source backup.env +./backup/gen-conf.sh # Generate secure Restic configuration +./backup/init-restic.sh # Initialize Restic repository +``` + +### Service Management +```bash +source backup.env +./backup/manage list # List all backup timers +./backup/manage status # Service backup status +./backup/manage run # Manual backup execution +./backup/manage logs # View backup logs +sudo ./backup/install-service # Install systemd timer +``` + +### Backup Operations +```bash +source backup.env +./backup/list-snapshots [service] # List available snapshots +./backup/restore --test # Test restoration +./backup/restore --production # Production restoration +``` + +### Configuration Testing +```bash +source backup.env +show_config # Display current configuration +validate_paths # Check directory existence +``` + +## Architecture + +### Configuration System +- **`backup.env`**: Master configuration file at project root containing all environment variables +- **`config/restic.conf`**: Generated Restic-specific configuration (created by `gen-conf.sh`) +- **Variable substitution**: systemd templates use `${VAR}` placeholders replaced during installation + +### Core Components +- **`backup/manage`**: Primary interface for all backup operations and service management +- **`backup/install-service`**: Systemd timer installer that performs variable substitution in templates +- **`backup/restore`**: Advanced restoration tool with test/production modes and safety checks +- **Templates**: `service-backup@.service` and `service-backup@.timer` are systemd unit templates + +### Variable Override Pattern +Configuration uses environment variable defaults with override capability: +```bash +BACKUP_USER="${BACKUP_USER:-citadel}" +PROJECT_ROOT="${PROJECT_ROOT:-/home/nicolas/dev/quantumrick}" +``` + +Users can customize by setting environment variables before sourcing `backup.env`. + +### Systemd Integration +- Templates in `backup/` directory contain `${VARIABLE}` placeholders +- `install-service` script performs `sed` substitution to generate final systemd units +- Generated units placed in `/etc/systemd/system/` with proper permissions + +### Security Model +- Restic passwords auto-generated with OpenSSL +- Configuration files have restricted permissions (600) +- Scripts validate directory existence before operations +- Restoration includes test mode for safety + +## Key Files and Their Roles + +### Configuration Layer +- `backup.env`: Master configuration with all variables and utility functions +- `config/restic.conf`: Generated Restic authentication and repository settings + +### Operational Scripts +- `backup/manage`: Main interface (list, status, run, logs commands) +- `backup/gen-conf.sh`: Secure configuration generator +- `backup/init-restic.sh`: Repository initialization +- `backup/install-service`: Systemd timer installation with template substitution + +### Templates and Restoration +- `backup/service-backup@.{service,timer}`: Systemd unit templates with variable placeholders +- `backup/restore`: Production-grade restoration with test mode and extensive validation +- `backup/list-snapshots`: Snapshot browsing utility + +## Development Guidelines + +When adding new scripts: +1. Add comment: `# Configuration should be sourced before running: source backup.env` +2. Use variables from `backup.env` directly (do not auto-source) +3. Follow the pattern of existing scripts for error handling and logging +4. For systemd integration, use template substitution pattern from `install-service` + +When modifying configuration: +- All path variables should have environment override capability +- Maintain backward compatibility with default values +- Update both `backup.env` and this documentation if adding new variables \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d2cb9d --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +# QuantumRick Backup System + +Système de sauvegarde automatisé basé sur Restic avec intégration systemd pour services containerisés. + +## Structure du Projet + +``` +quantumrick/ +├── backup.env # Configuration centralisée +├── config/ +│ └── restic.conf # Configuration Restic (générée) +└── backup/ + ├── manage # Interface principale de gestion + ├── gen-conf.sh # Génération de configuration Restic + ├── init-restic.sh # Initialisation repository + ├── install-service # Installation timers systemd + ├── list-snapshots # Liste des snapshots + ├── restore # Restauration + ├── service-backup@.service # Template service systemd + └── service-backup@.timer # Template timer systemd +``` + +## Prérequis + +**IMPORTANT:** Tous les scripts doivent être exécutés dans un environnement ayant sourcé la configuration : + +```bash +# Toujours commencer par sourcer la configuration +source backup.env + +# Puis exécuter les scripts +./backup/manage list +./backup/gen-conf.sh +./backup/init-restic.sh +``` + +## Installation + +1. **Configuration initiale** + ```bash + # Copier et adapter la configuration + cp backup.env.sample backup.env + # Éditer backup.env selon votre environnement + + # Puis générer la configuration Restic + source backup.env + ./backup/gen-conf.sh + ``` + +2. **Initialisation du repository** + ```bash + source backup.env + ./backup/init-restic.sh + ``` + +3. **Installation d'un service** + ```bash + source backup.env + sudo ./backup/install-service + ``` + +## Configuration + +### Variables principales (backup.env) + +- `BACKUP_USER` : Utilisateur système (défaut: citadel) +- `PROJECT_ROOT` : Racine du projet +- `SERVICES_BASE_DIR` : Répertoire des services à sauvegarder +- `BACKUP_REPOSITORY` : Chemin du repository Restic +- `DEFAULT_BACKUP_SCHEDULE` : Planning par défaut (défaut: *-*-* 03:00:00) + +### Personnalisation + +```bash +# Exemple pour un autre utilisateur +export BACKUP_USER="myuser" +export BACKUP_HOME="/home/myuser" +source backup.env + +# Puis utiliser normalement +./backup/manage list +``` + +## Utilisation + +### Gestion des sauvegardes +```bash +source backup.env + +# Lister les timers +./backup/manage list + +# Statut d'un service +./backup/manage status paperless + +# Lancer une sauvegarde manuelle +./backup/manage run paperless + +# Voir les logs +./backup/manage logs paperless +``` + +### Restauration +```bash +source backup.env + +# Lister les snapshots +./backup/list-snapshots paperless + +# Restaurer en mode test +./backup/restore paperless --test + +# Restaurer en production +./backup/restore paperless --production +``` + +## Développement + +Pour contribuer au projet, respectez la convention de sourcer `backup.env` dans tous vos scripts : + +```bash +#!/bin/bash +# Source configuration (scripts doivent être dans backup/) +source "$(dirname "$(dirname "$0")")/backup.env" +``` + +## Architecture + +- **Configuration centralisée** : `backup.env` contient toutes les variables +- **Scripts modulaires** : Chaque script a une responsabilité spécifique +- **Templates systemd** : Variables substituées à l'installation +- **Sécurité** : Mots de passe générés automatiquement \ No newline at end of file diff --git a/backup.env.sample b/backup.env.sample new file mode 100644 index 0000000..eed9c8c --- /dev/null +++ b/backup.env.sample @@ -0,0 +1,78 @@ +#!/bin/bash +# Configuration centralisée pour le système de backup quantumrick +# Copiez ce fichier vers backup.env et adaptez les valeurs selon votre environnement + +# === Configuration Utilisateur === +# Utilisateur système qui exécutera les backups +BACKUP_USER="${BACKUP_USER:-citadel}" +BACKUP_HOME="${BACKUP_HOME:-/home/$BACKUP_USER}" + +# === Répertoires Principaux === +# Racine du projet quantumrick +PROJECT_ROOT="${PROJECT_ROOT:-/home/nicolas/dev/quantumrick}" +# Répertoire où les scripts de backup sont installés +BACKUP_BASE_DIR="${BACKUP_BASE_DIR:-$BACKUP_HOME/backup}" +# Répertoire contenant les services à sauvegarder +SERVICES_BASE_DIR="${SERVICES_BASE_DIR:-$BACKUP_HOME/services}" +# Répertoire de configuration du projet +CONFIG_DIR="${CONFIG_DIR:-$PROJECT_ROOT/config}" + +# === Stockage des Sauvegardes === +# Chemin de base pour le stockage des backups +BACKUP_STORAGE_PATH="${BACKUP_STORAGE_PATH:-/mnt/data/backup}" +# Nom du repository Restic +BACKUP_REPO_NAME="${BACKUP_REPO_NAME:-quantumrick}" +# Chemin complet du repository Restic +BACKUP_REPOSITORY="${BACKUP_REPOSITORY:-$BACKUP_STORAGE_PATH/$BACKUP_REPO_NAME}" + +# === Système === +# Répertoire des services systemd +SYSTEMD_DIR="${SYSTEMD_DIR:-/etc/systemd/system}" +# Répertoire des logs système +LOG_DIR="${LOG_DIR:-/var/log}" +# Répertoire temporaire +TEMP_DIR="${TEMP_DIR:-/tmp}" + +# === Restic Configuration === +# Cache Restic +RESTIC_CACHE_DIR="${RESTIC_CACHE_DIR:-$TEMP_DIR/restic-cache}" +# Fichier de configuration Restic (généré automatiquement) +RESTIC_CONFIG_FILE="${RESTIC_CONFIG_FILE:-$CONFIG_DIR/restic.conf}" + +# === Templates systemd === +# Nom des fichiers templates +SERVICE_TEMPLATE="${SERVICE_TEMPLATE:-service-backup@.service}" +TIMER_TEMPLATE="${TIMER_TEMPLATE:-service-backup@.timer}" + +# === Planning par défaut === +# Planning de sauvegarde par défaut (format systemd) +# Exemples: "*-*-* 03:00:00" (quotidien 3h), "Mon *-*-* 04:00:00" (lundi 4h) +DEFAULT_BACKUP_SCHEDULE="${DEFAULT_BACKUP_SCHEDULE:-*-*-* 03:00:00}" + +# === Variables d'export pour Restic === +export RESTIC_REPOSITORY="$BACKUP_REPOSITORY" +export RESTIC_CACHE_DIR + +# Fonction utilitaire pour valider les chemins +validate_paths() { + local paths=("$BACKUP_HOME" "$SERVICES_BASE_DIR" "$(dirname "$BACKUP_STORAGE_PATH")") + for path in "${paths[@]}"; do + if [[ ! -d "$path" ]]; then + echo "WARN: Directory $path does not exist" >&2 + fi + done +} + +# Fonction pour afficher la configuration courante +show_config() { + echo "=== Configuration Backup ===" + echo "BACKUP_USER: $BACKUP_USER" + echo "BACKUP_HOME: $BACKUP_HOME" + echo "BACKUP_BASE_DIR: $BACKUP_BASE_DIR" + echo "SERVICES_BASE_DIR: $SERVICES_BASE_DIR" + echo "BACKUP_REPOSITORY: $BACKUP_REPOSITORY" + echo "RESTIC_CONFIG_FILE: $RESTIC_CONFIG_FILE" + echo "SYSTEMD_DIR: $SYSTEMD_DIR" + echo "LOG_DIR: $LOG_DIR" + echo "DEFAULT_BACKUP_SCHEDULE: $DEFAULT_BACKUP_SCHEDULE" +} \ No newline at end of file diff --git a/backup/gen-conf.sh b/backup/gen-conf.sh index 7b5cab8..d41a300 100644 --- a/backup/gen-conf.sh +++ b/backup/gen-conf.sh @@ -1,12 +1,13 @@ #!/bin/bash # Script to generate Restic configuration with secure password -# Location: /home/citadel/services/paperless/generate-restic-config.sh set -e -CONFIG_FILE="/home/citadel/backup/restic.conf" -REPO_PATH="/mnt/data/backup/quantumrick" +# Configuration should be sourced before running: source backup.env + +CONFIG_FILE="$RESTIC_CONFIG_FILE" +REPO_PATH="$BACKUP_REPOSITORY" echo "=== Generating Restic Configuration ===" @@ -28,7 +29,7 @@ RESTIC_PASSWORD=$(openssl rand -base64 32) # Create configuration file echo "Creating configuration file..." cat > "$CONFIG_FILE" << EOF -# Restic configuration for Paperless NGX backups +# Restic configuration for quantumrick backups # Generated on $(date) # Repository path @@ -38,7 +39,7 @@ export RESTIC_REPOSITORY="$REPO_PATH" export RESTIC_PASSWORD="$RESTIC_PASSWORD" # Cache directory (optional) -export RESTIC_CACHE_DIR="/tmp/restic-cache" +export RESTIC_CACHE_DIR="$RESTIC_CACHE_DIR" EOF # Set secure permissions @@ -50,5 +51,6 @@ echo "" echo "⚠️ IMPORTANT: Save this password somewhere safe!" echo " If you lose it, you won't be able to restore your backups!" echo "" -echo "Password: $RESTIC_PASSWORD" +echo "📁 Password saved in: $CONFIG_FILE" +echo " Use 'cat $CONFIG_FILE' to view the password if needed" echo "" diff --git a/backup/init-restic.sh b/backup/init-restic.sh index dfe55b8..aa07914 100644 --- a/backup/init-restic.sh +++ b/backup/init-restic.sh @@ -1,12 +1,13 @@ #!/bin/bash -# Script to initialize Restic repository for Paperless NGX backups -# Location: /home/citadel/services/paperless/init-restic-repo.sh +# Script to initialize Restic repository for quantumrick backups set -e -REPO_PATH="/mnt/data/backup/quantumrick" -CONFIG_FILE="/home/citadel/backup/restic.conf" +# Configuration should be sourced before running: source backup.env + +REPO_PATH="$BACKUP_REPOSITORY" +CONFIG_FILE="$RESTIC_CONFIG_FILE" echo "=== Initializing Restic Repository ===" diff --git a/backup/install-service b/backup/install-service index 50f9497..b55ea06 100755 --- a/backup/install-service +++ b/backup/install-service @@ -1,11 +1,12 @@ #!/bin/bash # Generic script to install systemd timer for any service backup -# Location: /home/citadel/backup/install-service-timer.sh # Usage: sudo ./install-service.sh [schedule] set -e +# Configuration should be sourced before running: source backup.env + # Check arguments if [ $# -lt 1 ]; then echo "Usage: $0 [schedule]" @@ -16,21 +17,21 @@ if [ $# -lt 1 ]; then echo " $0 gitlab \"Mon *-*-* 04:00:00\" # Weekly on Monday at 4:00 AM" echo "" echo "Available services with backup.sh:" - find /home/citadel/services -name "backup.sh" -printf " %h\n" | sed 's|/home/citadel/services/||' 2>/dev/null || echo " (none found)" + find "$SERVICES_BASE_DIR" -name "backup.sh" -printf " %h\n" | sed "s|$SERVICES_BASE_DIR/||" 2>/dev/null || echo " (none found)" exit 1 fi SERVICE_NAME="$1" -SCHEDULE="${2:-*-*-* 03:00:00}" # Default: daily at 3:00 AM +SCHEDULE="${2:-$DEFAULT_BACKUP_SCHEDULE}" # Use default from config -SERVICE_DIR="/home/citadel/services/$SERVICE_NAME" +SERVICE_DIR="$SERVICES_BASE_DIR/$SERVICE_NAME" BACKUP_SCRIPT="$SERVICE_DIR/backup.sh" -BACKUP_DIR="/home/citadel/backup" +BACKUP_DIR="$BACKUP_BASE_DIR" # Systemd template files -SERVICE_TEMPLATE="service-backup@.service" -TIMER_TEMPLATE="service-backup@.timer" -SYSTEMD_DIR="/etc/systemd/system" +SERVICE_TEMPLATE="$SERVICE_TEMPLATE" +TIMER_TEMPLATE="$TIMER_TEMPLATE" +SYSTEMD_DIR="$SYSTEMD_DIR" # Instance names SERVICE_INSTANCE="service-backup@$SERVICE_NAME.service" @@ -83,21 +84,20 @@ if systemctl is-enabled --quiet "$TIMER_INSTANCE" 2>/dev/null; then systemctl disable "$TIMER_INSTANCE" fi -# Copy template files if they don't exist in systemd directory -if [ ! -f "$SYSTEMD_DIR/$SERVICE_TEMPLATE" ]; then - echo "Installing service template..." - cp "$BACKUP_DIR/$SERVICE_TEMPLATE" "$SYSTEMD_DIR/" - chmod 644 "$SYSTEMD_DIR/$SERVICE_TEMPLATE" -fi +# Generate service and timer files with substituted variables +echo "Generating service file with current configuration..." +sed -e "s|\${BACKUP_USER}|$BACKUP_USER|g" \ + -e "s|\${SERVICES_BASE_DIR}|$SERVICES_BASE_DIR|g" \ + "$BACKUP_DIR/$SERVICE_TEMPLATE" > "$SYSTEMD_DIR/$SERVICE_TEMPLATE" +chmod 644 "$SYSTEMD_DIR/$SERVICE_TEMPLATE" -if [ ! -f "$SYSTEMD_DIR/$TIMER_TEMPLATE" ]; then - echo "Installing timer template..." - cp "$BACKUP_DIR/$TIMER_TEMPLATE" "$SYSTEMD_DIR/" - chmod 644 "$SYSTEMD_DIR/$TIMER_TEMPLATE" -fi +echo "Generating timer file with current configuration..." +sed -e "s|\${DEFAULT_BACKUP_SCHEDULE}|$DEFAULT_BACKUP_SCHEDULE|g" \ + "$BACKUP_DIR/$TIMER_TEMPLATE" > "$SYSTEMD_DIR/$TIMER_TEMPLATE" +chmod 644 "$SYSTEMD_DIR/$TIMER_TEMPLATE" # Create custom timer with specific schedule if different from default -if [ "$SCHEDULE" != "*-*-* 03:00:00" ]; then +if [ "$SCHEDULE" != "$DEFAULT_BACKUP_SCHEDULE" ]; then echo "Creating custom timer with schedule: $SCHEDULE" sed "s|OnCalendar=\*-\*-\* 03:00:00|OnCalendar=$SCHEDULE|" \ "$BACKUP_DIR/$TIMER_TEMPLATE" > "$SYSTEMD_DIR/$TIMER_INSTANCE" @@ -114,9 +114,9 @@ systemctl enable "service-backup@$SERVICE_NAME.timer" systemctl start "service-backup@$SERVICE_NAME.timer" # Create log file with proper permissions -LOG_FILE="/var/log/$SERVICE_NAME-backup.log" +LOG_FILE="$LOG_DIR/$SERVICE_NAME-backup.log" touch "$LOG_FILE" -chown citadel:citadel "$LOG_FILE" +chown "$BACKUP_USER:$BACKUP_USER" "$LOG_FILE" echo "" echo "✅ Systemd timer installed successfully for $SERVICE_NAME!" diff --git a/backup/list-snapshots b/backup/list-snapshots index 6d87801..c590b26 100755 --- a/backup/list-snapshots +++ b/backup/list-snapshots @@ -1,14 +1,15 @@ #!/bin/bash # Script to list available snapshots from Restic repository -# Location: /home/citadel/backup/list-snapshots # Usage: ./list-snapshots [service_name] set -e +# Configuration should be sourced before running: source backup.env + # Configuration -BACKUP_DIR="/home/citadel/backup" -CONFIG_FILE="$BACKUP_DIR/restic.conf" +BACKUP_DIR="$BACKUP_BASE_DIR" +CONFIG_FILE="$RESTIC_CONFIG_FILE" # Colors for output BLUE='\033[0;34m' diff --git a/backup/manage b/backup/manage index 6313a28..cd92537 100755 --- a/backup/manage +++ b/backup/manage @@ -1,12 +1,13 @@ #!/bin/bash # Script to manage backup timers for all services -# Location: /home/citadel/backup/manage set -e -BACKUP_DIR="/home/citadel/backup" -SERVICES_DIR="/home/citadel/services" +# Configuration should be sourced before running: source backup.env + +BACKUP_DIR="$BACKUP_BASE_DIR" +SERVICES_DIR="$SERVICES_BASE_DIR" show_help() { echo "Usage: $0 [service_name]" diff --git a/backup/restic.conf b/backup/restic.conf deleted file mode 100644 index 1e55159..0000000 --- a/backup/restic.conf +++ /dev/null @@ -1,11 +0,0 @@ -# Restic configuration for Paperless NGX backups -# Generated on Mon Jun 9 11:45:41 CEST 2025 - -# Repository path -export RESTIC_REPOSITORY="/mnt/data/backup/quantumrick" - -# Repository password -export RESTIC_PASSWORD="CHANGE-ME" - -# Cache directory (optional) -export RESTIC_CACHE_DIR="/tmp/restic-cache" diff --git a/backup/restore b/backup/restore index 4f239e9..098dd48 100755 --- a/backup/restore +++ b/backup/restore @@ -1,15 +1,16 @@ #!/bin/bash # Restic backup restore script -# Location: /home/citadel/backup/restore # Usage: ./restore [--test|--production] set -euo pipefail # Strict mode: exit on error, undefined vars, pipe failures +# Configuration should be sourced before running: source backup.env + # Configuration -readonly BACKUP_DIR="/home/citadel/backup" -readonly SERVICES_DIR="/home/citadel/services" -readonly CONFIG_FILE="$BACKUP_DIR/restic.conf" +readonly BACKUP_DIR="$BACKUP_BASE_DIR" +readonly SERVICES_DIR="$SERVICES_BASE_DIR" +readonly CONFIG_FILE="$RESTIC_CONFIG_FILE" readonly SCRIPT_NAME=$(basename "$0") readonly VERSION="2.1.0" @@ -269,7 +270,7 @@ create_secure_temp_dir() { local temp_dir # Use mktemp to create a secure temporary directory - temp_dir=$(mktemp -d "/tmp/${prefix}-$(date +%Y%m%d-%H%M%S)-XXXXXX") + temp_dir=$(mktemp -d "$TEMP_DIR/${prefix}-$(date +%Y%m%d-%H%M%S)-XXXXXX") if [ ! -d "$temp_dir" ]; then error "Failed to create temporary directory" @@ -324,7 +325,7 @@ run_custom_restore() { log "Mode: $mode" # Create restore log - local log_file="/tmp/restore-${service}-$(date +%Y%m%d-%H%M%S).log" + local log_file="$TEMP_DIR/restore-${service}-$(date +%Y%m%d-%H%M%S).log" # Change directory with verification if ! cd "$service_dir"; then diff --git a/backup/service-backup@.service b/backup/service-backup@.service index dfa64ae..fcbec62 100644 --- a/backup/service-backup@.service +++ b/backup/service-backup@.service @@ -5,14 +5,13 @@ Requires=docker.service [Service] Type=oneshot -User=citadel -Group=citadel -WorkingDirectory=/home/citadel/services/%i -ExecStart=/home/citadel/services/%i/backup.sh +User=${BACKUP_USER} +Group=${BACKUP_USER} +WorkingDirectory=${SERVICES_BASE_DIR}/%i +ExecStart=${SERVICES_BASE_DIR}/%i/backup.sh StandardOutput=journal StandardError=journal Environment=PATH=/usr/local/bin:/usr/bin:/bin - [Install] WantedBy=multi-user.target diff --git a/backup/service-backup@.timer b/backup/service-backup@.timer index cb97489..e41b3a3 100644 --- a/backup/service-backup@.timer +++ b/backup/service-backup@.timer @@ -3,8 +3,8 @@ Description=Daily Backup Timer for %i Requires=service-backup@%i.service [Timer] -# Run daily at 3:00 AM -OnCalendar=*-*-* 03:00:00 +# Run daily at configured time +OnCalendar=${DEFAULT_BACKUP_SCHEDULE} # If system was down during scheduled time, run on next boot Persistent=true # Add randomization to avoid conflicts with other services