Aller au contenu

PD-47 — Expression de Besoin

Contexte

ProbatioVault repose sur PostgreSQL (OVH Managed) pour les métadonnées transactionnelles : utilisateurs, documents, enveloppes probatoires, journaux append-only, ancrages Merkle. Si les documents sont redondés multi-cloud (OVH S3 WORM + AWS Glacier), les métadonnées PostgreSQL constituent le cœur transactionnel du système.

Une perte ou corruption de la base compromettrait : - La capacité à relier un document à son propriétaire - La reconstruction des enveloppes probatoires - La vérification probatoire (Merkle, tx_id, journaux append-only) - L'auditabilité complète exigée par NF Z42-013

Problème

Il n'existe actuellement aucun mécanisme de backup automatisé pour PostgreSQL. La redondance documentaire (S3 WORM + Glacier) ne protège pas les métadonnées. La résilience transactionnelle est incomplète.

Les exigences sont : 1. Résilience : RPO 24h pour pg_dump, PITR entre deux dumps via WAL archiving 2. Confidentialité : Aucune donnée en clair persistante — double chiffrement (applicatif + SSE) 3. Rotation : Rétention glissante 30 jours avec lifecycle S3 4. Traçabilité : Journal append-only de chaque événement de backup 5. Restauration : Procédure testable et vérifiable en environnement staging

Solution proposée

Stratégie hybride pg_dump + WAL archiving

Composant Mode Fréquence RPO
pg_dump -Fc -Z 9 Full logique compressé Quotidien 03h00 CET 24h
WAL archiving Continu (archive_command) Temps réel < 5 min
Basebackup Full physique Hebdomadaire (dimanche 02h00) N/A (base PITR)

Le pg_dump quotidien fournit un point de restauration garanti. Le WAL archiving permet le PITR (Point-In-Time Recovery) entre deux dumps.

Chiffrement double couche

Couche Mécanisme Clé Responsabilité
Applicatif AES-256-GCM K_backup dérivée via HKDF, sous enveloppe HSM ProbatioVault
Transport/Stockage SSE-KMS côté S3 KMS OVH/AWS Fournisseur cloud

Invariant : Aucun dump en clair ne persiste sur disque > 5 minutes. Le fichier temporaire est supprimé immédiatement après chiffrement et upload.

Scheduling : Prefect + cron watchdog

Composant Rôle Détail
Prefect flow (agent dédié) Orchestrateur principal Déclenche backup, gère retries, alerting, monitoring dashboard
Cron watchdog Filet de sécurité Vérifie que le backup du jour existe dans S3. Si absent → alerte + relance

Le backup est indépendant du backend NestJS : même si le backend est down, le backup s'exécute.

Stockage S3

  • Bucket : probatiovault-db-backups
  • Arborescence : {year}/{month}/backup_{YYYYMMDD}.enc + wal/{segment}.enc
  • Versioning : Activé
  • Chiffrement : SSE-KMS activé
  • Accès : IAM dédié, aucun accès public
  • Lifecycle : Suppression automatique > 30 jours, transition Glacier optionnelle après 7 jours

Logging probatoire

Chaque backup génère un événement dans le journal append-only interne :

{
  "event_type": "db_backup",
  "timestamp": "2026-03-02T03:00:00Z",
  "backup_id": "bkp-20260302-full",
  "backup_type": "pg_dump|wal|basebackup",
  "size_bytes": 123456789,
  "hash_sha3_256": "abc123...",
  "encryption": "AES-256-GCM+SSE-KMS",
  "status": "SUCCESS|FAILED",
  "duration_ms": 45000
}

Décision : Pas d'ancrage Merkle/blockchain pour les événements de backup. Journal interne uniquement.

Périmètre

Inclus

  • pg_dump quotidien compressé + chiffré
  • WAL archiving continu + chiffré
  • Basebackup hebdomadaire
  • Upload S3 bucket dédié avec IAM isolé
  • Politique lifecycle S3 : 30 jours
  • Prefect flow + cron watchdog
  • Script de restauration documenté
  • Logging append-only
  • Tests de restauration (staging)

Exclus

  • Sauvegarde des fichiers S3 (redondance multi-cloud existante)
  • Snapshot infra Terraform
  • Sauvegarde Redis (non critique, reconstituable)
  • Ancrage Merkle des événements de backup (décision PO)

Exigences non fonctionnelles

Critère Exigence
Confidentialité Double chiffrement (applicatif + SSE)
Traçabilité Journal append-only
Durée max < 15 min pour DB < 50 GB
RPO pg_dump 24h
RPO WAL < 5 min
RTO cible < 2h
Restauration Procédure testée trimestriellement

Invariants de sécurité

  1. INV-47-01 : Aucune donnée en clair ne persiste sur disque > 5 minutes
  2. INV-47-02 : Clé K_backup séparée de K_master (dérivation HKDF dédiée)
  3. INV-47-03 : Accès IAM isolé — aucun accès public au bucket
  4. INV-47-04 : Logging immuable (append-only) de chaque opération
  5. INV-47-05 : Cron watchdog indépendant du flow Prefect (filet de sécurité)

Critères d'acceptation

  • Backup automatique pg_dump visible chaque jour dans S3
  • WAL archivés en continu dans S3
  • Fichiers chiffrés (double couche) vérifiables
  • Rotation 30j effective (lifecycle S3)
  • Restauration testée en environnement staging (schéma + données intègres)
  • Hash SHA3-256 cohérent après restauration
  • Journal probatoire généré pour chaque opération
  • Prefect flow opérationnel + dashboard monitoring
  • Cron watchdog détecte un backup manquant et alerte

Cas d'erreur

Cas Action
Échec pg_dump Retry x3 (backoff exponentiel) + alerte Prefect
Échec upload S3 Retry x3 + alerte
Fichier > taille attendue Alerte (anomalie potentielle)
Hash mismatch post-upload Annulation + alerte critique
Échec chiffrement Abort — aucun upload de donnée en clair
WAL archiving interrompu Alerte + reprise automatique

Learnings injectés

Story Learning Impact PD-47
PD-5 Vault Lock vs Object Lock ; Deep Archive restoration 12-48h Lifecycle S3 : préférer Object Lock (réversible)
PD-264 Atomicité : clarifier DB sync vs post-commit async (BullMQ) Backup = opération infra async, hors transaction applicative
PD-8 Vault PostgreSQL backend, TLS end-to-end, AppRole K_backup sous enveloppe HSM via Vault AppRole

Décisions validées (PO)

# Question Décision
D1 WAL ou pg_dump ? Hybride : pg_dump quotidien + WAL archiving
D2 Chiffrement applicatif ou SSE ? Double : applicatif AES-256-GCM + SSE-KMS
D3 Ancrage Merkle ? Non — journal interne uniquement
D4 Scheduling ? Prefect flow (agent dédié) + cron watchdog