> ## 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.

# 02 restore vault from snapshot

# Runbook 02 — Restauration Vault depuis snapshot raft

## 1. Quand activer ce runbook

| Scénario                                                                      | Activer ?                                                       |
| ----------------------------------------------------------------------------- | --------------------------------------------------------------- |
| Vault sealed automatiquement après reboot, refus d'unseal avec shares connues | **OUI** (corruption raft)                                       |
| Volume `vault-data` corrompu ou supprimé                                      | **OUI**                                                         |
| Vault répond `Internal error: storage backend corrupt`                        | **OUI**                                                         |
| Vault opérationnel mais les secrets sont incorrects                           | **NON** (rotation des secrets, pas restore)                     |
| Plateforme exposée publiquement et Vault ne démarre pas                       | **OUI** (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](../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 :

```bash theme={null}
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é.

```bash theme={null}
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.

```bash theme={null}
# 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` :

```bash theme={null}
... 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.

```bash theme={null}
# 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

```bash theme={null}
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.

```bash theme={null}
# 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](../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 :

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
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

| Erreur                                           | Cause                                  | Solution                                                                      |
| ------------------------------------------------ | -------------------------------------- | ----------------------------------------------------------------------------- |
| `Error checking seal status: connection refused` | Vault container pas démarré            | `make vault-up` puis re-test                                                  |
| `Error: read-only mode active`                   | Snapshot apply en cours                | Attendre quelques secondes                                                    |
| `mismatch: persisted leader != my-leader`        | Snapshot d'une autre instance Vault    | Vérifier qu'on apply le bon snapshot                                          |
| Unseal échoue avec "invalid share"               | Share corrompue ou erreur de saisie    | Réessayer avec une autre des 3 shares Nicolas, ou récupérer enveloppe trustee |
| Application start fails 403 sur Vault            | AppRole secret-id expirée post-restore | Re-provisionner secret-ids (§5.7)                                             |
| Snapshot file corrompu (restic restore failed)   | Bucket OVH compromis                   | Switch 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/`.

| Version | Date       | Auteur             |
| ------- | ---------- | ------------------ |
| 1.0     | 2026-04-26 | Nicolas Schiffgens |
