DevSecOps

Sécuriser une pipeline GitLab CI/CD sans ralentir vos livraisons

SAST, scan de dépendances et d'images, secrets, SBOM, signature cosign : comment intégrer la sécurité dans GitLab CI/CD sans freiner vos mises en production.

CloudCorner6 min de lecture

Beaucoup d'équipes voient la sécurité comme un frein : un scan de vingt minutes, des centaines d'alertes que personne ne lit, des déploiements bloqués pour une vulnérabilité sans impact réel. Résultat, les contrôles sont contournés ou désactivés. Chez Cloud Corner, nous partons du principe inverse : une pipeline bien conçue rend la sécurité invisible tant que tout va bien, et bruyante uniquement quand un risque réel apparaît. Voici notre approche pour GitLab CI/CD.

Le principe : décaler à gauche, mais intelligemment

Le « shift left » ne consiste pas à tout exécuter à chaque commit. Il s'agit de placer chaque contrôle au moment où il coûte le moins cher et apporte le plus d'information :

  • Au commit : détection de secrets et SAST, rapides et incrémentaux.
  • À la merge request : scan des dépendances, analyse des fichiers Dockerfile et d'infrastructure.
  • Au build : scan de l'image, génération du SBOM, signature.
  • Au déploiement : vérification de la signature et des politiques d'admission.

Les jobs de sécurité tournent en parallèle des tests, jamais en série derrière eux. C'est le premier levier pour ne pas allonger le temps de pipeline.

Les briques à intégrer

SAST avec Semgrep

Semgrep analyse le code source avec des règles lisibles et facilement extensibles. Nous le lançons uniquement sur les fichiers modifiés en merge request, et sur l'ensemble du dépôt lors d'un passage planifié nocturne.

Scan des dépendances et des images

Les dépendances applicatives (lockfiles npm, pip, Maven) et les images de conteneurs sont la source la plus fréquente de vulnérabilités connues. Trivy couvre les deux avec un seul outil, en mode « filesystem » pour le code et en mode « image » pour le résultat du build.

Détection de secrets

Gitleaks ou la détection native de GitLab repèrent les clés d'API et mots de passe committés. Activez-la aussi en pré-commit côté développeur : un secret qui n'entre jamais dans l'historique Git n'a pas besoin d'être révoqué.

SBOM et signature

Un SBOM (au format CycloneDX ou SPDX) liste précisément ce que contient votre artefact. Associé à une signature cosign, il permet de prouver l'origine d'une image et de réagir en quelques minutes lorsqu'une nouvelle faille est publiée.

Un exemple de pipeline avec to-be-continuous

Le framework to-be-continuous fournit des composants GitLab prêts à l'emploi qui évitent de maintenir des dizaines de lignes de YAML. Voici un extrait réaliste :

include:
  - component: $CI_SERVER_FQDN/to-be-continuous/docker/gitlab-ci-docker@6
    inputs:
      trivy-enabled: true
      sbom-disabled: false
  - component: $CI_SERVER_FQDN/to-be-continuous/semgrep/gitlab-ci-semgrep@3
  - component: $CI_SERVER_FQDN/to-be-continuous/gitleaks/gitlab-ci-gitleaks@3

stages:
  - build
  - test
  - package-build
  - package-test
  - publish
  - deploy

sign-image:
  stage: publish
  image:
    name: gcr.io/projectsigstore/cosign:v2.4.1
    entrypoint: [""]
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  script:
    - cosign sign --yes "$CI_REGISTRY_IMAGE@$(cat digest.txt)"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Le job de signature utilise un jeton OIDC émis par GitLab (id_tokens) : aucune clé privée n'est stockée dans le projet. C'est la signature « keyless » de Sigstore, vérifiable ensuite par l'identité de la pipeline.

Pensez à épingler les versions des composants et des images d'outils, idéalement par digest. Une pipeline de sécurité qui tire un latest est elle-même un risque de chaîne d'approvisionnement.

