Aller au contenu
Q
QuoteNode

Wiki

Sauvegarde et restauration

Comment QuoteNode gère les sauvegardes de base de données et de fichiers — archives chiffrées avec age, restauration pilotée par manifeste, kits de récupération et profils d'exécution.

Sauvegarde et restauration

QuoteNode intègre un système de sauvegarde qui protège vos données commerciales contre les pannes matérielles, les suppressions accidentelles et les incidents d’exploitation.

Ce qui est sauvegardé

Chaque sauvegarde crée une archive contenant :

  1. Dump de la base de données — un dump PostgreSQL complet au format personnalisé (pg_dump --format=custom), incluant toutes les tables, séquences et contraintes.
  2. Stockage de fichiers — tous les fichiers téléversés (images de produits, logos d’entreprise) et les PDF générés, archivés sous forme de files.tar.gz avec des chemins relatifs.
  3. Manifeste de sauvegarde — un fichier backup-manifest.json contenant la version source, le mode de chiffrement, le mode de charge utile de la base de données, les sommes de contrôle et les métadonnées de compatibilité de restauration.
  4. Sommes de contrôle d’intégrité — un fichier checksums.sha256 contenant les empreintes SHA-256 de tous les composants de l’archive.

Chiffrement de l’archive

Les nouvelles sauvegardes chiffrées utilisent le chiffrement par destinataire age (AGE_RECIPIENT). C’est le seul modèle de chiffrement pris en charge pour les nouvelles archives.

  • Le processus de sauvegarde n’utilise qu’un destinataire age public (age1...) — aucune clé privée n’est nécessaire sur le serveur pour créer des sauvegardes.
  • Les archives chiffrées portent l’extension .tar.age.
  • La somme de contrôle SHA-256 de l’archive chiffrée est stockée à côté de l’archive pour vérification côté opérateur.
  • Le déchiffrement nécessite l’identité age privée correspondante, conservée exclusivement dans le kit de récupération de l’opérateur.
  • L’identité age privée brute n’est jamais stockée dans la base de données, les journaux, les DTO ou les arguments de processus après la confirmation de la configuration.

Sauvegardes locales non chiffrées

Les sauvegardes locales non chiffrées (.tar) ne sont autorisées que lorsque toutes les conditions suivantes sont réunies :

  • BACKUP_ARCHIVE_ENCRYPTION_MODE=NONE
  • BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true
  • La destination est le stockage local
  • BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED
  • L’administrateur a explicitement accepté le risque dans l’interface

Les archives non chiffrées sont bloquées pour le stockage distant et pour les charges utiles avec PII déchiffrées.

Configuration (.env). Le destinataire age est généré par le kit de récupération (Admin → Sécurité → Sauvegarde) et stocké dans l’instance — vous ne placez pas de clé dans le .env. Définissez uniquement les deux variables de mode :

BACKUP_ENABLED=true
BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT
BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED
# Alternative — encrypted archive + decrypted PII (cross-instance portability):
# BACKUP_DATABASE_PAYLOAD_MODE=PII_DECRYPTED
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=false

Pour désactiver le chiffrement de l’archive (local uniquement, non recommandé — les PII restent chiffrées dans la charge utile) :

BACKUP_ARCHIVE_ENCRYPTION_MODE=NONE
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true

Avec AGE_RECIPIENT, si aucun destinataire n’a encore été généré, la sauvegarde échoue bruyamment au lieu d’écrire une archive non protégée. La combinaison NONE + PII_DECRYPTED est rejetée.

Modes de charge utile de la base de données

Mode Description
APP_ENCRYPTED Par défaut. Les champs PII restent chiffrés avec la clé applicative DB_ENCRYPTION_KEY. La restauration nécessite la même clé de chiffrement ou une empreinte de clé correspondante.
PII_DECRYPTED Les champs PII sont déchiffrés dans une base de données temporaire avant l’archivage. Les empreintes des mots de passe utilisateurs restent inchangées. Nécessite le chiffrement d’archive AGE_RECIPIENT. Utile pour la portabilité entre instances.

