Aller au contenu principal
SécuritéCloudSecretsDevSecOps

HashiCorp Vault : les fondamentaux

30 min de lecture Sécurité Cloud — Chapitre 3

Pourquoi les secrets sont critiques, les principes d'une bonne gestion, et l'architecture complète de HashiCorp Vault (seal/unseal, auth, policies, secrets engines).

Pourquoi les secrets sont le maillon faible de toute infrastructure

Les secrets sont partout dans une infrastructure moderne : mots de passe de bases de données, clés API, certificats TLS, tokens OAuth, clés SSH, credentials de registres Docker. Et dans la majorité des organisations, ils sont gérés de manière artisanale — hardcodés dans le code, partagés sur Slack, copiés dans des fichiers .env qui finissent dans Git.

Le problème n’est pas théorique. En 2016, Uber a perdu 57 millions de comptes parce que des clés AWS traînaient dans un repo GitHub privé. En 2022, Samsung a vu ses clés de signing exposées dans un repo public. En 2023, CircleCI a dû demander à tous ses clients de roter leurs secrets après une exfiltration. GitHub scanne les repos publics et détecte plus de 12 millions de secrets exposés chaque année.

🔥 Cas réel : L’incident CircleCI de janvier 2023 est particulièrement instructif. Un malware a compromis le laptop d’un ingénieur, volé son cookie de session SSO, puis exfiltré les variables d’environnement et les clés de tous les projets clients. Résultat : chaque client CircleCI a dû considérer tous ses secrets comme compromis et les roter — un effort massif qui a pris des semaines pour certaines entreprises.

Les anti-patterns les plus courants sont toujours les mêmes : secrets en dur dans le code (password = "admin123"), variables d’environnement non chiffrées, fichiers .env committés (même gitignorés après coup, l’historique Git garde tout), secrets partagés par Slack ou email, même secret utilisé en dev/staging/prod, et secrets jamais rotés — la clé API qui a 3 ans et que 47 personnes connaissent.

Les six principes d’une gestion saine des secrets

Avant de plonger dans les outils, il faut comprendre les principes qui guident une bonne gestion des secrets. Ces principes sont indépendants de la technologie choisie :

1. CENTRALISATION
   → Un seul endroit fait autorité pour tous les secrets
   → Pas de copies dans des fichiers, des wikis ou des Slack

2. CHIFFREMENT
   → At rest (données stockées), in transit (réseau), en mémoire
   → La master key elle-même est protégée (HSM, KMS, Shamir)

3. ACCÈS CONTRÔLÉ (Least Privilege)
   → Chaque service accède uniquement aux secrets dont il a besoin
   → Les accès sont basés sur l'identité, pas sur le réseau

4. ROTATION AUTOMATIQUE
   → Les secrets ont une durée de vie limitée
   → La rotation est automatique, pas manuelle

5. AUDIT COMPLET
   → Chaque accès est loggé : qui, quoi, quand
   → Les anomalies déclenchent des alertes

6. INJECTION DYNAMIQUE
   → Les secrets arrivent au runtime, pas au build time
   → Jamais dans les images Docker, les manifests ou le code

🧠 À retenir : Si tu ne retiens qu’un principe, c’est celui-ci : les secrets ne doivent jamais être statiques. Un secret qui ne change jamais est un secret qui finira par fuiter. La rotation automatique avec des durées de vie courtes est la meilleure défense.

HashiCorp Vault : l’architecture en profondeur

Vault est le standard de facto pour la gestion des secrets en entreprise. C’est un système centralisé qui stocke, génère et contrôle l’accès aux secrets via une API unifiée. Comprendre son architecture est essentiel avant de l’opérer.

Vault s’organise autour de trois piliers : les Auth Methods (comment s’authentifier), les Secrets Engines (où et comment les secrets sont stockés/générés) et les Audit Devices (traçabilité complète).

