Aller au contenu

ProbatioVault - Architecture Stockage Hybride

PD-4: OVH S3 (HOT) + AWS Glacier Deep Archive (COLD + COMPLIANCE)


1. Vue d'Ensemble

1.1 Objectifs Architecture

┌──────────────────────────────────────────────────────────────┐
│              Architecture Hybride Multi-Cloud                │
│                                                              │
│  OVH S3 (HOT)               ←→          AWS Glacier (COLD)  │
│  ├─ Accès rapide < 100ms           ├─ Object Lock COMPLIANCE│
│  ├─ Coût modéré                    ├─ Durabilité 11 9's     │
│  └─ Région EU (Gravelines)         └─ Coût minimal          │
└──────────────────────────────────────────────────────────────┘

Objectifs: 1. ✅ Performance: Accès rapide OVH S3 (< 100ms latence) 2. ✅ Conformité: Object Lock COMPLIANCE AWS (WORM hardware-backed) 3. ✅ Coût: Optimisation Glacier Deep Archive (~$1/TB/mois) 4. ✅ Disponibilité: 99.99% SLA OVH + 99.999999999% AWS 5. ✅ Souveraineté: Données EU (Gravelines + Paris)

1.2 Décision Architecture: Pourquoi Hybride?

Pourquoi pas OVH uniquement? - ❌ OVH ne supporte PAS Object Lock S3 natif - ❌ Impossible d'implémenter WORM hardware-backed sur OVH - ❌ NF Z42-013 requiert immutabilité garantie

Pourquoi pas AWS uniquement? - ⚠️ Latence accès Glacier (restore 12-48h) - ⚠️ Coût S3 Standard élevé pour stockage HOT ($23/TB/mois) - ⚠️ Verrouillage fournisseur (vendor lock-in)

Solution hybride:

Upload → OVH S3 (HOT, fast access)
      → Worker async → AWS Glacier (COLD, compliance)
      → Best of both worlds


2. Architecture Détaillée

2.1 Flux d'Upload Document

┌─────────┐
│ Client  │ (React Native app)
│ Mobile  │
└────┬────┘
     │ 1. Upload fichier
     │    + metadata (type, tags)
┌──────────────────┐
│ Backend NestJS   │
│                  │
│ 2. Chiffrement   │───────────┐
│    AES-256-GCM   │           │ K_doc (unique par document)
│    K_doc derived │           │ dérivée de K_master_user
│    from K_master │◄──────────┘ (PD-35: Key Wrapping)
└────┬─────────────┘
     │ 3. Upload OVH S3 HOT
     │    (documents-hot bucket)
┌──────────────────────────────┐
│ OVH Object Storage (GRA)     │
│                              │
│ Bucket: documents-hot        │
│ ├─ Versioning: Enabled       │
│ ├─ Encryption: AES-256 (auto)│
│ ├─ Latency: < 100ms          │
│ └─ Cost: ~$10/TB/mois        │
└────┬─────────────────────────┘
     │ 4. Event → Queue (PD-6)
     │    (async replication)
┌──────────────────────────────┐
│ Worker Replication (PD-6)    │
│ (Background job)             │
│                              │
│ 5. Download from OVH         │
│ 6. Upload to AWS Glacier     │
│    + Object Lock COMPLIANCE  │
└────┬─────────────────────────┘
     │ 7. Upload AWS S3
     │    + Object Lock metadata
┌────────────────────────────────────────┐
│ AWS S3 Glacier Deep Archive (eu-west-3)│
│                                        │
│ Bucket: documents-cold                 │
│ ├─ Object Lock: COMPLIANCE (50 ans)   │
│ ├─ Versioning: Enabled (requis)       │
│ ├─ Encryption: AES-256 (S3 Managed)   │
│ ├─ Lifecycle: DEEP_ARCHIVE (90 jours) │
│ ├─ Durability: 99.999999999%          │
│ └─ Cost: ~$1/TB/mois                  │
└────────────────────────────────────────┘

2.2 Flux de Lecture Document

┌─────────┐
│ Client  │
└────┬────┘
     │ 1. GET /documents/:id
┌──────────────────┐
│ Backend NestJS   │
│                  │
│ 2. Check cache   │──NO─┐
│    (Redis)       │     │
└────┬─────────────┘     │
     │ YES               │
     │ 3a. Return        │
     │     from cache    │
     │                   │
     │◄──────────────────┘
     │ 3b. Download OVH S3
     │     (HOT storage)
┌──────────────────────────────┐
│ OVH Object Storage           │
│                              │
│ 4. GetObject                 │
│    (< 100ms latency)         │
└────┬─────────────────────────┘
     │ 5. Decrypt AES-256-GCM
     │    with K_doc
