Etapa F: cierra el hilo userland — el producto arma su userland desde el repo FIRMADO
Hasta hoy el bootstrap del producto hidrataba el userland desde recetas locales hardcodeadas (build_components sobre USERLAND_COMPONENTS/SERVICE_COMPONENTS). Cierre del dogfood: la IMAGEN ahora puede armar su userland por la CADENA DE SUMINISTRO de paquetes — verifica la firma del release y REPRODUCE cada componente desde su .swm. hammer-bootstrap: - product_from_repo(base, repo, trust, ...): igual que product() pero el userland + servicios salen de install_components_from_repo en vez de build_components. - install_components_from_repo: verifica firma del release (exige TRUSTED), por cada componente resuelve el cierre de deps + puebla el catalogo dep.toml + reproduce el source_patch con build_source_patch (chequea el expected_hash anclado). Devuelve los mismos (nombre,hash) que build_components. - seal_product_rootfs: pasos 2-3 comunes extraidos (seed+hash+ensamblado+sellado). hammer-cli: bootstrap product --from-repo DIR --trust DIR. PROPIEDAD CLAVE VERIFICADA E2E: como el hash es por-CONTENIDO, reproducir desde el .swm da los mismos (nombre,hash) que construir la receta -> el product-rootfs via repo firmado es BIT-IDENTICO al hardcodeado (ambas vias -> ba351f1b). Firma del release verificada (TRUSTED by release) antes de tocar nada. "Verificar, no confiar" aplicado al propio ensamblado de la imagen. 40 tests bootstrap verde. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -517,6 +517,15 @@ enum BootstrapCmd {
|
||||
/// determinista (firmas reproducibles). Sólo con `--attest`.
|
||||
#[arg(long)]
|
||||
rootkey: Option<String>,
|
||||
/// [Dogfood] Arma el userland REPRODUCIÉNDOLO desde un REPO FIRMADO (dir local) en vez de las
|
||||
/// recetas locales: verifica la firma del release y reconstruye cada paquete desde su `.swm`
|
||||
/// comparando el `expected_hash`. El `product-rootfs` sale bit-idéntico al de la vía local (hash
|
||||
/// por-contenido). Requiere `--trust`. La cadena de suministro de la propia imagen.
|
||||
#[arg(long)]
|
||||
from_repo: Option<String>,
|
||||
/// Directorio de claves de confianza para verificar la firma del release del `--from-repo`.
|
||||
#[arg(long)]
|
||||
trust: Option<String>,
|
||||
},
|
||||
/// [Stage 2] Ancla el content-hash del rootfs de Stage 1 como referencia de reproducibilidad
|
||||
/// (anota la línea del manifiesto). Con `--verify <hash>` compara contra un rebuild `stage1'`
|
||||
@@ -906,14 +915,31 @@ fn main() -> anyhow::Result<()> {
|
||||
let hash = hammer_bootstrap::stage1(&spec, &base_cfg, &store)?;
|
||||
println!("{hash}");
|
||||
}
|
||||
BootstrapCmd::Product { rootfs, recipes, attest, policy, rootkey } => {
|
||||
BootstrapCmd::Product { rootfs, recipes, attest, policy, rootkey, from_repo, trust } => {
|
||||
let store = hammer_core::Store::open(&cli.store)?;
|
||||
// El zig del lab (no la semilla): los servicios usan su `zig_version` propia
|
||||
// (openssh ⇒ 0.13.0, hermano del default), y si ya están sellados, salen cacheados.
|
||||
let base_cfg = hammer_build::BuildConfig::from_env_or_defaults(store.root());
|
||||
let base = hammer_core::ArtifactHash::from_hex(rootfs.trim_start_matches("b3:"));
|
||||
let recipes_dir = std::path::Path::new(&recipes);
|
||||
let hash = if attest {
|
||||
let hash = if let Some(repo) = from_repo {
|
||||
// [Dogfood] userland desde el repo FIRMADO (la imagen arma su userland por la cadena
|
||||
// de suministro de paquetes, no por recetas locales). --attest se combina como capa
|
||||
// posterior; acá producimos el product-rootfs base reproducido del repo.
|
||||
if attest {
|
||||
anyhow::bail!("--from-repo y --attest aún no se combinan; producí el base con --from-repo y atestá aparte");
|
||||
}
|
||||
let trust_dir = trust.ok_or_else(|| {
|
||||
anyhow::anyhow!("--from-repo requiere --trust <dir> (verificar la firma del release)")
|
||||
})?;
|
||||
hammer_bootstrap::product_from_repo(
|
||||
&base,
|
||||
std::path::Path::new(&repo),
|
||||
std::path::Path::new(&trust_dir),
|
||||
&base_cfg,
|
||||
&store,
|
||||
)?
|
||||
} else if attest {
|
||||
let mut cfg = hammer_bootstrap::AttestConfig { policy, ..Default::default() };
|
||||
if let Some(path) = rootkey {
|
||||
let bytes = std::fs::read(&path)?;
|
||||
|
||||
Reference in New Issue
Block a user