From 9a2d628351f9909eecea3c8d2b2e3bcd65db5389 Mon Sep 17 00:00:00 2001 From: cgwalters-bot Date: Thu, 24 Sep 2026 12:06:51 -0400 Subject: [PATCH 1/3] docs: Split the composefs backend docs out of the experimental chapter Prep for declaring the composefs backend stable. Its single page mixed architecture, image building, bootloader and install material, each of which has a natural home in the production docs, so move each section there verbatim: the storage layout into bootc-sysroot(7), sealed image building into a new bootc-sealed-images(7) under Building images, the bootloader and install notes into bootc-bootloaders(7) and bootc-installation(7), and the rest into a new bootc-composefs(7) under Architecture. Only headings and intra-page links change here, plus dropping a sentence in the bootloader section that pointed at bootc-bootloaders(7) itself (`git diff --color-moved` shows the rest as moves). The wording, including the "experimental" status text, is updated in the next commit. Redirects keep the old experimental-composefs.html URL, and its main anchors, working, as well as the short-lived bootc-experimental-composefs.7.html. Generated-by: AI Signed-off-by: Colin Walters --- CONTRIBUTING.md | 2 +- docs/book.toml | 13 +- docs/src/SUMMARY.md | 3 +- docs/src/bootc-bootloaders.7.md | 6 + docs/src/bootc-composefs.7.md | 120 +++++++ docs/src/bootc-experimental-composefs.7.md | 316 ------------------ .../bootc-experimental-unified-storage.7.md | 2 +- docs/src/bootc-installation.7.md | 6 +- docs/src/bootc-sysroot.7.md | 27 ++ docs/src/building/bootc-sealed-images.7.md | 153 +++++++++ 10 files changed, 327 insertions(+), 321 deletions(-) create mode 100644 docs/src/bootc-composefs.7.md delete mode 100644 docs/src/bootc-experimental-composefs.7.md create mode 100644 docs/src/building/bootc-sealed-images.7.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4d8b5e6ea1..6b0a7420c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -206,7 +206,7 @@ just validate-composefs-digest The `build-sealed` target generates test Secure Boot keys in `target/test-secureboot/` and builds a complete sealed image with all the sealed composefs settings. See -[experimental-composefs.md](docs/src/bootc-experimental-composefs.7.md) for +[sealed images](docs/src/building/bootc-sealed-images.7.md) for more information on sealed images. diff --git a/docs/book.toml b/docs/book.toml index 17ad2db3f3..5de3a0ab60 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -20,6 +20,7 @@ additional-js = ["mermaid.min.js", "mermaid-init.js"] # Preserve published URLs when canonical chapters adopt manual filenames. [output.html.redirect] "boot-failure-detection.html" = "bootc-boot-failure-detection.7.html" +"bootc-experimental-composefs.7.html" = "bootc-composefs.7.html" "bootc-images.html" = "bootc-compatible-images.7.html" "bootc-in-container.html" = "bootc-in-container.7.html" "bootc-install.html" = "bootc-installation.7.html" @@ -34,7 +35,17 @@ additional-js = ["mermaid.min.js", "mermaid-init.js"] "building/secrets.html" = "bootc-secrets.7.html" "building/users-and-groups.html" = "bootc-users-and-groups.7.html" "experimental-bootc-image.html" = "bootc-experimental-image.7.html" -"experimental-composefs.html" = "bootc-experimental-composefs.7.html" +"experimental-composefs.html" = "bootc-composefs.7.html" +"experimental-composefs.html#storage-and-repository-structure" = "bootc-sysroot.7.html#composefs-backend-storage" +"experimental-composefs.html#how-sealed-images-work" = "building/bootc-sealed-images.7.html#how-sealed-images-work" +"experimental-composefs.html#building-sealed-images" = "building/bootc-sealed-images.7.html#building-sealed-images" +"experimental-composefs.html#prerequisites" = "building/bootc-sealed-images.7.html#prerequisites" +"experimental-composefs.html#using-without-secure-boot" = "building/bootc-sealed-images.7.html#using-without-secure-boot" +"experimental-composefs.html#the-bootc-container-ukify-command" = "building/bootc-sealed-images.7.html#the-bootc-container-ukify-command" +"experimental-composefs.html#the-bootc-container-compute-composefs-digest-command" = "building/bootc-sealed-images.7.html#the-bootc-container-compute-composefs-digest-command" +"experimental-composefs.html#external-signing-workflow" = "building/bootc-sealed-images.7.html#external-signing-workflow" +"experimental-composefs.html#bootloader-support" = "bootc-bootloaders.7.html#composefs-backend" +"experimental-composefs.html#installation" = "bootc-installation.7.html#composefs-backend" "experimental-container-export.html" = "bootc-experimental-container-export.7.html" "experimental-fsck.html" = "bootc-experimental-fsck.7.html" "experimental-install-reset.html" = "bootc-experimental-install-reset.7.html" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index e6cda5f256..f9fc94b0ff 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -14,6 +14,7 @@ - [Users, groups, SSH keys](building/bootc-users-and-groups.7.md) - [`man bootc-sysusers-shadow-sync.service`](man/bootc-sysusers-shadow-sync.service.5.md) - [Kernel arguments](building/bootc-kernel-arguments.7.md) +- [Sealed images](building/bootc-sealed-images.7.md) - [Secrets](building/bootc-secrets.7.md) - [Management Services](building/bootc-management-services.7.md) @@ -69,6 +70,7 @@ - [Filesystem](bootc-filesystem.7.md) - [Filesystem: sysroot](bootc-sysroot.7.md) - [Container storage](bootc-container-storage.7.md) +- [composefs backend](bootc-composefs.7.md) - [Bootloader](bootc-bootloaders.7.md) - [`man bootc-loader-entries`](man/bootc-loader-entries.8.md) - [`man bootc-loader-entries-set-options-for-source`](man/bootc-loader-entries-set-options-for-source.8.md) @@ -81,7 +83,6 @@ # Experimental features - [bootc image](bootc-experimental-image.7.md) -- [composefs backend](bootc-experimental-composefs.7.md) - [`man bootc-composefs-finalize-staged`](man/bootc-composefs-finalize-staged.8.md) - [unified storage](bootc-experimental-unified-storage.7.md) - [`man bootc-root-setup.service`](man/bootc-root-setup.service.5.md) diff --git a/docs/src/bootc-bootloaders.7.md b/docs/src/bootc-bootloaders.7.md index 6cd22cb17d..9dbb971dc8 100644 --- a/docs/src/bootc-bootloaders.7.md +++ b/docs/src/bootc-bootloaders.7.md @@ -29,3 +29,9 @@ It is possible to skip bootloader installation entirely by using `--bootloader=n With this option, users can have explicit control over how the boot loading is handled, without bootc or bootupd intervention. NOTE: none is only supported for the Ostree backend and not for Composefs. It is also not supported for the s390x architecture. If used with `--generic-image`, it will lead to a generic image that does not have support for any bootloader. + +## composefs backend + +Whenever the container image has a UKI, bootc automatically selects the composefs backend during installation (see [Prerequisites](building/bootc-sealed-images.7.md#prerequisites) for the currently-supported UKI + systemd-boot configuration for building sealed images). Note that having a UKI does not by itself make an install sealed — that also depends on whether fs-verity enforcement is on, per [Overview](bootc-composefs.7.md#overview). + +Composefs installs using a traditional `vmlinuz`/`initramfs.img` layout instead of a UKI can enforce fs-verity, but are never sealed, since nothing authenticates the root digest. They can use either `bootupd` (GRUB) or systemd-boot, the same as the ostree backend. Under the hood, bootc writes standard BLS boot entries for both UKI and traditional kernels; see the [composefs boot module documentation](https://github.com/bootc-dev/bootc/blob/main/crates/lib/src/bootc_composefs/boot.rs) for details on how entry filenames and sort-keys are chosen to sort correctly on both GRUB and systemd-boot. diff --git a/docs/src/bootc-composefs.7.md b/docs/src/bootc-composefs.7.md new file mode 100644 index 0000000000..5df09362aa --- /dev/null +++ b/docs/src/bootc-composefs.7.md @@ -0,0 +1,120 @@ +# composefs backend + +Experimental features are subject to change or removal. Please +do provide feedback on them. + +## Overview + +The composefs backend is an experimental alternative storage backend that uses [composefs-rs](https://github.com/composefs/composefs-rs) instead of ostree for storing and managing bootc system deployments. + +The composefs backend has two independent integrity controls: + +- **fs-verity enforcement.** By default every object in the composefs + repository must have fs-verity enabled, and the root filesystem is only + mounted if its digest matches the one on the kernel command line. Building a + UKI with `--allow-missing-verity` adds a `?` marker to that argument, which + makes fs-verity optional (for filesystems such as XFS that lack it). Both UKI + and traditional kernel/initramfs installs can enforce fs-verity. +- **Boot authentication.** In a *sealed* deployment fs-verity is enforced and + the expected root digest is embedded in a UKI signed for Secure Boot, so + firmware authenticates the digest and the digest authenticates the root + filesystem. A BLS entry or an + unsigned UKI still has fs-verity checked at mount time, but nothing + authenticates the digest itself. + +## EROFS formats + +composefs-rs can encode the EROFS image for a root filesystem in two formats, +which produce different digests for the same content: + +- **V1** is compatible with the C composefs tools and is the default for new + repositories. Its kernel argument is + `composefs.digest=v1-sha512-12:`. +- **V2** is the older composefs-rs format, kept as a fallback. bootc writes + its kernel argument as the bare `composefs=`. + +By default `bootc container ukify` computes both digests and writes the V1 +argument followed by the V2 one. `--erofs-version=v2` writes only the V2 +argument. + +Each argument names one exact image. Staging fails unless every digest in the +UKI matches an image bootc generated for that container image. At boot, +bootc's initramfs tries the arguments in order and moves on to the next one if +an image is missing, but an image that fails fs-verity checks stops the boot. +bootc never substitutes a different digest. + +Existing repositories keep the format configuration recorded in their +metadata; opening one with a newer bootc doesn't convert it. + +### The bare `composefs=` argument + +Released UKIs have used the bare `composefs=` argument for different +formats: + +- bootc 1.16.0 through 1.16.2 predate format versioning and use the original + composefs-rs encoding that V2 descends from. +- bootc 1.16.3 writes a V2 digest. +- bootc 1.16.4 through 1.16.13 write a **V1** digest, because composefs-rs + switched its default while `ukify` kept emitting only the bare argument. +- Releases after 1.16.13 write V2 there again, after an explicit V1 argument. + +So bootc accepts a bare `composefs=` digest that matches either a V1 or a V2 +image, and only enforces the format for the explicit +`composefs.digest=v1-…`/`composefs.digest=v2-…` form. + +### Upgrading from bootc 1.16 + +When you update bootc in an image, **regenerate the initramfs before +generating the UKI**. The initramfs contains bootc's own mount logic, and +keeping an old initramfs with a newer bootc is not supported. + +For a sealed deployment, sign the new UKI with a key the existing machine +trusts. If the deployment was built with `--allow-missing-verity`, keep that +flag. Then publish the image and run `bootc upgrade` as usual. + +What happens next depends on the bootc version doing the staging. A client +that only understands `composefs=`, such as 1.16.0, stages the V2 fallback; +the new initramfs boots it, and the next upgrade (now staged by the new bootc) +moves the system to V1. bootc 1.16.4 and later already understand +`composefs.digest=` and stage V1 directly. + +The 1.16.0 path is covered by the `test-49-composefs-1-16-bridge` TMT test for +both sealed and `--allow-missing-verity` UKIs, including rollback and garbage +collection. Upgrades from other releases, and from BLS (non-UKI) composefs +installs, are not yet tested. + +## Developing and Testing bootc with composefs + +See [CONTRIBUTING.md](https://github.com/bootc-dev/bootc/blob/main/CONTRIBUTING.md) for information on building and testing bootc itself with composefs support. + +## Known issues + +The composefs backend is experimental; on-disk formats are subject to change. + +- Upgrades are tested only from bootc 1.16.0 UKI installs (see + [Upgrading from bootc 1.16](#upgrading-from-bootc-116)), and that test + doesn't yet run in CI. +- Recovery from missing or corrupt images and deployment state is not yet + tested, nor is garbage collection when deployments are referenced by both V1 + and V2 boot entries (for example, that GC keeps a V2 fallback image a + rollback deployment still boots from). +- How container signature enforcement carries over from installation into the + installed system is not settled yet. +- Extended install APIs: Ability to cleanly implement anaconda %post and osbuild post mutations and general post-install pre-reboot; right now some tools just mount the deployment directory (note this one also relates to [APIs in general](https://github.com/bootc-dev/bootc/issues/522)) + +## Related issues + +- [Unified storage](https://github.com/bootc-dev/bootc/issues/20): Not strictly a blocker but a really nice to have +- [Sealed image build UX](https://github.com/bootc-dev/bootc/issues/1498): Streamlined tooling for building sealed images +- In place transitions: + - First: support [factory reset](https://github.com/bootc-dev/bootc/issues/404) from ostree to composefs + - Next: Support copying /etc and /var + +## Additional Resources + +- See [filesystem.md](bootc-filesystem.7.md) for information about composefs in the standard ostree backend +- See [bootloaders.md](bootc-bootloaders.7.md) for bootloader configuration details +- [composefs-rs](https://github.com/composefs/composefs-rs) - The underlying composefs implementation +- [composefs-rs repository format](https://github.com/composefs/composefs-rs/blob/main/crates/composefs/src/repository_format.rs) - Detailed on-disk layout of the `/composefs` repository +- [Unified Kernel Images specification](https://uapi-group.org/specifications/specs/unified_kernel_image/) +- [ukify documentation](https://www.freedesktop.org/software/systemd/man/latest/ukify.html) - Tool for building UKIs diff --git a/docs/src/bootc-experimental-composefs.7.md b/docs/src/bootc-experimental-composefs.7.md deleted file mode 100644 index d99217cb0d..0000000000 --- a/docs/src/bootc-experimental-composefs.7.md +++ /dev/null @@ -1,316 +0,0 @@ -# composefs backend - -Experimental features are subject to change or removal. Please -do provide feedback on them. - -## Overview - -The composefs backend is an experimental alternative storage backend that uses [composefs-rs](https://github.com/composefs/composefs-rs) instead of ostree for storing and managing bootc system deployments. - -The composefs backend has two independent integrity controls: - -- **fs-verity enforcement.** By default every object in the composefs - repository must have fs-verity enabled, and the root filesystem is only - mounted if its digest matches the one on the kernel command line. Building a - UKI with `--allow-missing-verity` adds a `?` marker to that argument, which - makes fs-verity optional (for filesystems such as XFS that lack it). Both UKI - and traditional kernel/initramfs installs can enforce fs-verity. -- **Boot authentication.** In a *sealed* deployment fs-verity is enforced and - the expected root digest is embedded in a UKI signed for Secure Boot, so - firmware authenticates the digest and the digest authenticates the root - filesystem. A BLS entry or an - unsigned UKI still has fs-verity checked at mount time, but nothing - authenticates the digest itself. - -## EROFS formats - -composefs-rs can encode the EROFS image for a root filesystem in two formats, -which produce different digests for the same content: - -- **V1** is compatible with the C composefs tools and is the default for new - repositories. Its kernel argument is - `composefs.digest=v1-sha512-12:`. -- **V2** is the older composefs-rs format, kept as a fallback. bootc writes - its kernel argument as the bare `composefs=`. - -By default `bootc container ukify` computes both digests and writes the V1 -argument followed by the V2 one. `--erofs-version=v2` writes only the V2 -argument. - -Each argument names one exact image. Staging fails unless every digest in the -UKI matches an image bootc generated for that container image. At boot, -bootc's initramfs tries the arguments in order and moves on to the next one if -an image is missing, but an image that fails fs-verity checks stops the boot. -bootc never substitutes a different digest. - -Existing repositories keep the format configuration recorded in their -metadata; opening one with a newer bootc doesn't convert it. - -### The bare `composefs=` argument - -Released UKIs have used the bare `composefs=` argument for different -formats: - -- bootc 1.16.0 through 1.16.2 predate format versioning and use the original - composefs-rs encoding that V2 descends from. -- bootc 1.16.3 writes a V2 digest. -- bootc 1.16.4 through 1.16.13 write a **V1** digest, because composefs-rs - switched its default while `ukify` kept emitting only the bare argument. -- Releases after 1.16.13 write V2 there again, after an explicit V1 argument. - -So bootc accepts a bare `composefs=` digest that matches either a V1 or a V2 -image, and only enforces the format for the explicit -`composefs.digest=v1-…`/`composefs.digest=v2-…` form. - -### Upgrading from bootc 1.16 - -When you update bootc in an image, **regenerate the initramfs before -generating the UKI**. The initramfs contains bootc's own mount logic, and -keeping an old initramfs with a newer bootc is not supported. - -For a sealed deployment, sign the new UKI with a key the existing machine -trusts. If the deployment was built with `--allow-missing-verity`, keep that -flag. Then publish the image and run `bootc upgrade` as usual. - -What happens next depends on the bootc version doing the staging. A client -that only understands `composefs=`, such as 1.16.0, stages the V2 fallback; -the new initramfs boots it, and the next upgrade (now staged by the new bootc) -moves the system to V1. bootc 1.16.4 and later already understand -`composefs.digest=` and stage V1 directly. - -The 1.16.0 path is covered by the `test-49-composefs-1-16-bridge` TMT test for -both sealed and `--allow-missing-verity` UKIs, including rollback and garbage -collection. Upgrades from other releases, and from BLS (non-UKI) composefs -installs, are not yet tested. - -## Storage and repository structure - -Unlike the ostree backend, which keeps its repository at `/ostree/repo`, the composefs backend splits its on-disk state across two top-level directories in the physical sysroot: - -- `/composefs`: The [composefs-rs repository](https://github.com/composefs/composefs-rs/blob/main/crates/composefs/src/repository_format.rs) (mode `0700`), containing: - - `objects/`: content-addressed file storage, keyed by SHA-512 fs-verity digest and shared via reflink (`FICLONE`) where the filesystem supports it - - `images/`: EROFS images describing each deployment's root filesystem metadata, possibly in both [formats](#erofs-formats) - - `streams/`: OCI manifest, config, and layer splitstreams captured during image pulls - - `bootc/storage/`: the `containers-storage:` instance backing logically bound images, reflink-shared with the composefs object store -- `/state/deploy//`: Persistent per-deployment state, one directory per deployment (see below for how it is named): - - `etc/`: a writable copy of the deployment's `/etc`, bind-mounted onto the booted root's `/etc` - - `var`: a symlink to the shared `/state/os/default/var`, bind-mounted onto the booted root's `/var` - - `.origin`: an INI file recording the image reference, boot type (BLS or UKI) and digest, and the OCI manifest digest (the latter is what keeps a deployment's objects alive across garbage collection) - -Although composefs-rs supports other fs-verity hash algorithms, bootc currently hardcodes `SHA-512` for the repository. This is why EROFS image IDs and object identifiers are 128-character hex strings. - -Three kinds of digest show up here and are easy to confuse. The OCI manifest -digest names the pulled container image (see the origin file above). An EROFS -digest names one bootable image under `images/` and is what the kernel command -line refers to; a deployment may have one of each format. The deployment ID names the -state directory; it is the digest of the boot image selected when the -deployment was staged. - -There is no `/ostree/repo`; the composefs backend doesn't use the ostree repository at all. A minimal `/ostree` directory is still created, but only to hold a compatibility symlink (`ostree/bootc -> ../composefs/bootc`) so that existing tooling expecting `/usr/lib/bootc/storage` to resolve through `ostree/bootc` keeps working. - -Transient, not-yet-finalized deployment state (used while staging an update before reboot) lives under `/run/composefs/staged-deployment` and is never persisted to disk. - -## How Sealed Images Work - -A sealed image is a cryptographically signed and verified bootc image that provides end-to-end integrity protection. This is achieved through: - -- **Unified Kernel Images (UKIs)**: Combining kernel, initramfs, and boot parameters into a single signed binary -- **Composefs integration**: Using composefs with fs-verity for content-addressed filesystem verification -- **Secure Boot**: Cryptographic signatures on both the UKI and systemd-boot loader - -A sealed image includes: - -1. **composefs digest**: A SHA-512 hash of the entire root filesystem, computed at build time -2. **Unified Kernel Image (UKI)**: A single EFI binary containing the kernel, initramfs, and kernel command line with the composefs digest embedded -3. **Secure Boot signature**: The UKI is signed with your private key - -At boot time, the composefs digest in the kernel command line (e.g. `composefs.digest=v1-sha512-12:`) is verified against the mounted root filesystem. This creates a chain of trust from firmware to userspace, ensuring the system will only boot if the root filesystem matches exactly what was signed. - -## Building Sealed Images - -### Prerequisites - -For sealed images, the container must: - -- Include a kernel and initramfs in `/usr/lib/modules//` -- Have systemd-boot available (and NOT have `bootupd`) -- Not include a pre-built UKI (the build process generates one) - -Sealed images also require: - -- Secure Boot support in the target system firmware -- A filesystem with fs-verity support (e.g., ext4, btrfs) for the root partition - -#### Using without Secure Boot - -You can use a sealed UKI without Secure Boot enabled. The composefs and mounting -code is fully orthogonal to Secure Boot - the fs-verity digest of the root filesystem -and all of its contents will still be validated at runtime, which does provide -an increased level of integrity. - -However: nothing validates that root digest itself, meaning any locally running -code can replace the UKI (e.g. after a container breakout) and fully control -the next boot. - -It is intentional to support booting with Secure Boot disabled, because a -valid use case is to temporarily disable it in order to test a change locally -on e.g. one machine, then re-enable it later. However at the current time it -is not yet streamlined to regenerate the UKI locally. - -This is independent of `--allow-missing-verity` (see [Overview](#overview)), -which instead makes fs-verity on the root filesystem optional. - -### Build Pattern: Split the Kernel, Then Generate the UKI in a Separate Stage - -Building a sealed image involves three stages: build the rootfs, split the kernel and initramfs out of it, and generate the signed UKI from the split rootfs in a tools stage: - -```dockerfile -# Build your rootfs with all packages and configuration -FROM as rootfs -RUN apt|dnf|zypper install ... && bootc container lint --fatal-warnings - -# Split the kernel and initramfs out of the rootfs. This moves -# /usr/lib/modules//{vmlinuz,initramfs.img} into /kernel//, -# since for a sealed image they end up embedded in the UKI instead. -FROM rootfs as split -RUN mkdir /kernel && bootc container split-kernel-and-rootfs --rootfs / --output /kernel - -# Generate the sealed UKI in a tools stage -FROM as sealed-uki -RUN --mount=type=bind,from=split,target=/target \ - --mount=type=bind,from=split,source=/kernel,target=/kernel \ - --mount=type=secret,id=secureboot_key \ - --mount=type=secret,id=secureboot_cert < [OPTIONS] -- [UKIFY_ARGS...] -``` - -This is the recommended way to build a UKI for a bootc image. It computes the composefs digest of `--rootfs` (using the lower-level `compute-composefs-digest` primitive described below), reads extra kernel arguments from `/usr/lib/bootc/kargs.d`, and invokes the system `ukify` binary with the resulting cmdline. Anything after `--` is forwarded to `ukify` unchanged (e.g. `--output`, `--signtool`, signing key/cert options). - -**Options:** - -- `--rootfs `: Root filesystem to operate on (default: `/`) -- `--kernel-dir `: Directory containing `vmlinuz`/`initramfs.img`, named `/parent/`. Needed when the kernel has already been split out of `--rootfs`, e.g. via `split-kernel-and-rootfs` -- `--allow-missing-verity`: Make fs-verity validation optional, for filesystems that don't support it (e.g. XFS) -- `--erofs-version `: `v1` (the default) writes a V1 argument followed - by a V2 fallback; `v2` writes only V2. See [EROFS formats](#erofs-formats). -- `--write-dumpfile-to `: Write a composefs dumpfile for debugging - -### The `bootc container compute-composefs-digest` Command - -```bash -bootc container compute-composefs-digest [PATH] -``` - -A lower-level primitive, used internally by `ukify` above, that computes just the composefs digest for a filesystem without building a UKI. The digest is a 128-character SHA-512 hex string that uniquely identifies the filesystem contents. Useful for scripting or debugging outside of the UKI build flow. - -**Options:** - -- `PATH`: Path to the filesystem root (default: `/target`) -- `--erofs-version `: EROFS format for the computed digest (default: `v1`) -- `--write-dumpfile-to `: Generate a dumpfile for debugging - -> **Note**: This command is currently hidden from `--help` output as it's part of the experimental composefs feature set. - -### Final Image Structure - -The sealed image should have: - -- The signed UKI at `/boot/EFI/Linux/.efi` -- A signed systemd-boot at `/boot/EFI/BOOT/BOOTX64.EFI` and `/boot/EFI/systemd/systemd-bootx64.efi` -- The raw `vmlinuz` and `initramfs.img` removed from `/usr/lib/modules//` (they're now embedded in the UKI) - -### External Signing Workflow - -For production environments with dedicated signing infrastructure: - -1. **Build unsigned UKI**: Compute digest and create an unsigned UKI (omit `--signtool` from ukify) -2. **Sign externally**: Take the unsigned UKI to your signing infrastructure -3. **Complete the seal**: Inject the signed UKI into the final image - -This workflow is planned for streamlining in future releases (see [#1498](https://github.com/bootc-dev/bootc/issues/1498)). - -## Developing and Testing bootc with composefs - -See [CONTRIBUTING.md](https://github.com/bootc-dev/bootc/blob/main/CONTRIBUTING.md) for information on building and testing bootc itself with composefs support. - -## Bootloader Support - -Whenever the container image has a UKI, bootc automatically selects the composefs backend during installation (see [Prerequisites](#prerequisites) above for the currently-supported UKI + systemd-boot configuration for building sealed images). Note that having a UKI does not by itself make an install sealed — that also depends on whether fs-verity enforcement is on, per [Overview](#overview) above. - -Composefs installs using a traditional `vmlinuz`/`initramfs.img` layout instead of a UKI can enforce fs-verity, but are never sealed, since nothing authenticates the root digest. They can use either `bootupd` (GRUB) or systemd-boot, the same as the ostree backend. See [bootloaders.md](bootc-bootloaders.7.md) for the general bootloader selection rules. Under the hood, bootc writes standard BLS boot entries for both UKI and traditional kernels; see the [composefs boot module documentation](https://github.com/bootc-dev/bootc/blob/main/crates/lib/src/bootc_composefs/boot.rs) for details on how entry filenames and sort-keys are chosen to sort correctly on both GRUB and systemd-boot. - -## Installation - -There is a `--composefs-backend` option for `bootc install` to explicitly select a composefs backend apart from sealed images; this is not as heavily tested yet. - -An image built only for the composefs backend selects it by itself: if it ships -`/usr/lib/composefs/setup-root-conf.toml` (see [bootc-setup-root-conf.toml(5)](man/bootc-setup-root-conf.5.md); -it may be empty) and no ostree `prepare-root.conf` (in `/usr/lib/ostree` or `/etc/ostree`), -`bootc install` uses composefs without the flag. Like the install configuration, these files are -read from the root bootc runs in, also with `--source-imgref`, so this applies to tools like -bootc-image-builder that run bootc from the image they install. An image that ships both is installed with ostree. - -## Known issues - -The composefs backend is experimental; on-disk formats are subject to change. - -- Upgrades are tested only from bootc 1.16.0 UKI installs (see - [Upgrading from bootc 1.16](#upgrading-from-bootc-116)), and that test - doesn't yet run in CI. -- Recovery from missing or corrupt images and deployment state is not yet - tested, nor is garbage collection when deployments are referenced by both V1 - and V2 boot entries (for example, that GC keeps a V2 fallback image a - rollback deployment still boots from). -- How container signature enforcement carries over from installation into the - installed system is not settled yet. -- Extended install APIs: Ability to cleanly implement anaconda %post and osbuild post mutations and general post-install pre-reboot; right now some tools just mount the deployment directory (note this one also relates to [APIs in general](https://github.com/bootc-dev/bootc/issues/522)) - -## Related issues - -- [Unified storage](https://github.com/bootc-dev/bootc/issues/20): Not strictly a blocker but a really nice to have -- [Sealed image build UX](https://github.com/bootc-dev/bootc/issues/1498): Streamlined tooling for building sealed images -- In place transitions: - - First: support [factory reset](https://github.com/bootc-dev/bootc/issues/404) from ostree to composefs - - Next: Support copying /etc and /var - -## Additional Resources - -- See [filesystem.md](bootc-filesystem.7.md) for information about composefs in the standard ostree backend -- See [bootloaders.md](bootc-bootloaders.7.md) for bootloader configuration details -- [composefs-rs](https://github.com/composefs/composefs-rs) - The underlying composefs implementation -- [composefs-rs repository format](https://github.com/composefs/composefs-rs/blob/main/crates/composefs/src/repository_format.rs) - Detailed on-disk layout of the `/composefs` repository -- [Unified Kernel Images specification](https://uapi-group.org/specifications/specs/unified_kernel_image/) -- [ukify documentation](https://www.freedesktop.org/software/systemd/man/latest/ukify.html) - Tool for building UKIs diff --git a/docs/src/bootc-experimental-unified-storage.7.md b/docs/src/bootc-experimental-unified-storage.7.md index c9f0608453..4f0e955886 100644 --- a/docs/src/bootc-experimental-unified-storage.7.md +++ b/docs/src/bootc-experimental-unified-storage.7.md @@ -133,7 +133,7 @@ podman --storage-opt=additionalimagestore=/usr/lib/bootc/storage run localhost/b ## Relationship to composefs backend -Unified storage is complementary to the [composefs backend](bootc-experimental-composefs.7.md). +Unified storage is complementary to the [composefs backend](bootc-composefs.7.md). While unified storage changes *how images are pulled* (using containers/storage), the composefs backend changes *how the filesystem is stored and verified*. diff --git a/docs/src/bootc-installation.7.md b/docs/src/bootc-installation.7.md index dbd576da34..2d07e096f1 100644 --- a/docs/src/bootc-installation.7.md +++ b/docs/src/bootc-installation.7.md @@ -121,7 +121,7 @@ merge precedence, and the available configuration fields. ### The storage backend The storage backend is determined by the image. It is installed with the -[experimental composefs backend](bootc-experimental-composefs.7.md) when it +[experimental composefs backend](bootc-composefs.7.md) when it ships a UKI, or when it matches both of these rules: - it ships `/usr/lib/composefs/setup-root-conf.toml` (which may be empty; see @@ -143,6 +143,10 @@ unconfigured image does not have a default password or SSH key, etc. For more information, see [Image building and configuration guidance](building/bootc-building-images.7.md). +## composefs backend + +There is a `--composefs-backend` option for `bootc install` to explicitly select a composefs backend apart from sealed images; this is not as heavily tested yet. + ## More advanced installation with `to-filesystem` The basic `bootc install to-disk` logic is really a pretty small (but opinionated) wrapper diff --git a/docs/src/bootc-sysroot.7.md b/docs/src/bootc-sysroot.7.md index 75acc754f1..334500d155 100644 --- a/docs/src/bootc-sysroot.7.md +++ b/docs/src/bootc-sysroot.7.md @@ -76,3 +76,30 @@ is recommended, along with an `ExecStartPre=mount -o remount,rw /sysroot`. ### Detecting bootc/ostree systems See the [package managers](bootc-package-managers.7.md) section on "Detecting image based systems". + +## composefs backend storage + +Unlike the ostree backend, which keeps its repository at `/ostree/repo`, the composefs backend splits its on-disk state across two top-level directories in the physical sysroot: + +- `/composefs`: The [composefs-rs repository](https://github.com/composefs/composefs-rs/blob/main/crates/composefs/src/repository_format.rs) (mode `0700`), containing: + - `objects/`: content-addressed file storage, keyed by SHA-512 fs-verity digest and shared via reflink (`FICLONE`) where the filesystem supports it + - `images/`: EROFS images describing each deployment's root filesystem metadata, possibly in both [formats](bootc-composefs.7.md#erofs-formats) + - `streams/`: OCI manifest, config, and layer splitstreams captured during image pulls + - `bootc/storage/`: the `containers-storage:` instance backing logically bound images, reflink-shared with the composefs object store +- `/state/deploy//`: Persistent per-deployment state, one directory per deployment (see below for how it is named): + - `etc/`: a writable copy of the deployment's `/etc`, bind-mounted onto the booted root's `/etc` + - `var`: a symlink to the shared `/state/os/default/var`, bind-mounted onto the booted root's `/var` + - `.origin`: an INI file recording the image reference, boot type (BLS or UKI) and digest, and the OCI manifest digest (the latter is what keeps a deployment's objects alive across garbage collection) + +Although composefs-rs supports other fs-verity hash algorithms, bootc currently hardcodes `SHA-512` for the repository. This is why EROFS image IDs and object identifiers are 128-character hex strings. + +Three kinds of digest show up here and are easy to confuse. The OCI manifest +digest names the pulled container image (see the origin file above). An EROFS +digest names one bootable image under `images/` and is what the kernel command +line refers to; a deployment may have one of each format. The deployment ID names the +state directory; it is the digest of the boot image selected when the +deployment was staged. + +There is no `/ostree/repo`; the composefs backend doesn't use the ostree repository at all. A minimal `/ostree` directory is still created, but only to hold a compatibility symlink (`ostree/bootc -> ../composefs/bootc`) so that existing tooling expecting `/usr/lib/bootc/storage` to resolve through `ostree/bootc` keeps working. + +Transient, not-yet-finalized deployment state (used while staging an update before reboot) lives under `/run/composefs/staged-deployment` and is never persisted to disk. diff --git a/docs/src/building/bootc-sealed-images.7.md b/docs/src/building/bootc-sealed-images.7.md new file mode 100644 index 0000000000..c9c0ea52ac --- /dev/null +++ b/docs/src/building/bootc-sealed-images.7.md @@ -0,0 +1,153 @@ +# Sealed images + +## How Sealed Images Work + +A sealed image is a cryptographically signed and verified bootc image that provides end-to-end integrity protection. This is achieved through: + +- **Unified Kernel Images (UKIs)**: Combining kernel, initramfs, and boot parameters into a single signed binary +- **Composefs integration**: Using composefs with fs-verity for content-addressed filesystem verification +- **Secure Boot**: Cryptographic signatures on both the UKI and systemd-boot loader + +A sealed image includes: + +1. **composefs digest**: A SHA-512 hash of the entire root filesystem, computed at build time +2. **Unified Kernel Image (UKI)**: A single EFI binary containing the kernel, initramfs, and kernel command line with the composefs digest embedded +3. **Secure Boot signature**: The UKI is signed with your private key + +At boot time, the composefs digest in the kernel command line (e.g. `composefs.digest=v1-sha512-12:`) is verified against the mounted root filesystem. This creates a chain of trust from firmware to userspace, ensuring the system will only boot if the root filesystem matches exactly what was signed. + +## Building Sealed Images + +### Prerequisites + +For sealed images, the container must: + +- Include a kernel and initramfs in `/usr/lib/modules//` +- Have systemd-boot available (and NOT have `bootupd`) +- Not include a pre-built UKI (the build process generates one) + +Sealed images also require: + +- Secure Boot support in the target system firmware +- A filesystem with fs-verity support (e.g., ext4, btrfs) for the root partition + +#### Using without Secure Boot + +You can use a sealed UKI without Secure Boot enabled. The composefs and mounting +code is fully orthogonal to Secure Boot - the fs-verity digest of the root filesystem +and all of its contents will still be validated at runtime, which does provide +an increased level of integrity. + +However: nothing validates that root digest itself, meaning any locally running +code can replace the UKI (e.g. after a container breakout) and fully control +the next boot. + +It is intentional to support booting with Secure Boot disabled, because a +valid use case is to temporarily disable it in order to test a change locally +on e.g. one machine, then re-enable it later. However at the current time it +is not yet streamlined to regenerate the UKI locally. + +This is independent of `--allow-missing-verity` (see [Overview](../bootc-composefs.7.md#overview)), +which instead makes fs-verity on the root filesystem optional. + +### Build Pattern: Split the Kernel, Then Generate the UKI in a Separate Stage + +Building a sealed image involves three stages: build the rootfs, split the kernel and initramfs out of it, and generate the signed UKI from the split rootfs in a tools stage: + +```dockerfile +# Build your rootfs with all packages and configuration +FROM as rootfs +RUN apt|dnf|zypper install ... && bootc container lint --fatal-warnings + +# Split the kernel and initramfs out of the rootfs. This moves +# /usr/lib/modules//{vmlinuz,initramfs.img} into /kernel//, +# since for a sealed image they end up embedded in the UKI instead. +FROM rootfs as split +RUN mkdir /kernel && bootc container split-kernel-and-rootfs --rootfs / --output /kernel + +# Generate the sealed UKI in a tools stage +FROM as sealed-uki +RUN --mount=type=bind,from=split,target=/target \ + --mount=type=bind,from=split,source=/kernel,target=/kernel \ + --mount=type=secret,id=secureboot_key \ + --mount=type=secret,id=secureboot_cert < [OPTIONS] -- [UKIFY_ARGS...] +``` + +This is the recommended way to build a UKI for a bootc image. It computes the composefs digest of `--rootfs` (using the lower-level `compute-composefs-digest` primitive described below), reads extra kernel arguments from `/usr/lib/bootc/kargs.d`, and invokes the system `ukify` binary with the resulting cmdline. Anything after `--` is forwarded to `ukify` unchanged (e.g. `--output`, `--signtool`, signing key/cert options). + +**Options:** + +- `--rootfs `: Root filesystem to operate on (default: `/`) +- `--kernel-dir `: Directory containing `vmlinuz`/`initramfs.img`, named `/parent/`. Needed when the kernel has already been split out of `--rootfs`, e.g. via `split-kernel-and-rootfs` +- `--allow-missing-verity`: Make fs-verity validation optional, for filesystems that don't support it (e.g. XFS) +- `--erofs-version `: `v1` (the default) writes a V1 argument followed + by a V2 fallback; `v2` writes only V2. See [EROFS formats](../bootc-composefs.7.md#erofs-formats). +- `--write-dumpfile-to `: Write a composefs dumpfile for debugging + +### The `bootc container compute-composefs-digest` Command + +```bash +bootc container compute-composefs-digest [PATH] +``` + +A lower-level primitive, used internally by `ukify` above, that computes just the composefs digest for a filesystem without building a UKI. The digest is a 128-character SHA-512 hex string that uniquely identifies the filesystem contents. Useful for scripting or debugging outside of the UKI build flow. + +**Options:** + +- `PATH`: Path to the filesystem root (default: `/target`) +- `--erofs-version `: EROFS format for the computed digest (default: `v1`) +- `--write-dumpfile-to `: Generate a dumpfile for debugging + +> **Note**: This command is currently hidden from `--help` output as it's part of the experimental composefs feature set. + +### Final Image Structure + +The sealed image should have: + +- The signed UKI at `/boot/EFI/Linux/.efi` +- A signed systemd-boot at `/boot/EFI/BOOT/BOOTX64.EFI` and `/boot/EFI/systemd/systemd-bootx64.efi` +- The raw `vmlinuz` and `initramfs.img` removed from `/usr/lib/modules//` (they're now embedded in the UKI) + +### External Signing Workflow + +For production environments with dedicated signing infrastructure: + +1. **Build unsigned UKI**: Compute digest and create an unsigned UKI (omit `--signtool` from ukify) +2. **Sign externally**: Take the unsigned UKI to your signing infrastructure +3. **Complete the seal**: Inject the signed UKI into the final image + +This workflow is planned for streamlining in future releases (see [#1498](https://github.com/bootc-dev/bootc/issues/1498)). From 047c461e3a8db7c3f56e053b164f9ee42e3d59b4 Mon Sep 17 00:00:00 2001 From: cgwalters-bot Date: Thu, 24 Sep 2026 14:22:41 -0400 Subject: [PATCH 2/3] cli: Hide composefs-finalize-staged This is an internal entry point run by bootc-finalize-staged.service and bootc-finalize-staged-hold.service, not something users should invoke, so it shouldn't be listed in `bootc --help` or bootc(8) once the composefs backend is declared stable. Its man page stays, since it documents what the services do. Hidden commands are left out of the CLI JSON that the man page generator reads, so the page's OPTIONS section is now maintained by hand rather than between the generated markers. This takes just the CLI change from #2168 by cgwalters, which also documents the service; that part can still land from there. Generated-by: AI Signed-off-by: Colin Walters --- crates/lib/src/cli.rs | 5 ++--- docs/src/man/bootc-composefs-finalize-staged.8.md | 3 --- docs/src/man/bootc.8.md | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs index 578a74adb9..f49d37233c 100644 --- a/crates/lib/src/cli.rs +++ b/crates/lib/src/cli.rs @@ -1088,6 +1088,7 @@ pub(crate) enum Opt { #[clap(subcommand)] #[clap(hide = true)] Internals(InternalsOpts), + #[clap(hide = true)] ComposefsFinalizeStaged(ComposefsFinalizeStagedOpts), /// Diff current /etc configuration versus default #[clap(hide = true)] @@ -1102,9 +1103,7 @@ pub(crate) enum Opt { shell: clap_complete::aot::Shell, }, #[clap(hide = true)] - DeleteDeployment { - depl_id: String, - }, + DeleteDeployment { depl_id: String }, } /// Ensure we've entered a mount namespace, so that we can remount diff --git a/docs/src/man/bootc-composefs-finalize-staged.8.md b/docs/src/man/bootc-composefs-finalize-staged.8.md index a8b054ab35..1736e673fc 100644 --- a/docs/src/man/bootc-composefs-finalize-staged.8.md +++ b/docs/src/man/bootc-composefs-finalize-staged.8.md @@ -30,13 +30,10 @@ runs. # OPTIONS - **--hold** Hold /boot open until terminated, instead of finalizing - - # EXAMPLES Check whether a staged deployment is waiting to be finalized at the diff --git a/docs/src/man/bootc.8.md b/docs/src/man/bootc.8.md index e564852fb3..c741f8f758 100644 --- a/docs/src/man/bootc.8.md +++ b/docs/src/man/bootc.8.md @@ -37,7 +37,6 @@ For guides to building, installing, and managing bootable images, see | **bootc install** | Install the running container to a target | | **bootc container** | Operations which can be executed as part of a container build | | **bootc loader-entries** | Operations on Boot Loader Specification (BLS) entries | -| **bootc composefs-finalize-staged** | Finalize a staged composefs deployment | From 4831ab56b71327666e44b044d753e93efcf8f095 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 24 Sep 2026 12:08:27 -0400 Subject: [PATCH 3/3] composefs: Declare the backend stable Super excited to finally do this! In practice we've been aiming to support upgrades from existing systems that were installed when the backend was experimental, but it's time to just declare the on-disk setup stable. Assisted-by: AI Signed-off-by: Colin Walters --- CONTRIBUTING.md | 4 +- crates/lib/src/cli.rs | 7 +- crates/lib/src/lib.rs | 2 +- crates/lib/src/spec.rs | 2 +- docs/src/SUMMARY.md | 7 +- docs/src/bootc-bootloaders.7.md | 4 +- docs/src/bootc-compatible-images.7.md | 2 +- docs/src/bootc-composefs.7.md | 128 ++++++++++++------ docs/src/bootc-installation.7.md | 16 ++- docs/src/bootc-internals.7.md | 2 +- docs/src/bootc-overview.7.md | 5 +- docs/src/bootc-sysroot.7.md | 3 +- docs/src/bootc-upgrades.7.md | 2 +- docs/src/building/bootc-sealed-images.7.md | 4 +- docs/src/host-v1.schema.json | 2 +- ...tc-container-compute-composefs-digest.8.md | 62 +++++++++ docs/src/man/bootc-container.8.md | 1 + docs/src/man/bootc-setup-root-conf.5.md | 3 - 18 files changed, 192 insertions(+), 64 deletions(-) create mode 100644 docs/src/man/bootc-container-compute-composefs-digest.8.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6b0a7420c8..298b76cf4d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -144,8 +144,8 @@ etc. ### Building and testing with the composefs backend -bootc has two storage backends: `ostree` (default, production) and `composefs` -(experimental). The composefs backend has several axes of configuration: +bootc has two storage backends: `ostree` and `composefs`. +The composefs backend has several axes of configuration: | Variable | Values | Notes | |---|---|---| diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs index f49d37233c..0a7c0e5987 100644 --- a/crates/lib/src/cli.rs +++ b/crates/lib/src/cli.rs @@ -430,7 +430,10 @@ pub(crate) enum ContainerOpts { no_truncate: bool, }, /// Output the bootable composefs digest for a directory. - #[clap(hide = true)] + /// + /// This is the digest that `bootc container ukify` embeds in the kernel + /// command line of a UKI. It is useful for scripting and debugging outside + /// of that flow. ComputeComposefsDigest { /// Path to the filesystem root #[clap(default_value = "/target")] @@ -473,7 +476,7 @@ pub(crate) enum ContainerOpts { /// and RHEL derivatives ship. /// /// Example: - /// bootc container split-kernel-rootfs --rootfs /target-rootfs --output /out + /// bootc container split-kernel-and-rootfs --rootfs /target-rootfs --output /out SplitKernelAndRootfs { /// Operate on the provided rootfs #[clap(long, default_value = "/")] diff --git a/crates/lib/src/lib.rs b/crates/lib/src/lib.rs index 09a646c756..edd50e347b 100644 --- a/crates/lib/src/lib.rs +++ b/crates/lib/src/lib.rs @@ -39,7 +39,7 @@ //! //! ## Storage Backends //! -//! - [`bootc_composefs`] - Composefs backend implementation (experimental) +//! - [`bootc_composefs`] - Composefs backend implementation //! - The OSTree backend is implemented via `ostree-ext` and the [`store`] module //! //! ## Filesystem and Boot diff --git a/crates/lib/src/spec.rs b/crates/lib/src/spec.rs index 15ad1014fe..b58ed0ef18 100644 --- a/crates/lib/src/spec.rs +++ b/crates/lib/src/spec.rs @@ -237,7 +237,7 @@ pub enum Bootloader { /// Use Grub as the bootloader #[default] Grub, - /// Use Grub for confidential clusters as the bootloader + /// Use Grub for confidential clusters as the bootloader (experimental) #[serde(rename = "grub-cc")] GrubCC, /// Use SystemdBoot as the bootloader diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index f9fc94b0ff..fe4d0ebfa2 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -62,6 +62,7 @@ - [`man bootc-container-inspect`](man/bootc-container-inspect.8.md) - [`man bootc-container-split-kernel-and-rootfs`](man/bootc-container-split-kernel-and-rootfs.8.md) - [`man bootc-container-ukify`](man/bootc-container-ukify.8.md) +- [`man bootc-container-compute-composefs-digest`](man/bootc-container-compute-composefs-digest.8.md) - [`man bootc-container-lint`](man/bootc-container-lint.8.md) # Architecture @@ -71,6 +72,9 @@ - [Filesystem: sysroot](bootc-sysroot.7.md) - [Container storage](bootc-container-storage.7.md) - [composefs backend](bootc-composefs.7.md) +- [`man bootc-root-setup.service`](man/bootc-root-setup.service.5.md) +- [`man bootc-setup-root-conf.toml`](man/bootc-setup-root-conf.5.md) +- [`man bootc-composefs-finalize-staged`](man/bootc-composefs-finalize-staged.8.md) - [Bootloader](bootc-bootloaders.7.md) - [`man bootc-loader-entries`](man/bootc-loader-entries.8.md) - [`man bootc-loader-entries-set-options-for-source`](man/bootc-loader-entries-set-options-for-source.8.md) @@ -83,10 +87,7 @@ # Experimental features - [bootc image](bootc-experimental-image.7.md) -- [`man bootc-composefs-finalize-staged`](man/bootc-composefs-finalize-staged.8.md) - [unified storage](bootc-experimental-unified-storage.7.md) -- [`man bootc-root-setup.service`](man/bootc-root-setup.service.5.md) -- [`man bootc-setup-root-conf.toml`](man/bootc-setup-root-conf.5.md) - [fsck](bootc-experimental-fsck.7.md) - [install reset](bootc-experimental-install-reset.7.md) - [--progress-fd](bootc-experimental-progress-fd.7.md) diff --git a/docs/src/bootc-bootloaders.7.md b/docs/src/bootc-bootloaders.7.md index 9dbb971dc8..6f88aec2fb 100644 --- a/docs/src/bootc-bootloaders.7.md +++ b/docs/src/bootc-bootloaders.7.md @@ -32,6 +32,6 @@ NOTE: none is only supported for the Ostree backend and not for Composefs. It is ## composefs backend -Whenever the container image has a UKI, bootc automatically selects the composefs backend during installation (see [Prerequisites](building/bootc-sealed-images.7.md#prerequisites) for the currently-supported UKI + systemd-boot configuration for building sealed images). Note that having a UKI does not by itself make an install sealed — that also depends on whether fs-verity enforcement is on, per [Overview](bootc-composefs.7.md#overview). +Whenever the container image has a UKI, bootc automatically selects the composefs backend during installation. The [prerequisites for sealed images](building/bootc-sealed-images.7.md#prerequisites) describe the currently-supported UKI + systemd-boot configuration. Note that having a UKI does not by itself make an install sealed — that also depends on whether [fs-verity enforcement](bootc-composefs.7.md#overview) is on. -Composefs installs using a traditional `vmlinuz`/`initramfs.img` layout instead of a UKI can enforce fs-verity, but are never sealed, since nothing authenticates the root digest. They can use either `bootupd` (GRUB) or systemd-boot, the same as the ostree backend. Under the hood, bootc writes standard BLS boot entries for both UKI and traditional kernels; see the [composefs boot module documentation](https://github.com/bootc-dev/bootc/blob/main/crates/lib/src/bootc_composefs/boot.rs) for details on how entry filenames and sort-keys are chosen to sort correctly on both GRUB and systemd-boot. +Composefs installs using a traditional `vmlinuz`/`initramfs.img` layout instead of a UKI can enforce fs-verity, but are never sealed, since nothing authenticates the root digest. They can use either `bootupd` (GRUB) or systemd-boot. Under the hood, bootc writes standard BLS boot entries for both UKI and traditional kernels; see the [composefs boot module documentation](https://github.com/bootc-dev/bootc/blob/main/crates/lib/src/bootc_composefs/boot.rs) for details on how entry filenames and sort-keys are chosen to sort correctly on both GRUB and systemd-boot. diff --git a/docs/src/bootc-compatible-images.7.md b/docs/src/bootc-compatible-images.7.md index 64ebf04165..cd197d713c 100644 --- a/docs/src/bootc-compatible-images.7.md +++ b/docs/src/bootc-compatible-images.7.md @@ -23,7 +23,7 @@ The Linux kernel (and optionally initramfs) is embedded in the container image; ### Kernel (sealed UKI) -For the composefs backend, the UKI must be located at `/boot/EFI/Linux/$kver.efi`. +For the composefs backend, the UKI must be located at `/boot/EFI/Linux/$kver.efi`. See [sealed images](building/bootc-sealed-images.7.md). ### /ostree symlink and `bootc container lint` diff --git a/docs/src/bootc-composefs.7.md b/docs/src/bootc-composefs.7.md index 5df09362aa..d670580ae8 100644 --- a/docs/src/bootc-composefs.7.md +++ b/docs/src/bootc-composefs.7.md @@ -1,12 +1,19 @@ # composefs backend -Experimental features are subject to change or removal. Please -do provide feedback on them. +bootc has two storage backends. The default is [ostree](https://github.com/ostreedev/ostree), +and the composefs backend uses [composefs-rs](https://github.com/composefs/composefs-rs) +instead of ostree to store and manage deployments. Both are supported and +covered by the project's [stability guarantees](https://github.com/bootc-dev/bootc/blob/main/RELEASES.md#stability-guarantees). +In particular, the project is committed to upgrading every composefs system +installed since bootc 1.16.0 in place. + +The composefs backend is required for [sealed images](building/bootc-sealed-images.7.md), +and the image determines which backend `bootc install` uses; see +[Understanding `bootc install`](bootc-installation.7.md#composefs-backend). Its +on-disk layout is described in [Filesystem: sysroot](bootc-sysroot.7.md#composefs-backend-storage). ## Overview -The composefs backend is an experimental alternative storage backend that uses [composefs-rs](https://github.com/composefs/composefs-rs) instead of ostree for storing and managing bootc system deployments. - The composefs backend has two independent integrity controls: - **fs-verity enforcement.** By default every object in the composefs @@ -28,13 +35,17 @@ composefs-rs can encode the EROFS image for a root filesystem in two formats, which produce different digests for the same content: - **V1** is compatible with the C composefs tools and is the default for new - repositories. Its kernel argument is - `composefs.digest=v1-sha512-12:`. -- **V2** is the older composefs-rs format, kept as a fallback. bootc writes - its kernel argument as the bare `composefs=`. + repositories. +- **V2** is the older composefs-rs format, kept as a fallback. + +The `composefs.digest=` kernel argument names the format along with the +digest, for example `composefs.digest=v1-sha512-12:`, so it is +unambiguous. The older bare `composefs=` argument is not: released +UKIs carry either a V1 or a V2 digest there (see below), so bootc tries both. By default `bootc container ukify` computes both digests and writes the V1 -argument followed by the V2 one. `--erofs-version=v2` writes only the V2 +`composefs.digest=` argument followed by a bare `composefs=` argument with the +V2 digest, for older clients. `--erofs-version=v2` writes only the bare V2 argument. Each argument names one exact image. Staging fails unless every digest in the @@ -54,13 +65,12 @@ formats: - bootc 1.16.0 through 1.16.2 predate format versioning and use the original composefs-rs encoding that V2 descends from. - bootc 1.16.3 writes a V2 digest. -- bootc 1.16.4 through 1.16.13 write a **V1** digest, because composefs-rs - switched its default while `ukify` kept emitting only the bare argument. +- bootc 1.16.4 through 1.16.13 write a **V1** digest: the switch to the V1 + format shipped before the `composefs.digest=` argument did. - Releases after 1.16.13 write V2 there again, after an explicit V1 argument. So bootc accepts a bare `composefs=` digest that matches either a V1 or a V2 -image, and only enforces the format for the explicit -`composefs.digest=v1-…`/`composefs.digest=v2-…` form. +image, and only enforces the format for `composefs.digest=`. ### Upgrading from bootc 1.16 @@ -80,33 +90,72 @@ moves the system to V1. bootc 1.16.4 and later already understand The 1.16.0 path is covered by the `test-49-composefs-1-16-bridge` TMT test for both sealed and `--allow-missing-verity` UKIs, including rollback and garbage -collection. Upgrades from other releases, and from BLS (non-UKI) composefs -installs, are not yet tested. - -## Developing and Testing bootc with composefs - -See [CONTRIBUTING.md](https://github.com/bootc-dev/bootc/blob/main/CONTRIBUTING.md) for information on building and testing bootc itself with composefs support. - -## Known issues - -The composefs backend is experimental; on-disk formats are subject to change. - -- Upgrades are tested only from bootc 1.16.0 UKI installs (see - [Upgrading from bootc 1.16](#upgrading-from-bootc-116)), and that test - doesn't yet run in CI. -- Recovery from missing or corrupt images and deployment state is not yet - tested, nor is garbage collection when deployments are referenced by both V1 - and V2 boot entries (for example, that GC keeps a V2 fallback image a - rollback deployment still boots from). -- How container signature enforcement carries over from installation into the - installed system is not settled yet. -- Extended install APIs: Ability to cleanly implement anaconda %post and osbuild post mutations and general post-install pre-reboot; right now some tools just mount the deployment directory (note this one also relates to [APIs in general](https://github.com/bootc-dev/bootc/issues/522)) - -## Related issues - -- [Unified storage](https://github.com/bootc-dev/bootc/issues/20): Not strictly a blocker but a really nice to have +collection. Upgrades of UKI installs from other releases are not yet tested. + +## Supported configurations + +The following are supported with the composefs backend: + +- `bootc install`, `upgrade`, `switch`, `rollback`, `status`, `usr-overlay` + and soft reboots. +- [`bootc install mount`](man/bootc-install-mount.8.md), for changing an + installed deployment before its first boot. +- Traditional kernel and initramfs installs booted through BLS entries, with + either GRUB (via `bootupd`) or systemd-boot, and UKIs booted with + systemd-boot, including sealed UKIs signed for Secure Boot. See + [Bootloaders](bootc-bootloaders.7.md#composefs-backend). +- Root filesystems with fs-verity support, and filesystems without it (such + as XFS) with fs-verity made optional. Sealed images require fs-verity. + CI covers ext4 and XFS; btrfs is expected to work but is not tested. +- The [EROFS formats](#erofs-formats) and kernel arguments described above. +- The image build commands `bootc container ukify`, + `bootc container split-kernel-and-rootfs` and + `bootc container compute-composefs-digest`, and the initramfs setup + configured by [`setup-root-conf.toml`](man/bootc-setup-root-conf.5.md). + +On CentOS Stream 9, only sealed UKIs are tested; traditional kernel installs +require newer dracut and systemd features. Its dracut also doesn't install +`setup-root-conf.toml` into the initramfs automatically. + +## Experimental parts + +These remain [experimental](https://github.com/bootc-dev/bootc/blob/main/RELEASES.md#stability-guarantees) +and may change or be removed: + +- The `grub-cc` bootloader (`--bootloader=grub-cc`). +- UKI addons (`--uki-addon`). Addons are only installed by `bootc install`: + they aren't updated on upgrade, garbage collected, or reverted on + rollback. +- [Unified storage](bootc-experimental-unified-storage.7.md). + +## Limitations + +- `bootc edit` and [`bootc install reset`](bootc-experimental-install-reset.7.md) + are not yet implemented for the composefs backend. +- There is no `bootc-boot-complete.service` and no boot counting; see + [boot failure detection](bootc-boot-failure-detection.7.md#composefs-backend-boot-failure-detection). +- There is no in-place transition from an ostree system to the composefs + backend yet, so for now a system has to be reinstalled. We fully intend to + support moving to composefs without a reinstall; see + [Future work](#future-work). +- `--bootloader=none` is not supported. +- `--soft-reboot=auto` doesn't fall back to a regular reboot when the new + deployment can't be soft rebooted into; see + [Soft reboots](bootc-upgrades.7.md#soft-reboots). +- Only a single ESP is used (the first one found), and a separate + XBOOTLDR partition is not supported. +- Rollback with GRUB and UKIs assumes exactly two deployments. +- `bootc internals fsck` doesn't yet comprehensively cover the composefs + backend; see [#2497](https://github.com/bootc-dev/bootc/pull/2497). + +For building and testing bootc itself with the composefs backend, see +[CONTRIBUTING.md](https://github.com/bootc-dev/bootc/blob/main/CONTRIBUTING.md). + +## Future work + +- [Unified storage](https://github.com/bootc-dev/bootc/issues/20) - [Sealed image build UX](https://github.com/bootc-dev/bootc/issues/1498): Streamlined tooling for building sealed images -- In place transitions: +- In place transitions: - First: support [factory reset](https://github.com/bootc-dev/bootc/issues/404) from ostree to composefs - Next: Support copying /etc and /var @@ -114,6 +163,7 @@ The composefs backend is experimental; on-disk formats are subject to change. - See [filesystem.md](bootc-filesystem.7.md) for information about composefs in the standard ostree backend - See [bootloaders.md](bootc-bootloaders.7.md) for bootloader configuration details +- See [sealed images](building/bootc-sealed-images.7.md) for building UKIs and sealed images - [composefs-rs](https://github.com/composefs/composefs-rs) - The underlying composefs implementation - [composefs-rs repository format](https://github.com/composefs/composefs-rs/blob/main/crates/composefs/src/repository_format.rs) - Detailed on-disk layout of the `/composefs` repository - [Unified Kernel Images specification](https://uapi-group.org/specifications/specs/unified_kernel_image/) diff --git a/docs/src/bootc-installation.7.md b/docs/src/bootc-installation.7.md index 2d07e096f1..22f086adda 100644 --- a/docs/src/bootc-installation.7.md +++ b/docs/src/bootc-installation.7.md @@ -121,7 +121,7 @@ merge precedence, and the available configuration fields. ### The storage backend The storage backend is determined by the image. It is installed with the -[experimental composefs backend](bootc-composefs.7.md) when it +[composefs backend](bootc-composefs.7.md) when it ships a UKI, or when it matches both of these rules: - it ships `/usr/lib/composefs/setup-root-conf.toml` (which may be empty; see @@ -145,7 +145,19 @@ For more information, see [Image building and configuration guidance](building/b ## composefs backend -There is a `--composefs-backend` option for `bootc install` to explicitly select a composefs backend apart from sealed images; this is not as heavily tested yet. +The [storage backend](#the-storage-backend) is selected by the image; see +[composefs backend](bootc-composefs.7.md) and [sealed images](building/bootc-sealed-images.7.md). + +With a traditional kernel and initramfs, the initramfs of a composefs image +must also include bootc's dracut module (`51bootc`), which mounts the composefs +root. That module is not enabled by default: the reference +[baseimage](https://github.com/bootc-dev/bootc/tree/main/baseimage) configuration +enables it, and other base images need to as well; see +[bootc-root-setup.service(5)](man/bootc-root-setup.service.5.md). + +On a root filesystem without fs-verity support (such as XFS), fs-verity is +made optional automatically for a traditional kernel install; +`--allow-missing-verity` does this explicitly. ## More advanced installation with `to-filesystem` diff --git a/docs/src/bootc-internals.7.md b/docs/src/bootc-internals.7.md index f3ed61e46c..5241342957 100644 --- a/docs/src/bootc-internals.7.md +++ b/docs/src/bootc-internals.7.md @@ -42,7 +42,7 @@ Key paths: - `/sysroot/ostree/repo/` - OSTree repository - `/sysroot/ostree/deploy//` - Deployment directories -### Composefs Backend (experimental) +### Composefs Backend Uses [composefs-rs](https://github.com/containers/composefs-rs) directly, enabling native UKI support and sealed images with fsverity integrity. diff --git a/docs/src/bootc-overview.7.md b/docs/src/bootc-overview.7.md index bdbd2df53e..9f6435eeb1 100644 --- a/docs/src/bootc-overview.7.md +++ b/docs/src/bootc-overview.7.md @@ -19,7 +19,8 @@ systemd is in use, systemd acts as pid1 as usual - there's no "outer" process. The CLI and API for bootc are now considered stable. Every existing system can be upgraded in place seamlessly across any future changes. -However, the core underlying code uses the [ostree](https://github.com/ostreedev/ostree) +However, the default storage backend uses the [ostree](https://github.com/ostreedev/ostree) project which has been powering stable operating system updates for many years. The stability here generally refers to the surface -APIs, not the underlying logic. +APIs, not the underlying logic. There is also a [composefs backend](bootc-composefs.7.md), +which is required for sealed images. diff --git a/docs/src/bootc-sysroot.7.md b/docs/src/bootc-sysroot.7.md index 334500d155..9e3310c112 100644 --- a/docs/src/bootc-sysroot.7.md +++ b/docs/src/bootc-sysroot.7.md @@ -1,7 +1,8 @@ # Filesystem: Physical /sysroot -The bootc project uses [ostree](https://github.com/ostreedev/ostree/) as a backend, +By default, bootc uses [ostree](https://github.com/ostreedev/ostree/) as a backend, and maps fetched container images to a [deployment](https://ostreedev.github.io/ostree/deployment/). +The layout of the composefs backend is described [below](#composefs-backend-storage). ## stateroot diff --git a/docs/src/bootc-upgrades.7.md b/docs/src/bootc-upgrades.7.md index 48eabce0d9..de6f8f18f1 100644 --- a/docs/src/bootc-upgrades.7.md +++ b/docs/src/bootc-upgrades.7.md @@ -163,7 +163,7 @@ Without `--apply`, `--soft-reboot` prepares the deployment but does not restart the system immediately. Without `--soft-reboot`, `--apply` requests a regular reboot. -The [experimental composefs backend](bootc-experimental-composefs.7.md) currently +The [composefs backend](bootc-composefs.7.md) currently differs: both modes fail if systemd lacks soft-reboot support. If the target deployment is not soft-reboot capable, `auto` leaves it staged without restarting, even with `--apply`; it does not automatically fall back to a diff --git a/docs/src/building/bootc-sealed-images.7.md b/docs/src/building/bootc-sealed-images.7.md index c9c0ea52ac..202357974e 100644 --- a/docs/src/building/bootc-sealed-images.7.md +++ b/docs/src/building/bootc-sealed-images.7.md @@ -47,7 +47,7 @@ valid use case is to temporarily disable it in order to test a change locally on e.g. one machine, then re-enable it later. However at the current time it is not yet streamlined to regenerate the UKI locally. -This is independent of `--allow-missing-verity` (see [Overview](../bootc-composefs.7.md#overview)), +This is independent of [`--allow-missing-verity`](../bootc-composefs.7.md#overview), which instead makes fs-verity on the root filesystem optional. ### Build Pattern: Split the Kernel, Then Generate the UKI in a Separate Stage @@ -132,7 +132,7 @@ A lower-level primitive, used internally by `ukify` above, that computes just th - `--erofs-version `: EROFS format for the computed digest (default: `v1`) - `--write-dumpfile-to `: Generate a dumpfile for debugging -> **Note**: This command is currently hidden from `--help` output as it's part of the experimental composefs feature set. +See also [bootc-container-compute-composefs-digest(8)](../man/bootc-container-compute-composefs-digest.8.md). ### Final Image Structure diff --git a/docs/src/host-v1.schema.json b/docs/src/host-v1.schema.json index 686b71e046..8e0fa84dd6 100644 --- a/docs/src/host-v1.schema.json +++ b/docs/src/host-v1.schema.json @@ -216,7 +216,7 @@ "const": "grub" }, { - "description": "Use Grub for confidential clusters as the bootloader", + "description": "Use Grub for confidential clusters as the bootloader (experimental)", "type": "string", "const": "grub-cc" }, diff --git a/docs/src/man/bootc-container-compute-composefs-digest.8.md b/docs/src/man/bootc-container-compute-composefs-digest.8.md new file mode 100644 index 0000000000..3f5237a5bb --- /dev/null +++ b/docs/src/man/bootc-container-compute-composefs-digest.8.md @@ -0,0 +1,62 @@ +# NAME + +bootc-container-compute-composefs-digest - Output the bootable composefs +digest for a directory + +# SYNOPSIS + +bootc container compute-composefs-digest [OPTIONS] [PATH] + +# DESCRIPTION + +Output the bootable composefs digest for a directory + +This is the digest that `bootc container ukify` embeds in the kernel +command line of a UKI. It is a 128-character SHA-512 hex string that +identifies the filesystem contents. Most image builds should use +**bootc-container-ukify**(8), which computes it internally; this command is +useful for scripting and debugging outside of that flow. + +The digest depends on the EROFS format; see `--erofs-version`. It must be +run against a separate mount of the root filesystem, not the running root. + +# OPTIONS + + +**PATH** + + Path to the filesystem root + +**--write-dumpfile-to**=*WRITE_DUMPFILE_TO* + + Additionally generate a dumpfile for the preferred digest, written to the target path + +**--erofs-version**=*EROFS_VERSION* + + EROFS format version to use when computing the composefs digest + + Possible values: + - v1 + - v2 + + Default: v1 + + + +# EXAMPLES + +Compute the digest of a root filesystem mounted at `/target`: + + bootc container compute-composefs-digest /target + +Also write a composefs dumpfile, to compare against another build: + + bootc container compute-composefs-digest --write-dumpfile-to /tmp/rootfs.dump /target + +# SEE ALSO + +**bootc**(8), **bootc-container-ukify**(8) + +# VERSION + + diff --git a/docs/src/man/bootc-container.8.md b/docs/src/man/bootc-container.8.md index 1b1fce62fc..8f54ccc5b6 100644 --- a/docs/src/man/bootc-container.8.md +++ b/docs/src/man/bootc-container.8.md @@ -21,6 +21,7 @@ Operations which can be executed as part of a container build |---------|-------------| | **bootc container inspect** | Output information about the container image | | **bootc container lint** | Perform relatively inexpensive static analysis checks as part of a container build | +| **bootc container compute-composefs-digest** | Output the bootable composefs digest for a directory | | **bootc container split-kernel-and-rootfs** | Split kernel and rootfs from a container image | | **bootc container ukify** | Build a Unified Kernel Image (UKI) using ukify | diff --git a/docs/src/man/bootc-setup-root-conf.5.md b/docs/src/man/bootc-setup-root-conf.5.md index d4c3350c87..908373c731 100644 --- a/docs/src/man/bootc-setup-root-conf.5.md +++ b/docs/src/man/bootc-setup-root-conf.5.md @@ -28,9 +28,6 @@ when it is present on the host image. Image authors can therefore ship the file at this path in their container image and rebuild the initramfs with a plain `dracut --force`; no `--include` flags are needed. -**NOTE**: The composefs backend and this configuration file are experimental -and subject to change without notice. - # SECTIONS ## `[root]`