1 points par GN⁺ 2023-10-30 | 1 commentaires | Partager sur WhatsApp
  • L’ISO d’installation minimale de NixOS a pu être reconstruite indépendamment de façon strictement identique bit à bit à la version distribuée par Hydra, montrant qu’il est possible de vérifier la correspondance entre les binaires publiés et le code source
  • Cette vérification ne couvre pas seulement les paquets inclus dans l’ISO, mais aussi le processus de génération de l’ISO lui-même, ce qui confirme un périmètre plus large qu’une simple reproductibilité des paquets
  • La reconstruction a commencé depuis l’appliance VirtualBox de NixOS 20.03, en utilisant la révision 63678e9f3d3a de nixpkgs, avec --option substitute false pour désactiver la dépendance au cache binaire
  • Si l’OVA de 2020 ou le git téléchargé contenait une porte dérobée sophistiquée, cela resterait malgré tout un vecteur d’attaque ; une vérification reposant sur un système entièrement bootstrapped reste donc à faire
  • La reconstruction de l’ISO minimale constitue une étape importante, mais la suppression des contournements temporaires, la reproductibilité d’un plus grand nombre de supports d’installation, une infrastructure de reconstructions indépendantes régulières et des outils de preuve de build restent les prochaines tâches

Reproductibilité vérifiée sur l’ISO minimale

  • Le build ISO nixos-minimal publié par Hydra a été reconstruit indépendamment, avec un résultat strictement identique bit à bit
  • Le périmètre de la reproductibilité se divise en deux axes
    • tous les paquets inclus dans l’ISO
    • le processus de build de l’ISO lui-même
  • Des paquets nécessaires au build de l’ISO mais non inclus dans l’ISO ont également été compilés, sans dépendre de binaires mis en cache
  • Les builds reproductibles offrent une chaîne de confiance permettant de vérifier que les binaires distribués correspondent fidèlement au code source et n’ont pas été altérés dans un pipeline de build comme Hydra

Procédure de reconstruction et limites

  • La reconstruction a été effectuée en partant d’une nouvelle appliance VirtualBox avec NixOS 20.03
    • suffisamment de CPU et de mémoire ont été alloués, et le disque a été étendu à environ 65 Go
    • après l’installation de git, nixpkgs a été cloné puis la révision 63678e9f3d3a a été checkoutée
    • avec --option substitute false, les éléments nécessaires ont été compilés sur la machine locale au lieu d’être récupérés depuis le cache binaire
  • La procédure inclut des mesures temporaires pour contourner des problèmes connus
  • Des limites subsistent du point de vue de la confiance dans la supply chain
    • si l’OVA de 2020 ou le git téléchargé contenait une porte dérobée sophistiquée, cela resterait un vecteur d’attaque
    • reconstruire depuis un système entièrement bootstrapped serait préférable, mais ce stade n’a pas encore été atteint
    • les avancées associées se poursuivent dans le fil du projet nixpkgs supply-chain security

Différence entre l’annonce de 2021 et ce résultat

  • En 2021, une annonce indiquait que l’ISO minimale était reproductible à 100 %, mais à l’époque seuls les paquets nécessaires au build de l’ISO avaient été reproduits individuellement, et des écarts subsistaient encore lors de la reconstruction de l’ISO elle-même
    • la cause venait de problèmes restants liés au cache Hydra et à la manière de générer l’ISO
    • ensuite, alors que ces problèmes étaient corrigés, des régressions sont apparues, comme un problème upstream dans Python 3.10, et ce n’est que cette semaine que toute la chaîne a de nouveau pu être vérifiée
  • Les prochaines étapes consistent à supprimer les contournements temporaires, reproduire davantage de paquets et étendre la démarche à d’autres supports d’installation, comme l’ISO Gnome
  • Une infrastructure de reconstructions indépendantes régulières et des outils de partage et de consommation de preuves de build, comme trustix, sont également nécessaires

