firefox: BuildID determinista — se cierra la deuda de reproducibilidad, con guardián
Medido y probado hoy: `atuq` REPRODUCE bit a bit (`why-differs`: 85 entradas idénticas, 0 divergen), lo que valida el re-empaque determinista del omni.ja, el XPI, los iconos copiados y el BuildID derivado del contenido. El que NO reproducía era la BASE: el `BuildID` de firefox era la fecha y hora del build (`20260905035306` en el artefacto sellado), así que dos construcciones con el mismo ArtifactHash daban bytes distintos. Es el agujero exacto que el invariante de reproducibilidad existe para cerrar, y `build-state.json` no lo puede ver: el hash es input-addressed y no se mueve por esto. Verde y mintiendo. El valor sale de `SOURCE_DATE_EPOCH`, que el sandbox de hammer YA fija a 1 para todas las recetas — mejor que una constante escrita a mano, porque usa el mecanismo de hermeticidad que ya existe en vez de inventar un segundo. Alpine hace lo mismo en su APKBUILD. BuildID esperado: 19700101000001. Va en LAS TRES FASES, por la lección que costó 50 minutos esta misma tarde con MOZ_REQUIRE_SIGNING: son shells distintos y `mach build` re-ejecuta el configure cuando le parece. Y con su GUARDIÁN, hermano del de la firma: la fase install compara el BuildID del artefacto contra el que SOURCE_DATE_EPOCH obliga, y falla el build si no coinciden. Los dos fallos de esta familia —flag que el configure acepta y el artefacto ignora— son mudos y viajan lejos; la única defensa es comprobarlos donde importa, que es dentro del artefacto.
This commit is contained in:
+27
-10
@@ -133,16 +133,16 @@ flags = []
|
||||
# Entra en `hash_inputs` A PROPÓSITO (cambia el contenido del artefacto), y usa `zig objcopy`, que
|
||||
# siempre está en el sandbox ⇒ no agrega una dep de build.
|
||||
strip_debug = true
|
||||
# ⚠ DEUDA MEDIDA EL 2026-09-05: ESTE ARTEFACTO NO REPRODUCE BIT A BIT ═══════════════════════════
|
||||
# `application.ini` del artefacto sellado trae `BuildID=20260905035306`: la FECHA Y HORA del build.
|
||||
# Dos construcciones de esta misma receta, con el mismo hash, producen bytes distintos — que es
|
||||
# exactamente el agujero que el invariante de reproducibilidad existe para cerrar, y no se ve en
|
||||
# `build-state.json` porque el ArtifactHash es input-addressed y no se mueve.
|
||||
# El arreglo es el que ya usa Alpine: exportar `MOZ_BUILD_DATE` con un valor determinista en las
|
||||
# fases (su APKBUILD lo deriva de `SOURCE_DATE_EPOCH`; acá el candidato natural es un valor fijo,
|
||||
# que al vivir en el texto de la fase entra en `hash_inputs` y queda declarado).
|
||||
# NO se aplica de paso: cambiar una fase re-hashea la receta y cuesta otro build de cuatro horas
|
||||
# más el re-sellado de `atuq`, que cuelga de ella. Es su propia unidad de trabajo.
|
||||
# ══ `MOZ_BUILD_DATE`: SIN ESTO EL ARTEFACTO NO REPRODUCE ══════════════════════════════════════
|
||||
# El `BuildID` de `application.ini` es, por defecto, la FECHA Y HORA del build. Medido en el
|
||||
# artefacto sellado del 2026-09-05: `BuildID=20260905035306`. Dos construcciones de esta misma
|
||||
# receta, con el mismo ArtifactHash, producían bytes distintos — justo el agujero que el invariante
|
||||
# de reproducibilidad existe para cerrar, y que `build-state.json` no puede ver porque el hash es
|
||||
# input-addressed y no se mueve por esto.
|
||||
#
|
||||
# El valor sale de `SOURCE_DATE_EPOCH`, que el sandbox de hammer YA fija a 1 para todas las recetas.
|
||||
# Es mejor que una constante escrita a mano: usa el mecanismo de hermeticidad que ya existe, en vez
|
||||
# de inventar un segundo. Alpine hace exactamente lo mismo en su APKBUILD.
|
||||
|
||||
[build.phases]
|
||||
configure = '''
|
||||
@@ -183,6 +183,7 @@ export RUST_TARGET=x86_64-alpine-linux-musl
|
||||
# El `export` con valor vacío es lo que hace llegar la variable PRESENTE y vacía; desexportarla la
|
||||
# dejaría sin definir y volvería al default, que acá es «exigir firma».
|
||||
export MOZ_REQUIRE_SIGNING=
|
||||
export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"
|
||||
|
||||
# ── LOS PARCHES ROMPEN EL CHECKSUM DE LOS CRATES VENDORIZADOS ─────────────────────────────────
|
||||
# `error: the listed checksum of third_party/rust/zeitstempel/src/unix.rs has changed`. Los crates
|
||||
@@ -275,6 +276,7 @@ export RUST_TARGET=x86_64-alpine-linux-musl # ver la nota en la fase configure
|
||||
# aceptó — 50 minutos de build para descubrirlo al abrir el navegador. Es exactamente la razón por
|
||||
# la que `RUST_TARGET` se repite en las tres fases.
|
||||
export MOZ_REQUIRE_SIGNING=
|
||||
export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)" # ídem: ver configure
|
||||
export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
|
||||
export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
|
||||
export MOZ_NOSPAM=1
|
||||
@@ -287,6 +289,7 @@ echo "mach build -j$j (MemTotal ${gib} GiB, nproc $n)"
|
||||
install = '''
|
||||
export RUST_TARGET=x86_64-alpine-linux-musl # ver la nota en la fase configure
|
||||
export MOZ_REQUIRE_SIGNING= # ídem
|
||||
export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)" # ídem
|
||||
export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
|
||||
export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
|
||||
DESTDIR=/out ./mach install
|
||||
@@ -310,6 +313,20 @@ if "MOZ_REQUIRE_SIGNING: false" not in t:
|
||||
"las fases (son shells distintos y mach re-ejecuta configure).")
|
||||
print("guardián: MOZ_REQUIRE_SIGNING = false en el artefacto")
|
||||
PY
|
||||
|
||||
# ── GUARDIÁN: QUE EL BuildID NO SEA UNA FECHA ────────────────────────────────────────────────
|
||||
# Mismo razonamiento que el de arriba, y el mismo modo de fallo: si `MOZ_BUILD_DATE` no llega, el
|
||||
# BuildID vuelve a ser la hora del build y el artefacto deja de reproducir SIN QUE NADA LO DIGA —
|
||||
# el ArtifactHash es input-addressed y no se mueve por esto, así que `build-state.json` seguiría en
|
||||
# verde. Se comprueba contra el valor que `SOURCE_DATE_EPOCH=1` obliga.
|
||||
esperado="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"
|
||||
real="$(sed -n 's/^BuildID=//p' /out/usr/lib/firefox/application.ini)"
|
||||
if [ "$real" != "$esperado" ]; then
|
||||
echo "guardián: BuildID=$real y esperaba $esperado — el artefacto NO reproduce." >&2
|
||||
echo " Revisá que MOZ_BUILD_DATE se exporte en TODAS las fases." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "guardián: BuildID = $real (determinista, de SOURCE_DATE_EPOCH)"
|
||||
'''
|
||||
|
||||
[deps]
|
||||
|
||||
Reference in New Issue
Block a user