Gestion des secrets - 07 - Secrets dans CI/CD : GitLab, GitHub et les bonnes pratiques

Injecter des secrets dans tes pipelines CI/CD. Variables GitLab, GitHub secrets, masquage et bonnes pratiques.

07 - Secrets dans CI/CD : GitLab, GitHub et les bonnes pratiques

Ce que tu vas apprendre

  • Configurer les variables CI/CD dans GitLab et les secrets dans GitHub Actions
  • Les options de protection et de masquage
  • Les erreurs classiques qui font fuiter des secrets dans les logs

Prerequisites

Un pipeline CI/CD fonctionnel. Si tu pars de zero, commence par la serie sur le déploiement avec GitLab CI.


Les secrets dans le pipeline

Ton code a besoin de secrets pour tourner. Cle API, mot de passe de base de donnees, credentials SMTP. En local, tu as un .env. En production, il faut que ces secrets arrivent dans ton conteneur Docker au moment du déploiement, sans passer par git.

C'est le rôle des variables CI/CD : des secrets stockes dans la plateforme (GitLab, GitHub) et injectes dans l'environnement d'exécution du pipeline.

Sur paltemps.fr, j'utilise GitLab CI avec un runner shell sur le VPS. Le déploiement tient en quelques lignes parce que les secrets sont geres par GitLab.

GitLab CI/CD : variables

Tu les trouves dans Settings > CI/CD > Variables. Chaque variable a plusieurs options :