┌──────────────────┐
│ Client           │
│ (fichier clair)  │
└──────────────────┘

Note: Si document pas dans OVH (purge manuelle),
      fallback sur AWS Glacier restore (12-48h)

2.3 Flux Audit Logs (Append-Only)

┌──────────────────┐
│ Backend NestJS   │
│                  │
│ Event:           │
│ - User login     │
│ - Document access│
│ - Key rotation   │
│ - Device revoked │
└────┬─────────────┘
     │ 1. Log event
     │    (structured JSON)
┌────────────────────────────────────────┐
│ AWS S3 Audit Logs (eu-west-3)          │
│                                        │
│ Bucket: audit-logs                     │
│ ├─ Object Lock: COMPLIANCE (50 ans)   │
│ ├─ Storage Class: S3 STANDARD         │
│ │  (pas de transition Glacier pour    │
│ │   accès rapide si audit externe)    │
│ ├─ Bucket Policy: DENY DELETE         │
│ ├─ IAM Policy: Write-Only (append)    │
│ └─ Versioning: Enabled                │
└────────────────────────────────────────┘

Security:
- Backend peut uniquement PutObject (append)
- Lecture logs réservée aux admins (IAM tag Role=Admin)
- Suppression IMPOSSIBLE avant 50 ans (Object Lock COMPLIANCE)

3. Buckets et Configuration

3.1 Buckets OVH S3 (HOT)

Bucket Usage Versioning Lifecycle Encryption
documents-hot Documents utilisateurs (accès rapide) ✅ Enabled ❌ None (kept forever) ✅ AES-256 auto
backups Backups base de données ❌ Disabled ✅ Expire 30 jours ✅ AES-256 auto
temp-uploads Uploads temporaires (staging) ❌ Disabled ✅ Expire 7 jours ✅ AES-256 auto

Région: GRA (Gravelines, France) Endpoint: https://s3.gra.cloud.ovh.net Credentials: S3 Access Key / Secret Key (OVH Cloud Project)

3.2 Buckets AWS S3/Glacier (COLD)

Bucket Usage Object Lock Storage Class Lifecycle
documents-cold Documents WORM (compliance) ✅ COMPLIANCE 50 ans S3 Standard → Glacier Deep Archive (90j) ✅ Auto transition
audit-logs Audit logs append-only ✅ COMPLIANCE 50 ans S3 Standard (no transition) ✅ Expire 50 ans
s3-access-logs Logs d'accès S3 (metadata) ❌ Disabled S3 Standard ✅ Expire 90 jours

Région: eu-west-3 (Paris, France) Endpoint: https://s3.eu-west-3.amazonaws.com Credentials: IAM User probatiovault-backend-dev (Access Key / Secret Key)


4. Sécurité et Isolation

4.1 Chiffrement Multi-Couches

┌──────────────────────────────────────────────────────────┐
│ Fichier Original (document.pdf)                         │
└──────────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Couche 1: Chiffrement Client (Backend)                  │
│ ├─ Algorithme: AES-256-GCM                              │
│ ├─ Clé: K_doc (dérivée de K_master_user)               │
│ ├─ IV: Unique par document (12 bytes random)           │
│ └─ Output: document.pdf.enc (chiffré)                  │
└──────────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Couche 2: TLS 1.2+ (Transport)                          │
│ ├─ Cipher: TLS_AES_256_GCM_SHA384                      │
│ ├─ Certificats: Let's Encrypt (auto-renew)             │
│ └─ HSTS: Activé (force HTTPS)                          │
└──────────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Couche 3: Chiffrement au repos (S3/Glacier)             │
│ ├─ OVH: AES-256 (automatic, S3 managed)                │
│ ├─ AWS: AES-256 (S3 Managed Keys, SSE-S3)              │
│ └─ Bucket Key: Activé (optimisation coûts KMS)         │
└──────────────────────────────────────────────────────────┘

Résultat: Triple chiffrement (AES-256 client + TLS + AES-256 serveur)

4.2 Isolation Réseau

┌─────────────────────────────────────────────────────────┐
│                  Internet Public                        │
└────────────────┬────────────────────────────────────────┘
                 │ HTTPS uniquement (TLS 1.2+)
        ┌────────┴────────┐
        │                 │
        ▼                 ▼
┌──────────────┐  ┌──────────────┐
│  OVH S3      │  │  AWS S3      │
│  (Public)    │  │  (Public)    │
│              │  │              │
│ Bucket       │  │ Bucket       │
│ Policy:      │  │ Policy:      │
│ - DENY HTTP  │  │ - DENY HTTP  │
│ - Private    │  │ - DENY       │
│   (no public │  │   Public     │
│    access)   │  │   Access     │
└──────────────┘  └──────────────┘
        ▲                 ▲
        │                 │
        │ Credentials:    │ Credentials:
        │ OVH S3 Keys     │ AWS IAM User
        │                 │
        └────────┬────────┘
        ┌────────┴────────┐
        │                 │
        │  Backend NestJS │
        │  (VPS OVH)      │
        │                 │
        │ Security:       │
        │ - Firewall UFW  │
        │ - SSH Key only  │
        │ - Fail2ban      │
        └─────────────────┘

