{"id":533,"date":"2026-03-02T23:09:46","date_gmt":"2026-03-02T22:09:46","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=533"},"modified":"2026-03-24T12:58:31","modified_gmt":"2026-03-24T11:58:31","slug":"lab-securing-gitlab-ci-pipelines-protected-variables-runners-environments","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/lab-securing-gitlab-ci-pipelines-protected-variables-runners-environments\/","title":{"rendered":"Lab : S\u00e9curisation des Pipelines GitLab CI \u2014 Variables Prot\u00e9g\u00e9es, Runners et Environnements"},"content":{"rendered":"<h2>Aper\u00e7u<\/h2>\n<p>GitLab CI est la deuxi\u00e8me plateforme CI\/CD la plus utilis\u00e9e dans l\u2019industrie, alimentant des millions de pipelines au sein d\u2019organisations de toutes tailles. Son int\u00e9gration \u00e9troite avec le contr\u00f4le de version la rend exceptionnellement pratique \u2014 mais cette m\u00eame int\u00e9gration cr\u00e9e une surface d\u2019attaque \u00e9tendue si les pipelines ne sont pas d\u00e9lib\u00e9r\u00e9ment renforc\u00e9s.<\/p>\n<p>Dans ce lab pratique, vous parcourrez six exercices qui s\u00e9curisent progressivement un pipeline GitLab CI. Vous commencerez avec une configuration intentionnellement non s\u00e9curis\u00e9e o\u00f9 chaque variable est visible depuis toutes les branches, les runners partag\u00e9s g\u00e8rent tous les jobs, et il n\u2019y a aucune porte d\u2019approbation sur les environnements. \u00c0 la fin, vous disposerez d\u2019un pipeline qui applique un <strong>acc\u00e8s aux variables selon le principe du moindre privil\u00e8ge<\/strong>, des <strong>runners \u00e0 port\u00e9e limit\u00e9e<\/strong>, des <strong>environnements prot\u00e9g\u00e9s avec approbations de d\u00e9ploiement<\/strong>, un <strong>acc\u00e8s restreint au CI_JOB_TOKEN<\/strong>, des <strong>pipelines de merge request s\u00e9curis\u00e9s<\/strong>, ainsi que des <strong>contr\u00f4les de renforcement suppl\u00e9mentaires<\/strong> incluant la d\u00e9tection de secrets.<\/p>\n<p>Chaque commande, extrait YAML et chemin d\u2019interface dans ce lab est bas\u00e9 sur GitLab 16.x \/ 17.x et fonctionne sur le plan gratuit de GitLab.com.<\/p>\n<h2>Pr\u00e9requis<\/h2>\n<ul>\n<li>Un <strong>compte GitLab<\/strong> \u2014 le plan gratuit sur <a href=\"https:\/\/gitlab.com\" target=\"_blank\" rel=\"noopener\">gitlab.com<\/a> est suffisant pour chaque exercice.<\/li>\n<li>Un <strong>projet de test<\/strong> contenant une application simple (m\u00eame un simple fichier <code>index.html<\/code> suffit) et un fichier <code>.gitlab-ci.yml<\/code> \u00e0 la racine du d\u00e9p\u00f4t.<\/li>\n<li>Une familiarit\u00e9 de base avec la <strong>syntaxe GitLab CI<\/strong> : stages, jobs, scripts et rules.<\/li>\n<li>(Optionnel) Une machine Linux ou macOS si vous pr\u00e9voyez d\u2019enregistrer votre propre GitLab Runner dans l\u2019Exercice 2.<\/li>\n<\/ul>\n<h2>Mise en Place de l\u2019Environnement<\/h2>\n<h3>\u00c9tape 1 \u2014 Cr\u00e9er un Nouveau Projet GitLab<\/h3>\n<ol>\n<li>Naviguez vers <strong>GitLab &gt; New Project &gt; Create blank project<\/strong>.<\/li>\n<li>Nommez-le <code>secure-pipeline-lab<\/code>, d\u00e9finissez la visibilit\u00e9 sur <strong>Private<\/strong> et initialisez avec un README.<\/li>\n<li>Sous <strong>Settings &gt; Repository &gt; Protected branches<\/strong>, confirmez que <code>main<\/code> est list\u00e9e comme branche prot\u00e9g\u00e9e (c\u2019est le param\u00e8tre par d\u00e9faut).<\/li>\n<\/ol>\n<h3>\u00c9tape 2 \u2014 Ajouter une Application Simple<\/h3>\n<p>Cr\u00e9ez <code>index.html<\/code> \u00e0 la racine du d\u00e9p\u00f4t :<\/p>\n<pre><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;&lt;meta charset=\"UTF-8\"&gt;&lt;title&gt;Secure Pipeline Lab&lt;\/title&gt;&lt;\/head&gt;\n&lt;body&gt;&lt;h1&gt;Hello, GitLab CI!&lt;\/h1&gt;&lt;\/body&gt;\n&lt;\/html&gt;<\/code><\/pre>\n<h3>\u00c9tape 3 \u2014 Cr\u00e9er le Pipeline Initial (Non S\u00e9curis\u00e9)<\/h3>\n<p>Ajoutez le fichier <code>.gitlab-ci.yml<\/code> suivant. Il est <em>d\u00e9lib\u00e9r\u00e9ment non s\u00e9curis\u00e9<\/em> \u2014 c\u2019est le point de d\u00e9part que nous renforcerons tout au long du lab :<\/p>\n<pre><code># .gitlab-ci.yml \u2014 Point de d\u00e9part NON S\u00c9CURIS\u00c9\nstages:\n  - build\n  - test\n  - deploy\n\nbuild-job:\n  stage: build\n  script:\n    - echo \"Building the application...\"\n    - echo \"DB_PASSWORD is $DB_PASSWORD\"   # Variable affich\u00e9e dans les logs !\n\ntest-job:\n  stage: test\n  script:\n    - echo \"Running tests...\"\n    - echo \"API_KEY is $API_KEY\"            # Variable affich\u00e9e dans les logs !\n\ndeploy-job:\n  stage: deploy\n  script:\n    - echo \"Deploying to production...\"\n    - echo \"DEPLOY_TOKEN is $DEPLOY_TOKEN\" # Variable affich\u00e9e dans les logs !\n<\/code><\/pre>\n<p>Ce pipeline pr\u00e9sente plusieurs probl\u00e8mes :<\/p>\n<ul>\n<li>Toutes les variables CI\/CD sont accessibles depuis <strong>toutes les branches<\/strong>, y compris celles cr\u00e9\u00e9es par des contributeurs externes.<\/li>\n<li>Les variables sont affich\u00e9es directement dans les logs des jobs \u2014 toute personne ayant acc\u00e8s aux logs peut les lire.<\/li>\n<li>Les jobs s\u2019ex\u00e9cutent sur des <strong>runners partag\u00e9s<\/strong> sans garantie d\u2019isolation.<\/li>\n<li>Il n\u2019y a <strong>aucune porte d\u2019approbation sur les environnements<\/strong> \u2014 le job de d\u00e9ploiement s\u2019ex\u00e9cute automatiquement \u00e0 chaque push.<\/li>\n<\/ul>\n<p>Committez ce fichier sur <code>main<\/code> et v\u00e9rifiez que le pipeline s\u2019ex\u00e9cute. Maintenant, corrigeons chacun de ces probl\u00e8mes.<\/p>\n<h2>Exercice 1 : Variables Prot\u00e9g\u00e9es et Masqu\u00e9es<\/h2>\n<p>Les variables CI\/CD de GitLab prennent en charge trois indicateurs de protection qui r\u00e9duisent consid\u00e9rablement le rayon d\u2019impact d\u2019une branche ou d\u2019un fork compromis.<\/p>\n<h3>Comprendre les Trois Indicateurs<\/h3>\n<table>\n<thead>\n<tr>\n<th>Indicateur<\/th>\n<th>Effet<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Protected<\/strong><\/td>\n<td>La variable est <em>uniquement<\/em> inject\u00e9e dans les pipelines s\u2019ex\u00e9cutant sur des <strong>branches ou tags prot\u00e9g\u00e9s<\/strong>. Un pipeline d\u00e9clench\u00e9 depuis une branche de fonctionnalit\u00e9 ou un fork ne verra jamais la valeur.<\/td>\n<\/tr>\n<tr>\n<td><strong>Masked<\/strong><\/td>\n<td>GitLab masque la valeur de la variable dans les logs des jobs. Si un script affiche accidentellement la valeur, le log affiche <code>[MASKED]<\/code> \u00e0 la place.<\/td>\n<\/tr>\n<tr>\n<td><strong>Hidden<\/strong> (GitLab 17+)<\/td>\n<td>La valeur de la variable ne peut pas \u00eatre r\u00e9v\u00e9l\u00e9e dans l\u2019interface apr\u00e8s sa cr\u00e9ation \u2014 m\u00eame par les mainteneurs du projet. Utile pour les secrets g\u00e9r\u00e9s par une \u00e9quipe plateforme que les d\u00e9veloppeurs ne devraient jamais voir en texte clair.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>\u00c9tape 1 \u2014 Cr\u00e9er les Variables<\/h3>\n<ol>\n<li>Allez dans <strong>Settings &gt; CI\/CD &gt; Variables &gt; Expand &gt; Add variable<\/strong>.<\/li>\n<li>Cr\u00e9ez les variables suivantes :<\/li>\n<\/ol>\n<table>\n<thead>\n<tr>\n<th>Cl\u00e9<\/th>\n<th>Valeur (exemple)<\/th>\n<th>Protected<\/th>\n<th>Masked<\/th>\n<th>Hidden<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>DEPLOY_TOKEN<\/code><\/td>\n<td><code>glpat-xxxxxxxxxxxxxxxxxxxx<\/code><\/td>\n<td>Oui<\/td>\n<td>Oui<\/td>\n<td>Non<\/td>\n<\/tr>\n<tr>\n<td><code>DB_PASSWORD<\/code><\/td>\n<td><code>S3cur3P@ssw0rd!2024<\/code><\/td>\n<td>Oui<\/td>\n<td>Oui<\/td>\n<td>Oui<\/td>\n<\/tr>\n<tr>\n<td><code>API_KEY<\/code><\/td>\n<td><code>sk-test-abc123def456<\/code><\/td>\n<td>Non<\/td>\n<td>Oui<\/td>\n<td>Non<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>\u00c9tape 2 \u2014 Mettre \u00e0 Jour le Pipeline<\/h3>\n<pre><code># .gitlab-ci.yml \u2014 Exercice 1\nstages:\n  - build\n  - test\n  - deploy\n\nbuild-job:\n  stage: build\n  script:\n    - echo \"Building the application...\"\n    - echo \"API_KEY value length = ${#API_KEY}\"  # S\u00fbr : affiche la longueur, pas la valeur\n\ntest-job:\n  stage: test\n  script:\n    - echo \"Running tests...\"\n    # Tentative d'affichage d'une variable masqu\u00e9e :\n    - echo \"DB_PASSWORD is $DB_PASSWORD\"\n    # La sortie affichera : DB_PASSWORD is [MASKED]\n\ndeploy-job:\n  stage: deploy\n  script:\n    - echo \"Deploying with DEPLOY_TOKEN...\"\n    - echo \"Token is $DEPLOY_TOKEN\"\n    # Sur main (prot\u00e9g\u00e9e) : Token is [MASKED]\n    # Sur une branche de fonctionnalit\u00e9 : Token is &lt;vide \u2014 variable non inject\u00e9e&gt;\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n<\/code><\/pre>\n<h3>\u00c9tape 3 \u2014 V\u00e9rifier le Comportement de Protection<\/h3>\n<ol>\n<li><strong>Push sur <code>main<\/code><\/strong> \u2014 le job de d\u00e9ploiement s\u2019ex\u00e9cute et <code>DEPLOY_TOKEN<\/code> est inject\u00e9 (le log affiche <code>[MASKED]<\/code>).<\/li>\n<li><strong>Cr\u00e9ez une branche<\/strong> <code>feature\/test-vars<\/code>, poussez un commit \u2014 le job de d\u00e9ploiement ne s\u2019ex\u00e9cute pas (les rules le restreignent \u00e0 <code>main<\/code>). M\u00eame si vous modifiez les rules pour le laisser s\u2019ex\u00e9cuter, <code>DEPLOY_TOKEN<\/code> et <code>DB_PASSWORD<\/code> sont <strong>vides<\/strong> car la branche n\u2019est pas prot\u00e9g\u00e9e.<\/li>\n<li><code>API_KEY<\/code>, qui est masqu\u00e9e mais <em>non<\/em> prot\u00e9g\u00e9e, est disponible sur les deux branches \u2014 sa valeur est masqu\u00e9e dans les logs.<\/li>\n<\/ol>\n<p><strong>Le\u00e7on cl\u00e9 :<\/strong> Marquez toujours les identifiants de d\u00e9ploiement comme <strong>Protected<\/strong> et <strong>Masked<\/strong>. Utilisez <strong>Hidden<\/strong> pour les secrets que les d\u00e9veloppeurs ne devraient jamais r\u00e9cup\u00e9rer depuis l\u2019interface.<\/p>\n<h2>Exercice 2 : S\u00e9curit\u00e9 et Port\u00e9e des Runners<\/h2>\n<p>Les runners sont les moteurs de calcul qui ex\u00e9cutent vos jobs CI. Choisir le bon type de runner \u2014 et d\u00e9finir correctement sa port\u00e9e \u2014 est l\u2019une des d\u00e9cisions de s\u00e9curit\u00e9 les plus impactantes que vous puissiez prendre.<\/p>\n<h3>Types de Runners<\/h3>\n<table>\n<thead>\n<tr>\n<th>Type<\/th>\n<th>Port\u00e9e<\/th>\n<th>Posture de S\u00e9curit\u00e9<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Instance (partag\u00e9)<\/strong><\/td>\n<td>Disponible pour chaque projet sur l\u2019instance GitLab<\/td>\n<td>Multi-tenant. Les jobs d\u2019autres projets peuvent s\u2019ex\u00e9cuter sur la m\u00eame machine. Risque de fuite de donn\u00e9es via le syst\u00e8me de fichiers partag\u00e9, le socket Docker ou les couches en cache.<\/td>\n<\/tr>\n<tr>\n<td><strong>Group<\/strong><\/td>\n<td>Disponible pour chaque projet d\u2019un groupe sp\u00e9cifique<\/td>\n<td>Meilleure isolation que les runners d\u2019instance, mais toujours partag\u00e9 entre les projets du groupe.<\/td>\n<\/tr>\n<tr>\n<td><strong>Project<\/strong><\/td>\n<td>Disponible pour un seul projet uniquement<\/td>\n<td>Meilleure isolation. Vous contr\u00f4lez la machine, la configuration Docker et l\u2019acc\u00e8s r\u00e9seau.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>\u00c9tape 1 \u2014 Enregistrer un Runner Sp\u00e9cifique au Projet<\/h3>\n<p>Sur une machine que vous contr\u00f4lez (une VM, un serveur disponible ou m\u00eame un h\u00f4te Docker local), installez GitLab Runner et enregistrez-le :<\/p>\n<pre><code># Installer GitLab Runner (Linux amd64)\nsudo curl -L --output \/usr\/local\/bin\/gitlab-runner \\\n  https:\/\/gitlab-runner-downloads.s3.amazonaws.com\/latest\/binaries\/gitlab-runner-linux-amd64\nsudo chmod +x \/usr\/local\/bin\/gitlab-runner\nsudo gitlab-runner install --user=gitlab-runner --working-directory=\/home\/gitlab-runner\nsudo gitlab-runner start\n\n# Enregistrer le runner\n# Trouvez votre token d'enregistrement : Settings > CI\/CD > Runners > Expand > New project runner\nsudo gitlab-runner register \\\n  --non-interactive \\\n  --url https:\/\/gitlab.com\/ \\\n  --token \"$RUNNER_TOKEN\" \\\n  --executor docker \\\n  --docker-image alpine:latest \\\n  --description \"secure-deploy-runner\" \\\n  --tag-list \"secure-deploy\" \\\n  --access-level ref_protected\n<\/code><\/pre>\n<p>L\u2019indicateur critique est <code>--access-level ref_protected<\/code>. Il indique \u00e0 GitLab que le runner <strong>n\u2019acceptera que les jobs provenant de branches ou tags prot\u00e9g\u00e9s<\/strong>. Un pipeline d\u00e9clench\u00e9 par une branche de fonctionnalit\u00e9 ou une merge request de fork ne sera jamais planifi\u00e9 sur ce runner.<\/p>\n<h3>\u00c9tape 2 \u2014 D\u00e9sactiver les Runners Partag\u00e9s pour les Jobs Sensibles<\/h3>\n<p>Allez dans <strong>Settings &gt; CI\/CD &gt; Runners<\/strong> et basculez <strong>Enable shared runners for this project<\/strong> sur d\u00e9sactiv\u00e9 \u2014 ou laissez-les activ\u00e9s pour les \u00e9tapes non sensibles et utilisez des tags pour diriger les jobs sensibles vers votre runner de projet.<\/p>\n<h3>\u00c9tape 3 \u2014 Mettre \u00e0 Jour le Pipeline avec une S\u00e9lection de Runner par Tags<\/h3>\n<pre><code># .gitlab-ci.yml \u2014 Exercice 2\nstages:\n  - build\n  - test\n  - deploy\n\nbuild-job:\n  stage: build\n  # S'ex\u00e9cute sur n'importe quel runner disponible (partag\u00e9 convient pour les builds)\n  script:\n    - echo \"Building the application...\"\n\ntest-job:\n  stage: test\n  script:\n    - echo \"Running tests...\"\n\ndeploy-job:\n  stage: deploy\n  tags:\n    - secure-deploy            # S'ex\u00e9cute uniquement sur le(s) runner(s) avec ce tag\n  script:\n    - echo \"Deploying with DEPLOY_TOKEN...\"\n    - |\n      curl --fail --silent --header \"PRIVATE-TOKEN: $DEPLOY_TOKEN\" \\\n        https:\/\/gitlab.com\/api\/v4\/projects\/$CI_PROJECT_ID\/releases\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n<\/code><\/pre>\n<p>Parce que le runner <code>secure-deploy<\/code> est enregistr\u00e9 avec l\u2019acc\u00e8s <code>ref_protected<\/code>, ce job de d\u00e9ploiement ne s\u2019ex\u00e9cutera que sur le runner sp\u00e9cifique au projet <strong>et<\/strong> uniquement lorsque le pipeline provient d\u2019une r\u00e9f\u00e9rence prot\u00e9g\u00e9e.<\/p>\n<h2>Exercice 3 : Environnements Prot\u00e9g\u00e9s et Approbations de D\u00e9ploiement<\/h2>\n<p>M\u00eame avec des variables prot\u00e9g\u00e9es et des runners \u00e0 port\u00e9e limit\u00e9e, vous pouvez souhaiter une validation humaine avant que le code n\u2019atteigne la production. Les <strong>environnements prot\u00e9g\u00e9s<\/strong> de GitLab fournissent exactement cela.<\/p>\n<h3>\u00c9tape 1 \u2014 Cr\u00e9er les Environnements<\/h3>\n<ol>\n<li>Naviguez vers <strong>Operate &gt; Environments &gt; New environment<\/strong>.<\/li>\n<li>Cr\u00e9ez deux environnements : <code>staging<\/code> et <code>production<\/code>.<\/li>\n<\/ol>\n<h3>\u00c9tape 2 \u2014 Prot\u00e9ger l\u2019Environnement de Production<\/h3>\n<ol>\n<li>Allez dans <strong>Settings &gt; CI\/CD &gt; Protected environments<\/strong> (disponible sur Premium\/Ultimate, ou sur le plan gratuit en auto-h\u00e9berg\u00e9).<\/li>\n<li>S\u00e9lectionnez <code>production<\/code>.<\/li>\n<li>Sous <strong>Allowed to deploy<\/strong>, restreignez aux <code>Maintainers<\/code> (ou \u00e0 un utilisateur sp\u00e9cifique).<\/li>\n<li>Sous <strong>Required approvals<\/strong>, d\u00e9finissez \u00e0 <strong>1<\/strong> (ou plus, selon votre politique).<\/li>\n<li>Ajoutez le(s) approbateur(s) d\u00e9sign\u00e9(s).<\/li>\n<\/ol>\n<h3>\u00c9tape 3 \u2014 Mettre \u00e0 Jour le Pipeline avec les D\u00e9finitions d\u2019Environnements<\/h3>\n<pre><code># .gitlab-ci.yml \u2014 Exercice 3\nstages:\n  - build\n  - test\n  - deploy\n\nbuild-job:\n  stage: build\n  script:\n    - echo \"Building the application...\"\n\ntest-job:\n  stage: test\n  script:\n    - echo \"Running tests...\"\n\ndeploy-staging:\n  stage: deploy\n  environment:\n    name: staging\n    url: https:\/\/staging.example.com\n  script:\n    - echo \"Deploying to staging...\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\ndeploy-production:\n  stage: deploy\n  tags:\n    - secure-deploy\n  environment:\n    name: production\n    url: https:\/\/prod.example.com\n  script:\n    - echo \"Deploying to production...\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n      when: manual           # N\u00e9cessite un clic humain\n  allow_failure: false        # Le pipeline reste bloqu\u00e9 jusqu'\u00e0 approbation\n<\/code><\/pre>\n<h3>Fonctionnement de l\u2019Approbation<\/h3>\n<ol>\n<li>Un push sur <code>main<\/code> d\u00e9clenche le pipeline.<\/li>\n<li><code>deploy-staging<\/code> s\u2019ex\u00e9cute automatiquement.<\/li>\n<li><code>deploy-production<\/code> affiche un bouton <strong>Play<\/strong> dans l\u2019interface du pipeline.<\/li>\n<li>Cliquer sur <strong>Play<\/strong> ne lance pas imm\u00e9diatement le job \u2014 GitLab v\u00e9rifie les r\u00e8gles de protection de l\u2019environnement et pr\u00e9sente une <strong>bo\u00eete de dialogue d\u2019approbation<\/strong> au(x) approbateur(s) d\u00e9sign\u00e9(s).<\/li>\n<li>Le job ne d\u00e9marre qu\u2019apr\u00e8s avoir re\u00e7u le nombre d\u2019approbations requis.<\/li>\n<\/ol>\n<p>Cette double porte \u2014 <code>when: manual<\/code> plus approbation d\u2019environnement \u2014 garantit qu\u2019aucune personne seule ne peut pousser du code directement en production sans revue.<\/p>\n<h2>Exercice 4 : Port\u00e9e du CI_JOB_TOKEN<\/h2>\n<p>Chaque job GitLab CI re\u00e7oit un token automatique dans la variable <code>CI_JOB_TOKEN<\/code>. Ce token authentifie les requ\u00eates API et Git <em>en tant que projet du pipeline<\/em>. Par d\u00e9faut, sa port\u00e9e est dangereusement large.<\/p>\n<h3>Le Risque<\/h3>\n<p>Sans restrictions, un job dans le Projet A peut utiliser <code>CI_JOB_TOKEN<\/code> pour cloner ou appeler l\u2019API de <em>n\u2019importe quel autre projet<\/em> dans le m\u00eame groupe (ou instance, selon les param\u00e8tres). Si un contributeur malveillant injecte un script dans un job CI, il peut exfiltrer du code depuis des d\u00e9p\u00f4ts non li\u00e9s.<\/p>\n<h3>\u00c9tape 1 \u2014 Restreindre la Port\u00e9e du Token<\/h3>\n<ol>\n<li>Allez dans <strong>Settings &gt; CI\/CD &gt; Token Access<\/strong>.<\/li>\n<li>Basculez <strong>Limit access to this project<\/strong> sur <strong>Enabled<\/strong>.<\/li>\n<li>Sous <strong>Allow CI job tokens from the following projects to access this project<\/strong>, ajoutez uniquement les projets qui ont r\u00e9ellement besoin d\u2019acc\u00e8s (mod\u00e8le de liste d\u2019autorisation).<\/li>\n<li>Sous <strong>Limit CI_JOB_TOKEN access to the following projects<\/strong> (sortant), ajoutez uniquement les projets que votre pipeline doit atteindre.<\/li>\n<\/ol>\n<h3>\u00c9tape 2 \u2014 Tester l\u2019Acc\u00e8s<\/h3>\n<pre><code># .gitlab-ci.yml \u2014 Exercice 4\nstages:\n  - test\n\ntest-token-allowed:\n  stage: test\n  script:\n    - echo \"Cloning an allowed project...\"\n    - git clone https:\/\/gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com\/my-group\/allowed-project.git\n    - echo \"Success \u2014 access permitted\"\n\ntest-token-denied:\n  stage: test\n  script:\n    - echo \"Cloning a non-allowed project...\"\n    - git clone https:\/\/gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com\/my-group\/restricted-project.git\n    # Sortie attendue : remote: HTTP Basic: Access denied\n    # fatal: Authentication failed \u2014 403 Forbidden\n  allow_failure: true\n<\/code><\/pre>\n<h3>\u00c9tape 3 \u2014 V\u00e9rifier<\/h3>\n<ol>\n<li>Ex\u00e9cutez le pipeline. <code>test-token-allowed<\/code> r\u00e9ussit et clone le projet autoris\u00e9.<\/li>\n<li><code>test-token-denied<\/code> \u00e9choue avec <strong>403 Forbidden<\/strong> car <code>restricted-project<\/code> n\u2019est pas dans la liste d\u2019autorisation.<\/li>\n<\/ol>\n<p><strong>Le\u00e7on cl\u00e9 :<\/strong> Restreignez toujours le <code>CI_JOB_TOKEN<\/code> \u00e0 l\u2019ensemble minimal de projets dont votre pipeline a r\u00e9ellement besoin. Consid\u00e9rez la port\u00e9e par d\u00e9faut \u00ab ouverte \u00bb comme une mauvaise configuration.<\/p>\n<h2>Exercice 5 : S\u00e9curit\u00e9 des Pipelines de Merge Request<\/h2>\n<p>Les pipelines de merge request (MR) s\u2019ex\u00e9cutent lorsqu\u2019un contributeur ouvre ou met \u00e0 jour une merge request. Ils sont essentiels pour la qualit\u00e9 du code \u2014 mais ils peuvent aussi constituer un vecteur d\u2019attaque s\u2019ils ne sont pas configur\u00e9s avec soin.<\/p>\n<h3>Le Risque<\/h3>\n<p>Lorsqu\u2019un contributeur externe fork votre projet et ouvre une MR, GitLab peut ex\u00e9cuter un pipeline sur cette MR. Si le pipeline a acc\u00e8s \u00e0 des variables prot\u00e9g\u00e9es ou \u00e0 des runners privil\u00e9gi\u00e9s, le code du contributeur pourrait exfiltrer des secrets.<\/p>\n<h3>\u00c9tape 1 \u2014 Configurer les R\u00e8gles de Pipeline MR<\/h3>\n<pre><code># .gitlab-ci.yml \u2014 Exercice 5\nstages:\n  - validate\n  - build\n  - deploy\n\n# --- Jobs s\u00fbrs pour les pipelines MR (aucun secret n\u00e9cessaire) ---\nlint:\n  stage: validate\n  script:\n    - echo \"Linting code...\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\nunit-tests:\n  stage: validate\n  script:\n    - echo \"Running unit tests...\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\n# --- Jobs n\u00e9cessitant des secrets \u2014 ne jamais ex\u00e9cuter sur les pipelines MR ---\nbuild-image:\n  stage: build\n  script:\n    - echo \"Building and pushing Docker image...\"\n    - echo \"Using REGISTRY_TOKEN = $REGISTRY_TOKEN\"  # Protected + Masked\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\ndeploy-production:\n  stage: deploy\n  tags:\n    - secure-deploy\n  environment:\n    name: production\n    url: https:\/\/prod.example.com\n  script:\n    - echo \"Deploying to production...\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n      when: manual\n<\/code><\/pre>\n<h3>Gestion des Pipelines MR de Forks par GitLab<\/h3>\n<ul>\n<li>Les pipelines d\u00e9clench\u00e9s par <code>merge_request_event<\/code> depuis un <strong>fork<\/strong> s\u2019ex\u00e9cutent automatiquement avec des <strong>permissions limit\u00e9es<\/strong>.<\/li>\n<li>Les variables prot\u00e9g\u00e9es ne sont <strong>jamais<\/strong> inject\u00e9es dans les pipelines MR de forks.<\/li>\n<li>Le <code>CI_JOB_TOKEN<\/code> dans les pipelines de forks a une port\u00e9e r\u00e9duite \u2014 il ne peut acc\u00e9der qu\u2019au projet source (fork), pas au projet cible.<\/li>\n<\/ul>\n<p>En s\u00e9parant vos jobs en \u00ab s\u00fbrs pour les MR \u00bb (lint, test) et \u00ab n\u00e9cessitant des secrets \u00bb (build, deploy), vous garantissez que les contributeurs peuvent valider leur code sans exposer les identifiants.<\/p>\n<h3>Bonnes Pratiques pour les Pipelines MR<\/h3>\n<ul>\n<li>N\u2019utilisez jamais <code>only\/except<\/code> \u2014 pr\u00e9f\u00e9rez <code>rules:<\/code> pour plus de clart\u00e9 et de fiabilit\u00e9.<\/li>\n<li>Conditionnez les jobs d\u00e9pendant de secrets avec <code>if: $CI_COMMIT_BRANCH == \"main\"<\/code> (ou une autre r\u00e9f\u00e9rence prot\u00e9g\u00e9e).<\/li>\n<li>Envisagez d\u2019activer <strong>Pipelines must succeed<\/strong> sous <strong>Settings &gt; Merge requests<\/strong> pour exiger que le pipeline MR r\u00e9ussisse avant la fusion.<\/li>\n<li>Activez les <strong>Merged results pipelines<\/strong> pour tester le <em>r\u00e9sultat de la fusion<\/em> plut\u00f4t que la seule branche source \u2014 cela d\u00e9tecte les probl\u00e8mes d\u2019int\u00e9gration plus t\u00f4t.<\/li>\n<\/ul>\n<h2>Exercice 6 : Renforcement Suppl\u00e9mentaire<\/h2>\n<p>Avec les variables, runners, environnements, tokens et pipelines MR s\u00e9curis\u00e9s, plusieurs contr\u00f4les suppl\u00e9mentaires am\u00e8nent votre pipeline \u00e0 un niveau de s\u00e9curit\u00e9 pr\u00eat pour la production.<\/p>\n<h3>D\u00e9lais d\u2019Expiration des Jobs<\/h3>\n<p>Les jobs sans limite de temps peuvent \u00eatre exploit\u00e9s pour le crypto-mining ou utilis\u00e9s pour maintenir un acc\u00e8s persistant. D\u00e9finissez des d\u00e9lais explicites :<\/p>\n<pre><code>deploy-production:\n  stage: deploy\n  timeout: 10 minutes\n  script:\n    - echo \"Deploying...\"\n<\/code><\/pre>\n<h3>Pipelines Interruptibles<\/h3>\n<p>\u00c9vitez le gaspillage de ressources et limitez la fen\u00eatre d\u2019exploitation des jobs malveillants de longue dur\u00e9e en marquant les jobs non critiques comme interruptibles :<\/p>\n<pre><code>lint:\n  stage: validate\n  interruptible: true     # Annul\u00e9 automatiquement si un pipeline plus r\u00e9cent d\u00e9marre\n  script:\n    - echo \"Linting...\"\n<\/code><\/pre>\n<h3>R\u00e8gles de Push (Restreindre la Cr\u00e9ation de Pipelines)<\/h3>\n<p>Sous <strong>Settings &gt; Repository &gt; Push rules<\/strong>, vous pouvez :<\/p>\n<ul>\n<li><strong>Rejeter les commits non sign\u00e9s<\/strong> \u2014 garantit que chaque commit est sign\u00e9 GPG.<\/li>\n<li><strong>Restreindre les noms de branches<\/strong> \u2014 imposer une convention de nommage (ex. <code>feature\/*<\/code>, <code>bugfix\/*<\/code>).<\/li>\n<li><strong>Emp\u00eacher le push de secrets<\/strong> \u2014 la r\u00e8gle de push int\u00e9gr\u00e9e de GitLab peut bloquer les fichiers correspondant aux patterns de secrets courants.<\/li>\n<\/ul>\n<h3>D\u00e9tection de Secrets avec GitLab SAST<\/h3>\n<p>Ajoutez le template int\u00e9gr\u00e9 de D\u00e9tection de Secrets de GitLab pour d\u00e9tecter les identifiants accidentellement committ\u00e9s :<\/p>\n<pre><code>include:\n  - template: Security\/Secret-Detection.gitlab-ci.yml\n<\/code><\/pre>\n<p>Cela ajoute un job <code>secret_detection<\/code> qui analyse chaque commit \u00e0 la recherche de cl\u00e9s API, tokens, mots de passe et autres patterns de secrets. Les r\u00e9sultats apparaissent dans l\u2019onglet <strong>Security<\/strong> des merge requests.<\/p>\n<h2>Le Pipeline Renforc\u00e9 Final<\/h2>\n<p>Voici le fichier <code>.gitlab-ci.yml<\/code> complet combinant tous les contr\u00f4les de s\u00e9curit\u00e9 de ce lab. Chaque ligne li\u00e9e \u00e0 la s\u00e9curit\u00e9 est comment\u00e9e.<\/p>\n<pre><code># .gitlab-ci.yml \u2014 Pipeline GitLab CI Enti\u00e8rement Renforc\u00e9\n\n# Inclure le scanner de d\u00e9tection de secrets int\u00e9gr\u00e9 de GitLab\ninclude:\n  - template: Security\/Secret-Detection.gitlab-ci.yml  # Analyse les secrets divulgu\u00e9s\n\nstages:\n  - validate\n  - build\n  - deploy\n\n# --- Param\u00e8tres par d\u00e9faut appliqu\u00e9s \u00e0 tous les jobs ---\ndefault:\n  timeout: 10 minutes        # Emp\u00eacher les jobs incontr\u00f4l\u00e9s\/exploit\u00e9s\n\n# --- S\u00fbr pour les pipelines de merge request (aucun secret n\u00e9cessaire) ---\nlint:\n  stage: validate\n  interruptible: true        # Annul\u00e9 si un pipeline plus r\u00e9cent d\u00e9marre\n  script:\n    - echo \"Linting source code...\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"  # Ex\u00e9cuter sur les MR\n    - if: $CI_COMMIT_BRANCH == \"main\"                    # Ex\u00e9cuter sur main\n\nunit-tests:\n  stage: validate\n  interruptible: true\n  script:\n    - echo \"Running unit tests...\"\n    - echo \"API_KEY length = ${#API_KEY}\"  # S\u00fbr : affiche uniquement la longueur\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\n# --- N\u00e9cessite des secrets \u2014 s'ex\u00e9cute uniquement sur la branche prot\u00e9g\u00e9e ---\nbuild-image:\n  stage: build\n  script:\n    - echo \"Building Docker image...\"\n    - echo \"Authenticating to registry...\"  # Utilise REGISTRY_TOKEN (Protected + Masked)\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"       # Uniquement sur la branche prot\u00e9g\u00e9e\n\n# --- D\u00e9ploiement staging \u2014 automatique sur main ---\ndeploy-staging:\n  stage: deploy\n  environment:\n    name: staging                            # Environnement suivi\n    url: https:\/\/staging.example.com\n  script:\n    - echo \"Deploying to staging...\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\n# --- D\u00e9ploiement production \u2014 manuel + approbation requise ---\ndeploy-production:\n  stage: deploy\n  tags:\n    - secure-deploy                          # S'ex\u00e9cute uniquement sur le runner sp\u00e9cifique au projet\n  environment:\n    name: production                         # Environnement prot\u00e9g\u00e9 avec approbations\n    url: https:\/\/prod.example.com\n  script:\n    - echo \"Deploying to production...\"\n    - |\n      curl --fail --silent \\\n        --header \"PRIVATE-TOKEN: $DEPLOY_TOKEN\" \\   # Variable Protected + Masked\n        --request POST \\\n        \"https:\/\/gitlab.com\/api\/v4\/projects\/$CI_PROJECT_ID\/deployments\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n      when: manual                           # N\u00e9cessite un d\u00e9clenchement humain\n  allow_failure: false                       # Le pipeline bloque jusqu'\u00e0 approbation\n  timeout: 5 minutes                         # D\u00e9lai plus serr\u00e9 pour les d\u00e9ploiements\n<\/code><\/pre>\n<h2>Nettoyage<\/h2>\n<p>Apr\u00e8s avoir termin\u00e9 le lab, nettoyez les ressources de test :<\/p>\n<ol>\n<li><strong>Supprimer ou archiver le projet de test :<\/strong> Allez dans <strong>Settings &gt; General &gt; Advanced &gt; Delete project<\/strong>.<\/li>\n<li><strong>Supprimer les variables CI\/CD :<\/strong> Si vous pr\u00e9voyez de conserver le projet, allez dans <strong>Settings &gt; CI\/CD &gt; Variables<\/strong> et supprimez les variables de test (<code>DEPLOY_TOKEN<\/code>, <code>DB_PASSWORD<\/code>, <code>API_KEY<\/code>).<\/li>\n<li><strong>D\u00e9senregistrer le runner de test :<\/strong><\/li>\n<\/ol>\n<pre><code># Lister les runners enregistr\u00e9s\nsudo gitlab-runner list\n\n# D\u00e9senregistrer le runner de test\nsudo gitlab-runner unregister --name \"secure-deploy-runner\"\n\n# Optionnellement, supprimer compl\u00e8tement GitLab Runner\nsudo gitlab-runner stop\nsudo gitlab-runner uninstall\nsudo rm \/usr\/local\/bin\/gitlab-runner\n<\/code><\/pre>\n<h2>Points Cl\u00e9s \u00e0 Retenir<\/h2>\n<ul>\n<li><strong>Prot\u00e9gez et masquez chaque variable secr\u00e8te.<\/strong> Les variables prot\u00e9g\u00e9es ne sont inject\u00e9es que sur les branches prot\u00e9g\u00e9es, et le masquage emp\u00eache l\u2019exposition accidentelle dans les logs. Utilisez l\u2019indicateur Hidden pour les secrets qui ne devraient jamais \u00eatre lisibles dans l\u2019interface.<\/li>\n<li><strong>Limitez la port\u00e9e des runners au niveau de confiance minimum requis.<\/strong> Utilisez des runners sp\u00e9cifiques au projet avec l\u2019acc\u00e8s <code>ref_protected<\/code> pour les jobs de d\u00e9ploiement. R\u00e9servez les runners partag\u00e9s aux \u00e9tapes non sensibles de build et de test.<\/li>\n<li><strong>Conditionnez les d\u00e9ploiements en production avec la protection d\u2019environnement et les approbations.<\/strong> Combiner <code>when: manual<\/code> avec un environnement prot\u00e9g\u00e9 et des approbateurs requis garantit qu\u2019aucune personne seule ne peut pousser en production sans contr\u00f4le.<\/li>\n<li><strong>Restreignez le CI_JOB_TOKEN \u00e0 une liste d\u2019autorisation explicite.<\/strong> La port\u00e9e par d\u00e9faut est trop large. Limitez l\u2019acc\u00e8s entrant et sortant aux seuls projets dont votre pipeline a r\u00e9ellement besoin.<\/li>\n<li><strong>S\u00e9parez les jobs de pipeline MR des jobs de d\u00e9ploiement.<\/strong> Les jobs de lint et de test sont s\u00fbrs pour les pipelines de merge request ; les jobs de build et de d\u00e9ploiement n\u00e9cessitant des secrets ne doivent s\u2019ex\u00e9cuter que sur les branches prot\u00e9g\u00e9es.<\/li>\n<li><strong>Superposez les contr\u00f4les suppl\u00e9mentaires : d\u00e9lais d\u2019expiration, jobs interruptibles, r\u00e8gles de push et d\u00e9tection de secrets.<\/strong> Chaque couche adresse un vecteur d\u2019attaque diff\u00e9rent et ensemble, elles cr\u00e9ent une d\u00e9fense en profondeur.<\/li>\n<\/ul>\n<h2>Prochaines \u00c9tapes<\/h2>\n<p>Continuez \u00e0 d\u00e9velopper vos connaissances en s\u00e9curit\u00e9 CI\/CD avec ces guides connexes :<\/p>\n<ul>\n<li><a href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/ci-cd-execution-models-trust-assumptions-security-guide-2\/\">Mod\u00e8les d\u2019Ex\u00e9cution CI\/CD et Hypoth\u00e8ses de Confiance<\/a> \u2014 Comprenez les implications de s\u00e9curit\u00e9 des diff\u00e9rentes architectures CI\/CD et o\u00f9 se situent les fronti\u00e8res de confiance.<\/li>\n<li><a href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/separation-of-duties-least-privilege-ci-cd-pipelines\/\">S\u00e9paration des Responsabilit\u00e9s et Moindre Privil\u00e8ge dans les Pipelines CI\/CD<\/a> \u2014 Apprenez \u00e0 concevoir des pipelines o\u00f9 aucun r\u00f4le ou token n\u2019a plus d\u2019acc\u00e8s que n\u00e9cessaire.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Aper\u00e7u GitLab CI est la deuxi\u00e8me plateforme CI\/CD la plus utilis\u00e9e dans l\u2019industrie, alimentant des millions de pipelines au sein d\u2019organisations de toutes tailles. Son int\u00e9gration \u00e9troite avec le contr\u00f4le de version la rend exceptionnellement pratique \u2014 mais cette m\u00eame int\u00e9gration cr\u00e9e une surface d\u2019attaque \u00e9tendue si les pipelines ne sont pas d\u00e9lib\u00e9r\u00e9ment renforc\u00e9s. Dans &#8230; <a title=\"Lab : S\u00e9curisation des Pipelines GitLab CI \u2014 Variables Prot\u00e9g\u00e9es, Runners et Environnements\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/lab-securing-gitlab-ci-pipelines-protected-variables-runners-environments\/\" aria-label=\"En savoir plus sur Lab : S\u00e9curisation des Pipelines GitLab CI \u2014 Variables Prot\u00e9g\u00e9es, Runners et Environnements\">Lire la suite<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49,53],"tags":[],"post_folder":[],"class_list":["post-533","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-gitlab-ci"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/533","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/comments?post=533"}],"version-history":[{"count":2,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/533\/revisions"}],"predecessor-version":[{"id":582,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/533\/revisions\/582"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/media?parent=533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/categories?post=533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/tags?post=533"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/post_folder?post=533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}