Type : "Variable" (injectee comme variable d'environnement) ou "File" (écrite dans un fichier temporaire, le chemin est dans la variable). Pour un .env complet, le type "Variable" fonctionne bien.

Protected : la variable n'est disponible que sur les branches protegees (typiquement main). Active toujours cette option pour les secrets de production. Ca empeche un merge request malicieux de lire tes secrets depuis une branche de feature.

Masked : la valeur est remplacee par [MASKED] dans les logs du pipeline. Active cette option pour tout ce qui est sensible. Attention : GitLab ne peut masquer que les valeurs qui font au moins 8 caractères et qui ne contiennent pas de retours a la ligne (pour les valeurs multilignes, utilise le type "File").

Environment scope : tu peux limiter une variable a un environnement spécifique (production, staging). Utile si tes secrets différent entre les environnements.

Le setup paltemps.fr

Voici comment je gere les secrets pour paltemps.fr. J'ai une variable GitLab DOTENV qui contient tout le fichier .env :

ANTHROPIC_API_KEY=sk-ant-api03-xxxx
OPENAI_API_KEY=sk-xxxx
UNSPLASH_ACCESS_KEY=xxxx
PEXELS_API_KEY=xxxx
SMTP_USER=no-reply@paltemps.fr
SMTP_PASS=monmotdepasse
ADMIN_PASSWORD=admin123
WHITELISTED_IPS=1.2.3.4,5.6.7.8

Le .gitlab-ci.yml :

yamlstages:
  - deploy

deploy:
  stage: deploy
  script:
    - echo "$DOTENV" > .env
    - docker compose up -d --build
  after_script:
    - rm -f .env
  only:
    - main
  tags:
    - shell

Le runner shell exécuté ca directement sur le VPS. Le fichier .env existe pendant le build, puis il est supprime. En réalité, Docker Compose le lit et injecte les variables dans les conteneurs, donc le fichier n'a plus besoin d'exister apres le up.

Une alternative plus propre : écrire le .env dans un emplacement que Docker Compose référencé avec env_file, et le supprimer apres :

yamldeploy:
  script:
    - echo "$DOTENV" > /opt/paltemps/.env
    - cd /opt/paltemps && docker compose up -d --build
  after_script:
    - rm -f /opt/paltemps/.env

GitHub Actions : secrets

Chez GitHub, c'est dans Settings > Secrets and variables > Actions. Trois niveaux :

  • Repository secrets : disponibles dans tous les workflows du repo
  • Environment secrets : lies a un environnement (production, staging), avec des regles d'approbation possibles
  • Organization secrets : partages entre plusieurs repos

Utilisation dans un workflow :

yamlname: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Create .env
        run: echo "${{ secrets.DOTENV }}" > .env

      - name: Deploy
        run: |
          scp .env user@vps:/opt/app/.env
          ssh user@vps "cd /opt/app && docker compose up -d --build"
        env:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

La différence avec GitLab : GitHub Actions tourne sur les serveurs de GitHub (ou un runner self-hosted). Pour déployer sur ton VPS, tu as besoin d'une connexion SSH, ce qui veut dire stocker une clé privee SSH dans les secrets. Avec GitLab et un runner shell sur le VPS, tu n'as pas ce problème.

Les erreurs qui font fuiter des secrets

Ca arrive plus souvent qu'on le pense. Voici les classiques.

Le echo de debug :

yaml# NE FAIS PAS CA
script:
  - echo "La cle est $ANTHROPIC_API_KEY"
  - echo $DOTENV  # Affiche TOUT le .env dans les logs

Meme avec le masquage active, certaines formes d'affichage passent à travers. Le masquage de GitLab fonctionne par correspondance exacte : si la valeur apparaît en parties ou encodee differemment, elle peut ne pas etre masquee.

Le curl verbose :

yaml# NE FAIS PAS CA
script:
  - curl -v -H "Authorization: Bearer $API_KEY" https://api.example.com
  # Le -v affiche les headers, donc ta cle, dans les logs

Le set -x :

yaml# NE FAIS PAS CA
script:
  - set -x  # Affiche chaque commande avant execution
  - export API_KEY=$SECRET_KEY  # Secret visible dans les logs

Le docker build avec des ARG :

dockerfile# NE FAIS PAS CA
ARG API_KEY
RUN echo $API_KEY > /app/.env
# La valeur est dans l'historique des layers Docker

Utilise plutot des secrets Docker ou monte le .env au runtime, pas au build.

GitHub secret scanning

GitHub scanne automatiquement les commits pour détecter des patterns de secrets connus (clés AWS, tokens GitHub, clés API de dizaines de services). Si un secret est détecté, tu recois une alerte.

Active-le dans Settings > Code security and analysis > Secret scanning. C'est gratuit pour les repos publics, et disponible pour les repos prives avec GitHub Advanced Security.

C'est un filet de sécurité, pas une solution. Le secret a deja ete pousse dans l'historique git au moment ou l'alerte arrive. La bonne pratique, c'est de ne jamais committer de secret, pas de compter sur un scanner pour les trouver apres.

GitLab a un équivalent avec Secret Détection dans le pipeline CI.

Bonnes pratiques

Jamais d'echo de secrets dans les logs. Si tu as besoin de vérifier qu'une variable existe, teste sa longueur :

bashif [ -z "$ANTHROPIC_API_KEY" ]; then
  echo "ERREUR: ANTHROPIC_API_KEY non definie"
  exit 1
fi
echo "ANTHROPIC_API_KEY: definie (${#ANTHROPIC_API_KEY} caracteres)"

Variables protegees sur les branches protegees. Un contributeur externe ne devrait pas pouvoir lire tes secrets de production via un merge request.

Separe prod et staging. Deux jeux de secrets différents, deux environnements différents. Si le staging est compromis, la prod reste intacte.

Fais tourner les secrets periodiquement. Le prochain article détaillé comment faire la rotation sans casser ton déploiement.

Si un secret fuite dans les logs : considéré-le compromis. Regenere-le immédiatement, mets à jour la variable CI/CD, et vérifié les logs d'acces pour voir si quelqu'un l'a exploite.

Combiner avec SOPS

La stratégie que je recommande pour les petites équipes : les secrets chiffres dans git avec SOPS + age pour le versioning et le partage, et la clé privee age comme unique variable CI/CD. Le pipeline dechiffre le fichier au déploiement.

Ca réduit le nombre de variables CI/CD a une seule (la clé age), et tous les secrets sont geres dans git avec un historique propre.


Navigation : Precedent : 06 - SOPS + age | Suivant : 08 - Rotation des secrets


Sources

Retrouve d'autres articles techniques sur paltemps.fr.

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.