Le mécanisme le plus critique est le Seal/Unseal. Quand Vault démarre, il est sealed — il connaît l’emplacement des données chiffrées mais ne possède pas la clé pour les déchiffrer. La master key est découpée via l’algorithme de Shamir’s Secret Sharing en N fragments (shares), et il faut M fragments pour reconstruire la clé et unsealer Vault :

# Initialisation de Vault (première fois uniquement)
# Crée 5 shares, 3 nécessaires pour unseal
vault operator init -key-shares=5 -key-threshold=3

# Output (À SAUVEGARDER SÉPARÉMENT ET EN SÉCURITÉ) :
# Unseal Key 1: abc123...  → Admin A (coffre-fort physique)
# Unseal Key 2: def456...  → Admin B (coffre-fort physique)
# Unseal Key 3: ghi789...  → Admin C (coffre-fort physique)
# Unseal Key 4: jkl012...  → Admin D (backup sécurisé)
# Unseal Key 5: mno345...  → Admin E (backup sécurisé)
# Initial Root Token: hvs.xyz789...

# Unseal — 3 admins différents saisissent leur share
vault operator unseal  # Admin A saisit sa clé
vault operator unseal  # Admin B saisit sa clé
vault operator unseal  # Admin C saisit sa clé

# Alternative production : Auto-unseal via cloud KMS
# AWS KMS, GCP Cloud KMS ou Azure Key Vault
# Vault délègue la protection de la master key au KMS

⚠️ Attention : Les unseal keys et le root token initial sont les actifs les plus critiques de ton infrastructure Vault. Ne les stocke jamais au même endroit. Distribue les shares à des personnes différentes, dans des coffres physiques séparés. Le root token doit être révoqué après la configuration initiale et régénéré uniquement en cas d’urgence.

Secrets Engines et Policies : le cœur de Vault

Vault organise les données en paths comme un système de fichiers. Chaque path est géré par un secrets engine spécifique :

# Activer le KV v2 (key-value avec versioning)
vault secrets enable -path=secret kv-v2

# Écrire un secret
vault kv put secret/production/database/postgres \
    username="app_user" \
    password="$(openssl rand -base64 32)" \
    host="postgres.internal" \
    port="5432"

# Lire un secret
vault kv get secret/production/database/postgres

# Lire un champ spécifique (pour les scripts)
vault kv get -field=password secret/production/database/postgres

# Historique des versions (KV v2 garde les anciennes versions)
vault kv metadata get secret/production/database/postgres

Les policies définissent qui peut accéder à quoi. Elles suivent le principe du least privilege — chaque service ne voit que ses propres secrets :

# policy-payment-service.hcl
# Le service payment peut lire ses propres secrets DB et Stripe
path "secret/data/production/database/postgres" {
  capabilities = ["read"]
}

path "secret/data/production/api-keys/stripe" {
  capabilities = ["read"]
}

# Peut obtenir des credentials dynamiques DB
path "database/creds/payment-readwrite" {
  capabilities = ["read"]
}

# Peut utiliser le transit engine pour chiffrer/déchiffrer
path "transit/encrypt/payment-data" {
  capabilities = ["update"]
}
path "transit/decrypt/payment-data" {
  capabilities = ["update"]
}

# INTERDIT explicitement : pas d'accès aux autres secrets
path "secret/data/production/api-keys/*" {
  capabilities = ["deny"]
}
# Appliquer la policy et créer un AppRole
vault policy write payment-service policy-payment-service.hcl

vault auth enable approle
vault write auth/approle/role/payment-service \
    token_policies="payment-service" \
    token_ttl=1h \
    token_max_ttl=4h \
    secret_id_ttl=24h

💡 Tip DevOps : Utilise capabilities = ["deny"] avec parcimonie — les deny rules sont évaluées en priorité et ne peuvent pas être override. Préfère une approche whitelist : ne donne accès qu’aux paths nécessaires plutôt que de tout ouvrir puis fermer.

Dynamic Secrets : la killer feature de Vault

Les dynamic secrets sont la fonctionnalité qui distingue Vault de tous les autres gestionnaires de secrets. Au lieu de stocker un mot de passe fixe, Vault génère des credentials à la volée avec une durée de vie limitée.