Une politique qui ne bloque que l'exploitable

Bloquer sur chaque CVE est le meilleur moyen de se faire contourner. Notre règle de base :

  1. Bloquer les vulnérabilités de sévérité haute ou critique, avec un correctif disponible, dans un composant réellement présent à l'exécution.
  2. Alerter sans bloquer pour le reste, avec un ticket créé automatiquement et une échéance.
  3. Ignorer explicitement les faux positifs et les risques acceptés, dans un fichier versionné, avec un motif et une date d'expiration.

Pour Trivy, cela donne par exemple :

trivy-image:
  stage: package-test
  image:
    name: aquasec/trivy:0.58.0
    entrypoint: [""]
  script:
    - trivy image
        --severity HIGH,CRITICAL
        --ignore-unfixed
        --ignorefile .trivyignore.yaml
        --exit-code 1
        "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"

L'option --ignore-unfixed écarte les failles sans correctif disponible : bloquer sur ce qu'on ne peut pas corriger n'aide personne. Le fichier d'exceptions peut ressembler à ceci :

vulnerabilities:
  - id: CVE-2025-00000
    statement: "Fonction vulnérable non appelée par notre code"
    expired_at: 2026-12-31

L'expiration est essentielle : une exception sans date devient vite une dette oubliée. Pour affiner la priorisation, croisez la sévérité avec la présence de la faille dans le catalogue KEV de la CISA ou son score EPSS, qui estiment l'exploitation réelle.

Gérer les secrets proprement

Le meilleur secret est celui qui n'existe pas. Par ordre de préférence :

  • OIDC fédéré : la pipeline s'authentifie auprès du cloud (AWS, GCP, Azure) ou de Vault avec un jeton court, lié au projet et à la branche. Aucun identifiant statique.
  • HashiCorp Vault : les secrets sont lus à la volée via secrets: dans le job, avec des politiques en moindre privilège.
  • Variables CI/CD : en dernier recours, toujours masquées (absentes des logs), protégées (disponibles uniquement sur les branches et tags protégés) et limitées à l'environnement concerné.

Exemple de lecture depuis Vault avec un jeton OIDC :

deploy:
  stage: deploy
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.org
  secrets:
    DB_PASSWORD:
      vault: production/db/password@secret
      file: false
  script:
    - ./deploy.sh
  environment: production

Mesurer pour ne pas ralentir

Une pipeline sécurisée reste une pipeline rapide si l'on garde quelques réflexes :

  • exécuter les scans en parallèle des tests avec needs, pas après ;
  • mettre en cache les bases de vulnérabilités de Trivy entre les jobs ;
  • limiter les scans complets aux branches principales et aux exécutions planifiées ;
  • suivre le temps moyen de pipeline et le délai de correction des vulnérabilités, deux indicateurs qui disent si la sécurité aide ou gêne vos équipes.

Déployer progressivement

Inutile de tout activer le même jour. Nous recommandons de commencer en mode « observation » : les jobs tournent, remontent leurs résultats dans la merge request, mais ne bloquent rien (allow_failure: true). Après deux ou trois semaines, vous connaissez le bruit de fond, vous avez traité les faux positifs évidents et vous pouvez passer les contrôles les plus fiables en mode bloquant. Cette montée en charge évite le rejet par les équipes, qui voient la sécurité comme une aide plutôt que comme une sanction.

Désignez aussi un responsable par projet pour le traitement des alertes. Une alerte sans propriétaire finit toujours par être ignorée, quel que soit l'outil.

Besoin d'un coup de main ?

Intégrer ces contrôles dans vos pipelines existantes, définir une politique réaliste ou migrer vos secrets vers OIDC et Vault demande un peu de méthode. Si vous souhaitez un regard extérieur ou un accompagnement, n'hésitez pas à nous contacter.

#gitlab#ci-cd#devsecops#securite

Un projet similaire ?

Parlons-en : un expert vous répond sous 24 heures ouvrées.

Nous contacter