DevOps · C6 Observabilité et sécurité · Chapitre 2 · 3 h
DevSecOps
Décaler la sécurité vers la gauche, analyse des dépendances et des images, secrets jamais commités, moindre privilège, conformité intégrée au pipeline.
Dans le modèle traditionnel, la sécurité arrive à la fin : le développement livre, une équipe spécialisée audite, et rend un rapport de quatre-vingts pages deux semaines avant la mise en production.
Le résultat est prévisible, et c'est exactement le mur du chapitre 1 sous une autre forme. Les corrections coûtent cher parce qu'elles arrivent tard. Le rapport est partiellement ignoré parce qu'il n'est pas tenable dans le délai. Et l'équipe apprend que la sécurité est un obstacle plutôt qu'une exigence.
Décaler la sécurité vers la gauche consiste à la répartir tout au long du flux qu'on a construit — dans l'éditeur, dans la demande de fusion, dans le pipeline, dans l'exécution — plutôt qu'à la concentrer en une porte finale.
Le coût de la correction
L'argument est économique avant d'être moral.
| Découverte | Coût relatif | Pourquoi |
|---|---|---|
| À l'écriture | 1 | le contexte est présent, rien n'est déployé |
| En revue | 5 | il faut réexpliquer, refaire passer le pipeline |
| En recette | 15 | l'artefact est à reconstruire, la validation à refaire |
| En production | 60 | incident, correctif d'urgence, parfois notification légale |
| Après exploitation par un tiers | 500+ | fuite de données, obligations réglementaires, réputation |
Les ordres de grandeur varient selon les études ; la forme de la courbe, elle, ne varie pas. C'est la boucle de rétroaction du chapitre 1, appliquée à la sécurité : plus la détection est proche de l'écriture, moins elle coûte.
D'où le principe d'organisation : chaque étape du pipeline porte une vérification de sécurité, aucune n'attend la fin.
Les dépendances
C'est le sujet le plus important en volume, et le plus mal traité.
Une application moderne écrit quelques milliers de lignes et en importe des centaines de milliers. Un projet Node ou Python typique dépend de plusieurs centaines de paquets, dont la plupart n'ont jamais été choisis par personne : ce sont des dépendances transitives, tirées par d'autres dépendances.
L'analyse de composition compare cet arbre à des bases de vulnérabilités publiques et signale les paquets concernés. C'est indispensable, et cela produit immédiatement un problème que le chapitre 10 a déjà nommé : le bruit.
Un projet de taille moyenne remonte couramment cent à trois cents alertes. La quasi-totalité n'est pas exploitable dans son contexte : la vulnérabilité est dans une fonction jamais appelée, dans un outil de développement absent de l'image finale, ou demande un accès que l'attaquant n'a pas. Une équipe qui reçoit trois cents alertes ne les traite pas — elle les ignore, et une alerte vraiment grave se perd dans le tas.
Le tri repose donc sur trois questions, dans cet ordre, et pas sur le score de gravité seul.
Le code vulnérable est-il atteignable ? Une faille dans une fonction que le projet n'appelle jamais n'est pas exploitable. Les outils modernes savent en partie le déterminer, et c'est le filtre le plus efficace.
Est-elle dans l'image de production ? Une vulnérabilité dans une dépendance de développement, absente de l'image finale grâce à la construction multi-étapes du chapitre 3, ne menace rien.
Est-elle accessible depuis l'extérieur ? Une faille exploitable seulement avec un accès local sur une machine déjà compromise n'a pas la même urgence qu'une exécution de code à distance sur le point d'entrée public.
Deux mesures complètent l'analyse. Le verrouillage des versions — le fichier de verrouillage committé — garantit que tout le monde installe exactement les mêmes versions, et fait de la mise à jour une décision plutôt qu'un effet de bord. Et la mise à jour régulière en petits lots : c'est encore le chapitre 1, une montée de version par semaine étant infiniment moins risquée qu'une migration de deux ans de retard un jour d'urgence.
Les images
Une image hérite des vulnérabilités de sa base. Une image construite sur une distribution complète embarque des centaines de paquets dont l'application n'utilise aucun — et chacun est de la surface d'attaque.
Trois mesures, qui sont celles du chapitre 3 vues sous un autre angle.
Réduire la surface. Une image alpine ou distroless contient un ordre de grandeur de
paquets en moins, donc un ordre de grandeur de vulnérabilités en moins — sans aucun effort de
correction.
Ne pas tourner en root. Une faille d'application donne alors les droits d'un compte restreint, et non ceux de l'administrateur du conteneur.
Analyser l'image dans le pipeline, et pas seulement les dépendances applicatives : la base vieillit toute seule, sans qu'aucune ligne de code n'ait changé.
Les secrets
Le chapitre 8 a posé la règle et sa conséquence : un secret ne va jamais dans Git, et un secret commité doit être considéré comme compromis et changé, la réécriture d'historique n'étant qu'un nettoyage tardif.
Il reste à l'empêcher d'entrer, et cela se fait en trois couches complémentaires.
Avant le commit, un crochet local refuse un ajout contenant un motif de secret. Rapide, mais contournable et dépendant de la configuration de chaque poste.
Dans le pipeline, une analyse systématique de la demande de fusion. Non contournable — c'est la couche qui compte — mais elle intervient après que le secret est parti sur le serveur, donc la rotation reste nécessaire.
Dans l'exécution, les secrets ne sont jamais dans l'image mais injectés au démarrage, et le chapitre 3 rappelait que la variable d'environnement n'est pas un mécanisme de secret. Les coffres-forts du chapitre 8 ajoutent ce qui manque : journalisation des accès, rotation, et surtout durée de vie limitée — un identifiant valable une heure a beaucoup moins de valeur pour un attaquant qu'un mot de passe permanent.
Accès et moindre privilège
Le principe de moindre privilège est le même qu'au chapitre 8 du cours de systèmes : chaque acteur ne reçoit que les droits strictement nécessaires à sa tâche.
Trois applications concrètes dans le contexte de ce cours.
Le pipeline. C'est souvent l'acteur le plus privilégié de toute l'organisation — il peut déployer en production, lire les secrets, modifier l'infrastructure. Un dépôt compromis donne alors tout. On restreint donc par branche et par environnement, et l'on préfère des identifiants éphémères obtenus à l'exécution à des jetons permanents stockés dans l'outil.
Les comptes de service. Un service qui lit une base n'a pas besoin des droits d'écriture, ni de la table des utilisateurs. Le compte par défaut, souvent tout-puissant, est le raccourci à éviter.
Les accès humains. Nominatifs, révocables, et journalisés — c'est la traçabilité du chapitre 1 du cours de cybersécurité, sans laquelle aucun incident n'est analysable.
Conformité intégrée
Dernière idée du chapitre, et elle est cohérente avec tout le reste : une exigence de sécurité écrite dans un document est une intention ; écrite dans le pipeline, c'est une garantie.
Une politique comme code exprime des règles vérifiables automatiquement : aucun conteneur en root, aucun port de base exposé publiquement, chiffrement obligatoire sur les volumes, image provenant d'un registre approuvé. Le plan Terraform du chapitre 7 ou le manifeste Kubernetes du chapitre 9 sont validés contre ces règles avant application.
L'audit devient alors un sous-produit : plutôt que de reconstituer six mois après qui a déployé quoi, on dispose de l'historique Git, des exécutions de pipeline et des journaux d'accès. C'est la même bascule que dans tout le semestre — d'un contrôle ponctuel et manuel vers une propriété continue et automatique.
Quiz · 1 question
Un analyseur remonte 280 vulnérabilités sur un projet. Comment traiter cela ?
- Les corriger toutes avant la prochaine mise en production, par ordre de gravité décroissante — tout corriger
- Trier par EXPLOITABILITÉ : le code vulnérable est-il atteignable ? est-il dans l'image de production ? est-il accessible de l'extérieur ? Sans ce tri, l'équipe ignore les 280 et perd aussi les graves — trier par exploitabilité
- Désactiver l'analyseur : à ce volume, il n'apporte aucune information — désactiver
Réponse : Deux cent quatre-vingts alertes sont ingérables, et c'est précisément le bruit du chapitre 10 : une équipe qui reçoit ce volume ne le traite pas, elle l'ignore — et une alerte vraiment grave se perd dans le tas. Corriger par ordre de gravité ne marche pas non plus, parce que le score de gravité est calculé DANS L'ABSOLU, sans connaître votre contexte : une note de 9,8 sur une fonction que votre code n'appelle jamais est moins urgente qu'une note de 6,5 sur votre point d'entrée public. Le tri se fait donc sur l'EXPLOITABILITÉ, en trois questions : le code vulnérable est-il atteignable depuis le vôtre ? est-il présent dans l'image de production, ou seulement dans une dépendance de développement écartée par la construction multi-étapes ? est-il accessible de l'extérieur ? Ce tri ramène couramment 280 alertes à cinq ou six qui méritent une action cette semaine.
Quiz · 1 question
Pourquoi le pipeline d'intégration continue est-il une cible privilégiée ?
- Parce qu'il exécute du code non vérifié provenant de l'extérieur — code non vérifié
- Parce qu'il est souvent l'acteur le PLUS PRIVILÉGIÉ de l'organisation : il déploie en production, lit les secrets et modifie l'infrastructure — compromettre le dépôt donne alors tout, sans avoir à attaquer la production directement — concentration de privilèges
- Parce qu'il est généralement exposé sur Internet sans authentification — exposition réseau
Réponse : Le pipeline concentre des pouvoirs que personne ne détient individuellement : il peut construire, signer, déployer en production, lire les secrets de tous les environnements et modifier l'infrastructure décrite au chapitre 7. Un attaquant qui obtient la capacité d'y faire exécuter du code — par une dépendance compromise, une contribution malveillante, ou un jeton fuité — n'a plus besoin d'attaquer la production : il la déploie. C'est le principe de moindre privilège du cours de systèmes qui s'applique ici, et il se décline en trois mesures : restreindre les droits PAR BRANCHE et PAR ENVIRONNEMENT — une demande de fusion n'a aucune raison de pouvoir déployer en production —, préférer des identifiants ÉPHÉMÈRES obtenus à l'exécution à des jetons permanents stockés dans l'outil, et journaliser. L'exécution de code extérieur est un vecteur réel, mais c'est la concentration de privilèges qui en fait une cible.
À vous
L'exercice fait le tri que le chapitre décrit, et il montre ce que le tri change.
Vous disposez d'un arbre de dépendances, de vulnérabilités avec leur score, et de trois informations de contexte : la fonction vulnérable est-elle appelée, la dépendance est-elle dans l'image de production, la faille est-elle exploitable à distance. Vous écrirez le tri et comparerez le classement par gravité seule au classement par exploitabilité — les deux premières places ne sont pas les mêmes.
La seconde partie compare deux images de base sur le nombre de paquets et de vulnérabilités héritées, ce qui chiffre l'argument du chapitre 3.
La troisième audite des permissions : un compte de service, ses droits accordés et ses droits réellement utilisés sur un mois de journaux. Vous calculerez l'écart, qui est exactement ce que le moindre privilège demande de supprimer.
Exercice de code
Triez des vulnérabilités par exploitabilité, chiffrez l'image minimale, puis auditez des permissions.
Point de départ
// ── 1. Des vulnérabilités, et leur contexte ───────────────────────────────
const VULNERABILITES = [
{ id: "CVE-2025-1001", paquet: "lodash", gravite: 9.8,
atteignable: false, enProduction: true, distance: true,
note: "prototype pollution dans une fonction que le projet n'appelle pas" },
{ id: "CVE-2025-1002", paquet: "express", gravite: 6.5,
atteignable: true, enProduction: true, distance: true,
note: "sur le point d'entrée public" },
{ id: "CVE-2025-1003", paquet: "webpack", gravite: 8.1,
atteignable: true, enProduction: false, distance: false,
note: "outil de construction, absent de l'image finale" },
{ id: "CVE-2025-1004", paquet: "postgres", gravite: 7.5,
atteignable: true, enProduction: true, distance: false,
note: "exploitable avec un accès local sur la machine" },
{ id: "CVE-2025-1005", paquet: "jsonwebtoken", gravite: 7.2,
atteignable: true, enProduction: true, distance: true,
note: "vérification de signature contournable" },
{ id: "CVE-2025-1006", paquet: "chalk", gravite: 5.3,
atteignable: false, enProduction: false, distance: false,
note: "coloration de sortie, développement seulement" },
];
function score(v) {
// ← à écrire : pondérer la gravité par l'exploitabilité RÉELLE.
// Non atteignable, hors production, ou non exploitable à distance
// doivent faire chuter la priorité.
return v.gravite;
}
// ── 2. Deux images de base ────────────────────────────────────────────────
const IMAGES = {
"node:20": { paquets: 412, vulnHautes: 17, vulnMoyennes: 63, mo: 1100 },
"node:20-alpine": { paquets: 47, vulnHautes: 1, vulnMoyennes: 4, mo: 130 },
"distroless/nodejs": { paquets: 12, vulnHautes: 0, vulnMoyennes: 1, mo: 110 },
};
// ── 3. Audit de permissions ───────────────────────────────────────────────
const COMPTE = {
nom: "service-commandes",
accordes: ["db:read", "db:write", "db:drop", "s3:read", "s3:write",
"secrets:read", "users:read", "users:write", "deploy:prod"],
};
const JOURNAL_UN_MOIS = [
"db:read", "db:read", "db:write", "db:read", "s3:read",
"db:write", "db:read", "s3:read", "db:read", "db:write",
];
// ── À VOUS ────────────────────────────────────────────────────────────────
// 1. Écrivez score(). Comparez le classement par GRAVITÉ et par
// EXPLOITABILITÉ : les deux premières places sont-elles les mêmes ?
// 2. Chiffrez ce que l'image minimale supprime — sans aucune correction.
// 3. Calculez l'écart entre droits accordés et droits utilisés.
for (const v of VULNERABILITES) console.log(" " + v.id + " " + v.gravite);
Solution
const VULNERABILITES = [
{ id: "CVE-2025-1001", paquet: "lodash", gravite: 9.8,
atteignable: false, enProduction: true, distance: true,
note: "fonction jamais appelée par le projet" },
{ id: "CVE-2025-1002", paquet: "express", gravite: 6.5,
atteignable: true, enProduction: true, distance: true,
note: "sur le point d'entrée public" },
{ id: "CVE-2025-1003", paquet: "webpack", gravite: 8.1,
atteignable: true, enProduction: false, distance: false,
note: "outil de construction, hors image finale" },
{ id: "CVE-2025-1004", paquet: "postgres", gravite: 7.5,
atteignable: true, enProduction: true, distance: false,
note: "demande un accès local" },
{ id: "CVE-2025-1005", paquet: "jsonwebtoken", gravite: 7.2,
atteignable: true, enProduction: true, distance: true,
note: "signature contournable" },
{ id: "CVE-2025-1006", paquet: "chalk", gravite: 5.3,
atteignable: false, enProduction: false, distance: false,
note: "développement seulement" },
];
function score(v) {
// Le score de gravité est calculé DANS L'ABSOLU. On le pondère par le
// contexte, dans l'ordre d'efficacité des trois filtres du cours.
let s = v.gravite;
if (!v.atteignable) s *= 0.15; // le filtre le plus efficace
if (!v.enProduction) s *= 0.10; // écartée par la construction multi-étapes
if (!v.distance) s *= 0.40; // demande un accès déjà obtenu
return s;
}
const classer = (cle) => [...VULNERABILITES].sort((a, b) => cle(b) - cle(a));
console.log("— 1. deux classements, deux priorités —");
console.log(" par GRAVITÉ seule | par EXPLOITABILITÉ");
const g = classer((v) => v.gravite), e = classer(score);
for (let i = 0; i < VULNERABILITES.length; i++) {
console.log(" " + (i + 1) + ". " + g[i].paquet.padEnd(13) + g[i].gravite.toFixed(1) +
" | " + (i + 1) + ". " + e[i].paquet.padEnd(13) +
score(e[i]).toFixed(2).padStart(5) + " " + e[i].note.slice(0, 34));
}
console.log(" Les deux premières places diffèrent : lodash, noté 9,8, tombe en");
console.log(" queue parce que la fonction vulnérable n'est jamais appelée ; express,");
console.log(" noté 6,5, passe en tête parce qu'il est sur le point d'entrée public.");
const aTraiter = VULNERABILITES.filter((v) => score(v) >= 5);
console.log(" À traiter cette semaine : " + aTraiter.length + " sur " +
VULNERABILITES.length + " — un volume tenable, donc réellement traité.");
console.log("");
console.log("— 2. ce que l'image minimale supprime, sans corriger une ligne —");
const IMAGES = {
"node:20": { paquets: 412, vulnHautes: 17, vulnMoyennes: 63, mo: 1100 },
"node:20-alpine": { paquets: 47, vulnHautes: 1, vulnMoyennes: 4, mo: 130 },
"distroless/nodejs": { paquets: 12, vulnHautes: 0, vulnMoyennes: 1, mo: 110 },
};
const base = IMAGES["node:20"];
for (const [nom, i] of Object.entries(IMAGES)) {
console.log(" " + nom.padEnd(20) + String(i.paquets).padStart(4) + " paquets | " +
String(i.vulnHautes).padStart(2) + " hautes, " + String(i.vulnMoyennes).padStart(2) + " moyennes | " +
String(i.mo).padStart(5) + " Mo" +
(nom === "node:20" ? "" : " −" + Math.round((1 - i.vulnHautes / base.vulnHautes) * 100) +
" % de vulnérabilités hautes"));
}
console.log(" Changer une ligne de FROM supprime seize vulnérabilités hautes.");
console.log(" Aucune correction de code, aucune montée de version applicative :");
console.log(" c'est le meilleur rapport effort/résultat de tout le chapitre.");
console.log("");
console.log("— 3. audit de permissions —");
const COMPTE = { nom: "service-commandes",
accordes: ["db:read", "db:write", "db:drop", "s3:read", "s3:write",
"secrets:read", "users:read", "users:write", "deploy:prod"] };
const JOURNAL = ["db:read", "db:read", "db:write", "db:read", "s3:read",
"db:write", "db:read", "s3:read", "db:read", "db:write"];
const utilises = new Set(JOURNAL);
const inutiles = COMPTE.accordes.filter((d) => !utilises.has(d));
console.log(" compte : " + COMPTE.nom);
console.log(" accordés : " + COMPTE.accordes.length + " droits");
console.log(" utilisés : " + utilises.size + " droits — " + [...utilises].join(", "));
console.log(" JAMAIS UTILISÉS (" + inutiles.length + ") : " + inutiles.join(", "));
const graves = inutiles.filter((d) => /drop|secrets|deploy|users:write/.test(d));
console.log(" dont " + graves.length + " particulièrement dangereux : " + graves.join(", "));
console.log(" Écart : " + Math.round((inutiles.length / COMPTE.accordes.length) * 100) +
" % des droits accordés ne servent à rien.");
console.log(" Un service qui lit des commandes peut détruire des tables, lire tous");
console.log(" les secrets et déployer en production. Le moindre privilège consiste");
console.log(" exactement à supprimer cet écart — et un mois de journaux suffit à");
console.log(" le calculer, sans deviner.");
En travaux pratiques
Travaux pratiques 11 · 1 h
Ajouter la sécurité au pipeline, sans le rendre inutilisable
Brancher trois vérifications sur le pipeline du TP 5, puis trier leurs résultats pour qu'ils restent exploitables.
Avant de commencer
- Le pipeline du TP 5, qui construit et publie une image
- Les outils trivy et gitleaks, ou leurs équivalents
Énoncé
- Analyser les dépendances — Lancez une analyse de composition sur le projet et comptez les vulnérabilités remontées. Notez le nombre : c'est lui qui pose le problème du chapitre.
- Trier par exploitabilité — Pour les dix plus graves, répondez aux trois questions : le code vulnérable est-il atteignable, est-il dans l'image de production, est-il exploitable à distance ? Comparez le classement obtenu à celui par gravité.
- Analyser l'image — Analysez l'image du TP 3, puis la même application construite sur une base complète. Comparez le nombre de vulnérabilités héritées. Indice : Vous n'aurez modifié qu'une ligne : celle du FROM.
- Empêcher les secrets d'entrer — Ajoutez un crochet local et une étape de pipeline qui refusent une modification contenant un secret. Testez avec une fausse clé, puis avec une référence à une variable d'environnement — la seconde ne doit PAS déclencher d'alerte.
- Mesurer les faux positifs — Passez le détecteur sur tout l'historique du dépôt. Comptez les alertes et la part réellement problématique. Que se passerait-il si vous laissiez ce niveau de bruit ?
- Auditer les droits du pipeline — Listez ce que votre pipeline a le droit de faire. Retirez tout ce dont il n'a pas besoin sur une demande de fusion — notamment le droit de déployer en production.
- Écrire une règle de conformité — Ajoutez une vérification automatique qui refuse tout manifeste déclarant un conteneur tournant en root. Testez-la sur un manifeste fautif.
C'est réussi quand
- Le pipeline échoue sur une fausse clé et passe sur une référence à une variable
- Vous savez nommer les trois ou quatre vulnérabilités qui méritent une action cette semaine
- Une demande de fusion ne peut plus déployer en production
- Un manifeste sans utilisateur non privilégié est refusé
Correction
jobs:
secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # tout l'historique
- run: gitleaks detect --redact --exit-code 1
dependances:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: trivy fs --severity HIGH,CRITICAL --exit-code 1 .
image:
needs: [secrets, dependances]
runs-on: ubuntu-latest
steps:
- run: docker build -t tickets:test .
- run: trivy image --severity CRITICAL --exit-code 1 tickets:testChaque étape porte une vérification, et aucune n'attend la fin — c'est décaler vers la gauche. Le job « secrets » lit tout l'historique : un secret retiré au commit suivant y est toujours.
par gravité par exploitabilité
1. lodash 9,8 1. express 6,5 point d'entrée public
2. webpack 8,1 2. jsonwebtoken 7,2 signature contournable
3. postgres 7,5 3. postgres 7,5 accès local requis
4. jsonwebtoken 7,2 … lodash 9,8 fonction jamais appelée
… webpack 8,1 hors image de productionLes deux premières places changent. Le score de gravité est calculé DANS L'ABSOLU, sans connaître votre contexte : une note de 9,8 sur du code que vous n'appelez jamais est moins urgente qu'une note de 6,5 sur votre point d'entrée. Le tri ramène couramment deux cents alertes à quatre qui méritent une action — donc qui seront réellement traitées.
FROM node:20 → 17 vulnérabilités hautes, 412 paquets, 1,1 Go FROM node:20-alpine → 1 vulnérabilité haute, 47 paquets, 130 Mo
Aucune correction de code, aucune montée de version applicative : seize vulnérabilités hautes disparaissent parce que les paquets qui les portaient n'ont jamais eu de raison d'être là. C'est le meilleur rapport effort/résultat de tout le chapitre, et c'est déjà ce que le TP 3 avait fait pour la taille.
[allowlist]
description = "faux positifs connus, justifiés un par un"
regexes = [
"process[.]env[.][A-Z_]+", # une référence, pas une valeur
"[$]2[aby][$][0-9]{2}[$]", # une empreinte bcrypt, pas un secret
]
paths = ["tests/fixtures/"] # jeux d'essai, clés facticesUn détecteur qui crie sur chaque empreinte et chaque exemple est désactivé au bout d'une semaine, et ne protège plus de rien — c'est le bruit d'alertes du chapitre 10. Les exceptions se justifient une par une, en commentaire, et se relisent : une liste d'exceptions qui grossit sans être relue redevient un trou.
# droits minimaux sur une demande de fusion
permissions:
contents: read
pull-requests: write
# AUCUN droit de déploiement : réservé au job déclenché sur main
# règle de conformité, refusée avant application
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.securityContext.runAsNonRoot
msg := "le conteneur doit tourner sous un utilisateur non privilégié"
}Le pipeline est souvent l'acteur le plus privilégié de l'organisation : le compromettre dispense d'attaquer la production. Restreindre par branche et par environnement est la mesure la plus rentable. Et une exigence écrite dans un document est une intention — écrite dans le pipeline, c'est une garantie.
Ce que ce semestre laisse
Onze chapitres plus tôt, le problème était une contradiction d'organisation : livrer vite d'un côté, ne rien casser de l'autre.
Le trajet a suivi celui d'une modification de code. Elle est écrite dans un dépôt dont la stratégie de branchement décide de la taille des lots. Elle est empaquetée avec tout ce dont elle a besoin, ce qui rend « ça marche sur ma machine » sans objet. Elle est vérifiée par un pipeline qui échoue vite, puis livrée par une stratégie qui limite le rayon d'impact. Elle atterrit sur une infrastructure décrite dans des fichiers plutôt que construite à la main, qu'un orchestrateur maintient dans l'état déclaré. Et elle est observée, mesurée, défendue.
Ce qu'il faut en garder tient peut-être en trois idées.
Les petits lots gouvernent tout. Fréquence de livraison, taille des branches, granularité des demandes de fusion, mises à jour de dépendances : le même raisonnement revient à chaque chapitre, et il explique à lui seul pourquoi vitesse et stabilité vont ensemble.
On ne cherche pas à empêcher les incidents, mais à en réduire l'ampleur et la durée. Le canari, le retour arrière, l'auto-réparation et le budget d'erreur disent tous la même chose : la panne aura lieu, et ce qui se mesure est ce qu'elle coûte.
Le bruit détruit le signal. Un test instable, une alerte non actionnable, trois cents vulnérabilités non triées produisent tous le même effet — une équipe qui apprend à ignorer, et qui rate ce qui compte. Ce n'est pas une question d'outil mais d'exigence sur ce qu'on accepte d'afficher en rouge.
À retenir
Flashcards · 5 cartes
- Que signifie décaler la sécurité vers la gauche, et sur quel argument ?
- La répartir tout au long du flux — éditeur, demande de fusion, pipeline, exécution — plutôt que de la concentrer en une porte finale. L'argument est ÉCONOMIQUE : le coût de correction croît fortement avec le retard de la détection, d'un facteur 1 à l'écriture à 60 en production et bien davantage après exploitation. Les ordres de grandeur varient selon les études, la forme de la courbe non. C'est la boucle de rétroaction du chapitre 1, appliquée à la sécurité.
- Comment trier des centaines d'alertes de vulnérabilités ?
- Par EXPLOITABILITÉ, pas par score de gravité — celui-ci est calculé dans l'absolu, sans connaître votre contexte. Trois questions, dans l'ordre : 1) LE CODE VULNÉRABLE EST-IL ATTEIGNABLE depuis le vôtre ? — le filtre le plus efficace. 2) EST-IL DANS L'IMAGE DE PRODUCTION, ou seulement dans une dépendance de développement écartée par la construction multi-étapes ? 3) EST-IL ACCESSIBLE DE L'EXTÉRIEUR ? Sans ce tri, l'équipe ignore les 280 alertes et perd aussi les graves.
- Quelles mesures réduisent le risque lié aux dépendances et aux images ?
- DÉPENDANCES : verrouiller les versions (fichier de verrouillage committé, pour que la mise à jour soit une décision et non un effet de bord) et METTRE À JOUR RÉGULIÈREMENT EN PETITS LOTS — une montée par semaine est infiniment moins risquée qu'une migration de deux ans de retard un jour d'urgence. IMAGES : réduire la surface (alpine, distroless : un ordre de grandeur de paquets en moins, donc de vulnérabilités), ne pas tourner en root, et ANALYSER L'IMAGE dans le pipeline — la base vieillit toute seule, sans qu'aucune ligne n'ait changé.
- Quelles sont les trois couches qui empêchent un secret d'entrer dans le dépôt ?
- 1) AVANT LE COMMIT : un crochet local — rapide, mais contournable et dépendant de chaque poste. 2) DANS LE PIPELINE : analyse systématique de la demande de fusion — non contournable, c'est la couche qui compte, mais elle intervient après que le secret est parti sur le serveur, donc la rotation reste nécessaire. 3) À L'EXÉCUTION : jamais dans l'image, injecté au démarrage, et de préférence depuis un coffre-fort qui apporte journalisation, rotation et DURÉE DE VIE LIMITÉE.
- Pourquoi le pipeline est-il une cible, et comment le protéger ?
- Parce qu'il est souvent l'acteur LE PLUS PRIVILÉGIÉ de l'organisation : il construit, signe, déploie en production, lit les secrets de tous les environnements et modifie l'infrastructure. Le compromettre dispense d'attaquer la production. Trois mesures : restreindre PAR BRANCHE et PAR ENVIRONNEMENT (une demande de fusion n'a aucune raison de pouvoir déployer en production), préférer des identifiants ÉPHÉMÈRES à des jetons permanents, et journaliser. Même principe pour les comptes de service : lire une base ne demande pas les droits d'écriture.