Lors de la restauration d’une sauvegarde PII_DECRYPTED dans une cible avec ENCRYPT_PII=true, le processus de restauration rechiffre automatiquement les PII avec la DB_ENCRYPTION_KEY de la cible avant le démarrage normal de l’application.

Kit de récupération

Pendant la configuration, l’administrateur génère (ou importe) un destinataire age pour les sauvegardes chiffrées. Le kit de récupération est un fichier ZIP contenant :

  • quotenode-age-identity.txt — l’identité age privée (retournée une seule fois)
  • quotenode-age-recipient.txt — le destinataire public et l’empreinte
  • quotenode-backup-verify.sh — script de vérification local (Linux/macOS)
  • quotenode-backup-verify.ps1 — script de vérification local (Windows)
  • README.md — commandes de vérification et de restauration

Critique : L’identité age privée n’est retournée qu’une seule fois lors de la génération du kit. En cas de perte, les archives chiffrées ne pourront pas être déchiffrées. Conservez le kit de récupération dans un emplacement hors ligne sécurisé.

L’administrateur doit confirmer que le kit a été enregistré et testé avant que l’assistant de configuration ne marque la sauvegarde comme prête pour la production.

Vérification locale des sauvegardes

Les sauvegardes téléchargées peuvent être vérifiées en dehors de l’application en cours d’exécution à l’aide des scripts du kit de récupération :

scripts/quotenode-backup-verify.sh \
  --archive /path/backup.tar.age \
  --identity /path/quotenode-age-identity.txt \
  --expected-sha256 <sha256-from-ui-or-sidecar>

Le vérificateur :

  1. Compare la somme de contrôle SHA-256 de l’archive chiffrée à la valeur attendue ou au fichier annexe .sha256.
  2. Déchiffre avec age -d dans un répertoire temporaire privé.
  3. Valide les entrées tar (rejette les chemins absolus, .., les liens symboliques et les fichiers de périphérique).
  4. Vérifie que backup-manifest.json existe et possède une version prise en charge.
  5. Exécute sha256sum -c checksums.sha256.
  6. Exécute pg_restore --list db.dump lorsque pg_restore est disponible.
  7. Supprime par défaut les données temporaires déchiffrées (utilisez --keep-decrypted pour les conserver).

Sauvegarde en ligne (par défaut)

Les sauvegardes en ligne s’exécutent pendant que l’application traite les requêtes. Il n’y a aucune interruption de service.

  • pg_dump au format personnalisé crée un instantané cohérent sur le plan transactionnel sans verrouiller les tables ni bloquer les requêtes.
  • Le stockage de fichiers est archivé en parallèle — les fichiers téléversés et les PDF sont immuables après leur création, de sorte que l’archive est toujours cohérente.
  • PostgreSQL n’a pas besoin de s’arrêter pour produire un dump de base de données cohérent.

Comment s’exécutent les sauvegardes : choisir une topologie

Il existe deux façons d’exécuter en continu les sauvegardes planifiées. Toutes deux produisent des archives identiques — elles ne diffèrent que par l’endroit le job de sauvegarde s’exécute.

Topologie Quand l’utiliser Conteneurs
Worker dédié (par défaut) Production. Les sauvegardes s’exécutent dans un conteneur backup-worker distinct, de sorte qu’elles ne rivalisent jamais avec le trafic web pour la mémoire ou le CPU. backend web + backup-worker
In-process Ordinateur portable / évaluation / petites installations mono-hôte. Le backend web exécute lui-même les sauvegardes — un conteneur de moins à gérer. backend web uniquement

Le job de sauvegarde lui-même est le même pipeline shell (pg_dump → archivage → chiffrement) dans les deux cas, exécuté en dehors du tas de la JVM, de sorte que son coût en mémoire est modeste.

Comment sélectionner une topologie

Deux variables d’environnement fonctionnent ensemble :

  • JOBS_MODE détermine quel conteneur exécute réellement le job de sauvegarde planifié.
  • BACKUP_RUNTIME_PROFILE est une étiquette enregistrée dans les journaux et manifestes de sauvegarde afin que vous puissiez voir, après coup, comment chaque sauvegarde a été produite.