4.3 IAM Policies (Least Privilege)

Backend AWS IAM User:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DocumentsColdReadWrite",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:PutObjectRetention",
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::probatiovault-documents-cold-dev",
        "arn:aws:s3:::probatiovault-documents-cold-dev/*"
      ]
    },
    {
      "Sid": "AuditLogsWriteOnly",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:PutObjectRetention"
      ],
      "Resource": [
        "arn:aws:s3:::probatiovault-audit-logs-dev/*"
      ]
    }
  ]
}

Permissions NON accordées (sécurité): - ❌ s3:DeleteObject (impossible de supprimer) - ❌ s3:DeleteBucket (impossible de supprimer bucket) - ❌ s3:PutBucketVersioning (impossible de désactiver versioning) - ❌ s3:PutObjectLockConfiguration (impossible de modifier Object Lock) - ❌ s3:BypassGovernanceRetention (impossible de bypass retention)


5. Scalabilité et Performance

5.1 Capacité

Métrique OVH S3 AWS S3/Glacier
Stockage max Illimité Illimité
Fichier max 5 TB 5 TB
Débit upload 100 MB/s 100 MB/s
Débit download 100 MB/s 12-48h restore (Glacier)
IOPS 3500 IOPS N/A (archive)

5.2 Latence

Opération OVH S3 HOT AWS S3 COLD
Upload < 100 ms < 200 ms
Download < 100 ms 12-48h restore
List Objects < 50 ms < 50 ms
Head Object < 20 ms < 20 ms

5.3 Optimisations Performance

Multipart Upload (fichiers > 100 MB):

// Backend upload service
async uploadLargeDocument(file: Buffer, key: string) {
  const partSize = 100 * 1024 * 1024; // 100 MB parts

  // Initiate multipart upload
  const uploadId = await this.s3Client.createMultipartUpload({
    Bucket: 'documents-hot',
    Key: key,
  });

  // Upload parts en parallèle (max 5 threads)
  const parts = await Promise.all(
    chunks(file, partSize).map((chunk, index) =>
      this.s3Client.uploadPart({
        Bucket: 'documents-hot',
        Key: key,
        PartNumber: index + 1,
        UploadId: uploadId,
        Body: chunk,
      })
    )
  );

  // Complete multipart upload
  await this.s3Client.completeMultipartUpload({
    Bucket: 'documents-hot',
    Key: key,
    UploadId: uploadId,
    MultipartUpload: { Parts: parts },
  });
}

Cache Redis (lecture fréquente):

async getDocument(documentId: string) {
  // 1. Check cache Redis
  const cached = await this.redis.get(`doc:${documentId}`);
  if (cached) {
    return Buffer.from(cached, 'base64');
  }

  // 2. Download from OVH S3
  const s3Object = await this.s3Client.getObject({
    Bucket: 'documents-hot',
    Key: documentId,
  });

  // 3. Cache 1 hour (documents chiffrés)
  await this.redis.setex(
    `doc:${documentId}`,
    3600,
    s3Object.Body.toString('base64')
  );

  return s3Object.Body;
}


6. Coûts Estimés

6.1 Coûts OVH S3 (HOT)

Ressource Volume Prix unitaire Coût mensuel
Stockage 1 TB ~€0.01/GB/mois €10
Requêtes PUT 100k €0.005/1k req €0.50
Requêtes GET 1M €0.004/10k req €0.40
Transfert sortant 500 GB €0.01/GB €5
TOTAL ~€16/mois

6.2 Coûts AWS S3/Glacier (COLD)

Ressource Volume Prix unitaire Coût mensuel
S3 Standard (90j) 1 TB $0.023/GB/mois $23
Glacier Deep Archive 1 TB $0.00099/GB/mois $1
Object Lock 1M objets Inclus $0
Requêtes PUT 100k $0.005/1k $0.50
Restore Glacier 10 GB/mois $0.02/GB $0.20
Transfert sortant 50 GB $0.09/GB $4.50
TOTAL (après 90j) ~$6/mois

6.3 Économies Architecture Hybride

Scénario: 10 TB stockés, 90% archives (accès rare)

Solution Coût mensuel Économie
AWS S3 Standard uniquement $230 -
OVH S3 uniquement (❌ pas Object Lock) $100 -
Hybride OVH HOT (1 TB) + AWS COLD (9 TB) $16 + $9 = $25 -89%

