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 — Chiffrement data-in-motion + data-at-rest

Owner: Plateforme · Audience: MSSP_ADMIN, ops VPS Statut: V1 (avril 2026)

TL;DR

Persistance — depuis avril 2026, le Vault pré-prod tourne en mode prod (file storage + Shamir 1/1 auto-unsealed). State, certs, KV, AppRole IDs survivent à tous les docker restart / docker compose recreate. Plus de re-bootstrap après chaque recreate. Cf. compose/vault.preprod.yml.
PlanComposantStatut V1Cipher / mode
In motionBrowser → AppTLS 1.3 (Let’s Encrypt prod / mkcert dev) via Traefik
App → Microsoft Graph / GitLab / SentryTLS 1.3 (forcé par les fournisseurs)
App → PostgresTLS 1.2+ (self-signed, sslmode=require)
App → RedisTLS 1.2+ (self-signed, rediss://)
App → VaultTLS 1.2+ (self-signed, https://)
Vault → PostgresTLS 1.2+ (sslmode=require)
Vault → RedisTLS 1.2+ (tls=true insecure_tls=true)
At restClientSecret valuesVault Transit AES-256-GCM-96 (rotation 90j)
Backups Postgres → S3 OVH/Scalewaypgbackrest aes-256-cbc (cipher pass dans Vault)
Vault dataShamir seal 5/3 (file storage chiffré)
Audit logintégrité Ed25519 hash chain (PAS confidentialité — par design)
Postgres data files⚠️LUKS au niveau VPS (cf. §2.4)
Redis RDB/AOF⚠️LUKS au niveau VPS (cf. §2.4)
Reports / artifacts disk⚠️volume Docker privé, LUKS au niveau VPS
Niveaux de menace couverts :
  • ✅ Snooping passif sur data-net (entre containers)
  • ✅ Vol de DB dump non chiffré
  • ✅ Compromission S3 backup (chiffré client-side avant upload)
  • ⚠️ Vol disque physique du VPS (mitigé par LUKS, à valider sur l’infra cible)
  • ❌ MITM actif sur data-net (mitigation Q3 via Vault PKI + verify-ca)

1 — In motion (TLS sur data-net interne)

1.1 — Postgres TLS

Cert : self-signed CN=postgres, SAN=postgres,localhost,127.0.0.1, validité 5 ans, généré au premier initdb par pg-tls-init.sh puis persisté dans postgres-data volume. Config : postgresql.auto.conf reçoit ssl = on, ssl_min_protocol_version = TLSv1.2. Clients :
  • App : ?sslmode=require dans toutes les DATABASE_URL (compose env, instrumentation.ts injection, entrypoint.sh reconstruction)
  • Vault database/config/postgres : connection_url='…?sslmode=require'
Vérification post-déploiement :
docker exec mssp-postgres psql -U mssp -d mssp_platform -c "SHOW ssl;"
# attendu: ssl | on

docker exec mssp-postgres psql "host=postgres port=5432 user=mssp password=$(...) dbname=mssp_platform sslmode=require" \
  -c "SELECT ssl_is_used();"
# attendu: t

# Côté app: les logs Prisma doivent mentionner "SSL"
docker logs mssp-app | grep -i ssl | head
Rotation : volume-persisté → cert change uniquement si server.crt/server.key sont supprimés du PGDATA puis le container redémarré. Q3 : passer en Vault PKI avec rotation 90j auto.

1.2 — Redis TLS

Cert : self-signed CN=redis, SAN=redis,localhost, validité 5 ans, généré au premier boot par redis-tls-entrypoint.sh dans /data/tls/. Config : Redis bind sur --tls-port 6379, port plain --port 0 (TLS-only). --tls-auth-clients no : pas de mTLS client cert (V1, à activer Q3 avec Vault PKI). Clients :
  • App : REDIS_URL=rediss://... (ioredis détecte automatiquement le scheme TLS)
  • Vault database/config/mssp-redis : tls=true insecure_tls=true
NODE_TLS_REJECT_UNAUTHORIZED=0 est déjà setté dans l’env de mssp-app, donc ioredis accepte le cert self-signed sans broncher. À retirer en Q3 quand on aura le cert CA pinné. Vérification :
# Connexion plain bloquée
docker exec mssp-redis redis-cli -p 6379 ping
# expected: "Error: Connection reset by peer" or similar (TLS expected)

# Connexion TLS OK
docker exec mssp-redis redis-cli --tls --insecure -a "$REDIS_PASSWORD" ping
# expected: PONG

1.3 — Vault HTTPS interne

Cert :
  • Dev : généré automatiquement par vault server -dev -dev-tls -dev-tls-cert-dir=/vault/tls
  • Prod : généré par entrypoint.sh au premier boot dans /vault/tls/vault.crt + /vault/tls/vault.key. Référencé dans vault-prod.hcl tls_cert_file. Persisté dans le volume Vault.
Config :
  • Listener : tls_cert_file + tls_key_file + tls_min_version=tls12 (vault-prod.hcl)
  • api_addr = "https://mssp-vault:8200" (vault-prod.hcl) — Vault advertise cette URL aux clients
Clients :
  • App + workers : VAULT_ADDR=https://mssp-vault:8200
  • NODE_TLS_REJECT_UNAUTHORIZED=0 couvre la vérification self-signed (idem Redis ci-dessus)
  • Pour le CLI dans le container : VAULT_SKIP_VERIFY=true (env compose)
  • Scripts entrypoint (sh) : wget --no-check-certificate / curl -k
Vérification :
# CLI inside Vault container
docker exec mssp-vault vault status

# CLI from host (skip verify because self-signed)
docker exec mssp-vault sh -c 'VAULT_ADDR=https://localhost:8200 VAULT_SKIP_VERIFY=true vault status'

# Plain HTTP must fail (listener is TLS-only)
docker exec mssp-vault sh -c 'VAULT_ADDR=http://localhost:8200 vault status' || echo "HTTP correctly refused"

1.4 — Externes (HTTPS forcé fournisseur)

  • Microsoft Graph : HTTPS only, certs Microsoft, TLS 1.2+
  • GitLab API (gitlab.com) : HTTPS, cert Let’s Encrypt
  • Sentry EU : HTTPS, cert Sentry
  • Browser ↔ App : Traefik termine TLS — Let’s Encrypt en prod (certresolver=letsencrypt), mkcert en dev

2 — At rest

2.1 — ClientSecret values (Vault Transit)

Voir vault-transit-migration.md et secrets-rotation.md §1. AES-256-GCM-96, clé jamais hors de Vault, auto-rotate 90j.

2.2 — Backups Postgres (pgbackrest)

platform/docker/postgres/pgbackrest.conf configure :
repo1-cipher-type=aes-256-cbc   # OVH Object Storage
repo2-cipher-type=aes-256-cbc   # Scaleway Object Storage
Le cipher pass est lu depuis Vault (PGBACKREST_REPO{1,2}_CIPHER_PASS) au moment du backup par postgres-pgbackrest.sh. Aucun backup chiffré en clair sur disque, OVH/Scaleway voient des bytes random. Rotation cipher pass : conservée pour les 4 derniers fulls (pgbackrest gère). Pour rotation manuelle : générer nouveau, écrire dans Vault, lancer un full backup. Les diff/incremental basés sur l’ancien full continuent à utiliser l’ancien cipher pass jusqu’à expiration.

2.3 — Vault data (Shamir seal)

Trois modes selon l’environnement :
ModeComposeStorageShamirUnsealData persiste
Prodvault.prod.ymlfile sur /opt/mssp/data/vault (LUKS)5 shares, 3 thresholdmanuel via 3 keyholders
Pré-prodvault.preprod.ymlfile dans Docker volume platform_vault-data1 share, 1 thresholdauto via init-keys.json
Dev (CI/tests)vault.dev.ymlin-memoryn/a (-dev mode)auto❌ ephemeral
Pré-prod = mêmes garanties de persistance que prod, sans la complexité opérationnelle des keyholders. C’est volontaire : le pré-prod doit refléter prod pour valider les déploiements + migrations sans rejouer le bootstrap à chaque restart. init-keys.json (master key + root token) vit dans le volume Docker platform_vault-data, lui-même protégé par le disk encryption du host (FileVault/BitLocker en dev, LUKS sur VPS en prod). Promotion pré-prod → prod : copier /vault/data/ vers /opt/mssp/data/vault/, re-seal avec 5 shares (vault operator rekey-init), distribuer fragments selon docs/dr/01-keyholders.md. Bootstrap one-shot (à faire une seule fois après make preprod) :
make vault-bootstrap-db   # provisions le user PG "vault" + wire database engine
Voir secrets-rotation.md §2 — Pré-requis pour les détails.

2.4 — Disk-level (LUKS sur VPS)

Recommandation prod :
# Au provisionnement initial du VPS (one-shot, avant install Docker) :
cryptsetup luksFormat /dev/<data-disk>
cryptsetup luksOpen   /dev/<data-disk> mssp-data
mkfs.ext4 /dev/mapper/mssp-data
mount /dev/mapper/mssp-data /opt/mssp/data

# Au boot du VPS, l'unlock peut être :
#   - manuel (3 min downtime, plus secure : passphrase tapée en SSH au reboot)
#   - automatique via TPM2 (clé dérivée du TPM)
#   - automatique via Tang/Clevis (network-bound encryption)

# /etc/crypttab pour TPM2 auto-unlock :
mssp-data UUID=<luks-uuid> none luks,tpm2-device=auto,tpm2-pcrs=0+7
Couvre :
  • ✅ Vol disque physique du VPS
  • ✅ Snapshot de volume cloud (le snapshot est chiffré)
  • ✅ Récupération de disque en cas de panne hardware
Ne couvre pas :
  • ❌ Compromission OS du VPS (root sur l’OS = lecture clair)
  • ❌ Memory dump (RAM contient les secrets unsealed)
Volumes Docker à mettre derrière LUKS :
  • /opt/mssp/data/postgres (PGDATA)
  • /opt/mssp/data/redis
  • /opt/mssp/data/vault (storage Shamir)
  • /opt/mssp/data/approle (AppRole credentials)
  • /opt/mssp/data/artifacts (PDF/Excel/JSON reports)
  • /opt/mssp/data/archive (audit retention 12 mois)
Volumes mac dev : FileVault au niveau OS suffit (déjà actif par défaut sur macOS récent). Volumes Windows dev : BitLocker au niveau disque (vérifier dans Settings → Update & Security).

2.5 — Reports / artifacts disk (V1 plain)

Les rapports générés (PDF exécutif, Excel, HTML) atterrissent en clair dans reports-data Docker volume. Mitigation V1 : LUKS sur le disque VPS. Q3 : chiffrer chaque rapport via Vault Transit avant écriture sur disque, déchiffrer à la lecture. Coût : 2 round-trips Vault par téléchargement de rapport. Vaut le coup pour les rapports CERTIFICATION (signed Ed25519), pas pour les TRACKING (vivants, régénérables).

3 — Procédures rotation

Cert TLS Postgres (5y validity)

Trop long pour V1 (les 5y évitent un cycle de rotation à orchestrer). Q3 : Vault PKI émet des certs 90j auto-renouvelés. Forcer une rotation manuelle :
docker exec mssp-postgres bash -c '
  PGDATA_DIR="/var/lib/postgresql/data"
  rm -f "$PGDATA_DIR/server.crt" "$PGDATA_DIR/server.key"
'
docker compose restart postgres
# Le hook initdb ne re-run pas (PGDATA déjà initialisée). Re-générer manuellement:
docker exec mssp-postgres /docker-entrypoint-initdb.d/02-pg-tls-init.sh
docker compose restart postgres

Cert TLS Redis

Idem Postgres, supprimer /data/tls/server.{crt,key} puis restart redis.

Cert TLS Vault

Idem, supprimer /vault/tls/vault.{crt,key} puis restart vault. Attention : restart Vault prod = re-seal + besoin d’unseal manuel via les 3 fragments Shamir.

4 — Roadmap Q3

ChantierSortie deEffort
Vault PKI engine pour certs internes (verify-ca + rotation 90j auto)self-signed + skip-verifyMoyen
Drop NODE_TLS_REJECT_UNAUTHORIZED=0 global, pin certs explicitementenv globaleFaible
Reports disk encryption via Transitdisk plainMoyen
mTLS Redis (--tls-auth-clients yes)TLS unilatéralFaible
Postgres sslmode=verify-full + cert pinningsslmode=requireFaible (post-PKI)