Les connaissances théoriques ne suffisent pas : DevSecOps se valide en faisant. Le rapport DORA 2025, rebaptisé « State of AI-assisted Software Development », le confirme d'ailleurs à sa manière : l'IA générative amplifie les pratiques existantes, en bien comme en mal. Une équipe sans réflexes de sécurité solides ne devient pas meilleure parce qu'elle code plus vite avec un assistant IA, elle produit simplement des problèmes plus rapidement.
Ces trois projets fil rouge DevSecOps sont conçus pour ancrer les compétences de chaque niveau du parcours dans un projet concret, avec des livrables précis et des critères de validation clairs. Chaque projet est indépendant, mais ils s'enchaînent : les artefacts du projet 1 sont réutilisés dans le projet 2, et le projet 2 est déployé dans le projet 3. À la fin des trois, vous obtenez une application complète, sécurisée, déployée sur Kubernetes, avec GitOps, policy as code et métriques de sécurité.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Construire un dépôt Git local avec des garde-fous de sécurité actifs dès le premier commit
- Chaîner secrets scanning, SAST, SCA et génération de SBOM dans un pipeline CI/CD
- Signer une image de conteneur avec Cosign et vérifier cette signature avant déploiement
- Déployer une application sur Kubernetes avec RBAC minimal, Network Policies et GitOps via ArgoCD
- Définir des SLO et des alertes, puis mesurer votre maturité avec la grille OWASP DSOMM
Comment utiliser ces projets fil rouge
Section intitulée « Comment utiliser ces projets fil rouge »Les trois projets ne sont pas interchangeables : chacun suppose que le précédent est terminé, parce que ses livrables deviennent les prérequis techniques du suivant (le dépôt du projet 1 devient l'entrée du pipeline du projet 2, l'image signée du projet 2 est ce qui se déploie dans le projet 3). Repérez votre niveau actuel dans l'onglet correspondant pour savoir par où commencer.
Commencez ici si vous débutez ou si vous découvrez DevSecOps. Vous n'avez besoin que d'un poste Linux avec Docker installé. Le projet 1 prend 1 à 2 jours.
Passez au projet 2 une fois que vous avez un pipeline CI/CD fonctionnel et que vous maîtrisez les concepts SBOM et supply chain. Le projet 2 prend 3 à 5 jours.
Le projet 3 suppose une bonne maîtrise de Kubernetes (RBAC, Network Policies, Helm) et de GitOps. Comptez 1 à 2 semaines selon votre niveau.
Projet 1, Socle local sécurisé
Section intitulée « Projet 1, Socle local sécurisé »Niveau : Socle (débutant) Durée estimée : 1-2 jours Prérequis : Linux, Git, Docker installés
Objectif
Section intitulée « Objectif »Mettre en place un environnement de développement local reproductible, avec des garde-fous de sécurité activés dès la première ligne de code. À la fin de ce projet, votre dépôt est prêt à être branché à un pipeline CI/CD.
Ce que vous construisez
Section intitulée « Ce que vous construisez »Un dépôt Git contenant une application web simple (par exemple une API Node.js ou Python Flask) avec :
- Un
Dockerfilesuivant les bonnes pratiques (image non-root, multi-stage, couche minimale) - Un
docker-compose.ymlpour lancer l'environnement local - Des hooks Git
pre-commitqui bloquent les secrets et vérifient la syntaxe - Un
README.mddocumentant comment lancer, tester et contribuer
1. Créer la structure du dépôt
Section intitulée « 1. Créer la structure du dépôt »Un dépôt propre commence par un .gitignore strict : il évite qu'un fichier sensible se retrouve versionné par accident, avant même que le premier hook de sécurité ne soit installé.
mkdir devsecops-projet && cd devsecops-projetgit initmkdir -p src tests docs
# Créer un fichier .gitignore strictcat > .gitignore << 'EOF'.env*.key*.pemsecrets/node_modules/__pycache__/.DS_StoreEOFVérification : git status ne doit montrer aucun fichier sensible.
2. Écrire un Dockerfile sécurisé
Section intitulée « 2. Écrire un Dockerfile sécurisé »Un Dockerfile sécurisé respecte trois principes : image de base minimale, build multi-stage (compiler dans une étape jetable, ne copier que le résultat dans l'image finale) et utilisateur non-root (le processus applicatif ne tourne jamais avec les droits root du conteneur).
# Stage 1 : buildFROM node:24-alpine AS builderWORKDIR /appCOPY package*.json ./RUN npm ci --only=production
# Stage 2 : runtime minimalFROM node:24-alpine# Créer un utilisateur dédié non-rootRUN addgroup -S appgroup && adduser -S appuser -G appgroupWORKDIR /appCOPY --from=builder /app/node_modules ./node_modulesCOPY src/ ./src/USER appuserEXPOSE 3000CMD ["node", "src/index.js"]Cet exemple utilise Node.js 24, la version LTS (Long Term Support, maintenue plus longtemps qu'une version courante) la plus récente : elle reste en support jusqu'en avril 2028, avec une phase active jusqu'à l'automne 2026 puis une phase de maintenance limitée aux correctifs de sécurité (Node 20, longtemps la référence, est sorti de maintenance en avril 2026). Vérifiez toujours la page officielle des releases avant de fixer une version dans un projet réel : une image de base obsolète est une des façons les plus fréquentes d'importer des CVE dès le premier build.
Vérification : docker build -t monapp:dev . doit réussir sans erreur.
3. Configurer les hooks pre-commit
Section intitulée « 3. Configurer les hooks pre-commit »Les hooks pre-commit exécutent des vérifications automatiquement avant chaque commit. Cela empêche d'envoyer accidentellement des secrets dans le dépôt, bien avant qu'un pipeline CI/CD n'ait la moindre chance de les intercepter.
# Installer pre-commit (macOS/Linux)pip install pre-commit
# Créer la configurationcat > .pre-commit-config.yaml << 'EOF'repos: # Détection de secrets - repo: https://github.com/Yelp/detect-secrets rev: v1.5.0 hooks: - id: detect-secrets
# Vérification shellcheck pour les scripts bash - repo: https://github.com/shellcheck-py/shellcheck-py rev: v0.10.0.1 hooks: - id: shellcheck
# Vérification des fichiers YAML - repo: https://github.com/adrienverge/yamllint rev: v1.35.1 hooks: - id: yamllint args: [-d, relaxed]
# Vérification basique des fichiers - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-merge-conflictEOF
# Activer les hookspre-commit installVérification : créez un fichier test avec un faux secret (password=test123), faites git add, puis git commit. Le hook doit refuser le commit.
4. Écrire le README
Section intitulée « 4. Écrire le README »Un bon README répond aux questions : comment lancer en local ? Comment contribuer ? Comment tester ? Un lecteur qui clone votre dépôt pour la première fois doit pouvoir démarrer sans poser de question à l'auteur.
## Lancer en local
docker compose up -d
## Tester
curl http://localhost:3000/health
## Contribuer
Les hooks pre-commit s'installent avec : pre-commit installAucun secret ne doit figurer dans le code. Utilisez .env.example.Livrable validé : un dépôt Git propre, un Dockerfile non-root multi-stage, des hooks pre-commit actifs, et un README.
Projet 2, Pipeline CI/CD avec supply chain sécurisée
Section intitulée « Projet 2, Pipeline CI/CD avec supply chain sécurisée »Niveau : Opérationnel (intermédiaire) Durée estimée : 3-5 jours Prérequis : Projet 1 terminé, compte GitHub ou GitLab, notions CI/CD
Objectif
Section intitulée « Objectif »Transformer votre dépôt du projet 1 en un vrai pipeline CI/CD sécurisé : scan de secrets, analyse statique (SAST), analyse de dépendances (SCA), génération de SBOM, signature d'image avec attestation. À la fin, chaque image publiée est vérifiable et traçable, ce qui correspond directement à la catégorie A03 Software Supply Chain Failures de l'OWASP Top 10:2025, apparue pour la première fois dans ce référentiel.
Le tableau suivant résume ce que change ce projet par rapport à un pipeline classique qui se contente de builder et déployer :
| Sans pipeline sécurisé | Avec ce projet |
|---|---|
| Secrets détectés (si détectés) après la fuite | Bloqués avant le commit et avant le merge |
| Dépendances vulnérables inconnues | SBOM et scan CVE à chaque build |
| Image déployée sans preuve d'origine | Image signée, vérifiable, attestée |
| Déploiement possible même en cas d'échec de scan | Quality gate bloquant sur CVE critique |
Ce que vous construisez
Section intitulée « Ce que vous construisez »Un pipeline CI/CD qui enchaîne automatiquement :
- Détection de secrets dans le code
- Analyse statique (SAST) avec rapport
- Analyse des dépendances (SCA) avec seuil de blocage
- Génération d'un SBOM au format CycloneDX
- Build et scan de l'image Docker
- Signature cryptographique de l'image
- Publication dans un registre avec attestation
1. Structurer les stages du pipeline
Section intitulée « 1. Structurer les stages du pipeline »Chaque outil de sécurité correspond à un stage distinct du pipeline. Séparer les stages permet de paralléliser ce qui peut l'être et d'identifier immédiatement quel contrôle a échoué, plutôt que de recevoir un unique message d'erreur générique. Voici la structure cible :
secrets-scan → sast → sca → build → sbom → image-scan → sign → pushname: DevSecOps Pipeline
on: [push, pull_request]
permissions: {} # Rien par défaut, chaque job déclare ce dont il a besoin
jobs: secrets-scan: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 # Historique complet pour scanner les commits persist-credentials: false - name: Scan secrets (Gitleaks) uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
sast: runs-on: ubuntu-latest needs: secrets-scan permissions: contents: read container: image: semgrep/semgrep@sha256:59fbed6127ea7c5dde3ba6a85142733bb20ea9aaa36120c953904f1539aaf66e # 1.168.0 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: SAST scan run: semgrep scan --config p/security-audit --config p/owasp-top-ten --error
sca: runs-on: ubuntu-latest needs: sast permissions: contents: read steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: SCA avec Trivy (dépendances) uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: scan-type: fs severity: CRITICAL,HIGH exit-code: '1'stages: [secrets, sast, sca, build, publish]
secrets-scan: stage: secrets image: ghcr.io/gitleaks/gitleaks:v8.30.1 script: - gitleaks detect --source . --exit-code 1
sast: stage: sast image: semgrep/semgrep:1.168.0 script: - semgrep scan --config=auto --error
sca: stage: sca image: aquasec/trivy:0.72.0 script: - trivy fs --exit-code 1 --severity CRITICAL,HIGH .Trois réflexes issus des incidents de la supply chain CI/CD des dernières années apparaissent dans cet exemple : permissions minimales au niveau du workflow (permissions: {} par défaut), actions pinnées par SHA de commit plutôt que par tag mutable, et persist-credentials: false sur le checkout pour ne pas laisser traîner un token exploitable. L'ancienne action returntocorp/semgrep-action, longtemps recommandée, est dépréciée depuis 2024 au profit d'un appel direct au binaire Semgrep, comme montré ci-dessus.
2. Générer et publier le SBOM
Section intitulée « 2. Générer et publier le SBOM »Le SBOM (Software Bill of Materials, la liste de tous les composants logiciels de votre application) doit être généré après le build de l'image, pas avant : c'est la seule façon de refléter fidèlement ce qui part réellement en production, couches de base incluses. Attachez-le comme artefact du pipeline.
# Générer le SBOM avec Syftsyft $IMAGE_NAME:$TAG \ -o cyclonedx-json \ --file sbom-$CI_COMMIT_SHA.json
# Vérifier le contenu (doit lister vos dépendances)cat sbom-$CI_COMMIT_SHA.json | jq '.components | length'Vérification : le fichier SBOM doit contenir toutes les bibliothèques listées dans votre package.json ou requirements.txt.
3. Signer l'image avec Cosign
Section intitulée « 3. Signer l'image avec Cosign »La signature cryptographique prouve que l'image n'a pas été modifiée après le build. Cosign utilise l'identité OIDC du pipeline pour signer sans clé privée gérée manuellement : c'est la signature keyless, popularisée par le projet Sigstore.
- name: Installer Cosign uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: Signature keyless (OIDC GitHub) run: cosign sign --yes $REGISTRY/$IMAGE:$TAG
- name: Attacher le SBOM comme attestation run: | cosign attest --yes \ --predicate sbom.json \ --type cyclonedx \ $REGISTRY/$IMAGE:$TAGVérification : après push, vérifiez la signature :
cosign verify $REGISTRY/$IMAGE:$TAG \ --certificate-identity-regexp=".*" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com"4. Définir des quality gates
Section intitulée « 4. Définir des quality gates »Un quality gate bloque automatiquement le déploiement si les critères de sécurité ne sont pas atteints. Sans seuil explicite, un scan qui trouve des vulnérabilités critiques ne fait que produire un rapport ignoré : c'est le gate qui transforme la détection en action. Définissez votre seuil minimal :
# Bloquer si CVE critique sans fix disponible- trivy image --exit-code 1 --severity CRITICAL --ignore-unfixed $IMAGE:$TAG
# Bloquer si secrets détectés- gitleaks detect --exit-code 1
# Autoriser le déploiement uniquement si les 3 stages précédents ont réussideploy: needs: [secrets-scan, sast, image-scan]Livrable validé : une image Docker publiée, signée, avec SBOM attaché. Le pipeline bloque les CVE critiques et les secrets.
Ressources pour ce projet
Section intitulée « Ressources pour ce projet »Chaque étape ci-dessus s'appuie sur un concept documenté ailleurs sur ce site : approfondissez-les si un point reste flou avant de passer au projet 3.
- Tests de sécurité SAST/DAST/SCA
- Supply Chain Security
- OWASP Top 10 appliqué au DevSecOps
- Gestion des vulnérabilités CVE
- Pipeline sécurisé
Projet 3, Déploiement Kubernetes sécurisé avec GitOps
Section intitulée « Projet 3, Déploiement Kubernetes sécurisé avec GitOps »Niveau : Expert Durée estimée : 1-2 semaines Prérequis : Projet 2 terminé, Kubernetes local (kind), ArgoCD
Objectif
Section intitulée « Objectif »Déployer l'application du projet 2 sur un cluster Kubernetes avec une posture de sécurité complète : RBAC, Network Policies, Pod Security Standards, secrets dynamiques, GitOps via ArgoCD, et observabilité avec alertes SLO.
Ce que vous construisez
Section intitulée « Ce que vous construisez »Un environnement Kubernetes multi-environnements (dev et prod) avec :
- Déploiement GitOps via ArgoCD (le cluster se synchronise depuis Git)
- RBAC minimal (chaque composant a les droits minimaux)
- Network Policies (seuls les flux nécessaires sont autorisés)
- Pod Security Standards en mode
restricted - Secrets dynamiques injectés par un gestionnaire de secrets (pas de secrets en clair dans Git)
- SLO définis avec alertes Grafana/AlertManager
- Audit DSOMM pour mesurer votre maturité DevSecOps
1. Mettre en place le cluster local avec kind
Section intitulée « 1. Mettre en place le cluster local avec kind »Kind (Kubernetes IN Docker) fait tourner chaque nœud du cluster dans un conteneur Docker : pas de script d'installation à exécuter avec les droits root, pas de dépendance à une distribution Linux particulière. C'est l'option la plus simple et la plus reproductible pour un lab local ou une CI.
# Créer un cluster kind à 3 nœuds (1 control-plane + 2 workers)kind create cluster --name devsecops-lab \ --config - << 'EOF'kind: ClusterapiVersion: kind.x-k8s.io/v1alpha4nodes: - role: control-plane - role: worker - role: workerEOF
# Vérification : tous les nœuds doivent être Readykubectl get nodes2. Configurer RBAC minimal
Section intitulée « 2. Configurer RBAC minimal »Le RBAC (Role-Based Access Control) contrôle qui peut faire quoi sur le cluster. Chaque namespace doit avoir son propre ServiceAccount avec des droits limités au strict minimum nécessaire : c'est le principe de moindre privilège appliqué à Kubernetes.
apiVersion: v1kind: ServiceAccountmetadata: name: monapp-sa namespace: production---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: monapp-role namespace: productionrules: # Autoriser uniquement la lecture des ConfigMaps de l'application - apiGroups: [""] resources: ["configmaps"] resourceNames: ["monapp-config"] verbs: ["get"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: monapp-rolebinding namespace: productionsubjects: - kind: ServiceAccount name: monapp-saroleRef: kind: Role name: monapp-role apiGroup: rbac.authorization.k8s.ioVérification : testez que le ServiceAccount ne peut pas créer de pods :
kubectl auth can-i create pods \ --as=system:serviceaccount:production:monapp-sa \ -n production# Doit retourner : no3. Appliquer les Network Policies
Section intitulée « 3. Appliquer les Network Policies »Par défaut, tous les pods Kubernetes peuvent communiquer entre eux, quel que soit le namespace. Les Network Policies définissent explicitement les flux autorisés : tout ce qui n'est pas listé est refusé, ce qui réduit fortement la surface d'attaque en cas de compromission d'un pod.
# Bloquer tout le trafic entrant par défaut dans le namespaceapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: default-deny-ingress namespace: productionspec: podSelector: {} policyTypes: [Ingress]---# Autoriser uniquement le trafic depuis l'ingress controller vers l'appapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-ingress-to-app namespace: productionspec: podSelector: matchLabels: app: monapp policyTypes: [Ingress] ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: ingress-nginx ports: - protocol: TCP port: 3000Vérification : lancez un pod temporaire dans un autre namespace et tentez de joindre l'application (kubectl run test --rm -it --image=busybox -- wget -qO- monapp.production:3000). La connexion doit échouer, sauf depuis l'ingress controller.
4. Installer ArgoCD et configurer le déploiement GitOps
Section intitulée « 4. Installer ArgoCD et configurer le déploiement GitOps »ArgoCD synchronise en continu l'état du cluster avec un dépôt Git : plus besoin de lancer manuellement kubectl apply, le cluster converge automatiquement vers l'état déclaré dans Git. Le projet est passé en version majeure 3.x depuis 2025, avec des sorties mineures régulières (3.1, 3.2, 3.3...) qui renforcent en continu la sécurité interne (mTLS entre composants, vérification de signature des commits Git) : les commandes ci-dessous restent valables sur les versions récentes.
# Installer ArgoCDkubectl create namespace argocdkubectl apply -n argocd \ -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Attendre que ArgoCD soit prêtkubectl wait --for=condition=available deployment/argocd-server \ -n argocd --timeout=300s
# Récupérer le mot de passe initialkubectl get secret argocd-initial-admin-secret \ -n argocd -o jsonpath="{.data.password}" | base64 -dCréez une Application ArgoCD qui pointe vers votre dépôt Git :
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: monapp namespace: argocdspec: project: default source: repoURL: https://github.com/votre-org/devsecops-projet targetRevision: main path: k8s/ destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true selfHeal: trueVérification : modifiez une variable dans votre dépôt Git, commitez. ArgoCD doit synchroniser automatiquement le changement sur le cluster.
5. Définir les SLO et configurer les alertes
Section intitulée « 5. Définir les SLO et configurer les alertes »Un SLO (Service Level Objective) définit l'objectif de disponibilité de votre application, par exemple 99,5 % de requêtes réussies sur une fenêtre glissante. Sans mesure, pas d'amélioration possible : le SLO transforme une intuition (« l'appli tourne bien ») en un chiffre vérifiable et alertable.
# Exemple PrometheusRule pour un SLO de disponibilité 99.5%apiVersion: monitoring.coreos.com/v1kind: PrometheusRulemetadata: name: monapp-slo namespace: productionspec: groups: - name: monapp-availability rules: # Taux d'erreur sur 5 minutes - record: job:http_inbound_requests:rate5m expr: rate(http_requests_total{job="monapp"}[5m]) # Alerte si disponibilité < 99.5% - alert: MonAppAvailabilityBelowSLO expr: | sum(rate(http_requests_total{job="monapp",status=~"5.."}[5m])) / sum(rate(http_requests_total{job="monapp"}[5m])) > 0.005 for: 5m labels: severity: critical annotations: summary: "Disponibilité en dessous du SLO (99.5%)"Vérification : ouvrez l'interface Prometheus et vérifiez que la règle MonAppAvailabilityBelowSLO apparaît dans l'onglet Alerts, à l'état inactive tant que le seuil n'est pas dépassé. Pour approfondir la notion de SLO, son lien avec le SLI (l'indicateur mesuré) et l'error budget (la marge d'erreur tolérée avant de devoir ralentir les livraisons), consultez SLO, SLI et Error Budgets.
6. Mesurer votre maturité avec DSOMM
Section intitulée « 6. Mesurer votre maturité avec DSOMM »Le DSOMM (DevSecOps Maturity Model, un référentiel maintenu par la communauté OWASP) évalue votre maturité sur cinq dimensions : test des applications, durcissement des builds, culture, déploiement et monitoring. Faire cet audit à la fin du projet 3 donne une photo honnête de ce que vous avez réellement mis en place, par opposition à ce que vous pensiez avoir couvert.
Faites un audit rapide à la fin du projet :
- Avez-vous des scans SAST dans votre pipeline ? Dimension « Test des applications »
- Avez-vous des images signées et des SBOM ? Dimension « Durcissement des builds »
- Avez-vous des Security Champions dans votre équipe ? Dimension « Culture »
- Avez-vous des SLO avec alertes ? Dimension « Monitoring »
Consultez l'audit de maturité DSOMM pour un guide complet, avec une grille détaillée par dimension et un plan d'action priorisé.
Livrable validé : une application déployée sur Kubernetes via GitOps, avec RBAC minimal, Network Policies, secrets dynamiques, SLO actifs, et un score DSOMM documenté.
À retenir
Section intitulée « À retenir »- Les trois projets forment une progression cohérente : environnement local, pipeline sécurisé, déploiement Kubernetes.
- Le projet 1 installe les réflexes : hooks pre-commit, Dockerfile non-root, pas de secrets dans Git, image de base à jour (Node 24, pas Node 20 qui est sorti de maintenance).
- Le projet 2 est le cœur opérationnel : SBOM, SCA, signature d'image, quality gates. C'est là que se concrétise la supply chain security, y compris le réflexe de pinner les actions CI par SHA et non par tag mutable.
- Le projet 3 ajoute la profondeur architecturale : RBAC, Network Policies, GitOps, SLO, audit de maturité.
- Chaque projet produit des livrables concrets vérifiables, pas juste du code qui « tourne ».
- Même les outils de sécurité peuvent être compromis : pinnez-les avec la même rigueur que le reste de votre supply chain.
- La montée en maturité est progressive : ne passez pas au projet 3 sans avoir validé le projet 2.