Le flux est simple : le service demande un accès à la base de données → Vault vérifie son identité et sa policy → Vault se connecte à PostgreSQL en admin → Vault crée un utilisateur éphémère avec les permissions exactes → Vault retourne les credentials avec un TTL de 1h → à l’expiration, Vault supprime automatiquement l’utilisateur.

# Configurer le database secrets engine
vault secrets enable database

vault write database/config/postgres \
    plugin_name=postgresql-database-plugin \
    allowed_roles="payment-readonly,payment-readwrite" \
    connection_url="postgresql://{{username}}:{{password}}@postgres.internal:5432/payments?sslmode=verify-full" \
    username="vault_admin" \
    password="vault_admin_password"

# Rôle avec credentials éphémères (TTL 1h, max 24h)
vault write database/roles/payment-readonly \
    db_name=postgres \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
        GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    revocation_statements="REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; \
        DROP ROLE IF EXISTS \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"

# Obtenir des credentials dynamiques — chaque appel crée un nouvel utilisateur
vault read database/creds/payment-readonly
# Key                Value
# lease_id           database/creds/payment-readonly/abc123
# lease_duration     1h
# username           v-payment-readonly-xyz123-1234567890
# password           A1b2C3d4-E5f6G7h8

🔥 Cas réel : Chez Adidas, la migration vers les dynamic secrets Vault a réduit le nombre de credentials de base de données à long terme de 800+ à 0. Chaque microservice obtient des credentials éphémères qui expirent en 1 heure. Quand un service est compromis, le blast radius est limité à 1 heure maximum — contre un accès illimité avec un mot de passe statique.

Intégration Kubernetes : Vault en production

Sur Kubernetes, le Vault Agent Injector est la méthode recommandée. Un sidecar container gère automatiquement l’authentification et l’injection des secrets dans le pod :

# Le Vault Agent Injector via annotations
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "payment-service"
        vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/payment-readonly"
        vault.hashicorp.com/agent-inject-template-db-creds: |
          {{- with secret "database/creds/payment-readonly" -}}
          export DB_USERNAME="{{ .Data.username }}"
          export DB_PASSWORD="{{ .Data.password }}"
          {{- end }}
    spec:
      serviceAccountName: payment-service
      containers:
        - name: app
          image: payment-service:latest
          command: ["sh", "-c", "source /vault/secrets/db-creds && ./start.sh"]

Le Vault Agent s’authentifie auprès de Vault en utilisant le service account Kubernetes du pod, obtient les secrets selon la policy, les écrit dans un volume partagé, et les renouvelle automatiquement avant expiration. L’application n’a jamais besoin de connaître Vault directement — elle lit simplement un fichier.

⚠️ Attention : Ne confonds pas le Vault Agent Injector (sidecar) avec le CSI Provider (volume driver). Le sidecar est plus flexible (il gère le renouvellement automatique), mais consomme plus de ressources. Le CSI Provider est plus léger mais ne gère pas le renouvellement — les secrets sont injectés une seule fois au démarrage du pod.

Résumé — Ce qu’il faut retenir

🧠 À retenir :

  • Les secrets statiques dans le code, les variables d’environnement ou les fichiers .env sont la première cause de breaches — centralise et chiffre tout
  • Vault utilise Shamir’s Secret Sharing pour protéger la master key — en production, utilise l’auto-unseal via cloud KMS
  • Les policies suivent le least privilege : chaque service accède uniquement à ses propres secrets, avec des capabilities explicites
  • Les dynamic secrets sont la killer feature : credentials générés à la demande, TTL court, supprimés automatiquement à l’expiration
  • Sur Kubernetes, le Vault Agent Injector gère l’authentification, l’injection et le renouvellement automatique des secrets
  • Audit everything : chaque accès à un secret doit être loggé et monitoré — c’est ta source de vérité en cas d’incident

🖥️ Pratique sur ton propre serveur

Pour suivre Sécurité Cloud en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.

Obtenir 200$

Articles liés