Skip to content

Commit 2a0678b

Browse files
committed
blog: add sealed images deploying post
Fourth post in the sealed images series, covering deployment via bcvk and verification that the full security chain is active. Assisted-by: OpenCode (claude-opus-4-6)
1 parent 1a6887e commit 2a0678b

1 file changed

Lines changed: 162 additions & 0 deletions

File tree

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
+++
2+
title = "Sealed images: deploying and verifying a sealed image"
3+
date = 2026-04-30
4+
slug = "2026-apr-30-sealed-images-deploying"
5+
6+
[extra]
7+
author = "jeckersb"
8+
+++
9+
10+
# Sealed images: deploying and verifying a sealed image
11+
12+
In the [previous post](@/blog/2026-apr-29-sealed-images-building.md)
13+
we built a sealed, signed container image. Now it's time to deploy
14+
it to a virtual machine and verify that the full security chain is
15+
active.
16+
17+
## bcvk: a tool for running bootc VMs
18+
19+
[bcvk](https://github.com/bootc-dev/bcvk) (bootc virtualization kit)
20+
is a tool that makes it easy to run bootc container images as virtual
21+
machines. Under the hood, it calls `bootc install to-disk` to create
22+
a bootable disk image, then manages the VM lifecycle via libvirt.
23+
For sealed images, bcvk also handles Secure Boot key enrollment into
24+
the VM's UEFI firmware automatically using
25+
[virt-firmware](https://github.com/rhuefi/virt-firmware).
26+
27+
bcvk is available as a package on Fedora and EPEL:
28+
29+
```
30+
$ dnf install bcvk
31+
```
32+
33+
## Deploying the image
34+
35+
With the sealed image built from the previous post and keys generated
36+
from the key management post, deploying is a single command:
37+
38+
```
39+
$ bcvk libvirt run \
40+
--ssh-wait \
41+
--name sealed-demo \
42+
--filesystem=ext4 \
43+
--secure-boot-keys target/keys \
44+
localhost/sealed-host:latest
45+
```
46+
47+
Let's break down what's happening here:
48+
49+
- `--filesystem=ext4` tells `bootc install` to use ext4 for the root
50+
filesystem. fs-verity is supported on ext4, btrfs, and XFS (on
51+
modern kernels).
52+
- `--secure-boot-keys target/keys` points bcvk to the directory
53+
containing our PK, KEK, and db certificates. bcvk uses
54+
`virt-fw-vars` to enroll these keys into a copy of the OVMF
55+
firmware variable store, so the VM boots with Secure Boot enabled
56+
and configured to trust our signing key.
57+
- `--ssh-wait` tells bcvk to wait until SSH is available before
58+
returning, so we know the system has fully booted.
59+
60+
If you're using the
61+
[examples repository](https://github.com/redhat-cop/rhel-bootc-examples/tree/main/sealing),
62+
this is wrapped up in a convenient `just` recipe:
63+
64+
```
65+
$ just bcvk-ssh
66+
```
67+
68+
This builds the image, boots the VM, waits for it to reach
69+
`multi-user.target`, and opens an interactive SSH session.
70+
71+
## Verifying the seal
72+
73+
Once the system is booted and we have an SSH session, we can verify
74+
that the full security chain is active.
75+
76+
### Check the kernel command line
77+
78+
```
79+
$ cat /proc/cmdline
80+
composefs=3a7f... (128-character SHA-512 hex digest) rw
81+
```
82+
83+
The presence of `composefs=<digest>` in the kernel command line
84+
confirms that the initramfs received the composefs digest from the
85+
signed UKI. This is the digest that was computed during the build
86+
and embedded in the UKI's command line section.
87+
88+
### Check the root mount
89+
90+
```
91+
$ findmnt -t overlay /
92+
TARGET SOURCE FSTYPE OPTIONS
93+
/ overlay[/sysroot/state..] overlay ro,relatime,...,verity=require,...
94+
```
95+
96+
The key thing to look for is `verity=require` in the mount options.
97+
This confirms that the kernel is enforcing fs-verity verification
98+
on every file access through the composefs mount. Any file in the
99+
operating system whose content doesn't match its expected fs-verity
100+
digest will produce an I/O error when read.
101+
102+
### Check Secure Boot status
103+
104+
```
105+
$ mokutil --sb-state
106+
SecureBoot enabled
107+
```
108+
109+
This confirms that the firmware verified the bootloader and UKI
110+
signatures before allowing them to execute.
111+
112+
### What this tells us
113+
114+
These three checks together confirm the full chain from the
115+
[first post](@/blog/2026-apr-27-sealed-images-security-chain.md):
116+
117+
1. Secure Boot verified the bootloader and UKI (`mokutil --sb-state`)
118+
2. The UKI delivered the composefs digest to the initramfs
119+
(`/proc/cmdline`)
120+
3. The kernel is enforcing per-file verification against that digest
121+
(`verity=require`)
122+
123+
## What happens if something is tampered with?
124+
125+
It's worth understanding why tampering with a sealed system is
126+
difficult in practice.
127+
128+
The operating system files are stored in a content-addressed object
129+
store on disk. Each object has fs-verity enabled, which means the
130+
kernel has recorded a cryptographic hash of its contents. The files
131+
are immutable: you cannot write to an fs-verity protected file. Any
132+
attempt to open a verity-protected file for writing will fail.
133+
134+
Even if an attacker were to bypass the filesystem and corrupt data
135+
directly on the underlying block device, the kernel would detect
136+
the corruption. When a process attempts to read the tampered file,
137+
the kernel verifies the data against the stored hash before
138+
returning it. If the data doesn't match, the read fails with
139+
`EIO`. The corrupted data is never served to any process.
140+
141+
This is a meaningful distinction from a system where tampered files
142+
are silently served. On a sealed system, corruption doesn't go
143+
unnoticed -- it causes an immediate, visible failure.
144+
145+
## Conclusion
146+
147+
Over the course of this series, we've walked through the complete
148+
lifecycle of a sealed image:
149+
150+
1. [The security chain](@/blog/2026-apr-27-sealed-images-security-chain.md)
151+
from firmware to filesystem that makes sealed images possible
152+
2. [Key management](@/blog/2026-apr-28-sealed-images-key-management.md)
153+
for generating and enrolling Secure Boot keys
154+
3. [Building a sealed image](@/blog/2026-apr-29-sealed-images-building.md)
155+
with a multi-stage container build
156+
4. Deploying and verifying the seal (this post)
157+
158+
The complete working example is available in the
159+
[rhel-bootc-examples](https://github.com/redhat-cop/rhel-bootc-examples)
160+
repository under the
161+
[sealing](https://github.com/redhat-cop/rhel-bootc-examples/tree/main/sealing)
162+
directory.

0 commit comments

Comments
 (0)