{"id":503,"date":"2026-02-01T13:15:44","date_gmt":"2026-02-01T12:15:44","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=503"},"modified":"2026-03-24T12:56:12","modified_gmt":"2026-03-24T11:56:12","slug":"build-integrity-reproducible-builds-ci-cd","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/build-integrity-reproducible-builds-ci-cd\/","title":{"rendered":"Int\u00e9grit\u00e9 des Builds et Builds Reproductibles : Guide Pratique pour le CI\/CD"},"content":{"rendered":"<h2>Introduction<\/h2>\n<p>Si vous ne pouvez pas reproduire un build, vous ne pouvez pas le v\u00e9rifier. Cette v\u00e9rit\u00e9 simple se trouve au c\u0153ur de la s\u00e9curit\u00e9 de la cha\u00eene d&rsquo;approvisionnement logicielle. L&rsquo;int\u00e9grit\u00e9 des builds garantit que ce que vous d\u00e9ployez est exactement ce que vous aviez l&rsquo;intention de construire \u2014 rien d&rsquo;ajout\u00e9, rien de modifi\u00e9, rien d&rsquo;alt\u00e9r\u00e9 entre le code source et l&rsquo;artefact de production.<\/p>\n<p>Ces derni\u00e8res ann\u00e9es, les attaques sur la cha\u00eene d&rsquo;approvisionnement ont d\u00e9montr\u00e9 que le processus de build lui-m\u00eame est une cible de grande valeur. Les attaquants qui compromettent un pipeline de build peuvent injecter du code malveillant dans des logiciels de confiance, affectant des millions d&rsquo;utilisateurs en aval. La seule d\u00e9fense fiable consiste \u00e0 rendre les builds reproductibles : \u00e0 partir des m\u00eames entr\u00e9es, vous devez toujours obtenir les m\u00eames sorties. Lorsque cette garantie est assur\u00e9e, tout \u00e9cart devient d\u00e9tectable.<\/p>\n<p>Ce guide parcourt les principes de l&rsquo;int\u00e9grit\u00e9 des builds et des builds reproductibles, explique pourquoi ils sont importants pour la s\u00e9curit\u00e9 du CI\/CD, et fournit des techniques pratiques \u2014 du pinning des d\u00e9pendances aux syst\u00e8mes de build herm\u00e9tiques \u2014 que vous pouvez adopter progressivement dans vos propres pipelines.<\/p>\n<h2>Qu&rsquo;est-ce que l&rsquo;int\u00e9grit\u00e9 des builds ?<\/h2>\n<p>L&rsquo;int\u00e9grit\u00e9 des builds est la garantie que les entr\u00e9es de build produisent de mani\u00e8re d\u00e9terministe les sorties de build. Autrement dit, si vous prenez le m\u00eame code source, les m\u00eames d\u00e9pendances, la m\u00eame cha\u00eene d&rsquo;outils et les m\u00eames instructions de build, vous devez obtenir un artefact identique bit \u00e0 bit \u00e0 chaque fois, ind\u00e9pendamment du moment ou de l&rsquo;endroit o\u00f9 vous ex\u00e9cutez le build.<\/p>\n<h3>Pourquoi les builds non reproductibles sont un risque de s\u00e9curit\u00e9<\/h3>\n<p>Lorsque les builds ne sont pas reproductibles, vous perdez la capacit\u00e9 de les v\u00e9rifier. Si deux builds \u00e0 partir du m\u00eame code source produisent des binaires diff\u00e9rents, comment savoir lequel est correct ? Comment d\u00e9tecter si un attaquant a inject\u00e9 du code pendant le processus de build ? Vous ne pouvez tout simplement pas. La non-reproductibilit\u00e9 cr\u00e9e un brouillard que les attaquants exploitent.<\/p>\n<p>Consid\u00e9rez le probl\u00e8me de v\u00e9rification : un auditeur souhaite confirmer qu&rsquo;un binaire publi\u00e9 correspond \u00e0 son code source publi\u00e9. Il r\u00e9cup\u00e8re le commit \u00e9tiquet\u00e9, lance le build et compare. Si le build n&rsquo;est pas reproductible, les sorties diff\u00e9reront \u2014 et l&rsquo;auditeur n&rsquo;a aucun moyen de distinguer une diff\u00e9rence l\u00e9gitime (caus\u00e9e par un horodatage ou un ordonnancement al\u00e9atoire) d&rsquo;une modification malveillante.<\/p>\n<h3>La relation avec les niveaux SLSA Build<\/h3>\n<p>Le <a href=\"https:\/\/slsa.dev\" target=\"_blank\" rel=\"noopener\">framework SLSA<\/a> (Supply-chain Levels for Software Artifacts) traite directement de l&rsquo;int\u00e9grit\u00e9 des builds \u00e0 travers ses niveaux de build :<\/p>\n<ul>\n<li><strong>SLSA Build L1 :<\/strong> Le processus de build est document\u00e9 et produit des m\u00e9tadonn\u00e9es de provenance.<\/li>\n<li><strong>SLSA Build L2 :<\/strong> Le build s&rsquo;ex\u00e9cute sur un service h\u00e9berg\u00e9 qui g\u00e9n\u00e8re une provenance authentifi\u00e9e.<\/li>\n<li><strong>SLSA Build L3 :<\/strong> L&rsquo;environnement de build est renforc\u00e9, isol\u00e9 et r\u00e9sistant aux alt\u00e9rations \u2014 m\u00eame par les mainteneurs du projet.<\/li>\n<\/ul>\n<p>Les builds reproductibles compl\u00e8tent SLSA en fournissant un m\u00e9canisme de v\u00e9rification ind\u00e9pendant. M\u00eame si vous faites confiance au service de build (L2\/L3), la reproductibilit\u00e9 permet \u00e0 quiconque de reconstruire \u00e0 partir du source et de v\u00e9rifier que la sortie correspond.<\/p>\n<h3>Exemples concrets<\/h3>\n<p><strong>SolarWinds (2020) :<\/strong> Des attaquants ont compromis le syst\u00e8me de build de SolarWinds et inject\u00e9 une porte d\u00e9rob\u00e9e dans la mise \u00e0 jour de la plateforme Orion. Le code malveillant a \u00e9t\u00e9 ajout\u00e9 pendant le processus de build, de sorte que le d\u00e9p\u00f4t de code source semblait propre. Un syst\u00e8me de build reproductible aurait rendu cela d\u00e9tectable \u2014 reconstruire \u00e0 partir du source publi\u00e9 aurait produit un artefact diff\u00e9rent de celui distribu\u00e9 aux clients.<\/p>\n<p><strong>XZ Utils (2024) :<\/strong> Une attaque sophistiqu\u00e9e sur la cha\u00eene d&rsquo;approvisionnement a cibl\u00e9 la biblioth\u00e8que de compression xz. Un mainteneur malveillant a introduit du code de porte d\u00e9rob\u00e9e obfusqu\u00e9 via l&rsquo;infrastructure de test du syst\u00e8me de build. Le code inject\u00e9 \u00e9tait con\u00e7u pour compromettre l&rsquo;authentification SSH sur les syst\u00e8mes Linux affect\u00e9s. L&rsquo;attaque a exploit\u00e9 la complexit\u00e9 du processus de build, injectant la charge malveillante via des fichiers de test binaires trait\u00e9s pendant le build. Des builds reproductibles et un examen attentif du comportement au moment du build auraient lev\u00e9 des signaux d&rsquo;alerte bien plus t\u00f4t.<\/p>\n<h2>Sources de non-reproductibilit\u00e9<\/h2>\n<p>Comprendre pourquoi les builds diff\u00e8rent entre les ex\u00e9cutions est la premi\u00e8re \u00e9tape pour les corriger. Voici les sources les plus courantes de non-d\u00e9terminisme dans les processus de build :<\/p>\n<h3>Horodatages int\u00e9gr\u00e9s dans les artefacts<\/h3>\n<p>De nombreux outils de build int\u00e8grent la date et l&rsquo;heure actuelles dans les fichiers de sortie. Les fichiers JAR Java contiennent des horodatages dans leurs entr\u00e9es ZIP. Les compilateurs C\/C++ peuvent enregistrer les macros <code>__DATE__<\/code> et <code>__TIME__<\/code>. Les ex\u00e9cutables PE sous Windows incluent un horodatage dans leurs en-t\u00eates. \u00c0 chaque build, l&rsquo;horodatage change, produisant une sortie diff\u00e9rente.<\/p>\n<h3>Ordonnancement non d\u00e9terministe des fichiers<\/h3>\n<p>Les formats d&rsquo;archive comme tar et zip ne garantissent pas un ordre de fichiers coh\u00e9rent. L&rsquo;ordre dans lequel les fichiers sont ajout\u00e9s \u00e0 une archive peut d\u00e9pendre de l&rsquo;ordre de listage des r\u00e9pertoires du syst\u00e8me de fichiers, qui peut varier entre les machines ou m\u00eame entre les ex\u00e9cutions sur la m\u00eame machine. Cela produit des archives diff\u00e9rentes avec un contenu identique.<\/p>\n<h3>Versions flottantes des d\u00e9pendances<\/h3>\n<p>Si votre configuration de build sp\u00e9cifie <code>express: ^4.18.0<\/code> au lieu d&rsquo;une version exacte, vous pourriez obtenir 4.18.1 aujourd&rsquo;hui et 4.18.2 demain. Les d\u00e9pendances non \u00e9pingl\u00e9es sont l&rsquo;une des sources de non-reproductibilit\u00e9 les plus courantes et les plus impactantes.<\/p>\n<h3>Diff\u00e9rences d&rsquo;environnement de build<\/h3>\n<p>Des versions diff\u00e9rentes du syst\u00e8me d&rsquo;exploitation, du compilateur, des biblioth\u00e8ques syst\u00e8me, des param\u00e8tres de locale, des configurations de fuseau horaire, et m\u00eame le nombre de c\u0153urs CPU peuvent affecter la sortie du build. Un build sur Ubuntu 22.04 peut diff\u00e9rer d&rsquo;un build sur Ubuntu 24.04, m\u00eame avec le m\u00eame source et les m\u00eames d\u00e9pendances.<\/p>\n<h3>T\u00e9l\u00e9chargements r\u00e9seau pendant le build<\/h3>\n<p>Les builds qui t\u00e9l\u00e9chargent des d\u00e9pendances au moment du build sont intrins\u00e8quement non reproductibles. Un registre de paquets pourrait servir une version diff\u00e9rente, un CDN pourrait retourner du contenu mis en cache ou mis \u00e0 jour, ou le r\u00e9seau pourrait \u00eatre enti\u00e8rement indisponible. Tout build n\u00e9cessitant un acc\u00e8s r\u00e9seau est \u00e0 la merci de syst\u00e8mes externes.<\/p>\n<h3>Valeurs al\u00e9atoires et adresses m\u00e9moire<\/h3>\n<p>Certains processus de build int\u00e8grent des UUID al\u00e9atoires, utilisent des ordres d&rsquo;it\u00e9ration de hash maps non d\u00e9terministes, ou incluent des adresses m\u00e9moire dans leur sortie. Les donn\u00e9es de profilage, les informations de couverture et les symboles de d\u00e9bogage peuvent tous introduire du caract\u00e8re al\u00e9atoire dans les artefacts de build.<\/p>\n<h2>Builds herm\u00e9tiques : le standard de r\u00e9f\u00e9rence<\/h2>\n<p>Un build herm\u00e9tique est enti\u00e8rement autonome : il n&rsquo;a pas d&rsquo;acc\u00e8s r\u00e9seau, toutes les entr\u00e9es sont explicitement d\u00e9clar\u00e9es, et l&rsquo;environnement de build est enti\u00e8rement sp\u00e9cifi\u00e9. Les builds herm\u00e9tiques sont le standard de r\u00e9f\u00e9rence pour la reproductibilit\u00e9 car ils \u00e9liminent des cat\u00e9gories enti\u00e8res de non-d\u00e9terminisme par construction.<\/p>\n<h3>Ce que signifie un build herm\u00e9tique<\/h3>\n<p>Dans un build herm\u00e9tique :<\/p>\n<ul>\n<li>Le processus de build ne peut pas acc\u00e9der au r\u00e9seau. Toutes les d\u00e9pendances doivent \u00eatre pr\u00e9-t\u00e9l\u00e9charg\u00e9es et d\u00e9clar\u00e9es.<\/li>\n<li>Chaque entr\u00e9e \u2014 code source, d\u00e9pendances, cha\u00eene d&rsquo;outils, configuration \u2014 est explicitement list\u00e9e et versionn\u00e9e.<\/li>\n<li>L&rsquo;environnement de build est d\u00e9fini avec pr\u00e9cision, jusqu&rsquo;au syst\u00e8me d&rsquo;exploitation, aux paquets install\u00e9s et aux variables d&rsquo;environnement.<\/li>\n<li>Le build est isol\u00e9 dans un bac \u00e0 sable afin qu&rsquo;il ne puisse pas lire de fichiers non d\u00e9clar\u00e9s du syst\u00e8me h\u00f4te.<\/li>\n<\/ul>\n<h3>Bazel comme syst\u00e8me de build herm\u00e9tique<\/h3>\n<p>Bazel est con\u00e7u d\u00e8s le d\u00e9part pour des builds herm\u00e9tiques et reproductibles. Il isole chaque action de build dans un bac \u00e0 sable, d\u00e9clare explicitement toutes les entr\u00e9es et sorties, et met en cache les r\u00e9sultats en fonction des hachages des entr\u00e9es plut\u00f4t que des horodatages. Les fonctionnalit\u00e9s de mise en cache distante et d&rsquo;ex\u00e9cution distante de Bazel maintiennent l&rsquo;herm\u00e9ticit\u00e9 m\u00eame dans les environnements de build distribu\u00e9s.<\/p>\n<pre><code># Bazel WORKSPACE file: all external dependencies declared\nload(\"@bazel_tools\/\/tools\/build_defs\/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"com_google_protobuf\",\n    sha256 = \"a79d19dcdf9139fa4b81206e318e33d245c4c9da1ffed21c87288f9142c5f4ef\",\n    strip_prefix = \"protobuf-23.2\",\n    urls = [\"https:\/\/github.com\/protocolbuffers\/protobuf\/archive\/v23.2.tar.gz\"],\n)<\/code><\/pre>\n<h3>Builds multi-\u00e9tapes Docker avec images de base \u00e9pingl\u00e9es<\/h3>\n<p>Les builds multi-\u00e9tapes Docker peuvent approximer l&rsquo;herm\u00e9ticit\u00e9 lorsqu&rsquo;ils sont combin\u00e9s avec des images de base \u00e9pingl\u00e9es et des d\u00e9pendances pr\u00e9-t\u00e9l\u00e9charg\u00e9es :<\/p>\n<pre><code># Stage 1: Build with all dependencies pre-installed\nFROM golang@sha256:2c3f3c4a1f8e4c2b7d5e1a9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b AS builder\n\nWORKDIR \/app\nCOPY go.mod go.sum .\/\nRUN go mod download\n\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \\\n    go build -trimpath -ldflags='-s -w -buildid=' \\\n    -o \/app\/server .\/cmd\/server\n\n# Stage 2: Minimal runtime image\nFROM gcr.io\/distroless\/static@sha256:1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b AS runtime\nCOPY --from=builder \/app\/server \/server\nENTRYPOINT [\"\/server\"]<\/code><\/pre>\n<p>Notez l&rsquo;utilisation de <code>-trimpath<\/code> pour supprimer les chemins de fichiers locaux du binaire, <code>-buildid=<\/code> pour effacer l&rsquo;identifiant de build, et <code>-s -w<\/code> pour supprimer les informations de d\u00e9bogage. Ces drapeaux sont essentiels pour la reproductibilit\u00e9 des builds Go.<\/p>\n<h3>Fichiers de verrouillage<\/h3>\n<p>Les fichiers de verrouillage (lock files) sont l&rsquo;outil le plus accessible pour am\u00e9liorer la reproductibilit\u00e9. Ils enregistrent les versions exactes r\u00e9solues de toutes les d\u00e9pendances, y compris les d\u00e9pendances transitives :<\/p>\n<ul>\n<li><strong>Node.js :<\/strong> <code>package-lock.json<\/code> ou <code>yarn.lock<\/code> \u2014 utilisez toujours <code>npm ci<\/code> au lieu de <code>npm install<\/code><\/li>\n<li><strong>Go :<\/strong> <code>go.sum<\/code> \u2014 enregistre les hachages cryptographiques de toutes les versions de modules<\/li>\n<li><strong>Python :<\/strong> <code>poetry.lock<\/code> ou la sortie de <code>pip-compile<\/code> \u2014 \u00e9pingle chaque d\u00e9pendance transitive<\/li>\n<li><strong>Rust :<\/strong> <code>Cargo.lock<\/code> \u2014 toujours commit\u00e9 pour les projets binaires<\/li>\n<\/ul>\n<p>Les fichiers de verrouillage doivent toujours \u00eatre commit\u00e9s dans le contr\u00f4le de version. Un build qui ignore les fichiers de verrouillage est un build que vous ne pouvez pas reproduire.<\/p>\n<h3>Vendoring des d\u00e9pendances<\/h3>\n<p>Le vendoring va plus loin que les fichiers de verrouillage en stockant le code source r\u00e9el des d\u00e9pendances dans votre d\u00e9p\u00f4t. Cela \u00e9limine toute d\u00e9pendance envers les registres externes au moment du build :<\/p>\n<pre><code># Go: vendor all dependencies\ngo mod vendor\n\n# Build using vendored dependencies\ngo build -mod=vendor .\/cmd\/server<\/code><\/pre>\n<p>Le vendoring \u00e9change la taille du d\u00e9p\u00f4t contre la fiabilit\u00e9 et la reproductibilit\u00e9. Il est particuli\u00e8rement pr\u00e9cieux pour les builds qui doivent fonctionner dans des environnements isol\u00e9s (air-gapped) ou pour les projets o\u00f9 la reproductibilit\u00e9 \u00e0 long terme est importante.<\/p>\n<h3>Compromis pratiques : herm\u00e9ticit\u00e9 vs exp\u00e9rience d\u00e9veloppeur<\/h3>\n<p>L&rsquo;herm\u00e9ticit\u00e9 compl\u00e8te a un co\u00fbt. Bazel a une courbe d&rsquo;apprentissage abrupte. Le vendoring augmente la taille du d\u00e9p\u00f4t. Le pr\u00e9-t\u00e9l\u00e9chargement de chaque d\u00e9pendance n\u00e9cessite une infrastructure. L&rsquo;essentiel est d&rsquo;adopter l&rsquo;herm\u00e9ticit\u00e9 de mani\u00e8re progressive : commencez par les fichiers de verrouillage et les images \u00e9pingl\u00e9es, puis ajoutez le vendoring et le sandboxing \u00e0 mesure que vos exigences de s\u00e9curit\u00e9 l&rsquo;imposent.<\/p>\n<h2>Tout \u00e9pingler<\/h2>\n<p>Le pinning est la pratique consistant \u00e0 sp\u00e9cifier des versions exactes et immuables pour chaque composant de votre build. Les tags sont mutables \u2014 ils peuvent \u00eatre d\u00e9plac\u00e9s pour pointer vers un contenu diff\u00e9rent. Les digests et les SHA de commits sont immuables. \u00c9pinglez toujours vers des r\u00e9f\u00e9rences immuables.<\/p>\n<h3>\u00c9pingler les images de base par digest<\/h3>\n<p>Les tags d&rsquo;images Docker comme <code>node:20<\/code> ou <code>python:3.12-slim<\/code> peuvent changer \u00e0 tout moment. Le registre peut pousser une nouvelle image sur le m\u00eame tag. \u00c9pinglez par digest \u00e0 la place :<\/p>\n<pre><code># BAD: tag can change at any time\nFROM node:20-alpine\n\n# GOOD: pinned to an immutable digest\nFROM node@sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2\n\n# BETTER: tag for readability, digest for immutability\nFROM node:20-alpine@sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2<\/code><\/pre>\n<p>Vous pouvez trouver le digest actuel avec :<\/p>\n<pre><code>docker inspect --format='{{index .RepoDigests 0}}' node:20-alpine<\/code><\/pre>\n<h3>\u00c9pingler les GitHub Actions par SHA<\/h3>\n<p>Les tags des GitHub Actions sont mutables. Une action compromise peut pousser du code malveillant sur un tag existant. \u00c9pinglez toujours vers le SHA complet du commit :<\/p>\n<pre><code># BAD: tag can be moved to malicious commit\n- uses: actions\/checkout@v4\n\n# GOOD: pinned to immutable commit SHA\n- uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1<\/code><\/pre>\n<p>Utilisez des outils comme <a href=\"https:\/\/github.com\/sethvargo\/ratchet\" target=\"_blank\" rel=\"noopener\">ratchet<\/a> ou <a href=\"https:\/\/github.com\/suzuki-shunsuke\/pinact\" target=\"_blank\" rel=\"noopener\">pinact<\/a> pour automatiser le pinning par SHA dans vos fichiers de workflow et conserver les commentaires avec le tag de version pour la lisibilit\u00e9.<\/p>\n<h3>\u00c9pingler les versions de la cha\u00eene d&rsquo;outils<\/h3>\n<p>Votre cha\u00eene d&rsquo;outils de build \u2014 compilateur, runtime, gestionnaire de paquets \u2014 doit \u00eatre \u00e9pingl\u00e9e en version. Utilisez des fichiers de configuration de gestionnaire de versions pour d\u00e9clarer les versions exactes :<\/p>\n<pre><code># .tool-versions (used by asdf version manager)\nnodejs 20.11.0\npython 3.12.1\ngolang 1.22.0\nrust 1.75.0<\/code><\/pre>\n<pre><code># .nvmrc (Node version manager)\n20.11.0<\/code><\/pre>\n<pre><code># rust-toolchain.toml\n[toolchain]\nchannel = \"1.75.0\"\ncomponents = [\"rustfmt\", \"clippy\"]\ntargets = [\"x86_64-unknown-linux-gnu\"]<\/code><\/pre>\n<h3>Utiliser Nix pour la reproductibilit\u00e9 de l&rsquo;environnement de build<\/h3>\n<p>Nix fournit l&rsquo;approche la plus rigoureuse pour la reproductibilit\u00e9 de l&rsquo;environnement. Un Nix flake d\u00e9clare l&rsquo;environnement de build complet comme une fonction de ses entr\u00e9es :<\/p>\n<pre><code># flake.nix\n{\n  inputs = {\n    nixpkgs.url = \"github:NixOS\/nixpkgs\/nixos-24.05\";\n    flake-utils.url = \"github:numtide\/flake-utils\";\n  };\n  outputs = { self, nixpkgs, flake-utils }:\n    flake-utils.lib.eachDefaultSystem (system:\n      let pkgs = nixpkgs.legacyPackages.${system}; in {\n        devShells.default = pkgs.mkShell {\n          buildInputs = [ pkgs.go_1_22 pkgs.nodejs_20 pkgs.protobuf ];\n        };\n      });\n}<\/code><\/pre>\n<p>Avec Nix, chaque d\u00e9veloppeur et chaque runner CI obtient exactement les m\u00eames versions de chaque outil, jusqu&rsquo;aux biblioth\u00e8ques syst\u00e8me. Le fichier <code>flake.lock<\/code> \u00e9pingle l&rsquo;ensemble de l&rsquo;arbre de d\u00e9pendances vers des r\u00e9visions Git sp\u00e9cifiques.<\/p>\n<h2>V\u00e9rifier l&rsquo;int\u00e9grit\u00e9 des builds<\/h2>\n<p>Atteindre la reproductibilit\u00e9 n&rsquo;est que la moiti\u00e9 du combat. Vous devez \u00e9galement la v\u00e9rifier \u2014 confirmer que des builds ind\u00e9pendants \u00e0 partir du m\u00eame source produisent effectivement des artefacts identiques.<\/p>\n<h3>Comparer les builds entre environnements<\/h3>\n<p>La v\u00e9rification la plus simple consiste \u00e0 construire le m\u00eame artefact dans deux environnements diff\u00e9rents et \u00e0 comparer les sorties :<\/p>\n<pre><code># Build in CI\nsha256sum build\/output\/myapp.tar.gz\n# Output: a1b2c3d4... build\/output\/myapp.tar.gz\n\n# Rebuild locally from the same commit\ngit checkout v1.2.3\nmake build\nsha256sum build\/output\/myapp.tar.gz\n# Output should match: a1b2c3d4...<\/code><\/pre>\n<p>Si les hachages correspondent, le build est reproductible. S&rsquo;ils ne correspondent pas, vous devez investiguer ce qui diff\u00e8re.<\/p>\n<h3>Utiliser diffoscope pour une analyse approfondie<\/h3>\n<p><code>diffoscope<\/code> est un outil essentiel pour diagnostiquer les probl\u00e8mes de reproductibilit\u00e9. Il d\u00e9compresse r\u00e9cursivement les archives, d\u00e9compile les binaires et vous montre exactement o\u00f9 deux builds diff\u00e8rent :<\/p>\n<pre><code># Install diffoscope\npip install diffoscope\n\n# Compare two builds\ndiffoscope build-1\/myapp.tar.gz build-2\/myapp.tar.gz --html report.html\n\n# Compare two container images\ndiffoscope image-1.tar image-2.tar --html report.html<\/code><\/pre>\n<p>Le rapport HTML montre les diff\u00e9rences \u00e0 chaque niveau : m\u00e9tadonn\u00e9es d&rsquo;archive, contenu des fichiers, sections binaires et ressources int\u00e9gr\u00e9es. C&rsquo;est inestimable pour identifier et \u00e9liminer les sources de non-d\u00e9terminisme une par une.<\/p>\n<h3>Stocker les m\u00e9tadonn\u00e9es de build<\/h3>\n<p>M\u00eame avant d&rsquo;atteindre une reproductibilit\u00e9 compl\u00e8te, l&rsquo;enregistrement des m\u00e9tadonn\u00e9es de build assure la tra\u00e7abilit\u00e9. Capturez et stockez :<\/p>\n<ul>\n<li>Le SHA du commit Git et la branche<\/li>\n<li>Les hachages de toutes les d\u00e9pendances en entr\u00e9e<\/li>\n<li>Les d\u00e9tails de l&rsquo;environnement de build (version de l&rsquo;OS, version de la cha\u00eene d&rsquo;outils, variables d&rsquo;environnement)<\/li>\n<li>Les hachages de tous les artefacts de sortie<\/li>\n<li>Les horodatages et la dur\u00e9e du build<\/li>\n<\/ul>\n<h3>Provenance SLSA<\/h3>\n<p>La provenance SLSA est un format standardis\u00e9 pour les m\u00e9tadonn\u00e9es de build. Elle enregistre ce qui a \u00e9t\u00e9 construit, \u00e0 partir de quel source, en utilisant quel processus de build, et dans quel environnement. Des outils comme <a href=\"https:\/\/github.com\/slsa-framework\/slsa-github-generator\" target=\"_blank\" rel=\"noopener\">slsa-github-generator<\/a> peuvent automatiquement g\u00e9n\u00e9rer une provenance sign\u00e9e pour vos builds GitHub Actions :<\/p>\n<pre><code># .github\/workflows\/release.yml\njobs:\n  build:\n    outputs:\n      digest: ${{ steps.hash.outputs.digest }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11\n      - run: make build\n      - id: hash\n        run: echo \"digest=$(sha256sum myapp | cut -d' ' -f1)\" >> \"$GITHUB_OUTPUT\"\n\n  provenance:\n    needs: build\n    permissions:\n      actions: read\n      id-token: write\n      contents: write\n    uses: slsa-framework\/slsa-github-generator\/.github\/workflows\/generator_generic_slsa3.yml@v1.9.0\n    with:\n      base64-subjects: ${{ needs.build.outputs.digest }}<\/code><\/pre>\n<h3>Analyse des images de conteneurs<\/h3>\n<p>Pour les images de conteneurs, v\u00e9rifiez l&rsquo;int\u00e9grit\u00e9 en inspectant les couches de l&rsquo;image \u00e0 la recherche de contenu inattendu :<\/p>\n<pre><code># List all layers in an image\ndocker history myapp:latest --no-trunc\n\n# Export and inspect image contents\ndocker save myapp:latest -o image.tar\ntar -tf image.tar\n\n# Use dive to inspect layer contents interactively\ndive myapp:latest<\/code><\/pre>\n<h2>Builds reproductibles en CI\/CD<\/h2>\n<p>Les plateformes CI\/CD introduisent leurs propres d\u00e9fis de reproductibilit\u00e9. Les images de runners changent, les caches expirent et les environnements de build sont \u00e9ph\u00e9m\u00e8res. Voici comment atteindre la reproductibilit\u00e9 sur les principales plateformes.<\/p>\n<h3>GitHub Actions<\/h3>\n<p>Un workflow GitHub Actions reproductible \u00e9pingle chaque composant externe :<\/p>\n<pre><code>name: Reproducible Build\non:\n  push:\n    branches: [main]\n\njobs:\n  build:\n    # Pin the runner image (or use a self-hosted runner with a known image)\n    runs-on: ubuntu-22.04\n\n    steps:\n      # Pin action by SHA\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1\n\n      # Pin setup action and toolchain version\n      - uses: actions\/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0\n        with:\n          go-version: '1.22.0' # Exact version, not '1.22.x'\n\n      # Cache with hash-based key for determinism\n      - uses: actions\/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1\n        with:\n          path: ~\/go\/pkg\/mod\n          key: go-mod-${{ hashFiles('go.sum') }}\n\n      # Build with reproducibility flags\n      - run: |\n          CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \\\n          go build -trimpath -ldflags='-s -w -buildid=' \\\n          -o myapp .\/cmd\/server\n\n      # Record artifact hash\n      - run: sha256sum myapp >> checksums.txt\n\n      - uses: actions\/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1\n        with:\n          name: build-artifacts\n          path: |\n            myapp\n            checksums.txt<\/code><\/pre>\n<h3>GitLab CI<\/h3>\n<p>Dans GitLab CI, utilisez des images Docker fixes pour les runners et \u00e9pinglez toutes les d\u00e9pendances :<\/p>\n<pre><code># .gitlab-ci.yml\nvariables:\n  # Use a specific image digest for the build environment\n  BUILD_IMAGE: golang@sha256:2c3f3c4a1f8e4c2b7d5e1a9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b\n\nstages:\n  - build\n  - verify\n\nbuild:\n  stage: build\n  image: $BUILD_IMAGE\n  script:\n    - go mod download\n    - CGO_ENABLED=0 GOOS=linux GOARCH=amd64\n      go build -trimpath -ldflags='-s -w -buildid='\n      -o myapp .\/cmd\/server\n    - sha256sum myapp | tee checksums.txt\n  artifacts:\n    paths:\n      - myapp\n      - checksums.txt\n  cache:\n    key:\n      files:\n        - go.sum\n    paths:\n      - \/go\/pkg\/mod\n\nverify-reproducibility:\n  stage: verify\n  image: $BUILD_IMAGE\n  script:\n    - go mod download\n    - CGO_ENABLED=0 GOOS=linux GOARCH=amd64\n      go build -trimpath -ldflags='-s -w -buildid='\n      -o myapp-verify .\/cmd\/server\n    - sha256sum myapp-verify\n    - diff <(sha256sum myapp | cut -d' ' -f1) <(sha256sum myapp-verify | cut -d' ' -f1)<\/code><\/pre>\n<h3>Utiliser Nix en CI pour une reproductibilit\u00e9 compl\u00e8te<\/h3>\n<p>Nix fournit les garanties de reproductibilit\u00e9 les plus solides en CI en sp\u00e9cifiant l'ensemble de la fermeture de build :<\/p>\n<pre><code># GitHub Actions with Nix\nname: Nix Build\non: push\n\njobs:\n  build:\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11\n      - uses: cachix\/install-nix-action@ba0dd844c9180cbf77aa557a09b7b0d890fbd0fb # v26\n        with:\n          nix_path: nixpkgs=channel:nixos-24.05\n      - run: nix build .#myapp\n      - run: sha256sum result\/bin\/myapp<\/code><\/pre>\n<p>Avec Nix, le fichier <code>flake.lock<\/code> \u00e9pingle la version exacte de chaque paquet dans la fermeture de build. Deux d\u00e9veloppeurs ex\u00e9cutant <code>nix build<\/code> sur des machines diff\u00e9rentes obtiendront une sortie identique, car l'ensemble du graphe de d\u00e9pendances \u2014 y compris le compilateur C, les biblioth\u00e8ques syst\u00e8me et chaque d\u00e9pendance transitive \u2014 est sp\u00e9cifi\u00e9 avec pr\u00e9cision.<\/p>\n<h3>Builds d'images de conteneurs reproductibles<\/h3>\n<p>La construction d'images de conteneurs reproductibles n\u00e9cessite une attention particuli\u00e8re. La commande traditionnelle <code>docker build<\/code> int\u00e8gre des horodatages dans chaque couche. Des outils comme <a href=\"https:\/\/github.com\/GoogleContainerTools\/kaniko\" target=\"_blank\" rel=\"noopener\">kaniko<\/a>, <a href=\"https:\/\/github.com\/moby\/buildkit\" target=\"_blank\" rel=\"noopener\">BuildKit<\/a> et <a href=\"https:\/\/github.com\/google\/ko\" target=\"_blank\" rel=\"noopener\">ko<\/a> offrent une meilleure reproductibilit\u00e9 :<\/p>\n<pre><code># Using BuildKit with reproducible output\nDOCKER_BUILDKIT=1 docker build \\\n  --build-arg SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) \\\n  --output type=docker,rewrite-timestamp=true \\\n  -t myapp:latest .<\/code><\/pre>\n<p>La variable d'environnement <code>SOURCE_DATE_EPOCH<\/code> est un <a href=\"https:\/\/reproducible-builds.org\/specs\/source-date-epoch\/\" target=\"_blank\" rel=\"noopener\">m\u00e9canisme standardis\u00e9<\/a> pour indiquer aux outils de build d'utiliser un horodatage fixe au lieu de l'heure actuelle. De nombreux outils la respectent, notamment GCC, dpkg, tar et zip.<\/p>\n<h2>Quand la reproductibilit\u00e9 parfaite n'est pas r\u00e9alisable<\/h2>\n<p>La reproductibilit\u00e9 bit \u00e0 bit est l'id\u00e9al, mais elle n'est pas toujours atteignable \u2014 et ce n'est pas grave. Ce qui compte, c'est de comprendre o\u00f9 vous vous situez sur le spectre de la reproductibilit\u00e9 et d'appliquer des contr\u00f4les compensatoires l\u00e0 o\u00f9 c'est n\u00e9cessaire.<\/p>\n<h3>Non-reproductibilit\u00e9 acceptable<\/h3>\n<p>Certaines non-reproductibilit\u00e9s sont inoffensives. Un horodatage de build dans un fichier de m\u00e9tadonn\u00e9es qui n'est jamais ex\u00e9cut\u00e9 n'affecte pas la s\u00e9curit\u00e9 de l'artefact. Une entr\u00e9e de journal enregistrant quand le build a \u00e9t\u00e9 ex\u00e9cut\u00e9 n'est pas un risque de s\u00e9curit\u00e9. La distinction cl\u00e9 est de savoir si l'\u00e9l\u00e9ment non reproductible se trouve dans l'artefact lui-m\u00eame (risqu\u00e9) ou uniquement dans les m\u00e9tadonn\u00e9es qui l'accompagnent (acceptable).<\/p>\n<h3>Reproductibilit\u00e9 \u00ab suffisante \u00bb<\/h3>\n<p>Pour de nombreuses \u00e9quipes, l'objectif pratique est la reproductibilit\u00e9 fonctionnelle : les m\u00eames entr\u00e9es produisent la m\u00eame sortie fonctionnelle. Les binaires peuvent diff\u00e9rer dans les horodatages int\u00e9gr\u00e9s ou les symboles de d\u00e9bogage, mais ils se comportent de mani\u00e8re identique. Ce niveau de reproductibilit\u00e9 est atteignable avec des outils et pratiques standards :<\/p>\n<ul>\n<li>\u00c9pingler toutes les versions de d\u00e9pendances avec des fichiers de verrouillage<\/li>\n<li>\u00c9pingler les images de base par digest<\/li>\n<li>\u00c9pingler les actions CI par SHA<\/li>\n<li>Utiliser des versions fixes de la cha\u00eene d'outils<\/li>\n<li>Supprimer les horodatages lorsque c'est possible<\/li>\n<\/ul>\n<h3>Contr\u00f4les compensatoires<\/h3>\n<p>Lorsque la reproductibilit\u00e9 compl\u00e8te n'est pas r\u00e9alisable, les contr\u00f4les compensatoires fournissent des assurances alternatives :<\/p>\n<ul>\n<li><strong>Signature de code :<\/strong> Signez cryptographiquement vos artefacts afin que les consommateurs puissent v\u00e9rifier qu'ils proviennent de votre syst\u00e8me de build.<\/li>\n<li><strong>Provenance SLSA :<\/strong> G\u00e9n\u00e9rez et publiez des m\u00e9tadonn\u00e9es de provenance qui enregistrent les entr\u00e9es, l'environnement et le processus de build.<\/li>\n<li><strong>Nomenclature logicielle (SBOM) :<\/strong> Publiez une liste compl\u00e8te des composants de votre artefact afin que les consommateurs sachent exactement ce qu'ils obtiennent.<\/li>\n<li><strong>Conservation des journaux de build :<\/strong> Stockez les journaux de build complets pour une analyse forensique en cas de compromission suspect\u00e9e.<\/li>\n<li><strong>Builds multi-parties :<\/strong> Faites construire plusieurs parties ind\u00e9pendantes \u00e0 partir du m\u00eame source et comparez les r\u00e9sultats.<\/li>\n<\/ul>\n<h3>Le spectre de la reproductibilit\u00e9<\/h3>\n<p>Consid\u00e9rez la reproductibilit\u00e9 comme un spectre, pas comme un \u00e9tat binaire :<\/p>\n<ul>\n<li><strong>Niveau 0 \u2014 Rien :<\/strong> Pas de pinning de version, pas de fichiers de verrouillage, les builds d\u00e9pendent de la derni\u00e8re version disponible. C'est l\u00e0 que la plupart des projets commencent.<\/li>\n<li><strong>Niveau 1 \u2014 D\u00e9pendances \u00e9pingl\u00e9es :<\/strong> Fichiers de verrouillage commit\u00e9s, les d\u00e9pendances sont en versions fixes. Les builds sont g\u00e9n\u00e9ralement reproductibles.<\/li>\n<li><strong>Niveau 2 \u2014 Environnement \u00e9pingl\u00e9 :<\/strong> Les versions de la cha\u00eene d'outils et les images de base sont \u00e9pingl\u00e9es. L'environnement de build est contr\u00f4l\u00e9.<\/li>\n<li><strong>Niveau 3 \u2014 Builds herm\u00e9tiques :<\/strong> Pas d'acc\u00e8s r\u00e9seau pendant le build. Toutes les entr\u00e9es sont explicitement d\u00e9clar\u00e9es. Garanties de reproductibilit\u00e9 solides.<\/li>\n<li><strong>Niveau 4 \u2014 Reproductibilit\u00e9 bit \u00e0 bit :<\/strong> Les builds ind\u00e9pendants produisent des artefacts identiques. V\u00e9rification compl\u00e8te possible.<\/li>\n<\/ul>\n<p>Chaque niveau s'appuie sur le pr\u00e9c\u00e9dent. Passer du Niveau 0 au Niveau 1 est souvent l'am\u00e9lioration la plus impactante que vous puissiez faire, et elle n\u00e9cessite un effort minimal.<\/p>\n<h2>Conclusion<\/h2>\n<p>Les builds reproductibles sont le fondement de la confiance dans la cha\u00eene d'approvisionnement. Sans eux, vous vous fiez aveugl\u00e9ment au fait que votre syst\u00e8me de build n'a pas \u00e9t\u00e9 compromis \u2014 une confiance que les clients de SolarWinds, les utilisateurs de XZ Utils et d'innombrables autres ont appris \u00e0 leurs d\u00e9pens qu'elle \u00e9tait mal plac\u00e9e.<\/p>\n<p>La bonne nouvelle est que vous n'avez pas besoin d'atteindre la perfection d\u00e8s le premier jour. Commencez par les bases :<\/p>\n<ul>\n<li><strong>Commitez vos fichiers de verrouillage<\/strong> et utilisez <code>npm ci<\/code> au lieu de <code>npm install<\/code>.<\/li>\n<li><strong>\u00c9pinglez vos images de base par digest<\/strong> dans chaque Dockerfile.<\/li>\n<li><strong>\u00c9pinglez vos actions CI par SHA<\/strong> dans chaque fichier de workflow.<\/li>\n<li><strong>\u00c9pinglez les versions de votre cha\u00eene d'outils<\/strong> avec <code>.tool-versions<\/code>, <code>rust-toolchain.toml<\/code> ou similaire.<\/li>\n<\/ul>\n<p>Puis ajoutez l'herm\u00e9ticit\u00e9 progressivement : vendorez vos d\u00e9pendances, utilisez Nix ou Bazel pour l'isolation des builds, supprimez les horodatages de vos artefacts, et mettez en place des jobs de v\u00e9rification qui reconstruisent et comparent.<\/p>\n<p>Chaque \u00e9tape sur le spectre de la reproductibilit\u00e9 rend vos builds plus fiables, votre cha\u00eene d'approvisionnement plus auditable et votre logiciel plus s\u00e9curis\u00e9. Dans un monde o\u00f9 les syst\u00e8mes de build sont un vecteur d'attaque principal, les builds reproductibles ne sont pas optionnels \u2014 ils sont essentiels.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Si vous ne pouvez pas reproduire un build, vous ne pouvez pas le v\u00e9rifier. Cette v\u00e9rit\u00e9 simple se trouve au c\u0153ur de la s\u00e9curit\u00e9 de la cha\u00eene d&rsquo;approvisionnement logicielle. L&rsquo;int\u00e9grit\u00e9 des builds garantit que ce que vous d\u00e9ployez est exactement ce que vous aviez l&rsquo;intention de construire \u2014 rien d&rsquo;ajout\u00e9, rien de modifi\u00e9, rien &#8230; <a title=\"Int\u00e9grit\u00e9 des Builds et Builds Reproductibles : Guide Pratique pour le CI\/CD\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/build-integrity-reproducible-builds-ci-cd\/\" aria-label=\"En savoir plus sur Int\u00e9grit\u00e9 des Builds et Builds Reproductibles : Guide Pratique pour le CI\/CD\">Lire la suite<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49,50],"tags":[],"post_folder":[],"class_list":["post-503","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-software-supply-chain"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/503","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/comments?post=503"}],"version-history":[{"count":1,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/503\/revisions"}],"predecessor-version":[{"id":513,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/503\/revisions\/513"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/media?parent=503"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/categories?post=503"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/tags?post=503"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/post_folder?post=503"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}