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.
Runbook — Auto-rotation des secrets
Owner: Plateforme · Audience: MSSP_ADMIN avec accès Vault root
Statut: V1 (avril 2026)
Résumé exécutif
| Type de secret | Engine Vault | TTL | Rotation | Effort opérateur |
|---|
| Clés Transit (Encryption) | transit/ | — | Auto-rotate 90j | Aucun |
| Postgres app creds | database/ (postgresql-database-plugin) | 7j (max 30j) | À chaque boot d’un conteneur | Restart hebdo |
| Redis app creds | database/ (redis-database-plugin) | 7j (max 30j) | À chaque boot d’un conteneur | Restart hebdo |
| GitLab PAT | KV v2 + worker BullMQ | 90j | Worker worker-gitlab-rotator mensuel | Aucun |
AUTH_SECRET, certs Entra | KV v2 | — | Manuel | Cf. key-rotation.md |
1 — Clés Transit (Encryption)
Vault Transit garde la clé maître en interne ; l’app ne fait que des appels HTTP transit/encrypt|decrypt. Auto-rotate 90j configuré au boot de Vault via docker/vault/init.sh :
vault write transit/keys/mssp-platform/config auto_rotate_period=2160h
vault write transit/keys/mssp-clients/config auto_rotate_period=2160h
Vault crée automatiquement une nouvelle version (vault:v2:, vault:v3: …). Tous les ciphertexts existants restent déchiffrables sans migration applicative ; seule la version active utilisée pour les nouvelles encryptions change.
Vérifier :
docker exec mssp-vault vault read transit/keys/mssp-clients
# auto_rotate_period: 2160h
# latest_version: 1 ← s'incrémente après 90j
Pour forcer une rotation manuelle (urgence) :
docker exec mssp-vault vault write -f transit/keys/mssp-clients/rotate
Pour rewrap tous les ciphertexts vers la dernière version (optionnel, n’affecte pas le déchiffrement) :
node platform/scripts/rewrap-legacy-secrets.mjs --dry-run
node platform/scripts/rewrap-legacy-secrets.mjs
2 — Postgres dynamic credentials
Architecture
Boot Runtime (every lease/4 sec) Max TTL (30j)
──── ────────────────────────── ──────────────
GET database/creds/ → PUT sys/leases/renew → Vault refuses renewal
mssp-app (lease prolongé +7j) → Sentry alert
{ username: v-token-... → restart container
password: ... → fresh creds
lease_duration: 168h }
Pré-requis (one-time setup)
- Postgres : créer un user
vault avec CREATEROLE + grant du rôle mssp_app_role.
- Vault :
vault_pg_password dans mssp/data/platform, engine database/ configuré.
Bootstrap (run après make preprod initial OU après reset de la KV Vault) :
# 1. Génère un password dédié vault → Postgres
VAULT_PG_PASS=$(openssl rand -hex 32)
# 2. Stocke dans Vault
docker exec mssp-vault vault kv patch mssp/platform vault_pg_password="$VAULT_PG_PASS"
# 3. Crée user Postgres (idempotent)
docker exec mssp-postgres psql -U mssp -d mssp_platform <<SQL
DO \$\$
BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'vault') THEN
CREATE ROLE vault WITH LOGIN CREATEROLE PASSWORD '$VAULT_PG_PASS';
ELSE
ALTER ROLE vault WITH PASSWORD '$VAULT_PG_PASS';
END IF;
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'mssp_app_role') THEN
CREATE ROLE mssp_app_role INHERIT;
END IF;
END
\$\$;
GRANT CONNECT ON DATABASE mssp_platform TO mssp_app_role;
GRANT USAGE ON SCHEMA public TO mssp_app_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO mssp_app_role;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO mssp_app_role;
ALTER DEFAULT PRIVILEGES FOR ROLE mssp IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO mssp_app_role;
ALTER DEFAULT PRIVILEGES FOR ROLE mssp IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO mssp_app_role;
GRANT mssp_app_role TO vault WITH ADMIN TRUE, INHERIT TRUE, SET TRUE;
SQL
# 4. Re-run init.sh pour configurer le database engine
docker exec mssp-vault sh /vault/init.sh
Activer le mode dynamique côté app
# .env (dev) ou docker-compose env (prod)
VAULT_DYNAMIC_DB=true
Recreate les conteneurs (make preprod / make app-rebuild). Au boot :
[INFO] Vault dynamic Postgres creds issued { leaseId, role, leaseDurationSec: 604800 }
[INFO] Vault secrets injected { injected: 2, dynamicDb: true, dynamicRedis: false }
Vérifier la rotation
# Lister les leases actifs
docker exec mssp-vault vault list sys/leases/lookup/database/creds/mssp-app
# Inspecter un lease
docker exec mssp-vault vault read sys/leases/lookup lease_id=<id>
Toutes les 168/4h ≈ 42h, l’app appelle sys/leases/renew automatiquement (platform/src/lib/vault-lease-renewer.ts). Logs :
[INFO] Vault lease renewed { leaseId, label: "database/mssp-app", newTtlSec: 604800 }
Restart hebdo
Vault refuse de renouveler après max_ttl=720h (30j). À ce moment, l’app doit avoir été redémarrée pour fetch des creds frais. Cadence recommandée : deploy/restart hebdomadaire, soit via :
- pipeline GitLab CI nightly/weekly redeploy
- cron host :
0 4 * * 0 docker compose restart (dimanche 04:00)
Si le restart est manqué, Sentry alerte via la signature vault-lease-renewer quand TTL < 24h.
3 — Redis dynamic credentials
Identique à Postgres mais via le plugin redis-database-plugin du même engine database/. Les rôles sont nommés avec suffixe -redis pour ne pas collisionner :
database/roles/mssp-app-redis
database/roles/mssp-worker-redis
Activer
# .env
VAULT_DYNAMIC_REDIS=true
Le password Redis admin (mssp/platform.redis_password) reste utilisé par Vault pour créer/révoquer les users dynamiques. Il n’est plus utilisé par l’app au runtime quand VAULT_DYNAMIC_REDIS=true.
ACL Redis
V1 = [+@all, ~*] (toutes commandes, toutes clés). À tightener Q3 vers [+@read, +@write, +@stream, ~*] (BullMQ utilise les streams).
4 — GitLab PAT — worker BullMQ mensuel
GitLab n’a pas d’engine Vault dynamique. Le worker gitlab-token-rotator-worker.ts rotates le PAT le 15 de chaque mois à 03:00 UTC :
// Pseudo-code
const self = await GET /personal_access_tokens/self
const rotated = await POST /personal_access_tokens/{self.id}/rotate
{ expires_at: now + 90j }
await putSecret("mssp/platform", { ...secrets, gitlab_token: rotated.token })
POST /rotate est atomique : nouveau token créé + ancien révoqué dans la même requête.
Audit log
Chaque rotation émet un événement secret.rotate dans platform_audit_log (chaîne intègre Ed25519). Filtrer par :
SELECT created_at, after FROM platform_audit_log
WHERE action = 'secret.rotate'
AND resource_label = 'mssp/platform.gitlab_token'
ORDER BY created_at DESC
LIMIT 10;
Alertes Sentry
| Signal | Niveau |
|---|
| Token expire dans <14j (cycle de rotation manqué) | warning |
POST /rotate échoue | error + captureException |
Token introuvable via /self (déjà révoqué ?) | error |
Forcer une rotation manuelle
Cas : fuite de token, lockdown urgence.
docker exec mssp-app node /app/dist/workers/gitlab-token-rotator-worker.js
# Ou via BullMQ board : POST job "rotate" sur queue "gitlab-token-rotator"
Note : le worker logue dans platform_audit_log même en run manuel.
5 — Garde-fous communs
Test post-rotation
Après chaque rotation (auto ou manuelle), vérifier :
# 1. Health endpoint
curl -fs http://localhost:3000/api/health
# attendu: {"db":"ok","redis":"ok"}
# 2. Lease actif et frais
docker exec mssp-vault vault list sys/leases/lookup/database/creds/mssp-app
# 3. Pas d'erreurs Sentry sur composant vault-*
Rollback Vault Transit
Vault Transit conserve toutes les versions (min_decryption_version=1). Pas de risque de perte de données — un rewrap raté laisse les anciens ciphertexts utilisables.
Rollback dynamic DB / Redis
Désactiver le flag :
# .env
VAULT_DYNAMIC_DB=false
VAULT_DYNAMIC_REDIS=false
Recreate les conteneurs → fallback automatique sur les passwords statiques de mssp/data/platform.
Rollback GitLab PAT
# Lire la version précédente dans Vault KV v2 (10 versions retenues par défaut)
docker exec mssp-vault vault kv get -version=N mssp/platform | grep gitlab_token
# Réécrire en version courante
docker exec mssp-vault vault kv patch mssp/platform gitlab_token="<old_token>"
6 — Calendrier de bascule
| Date | Action | Owner |
|---|
| 2026-04-30 | Phase 0/1/2/3 mergée sur main, code activable via flags | Dev |
| 2026-05-15 | Phase 1 (Postgres) bascule sur preprod, monitoring 2 semaines | Ops |
| 2026-06-01 | Phase 1 + 2 + 3 bascule en prod, alertes Sentry actives | Ops |
| 2026-09-01 | Premier auto-rotate Transit observé (90j depuis init) | — |
| 2026-Q3 | Tightening ACL Redis (@read+@write+@stream) | Dev |