Retour aux articles
Use Case

Protéger les données sensibles entre les étapes d'un pipeline

Les pipelines multi-étapes font circuler des secrets entre les jobs. Voici comment nous gérons les valeurs chiffrées et les jetons à usage unique pour que les identifiants restent protégés du build au déploiement.

·Par The KeyHarbour Team

Le problème avec les secrets dans les pipelines

Les pipelines multi-étapes ont un problème de distribution des secrets.

Un job de build lit un identifiant de base de données. Un job de test a besoin du même identifiant. Un job de déploiement a besoin d’un jeton d’amorçage pour initialiser la nouvelle infrastructure. Chaque étape a besoin de quelque chose de sensible.

La plupart des équipes résolvent ce problème en collant les identifiants dans des variables d’environnement CI et en laissant le runner les injecter à l’exécution. L’approche fonctionne, mais la surface d’exposition grandit à chaque étape. Les variables d’environnement apparaissent dans les logs, dans l’interface de configuration du pipeline et dans les sorties d’erreur. Elles persistent d’une exécution à l’autre. Un identifiant stocké de cette façon en janvier est toujours là en juillet, et personne ne le remarque souvent.

Quand un pipeline s’exécute sur trois ou quatre étapes, le même secret a touché quatre contextes d’exécution distincts et est potentiellement journalisé dans chacun d’eux.

Deux outils pour deux problèmes distincts

KeyHarbour adresse ce problème avec deux mécanismes qui fonctionnent ensemble.

Le premier est le chiffrement côté client. Un secret est chiffré sur votre machine avant d’être envoyé au serveur. Le serveur ne stocke que le texte chiffré. Chaque étape du pipeline déchiffre la valeur localement en utilisant une clé qui ne quitte jamais votre environnement CI.

Le second est les valeurs à usage unique. Lorsqu’une clé est marquée comme à usage unique, KeyHarbour la supprime dès que la première lecture est complète. Une seconde lecture retourne un 404. Pour les jetons destinés à être utilisés exactement une fois, c’est une garantie opérationnelle, pas seulement une convention de nommage.

La mise en place

Générez une clé AES-256 et stockez la chaîne hexadécimale dans votre fournisseur CI comme variable protégée nommée KH_ENCRYPTION_KEY. C’est le seul secret qui doit vivre dans votre configuration CI.

openssl rand -hex 32

Stockez votre identifiant de base de données en version chiffrée avec la CLI kh :

export KH_ENCRYPTION_KEY=<votre-clé-hexadécimale>

kh kv set DATABASE_URL "postgres://user:pass@db.prod.example.com/app" \
  --workspace <workspace-uuid> \
  --encrypt

Stockez le jeton d’amorçage comme valeur chiffrée à usage unique :

kh kv set BOOTSTRAP_ADMIN_TOKEN "eyJhbGc..." \
  --workspace <workspace-uuid> \
  --encrypt \
  --one-time-only

Le serveur contient maintenant deux blocs chiffrés. Aucune valeur n’est lisible sans KH_ENCRYPTION_KEY.

Le pipeline en trois étapes

Voici un workflow GitHub Actions concret qui met les deux mécanismes en pratique.

name: Deploy

on:
  push:
    branches: [main]

env:
  KH_TOKEN: ${{ secrets.KH_TOKEN }}
  KH_ENDPOINT: ${{ secrets.KH_ENDPOINT }}
  KH_ENCRYPTION_KEY: ${{ secrets.KH_ENCRYPTION_KEY }}

jobs:
  test:
    name: Tests d'intégration
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Installer kh CLI
        run: go install github.com/key-harbour/kh/cmd/kh@latest
      - name: Lancer les tests d'intégration
        run: |
          export DATABASE_URL=$(kh kv get DATABASE_URL --encrypt)
          pytest tests/integration/

  deploy:
    name: Déploiement
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - name: Installer kh CLI
        run: go install github.com/key-harbour/kh/cmd/kh@latest
      - name: Appliquer Terraform
        run: |
          export DATABASE_URL=$(kh kv get DATABASE_URL --encrypt)
          terraform apply -auto-approve

  bootstrap:
    name: Amorçage
    runs-on: ubuntu-latest
    needs: deploy
    steps:
      - uses: actions/checkout@v4
      - name: Installer kh CLI
        run: go install github.com/key-harbour/kh/cmd/kh@latest
      - name: Initialiser l'environnement
        run: |
          ADMIN_TOKEN=$(kh kv get BOOTSTRAP_ADMIN_TOKEN --encrypt)
          ./scripts/initialize-environment.sh "$ADMIN_TOKEN"

KH_TOKEN, KH_ENDPOINT et KH_ENCRYPTION_KEY sont définis une seule fois sous Settings > Secrets and variables > Actions dans le dépôt GitHub. Aucun identifiant n’apparaît dans le YAML.

Étape 1 : Tests d’intégration

Le job de test récupère l’identifiant de base de données et le déchiffre localement. La valeur en clair n’existe qu’en mémoire pendant la durée du job. Rien n’est écrit sur le disque ni dans les logs du pipeline.

Étape 2 : Déploiement

Le job de déploiement récupère le même identifiant de façon indépendante. Les deux étapes partagent la clé de chiffrement via la variable CI, pas l’une par rapport à l’autre. Un échec à l’étape de test n’affecte pas la capacité de l’étape de déploiement à récupérer son identifiant.

Étape 3 : Amorçage

Le job d’amorçage lit BOOTSTRAP_ADMIN_TOKEN une seule fois. Dès que la lecture est complète, KeyHarbour supprime la clé. Si l’étape d’amorçage échoue et qu’un développeur relance le job, la seconde lecture retourne un 404. Cela force une décision délibérée : recréer le jeton, confirmer que la première exécution n’a pas partiellement réussi, puis réessayer.

C’est le comportement attendu. Un jeton destiné à être utilisé une seule fois ne devrait pas survivre silencieusement à une boucle de relance.

Ce que cela change

Les identifiants dans les pipelines CI sont généralement un risque partagé. Toute personne ayant accès à la configuration du pipeline voit la variable. Les logs la conservent d’une exécution à l’autre. Une erreur dans un script l’affiche.

Avec les valeurs chiffrées et les jetons à usage unique :

  • Le serveur ne stocke que du texte chiffré. Une compromission côté serveur n’expose pas vos identifiants.
  • Les jetons destinés à un seul usage sont détruits lors de cet usage. Le rejeu n’est pas possible.
  • Chaque étape récupère exactement ce dont elle a besoin, déchiffre localement et détient la valeur en clair uniquement pendant la durée du job.
  • Un échec lors d’une relance sur un jeton à usage unique signale que la première tentative s’est au moins partiellement exécutée.

Le YAML du pipeline reste propre. Le seul secret dans le magasin de secrets de votre fournisseur CI est la clé de chiffrement.

Pour commencer

# Authentification
kh auth login --token <api-token> --endpoint https://app.keyharbour.ca/api/v2

# Générer et exporter votre clé de chiffrement
export KH_ENCRYPTION_KEY=$(openssl rand -hex 32)

# Stocker un secret chiffré
kh kv set MON_SECRET "valeur" --workspace <uuid> --encrypt

# Récupérer et déchiffrer
kh kv get MON_SECRET --encrypt

La référence complète de la CLI est disponible sur docs.keyharbour.ca. Contactez-nous à info@keyharbour.ca pour toute question.

devopssecretsci-cdsecuritypipelines