Topologie Backend web Worker de sauvegarde
Worker dédié (par défaut) JOBS_MODE=web + BACKUP_RUNTIME_PROFILE=WORKER conteneur JOBS_MODE=backup-only
In-process JOBS_MODE=all + BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE (aucun)

Le fichier Compose de production inclus utilise la topologie worker dédié. Les guides ordinateur portable/évaluation utilisent l’in-process (JOBS_MODE=all + BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE) afin de n’exécuter qu’un seul conteneur backend.

Référence des profils d’exécution

BACKUP_RUNTIME_PROFILE accepte quatre valeurs. Les deux premières concernent les topologies continues ci-dessus ; les deux dernières concernent les sauvegardes ponctuelles pilotées par l’opérateur.

Profil Description
WORKER Par défaut. Un conteneur backup-worker permanent exécute les sauvegardes planifiées via cron.
WEB_MAINTENANCE Le backend web exécute directement les sauvegardes planifiées. Aucun conteneur worker supplémentaire n’est nécessaire.
ONE_SHOT Un cron externe ou un opérateur exécute scripts/backup-one-shot.sh puis se termine. Aucun worker permanent.
OFFLINE_MAINTENANCE Un script automatisé annonce l’interruption, arrête frontend/backend/backup-worker, maintient PostgreSQL en fonctionnement, exécute la sauvegarde, redémarre les services et vérifie leur état.

Sauvegarde ponctuelle (one-shot)

COMPOSE_PROJECT_NAME=quotenode COMPOSE_ENV_FILE=infra/.env.prod \
  BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT \
  BACKUP_AGE_RECIPIENT=age1... \
  bash scripts/backup-one-shot.sh

Maintenance hors ligne

COMPOSE_PROJECT_NAME=quotenode COMPOSE_ENV_FILE=infra/.env.prod \
  BACKUP_OFFLINE_NOTICE_SECONDS=300 \
  bash scripts/backup-offline-maintenance.sh

Le script de maintenance hors ligne :

  1. Annonce une maintenance planifiée (période de grâce configurable).
  2. Active éventuellement une page de maintenance statique pendant l’arrêt des services.
  3. Arrête le frontend, le backend et le backup-worker (maintient PostgreSQL en fonctionnement).
  4. Exécute une sauvegarde ponctuelle.
  5. Redémarre tous les services.
  6. Vérifie l’état du backend.
  7. Écrit un journal de maintenance JSONL au niveau de l’hôte.

Si le script échoue à un moment quelconque, un piège de sécurité redémarre automatiquement les services.

Utilisez --dry-run pour prévisualiser le plan sans l’exécuter :

bash scripts/backup-offline-maintenance.sh --dry-run

Planification

  • Planification par défaut : tous les jours à 2h00 (0 0 2 * * *, configurable via BACKUP_CRON)
  • Activer/désactiver : BACKUP_ENABLED=true|false
  • Déclenchement manuel : les administrateurs peuvent déclencher une sauvegarde immédiate depuis le panneau d’administration ou via l’API (POST /api/v1/admin/backup/trigger). Le déclencheur répond immédiatement (HTTP 202) tandis que la sauvegarde s’exécute de manière asynchrone.
  • Protection contre la concurrence : un verrou stocké en base de données garantit qu’une seule sauvegarde s’exécute à la fois, sur l’ensemble des workers et des déclenchements manuels.

Pour les profils ONE_SHOT et OFFLINE_MAINTENANCE, le planificateur interne est désactivé — les sauvegardes sont gérées en externe.

Options de stockage

Stockage local (par défaut)

Les sauvegardes sont stockées dans BACKUP_LOCAL_DIR (par défaut : /app/data/backups), mappé sur un volume Docker.

En production, les sauvegardes locales doivent être complétées par des copies distantes.

Stockage distant via rclone

Si BACKUP_RCLONE_REMOTE est configuré, les sauvegardes sont automatiquement téléversées vers une destination distante après leur création locale. rclone prend en charge plus de 70 fournisseurs de stockage cloud, dont les fournisseurs compatibles S3, Google Cloud Storage, Azure Blob, SFTP et WebDAV.

Téléchargement des sauvegardes

Le téléchargement des archives de sauvegarde nécessite une autorisation renforcée (step-up) :

  1. Accédez à Paramètres > Sauvegarde.
  2. Cliquez sur Télécharger sur l’entrée de sauvegarde.
  3. Saisissez votre mot de passe actuel (et le code TOTP si la MFA est activée).
  4. Le système émet une autorisation de téléchargement à usage unique et de courte durée (par défaut : 120 secondes).
  5. L’archive se télécharge automatiquement.

Le jeton d’autorisation n’est stocké que sous forme d’empreinte SHA-256. La création de l’autorisation et le téléchargement sont audités et les autres administrateurs sont notifiés.

Remarque : un téléchargement direct sans autorisation renvoie 403. Les sauvegardes sur stockage distant ne peuvent pas être téléchargées via le panneau d’administration.

Métadonnées et piste d’audit

Chaque sauvegarde est suivie dans la table backup_logs avec :

  • Statut (RUNNING, SUCCESS, FAILED)
  • Initiée par (SCHEDULER ou ADMIN)
  • Heure de début/de fin
  • Taille (octets) et somme de contrôle SHA-256
  • Destination (chemin local ou URL distante)
  • Version du manifeste et version de l’application source
  • Mode de chiffrement de l’archive et empreinte
  • Mode de charge utile de la base de données
  • Profil d’exécution
  • Statut de vérification (NOT_RUN, PASSED, FAILED, SKIPPED_TIMEOUT)
  • Statut du test de restauration et statut de compatibilité de restauration

Restauration

Utilisation de scripts/restore.sh

Le script de restauration canonique gère l’intégralité du cycle de vie :

COMPOSE_PROJECT_NAME=quotenode \
  COMPOSE_ENV_FILE=infra/.env.prod \
  RESTORE_AGE_IDENTITY_FILE=/path/to/quotenode-age-identity.txt \
  [email protected] \
  RESTORE_ADMIN_PASSWORD_FILE=/path/to/password \
  bash scripts/restore.sh /path/to/backup.tar.age

Le script :

  1. Extrait et valide l’archive (manifeste, sommes de contrôle, sécurité des entrées tar).
  2. Effectue des contrôles préalables (capacité disque, version de PostgreSQL, connectivité à la base de données).
  3. Crée une sauvegarde de sécurité de la cible avant toute opération destructrice.
  4. Écrit un journal de restauration au niveau de l’hôte (JSONL, qui survit au remplacement de la base de données).
  5. Restaure la base de données.
  6. Prépare les fichiers dans un répertoire temporaire et les met en place par échange.
  7. Préserve l’identité de la cible (ID d’instance, métadonnées de chiffrement des sauvegardes, état de la licence).
  8. Efface les sessions, les liens publics d’offres et les jetons de notification.
  9. Réinitialise ou force la modification de l’accès administrateur.
  10. Rechiffre les PII si la sauvegarde est PII_DECRYPTED et que la cible a ENCRYPT_PII=true.

Restauration entre instances

Lors de la restauration d’une sauvegarde provenant d’une autre installation, le mode par défaut est PRESERVE_TARGET_IDENTITY :

  • L’ID d’instance cible, le destinataire age des sauvegardes, les métadonnées du kit de récupération et l’état de la licence sont préservés.
  • Les sessions, les liens publics d’offres et les jetons de notification sont effacés.
  • Le mot de passe administrateur est réinitialisé/sa modification est forcée.
  • Les données métier sont entièrement remplacées (pas de fusion).

Compatibilité de restauration

