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
46 changes: 38 additions & 8 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,7 +221,7 @@ 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-penultimate-source
ARG variant
ARG bootloader
ARG boot_type
Expand Down Expand Up @@ -247,6 +250,10 @@ rm -rf /var/cache
rm -rf /run/rhsm

EORUN

FROM base-penultimate-source as base-penultimate
ARG boot_type

# Configure the rootfs
ARG rootfs=""
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
Expand All @@ -265,9 +272,19 @@ 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=base-penultimate-source,src=/,target=/run/base-penultimate-src \
--mount=type=bind,from=packaging,src=/,target=/run/packaging <<EORUN
/run/packaging/cleanup

# 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 +281 to +285
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?

EORUN

# Generate the sealed UKI in a separate stage
# This computes the composefs digest from base-penultimate and creates a signed UKI
# We need our newly-built bootc for the compute-composefs-digest command
Expand All @@ -283,18 +300,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 +332,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[@]}"
22 changes: 19 additions & 3 deletions crates/lib/src/bootc_composefs/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,28 @@ pub(crate) fn list_bootloader_entries(storage: &Storage) -> Result<Vec<Bootloade
pub(crate) async fn get_container_manifest_and_config(
imgref: &String,
) -> Result<ImgConfigManifest> {
use containers_image_proxy::{ImageProxy, ImageReference, Transport};

let imgref = ImageReference::from_str(&imgref)
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.

In the caller we convert it to a string, here we convert back...

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.

The problem is they're two different types. In the caller we have ostree_container::ImageReference and ImageProxy::open_image_ref asks for containers_image_proxy::ImageReference

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.

We can convert it to string in Proxy::open_image. I also got confused by the two ImageReference in the codebase with one having Transport as an enum and the other one having it as a string, and we're using both...

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.

I ended up doing #2204, which does require bootc-dev/containers-image-proxy-rs#138 first

.context("Failed to parse '{imgref}' into ImageReference")?;

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?;

if imgref.transport == Transport::ContainerStorage {
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.

I think this would be the second copy of this, probably worth deduplicating

// Fetching from containers-storage, may require privileges to read files
ostree_ext::container::merge_default_container_proxy_opts_with_isolation(
&mut config,
None,
)?;
} else {
// Apply our defaults to the proxy config
ostree_ext::container::merge_default_container_proxy_opts(&mut config)?;
}

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
33 changes: 33 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 @@ -1896,12 +1909,32 @@ 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) => 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
Loading
Loading