Skip to content

libvirt: Don't reuse a stale secure boot VARS template - #378

Merged
cgwalters merged 2 commits into
bootc-dev:mainfrom
cgwalters-forge:bot/libvirt-rm-ovmf-vars
Oct 1, 2026
Merged

cgwalters merged 2 commits into
bootc-dev:mainfrom
cgwalters-forge:bot/libvirt-rm-ovmf-vars

Conversation

@cgwalters-bot

@cgwalters-bot cgwalters-bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

bcvk libvirt run --secure-boot-keys wrote a per-VM <name>_OVMF_VARS.fd, with the keys enrolled, into the storage pool and passed it to libvirt as the NVRAM template. libvirt removes its own copy on undefine --nvram, but nothing removed bcvk's file, and bcvk only wrote it if it didn't exist yet. So re-creating a VM under the same name (after libvirt rm, or with --replace) got the old VM's keys, and a UKI signed with the new keys failed with "Access Denied".

The first commit makes that file the domain's NVRAM itself, with no template, so libvirt owns it and the --nvram that rm and rm-all already pass removes it. bcvk regenerates it on every libvirt run, because libvirt uses an existing NVRAM file as is and a transient VM leaves its file behind. libvirt start doesn't touch it, so variables the guest writes survive restarts. The second commit adds an integration test.

Testing, on a 16-core RHEL 10.2 devspace (libvirt 11.10, qemu:///session):

  • test_libvirt_secure_boot_vars_not_reused enrolls key set A, runs rm and checks the NVRAM file is gone, then puts the old file back, re-creates the VM with key set B and checks with virt-fw-vars that B's PK is enrolled. It fails on main and passes with this branch.
  • Libvirt integration suite: 21 of 23 passed. The other two failed for environmental reasons: test_libvirt_port_forward_xml because port 9090 was taken on the devspace, and test_libvirt_run_transient_vm with a permission error on the shared base disk during the parallel run; it passed when rerun alone.
  • Not tested with qemu:///system.

The Signed-off-by: Colin Walters <walters@verbum.org> on these commits was added on cgwalters' approval of the review draft (cgwalters-forge#9 (review)), and kept through the rework that followed his review comment here.

Generated-by: https://github.com/cgwalters/#llms

Comment thread crates/kit/src/libvirt/rm.rs Outdated

/// Files bcvk created for a domain outside of its libvirt-managed storage:
/// the persistent Ignition config and the secure boot VARS template.
///

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.

Hmm is there no way we can convince libvirt to "take ownership" of this stuff?

But ok as is

@cgwalters-bot cgwalters-bot Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

libvirt removes a domain's NVRAM file on undefine --nvram (since 1.2.9, so on every RHEL 9/10 and Fedora version), but it can't enroll custom keys itself: firmware autoselection's enrolled-keys only picks the distro's vars. So bcvk now writes the keyed vars as the domain's <nvram> path, where it used to pass them as a template, and rm and rm-all's existing --nvram removes the file. The rm changes are dropped. The file is still regenerated on every run, since libvirt leaves a transient domain's NVRAM behind. Your sign-off is kept on 4128e8a and d22d3bc.
Tested on a 16-core RHEL 10.2 devspace (libvirt 11.10): I created a VM with key set A, ran rm, then re-created it with key set B. B's PK was enrolled and no file was left behind. The new integration test passes, and it fails against main.

Generated-by: https://github.com/cgwalters/#llms

Comment thread crates/kit/src/libvirt/rm.rs Outdated
};

let ignition = dom
.find("bootc:ignition-persistent-path")

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.

Ah I see. We should perhaps have a const for this

With --secure-boot-keys, bcvk wrote <name>_OVMF_VARS.fd into the storage
pool and passed it to libvirt as the NVRAM template. libvirt copied it
into its own NVRAM file, which `virsh undefine --nvram` removes, but
the template was bcvk's and nothing removed it. It was also only
created if it didn't exist yet, so a VM re-created under the same name
(after `libvirt rm`, or with --replace) silently got the previous VM's
enrolled keys instead of the ones passed. A UKI signed with the new
keys then fails with "Access Denied", which looks like a signing
problem rather than a stale file.

Point <nvram> at the file itself instead of using it as a template, so
it is the domain's NVRAM and libvirt removes it on `undefine --nvram`,
which rm and rm-all already pass. It is also always regenerated, since
libvirt uses an existing NVRAM file as is and a transient VM (which is
never undefined) leaves its file behind.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
Enroll one key set, remove the VM and check its NVRAM file is gone,
then put the old file back (as an older bcvk or a transient VM leaves
it) and re-create the VM under the same name with another key set: the
new PK must be the one enrolled. This is the case where a UKI signed
with the new keys failed with "Access Denied".

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
@cgwalters-bot
cgwalters-bot force-pushed the bot/libvirt-rm-ovmf-vars branch from 061e73b to d22d3bc Compare September 28, 2026 20:37
@cgwalters
cgwalters merged commit f96585b into bootc-dev:main Oct 1, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants