Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
# Install systemd-ukify and systemd-boot for UKIs
# This also installs systemd-boot for the grub UKI case which is not ideal...
if [[ "${boot_type}" == "uki" ]]; then
pkgs_to_install+=(systemd-ukify)
pkgs_to_install+=(systemd-ukify binutils)
fi

if [[ ${#pkgs_to_install[@]} -gt 0 ]]; then
Expand Down Expand Up @@ -135,7 +135,10 @@ ARG pkgversion
ARG SOURCE_DATE_EPOCH
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
# Build RPM directly from source, using cached target directory
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome RPM_VERSION="${pkgversion}" /src/contrib/packaging/build-rpm
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=cache,target=/src/target \
--mount=type=cache,target=/var/roothome \
RPM_VERSION="${pkgversion}" /src/contrib/packaging/build-rpm

# Build a systemd-sysext containing just the bootc binary.
# Skips RPM machinery entirely for fast incremental rebuilds.
Expand Down Expand Up @@ -218,11 +221,10 @@ COPY --from=update-generated-from-code /src/docs/src/*.schema.json /docs/src/
# ----

# Perform all filesystem transformations except generating the sealed UKI (if configured)
FROM base as base-penultimate
FROM base as base-source
ARG variant
ARG bootloader
ARG boot_type
ARG baseconfigs=""

# Switch to a signed systemd-boot, if configured
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
Expand All @@ -247,6 +249,12 @@ rm -rf /var/cache
rm -rf /run/rhsm

EORUN

FROM base-source as base-penultimate-source
ARG boot_type
ARG variant
ARG baseconfigs=""

# Configure the rootfs
ARG rootfs=""
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
Expand All @@ -265,8 +273,23 @@ RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp
COPY --from=packaging /usr-extras/ /usr/
# Clean up package manager caches
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
--mount=type=bind,from=packaging,src=/,target=/run/packaging <<EORUN
/run/packaging/cleanup
EORUN

FROM base-penultimate-source as base-penultimate
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=base-penultimate-source,src=/,target=/run/base-penultimate-src \
--mount=type=bind,from=packaging,src=/,target=/run/packaging <<EORUN

# Remove kernel + initrd if UKI
if [[ "${boot_type}" == "uki" ]]; then
kver=$(bootc container inspect --rootfs /run/base-penultimate-src --json | jq -r '.kernel.version')

rm -v "/usr/lib/modules/$kver/vmlinuz"
rm -v "/usr/lib/modules/$kver/initramfs.img"
fi
Comment on lines +287 to +291
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this one would end up being closer to user-facing, I would prefer to streamline this more.

I think what would work best is to have our "base" build process be a stage that generates a single intermediate image with /rootfs and /kernel or so. This could be part of bootc-base-imagectl, or we could have it be part of something more like bootc container split-root-and-uki?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sgtm. By the "base" build process, are you referring to the Dockerfile (in bootc) or is it about something else?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern we apply to this should generalize to https://github.com/cgwalters/rhel-bootc-examples/tree/sealed/sealing as well etc.

EORUN

# Generate the sealed UKI in a separate stage
# This computes the composefs digest from base-penultimate and creates a signed UKI
Expand All @@ -283,18 +306,29 @@ RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=secret,id=secureboot_key \
--mount=type=secret,id=secureboot_cert \
--mount=type=bind,from=base-penultimate-source,src=/,target=/run/base-penultimate-src \
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
--mount=type=bind,from=base-penultimate,src=/,target=/run/target <<EORUN
set -xeuo pipefail

allow_missing_verity=false
allow_missing_verity=()

if [[ $filesystem == "xfs" ]]; then
allow_missing_verity=true
allow_missing_verity=(--allow-missing-verity)
fi

if test "${boot_type}" = "uki"; then
/run/packaging/seal-uki /run/target /out /run/secrets $allow_missing_verity $seal_state
kver=$(bootc container inspect --rootfs /run/base-penultimate-src --json | jq -r '.kernel.version')

/run/packaging/seal-uki \
--target /run/target \
--output /out \
--secrets /run/secrets \
"${allow_missing_verity[@]}" \
--kernel "/run/base-penultimate-src/usr/lib/modules/$kver/vmlinuz" \
--kver "$kver" \
--initramfs "/run/base-penultimate-src/usr/lib/modules/$kver/initramfs.img" \
--seal-state $seal_state
fi
EORUN

Expand All @@ -304,11 +338,13 @@ ARG variant
ARG boot_type
# Copy the sealed UKI and finalize the image (remove raw kernel, create symlinks)
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
--mount=type=bind,from=base-penultimate-source,src=/,target=/run/base-penultimate-src \
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
--mount=type=bind,from=sealed-uki,src=/,target=/run/sealed-uki <<EORUN
set -xeuo pipefail
if test "${boot_type}" = "uki"; then
/run/packaging/finalize-uki /run/sealed-uki/out
kver=$(bootc container inspect --rootfs /run/base-penultimate-src --json | jq -r '.kernel.version')
/run/packaging/finalize-uki /run/sealed-uki/out "$kver"
fi
EORUN
# And finally, test our linting
Expand Down
15 changes: 2 additions & 13 deletions contrib/packaging/finalize-uki
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,8 @@ set -xeuo pipefail
# Path to directory containing the generated UKI
uki_src=$1
shift

# Find the kernel version from the current system
kver=$(bootc container inspect --json | jq -r '.kernel.version')
if [ -z "$kver" ] || [ "$kver" = "null" ]; then
echo "Error: No kernel found" >&2
exit 1
fi
kver=$1
shift

# Create the EFI directory structure
mkdir -p /boot/EFI/Linux
Expand All @@ -36,12 +31,6 @@ mkdir -p /boot/EFI/Linux
target=/boot/EFI/Linux/${kver}.efi
cp "${uki_src}/${kver}.efi" "${target}"

# Remove the raw kernel and initramfs since we're using a UKI now.
# NOTE: We intentionally keep these for now until bcvk is updated to extract
# kernel/initramfs from UKIs in subdirectories. Once bcvk PR #144 is fixed
# to look for .efi files in /usr/lib/modules/<kver>/, we can uncomment this.
# rm -v "/usr/lib/modules/${kver}/vmlinuz" "/usr/lib/modules/${kver}/initramfs.img"

# NOTE: We used to create a symlink from /usr/lib/modules/${kver}/${kver}.efi to the UKI
# for tooling compatibility. However, composefs-boot's find_uki_components() doesn't
# handle symlinks correctly and fails with "is not a regular file". The UKI is already
Expand Down
98 changes: 71 additions & 27 deletions contrib/packaging/seal-uki
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,82 @@
# Generate a sealed UKI with embedded composefs digest
set -xeuo pipefail

# Path to the desired root filesystem
target=$1
shift
# Write to this directory
output=$1
shift
# Path to secrets directory
secrets=$1
shift
allow_missing_verity=$1
shift
seal_state=$1
shift

if [[ $seal_state == "sealed" && $allow_missing_verity == "true" ]]; then
missing_verity=()

while [ ! -z "${1:-}" ]; do
case "$1" in
# Path to the desired root filesystem
"--target")
target="$2"
shift
shift
;;

# Write to this directory
"--output")
output="$2"
shift
shift
;;

# Path to secrets directory
"--secrets")
secrets="$2"
shift
shift
;;

"--allow-missing-verity")
missing_verity=(--allow-missing-verity)
shift
;;

"--seal-state")
seal_state="$2"
shift
shift
;;

# The kernel version
"--kver")
kver="$2"
shift
shift
;;

# Path to the kernel
"--kernel")
kernel="$2"
shift
shift
;;

# Path to the initrd
"--initramfs")
initramfs="$2"
shift
shift
;;

* )
echo "Argument $1 not understood"
exit 1
;;
esac
done

if [[ $seal_state == "sealed" && ${#missing_verity[@]} -gt 0 ]]; then
echo "Cannot have missing verity with sealed UKI" >&2
exit 1
fi

# Find the kernel version (needed for output filename)
kver=$(bootc container inspect --rootfs "${target}" --json | jq -r '.kernel.version')
if [ -z "$kver" ] || [ "$kver" = "null" ]; then
echo "Error: No kernel found" >&2
exit 1
if [[ -z $kernel || -z $initramfs || -z $kver ]]; then
echo "kernel, initramfs and kver are required" >&2
exit 1
fi

kernel_params=(--kernel "$kernel" --initramfs "$initramfs" --kver "$kver")

mkdir -p "${output}"

# Baseline ukify options
Expand All @@ -45,12 +95,6 @@ fi
# Baseline container ukify options
containerukifyargs=(--rootfs "${target}")

missing_verity=()

if [[ $allow_missing_verity == "true" ]]; then
missing_verity+=(--allow-missing-verity)
fi

# Build the UKI using bootc container ukify
# This computes the composefs digest, reads kargs from kargs.d, and invokes ukify
bootc container ukify "${containerukifyargs[@]}" "${missing_verity[@]}" -- "${ukifyargs[@]}"
bootc container ukify "${containerukifyargs[@]}" "${kernel_params[@]}" "${missing_verity[@]}" -- "${ukifyargs[@]}"
13 changes: 8 additions & 5 deletions crates/lib/src/bootc_composefs/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ use bootc_utils::try_deserialize_timestamp;
use cap_std_ext::{cap_std::fs::Dir, dirext::CapStdExtDirExt};
use ostree_container::OstreeImageReference;
use ostree_ext::container::{self as ostree_container};
use ostree_ext::containers_image_proxy;
use ostree_ext::containers_image_proxy::{ImageProxy, ImageReference};

use ostree_ext::oci_spec;
use ostree_ext::{container::deploy::ORIGIN_CONTAINER, oci_spec::image::ImageConfiguration};

Expand Down Expand Up @@ -379,14 +380,16 @@ pub(crate) fn list_bootloader_entries(storage: &Storage) -> Result<Vec<Bootloade
/// imgref = transport:image_name
#[context("Getting container info")]
pub(crate) async fn get_container_manifest_and_config(
imgref: &String,
imgref: &ImageReference,
) -> Result<ImgConfigManifest> {
let mut config = crate::deploy::new_proxy_config();
ostree_ext::container::merge_default_container_proxy_opts(&mut config)?;
let proxy = containers_image_proxy::ImageProxy::new_with_config(config).await?;

ostree_ext::container::apply_container_proxy_opts_for_transport(&mut config, imgref.transport)?;

let proxy = ImageProxy::new_with_config(config).await?;

let img = proxy
.open_image(&imgref)
.open_image_ref(&imgref)
.await
.with_context(|| format!("Opening image {imgref}"))?;

Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/bootc_composefs/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub(crate) async fn is_image_pulled(
imgref: &ImageReference,
) -> Result<(Option<Sha512HashValue>, ImgConfigManifest)> {
let imgref_repr = imgref.to_image_proxy_ref()?;
let img_config_manifest = get_container_manifest_and_config(&imgref_repr.to_string()).await?;
let img_config_manifest = get_container_manifest_and_config(&imgref_repr).await?;

let img_digest = img_config_manifest.manifest.config().digest().digest();

Expand Down
38 changes: 38 additions & 0 deletions crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,19 @@ pub(crate) enum ContainerOpts {
#[clap(long)]
write_dumpfile_to: Option<Utf8PathBuf>,

/// The kernel version.
/// Required if kernel is passed
#[clap(long, requires = "kernel")]
kver: Option<String>,

/// Path to the kernel
#[clap(long, requires = "initramfs", requires = "kver")]
kernel: Option<Utf8PathBuf>,

/// Path to the initramfs
#[clap(long, requires = "kernel")]
initramfs: Option<Utf8PathBuf>,

/// Additional arguments to pass to ukify (after `--`).
#[clap(last = true)]
args: Vec<OsString>,
Expand Down Expand Up @@ -1902,12 +1915,37 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
kargs,
allow_missing_verity,
write_dumpfile_to,
kernel,
kver,
initramfs,
args,
} => {
let kernel = match (kernel, initramfs) {
(Some(path), Some(initramfs)) => Some(crate::kernel::KernelInternal {
kernel: crate::kernel::Kernel {
unified: false,
version: kver
.ok_or_else(|| anyhow::anyhow!("Expected kver to be present"))?,
},
k_type: crate::kernel::KernelType::Vmlinuz { path, initramfs },
}),

(None, None) => {
eprintln!(
"Warning: `bootc container ukify` without --kernel and --initramfs parameters is deprecated and will be removed in a future version"
);
None
}

// Shouldn't happen due to clap constraints but for sanity
_ => anyhow::bail!("--kernel and --initramfs must be provided together"),
};

crate::ukify::build_ukify(
&rootfs,
&kargs,
&args,
kernel,
allow_missing_verity,
write_dumpfile_to.as_deref(),
)
Expand Down
3 changes: 1 addition & 2 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2017,8 +2017,7 @@ async fn install_to_filesystem_impl(
// Pre-flight disk space check for native composefs install path.
{
let imgref = &state.source.imageref;
let imgref_repr = imgref.to_string();
let img_manifest_config = get_container_manifest_and_config(&imgref_repr).await?;
let img_manifest_config = get_container_manifest_and_config(&imgref).await?;
crate::store::ensure_composefs_dir(&rootfs.physical_root)?;
// Use init_path since the repo may not exist yet during install
let (cfs_repo, _created) = crate::store::ComposefsRepository::init_path(
Expand Down
Loading
Loading