Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.snakysec.com/llms.txt

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

Runbook 02 — Restauration Vault depuis snapshot raft

1. Quand activer ce runbook

ScénarioActiver ?
Vault sealed automatiquement après reboot, refus d’unseal avec shares connuesOUI (corruption raft)
Volume vault-data corrompu ou suppriméOUI
Vault répond Internal error: storage backend corruptOUI
Vault opérationnel mais les secrets sont incorrectsNON (rotation des secrets, pas restore)
Plateforme exposée publiquement et Vault ne démarre pasOUI (priorité absolue, app ne peut pas démarrer sans Vault)

2. Objectifs

  • RPO atteint : 24 heures (snapshots quotidiens 04:00 UTC)
  • RTO cible : 1 heure
  • WRT cible : 30 minutes (vérification que app + worker peuvent lire Vault)

3. Prérequis CRITIQUES

  • Accès SSH au VPS OVH avec utilisateur mssp
  • 3 shares Shamir au minimum (threshold) :
    • Cas normal : DR Owner détient 3 shares (S1 YubiKey + S2 KeePass + S3 papier coffre domicile) → suffisant
    • Cas dégradé : si l’une des 3 est inaccessible, récupérer 1 share de l’enveloppe trustee notaire (cf. 01-keyholders.md)
  • Restic password disponible (KeePass + papier coffre + enveloppe trustee). Sans cette passphrase, le snapshot S3 est inutilisable.
  • DR S3 access keys OVH ou Scaleway (au moins l’un des deux)
  • Container mssp-vault arrêté (pas de Vault qui tente de servir pendant le restore)

4. Communication client

Identique au runbook 01 §4. Vault HS = plateforme entièrement HS (login, audits, rapports, tout). Notification immédiate.

5. Procédure de restauration

5.1 Sauvegarde de l’état actuel (si données partiellement présentes)

Même si le volume semble corrompu, on conserve avant tout effacement :
ssh mssp@vps.snakysec.com
sudo tar czf /opt/mssp/snapshots/vault-pre-restore-$(date +%Y%m%dT%H%M%SZ).tar.gz \
  -C /opt/mssp/data vault

5.2 Stop de toute la stack

L’application tente de lire Vault au démarrage — si on restaure Vault avec l’app vivante, on risque des reads sur Vault à moitié déverrouillé.
cd /opt/mssp/app/platform
make prod-down

5.3 Récupération du snapshot le plus récent

Le snapshot est dans le repo restic OVH au chemin vault/. La passphrase restic est requise. On utilise un container temporaire dr-runner.
# Démarrer Vault d'abord (pour que le container temporaire puisse contacter
# le service avant restore — Vault recevra le snapshot via API).
# NON. Vault ne doit PAS être démarré avant restore. Le restore se fait via
# `vault operator raft snapshot restore` qui REMPLACE le storage avant unseal.

# On lance un container restic standalone pour télécharger le snapshot
docker run --rm -it \
  --network platform_mssp-net \
  -v platform_dr-runtime:/dr-runtime \
  -v platform_mssp-approle-dr:/vault/approle-dr:ro \
  -v $(pwd)/../../scripts/dr:/dr:ro \
  -e VAULT_ADDR=http://mssp-vault:8200 \
  -e VAULT_DR_APPROLE_FILE=/vault/approle-dr/dr.env \
  registry.gitlab.com/snakysec/mssp-snakysec-multi-tenants/dr-runner:latest \
  /dr/restore/vault-restore.sh --download-only
Le script vault-restore.sh --download-only télécharge le snapshot le plus récent dans /dr-runtime/tmp/vault-snapshot-latest.snap. Si OVH inaccessible, ajouter --repo=scaleway :
... vault-restore.sh --download-only --repo=scaleway

5.4 Démarrage Vault avec storage VIDE (mode init)

Pour appliquer un snapshot raft, Vault doit être initialisé. Si le storage existant est corrompu, on le vide et on ré-initialise.
# Vider le volume vault-data
docker run --rm \
  -v platform_vault-data:/data \
  alpine sh -c "rm -rf /data/* /data/.[!.]* 2>/dev/null || true"

# Démarrer Vault (en mode prod, pas dev)
make vault-up
sleep 5

# Vault sera "Not initialized". Ré-initialiser avec 5/3 keyholders existants
# n'est PAS possible directement (init génère NOUVELLES shares). Il faut un
# init initial + apply snapshot avant unseal.

# Étape 1 : init temporaire avec 1 share (juste pour pouvoir poster le snapshot)
docker exec mssp-vault vault operator init -key-shares=1 -key-threshold=1 \
  > /tmp/vault-temp-init.json
TEMP_ROOT_TOKEN=$(grep "Initial Root Token" /tmp/vault-temp-init.json | awk '{print $NF}')
TEMP_UNSEAL_KEY=$(grep "Unseal Key 1" /tmp/vault-temp-init.json | awk '{print $NF}')

# Étape 2 : unseal temporaire
docker exec mssp-vault vault operator unseal "${TEMP_UNSEAL_KEY}"

5.5 Apply du snapshot

