all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Fiona Ebner <f.ebner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH v2 qemu-server 1/1] fix #6985: ovmf: auto-enroll Microsoft UEFI CA 2023 for Windows
Date: Fri, 14 Nov 2025 12:59:17 +0100	[thread overview]
Message-ID: <20251114115929.110035-1-f.ebner@proxmox.com> (raw)

Microsoft's UEFI CA 2011 will expire in June 2026. It's necessary to
ensure that the new UEFI CA 2023 is enrolled for Windows to keep
updates working.

pve-edk2-firmware >= 4.2025.05-1 includes the 2023 certificate
already, so new disks are fine. Still, check during EFI disk creation
what is actually there, since there is no guarantee that a new enough
version of pve-edk2-firmware is installed.

A new 'ms-cert' drive property for EFI disks records the year of the
last known-to-be-enrolled MS UEFI CA. This avoids the need to re-check
every time if the 2023 certificate needs to be enrolled. The downside
is that this breaks backwards migration, because a disk with an
unknown option is dropped from the configuration.

Enrollment and checking for existing disks is done via virt-fw-vars,
which is a new dependency recorded in d/control. While virt-fw-vars
supports raw and qcow2 files out of the box, this is not enough,
because EFI disks can also be vmdk formatted and there are also
storages that use a protocol path like 'rbd://' or 'iscsi://' for
QEMU, which virt-fw-vars cannot handle. Thus, use a FUSE export to
cover all cases.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---

Changes in v2:
* Rebase on current master.
* Add comment that ensure_ms_2023_cert_enrolled() helper can currently
  only be called as part of the VM start task, because it uses the
  main QSD instance associated to the VM.

 debian/control              |  1 +
 src/PVE/QemuServer.pm       | 20 ++++++++++++
 src/PVE/QemuServer/Drive.pm |  9 ++++++
 src/PVE/QemuServer/OVMF.pm  | 61 +++++++++++++++++++++++++++++++++++++
 4 files changed, 91 insertions(+)

diff --git a/debian/control b/debian/control
index 78188c2c..d439d58b 100644
--- a/debian/control
+++ b/debian/control
@@ -58,6 +58,7 @@ Depends: conntrack,
          pve-firewall (>= 6.0.3),
          pve-ha-manager (>= 5.0.3),
          pve-qemu-kvm (>= 7.1~),
+         python3-virt-firmware,
          socat,
          swtpm,
          swtpm-tools,
diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index bac29ac9..42f1bf49 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -5406,6 +5406,24 @@ sub generate_storage_hints {
     return $hints;
 }
 
+my sub check_efi_vars {
+    my ($storecfg, $vmid, $conf) = @_;
+
+    return if PVE::QemuConfig->is_template($conf);
+    return if !$conf->{efidisk0};
+    return if $conf->{ostype} ne 'win10' && $conf->{ostype} ne 'win11';
+
+    if (
+        my $updated = PVE::QemuServer::OVMF::ensure_ms_2023_cert_enrolled(
+            $storecfg, $vmid, $conf->{efidisk0},
+        )
+    ) {
+        $conf->{efidisk0} = $updated;
+        PVE::QemuConfig->write_config($vmid, $conf);
+    }
+    return;
+}
+
 # see vm_start_nolock for parameters, additionally:
 # migrate_opts:
 #   storagemap = parsed storage map for allocating NBD disks