7. Monitoring et Observabilité

7.1 Métriques CloudWatch (AWS)

Métriques S3:
├─ BucketSizeBytes (taille bucket)
├─ NumberOfObjects (nombre objets)
├─ 4xxErrors (erreurs client)
├─ 5xxErrors (erreurs serveur)
└─ AllRequests (total requêtes)

Alarmes configurées:
├─ Object Lock violations (tentatives suppression)
├─ Glacier transition errors (échecs lifecycle)
└─ Insecure access (requêtes non-HTTPS)

7.2 Logs d'Accès S3

Localisation: s3://probatiovault-s3-access-logs-dev/

Format:
documents-cold-logs/2024/01/19/access-log-*.txt
audit-logs-logs/2024/01/19/access-log-*.txt

Rétention: 90 jours (lifecycle auto-expire)

Champs loggés:
- Bucket, Key (ressource accédée)
- Operation (GET, PUT, DELETE)
- RequestDateTime, ResponseStatus
- BytesSent, BytesReceived
- UserAgent, IPAddress

7.3 Dashboard Grafana (Recommandé PD-XX)

┌─────────────────────────────────────────────────┐
│        ProbatioVault Storage Dashboard         │
├─────────────────────────────────────────────────┤
│                                                 │
│ ┌─────────────┐  ┌─────────────┐               │
│ │ OVH Hot     │  │ AWS Cold    │               │
│ │ 1.2 TB      │  │ 8.9 TB      │               │
│ └─────────────┘  └─────────────┘               │
│                                                 │
│ ┌──────────────────────────────────────────┐   │
│ │ Upload Rate (last 24h)                   │   │
│ │ ▁▂▃▅▄▃▂▁▂▃▅▇▆▅▄▃▂▁                      │   │
│ └──────────────────────────────────────────┘   │
│                                                 │
│ ┌──────────────────────────────────────────┐   │
│ │ Object Lock Compliance Status            │   │
│ │ ✅ 1,234,567 objects locked              │   │
│ │ ⚠️  123 objects pending lock             │   │
│ └──────────────────────────────────────────┘   │
│                                                 │
│ ┌──────────────────────────────────────────┐   │
│ │ Storage Cost (monthly)                   │   │
│ │ OVH: $16 | AWS: $9 | Total: $25         │   │
│ └──────────────────────────────────────────┘   │
└─────────────────────────────────────────────────┘

8. Disaster Recovery

8.1 Scénarios de Panne

Panne OVH S3:

Impact: Uploads impossibles, lectures impossibles
Durée: SLA 99.9% → max 43 min/mois

Mitigation:
1. Fallback automatique vers AWS S3 (lecture seulement)
2. Glacier restore (12-48h délai)
3. Queue uploads (retry automatique quand OVH up)

Panne AWS S3:

Impact: Compliance logs impossibles, réplication impossible
Durée: SLA 99.99% → max 4 min/mois

Mitigation:
1. Logs temporaires en base PostgreSQL
2. Sync vers AWS quand service up
3. Pas d'impact utilisateur (lectures depuis OVH HOT)

8.2 Backup et Restore

Base de données (metadata):

# Backup quotidien PostgreSQL → OVH S3
pg_dump probatiovault | gzip > backup-$(date +%Y%m%d).sql.gz
s3cmd put backup-*.sql.gz s3://probatiovault-backups-dev/

# Lifecycle: Expire après 30 jours

Documents (fichiers):

Réplication:
OVH S3 → AWS Glacier = Backup automatique
99.999999999% durabilité AWS = Risque perte négligeable

Restore:
1. Metadata depuis backup PostgreSQL
2. Fichiers depuis AWS Glacier (restore 12-48h)


9. Migration et Évolution

9.1 Migration Future vers Multi-Region

Phase 1 (actuel):

OVH GRA (Gravelines, France)
AWS eu-west-3 (Paris, France)

Phase 2 (future):

OVH GRA (Gravelines) + OVH UK (Londres)
AWS eu-west-3 (Paris) + AWS eu-west-2 (Londres)

→ Geo-replication automatique
→ Haute disponibilité multi-région

9.2 Évolution vers S3 Intelligent-Tiering

Actuel: Transition fixe (90 jours → Glacier Deep Archive)

Future: S3 Intelligent-Tiering

Automatic cost optimization:
- Frequent Access Tier (< 30 jours)
- Infrequent Access Tier (30-90 jours)
- Archive Instant Access Tier (90-180 jours)
- Deep Archive Tier (> 180 jours)

Compatible avec Object Lock COMPLIANCE


Document rédigé le: 2024-01-19 Version: 1.0 Auteur: Claude Code (PD-4) Prochaine révision: 2024-04-19 (trimestrielle)