Aller au contenu
Culture DevOps high

Fil rouge DevSecOps : 3 projets progressifs

27 min de lecture

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é.

  • 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

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.


Niveau : Socle (débutant) Durée estimée : 1-2 jours Prérequis : Linux, Git, Docker installés

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.

Un dépôt Git contenant une application web simple (par exemple une API Node.js ou Python Flask) avec :

  • Un Dockerfile suivant les bonnes pratiques (image non-root, multi-stage, couche minimale)
  • Un docker-compose.yml pour lancer l'environnement local
  • Des hooks Git pre-commit qui bloquent les secrets et vérifient la syntaxe
  • Un README.md documentant comment lancer, tester et contribuer

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é.

Fenêtre de terminal
mkdir devsecops-projet && cd devsecops-projet
git init
mkdir -p src tests docs
# Créer un fichier .gitignore strict
cat > .gitignore << 'EOF'
.env
*.key
*.pem
secrets/
node_modules/
__pycache__/
.DS_Store
EOF

Vérification : git status ne doit montrer aucun fichier sensible.

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 : build
FROM node:24-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Stage 2 : runtime minimal
FROM node:24-alpine
# Créer un utilisateur dédié non-root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY src/ ./src/
USER appuser
EXPOSE 3000
CMD ["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.

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.

Fenêtre de terminal
# Installer pre-commit (macOS/Linux)
pip install pre-commit
# Créer la configuration
cat > .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-conflict
EOF
# Activer les hooks
pre-commit install

Vérification : créez un fichier test avec un faux secret (password=test123), faites git add, puis git commit. Le hook doit refuser le commit.

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 install
Aucun 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

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 fuiteBloqués avant le commit et avant le merge
Dépendances vulnérables inconnuesSBOM et scan CVE à chaque build
Image déployée sans preuve d'origineImage signée, vérifiable, attestée
Déploiement possible même en cas d'échec de scanQuality gate bloquant sur CVE critique

Un pipeline CI/CD qui enchaîne automatiquement :

  1. Détection de secrets dans le code
  2. Analyse statique (SAST) avec rapport
  3. Analyse des dépendances (SCA) avec seuil de blocage
  4. Génération d'un SBOM au format CycloneDX
  5. Build et scan de l'image Docker
  6. Signature cryptographique de l'image
  7. Publication dans un registre avec attestation

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 → push
.github/workflows/devsecops.yml
name: 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'

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.

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.

Fenêtre de terminal
# Générer le SBOM avec Syft
syft $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.

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:$TAG

Vérification : après push, vérifiez la signature :

Fenêtre de terminal
cosign verify $REGISTRY/$IMAGE:$TAG \
--certificate-identity-regexp=".*" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"

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éussi
deploy:
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.

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.


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

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.

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

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.

Fenêtre de terminal
# Créer un cluster kind à 3 nœuds (1 control-plane + 2 workers)
kind create cluster --name devsecops-lab \
--config - << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
# Vérification : tous les nœuds doivent être Ready
kubectl get nodes

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.

rbac-app.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: monapp-sa
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: monapp-role
namespace: production
rules:
# Autoriser uniquement la lecture des ConfigMaps de l'application
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["monapp-config"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: monapp-rolebinding
namespace: production
subjects:
- kind: ServiceAccount
name: monapp-sa
roleRef:
kind: Role
name: monapp-role
apiGroup: rbac.authorization.k8s.io

Vérification : testez que le ServiceAccount ne peut pas créer de pods :

Fenêtre de terminal
kubectl auth can-i create pods \
--as=system:serviceaccount:production:monapp-sa \
-n production
# Doit retourner : no

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 namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes: [Ingress]
---
# Autoriser uniquement le trafic depuis l'ingress controller vers l'app
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-to-app
namespace: production
spec:
podSelector:
matchLabels:
app: monapp
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 3000

Vé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.

Fenêtre de terminal
# Installer ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Attendre que ArgoCD soit prêt
kubectl wait --for=condition=available deployment/argocd-server \
-n argocd --timeout=300s
# Récupérer le mot de passe initial
kubectl get secret argocd-initial-admin-secret \
-n argocd -o jsonpath="{.data.password}" | base64 -d

Créez une Application ArgoCD qui pointe vers votre dépôt Git :

argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: monapp
namespace: argocd
spec:
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: true

Vérification : modifiez une variable dans votre dépôt Git, commitez. ArgoCD doit synchroniser automatiquement le changement sur le cluster.

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/v1
kind: PrometheusRule
metadata:
name: monapp-slo
namespace: production
spec:
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.

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é.

  • 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.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn