Skip to main content

Documentation Index

Fetch the complete documentation index at: https://snakysec.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Test de restore E2E — runbook mensuel

Pourquoi ce test

Les backups Postgres (pgbackrest) + Vault (raft snapshot via restic) + artifacts (restic) tournent quotidien. Sans test régulier de restore, on ne sait pas s’ils sont restorables. Le RTO et RPO documentés dans docs/dr/03-backup-strategy.md sont théoriques tant que personne n’a vérifié. Le test E2E mensuel :
  • Spin up des cibles éphémères (Postgres + Vault containers, volumes tmpfs, jamais persistés)
  • Restore le dernier backup de chaque asset depuis OVH (fallback Scaleway si OVH down)
  • Smoke test : compte les tables Postgres, vérifie que le snapshot Vault est intact, sample les artifacts
  • Mesure le RTO réel + le RPO du backup utilisé
  • Écrit un rapport markdown horodaté dans docs/dr/test-restore-reports/YYYY-MM-DD.md

Cibles documentées

AssetRTO targetRPO target
Postgres≤ 4h≤ 24h (full quotidien + diff hourly post-V2)
Vault≤ 30min≤ 24h (snapshot quotidien)
Artifacts≤ 2h≤ 24h (restic quotidien)
Si le rapport mesure RTO > 6h ou RPO > 36h sur un asset → investigation. Cf. §Troubleshooting.

Quand le lancer

V1 — Manuel mensuel par l’opérateur (le 1er du mois, après les backups Sentinel).
cd /chemin/vers/mssp-snakysec-multi-tenants
bash scripts/dr/restore/test-full-restore.sh
Durée typique : 5-15 minutes selon la taille des backups. V2 (futur, Q3+) — Automatisation. Deux paths possibles :
  1. Host cron (Linux/macOS) : ajouter à /etc/cron.monthly/ :
    #!/bin/bash
    cd /opt/mssp/repo && bash scripts/dr/restore/test-full-restore.sh \
      >> /var/log/dr-test-restore.log 2>&1
    
  2. Windows Task Scheduler : tâche déclenchée le 1er du mois 06:00, action = bash.exe scripts/dr/restore/test-full-restore.sh.
  3. Ofelia : décommenter le job dans platform/docker/ofelia/config.ini après avoir ajouté un dr-test-runner image avec docker CLI + accès au docker.sock via socket-proxy.

Pré-requis

