Déployer le code en sécurité
Sécuriser la chaîne d'approvisionnement logicielle : revues, automatisation, provenance des artefacts, builds vérifiables et autorisation au déploiement.
Le code que vous avez écrit avec soin et testé rigoureusement n'a aucun effet tant qu'il n'est pas construit et déployé — et c'est précisément à ce moment-là qu'un attaquant peut tout défaire. La question fondatrice de ce chapitre est troublante de simplicité : le code qui s'exécute en production est-il bien celui que vous croyez ? Un adversaire qui compromet la chaîne de build et de déploiement (build and deployment pipeline) contourne d'un seul coup toutes vos revues, tous vos tests, tout votre durcissement applicatif. Inspecter un artefact (artifact) déjà déployé pour décider s'il est sûr est, à lui seul, presque impossible. La parade consiste à poser des contrôles à chaque étape de la chaîne d'approvisionnement logicielle (software supply chain), de sorte que chaque maillon puisse prouver qu'il s'est exécuté correctement. Ce chapitre, fidèle au cadre défensif du livre, expose le modèle de menace pour mieux s'en protéger : modèle de menace, bonnes pratiques, stratégies avancées de mitigation, puis conseils pratiques de déploiement.
La chaîne d'approvisionnement logicielle
Les auteurs nomment chaîne d'approvisionnement logicielle le processus complet d'écriture, de construction, de test et de déploiement d'un système. Ces étapes recouvrent les responsabilités classiques d'un système de gestion de versions (version control system, VCS), d'un pipeline d'intégration continue (continuous integration, CI) et d'un pipeline de livraison continue (continuous delivery, CD). Quelle que soit l'organisation, on retrouve presque toujours les mêmes briques de base.
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐
│ Écrire │ ──► │ Construire│ ──► │ Tester │ ──► │ Déployer │
│ (commit │ │ (build du │ │ (le │ │ (config + │
│ au VCS) │ │ binaire) │ │ binaire) │ │ exécution) │
└──────────┘ └──────────┘ └──────────┘ └─────────────┘
│ │ │ │
▼ ▼ ▼ ▼
revue de provenance tests auto point de contrôle
code du binaire + scans (autorisation au
(multi-party) (signée) déploiement) Le vocabulaire mérite d'être précisé, car le livre l'emploie au sens large. Un build est toute transformation d'artefacts d'entrée en artefacts de sortie — où un artefact est n'importe quelle donnée : un fichier, un paquet, un commit Git, une image de machine virtuelle. Un test est un cas particulier de build dont la sortie est un résultat logique, en général « pass » ou « fail ». Les builds peuvent être chaînés : un processus de release peut d'abord « construire » des binaires depuis le code source, puis « construire » une image Docker depuis ces binaires, puis « tester » cette image en l'exécutant. Un déploiement (deployment), enfin, est toute affectation d'un artefact à un environnement : pousser un binaire sur un serveur, mettre à jour un objet Deployment de Kubernetes pour adopter une nouvelle image, démarrer une VM, modifier un schéma de base de données, publier un paquet .deb dans un dépôt apt, ou téléverser une APK sur le Google Play Store.
Note
Les contrôles posés autour des infrastructures de source, de build et de test n'ont qu'un effet limité si l'adversaire peut les contourner en déployant directement en production. Le principe directeur est donc clair : un système doit rejeter tout déploiement qui ne provient pas de la chaîne d'approvisionnement légitime. C'est cette exigence qui impose que chaque étape produise une preuve vérifiable de son passage.
Le modèle de menace de la chaîne de build
Avant de durcir quoi que ce soit, il faut identifier l'adversaire. Le chapitre se concentre — comme l'invite le chapitre 2 du livre sur le risque interne (insider risk) — sur les menaces que font peser les personnes de l'intérieur (insiders), ou les attaquants externes qui usurpent leur identité, sans présager de leur intention. Le livre distingue trois profils.
| Profil d'adversaire | Description | Posture |
|---|---|---|
| Initié bienveillant (benign insider) | Ingénieur de bonne foi qui peut commettre une erreur | Non malveillant, mais faillible |
| Initié malveillant (malicious insider) | Cherche à obtenir plus d'accès que son rôle ne l'autorise | Malveillant, légitime au départ |
| Attaquant externe | Compromet la machine ou le compte d'un ou plusieurs initiés | Malveillant, identité usurpée |
Le livre traite à égalité l'ingénieur qui, par mégarde, construit depuis une copie locale contenant des changements non revus, et l'attaquant qui tente de déployer un binaire piégé avec les privilèges d'un compte d'ingénieur compromis. Pour simplifier, on appelle ingénieur l'initié bienveillant et adversaire malveillant aussi bien l'initié hostile que l'attaquant externe. Penser comme un attaquant — toujours pour défendre — fait émerger une liste de menaces concrètes :
- un ingénieur soumet un changement qui introduit accidentellement une vulnérabilité ;
- un adversaire malveillant soumet un changement qui ouvre une porte dérobée (backdoor) ;
- un ingénieur construit par erreur depuis une version locale modifiée, non revue ;
- un ingénieur déploie une configuration nuisible — par exemple, des fonctions de débogage destinées au test mais activées en production ;
- un adversaire malveillant déploie un binaire modifié qui exfiltre les identifiants des clients ;
- un adversaire modifie les listes de contrôle d'accès (ACL) d'un bucket cloud pour exfiltrer des données ;
- un adversaire vole la clé d'intégrité (integrity key) servant à signer le logiciel ;
- un ingénieur déploie une vieille version au défaut connu ;
- le système de CI est mal configuré et accepte des builds depuis des dépôts arbitraires ;
- un adversaire téléverse un script de build sur mesure qui exfiltre la clé de signature, puis signe et déploie un binaire malveillant ;
- un adversaire piège le système de CD pour qu'il utilise un compilateur ou un outil de build « backdooré » qui produit un binaire malveillant.
Une fois cette liste dressée, on la met en regard des mitigations déjà en place et on documente leurs limites. Les menaces sans contre-mesure, ou dont les contre-mesures ont des angles morts significatifs, sont les axes d'amélioration prioritaires.
Les bonnes pratiques fondamentales
Quatre pratiques forment le socle. Elles comblent l'essentiel des failles du modèle de menace et instaurent une culture de sécurité et de fiabilité.
Exiger des revues de code
La revue de code (code review) consiste à faire relire toute modification du source par une ou plusieurs autres personnes avant son intégration ou son déploiement. Au-delà de la sécurité, elle diffuse la connaissance, installe des normes de codage, améliore la lisibilité et réduit les erreurs. Surtout, du point de vue défensif, la revue de code est une forme d'autorisation multipartite (multi-party authorization, MPA) : aucun individu seul n'a le privilège de soumettre un changement.
Pour être efficace, la revue doit remplir deux conditions. Elle doit être obligatoire — un adversaire n'est pas dissuadé s'il peut simplement s'en exempter. Et elle doit être suffisamment approfondie pour attraper les problèmes : le relecteur doit comprendre le détail du changement et ses implications, ou demander des éclaircissements à l'auteur, faute de quoi le processus dégénère en simple tamponnage (rubber-stamping). Des outils publics permettent de l'imposer : GitHub, GitLab ou BitBucket peuvent exiger un nombre minimal d'approbations par pull request ; Gerrit ou Phabricator peuvent s'articuler avec un dépôt configuré pour n'accepter que les poussées venant du système de revue.
Astuce
La revue de code a ses limites en sécurité : un changement habile peut passer à travers, et elle ne protège pas contre la collusion ni contre un attaquant qui compromet plusieurs comptes d'initiés. Elle est donc à déployer comme une mesure de défense en profondeur (defense in depth), aux côtés des tests automatisés et des autres recommandations — jamais comme rempart unique.
S'appuyer sur l'automatisation
Idéalement, des systèmes automatisés exécutent la plupart des étapes de la chaîne. L'automatisation offre un processus cohérent et reproductible, retire les humains de la boucle — donc prévient les erreurs et réduit la corvée (toil) — et, exécutée sur un système verrouillé, durcit la chaîne contre la subversion. Imaginez le scénario inverse : des ingénieurs construisant manuellement les binaires « de production » sur leur poste de travail. Chacun peut construire depuis la mauvaise version, inclure du code non revu ; pire, un attaquant ayant compromis le poste peut écraser le binaire local par une version piégée.
Ajouter de l'automatisation de façon sûre est délicat, car le système automatisé peut lui-même ouvrir des brèches. Le livre recommande au minimum trois choses.
| Recommandation | Intention défensive |
|---|---|
| Scripter et automatiser tous les pas de build, test, déploiement | Humains et machines exécutent les mêmes étapes, sans dérive |
| Exiger une revue par les pairs pour tout changement de configuration de la chaîne | Réduire erreurs et augmenter le coût des attaques (souvent via config-as-code) |
| Verrouiller le système automatisé contre toute altération par admins ou utilisateurs | Fermer tout chemin où un admin modifierait le pipeline ou ferait du SSH sans revue |
Le verrouillage est le plus ardu : il faut recenser tous les chemins par lesquels un administrateur pourrait modifier le comportement sans revue — configuration directe du pipeline CI/CD, accès SSH à la machine — et neutraliser chacun. « L'automatisation est un pari gagnant-gagnant : elle réduit la corvée tout en augmentant fiabilité et sécurité. Appuyez-vous dessus chaque fois que possible. »
Vérifier les artefacts, pas seulement les personnes
C'est le pivot conceptuel du chapitre. Vérifier qui a lancé un déploiement est insuffisant : cet acteur peut se tromper ou déployer sciemment du code malveillant. L'environnement de déploiement doit vérifier ce qui est déployé. Concrètement, il exige la preuve que chaque étape automatisée du processus a bien eu lieu, et les humains ne doivent pas pouvoir contourner l'automatisation — sauf si un autre contrôle compense cette action.
L'exemple Google est ici central. Sur Google Kubernetes Engine (GKE), l'autorisation binaire (Binary Authorization) n'accepte par défaut que les images signées par votre système de CI/CD, et l'on surveille le journal d'audit du cluster pour être alerté dès qu'un opérateur recourt au mécanisme de bris de glace (breakglass) pour déployer une image non conforme. La limite de cette approche est qu'elle suppose tous les composants sains : que la CI/CD n'accepte que des sources autorisées en production, que les clés de signature ne soient accessibles qu'à la CI/CD, et ainsi de suite. Les stratégies avancées, plus loin, lèvent une partie de ces hypothèses implicites.
À retenir
La vérification du qui reste néanmoins nécessaire, au titre du moindre privilège (least privilege). Elle ne suffit pas, mais elle n'est pas inutile : on combine « qui a le droit de déclencher » (autorisation) et « quel artefact est légitime » (vérification), deux contrôles indépendants qui se renforcent.
Traiter la configuration comme du code
La configuration d'un service est tout aussi critique pour la sécurité et la fiabilité que son code. Toutes les bonnes pratiques de versionnement et de revue s'y appliquent donc : traiter la configuration comme du code (configuration as code), c'est exiger que tout changement de configuration soit commité, revu et testé avant déploiement. Si quelqu'un pointait votre frontend de production vers un backend de test, vous auriez un grave problème de sécurité et de fiabilité. Exemple pratique : un système Kubernetes stocke sa configuration dans un fichier YAML sous contrôle de version ; le déploiement passe ce YAML à kubectl. En n'autorisant que du YAML « approuvé » — issu du contrôle de version avec revue obligatoire —, on rend bien plus difficile une mauvaise configuration du service.
L'avantage majeur : on réutilise tous les contrôles du chapitre pour protéger aussi la configuration, ce qui est généralement plus simple que de bâtir un système d'autorisation multipartite séparé. La pratique reste pourtant moins répandue que pour le code : un ingénieur qui ne songerait jamais à construire un binaire de prod depuis une copie locale modifiée pourrait, lui, déployer un changement de config sans l'enregistrer ni le faire relire. La bascule exige des changements de culture (valoriser la revue), d'outillage (comparer facilement les changements, prévoir une surcharge manuelle d'urgence) et de processus.
Piège courant
N'enregistrez jamais de secrets dans le contrôle de version ni dans le code source. Mots de passe, clés cryptographiques et jetons d'autorisation doivent vivre dans un système de gestion de secrets dédié, ou être chiffrés via un système de gestion de clés comme Cloud KMS. Limitez strictement leur accès : n'accordez l'accès qu'aux services, et seulement quand c'est nécessaire — jamais directement aux humains. Si un humain a besoin d'un secret, c'est probablement un mot de passe, pas un secret applicatif ; créez alors des identifiants distincts pour les humains et pour les services.
Mapper les menaces aux contre-mesures
Une fois les bonnes pratiques posées, on les confronte aux menaces du modèle. Le tableau suivant — directement adapté du livre — illustre menaces, mitigations et limites, car nommer honnêtement les angles morts fait partie de la démarche.
| Menace | Contre-mesure | Limite résiduelle |
|---|---|---|
| Ingénieur introduisant une vulnérabilité par accident | Revue de code + tests automatisés | Réduit fortement, sans éliminer |
| Adversaire ouvrant une porte dérobée | Revue de code (augmente coût et détection) | Ne protège pas contre collusion ni contre plusieurs comptes compromis |
| Build accidentel depuis une copie locale modifiée | CI/CD automatisée tirant toujours du bon dépôt | — |
| Déploiement d'une configuration nuisible | Config-as-code, même niveau de revue | Toute config ne se traite pas « comme du code » |
| Binaire piégé déployé en production | L'env. exige la preuve que la CI/CD l'a construit | Contournable par le breakglass d'urgence — d'où journalisation et audit |
| ACL d'un bucket modifiées pour exfiltrer | Traiter les ACL comme de la configuration | Ne protège pas contre collusion / comptes multiples |
| Vol de la clé d'intégrité de signature | Clé dans un KMS accessible à la seule CI/CD, avec rotation | Renvoie aux stratégies avancées |
Note
Le code tiers et open source mérite la même rigueur. Si vous faites pleinement confiance aux mainteneurs, à leur revue et à un import inviolable, importez-le comme du code « maison ». Sinon, faites une revue avant build, gardez éventuellement une copie interne et relisez chaque patch venant de l'amont — le niveau de revue suit votre niveau de confiance. Quoi qu'il en soit, surveillez toujours vos dépendances pour les rapports de vulnérabilité et appliquez vite les correctifs.
Restent quatre menaces que ces pratiques ne couvrent pas : le déploiement d'une vieille version au défaut connu, le CI mal configuré buildant depuis des dépôts arbitraires, l'exfiltration de la clé de signature via un script de build, et le compilateur « backdooré ». Elles appellent des contrôles avancés.
Stratégies avancées de mitigation
Ces mitigations, encore peu standardisées dans l'industrie, conviennent surtout aux organisations grandes ou particulièrement sensibles ; elles peuvent ne pas avoir de sens pour une petite structure à faible exposition au risque interne.
Provenance des binaires
Chaque build devrait produire une provenance du binaire (binary provenance) : un descriptif exact de comment l'artefact a été construit — ses entrées, la transformation appliquée, et l'entité qui a réalisé le build. À quoi cela sert-il ? Imaginez une enquête sur incident : un déploiement a eu lieu dans une fenêtre suspecte. Rétro-concevoir le binaire serait prohibitif ; il est bien plus simple d'inspecter le code source, de préférence via le contrôle de version. Mais encore faut-il savoir de quel source le binaire provient. La provenance répond à cette question — et elle est, de plus, indispensable aux politiques de déploiement décrites plus bas.
Les champs recommandés pour une provenance riche :
| Champ | Rôle |
|---|---|
| Authenticité (requis) | Pourquoi faire confiance à la provenance — typiquement une signature cryptographique couvrant le reste des champs (l'authenticité implique l'intégrité) |
| Sorties (requis) | Les artefacts produits, identifiés par hachage cryptographique de leur contenu |
| Entrées | Sources (commit Git, archive avec son SHA-256…) et dépendances (bibliothèques, outils, compilateurs) |
| Commande | La commande de build, idéalement structurée pour l'analyse automatisée |
| Environnement | Tout ce qu'il faut pour reproduire le build : architecture, variables d'environnement |
| Métadonnées d'entrée | Ex. l'horodatage du commit source, utile à l'évaluation de politique |
| Versionnement | Horodatage et numéro de format, pour invalider d'anciens builds sans subir d'attaque par retour arrière (rollback) |
La règle de prudence : tout ce qui n'est ni vérifié par le système de build (donc impliqué par la signature) ni présent dans les sources (donc relu en revue) doit être vérifié en aval. Si l'utilisateur peut passer des drapeaux de compilateur arbitraires — le drapeau -D de GCC permet de réécrire des symboles et donc de changer entièrement le comportement d'un binaire — le vérificateur doit valider ces drapeaux. Plus le processus de build valide de choses lui-même, mieux c'est. Pour un format concret, le livre cite deb-buildinfo de Debian, la documentation du projet Reproducible Builds, et les jetons JWT (JSON Web Tokens) pour signer et encoder.
Attention
La signature de code (code signing) augmente la confiance dans les binaires, mais sa valeur tient entièrement à ce qu'elle représente et à la protection de la clé. Faire confiance à un binaire Windows au seul motif qu'il porte une signature Authenticode valide est faible : un attaquant peut acheter ou voler un certificat valide pour quelques centaines à quelques milliers de dollars. Pour renforcer la signature, listez explicitement les signataires acceptés, verrouillez l'accès aux clés, et durcissez l'environnement de signature — considérez l'obtention d'une signature comme un « déploiement » à protéger selon ce chapitre.
Politiques de déploiement fondées sur la provenance
Plutôt qu'une approche purement « par signature », on décrit des politiques de déploiement (deployment policies) explicites énonçant les propriétés attendues de chaque environnement, que celui-ci confronte à la provenance des artefacts. Avantages : moins d'hypothèses implicites dans la chaîne, donc plus facile à analyser ; contrat de chaque étape clarifié, donc moins de risque de mauvaise configuration ; et une seule clé de signature par étape de build au lieu d'une clé par environnement — puisque c'est désormais la provenance, et non la clé, qui porte la décision.
L'exemple type est une architecture de microservices où l'on veut garantir que chaque microservice ne peut être construit que depuis le dépôt source qui lui correspond. Avec la seule signature, il faudrait une clé par dépôt et une CI/CD qui choisit la bonne — difficile à vérifier. Avec la provenance, la CI/CD émet une provenance indiquant le dépôt d'origine, signée d'une unique clé ; la politique de chaque microservice énonce le dépôt autorisé, en un seul endroit facile à auditer. Quelques règles types : source soumise au contrôle de version et relue ; source venant d'un emplacement précis ; build passé par le pipeline officiel ; tests réussis ; binaire explicitement autorisé pour cet environnement (pas de binaire « test » en production) ; version suffisamment récente ; code exempt de vulnérabilité connue selon un scan récent. Le framework in-toto offre un standard d'implémentation.
Un moteur de décision doit toujours franchir trois étapes, dans l'ordre :
1. AUTHENTICITÉ ── La provenance est-elle authentique ?
(vérifie la signature → garantit son intégrité)
│
▼
2. APPLICABILITÉ ── La provenance s'applique-t-elle à CET artefact ?
(compare le hash de l'artefact à celui du payload)
│
▼
3. CONFORMITÉ ── La provenance respecte-t-elle toutes les règles
de la politique ?
│
▼
✓ Déploiement autorisé / ✗ rejeté Le cas le plus simple — « l'artefact doit être signé par telle clé » — réalise les trois étapes d'un coup. Un cas plus riche, « l'image Docker doit provenir du dépôt GitHub mysql/mysql-server », demande de vérifier que la signature JWT valide avec la clé du build, que le sujet sub correspond au SHA-256 de l'artefact, et que input.source_uri vaut exactement l'URI du dépôt attendu.
Builds vérifiables, hermétiques, reproductibles
Un build est vérifiable (verifiable) si la provenance qu'il produit est digne de confiance — notion relative, qui dépend de votre modèle de menace. Le livre liste des exigences possibles : la compromission d'un poste de développeur ne doit pas compromettre l'intégrité de la provenance ; un adversaire ne peut altérer provenance ou sorties sans détection ; un build ne peut affecter l'intégrité d'un autre, en parallèle ou en série ; un build ne peut produire une provenance mensongère ; les non-administrateurs ne peuvent configurer des étapes de build qui violeraient ces règles. Trois architectures permettent d'atteindre la vérifiabilité.
| Architecture | Principe | Compromis |
|---|---|---|
| Service de build de confiance (trusted build service) | Le vérificateur exige que le build vienne d'un service qu'il approuve, qui signe la provenance avec une clé qui lui est exclusive | Construire une seule fois, sans exiger la reproductibilité. C'est le modèle interne de Google. |
| Reconstruction par soi-même (rebuild yourself) | Le vérificateur reproduit le build à la volée et compare bit à bit | Exige la reproductibilité ; non scalable (un build prend minutes/heures, une décision se prend en millisecondes) |
| Service de reconstruction (rebuilding service) | Un quorum de « rebuilders » reproduit le build et atteste la provenance | Hybride ; modèle utilisé par Debian quand l'autorité centrale est indésirable |
Trois concepts voisins, dont la terminologie n'est pas encore standard, méritent d'être distingués. Un build est hermétique (hermetic) quand toutes ses entrées — code, mais aussi compilateurs, outils, bibliothèques — sont spécifiées d'avance, sans ambiguïté (versions résolues ou hachages). L'hermétisme autorise l'analyse des entrées (détecter via la base CVE les composants vulnérables, vérifier la conformité des licences open source, bannir une bibliothèque non sûre), garantit l'intégrité des imports tiers, et permet le cherry-picking d'un correctif sans embarquer d'autres changements — ce qui réduit fortement le risque des releases d'urgence. Bazel en mode bac à sable (sandboxed), ou npm avec package-lock.json, en sont des exemples. Un build est reproductible (reproducible) quand les mêmes commandes sur les mêmes entrées produisent une sortie bit à bit identique ; cela exige presque toujours l'hermétisme et impose d'éliminer toute source de non-déterminisme (un compilateur qui insère un horodatage doit le figer). La reproductibilité rend la vérification possible par reconstruction, détecte tôt les fuites d'hermétisme, et améliore la mise en cache. Un build vérifiable, enfin, permet de déterminer la provenance de manière digne de confiance — il est souhaitable, sans être strictement requis, qu'il soit aussi reproductible et hermétique.
Entrées non fiables et non authentifiées
Au-delà de la simple signature, deux risques émergent quand l'organisation grandit. Les entrées non fiables (untrusted inputs) : beaucoup de services de build laissent des utilisateurs non-admins définir des commandes arbitraires (Jenkinsfile, .travis.yml, Makefile, BUILD). C'est, du point de vue de la sécurité, de « l'exécution de code à distance par conception » : une commande malveillante en environnement privilégié pourrait voler la clé de signature, falsifier la provenance, modifier l'état du système ou perturber un build voisin. La parade est la séparation des privilèges (privilege separation) : un orchestrateur de confiance met en place l'état initial sain, lance le build et crée la provenance signée, tandis que toutes les commandes définies par l'utilisateur s'exécutent dans un environnement sans accès à la clé ni à aucun privilège — bac à sable local ou machine séparée. Les entrées non authentifiées (unauthenticated inputs) : la moindre dépendance est une surface d'attaque ; une dépendance récupérée en HTTP sans TLS s'expose à une attaque de l'homme du milieu. D'où la recommandation de builds hermétiques, où seul l'orchestrateur récupère les entrées déclarées d'avance.
Points de contrôle et vérification post-déploiement
Pour « vérifier les artefacts, pas seulement les personnes », la décision doit se prendre à un point de contrôle (choke point) : un passage obligé par lequel transite toute requête de déploiement. Une décision prise ailleurs qu'à un choke point est contournable.
┌───────────┐ admission webhook /
│ Requête │ proxy de politique
│ déploiement├──► ┌─────────────────────┐
└───────────┘ │ MASTER (choke point)│ ◄── décision de politique
└──────────┬────────────┘
│ (accepte uniquement du master)
┌────────────────┼────────────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ worker │ │ worker │ │ worker │
└────────┘ └────────┘ └────────┘
▲ un adversaire ne peut PAS déployer directement sur un worker Avec Kubernetes, le nœud maître (master) est un bon choke point puisque tous les déploiements y transitent ; on configure les nœuds ouvriers (workers) pour n'accepter que les requêtes du master. Idéalement, le choke point prend lui-même la décision, directement ou par RPC : Kubernetes offre un Admission Controller webhook à cette fin, et sur GKE, Binary Authorization fournit un contrôleur d'admission hébergé. À défaut, on place un proxy devant le point d'admission — en veillant à n'autoriser l'admission que via ce proxy, sans quoi l'adversaire le contourne en parlant directement à l'admission.
Même avec une enforcement au déploiement, la vérification post-déploiement et la journalisation restent presque toujours souhaitables : les politiques évoluent et il faut réévaluer l'existant ; une requête a pu passer parce que le service de décision était indisponible (conception fail open, souvent nécessaire à la disponibilité au démarrage d'une enforcement) ; un opérateur a pu utiliser un breakglass ; les utilisateurs veulent tester une politique avant de la committer, ou un mode dry run qui n'alerte sans bloquer ; un enquêteur peut en avoir besoin a posteriori. Le point d'enforcement doit donc journaliser assez d'information pour rejouer la décision — la requête complète, plus tout état nécessaire.
Note
Chez Google, la vérification post-déploiement de Borg a buté sur un détail révélateur : les requêtes de « job » référencent des « allocs » et des « packages » existants. Il a fallu joindre trois sources de journaux — jobs, allocs et packages — pour reconstituer l'état complet et pouvoir évaluer la politique. Leçon : journaliser la requête seule est nécessaire mais pas toujours suffisant.
Conseils pratiques de déploiement
Les leçons les plus dures portent moins sur la technologie que sur la façon de déployer des changements fiables, débogables et compréhensibles.
- Y aller pas à pas. Sécuriser un aspect de la chaîne à la fois minimise le risque de perturbation et laisse aux équipes le temps d'apprendre les nouveaux flux. Un bug dans ces contrôles peut, au pire, provoquer une panne du service.
- Fournir des messages d'erreur exploitables. Un rejet doit expliquer quoi a échoué et comment corriger. Dire « ne respecte pas la politique » laisse l'utilisateur démuni ; dire « la source était X, la politique attendait Y » l'oriente. Google a abandonné un langage de politique très expressif au profit d'un langage très limité, justement parce qu'il permettait de meilleurs messages.
- Rendre la provenance non ambiguë. Le système de build de Google téléversait d'abord la provenance dans une base, asynchrone, indexée par le hash de l'artefact. Le cas du fichier vide est resté célèbre : des millions d'enregistrements de provenance liés au même hash, car de nombreux builds produisent un fichier vide. Conséquence : impossible de donner un message exploitable (« aucun de ces 497 129 enregistrements ne respecte la politique »), et un temps de vérification linéaire qui faisait exploser le SLO de latence de 100 ms de plusieurs ordres de grandeur. La recommandation : propager la provenance en ligne avec l'artefact (par exemple via une annotation passée à l'Admission Controller), plutôt que par une base.
- Créer des politiques non ambiguës. Concevez le système pour qu'une seule politique s'applique à un déploiement donné, et évitez la question « faut-il que les deux passent, ou une seule ? ». Une politique globale d'organisation se traite comme une méta-politique vérifiant que chaque politique individuelle respecte un critère commun.
- Inclure un bris de glace (breakglass). En urgence — détourner du trafic d'un backend défaillant quand la config-as-code serait trop lente —, il faut pouvoir contourner la politique. Mais parce qu'un adversaire peut exploiter ce mécanisme, tout déploiement breakglass doit déclencher une alarme et être audité vite ; il doit donc rester rare, sans quoi on ne distingue plus l'usage légitime de l'attaque.
Avec ces contrôles avancés, on couvre enfin les quatre menaces orphelines : la politique exige un scan de vulnérabilité récent (vieille version au défaut connu) ; la provenance prouve le dépôt source autorisé (CI mal configuré) ; la séparation des privilèges prive le composant exécutant les scripts utilisateur d'accès à la clé (exfiltration de clé) ; les builds hermétiques forcent à déclarer le compilateur dans le source, relu comme tout code (compilateur backdooré).
À retenir
- La chaîne d'approvisionnement logicielle (écrire → construire → tester → déployer) est une cible de choix : qui la compromet contourne toutes les autres défenses. La question fondatrice est « le code en production est-il bien celui qu'on croit ? ».
- Le modèle de menace vise les initiés — bienveillants, malveillants, ou usurpés par un attaquant externe — et l'on traite à égalité l'erreur et l'acte hostile.
- Quatre pratiques socles : revues de code obligatoires (une forme d'autorisation multipartite), automatisation verrouillée, vérifier les artefacts et pas seulement les personnes, et configuration comme du code.
- Le principe pivot : un environnement de déploiement doit rejeter tout ce qui ne vient pas de la chaîne légitime et exiger une preuve — incarné chez Google par Binary Authorization sur GKE et l'autorisation au déploiement de Borg.
- Les stratégies avancées reposent sur la provenance des binaires (provenance signée des entrées/transformation/sorties) et des politiques de déploiement fondées sur la provenance, vérifiées en trois temps : authenticité, applicabilité, conformité.
- Les builds vérifiables — hermétiques et reproductibles — produisent une provenance digne de confiance ; ils s'appuient sur la séparation des privilèges contre les entrées non fiables et sur l'hermétisme contre les entrées non authentifiées.
- En pratique : avancer pas à pas, messages d'erreur exploitables, provenance et politiques non ambiguës, points de contrôle (choke points) incontournables, vérification post-déploiement journalisée, et un breakglass rare, alarmé et audité.