Tôt ou tard, votre pipeline aura besoin d’un secret : l’URL de la base de données, un jeton d’API, une clé pour pousser une image. La tentation du débutant est de les écrire « juste pour tester » dans le YAML — et de les pousser sur un dépôt public où le monde entier les lira. Un secret commité est un secret brûlé. Ce tutoriel vous apprend à fournir ces valeurs sensibles à vos workflows proprement : stockées chiffrées, masquées dans les journaux, et — pour le déploiement — protégées par une approbation humaine.
Ce tutoriel fait partie de la série « CI/CD avec GitHub Actions ». Pour la vue d’ensemble, lisez d’abord le guide principal.
🎯 Ce que vous allez apprendre
- Stocker un secret de dépôt et l’utiliser dans un workflow via
${{ secrets.NOM }}. - Distinguer un secret (chiffré, masqué) d’une variable (configuration non sensible).
- Restreindre les permissions du
GITHUB_TOKENau strict nécessaire. - Créer un environnement (production) avec ses propres secrets et des règles de protection.
- Exiger une approbation manuelle avant qu’un job touche la production.
- Comprendre l’authentification OIDC : se connecter à un cloud sans stocker de mot de passe.
🛠️ Ce que vous allez construire
L’API Récolte va bientôt se connecter à une base et se déployer. On prépare le terrain : un secret pour l’URL de base de données, une variable pour le nom du service, un job de déploiement factice qui ne s’exécute qu’après validation d’un relecteur, et le squelette d’une connexion cloud sans mot de passe. Aucune valeur sensible ne touchera jamais le dépôt.
Prérequis
- Avoir suivi les tutoriels précédents, en particulier Automatiser ses tests.
- Être administrateur du dépôt (la gestion des secrets et environnements demande ce droit).
- Test express : si vous savez naviguer dans Settings de votre dépôt, vous êtes prêt.
- ⏱️ Temps estimé : ~40 minutes.
Étape 1 — Stocker et utiliser un secret de dépôt (quick win)
Un secret de dépôt est une valeur chiffrée que GitHub stocke et n’expose qu’au moment de l’exécution, jamais en clair dans l’interface. On le crée dans Settings → Secrets and variables → Actions → New repository secret. Créons-en un nommé DATABASE_URL avec une valeur de test. On y accède ensuite dans le workflow via le contexte secrets.
jobs:
verifier-config:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Lire un secret sans l'afficher
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
if [ -z "$DATABASE_URL" ]; then
echo "Aucune URL de base configurée" && exit 1
fi
echo "URL de base présente, longueur : ${#DATABASE_URL}"
On injecte le secret dans une variable d’environnement DATABASE_URL via la clé env, puis on l’utilise dans le shell. Remarquez qu’on n’affiche jamais sa valeur : on teste sa présence et on montre seulement sa longueur. GitHub masque de toute façon automatiquement toute occurrence d’un secret dans les journaux — si vous tentiez un echo "$DATABASE_URL", vous verriez ***. Mais ne comptez pas là-dessus comme seule défense : un secret transformé (encodé en base64, par exemple) échappe au masquage. La règle d’or reste : on consomme un secret, on ne l’imprime pas.
✅ Point d’étape — Le job doit afficher la longueur du secret sans révéler sa valeur. Supprimez le secret et relancez : le job échoue avec « Aucune URL de base configurée ». Vous tenez un garde-fou de configuration.
Étape 2 — Secrets contre variables
Tout ce qui transite par un workflow n’est pas sensible. Le nom du service, la région d’un serveur, un indicateur de fonctionnalité : ce sont des configurations, pas des secrets. Les mettre en secret est contre-productif — vous ne pourriez plus les relire pour vérification, et le masquage polluerait vos journaux. GitHub distingue donc les deux. Dans le même écran Secrets and variables → Actions, l’onglet Variables stocke des valeurs en clair, lisibles, accessibles via le contexte vars.
- name: Afficher la configuration non sensible
run: |
echo "Service : ${{ vars.NOM_SERVICE }}"
echo "Région : ${{ vars.REGION }}"
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }} # ça, c'est sensible
La règle de tri est simple : si la fuite de cette valeur crée un risque, c’est un secret ; sinon, c’est une variable. Une URL de base avec mot de passe, un jeton, une clé privée : secrets. Un nom, une région, un drapeau on/off : variables. Bien séparer les deux rend vos workflows lisibles et vos vrais secrets faciles à auditer — vous savez exactement quelle poignée de valeurs protéger.
Quand vous gérez plusieurs dépôts — une API, un site, un outil interne — vous remarquerez vite que certains secrets sont communs (le même jeton de registre, par exemple). Plutôt que de les recopier dans chaque dépôt, GitHub permet de définir des secrets et des variables au niveau de l’organisation, partagés à une liste de dépôts choisie. Le bénéfice est double : une seule valeur à faire tourner le jour où vous la changez, et un point d’audit unique. La même hiérarchie s’applique d’ailleurs aux trois niveaux — organisation, dépôt, environnement — du plus large au plus restreint, le niveau le plus spécifique l’emportant en cas de même nom.
Étape 3 — Restreindre les permissions du GITHUB_TOKEN
À chaque exécution, GitHub fabrique un jeton temporaire, le GITHUB_TOKEN, qui permet au workflow d’agir sur le dépôt — commenter une PR, publier un paquet, créer une release. Ce jeton est pratique mais puissant ; selon la configuration de l’organisation, il peut démarrer avec des droits d’écriture étendus. Le principe de moindre privilège commande de ne lui accorder que ce dont chaque job a besoin, explicitement.
name: CI
on: [push, pull_request]
# par défaut, tous les jobs n'ont qu'un accès en lecture au dépôt
permissions:
contents: read
jobs:
publier-paquet:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # ce job précis a le droit de publier
steps:
- uses: actions/checkout@v6
- run: echo "Publication autorisée pour ce job uniquement"
On pose un permissions global minimal (contents: read), puis on élargit au cas par cas dans le job qui en a besoin. Ici, seul publier-paquet obtient packages: write ; les autres jobs restent en lecture seule. Si un jour une action tierce malveillante se glisse dans votre pipeline, elle hérite de ces permissions restreintes et ne peut pas faire de dégâts hors de son périmètre. Déclarer les permissions explicitement est l’un des réflexes de sécurité les plus rentables : une ligne de YAML contre une porte grande ouverte.
Étape 4 — Environnements et approbation manuelle
Tous les secrets ne se valent pas. La clé qui déploie en production mérite plus de protection qu’un jeton de test. Les environnements répondent à ce besoin : ce sont des espaces nommés (typiquement staging et production) qui portent leurs propres secrets et, surtout, leurs propres règles de protection. On les crée dans Settings → Environments → New environment.
Créez un environnement production. Dans ses réglages, activez Required reviewers et désignez-vous : tout job ciblant cet environnement se mettra en pause et attendra votre clic « Approuver » avant de continuer. Ajoutez-y un secret DEPLOY_TOKEN propre à l’environnement. Un job s’y rattache avec la clé environment :
deployer:
runs-on: ubuntu-latest
needs: tests # ne part qu'après le job de tests
environment: production
steps:
- name: Déployer (simulation)
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }} # secret de l'environnement
run: echo "Déploiement de Récolte en production…"
Trois protections se combinent ici. needs: tests impose que la suite de tests soit verte avant même d’envisager le déploiement. environment: production rattache le job à l’environnement protégé : GitHub suspend l’exécution et vous notifie pour approbation. Et le secret DEPLOY_TOKEN lu n’est pas celui du dépôt mais celui de l’environnement — il n’existe que pour les jobs qui passent par cette porte. Vous pouvez aussi ajouter un wait timer (un délai imposé avant déploiement) ou restreindre les branches autorisées. C’est exactement le mécanisme sur lequel s’appuiera le tutoriel de déploiement.
Un effet de bord appréciable : chaque environnement tient son historique de déploiements. La page du dépôt affiche, par environnement, qui a déployé quoi et quand, avec un lien vers l’exécution correspondante. En cas d’incident, vous remontez en quelques secondes au commit exact mis en production et à la personne qui l’a approuvé — une traçabilité précieuse quand on cherche « depuis quand ça ne marche plus ».
✅ Point d’étape — Lancez le workflow : le job
deployerdoit apparaître « En attente d’approbation ». Cliquez « Review deployments → Approuver » et il reprend. Vous avez inséré un humain dans la boucle, là où il le faut.
Étape 5 — OIDC : se connecter sans mot de passe
Stocker une clé d’accès cloud (AWS, Google Cloud, Azure) en secret fonctionne, mais pose un problème de fond : cette clé est longue durée. Si elle fuite, elle reste valable jusqu’à ce que quelqu’un la révoque. L’OIDC (OpenID Connect) supprime ce risque : au lieu d’un mot de passe stocké, GitHub présente au cloud une identité vérifiable et éphémère, et le cloud renvoie un accès valable quelques minutes seulement.
deployer-cloud:
runs-on: ubuntu-latest
permissions:
id-token: write # indispensable pour réclamer un jeton OIDC
contents: read
steps:
- uses: actions/checkout@v6
- name: S'authentifier au cloud sans clé stockée
run: echo "GitHub présente une identité OIDC ; le cloud délivre un accès temporaire"
La clé id-token: write autorise le workflow à demander un jeton d’identité signé par GitHub. Côté cloud, on configure une relation de confiance qui accepte les jetons venant de votre dépôt précis (et même d’une branche ou d’un environnement précis). Aucune clé secrète ne dort donc dans GitHub : il n’y a rien à voler. Mettre en place la confiance côté fournisseur demande quelques réglages spécifiques à chacun, mais le gain est considérable — c’est aujourd’hui la méthode recommandée pour tout déploiement cloud sérieux. Pour un VPS classique en SSH, on restera sur un secret de clé, vu au tutoriel suivant ; l’OIDC brille surtout face aux grands fournisseurs cloud.
🐞 Pièges fréquents
| Symptôme / erreur | Cause probable | Correctif |
|---|---|---|
${{ secrets.X }} est vide dans une PR externe |
Sécurité : les secrets ne sont pas exposés aux PR venant d’un fork | Normal ; faire les jobs sensibles sur push/après merge, pas sur la PR du fork |
| Le secret apparaît en clair dans les logs | Valeur transformée (base64, JSON) qui échappe au masquage | Ne jamais transformer ni afficher un secret ; le consommer tel quel |
Resource not accessible by integration |
GITHUB_TOKEN sans la permission requise |
Ajouter la permission précise dans le bloc permissions du job |
| Le job ne lit pas le secret d’environnement | Clé environment: absente du job |
Rattacher le job à l’environnement avec environment: production |
| Le déploiement part sans approbation | Required reviewers non activé sur l’environnement | Activer la règle dans Settings → Environments |
🌍 Adaptation au contexte ouest-africain
La gestion des secrets prend un relief particulier quand on travaille en collectif distribué — un dev à Dakar, un autre à Abidjan, un serveur à Lomé. Les secrets de dépôt évitent de s’échanger des mots de passe par messagerie, pratique aussi répandue que dangereuse. Chacun pousse du code ; personne n’a besoin de connaître la clé de production, qui vit chiffrée dans GitHub et n’est lisible que par le pipeline.
L’approbation manuelle d’environnement est, elle, un filet précieux pour une petite structure sans équipe d’exploitation dédiée : le responsable technique garde la main sur le moment exact où le code part en production, depuis son téléphone si besoin, en un clic. On évite à la fois le déploiement accidentel du vendredi soir et la lourdeur d’un processus formel. Quant à l’OIDC, il supprime le cauchemar de la clé cloud oubliée dans un vieux dépôt — un incident de sécurité classique et coûteux.
✅ Récapitulatif
Vous savez désormais alimenter un pipeline en valeurs sensibles sans jamais les exposer. Vous distinguez un secret (chiffré, masqué) d’une variable (configuration lisible), et vous savez où ranger chaque chose. Vous bridez le GITHUB_TOKEN au strict nécessaire, job par job. Vous avez créé un environnement production avec ses secrets propres et une approbation humaine obligatoire avant tout déploiement. Et vous comprenez pourquoi l’OIDC — une identité éphémère plutôt qu’une clé stockée — est la direction à prendre pour le cloud. Votre pipeline est prêt à manipuler du sérieux sans rien laisser fuir.
🧾 Aide-mémoire
| Élément | Rôle |
|---|---|
${{ secrets.NOM }} |
Lire un secret (chiffré, masqué) |
${{ vars.NOM }} |
Lire une variable (configuration non sensible) |
permissions: |
Restreindre les droits du GITHUB_TOKEN |
environment: production |
Rattacher un job à un environnement protégé |
| Required reviewers | Imposer une approbation manuelle |
id-token: write |
Activer l’authentification OIDC |
needs: |
Lancer un job seulement après un autre |
💪 À vous de jouer
1. Créez un environnement staging sans approbation, et faites en sorte que le déploiement vers staging soit automatique mais celui vers production nécessite une validation.
2. Ajoutez un wait timer de 5 minutes sur l’environnement production et observez la mise en attente.
Voir une piste pour le défi 1
deployer-staging:
runs-on: ubuntu-latest
needs: tests
environment: staging # pas de reviewers → automatique
steps:
- run: echo "Mise en préproduction automatique"
deployer-prod:
runs-on: ubuntu-latest
needs: deployer-staging # ne part qu'après staging
environment: production # reviewers activés → attend l'approbation
steps:
- run: echo "Mise en production après validation"
La séquence tests → staging → production est imposée par les needs chaînés, et seule la dernière étape attend un humain grâce aux règles de l’environnement production.
Tutoriels frères
- Matrice de tests et cache — la couverture multi-versions, en amont du déploiement.
- Construire et publier une image Docker — où le
GITHUB_TOKENetpackages: writeservent pour de vrai.
Pour aller plus loin
- 🔝 Retour au guide principal : GitHub Actions : le guide CI/CD pour bien démarrer
- Documentation officielle : GitHub Docs — secrets chiffrés et OIDC.
- Étape suivante conseillée : construire et publier une image Docker.
FAQ
Un secret est-il visible par les contributeurs ?
Non. Une fois enregistré, un secret ne peut plus être relu dans l’interface, même par un administrateur : on peut seulement le remplacer. Le pipeline y accède au moment de l’exécution, masqué dans les journaux.
Pourquoi mes secrets sont-ils vides sur une pull request externe ?
C’est une protection volontaire : GitHub n’expose pas les secrets aux workflows déclenchés par une PR venant d’un fork, sinon n’importe qui pourrait les exfiltrer en proposant un faux correctif. Exécutez les jobs sensibles après fusion.
Secret de dépôt ou d’environnement ?
Dépôt pour ce qui sert partout (un jeton de tests). Environnement pour ce qui ne doit servir qu’à un contexte précis et protégé (la clé de production), avec approbation à la clé.
Faut-il toujours déclarer permissions ?
Oui, c’est une bonne pratique forte. Poser un défaut restrictif (contents: read) puis élargir au cas par cas limite la casse si une dépendance d’action est compromise.
L’OIDC est-il utile pour un simple VPS ?
Moins : l’OIDC vise les fournisseurs cloud qui savent valider une identité fédérée. Pour un VPS en SSH, on utilise une clé privée stockée en secret, comme on le verra au tutoriel suivant.
Mots-clés : secrets GitHub Actions, variables Actions, GITHUB_TOKEN permissions, environnements GitHub, required reviewers, OIDC GitHub Actions, déploiement sécurisé.