@@ -5583,6 +5601,8 @@ sub vm_start_nolock {
         my $storage_hints = generate_storage_hints($conf, 1);
         PVE::Storage::activate_volumes($storecfg, $vollist, undef, $storage_hints);
 
+        check_efi_vars($storecfg, $vmid, $conf) if $conf->{bios} && $conf->{bios} eq 'ovmf';
+
         # Note that for certain cases like templates, the configuration is minimized, so need to ensure
         # the rest of the function here uses the same configuration that was used to build the command
         ($cmd, $spice_port, my $pci_devices, $conf) = config_to_command(
diff --git a/src/PVE/QemuServer/Drive.pm b/src/PVE/QemuServer/Drive.pm
index f54f9612..c772c803 100644
--- a/src/PVE/QemuServer/Drive.pm
+++ b/src/PVE/QemuServer/Drive.pm
@@ -521,6 +521,15 @@ my %efitype_fmt = (
         optional => 1,
         default => 0,
     },
+    'ms-cert' => {
+        type => 'string',
+        enum => [qw(2011 2023)],
+        description =>
+            "Informational marker indicating the version of the latest Microsof UEFI certificate"
+            . " that has been enrolled by Proxmox VE.",
+        optional => 1,
+        default => '2011',
+    },
 );
 
 my $efidisk_fmt = {
diff --git a/src/PVE/QemuServer/OVMF.pm b/src/PVE/QemuServer/OVMF.pm
index aa88bfa6..c258ae50 100644
--- a/src/PVE/QemuServer/OVMF.pm
+++ b/src/PVE/QemuServer/OVMF.pm
@@ -11,7 +11,9 @@ use PVE::Tools;
 
 use PVE::QemuServer::Blockdev;
 use PVE::QemuServer::Drive qw(checked_volume_format parse_drive print_drive);
+use PVE::QemuServer::Helpers;
 use PVE::QemuServer::QemuImage;
+use PVE::QemuServer::QSD;
 
 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
 my $OVMF = {
@@ -139,6 +141,30 @@ sub get_efivars_size {
     return -s $ovmf_vars;
 }
 
+my sub is_ms_2023_cert_enrolled {
+    my ($path) = @_;
+
+    my $inside_db_section;
+    my $found_ms_2023_cert;
+
+    my $detect_ms_2023_cert = sub {
+        my ($line) = @_;
+        return if $found_ms_2023_cert;
+        $inside_db_section = undef if !$line;
+        $found_ms_2023_cert = 1
+            if $inside_db_section && $line =~ m/CN=Microsoft UEFI CA 2023/;
+        $inside_db_section = 1 if $line =~ m/^name=db guid=guid:EfiImageSecurityDatabase/;
+        return;
+    };
+
+    PVE::Tools::run_command(
+        ['virt-fw-vars', '--input', $path, '--print', '--verbose'],
+        outfunc => $detect_ms_2023_cert,
+    );
+
+    return $found_ms_2023_cert;
+}
+
 sub create_efidisk($$$$$$$$) {
     my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm, $cvm_type) = @_;
 
@@ -152,6 +178,10 @@ sub create_efidisk($$$$$$$$) {
     PVE::QemuServer::QemuImage::convert($ovmf_vars, $volid, $vars_size_b);
     my $size = PVE::Storage::volume_size_info($storecfg, $volid, 3);
 
+    if ($efidisk->{'pre-enrolled-keys'} && is_ms_2023_cert_enrolled($ovmf_vars)) {
+        $efidisk->{'ms-cert'} = '2023'
+    }
+
     return ($volid, $size / 1024);
 }
 
@@ -248,4 +278,35 @@ sub print_ovmf_commandline {
     return ($cmd, $machine_flags);
 }
 
+# May only be called as part of VM start right now, because it uses the main QSD associated to the
+# VM. If required for another scenario, change the QSD ID to something else.
+sub ensure_ms_2023_cert_enrolled {
+    my ($storecfg, $vmid, $efidisk_str) = @_;
+
+    my $efidisk = parse_drive('efidisk0', $efidisk_str);
+    return if !$efidisk->{'pre-enrolled-keys'};
+    return if $efidisk->{'ms-cert'} && $efidisk->{'ms-cert'} eq '2023';
+
+    print "efidisk0: enrolling Microsoft UEFI CA 2023\n";
+
+    my $new_qsd = !PVE::QemuServer::Helpers::qsd_running_locally($vmid);
+    PVE::QemuServer::QSD::start($vmid) if $new_qsd;
+
+    eval {
+        my $efi_vars_path =
+            PVE::QemuServer::QSD::add_fuse_export($vmid, $efidisk, 'efidisk0-enroll');
+        PVE::Tools::run_command(
+            ['virt-fw-vars', '--inplace', $efi_vars_path, '--distro-keys', 'ms-uefi']);
+        PVE::QemuServer::QSD::remove_fuse_export($vmid, 'efidisk0-enroll');
+    };
+    my $err = $@;
+
+    PVE::QemuServer::QSD::quit($vmid) if $new_qsd;
+
+    die "efidisk0: enrolling Microsoft UEFI CA 2023 failed - $err" if $err;
+
+    $efidisk->{'ms-cert'} = '2023';
+    return print_drive($efidisk);
+}
+
 1;
-- 
2.47.3



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


             reply	other threads:[~2025-11-14 11:59 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-14 11:59 Fiona Ebner [this message]
2025-11-14 12:02 ` Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251114115929.110035-1-f.ebner@proxmox.com \
    --to=f.ebner@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal