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 :
- 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. - 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.gzavec des chemins relatifs. - Manifeste de sauvegarde — un fichier
backup-manifest.jsoncontenant 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. - Sommes de contrôle d’intégrité — un fichier
checksums.sha256contenant 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=NONEBACKUP_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 combinaisonNONE+PII_DECRYPTEDest 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’empreintequotenode-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 :
- Compare la somme de contrôle SHA-256 de l’archive chiffrée à la valeur attendue ou au fichier annexe
.sha256. - Déchiffre avec
age -ddans un répertoire temporaire privé. - Valide les entrées tar (rejette les chemins absolus,
.., les liens symboliques et les fichiers de périphérique). - Vérifie que
backup-manifest.jsonexiste et possède une version prise en charge. - Exécute
sha256sum -c checksums.sha256. - Exécute
pg_restore --list db.dumplorsquepg_restoreest disponible. - Supprime par défaut les données temporaires déchiffrées (utilisez
--keep-decryptedpour 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_dumpau 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 où 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_MODEdétermine quel conteneur exécute réellement le job de sauvegarde planifié.BACKUP_RUNTIME_PROFILEest 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 :
- Annonce une maintenance planifiée (période de grâce configurable).
- Active éventuellement une page de maintenance statique pendant l’arrêt des services.
- Arrête le frontend, le backend et le backup-worker (maintient PostgreSQL en fonctionnement).
- Exécute une sauvegarde ponctuelle.
- Redémarre tous les services.
- Vérifie l’état du backend.
- É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 viaBACKUP_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) :
- Accédez à Paramètres > Sauvegarde.
- Cliquez sur Télécharger sur l’entrée de sauvegarde.
- Saisissez votre mot de passe actuel (et le code TOTP si la MFA est activée).
- Le système émet une autorisation de téléchargement à usage unique et de courte durée (par défaut : 120 secondes).
- 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 :
- Extrait et valide l’archive (manifeste, sommes de contrôle, sécurité des entrées tar).
- Effectue des contrôles préalables (capacité disque, version de PostgreSQL, connectivité à la base de données).
- Crée une sauvegarde de sécurité de la cible avant toute opération destructrice.
- Écrit un journal de restauration au niveau de l’hôte (JSONL, qui survit au remplacement de la base de données).
- Restaure la base de données.
- Prépare les fichiers dans un répertoire temporaire et les met en place par échange.
- Préserve l’identité de la cible (ID d’instance, métadonnées de chiffrement des sauvegardes, état de la licence).
- Efface les sessions, les liens publics d’offres et les jetons de notification.
- Réinitialise ou force la modification de l’accès administrateur.
- Rechiffre les PII si la sauvegarde est
PII_DECRYPTEDet que la cible aENCRYPT_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_DECRYPTEDsans 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 avecforcePasswordChange=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_KEYque 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
- Activez les sauvegardes automatiques quotidiennes avec le chiffrement
AGE_RECIPIENTet le stockage distant. - Générez un kit de récupération lors de la configuration et conservez-le dans un emplacement hors ligne sécurisé.
- Vérifiez votre kit de récupération sur une sauvegarde réelle à l’aide des scripts de vérification inclus.
- Testez régulièrement les procédures de restauration — une sauvegarde qui n’a jamais été testée n’est pas une sauvegarde.
- Téléchargez une sauvegarde au moins une fois par mois vers un emplacement hors ligne (nécessite une autorisation renforcée).
- Surveillez l’état des sauvegardes dans le panneau d’administration — des avertissements apparaissent pour les sauvegardes obsolètes, échouées ou non chiffrées.
- Conservez au moins 7 jours d’historique de sauvegarde (la valeur par défaut).