Avant de lancer le test :
  1. Le stack pré-prod doit tourner : make preprod. Au minimum mssp-vault et mssp-dr-runner doivent être up.
  2. DR_BACKUPS_ENABLED=true au moins une fois dans le passé pour qu’il y ait des backups dans les buckets. Sinon le test détecte « no snapshot found » et exit en SKIP.
  3. Les credentials DR sont peuplés dans mssp/dr/* (pas REPLACE_ME). Vérifier :
    docker exec -e VAULT_TOKEN=$(docker exec mssp-vault sh -c 'cat /vault/data/init-keys.json' | python -c "import sys,json;print(json.load(sys.stdin)['root_token'])") \
      -e VAULT_SKIP_VERIFY=true mssp-vault vault kv get mssp/dr
    
    Tous les fields doivent être set (pas REPLACE_ME, pas vides).
  4. Les ports 5444 + 8244 doivent être libres sur l’host (utilisés par les containers éphémères du test).

Lecture d’un rapport

Format docs/dr/test-restore-reports/YYYY-MM-DD.md :
# DR test restore — 2026-05-02

**Run started:** 2026-05-02T08:30:00Z
**Run finished:** 2026-05-02T08:42:34Z
**Total RTO (wall-clock):** 754s
**Overall status:** **OK**

## Per-asset results

| Asset | Status | RPO (lag from latest backup) |
|---|---|---|
| Postgres (pgbackrest) | **OK** | 5h32min |
| Vault (raft snapshot)  | **OK** | 4h30min |
| Artifacts (restic)     | **OK** | — |
Lecture rapide :
  • Overall status = OK + RTO < 4h + RPO < 24h → Tout va bien, garde le rapport pour audit.
  • Overall status = FAIL → Au moins un asset n’a pas pu être restoré → §Troubleshooting.
  • Status = SKIP → Un asset n’avait pas de backup à restorer (typique en pré-prod si DR_BACKUPS_ENABLED a toujours été off).

Troubleshooting

”DR secret ‘mssp/dr/X’ is missing or REPLACE_ME”

Les credentials DR ne sont pas tous renseignés dans Vault. Lance :
cd platform && make vault-bootstrap-secrets
# Catégorie 8/8 — DR storage

“Ephemeral Postgres did not become ready within 60s”

Soit l’image postgres:16-alpine n’est pas dispo, soit le port 5444 est occupé. Vérifier :
docker ps --filter "publish=5444"   # devrait être vide
docker pull postgres:16-alpine     # warm le cache image

“Postgres restore appears empty (0 tables)”

Le restore pgbackrest ne s’est pas appliqué correctement. Causes courantes :
  • pgbackrest n’est pas pré-installé dans l’image postgres:16-alpine éphémère utilisée pour le test (le script tente apk add mais ça échoue offline). Mitigation V2 : utiliser l’image custom mssp-postgres:16-pgbackrest qui a déjà pgbackrest baked in.
  • Le cipher pass diverge entre Vault et le backup (rotation accidentelle = backup illisible). Crisis : tu ne peux pas récupérer les backups antérieurs à la rotation.
Investigation :
cat /tmp/dr-test-<run-id>/pg-restore.log

“No restic snapshot found for vault”

Pas de snapshot Vault dans le bucket OVH. Vérifier :
docker exec mssp-dr-runner sh -c '
  AWS_ACCESS_KEY_ID=$(vault kv get -field=ovh_s3_access_key mssp/dr) \
  AWS_SECRET_ACCESS_KEY=$(vault kv get -field=ovh_s3_secret_key mssp/dr) \
  aws --endpoint-url=https://s3.gra.io.cloud.ovh.net \
    s3 ls s3://mssp-backup-ovh/vault/ | tail
'
Si vide → vault-snapshot.sh n’a jamais tourné avec succès. Force un run :
docker exec mssp-cron ofelia exec --config=/etc/ofelia/config.ini --job=vault-snapshot

“RTO > 6h sur Postgres”

Le restore est lent. Causes possibles :
  • Le bucket OVH est congestionné ou la liaison Internet de l’host est limitée.
  • Trop de WAL à rejouer depuis le full (post-Day 80 le WAL accumulé peut être volumineux).
  • Le test tourne pendant que d’autres backups poussent en parallèle.
Mitigation : lancer le test à une heure off-peak (ex 14h le 1er du mois).

Sentry monitor dr-test-restore rouge

Le script appelle sentry_check_in dr-test-restore <status> à la fin (lecture du SENTRY_DSN dans le secret partagé). Si le monitor passe en rouge :
  1. Lire le rapport markdown du jour
  2. Reproduire en local : bash scripts/dr/restore/test-full-restore.sh à la main
  3. Si rouge persistant > 2 mois consécutifs → escalation : la promesse de DR est cassée, faut reconstruire la chaîne de backup (full sync + verify).

Rétention des rapports

Les rapports sont versionnés dans Git (docs/dr/test-restore-reports/). Pas de purge — c’est notre piste d’audit pour les certifications futures (NIS2, ISO 27001 §A.17 plan de continuité). Coût stockage = négligeable (~5 KB par rapport, 12 par an). L’index docs/dr/test-restore-reports/index.md peut être généré périodiquement :
cd docs/dr/test-restore-reports
{
  echo "# Index des rapports test restore"
  echo ""
  ls -1 *.md | grep -v "^index.md$" | sort -r | head -36 | while read f; do
    date=$(basename "$f" .md)
    overall=$(grep -m1 "Overall status" "$f" | sed -E 's/.*\*\*([A-Z]+)\*\*.*/\1/')
    rto=$(grep -m1 "Total RTO" "$f" | sed -E 's/.*: ([0-9]+s).*/\1/')
    echo "- [${date}](${f}) — ${overall} (RTO ${rto})"
  done
} > index.md

Éviter les pièges

  • Ne jamais lancer le test pendant un backup réel (collision OVH/Scaleway list operations). Le test est volontairement programmé après dr-verify (06:30 UTC) pour éviter le créneau full backup (02:00).
  • Ne jamais ALTER les buckets OVH/Scaleway depuis le test. Read-only seulement. Si le test fait écrire = bug à fixer immédiatement.
  • Le test ne valide PAS la chaîne de Shamir keyholders Vault — c’est un autre runbook (docs/dr/01-keyholders.md) avec un test annuel manuel séparé.

Décisions historiques

DateDécisionRaison
2026-05-02V1 = manual run, V2 = Ofelia integration via dr-test-runner imageSimplicité initiale, éviter docker-socket-proxy dans le scope V1
2026-05-02Vault restore = check intégrité du snapshot file (restic restore OK), pas apply complet dans ephemeral VaultApply nécessite Shamir unseal complexe, hors scope V1