Skip to content

Commit 766b534

Browse files
committed
docs: Expand composefs backend documentation
Add comprehensive documentation for building sealed bootc images, focusing on the core concepts and the key command: `bootc container compute-composefs-digest`. Key additions: - Document how sealed images work (UKI + composefs digest + Secure Boot) - Explain the build workflow abstractly without distribution-specific details - Document the compute-composefs-digest command and its options - Add section on generating/signing UKIs with ukify - Document developer testing commands (just variant=composefs-sealeduki-sdboot) - Add validation tooling documentation This provides the foundation for distribution-specific documentation to build upon with concrete Containerfile examples. Assisted-by: OpenCode (Claude Sonnet 4) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent a4d6438 commit 766b534

1 file changed

Lines changed: 123 additions & 13 deletions

File tree

docs/src/experimental-composefs.md

Lines changed: 123 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,150 @@ This is based on [Unified Kernel Images](https://uapi-group.org/specifications/s
1616
that embed a digest of the target container root filesystem, typically alongside a bootloader (such
1717
as systemd-boot) also signed with your key.
1818

19-
### UKIs in bootc containers
19+
## How Sealed Images Work
2020

21-
There must be exactly one UKI placed in `/boot/EFI/Linux/<name>.efi`.
21+
A sealed image is a cryptographically signed and verified bootc image that provides end-to-end integrity protection. This is achieved through:
2222

23-
### Bootloader support
23+
- **Unified Kernel Images (UKIs)**: Combining kernel, initramfs, and boot parameters into a single signed binary
24+
- **Composefs integration**: Using composefs with fsverity for content-addressed filesystem verification
25+
- **Secure Boot**: Cryptographic signatures on both the UKI and systemd-boot loader
2426

25-
To use sealed images, ensure that the target container image has systemd-boot,
26-
and does not have `bootupd`.
27+
A sealed image includes:
2728

28-
### Installation
29+
1. **composefs digest**: A SHA-512 hash of the entire root filesystem, computed at build time
30+
2. **Unified Kernel Image (UKI)**: A single EFI binary containing the kernel, initramfs, and kernel command line with the composefs digest embedded
31+
3. **Secure Boot signature**: The UKI is signed with your private key
2932

30-
There is a `--composefs-backend` option for `bootc install`; however, if
31-
a UKI and systemd-boot are detected, it will automatically be used.
33+
At boot time, the composefs digest in the kernel command line (e.g., `composefs=<sha512-hash>`) 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.
3234

33-
### Developing and testing bootc with sealed composefs
35+
## Building Sealed Images
3436

35-
Use `just variant=composefs-sealeduki-sdboot build` to build a local sealed
36-
UKI, using Secure Boot keys generated in `target/test-secureboot`. This is
37-
not a production path.
37+
### Prerequisites
38+
39+
For sealed images, the container must:
40+
41+
- Include a kernel and initramfs in `/usr/lib/modules/<kver>/`
42+
- Have systemd-boot available (and NOT have `bootupd`)
43+
- Not include a pre-built UKI (the build process generates one)
44+
45+
Sealed images also require:
46+
47+
- Secure Boot support in the target system firmware
48+
- A filesystem with fsverity support (e.g., ext4, btrfs) for the root partition
49+
50+
### Build Pattern: Compute Digest and Generate UKI in One Stage
51+
52+
The key to building sealed images is using a multi-stage Dockerfile where a separate stage mounts the target rootfs, computes its composefs digest, and generates the signed UKI in one step:
53+
54+
```dockerfile
55+
# Build your rootfs with all packages and configuration
56+
FROM <base-image> as rootfs
57+
RUN apt|dnf|zypper install ... && bootc container lint --fatal-warnings
58+
59+
# Generate the sealed UKI in a tools stage
60+
FROM <tools-image> as sealed-uki
61+
RUN --mount=type=bind,from=rootfs,target=/target \
62+
--mount=type=secret,id=secureboot_key \
63+
--mount=type=secret,id=secureboot_cert <<EORUN
64+
set -euo pipefail
65+
66+
# Compute the composefs digest from the mounted rootfs
67+
digest=$(bootc container compute-composefs-digest /target)
68+
69+
# Find the kernel version
70+
kver=$(ls /target/usr/lib/modules)
71+
72+
# Generate and sign the UKI with the digest embedded
73+
ukify build \
74+
--linux "/target/usr/lib/modules/${kver}/vmlinuz" \
75+
--initrd "/target/usr/lib/modules/${kver}/initramfs.img" \
76+
--cmdline "composefs=${digest} rw" \
77+
--os-release "@/target/usr/lib/os-release" \
78+
--signtool sbsign \
79+
--secureboot-private-key /run/secrets/secureboot_key \
80+
--secureboot-certificate /run/secrets/secureboot_cert \
81+
--output "/out/${kver}.efi"
82+
EORUN
83+
84+
# Final image: copy the sealed UKI into place
85+
FROM rootfs
86+
COPY --from=sealed-uki /out/*.efi /boot/EFI/Linux/
87+
# Remove raw kernel/initramfs (now embedded in UKI)
88+
RUN rm -f /usr/lib/modules/*/vmlinuz /usr/lib/modules/*/initramfs.img
89+
```
90+
91+
This pattern works because:
92+
93+
1. The `--mount=type=bind,from=rootfs` provides read-only access to the target filesystem
94+
2. `bootc container compute-composefs-digest` computes the SHA-512 hash of the rootfs
95+
3. `ukify` creates the UKI with that digest in the kernel command line (`composefs=<digest>`)
96+
4. The final stage copies the signed UKI into the rootfs without modifying any files used in the digest calculation
97+
98+
### The `bootc container compute-composefs-digest` Command
99+
100+
```bash
101+
bootc container compute-composefs-digest [PATH]
102+
```
103+
104+
Computes the composefs digest for a filesystem. The digest is a 128-character SHA-512 hex string that uniquely identifies the filesystem contents.
105+
106+
**Options:**
107+
108+
- `PATH`: Path to the filesystem root (default: `/target`)
109+
- `--write-dumpfile-to <PATH>`: Generate a dumpfile for debugging
110+
111+
> **Note**: This command is currently hidden from `--help` output as it's part of the experimental composefs feature set.
112+
113+
### Final Image Structure
114+
115+
The sealed image should have:
116+
117+
- The signed UKI at `/boot/EFI/Linux/<kver>.efi`
118+
- A signed systemd-boot at `/boot/EFI/BOOT/BOOTX64.EFI` and `/boot/EFI/systemd/systemd-bootx64.efi`
119+
- The raw `vmlinuz` and `initramfs.img` removed from `/usr/lib/modules/<kver>/` (they're now embedded in the UKI)
120+
121+
### External Signing Workflow
122+
123+
For production environments with dedicated signing infrastructure:
124+
125+
1. **Build unsigned UKI**: Compute digest and create an unsigned UKI (omit `--signtool` from ukify)
126+
2. **Sign externally**: Take the unsigned UKI to your signing infrastructure
127+
3. **Complete the seal**: Inject the signed UKI into the final image
128+
129+
This workflow is planned for streamlining in future releases (see [#1498](https://github.com/bootc-dev/bootc/issues/1498)).
130+
131+
## Developing and Testing bootc with composefs
132+
133+
See [CONTRIBUTING.md](https://github.com/bootc-dev/bootc/blob/main/CONTRIBUTING.md) for information on building and testing bootc itself with composefs support.
134+
135+
## Bootloader Support
136+
137+
To use sealed images, the container image must have a UKI and systemd-boot installed (and not have `bootupd`). If these conditions are met, bootc will automatically detect and use the composefs backend during installation.
138+
139+
## Installation
140+
141+
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.
38142

39143
## Current Limitations
40144

41-
- **Experimental**: In particular, the on-disk formats are subject to change
145+
- **Experimental**: The on-disk formats are subject to change
42146
- **UX refinement**: The user experience for building and managing sealed images is still being improved
147+
- **SELinux**: Currently uses `enforcing=0` in the kernel command line (see [#1826](https://github.com/bootc-dev/bootc/issues/1826))
148+
- **kargs.d**: Custom kernel arguments from `/usr/lib/bootc/kargs.d` are not yet automatically included in sealed UKIs
43149

44150
## Related Issues
45151

46152
- [#1190](https://github.com/bootc-dev/bootc/issues/1190) - composefs-native backend (main tracker)
47153
- [#1498](https://github.com/bootc-dev/bootc/issues/1498) - Sealed image build UX + implementation
48154
- [#1703](https://github.com/bootc-dev/bootc/issues/1703) - OCI config mismatch issues
155+
- [#1826](https://github.com/bootc-dev/bootc/issues/1826) - SELinux enforcement with composefs
49156
- [#20](https://github.com/bootc-dev/bootc/issues/20) - Unified storage (long-term goal)
50157
- [#806](https://github.com/bootc-dev/bootc/issues/806) - UKI/systemd-boot tracker
51158

52159
## Additional Resources
53160

54161
- See [filesystem.md](filesystem.md) for information about composefs in the standard ostree backend
55162
- See [bootloaders.md](bootloaders.md) for bootloader configuration details
163+
- [composefs-rs](https://github.com/containers/composefs-rs) - The underlying composefs implementation
164+
- [Unified Kernel Images specification](https://uapi-group.org/specifications/specs/unified_kernel_image/)
165+
- [ukify documentation](https://www.freedesktop.org/software/systemd/man/latest/ukify.html) - Tool for building UKIs

0 commit comments

Comments
 (0)