docker cp /dr-runtime/tmp/vault-snapshot-latest.snap mssp-vault:/tmp/snap.snap

docker exec -e VAULT_TOKEN="${TEMP_ROOT_TOKEN}" mssp-vault \
  vault operator raft snapshot restore -force /tmp/snap.snap

# À ce stade, Vault redevient "sealed" car les keys du snapshot précédent
# ne correspondent pas au TEMP_UNSEAL_KEY.

# Cleanup snapshot file (contient TOUS les secrets en clair)
docker exec mssp-vault shred -u /tmp/snap.snap

5.6 Unseal avec les 3 shares Shamir originales

Maintenant Vault attend les shares qui étaient actives au moment du snapshot.
# Saisir les 3 shares interactivement (NE PAS les loguer)
docker exec -it mssp-vault vault operator unseal
# Prompt 1 : entrer share 1 (depuis YubiKey décodée)
docker exec -it mssp-vault vault operator unseal
# Prompt 2 : entrer share 2 (depuis KeePass)
docker exec -it mssp-vault vault operator unseal
# Prompt 3 : entrer share 3 (depuis papier coffre domicile)

# Vérifier sealed=false
docker exec mssp-vault vault status
# attendu : Sealed: false
Si l’une des 3 shares ne fonctionne pas, DO NOT PANIC — récupérer l’enveloppe trustee chez le notaire (cf. 01-keyholders.md §4) et utiliser une share de cette enveloppe.

5.7 Re-provisioning AppRoles (si invalides après restore)

Les AppRoles mssp-app, mssp-worker, mssp-dr peuvent avoir des secret-ids expirées si le snapshot date de >TTL. Re-générer si nécessaire :
docker exec mssp-vault vault login "${OLD_ROOT_TOKEN_FROM_SNAPSHOT}"

# Régénérer secret-id pour mssp-app
NEW_SECRET_ID=$(docker exec mssp-vault \
  vault write -force -format=json auth/approle/role/mssp-app/secret-id | \
  jq -r .data.secret_id)

# Mettre à jour le shared volume
docker run --rm \
  -v platform_mssp-approle:/vault/approle \
  alpine sh -c "echo 'VAULT_ROLE_ID=...' > /vault/approle/app.env && \
                echo 'VAULT_SECRET_ID=${NEW_SECRET_ID}' >> /vault/approle/app.env"

# Idem pour mssp-worker et mssp-dr

6. Validation post-restore

6.1 Sanity check Vault

docker exec mssp-vault vault status
# Sealed: false, Initialized: true

docker exec -e VAULT_TOKEN="${ROOT_TOKEN}" mssp-vault \
  vault kv list mssp/
# attendu : platform, clients, dr

6.2 Smoke check secrets accessibles

docker exec -e VAULT_TOKEN="${ROOT_TOKEN}" mssp-vault \
  vault kv get -field=auth_secret mssp/platform | wc -c
# attendu : >32 (auth secret est >32 chars)

docker exec -e VAULT_TOKEN="${ROOT_TOKEN}" mssp-vault \
  vault kv get -field=encryption_key mssp/platform | wc -c
# attendu : >32

6.3 Démarrage application

make prod
sleep 30  # laisse les containers démarrer + AppRole login

curl -sI https://snakysec.com/api/health
# attendu : HTTP/2 200
Si l’app ne démarre pas, lire les logs make app-logs — les erreurs Vault les plus communes sont :
  • 403 Forbidden : AppRole secret-id expirée → re-provisionner (§5.7)
  • connection refused : Vault sealed → re-unseal
  • permission denied on path : policies AppRole modifiées dans le snapshot → re-créer

6.4 Smoke functional

Login plateforme + vérifier qu’on accède au dashboard. Si OK, restore validé.

7. Communication post-incident

Identique runbook 01 §8. Mention spécifique sur la fenêtre RPO 24h : si le snapshot date de plus de 12h, certains secrets clients récents (rotation cert, nouveau client ajouté) peuvent manquer. Lister ces clients dans le post-incident report et re-saisir les credentials manuellement après confirmation client.

8. Erreurs courantes et solutions

ErreurCauseSolution
Error checking seal status: connection refusedVault container pas démarrémake vault-up puis re-test
Error: read-only mode activeSnapshot apply en coursAttendre quelques secondes
mismatch: persisted leader != my-leaderSnapshot d’une autre instance VaultVérifier qu’on apply le bon snapshot
Unseal échoue avec “invalid share”Share corrompue ou erreur de saisieRéessayer avec une autre des 3 shares Nicolas, ou récupérer enveloppe trustee
Application start fails 403 sur VaultAppRole secret-id expirée post-restoreRe-provisionner secret-ids (§5.7)
Snapshot file corrompu (restic restore failed)Bucket OVH compromisSwitch vers Scaleway : vault-restore.sh --repo=scaleway

9. Validation du runbook

Testé annuellement (Q2) avec restore réel sur env pré-prod. Résultats dans docs/dr/test-results/.
VersionDateAuteur
1.02026-04-26Nicolas Schiffgens