Le contrôle préalable de restauration valide :

  • Sauvegarde plus ancienne vers une application plus récente : autorisé (compatible).
  • Sauvegarde plus récente vers une application plus ancienne : bloqué.
  • Manifeste manquant : bloqué.
  • Empreinte de clé de chiffrement incorrecte pour une charge utile APP_ENCRYPTED : bloqué avant tout travail destructeur.
  • PII_DECRYPTED sans chiffrement d’archive : bloqué.

Garantie d’accès administrateur

La restauration garantit l’accès administrateur via l’une des options suivantes :

  • RESTORE_ADMIN_PASSWORD_FILE — mot de passe fourni par l’opérateur (défini avec forcePasswordChange=true)
  • RESTORE_ADMIN_PASSWORD — pour un usage local/de test uniquement
  • Jeton de réinitialisation à usage unique généré automatiquement — affiché dans la console et dans le journal au niveau de l’hôte

Exécution à blanc (dry run)

RESTORE_DRY_RUN=true bash scripts/restore.sh /path/to/backup.tar.age

Valide l’archive, vérifie les sommes de contrôle et le manifeste, et signale le plan de restauration sans rien modifier.

Reprise après sinistre — reconstruction à partir de zéro

Votre serveur a disparu. Vous disposez d’une archive de sauvegarde (.tar.age ou .tar) et d’un nouveau serveur avec Docker. Voici comment effectuer la restauration.

Étape 1 — Préparer le nouveau serveur

mkdir quotenode && cd quotenode

Configurez docker-compose.yml et .env comme décrit dans le guide d’installation.

Critique pour les sauvegardes APP_ENCRYPTED : utilisez la même DB_ENCRYPTION_KEY que votre installation d’origine. Sans elle, les PII chiffrées seront définitivement illisibles.

Critique pour les archives chiffrées : vous avez besoin du fichier d’identité privée du kit de récupération (quotenode-age-identity.txt) pour déchiffrer l’archive.

Étape 2 — Copier la sauvegarde et le script de restauration

scp backup.tar.age user@new-server:~/quotenode/
scp scripts/restore.sh user@new-server:~/quotenode/

Étape 3 — Exécuter la restauration

RESTORE_AGE_IDENTITY_FILE=./quotenode-age-identity.txt \
  [email protected] \
  RESTORE_ADMIN_PASSWORD_FILE=./admin-password \
  ./restore.sh --fresh-install backup.tar.age

Étape 4 — Vérifier

Ouvrez votre domaine et connectez-vous. Tous les clients, offres, produits et paramètres devraient être exactement tels qu’ils étaient au moment de la sauvegarde.

Rétention des sauvegardes

  • Local : les BACKUP_RETENTION_DAILY (par défaut : 7) sauvegardes réussies les plus récentes sont conservées.
  • Distant : configurez des politiques de cycle de vie chez votre fournisseur de stockage.
  • Les sauvegardes avec PII déchiffrées devraient avoir une rétention plus stricte en raison de leur nature sensible.

Délai d’expiration des sauvegardes

Chaque sauvegarde a un délai d’expiration configurable (par défaut : 30 minutes via BACKUP_TIMEOUT_MINUTES). En cas de dépassement, la sauvegarde est interrompue et enregistrée comme FAILED.

Stratégie recommandée

  1. Activez les sauvegardes automatiques quotidiennes avec le chiffrement AGE_RECIPIENT et le stockage distant.
  2. Générez un kit de récupération lors de la configuration et conservez-le dans un emplacement hors ligne sécurisé.
  3. Vérifiez votre kit de récupération sur une sauvegarde réelle à l’aide des scripts de vérification inclus.
  4. Testez régulièrement les procédures de restauration — une sauvegarde qui n’a jamais été testée n’est pas une sauvegarde.
  5. Téléchargez une sauvegarde au moins une fois par mois vers un emplacement hors ligne (nécessite une autorisation renforcée).
  6. Surveillez l’état des sauvegardes dans le panneau d’administration — des avertissements apparaissent pour les sauvegardes obsolètes, échouées ou non chiffrées.
  7. Conservez au moins 7 jours d’historique de sauvegarde (la valeur par défaut).

Last reviewed: Recently