From 2db0f2a352a64cfb76f787b9b32e856b4dacaf33 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 29 Aug 2024 17:57:54 -0400 Subject: [PATCH] Require matching architecture for bootable images xref https://github.com/containers/bootc/issues/682 --- lib/src/container/store.rs | 9 ++++++- lib/src/integrationtest.rs | 21 ++++++++++++--- lib/tests/it/main.rs | 52 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/lib/src/container/store.rs b/lib/src/container/store.rs index 0266345f..7f51e3f0 100644 --- a/lib/src/container/store.rs +++ b/lib/src/container/store.rs @@ -20,7 +20,9 @@ use containers_image_proxy::{ImageProxy, OpenedImage}; use flate2::Compression; use fn_error_context::context; use futures_util::TryFutureExt; -use oci_spec::image::{self as oci_image, Descriptor, History, ImageConfiguration, ImageManifest}; +use oci_spec::image::{ + self as oci_image, Arch, Descriptor, History, ImageConfiguration, ImageManifest, +}; use ostree::prelude::{Cast, FileEnumeratorExt, FileExt, ToVariant}; use ostree::{gio, glib}; use std::collections::{BTreeSet, HashMap}; @@ -584,6 +586,11 @@ impl ImageImporter { if !bootable { anyhow::bail!("Target image does not have {bootable_key} label"); } + let container_arch = config.architecture(); + let target_arch = &Arch::default(); + if container_arch != target_arch { + anyhow::bail!("Image has architecture {container_arch}; expected {target_arch}"); + } } let (commit_layer, component_layers, remaining_layers) = diff --git a/lib/src/integrationtest.rs b/lib/src/integrationtest.rs index 3b3e82e0..1ceadb63 100644 --- a/lib/src/integrationtest.rs +++ b/lib/src/integrationtest.rs @@ -10,8 +10,10 @@ use cap_std_ext::cap_std; use containers_image_proxy::oci_spec; use fn_error_context::context; use gio::prelude::*; -use oci_spec::image as oci_image; -use ocidir::GzipLayerWriter; +use ocidir::{ + oci_spec::image::{Arch, Platform}, + GzipLayerWriter, +}; use ostree::gio; use xshell::cmd; @@ -46,6 +48,7 @@ pub fn generate_derived_oci( Ok(()) }, tag, + None, ) } @@ -56,6 +59,7 @@ pub fn generate_derived_oci_from_tar( src: impl AsRef, f: F, tag: Option<&str>, + arch: Option, ) -> Result<()> where F: FnOnce(&mut GzipLayerWriter) -> Result<()>, @@ -67,6 +71,10 @@ where let mut manifest = src.read_manifest()?; let mut config: oci_spec::image::ImageConfiguration = src.read_json_blob(manifest.config())?; + if let Some(arch) = arch.as_ref() { + config.set_architecture(arch.clone()); + } + let mut bw = src.create_gzip_layer(None)?; f(&mut bw)?; let new_layer = bw.complete()?; @@ -92,10 +100,15 @@ where let new_config_desc = src.write_config(config)?; manifest.set_config(new_config_desc); + let mut platform = Platform::default(); + if let Some(arch) = arch.as_ref() { + platform.set_architecture(arch.clone()); + } + if let Some(tag) = tag { - src.insert_manifest(manifest, Some(tag), oci_image::Platform::default())?; + src.insert_manifest(manifest, Some(tag), platform)?; } else { - src.replace_with_single_manifest(manifest, oci_image::Platform::default())?; + src.replace_with_single_manifest(manifest, platform)?; } Ok(()) } diff --git a/lib/tests/it/main.rs b/lib/tests/it/main.rs index 5d8344c5..2732217b 100644 --- a/lib/tests/it/main.rs +++ b/lib/tests/it/main.rs @@ -4,6 +4,7 @@ use cap_std::fs::{Dir, DirBuilder, DirBuilderExt}; use cap_std_ext::cap_std; use containers_image_proxy::oci_spec; use containers_image_proxy::oci_spec::image::ImageManifest; +use ocidir::oci_spec::image::Arch; use once_cell::sync::Lazy; use ostree_ext::chunking::ObjectMetaSized; use ostree_ext::container::{store, ManifestDiff}; @@ -768,6 +769,55 @@ fn validate_chunked_structure(oci_path: &Utf8Path) -> Result<()> { Ok(()) } +#[tokio::test] +async fn test_container_arch_mismatch() -> Result<()> { + let fixture = Fixture::new_v1()?; + + let imgref = fixture.export_container().await.unwrap().0; + + // Build a derived image + let derived_path = &fixture.path.join("derived.oci"); + let srcpath = imgref.name.as_str(); + oci_clone(srcpath, derived_path).await.unwrap(); + ostree_ext::integrationtest::generate_derived_oci_from_tar( + derived_path, + |w| { + let mut layer_tar = tar::Builder::new(w); + let mut h = tar::Header::new_gnu(); + h.set_uid(0); + h.set_gid(0); + h.set_size(0); + h.set_mode(0o755); + h.set_entry_type(tar::EntryType::Directory); + layer_tar.append_data( + &mut h.clone(), + "etc/mips-operating-system", + &mut std::io::empty(), + )?; + layer_tar.into_inner()?; + Ok(()) + }, + None, + Some(Arch::Mips64le), + )?; + + let derived_imgref = OstreeImageReference { + sigverify: SignatureSource::ContainerPolicyAllowInsecure, + imgref: ImageReference { + transport: Transport::OciDir, + name: derived_path.to_string(), + }, + }; + let mut imp = + store::ImageImporter::new(fixture.destrepo(), &derived_imgref, Default::default()).await?; + imp.require_bootable(); + imp.set_ostree_version(2023, 11); + let r = imp.prepare().await; + assert_err_contains(r, "Image has architecture mips64le"); + + Ok(()) +} + #[tokio::test] async fn test_container_chunked() -> Result<()> { let nlayers = LAYERS_V0_LEN - 1; @@ -1118,6 +1168,7 @@ async fn test_container_etc_hardlinked() -> Result<()> { Ok(()) }, None, + None, )?; let derived_imgref = OstreeImageReference { @@ -1534,6 +1585,7 @@ async fn test_container_write_derive_sysroot_hardlink() -> Result<()> { Ok::<_, anyhow::Error>(()) }, None, + None, )?; let derived_ref = &OstreeImageReference { sigverify: SignatureSource::ContainerPolicyAllowInsecure,