Aller au contenu

TP DevSecOps complet avec GitLab (SAST, DAST, Dependency Scanning, Secret Detection)

Voici un TP DevSecOps complet qui combine SAST, DAST, Dependency Scanning et Secret Detection dans GitLab CI/CD — avec un maximum d’explications pas à pas.

Objectif : Intégrer la sécurité à chaque étape du pipeline CI/CD et apprendre à lire/corriger les rapports.


Compétences visées


Pré-requis

  1. Un projet GitLab (SaaS ou self-managed).
  2. Un Runner Docker (idéal en TP partagé) ou Shell local :
    • Linux : gitlab-runner register --executor docker (image par défaut alpine:latest)
    • Windows possible en Shell (SAST/Secret OK ; DAST conseillé sous Runner Docker Linux).
  3. Droit de créer des variables de projet (pour l’URL DAST si cible distante).

Note licensing : Sur GitLab Ultimate, les templates Security sont natifs.
Si certaines fonctionnalités sont limitées, vous trouverez en fin de TP une alternative “outils open source” (Gitleaks, Trivy, Bandit, etc.).


Projet de démonstration (mini app Node.js)

Arborescence

app/index.js

const express = require('express');
const app = express();

// Exemple volontairement simple
// on améliorera plus tard les en-têtes de sécurité et la validation des entrées

app.get('/', (req, res) => res.send('Salut depuis GitLab Security !'));
app.listen(8080, () => console.log('Serveur démarré sur le port 8080'));

app/package.json

{
  "name": "securite-demo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"(placeholder)\" && exit 0"
  },
  "dependencies": {
    "express": "^4.19.2"
  }
}

app/Dockerfile

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 8080
CMD ["npm", "start"]

Emplacement des contrôles sécurité dans le pipeline

Si on reprend la métaphore de la constrution d’une maison :

Pipeline GitLab — version complète

But : avoir un pipeline clair, segmenté par stages.

Vous allez créer un fichier .gitlab-ci.yml à la racine de votre projet :

# -------------------
# 1) Définition des stages
# -------------------
stages:
  - build
  - test
  - sast
  - secrets
  - deps
  - package
  - dast

# ----------------------
# 2) Variables utiles
# ----------------------
variables:
  # Utilisées si vous faites tourner l'app localement dans un job précédent
  # et que vous exposez le service sur le même runner (option "services")
  DAST_WEBSITE: "http://app:8080"
  DAST_FULL_SCAN_ENABLED: "true"

# ----------------------
# 3) Build de l'image app (Docker)
# -----------------------
build-image:
  stage: build
  image: docker:24.0.5
  services:
    - docker:24.0.5-dind
  variables:
    DOCKER_DRIVER: overlay2
  script:
    - echo "Build de l'image de l'application"
    - docker build -t "$CI_REGISTRY_IMAGE/app:$CI_COMMIT_SHORT_SHA" app
  rules:
    - if: $CI_COMMIT_BRANCH
  tags: [docker]
  artifacts:
    when: on_success
    paths:
      - app/
    expire_in: 1 day

# ------------------------------------------
# 4) Tests applicatifs (placeholder ici)
# ------------------------------------------
unit-tests:
  stage: test
  image: node:18-alpine
  script:
    - cd app && npm ci
    - npm test
  tags: [docker]

# ---------------------------
# 5) SAST — Static Application Security Testing
#    Inclut les scanners GitLab en fonction du langage
# ---------------------------
include:
  - template: Security/SAST.gitlab-ci.yml

sast:
  stage: sast
  needs: []       # SAST n'a pas besoin que l'app tourne
  tags: [docker]

# ---------------------------
# 6) Secret Detection — secrets dans l'historique git
# ---------------------------
include:
  - template: Security/Secret-Detection.gitlab-ci.yml

secret_detection:
  stage: secrets
  needs: []
  tags: [docker]

# ---------------------------
# 7) Dependency Scanning — CVE dans les libs & lockfiles
# ---------------------------
include:
  - template: Security/Dependency-Scanning.gitlab-ci.yml

dependency_scanning:
  stage: deps
  needs: []
  tags: [docker]

# ---------------------------
# 8) DAST — Dynamic Application Security Testing
#    Option A: scanner cible *locale* via "services" (app conteneurisée)
#    Option B: scanner une URL *déjà déployée* (staging)
# ---------------------------

# --- Option A : lancer l'app comme service pour DAST ---
dast:
  stage: dast
  image: docker:24.0.5
  services:
    - name: docker:24.0.5-dind
    - name: $CI_REGISTRY_IMAGE/app:$CI_COMMIT_SHORT_SHA
      alias: app
  variables:
    DOCKER_DRIVER: overlay2
    # DAST_WEBSITE défini plus haut → "http://app:8080"
    DAST_FULL_SCAN_ENABLED: "true"
  script:
    - echo "Lancement du scan DAST contre ${DAST_WEBSITE}"
    # Le template DAST s'exécute via 'dast' quand inclus,
    # ici, on fait un echo pour lisibilité du job.
  tags: [docker]
  needs:
    - job: build-image
  artifacts:
    reports:
      dast: gl-dast-report.json
    expire_in: 1 week
  # On injecte le template DAST au niveau global, mais selon GitLab,
  # il peut créer son propre job 'dast' ; si doublon, renommer ce job 'dast:active'

# --- Option B : (alternative) scanner une URL déjà déployée ---
# dast-staging:
#   stage: dast
#   variables:
#     DAST_WEBSITE: "https://votre-staging.exemple.com"
#     DAST_FULL_SCAN_ENABLED: "true"
#   tags: [docker]
#   rules:
#     - if: '$CI_COMMIT_BRANCH == "main"'
#   artifacts:
#     reports:
#       dast: gl-dast-report.json
#     expire_in: 1 week

Quelques explications et rappels

les stages ordonne le pipeline (build puis test puis analyses puis package puis dast).

l’include : template: Security/... active les scanners GitLab pour SAST, Secrets et les dépendances.

Avec DAST Option A (TP local) : on lance l’app comme “service” Docker dans le job DAST, accessible via http://app:8080.

Avec DAST Option B (prod/stage) : on scanne directement une URL déployée.

artifacts > reports > dast produit un rapport DAST lisible dans l’onglet Security et dans les MR widgets.

À vous de jouer : commencez par l’option A pour le TP local, puis passez à l’option B quand vous avez une URL de staging.

Exercices guidés

Exercice 1 — Lancer le pipeline sécurité

  1. Poussez le projet avec .gitlab-ci.yml et l’app Node.
  2. Vérifiez que SAST / Secret / Deps créent bien des jobs.
  3. Confirmez que le job DAST s’exécute (Option A ou B selon votre choix).

Critères de réussite :

Exercice 2 — Provoquer & corriger des alertes

Objectif : apprendre à lire/prioriser les vulnérabilités.

Exemple d’ajout d’en-têtes de sécurité (Node/Express) :

// app/index.js
const helmet = require('helmet');
app.use(helmet());

Pensez à l’installer : npm i helmet

Exercice 3 — Lecture des rapports & MR Security Widget

  1. Ouvrez une Merge Request vers main.
  2. Regardez le widget Security (s’il est dispo), il indique les vulnérabilités nouvelles vs existantes.
  3. Cliquez sur une vulnérabilité puis lisez la description, l’impact, la remédiation.
  4. Documentez (README ou commentaire MR) votre plan de correction.

Objectif : capacité à expliquer la vulnérabilité et justifier la correction.

Conclusion

Nous pourrions aller plus loin, mais ce serait trop long…

Auteur : Philippe Bouget