1 commentaires

 
GN⁺ 2023-10-30
Commentaires sur Hacker News
  • Reconstruire à nouveau l’ISO minimale à partir des sources constitue une étape impressionnante dans le parcours vers un système pouvant être compilé de manière reproductible à partir des sources
    Guix a récemment accompli, dans le même parcours, une avancée orthogonale mais tout aussi impressionnante, en amorçant toute la chaîne d’outils du compilateur à partir d’un unique binaire reproductible de 357 octets, sans autre blob de compilateur binaire
    Peut-être qu’un jour prochain, les deux seront combinés pour permettre de reconstruire de manière reproductible une distribution entière à partir des sources
    https://guix.gnu.org/en/blog/2023/the-full-source-bootstrap-...

    • C’est formidable de voir qu’il y a des gens qui mènent le bon combat, même face à des réactions du genre : « S’il y a une backdoor, tout le monde ne reçoit-il pas de toute façon la même backdoor ? »
      Le point clé que beaucoup ne comprennent pas, c’est qu’il ne s’agit pas de prouver que le résultat est fiable à 100 %, mais de prouver qu’il est fidèle à 100 % au code source
      Autrement dit, si quelque chose de suspect comme une backdoor furtive est découvert, on peut alors toujours le reproduire de façon déterministe
      Du point de vue des acteurs malveillants, il n’y a plus nulle part où fuir ni se cacher
    • Ce n’est pas encore au niveau du stage0 de Guix, mais il y a eu à NixCon une présentation intéressante sur l’amorçage de Nix à partir de TinyCC : https://media.ccc.de/v/nixcon-2023-34402-bootstrapping-nix-a...
    • Le fait que le binaire du compilateur d’amorçage fasse 357 octets est vraiment impressionnant
    • À 357 octets, on peut presque se demander s’il est vraiment nécessaire d’avoir un binaire reproductible
      On pourrait probablement documenter à la main l’intégralité de ces 357 octets de code machine pour les rendre compréhensibles par un humain
  • C’est peut-être une question naïve, car je n’ai jamais fait ce genre de chose, mais je me demande pourquoi la reproductibilité n’est pas le comportement par défaut
    Si l’on compile deux fois un logiciel à partir du même code source, je ne vois pas très bien ce qui empêche d’obtenir exactement le même résultat à chaque fois
    Je sais qu’il y a beaucoup d’éléments en mouvement, mais j’ai encore du mal à comprendre comment les différences apparaissent

    • Il y a de nombreuses causes précises, et le problème le plus courant est probablement celui des horodatages
      On peut voir ici une liste des problèmes fréquents : https://reproducible-builds.org/docs/
      Le fond du problème, de manière générale, c’est que les développeurs ne testent pas si la compilation est reproductible
      Quand cela fait partie des tests de release, cela reste généralement reproductible dans le temps
    • Les causes sont très nombreuses
      Parmi les exemples classiques qui rendent explicitement une compilation non reproductible, il y a les horodatages et les informations sur l’auteur
      Il existe aussi des endroits où la reproductibilité est rompue implicitement : par exemple, beaucoup de runtimes ne définissent pas l’ordre des éléments d’une table de hachage, et le compilateur peut parcourir cette table pour produire le binaire
    • Cela peut venir du parallélisme
      Il peut y avoir des opérations qui ne sont pas indépendantes de l’ordre, et selon l’état du CPU, on peut obtenir des binaires légèrement différents tout en restant corrects dans tous les cas
    • L’équipe Go a récemment publié un billet expliquant ce qu’elle a fait pour rendre entièrement reproductible la chaîne d’outils de Go : https://go.dev/blog/rebuild
    • Parfois, cela vient d’algorithmes aléatoires, parfois de questions de performance, par exemple lorsqu’il est plus rapide de ne pas trier quelque chose
      Parfois aussi, la cause est une métadonnée dépendante du temps ou de l’environnement, ou encore l’ordre d’exécution des threads
  • Désolé si je me trompe, mais je pensais que l’une des principales raisons d’être de NixOS était justement la reproductibilité
    Je croyais que ces problèmes avaient déjà été résolus
    Je n’ai utilisé NixOS qu’environ deux heures, je voulais essayer Hyprland, et comme Hyprland demande un peu de configuration, je pensais qu’il serait plus facile de reprendre la configuration de quelqu’un d’autre sur NixOS que sur d’autres distributions
    Mais il était difficile de trouver une configuration, j’ai trouvé trois gist GitHub au hasard, aucun ne fonctionnait, alors j’ai abandonné

    • L’avantage de NixOS, c’est que tout est compilé dans sa propre sandbox, uniquement avec des dépendances explicitement déclarées et hachées
      Contrairement aux distributions classiques, il ne dépend pas de l’environnement global du système, donc dans bien des cas on obtient déjà le même binaire à chaque fois
      Mais le processus de build lui-même peut rester non déterministe dans de nombreux paquets, donc cela ne suffit pas à garantir immédiatement une reproductibilité complète
    • Le mot reproductibilité a deux sens
      Celui auquel vous pensez sans doute, c’est la possibilité de reconstruire facilement un paquet binaire en utilisant les mêmes versions de dépendances, les mêmes options de build, etc.
      Autrement dit, il ne devrait pas y avoir de nouveaux échecs de compilation du type « chez moi ça marchait »
      Le sens utilisé ici, c’est que tous les artefacts de build deviennent des binaires identiques octet par octet
      Ils ne doivent pas dépendre du nom de la machine, de l’heure de compilation, de l’ordre dans lequel les fichiers finissent d’être compilés lors d’un build parallèle, etc., et c’est nettement plus difficile
    • Nix est un outil difficile à apprendre
      Si l’on n’y est pas habitué, ce n’est pratiquement pas l’outil à choisir quand on veut simplement que « ça marche tout de suite »
      Sur NixOS, quand on dit « reproductible », cela veut plutôt dire « obtenir le même comportement logiciel à partir du même code Nix »
      C’est assez proche de ce que les gens attendent d’un Dockerfile, et vise à résoudre les problèmes du type « ça marchait sur ma machine » ou « ça marchait la dernière fois »
      En revanche, les « builds reproductibles » visent à ce que les artefacts produits sur des machines différentes soient identiques bit à bit
      Cela ajoute une couche de sécurité, car on peut alors vérifier qu’un code a bien été compilé à partir d’un ensemble précis de sources
      Je suis aussi curieux de savoir quels mots-clés ont été utilisés pour chercher une configuration
      En recherchant « nixos configuration », on obtient des résultats comme https://github.com/search?q=nixos%20configuration&type=repos..., et rien que pour Hyprland il y en a déjà pas mal, par exemple https://github.com/search?q=wayland.windowManager.hyprland&t...
  • Jeter un œil à https://github.com/donovanglover/nix-config peut être utile
    C’est une configuration basée sur Flake, avec Hyprland et plusieurs autres choses intéressantes
    À l’heure actuelle, NixOS n’est pas vraiment un outil pour les personnes peu endurantes ou qui manquent de temps
    J’espère que cela changera un jour, mais si on tient bon et qu’on passe le cap, on peut en tirer des avantages

  • Il faut se rappeler que la reproductibilité de Nix / NixOS / Nixpkgs est une reproductibilité des sources
    Si les sources changent, on en est averti, mais ce n’est pas la même chose que la reproductibilité des binaires, qui peuvent varier d’un build à l’autre
    La reproductibilité binaire de Nix / NixOS / Nixpkgs n’est, au moins de manière systématique, pas particulièrement bien testée
    Guix, Arch Linux et Debian traitent mieux la reproductibilité binaire que Nix / NixOS / Nixpkgs
    Références : https://r13y.com/ (Nix*) / https://tests.reproducible-builds.org/debian/reproducible.ht... (Debian) / https://tests.reproducible-builds.org/archlinux/archlinux.ht... (Arch Linux) / https://data.guix.gnu.org/repository/1/branch/master/latest-... (Guix, le chargement peut être lent et une copie en cache est disponible sur https://archive.is/lTuPk)

    • Dans ce contexte, il y a deux définitions de la « reproductibilité »
      La reproductibilité des entrées signifie une « invalidation de cache parfaite des entrées »
      Nix et Guix le font parfaitement par conception, au point de provoquer parfois trop de rebuilds
      Debian et Arch Linux n’en font pas une préoccupation principale et gèrent la question de savoir quels paquets reconstruire lorsqu’un fichier source donné est mis à jour via des méthodes ad hoc comme des déclencheurs manuels de rebuild
      La reproductibilité des sorties signifie que « le processus de build est déterministe et produit toujours le même binaire », et c’est le sujet du billet d’origine
      Nix aide sur ce point en construisant les paquets dans un bac à sable, mais ce n’est pas une solution miracle
      Sur cet aspect, Nix est dans le même bateau que Debian et Arch Linux
      En pratique, les distributions envoient souvent en amont des correctifs pour améliorer la reproductibilité, et les autres distributions en profitent aussi
      Dans ce contexte, https://reproducible.nixos.org est l’équivalent des autres liens présentés ; et je suis d’accord pour dire que le rapport Nix est moins détaillé, mais cela ne veut pas dire que la reproductibilité binaire de Nix est pire
      Lire cela comme « Nix ne gère bien que la reproductibilité des entrées et mal la reproductibilité binaire » serait incorrect
      C’est précisément ce jalon qu’on est en train de célébrer ici
    • Ce serait bien de lire l’article
      Il parle de reproduire bit à bit non seulement les binaires, mais aussi la manière dont ils sont empaquetés en ISO
      r13y.com est obsolète, et de mémoire les moins de 1 % manquants venaient d’une régression upstream de Python
      La reproductibilité des binaires eux-mêmes, en dehors de l’empaquetage ISO, avait déjà été atteinte il y a plusieurs années
      Dès qu’on va au-delà de l’ISO de base vers les autres paquets, la comparaison devient plus compliquée
      La manière de traiter les paquets est subtilement, mais de façon importante, différente dans ce contexte, et beaucoup de paquets qui seraient probablement sur l’AUR d’Arch sont des paquets ordinaires dans Nix, tandis que la plupart des paquets upstream en -bin ne sont tout simplement pas nécessaires dans Nix
      En général, Nix facilite la création de builds reproductibles, mais ce n’est pas toujours possible indépendamment de Nix et cela nécessite souvent des correctifs
      Si on ajoute à cela que le dépôt principal de paquets de Nix en compte plus de 80 000, contre moins de 15 000 pour Arch hors AUR, comparer les pourcentages n’est pas très utile
      Une confusion très fréquente consiste à penser que le hash des chemins du Nix store est basé sur les sorties du build, alors qu’en réalité il est basé sur l’ensemble des sources et des entrées utilisées pour construire les binaires dans un environnement isolé, qu’il s’agisse ou non de binaires
      Cela signifie qu’on n’obtient pas exactement les avantages de sécurité que certains imaginent, mais qu’en contrepartie on peut utiliser, sous une forme de distribution raisonnablement reproductible, même des logiciels qui ne se buildent pas de manière reproductible, dès lors que les fonctionnalités, les options du compilateur, les versions des dépendances, l’utilisateur, la configuration, etc. restent identiques
    • J’ai l’impression que c’est exactement ce dont parlent la première source et le billet d’origine
      Il s’agit de vérifier si, à partir des mêmes sources, des builds effectués sur des machines différentes produisent les mêmes binaires
      L’idée essentielle est que les binaires ne changent pas d’un build à l’autre
      La méthode de test indique aussi que chaque build est exécuté deux fois, à des moments différents, sur du matériel différent et avec des noyaux différents
    • Je ne savais pas qu’Arch Linux testait la reproductibilité
      Je vois que cela indique 85,6 % de reproductibilité : https://reproducible.archlinux.org
      Je me demande quelle quantité de travail cela demanderait pour NixOS, avec plus de 80 000 paquets dans les dépôts officiels
  • Ce n’est absolument pas vrai, que l’on considère les objectifs de nixpkgs ou la réalité
    Le billet d’origine parle de la reproduction d’une ISO minimale binaire contenant plusieurs paquets binaires

  • Il est assez ironique, et amusant, que le projet OpenBSD avance avec énergie dans la direction exactement opposée
    OpenBSD donne à chaque installation des décalages d’adresses uniques et aléatoires
    Je comprends que les deux objectifs, builds reproductibles et installations uniques, soient orthogonaux et puissent être atteints simultanément, mais cette dualité reste drôle

    • S’il est possible d’aléatoiriser les décalages d’adresses à partir d’une graine fournie, il reste possible de démontrer la reproductibilité
      Ou alors on peut aléatoiriser les décalages au démarrage du programme, ce qui préserverait la reproductibilité tout en améliorant encore la sécurité
      Dans ce cas, les décalages changeraient à chaque exécution
    • OpenBSD effectue une édition de liens aléatoire au moment du démarrage
      Les paquets eux-mêmes peuvent donc toujours être reproductibles
      Toute l’aléatoirisation est effectuée en local après le téléchargement du paquet et la vérification de sa somme de contrôle
  • J’aimerais maintenant que, comme presque toutes les autres distributions Linux le font depuis les années 1990, les mainteneurs se contentent de signer les paquets
    Ainsi, tout le monde pourrait au moins savoir dans une certaine mesure que le code qu’il compile est bien le même code soumis et relu par des personnes connues
    Tant que la signature ne sera pas standardisée, j’ai du mal à imaginer utiliser Nix en production pour protéger quelque chose qui a de la valeur

    • Je vois plutôt les mainteneurs de paquets Nix comme des gens qui fournissent une interface utile permettant de combiner facilement des logiciels
      Pour la plupart, ils n’offrent aucune garantie sur le contenu des paquets
      Attendre de leur signature une garantie significative me semble un peu comme attendre du support produit d’un livreur
      Il n’est même pas nécessaire de leur faire confiance pour ne pas empaqueter quelque chose de malveillant
      Nix fait des builds reproductibles, donc si vous ne voulez pas dépendre du cache binaire, il suffit d’examiner la derivation et de builder vous-même
      Le caractère fondamentalement malveillant ou non du contenu reste au final une question entre le développeur et l’utilisateur
      Si d’autres distributions vous ont amené à croire le contraire, je pense que c’est assez trompeur
      La seule exception qui me vienne à l’esprit serait Tails, mais Tails n’est pas aussi large dans son périmètre que Nix
    • Je me demande quelles distributions font cela
      De ce que j’ai vu, Debian ne le fait plus et c’est le système de build qui signe les builds, Fedora ne le fait pas non plus, et pour Arch je ne suis pas certain mais j’ai l’impression que non
      Le système de build de NixOS signe tous les artefacts de build avec sa propre clé et vérifie les signatures au téléchargement
      Si l’on veut être paranoïaque, Nix permet au moins de tout builder directement depuis les sources
  • C’est une étape très impressionnante, bravo à toutes les personnes qui l’ont rendue possible
    Il est indiqué qu’au moment de rebuilder réellement l’ISO, il y avait encore des différences, dues à des problèmes restants dans le cache Hydra ainsi qu’à la manière dont l’ISO était générée
    Je me demande si quelqu’un peut expliquer comment ils ont corrigé « la manière dont l’ISO était générée »
    J’avais essayé autrefois de produire une ISO reproductible, mais je n’arrivais pas à forcer le système de fichiers à créer les extents de manière déterministe

    • Dans le cas de NixOS, c’est dans la section « comment cela a été reproduit » du billet
      La dernière étape de ce processus génère l’ISO dans le répertoire ./result/iso
      J’imagine que ce que vous cherchez, c’est la commande invoquée par ce build, mais je ne sais pas exactement quelle étape vous cherchez
      Par exemple, l’appel à xorriso se trouve ici : https://github.com/NixOS/nixpkgs/blob/master/nixos/lib/make-...
  • Pour faire ça, ne faut-il pas truquer l’heure système ?
    Le temps finit souvent intégré dans les binaires d’une manière ou d’une autre

    • En pratique, les horodatages sont probablement la cause la plus fréquente de non-déterminisme
      C’est tellement courant que de nombreux compilateurs implémentent SOURCE_DATE_EPOCH, une variable devenue un standard de fait pour falsifier les horodatages : https://reproducible-builds.org/docs/source-date-epoch/
    • Je serais curieux d’avoir un exemple de la manière dont cela arrive, et pour quelle raison
    • Il suffit de ne jamais inclure d’horodatage dans les builds, ou de le fixer à 0, pour que la date de build soit partout 1970
  • Est-ce que cela n’aide pas à résoudre le problème décrit par Ken Thompson dans « Reflections on Trusting Trust » ?
    Si l’on peut bootstrap entièrement tout le système à partir du code source, il me semble qu’il devient plus difficile d’y glisser quelque chose comme un compilateur backdooré

    • En pratique, oui, cela aide, mais ce n’est pas une « solution » complète
      En théorie, il pourrait toujours y avoir une backdoor sophistiquée dans l’environnement où l’ISO est buildée
      Si vous voulez vraiment traiter ce problème, vous pouvez regarder le Diverse Double Compiling(https://dwheeler.com/trusting-trust/) ou le bootstrap de l’environnement complet(https://bootstrappable.org/)
      La section « cette approche n’a-t-elle pas un problème de bootstrap ? » du billet est également pertinente
      Cela dit, le simple fait de reproduire le build aide déjà beaucoup à rendre ce type d’attaque de moins en moins plausible
  • J’ai récemment passé du temps dans l’écosystème Red Hat pour le travail
    Je me demande comment cela se compare à Fedora Silverblue, Ansible, ou Fedora Silverblue + Ansible

    • Dans l’écosystème Fedora, ce qui se rapproche le plus du builder d’ISO NixOS et de sa reproductibilité, c’est osbuild / imagebuilder : https://www.osbuild.org/guides/introduction.html
      Imagebuilder revendique la reproductibilité, mais à ma connaissance il installe la plupart des paquets rpm sous forme binaire et non depuis les sources
      Donc, à moins que tous les paquets d’entrée ne soient eux aussi reproductibles, il ne s’agit pas d’une reproductibilité stricte
      Si les explications sur le build de paquets depuis les sources, la création d’images de distribution et la question de la reproductibilité ne vous parlent pas vraiment, il est probable que vous ne soyez pas le public principal visé
    • Nix est un OS déclaratif, qui décrit à quoi le système d’exploitation doit ressembler
      À l’inverse, Ansible indique les étapes que l’OS doit suivre

Silverblue et Nix sont orthogonaux, hormis le fait que ce sont tous deux des distributions Linux
Silverblue est une tentative de changer la manière de distribuer les logiciels en n’utilisant que des conteneurs sur un hôte immuable
Si vous cherchez une alternative à Ansible qui imite dans une certaine mesure Nix avec Jsonnet et le suivi d’état, Etcha vaut le détour : https://etcha.dev

  • Ansible applique des modifications mutables à l’OS par unités de travail
    Nix est immuable
    Les nouveaux changements sont construits entièrement à neuf, et ce n’est qu’une fois la compilation réussie que tous les paquets sont « liés symboliquement » au système actuel
    Fedora Silverblue repose sur ostree https://github.com/ostreedev/ostree
    Cela fonctionne un peu comme git pour l’arborescence racine, mais il faut redémarrer tout le système pour appliquer les changements
    Nix, lui, fonctionne en liant symboliquement les paquets, il n’est donc pas nécessaire de redémarrer le système
    Une explication plus détaillée est disponible ici : https://dataswamp.org/~solene/2023-07-12-intro-to-immutable-...