diff --git a/Dockerfile.cfsuki b/Dockerfile.cfsuki index 6dab68449..2fd9bb047 100644 --- a/Dockerfile.cfsuki +++ b/Dockerfile.cfsuki @@ -28,10 +28,7 @@ RUN --mount=type=secret,id=key \ # Should be generated externally test -n "${COMPOSEFS_FSVERITY}" - # Inject the composefs kernel argument and specify a root with the x86_64 DPS UUID. - # TODO: Discoverable partition fleshed out, or drop root UUID as systemd-stub extension - # TODO: https://github.com/containers/composefs-rs/issues/183 - cmdline="composefs=${COMPOSEFS_FSVERITY} root=UUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 console=ttyS0,115200n8 enforcing=0 rw" + cmdline="composefs=${COMPOSEFS_FSVERITY} console=ttyS0,115200n8 enforcing=0 rw" # pesign uses NSS database so create it from input cert/key mkdir pesign diff --git a/crates/blockdev/src/blockdev.rs b/crates/blockdev/src/blockdev.rs index 284a76031..3905b7b87 100644 --- a/crates/blockdev/src/blockdev.rs +++ b/crates/blockdev/src/blockdev.rs @@ -37,6 +37,7 @@ pub struct Device { // Filesystem-related properties pub label: Option, pub fstype: Option, + pub uuid: Option, pub path: Option, } diff --git a/crates/lib/src/bootc_composefs/boot.rs b/crates/lib/src/bootc_composefs/boot.rs index fd4c71981..08c19d0ee 100644 --- a/crates/lib/src/bootc_composefs/boot.rs +++ b/crates/lib/src/bootc_composefs/boot.rs @@ -51,7 +51,6 @@ use crate::{ BOOT_LOADER_ENTRIES, COMPOSEFS_CMDLINE, ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_DIGEST, STAGED_BOOT_LOADER_ENTRIES, STATE_DIR_ABS, USER_CFG, USER_CFG_STAGED, }, - discoverable_partition_specification::this_arch_root, install::RW_KARG, spec::{Bootloader, Host}, }; @@ -412,10 +411,7 @@ pub(crate) fn setup_composefs_bls_boot( ( Utf8PathBuf::from("/sysroot"), get_esp_partition(&sysroot_parent)?.0, - Cmdline::from(format!( - "root=UUID={} {RW_KARG} {COMPOSEFS_CMDLINE}={id_hex}", - this_arch_root() - )), + Cmdline::from(format!("{RW_KARG} {COMPOSEFS_CMDLINE}={id_hex}")), fs, bootloader, ) diff --git a/crates/lib/src/install/baseline.rs b/crates/lib/src/install/baseline.rs index cff80ce7c..40a537e11 100644 --- a/crates/lib/src/install/baseline.rs +++ b/crates/lib/src/install/baseline.rs @@ -41,8 +41,6 @@ pub(crate) const EFIPN_SIZE_MB: u32 = 512; /// We need more space than ostree as we have UKIs and UKI addons /// We might also need to store UKIs for pinned deployments pub(crate) const CFS_EFIPN_SIZE_MB: u32 = 1024; -/// The GPT type for "linux" -pub(crate) const LINUX_PARTTYPE: &str = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"; #[cfg(feature = "install-to-disk")] pub(crate) const PREPBOOT_GUID: &str = "9E1A2D38-C612-4316-AA26-8B49521E5A8B"; #[cfg(feature = "install-to-disk")] @@ -113,7 +111,8 @@ fn mkfs<'a>( let devinfo = bootc_blockdev::list_dev(dev.into())?; let size = ostree_ext::glib::format_size(devinfo.size); - let u = uuid::Uuid::parse_str(crate::discoverable_partition_specification::this_arch_root())?; + // Generate a random UUID for the filesystem + let u = uuid::Uuid::new_v4(); let mut t = Task::new( &format!("Creating {label} filesystem ({fs}) on device {dev} (size={size})"), @@ -315,9 +314,11 @@ pub(crate) fn install_create_rootfs( let root_size = root_size .map(|v| Cow::Owned(format!("size={v}MiB, "))) .unwrap_or_else(|| Cow::Borrowed("")); + let rootpart_uuid = + uuid::Uuid::parse_str(crate::discoverable_partition_specification::this_arch_root())?; writeln!( &mut partitioning_buf, - r#"{root_size}type={LINUX_PARTTYPE}, name="root""# + r#"{root_size}type={rootpart_uuid}, name="root""# )?; tracing::debug!("Partitioning: {partitioning_buf}"); Task::new("Initializing partitions", "sfdisk") @@ -336,9 +337,14 @@ pub(crate) fn install_create_rootfs( let base_partitions = &bootc_blockdev::partitions_of(&devpath)?; let root_partition = base_partitions.find_partno(rootpn)?; - if root_partition.parttype.as_str() != LINUX_PARTTYPE { + // Verify the partition type matches the DPS root partition type for this architecture + let expected_parttype = crate::discoverable_partition_specification::this_arch_root(); + if !root_partition + .parttype + .eq_ignore_ascii_case(expected_parttype) + { anyhow::bail!( - "root partition {partno} has type {}; expected {LINUX_PARTTYPE}", + "root partition {rootpn} has type {}; expected {expected_parttype}", root_partition.parttype.as_str() ); } diff --git a/crates/tests-integration/src/composefs_bcvk.rs b/crates/tests-integration/src/composefs_bcvk.rs index 41f46e596..35eae10f6 100644 --- a/crates/tests-integration/src/composefs_bcvk.rs +++ b/crates/tests-integration/src/composefs_bcvk.rs @@ -80,6 +80,13 @@ fn inner_tests() -> Vec { assert_eq!(verity_from_status.unwrap(), verity_from_cmdline); + // Verify that we booted via systemd-gpt-auto-generator by checking + // that /proc/cmdline does NOT contain a root= parameter + let has_root_param = cmdline.iter().any(|entry| { + entry.key() == "root".into() + }); + assert!(!has_root_param, "Sealed composefs image should not have root= in kernel cmdline; systemd-gpt-auto-generator should discover the root partition via DPS"); + Ok(()) })] .into_iter() diff --git a/docs/src/man/bootc-install-to-disk.8.md b/docs/src/man/bootc-install-to-disk.8.md index 124456336..b470b7200 100644 --- a/docs/src/man/bootc-install-to-disk.8.md +++ b/docs/src/man/bootc-install-to-disk.8.md @@ -19,6 +19,19 @@ the container image, alongside any required system partitions such as the EFI system partition. Use `install to-filesystem` for anything more complex such as RAID, LVM, LUKS etc. +## Partitioning details + +The default as of bootc 1.11 uses the [Discoverable Partitions Specification](https://uapi-group.org/specifications/specs/discoverable_partitions_specification/) +for the generated root filesystem, as well as any required system partitions +such as the EFI system partition. + +Note that by default when used with "type 1" bootloader setups (i.e. non-UKI) +a kernel argument `root=UUID=` is injected by default. + +When used with the composefs backend and UKIs, it's recommended that +a bootloader implementing the DPS specification is used and that the root +partition is auto-discovered. + # OPTIONS diff --git a/tmt/tests/booted/readonly/030-test-composefs.nu b/tmt/tests/booted/readonly/030-test-composefs.nu index b9978c4a8..b7f028e44 100644 --- a/tmt/tests/booted/readonly/030-test-composefs.nu +++ b/tmt/tests/booted/readonly/030-test-composefs.nu @@ -3,12 +3,26 @@ use tap.nu tap begin "composefs integration smoke test" +def parse_cmdline [] { + open /proc/cmdline | str trim | split row " " +} + # Detect composefs by checking if composefs field is present let st = bootc status --json | from json let is_composefs = ($st.status.booted.composefs? != null) let expecting_composefs = ($env.BOOTC_variant? | default "" | find "composefs") != null if $expecting_composefs { assert $is_composefs + # When using systemd-boot with DPS (Discoverable Partition Specification), + # /proc/cmdline should NOT contain a root= parameter because systemd-gpt-auto-generator + # discovers the root partition automatically + # Note that there is `bootctl --json=pretty` but it doesn't actually output JSON + let bootctl_output = (bootctl) + if ($bootctl_output | str contains 'Product: systemd-boot') { + let cmdline = parse_cmdline + let has_root_param = ($cmdline | any { |param| $param | str starts-with 'root=' }) + assert (not $has_root_param) "systemd-boot image should not have root= in kernel cmdline; systemd-gpt-auto-generator should discover the root partition via DPS" + } } if $is_composefs {