Builds reproductibles
Construire deux fois le même commit donne les mêmes octets. Deux builds indépendants d’un commit (builders neufs, sans cache, chemins différents, dates de fichiers différentes) produisent des résultats identiques bit à bit :
- le binaire du serveur (
dumbmonit, musl statique, interface web embarquée) ; - les binaires de l’agent pour Linux x86_64 et aarch64, Windows x86_64 et FreeBSD x86_64 ;
- les fichiers de l’interface web embarqués dans le serveur ;
- l’image du serveur et l’image de l’agent, comparées par le digest du manifeste
OCI (chaque couche et la configuration de l’image), lorsqu’elles sont
construites avec la date du commit comme
SOURCE_DATE_EPOCH; - les binaires de l’agent macOS, lorsqu’ils sont construits sur la même version de macOS et de Xcode.
Cela est vérifié à chaque pull request qui touche au code et à chaque push sur
main par le workflow
Reproducible builds :
deux runners construisent le commit indépendamment et le job échoue dès qu’un
seul fichier diffère, avec un rapport diffoscope
en artefact.
Ce qui rend le build répétable
Section intitulée « Ce qui rend le build répétable »- Dépendances verrouillées.
Cargo.locketweb/package-lock.jsonsont versionnés ; le build utilisecargo build --lockedetnpm ci. - Outils épinglés. Chaque image de base du
Dockerfileest épinglée par digest SHA-256 : Node pour l’interface web, Rust sur Alpine pour le serveur (songccetmusl-dev, qui compilent SQLite, viennent avec cette image), cargo-zigbuild pour les agents (il fixe la version de zig, qui les lie) et la version de VictoriaMetrics. La toolchain Rust des agents est épinglée avecRUST_TOOLCHAIN. - Aucune date de build dans le résultat. Le commit identifie un build,
jamais l’heure du build :
- l’identifiant de version de l’interface web (
version.namede SvelteKit, qui vaut par défaut l’heure courante) est le commit (DUMBMONIT_BUILD), ou à défautSOURCE_DATE_EPOCH; - les fichiers embarqués dans le serveur ne portent aucune date de
modification (
rust-embedavecdeterministic-timestamps) ; - l’en-tête PE de l’agent Windows reçoit une date d’édition de liens fixe
(
SOURCE_DATE_EPOCH=0dans l’étape de l’agent) ; - BuildKit écrit la date du commit dans la configuration de l’image, son
historique et, avec
rewrite-timestamp=true, les dates de fichiers des couches.
- l’identifiant de version de l’interface web (
- Aucun chemin de machine dans les binaires. Les builds s’exécutent dans des
conteneurs aux chemins fixes (
/build,/usr/local/cargo) ; le build macOS remappe ses chemins avec--remap-path-prefix. - Profil de build fixe.
Cargo.tomlfixelto, une seule unité de génération de code,stripetpanic = "abort".
Comment le vérifier vous-même
Section intitulée « Comment le vérifier vous-même »Il vous faut Docker avec Buildx (BuildKit 0.13 ou plus récent) et une copie du commit ou du tag à vérifier. Passez les deux mêmes entrées que le workflow de publication, le commit et sa date :
git clone https://github.com/laupernoe/dumbmonit && cd dumbmonitgit checkout v0.9.1 # or any commitexport SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)set -- --build-arg DUMBMONIT_BUILD=$(git rev-parse HEAD) --platform linux/amd64 \ --provenance=false --sbom=false
docker buildx build "$@" --target server-dist --output type=local,dest=out/server .docker buildx build "$@" --target agent-dist --output type=local,dest=out/agent .sha256sum out/server/* out/agent/*Construisez une seconde fois (autre machine, autre répertoire, --no-cache) et
comparez les sommes de contrôle : elles sont identiques.
Pour comparer avec une version publiée :
-
Binaires de l’agent : comparez avec les fichiers
.sha256joints à la version GitHub du tag. -
Binaire du serveur : extrayez-le de l’image publiée et comparez :
Fenêtre de terminal docker create --name dm ghcr.io/laupernoe/dumbmonit:0.9.1docker cp dm:/dumbmonit published-dumbmonit && docker rm dmsha256sum published-dumbmonit out/server/dumbmonit -
Images : construisez l’image sous forme d’archive OCI et comparez ses couches avec celles de l’image publiée (l’image publiée porte aussi des étiquettes, une provenance de build et un SBOM, qui changent le digest de son manifeste mais pas ses couches ; image de l’agent : mêmes commandes avec
--target agent-imageetghcr.io/laupernoe/dumbmonit-agent) :Fenêtre de terminal docker buildx build "$@" --output type=oci,dest=image.tar,rewrite-timestamp=true .mkdir image && tar -xf image.tar -C imageblob() { echo "image/blobs/sha256/${1#sha256:}"; }manifest=$(jq -r '.manifests[0].digest' image/index.json)config=$(jq -r '.config.digest' "$(blob "$manifest")")# Layers of the local build (uncompressed digests):jq -r '.rootfs.diff_ids[]' "$(blob "$config")"# Layers of the published image:docker buildx imagetools inspect ghcr.io/laupernoe/dumbmonit:0.9.1 \--format '{{range .Image.RootFS.DiffIDs}}{{println .}}{{end}}'
Un écart mérite un ticket : ouvrez-en un en indiquant les commandes exécutées.
Signatures de l’agent
Section intitulée « Signatures de l’agent »L’image serveur publiée a une couche de plus qu’un build reproductible : les
fichiers .sig, copiés depuis un contexte de build fourni par le workflow de
release (voir Signer les binaires de l’agent). C’est la
seule différence. Les binaires de l’agent eux-mêmes sont inchangés, et les
vérifications de reproductibilité construisent sans cette couche.
- Les images publiées avant la 0.9.1 ont été construites sans
SOURCE_DATE_EPOCH: leurs binaires et leurs couches diffèrent d’une reconstruction. La comparaison d’un build local avec une image publiée n’a pas encore été exécutée : la première version construite ainsi est la 0.9.1. - L’agent macOS est construit sur un runner macOS de GitHub (le SDK d’Apple
ne peut pas être redistribué, il ne peut donc pas être construit dans un
conteneur). Il est identique entre deux builds sur les mêmes versions de macOS
et de Xcode, que le journal de la version affiche (
xcodebuild -version) ; un autre Xcode peut donner un autre binaire. - Le temps. Les images de base sont épinglées par digest et restent
disponibles, mais l’index de paquets Alpine utilisé pour
make(un outil de build, qui ne fait pas partie du résultat), npm et crates.io sont des services en ligne : une reconstruction des années plus tard dépend de leur capacité à servir encore les versions verrouillées. - Sans les arguments de build, un
docker build .local sansDUMBMONIT_BUILDniSOURCE_DATE_EPOCHdonne un identifiant de version de l’interface web tiré de l’heure courante, et donc un binaire de serveur différent à chaque fois. Passez les deux, comme ci-dessus.