Skip to content

petri: Disable secure boot by default for HyperV tests and introduce specific secureboot tests #1386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
43 changes: 33 additions & 10 deletions petri/src/vm/hyperv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,18 @@ impl PetriVmConfig for PetriVmConfigHyperV {
Ok((Box::new(vm), client))
}

fn with_secure_boot(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_secure_boot(*self))
}

fn with_windows_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_windows_secure_boot_template(*self))
}

fn with_uefi_ca_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_uefi_ca_template(*self))
}

fn with_processor_topology(
self: Box<Self>,
topology: ProcessorTopology,
Expand Down Expand Up @@ -278,16 +286,7 @@ impl PetriVmConfigHyperV {
memory: 0x1_0000_0000,
proc_topology: ProcessorTopology::default(),
vhd_paths,
secure_boot_template: matches!(generation, powershell::HyperVGeneration::Two)
.then_some(match firmware.os_flavor() {
OsFlavor::Windows => powershell::HyperVSecureBootTemplate::MicrosoftWindows,
OsFlavor::Linux => {
powershell::HyperVSecureBootTemplate::MicrosoftUEFICertificateAuthority
}
OsFlavor::FreeBsd | OsFlavor::Uefi => {
powershell::HyperVSecureBootTemplate::SecureBootDisabled
}
}),
secure_boot_template: None,
openhcl_igvm,
agent_image,
openhcl_agent_image,
Expand Down Expand Up @@ -535,6 +534,20 @@ impl PetriVmConfigHyperV {
self
}

/// Set the VM to enable secure boot and inject the templates.
pub fn with_secure_boot(self) -> Self {
if !matches!(self.generation, powershell::HyperVGeneration::Two) {
panic!("Secure boot is only supported for UEFI firmware.");
}
match self.os_flavor {
OsFlavor::Windows => self.with_windows_secure_boot_template(),
OsFlavor::Linux => self.with_uefi_ca_template(),
OsFlavor::FreeBsd | OsFlavor::Uefi => {
panic!("Secure boot is not supported for this OS flavor.")
}
}
}

/// Inject Windows secure boot templates into the VM's UEFI.
pub fn with_windows_secure_boot_template(mut self) -> Self {
if !matches!(self.generation, powershell::HyperVGeneration::Two) {
Expand All @@ -544,6 +557,16 @@ impl PetriVmConfigHyperV {
self
}

/// Inject UEFI CA template into the VM's UEFI
pub fn with_uefi_ca_template(mut self) -> Self {
if !matches!(self.generation, powershell::HyperVGeneration::Two) {
panic!("Secure boot templates are only supported for UEFI firmware.");
}
self.secure_boot_template =
Some(powershell::HyperVSecureBootTemplate::MicrosoftUEFICertificateAuthority);
self
}

/// Sets a custom OpenHCL IGVM image to use.
pub fn with_custom_openhcl(mut self, artifact: ResolvedArtifact) -> Self {
self.openhcl_igvm = Some(artifact);
Expand Down
8 changes: 8 additions & 0 deletions petri/src/vm/hyperv/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ impl HyperVVM {
},
)?;

// Disable secure boot for generation 2 VMs
if generation == powershell::HyperVGeneration::Two {
powershell::run_set_vm_firmware(powershell::HyperVSetVMFirmwareArgs {
vmid: &vmid,
secure_boot_template: None,
})?;
}

Ok(this)
}

Expand Down
4 changes: 4 additions & 0 deletions petri/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ pub trait PetriVmConfig: Send {
/// Run the VM, launching pipette and returning a client to it.
async fn run(self: Box<Self>) -> anyhow::Result<(Box<dyn PetriVm>, PipetteClient)>;

/// Set the VM to enable secure boot and inject the templates.
fn with_secure_boot(self: Box<Self>) -> Box<dyn PetriVmConfig>;
/// Inject Windows secure boot templates into the VM's UEFI.
fn with_windows_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig>;
/// Inject UEFI CA template into the VM's UEFI.
fn with_uefi_ca_template(self: Box<Self>) -> Box<dyn PetriVmConfig>;
/// Set the VM to use the specified processor topology.
fn with_processor_topology(
self: Box<Self>,
Expand Down
8 changes: 8 additions & 0 deletions petri/src/vm/openvmm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,18 @@ impl PetriVmConfig for PetriVmConfigOpenVmm {
Ok((Box::new(vm), client))
}

fn with_secure_boot(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_secure_boot(*self))
}

fn with_windows_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_windows_secure_boot_template(*self))
}

fn with_uefi_ca_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
Box::new(Self::with_uefi_ca_template(*self))
}

fn with_processor_topology(
self: Box<Self>,
topology: ProcessorTopology,
Expand Down
30 changes: 28 additions & 2 deletions petri/src/vm/openvmm/modify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use hvlite_defs::config::VpciDeviceConfig;
use hvlite_defs::config::Vtl2BaseAddressType;
use petri_artifacts_common::tags::IsTestVmgs;
use petri_artifacts_common::tags::MachineArch;
use petri_artifacts_common::tags::OsFlavor;
use petri_artifacts_core::ResolvedArtifact;
use tpm_resources::TpmDeviceHandle;
use tpm_resources::TpmRegisterLayout;
Expand Down Expand Up @@ -127,17 +128,28 @@ impl PetriVmConfigOpenVmm {
self
}

/// Enable secure boot for the VM.
/// Set the VM to enable secure boot and inject the templates.
pub fn with_secure_boot(mut self) -> Self {
// Restrict to UEFI firmware
if !self.firmware.is_uefi() {
panic!("Secure boot is only supported for UEFI firmware.");
}

// Enable secure boot
if self.firmware.is_openhcl() {
self.ged.as_mut().unwrap().secure_boot_enabled = true;
} else {
self.config.secure_boot_enabled = true;
}
self

// Inject templates per os flavor
match self.os_flavor() {
OsFlavor::Windows => self.with_windows_secure_boot_template(),
OsFlavor::Linux => self.with_uefi_ca_template(),
OsFlavor::FreeBsd | OsFlavor::Uefi => {
panic!("Secure boot is not supported for this OS flavor.")
}
}
}

/// Inject Windows secure boot templates into the VM's UEFI.
Expand All @@ -154,6 +166,20 @@ impl PetriVmConfigOpenVmm {
self
}

/// Inject UEFI CA template into the VM's UEFI
pub fn with_uefi_ca_template(mut self) -> Self {
if !self.firmware.is_uefi() {
panic!("Secure boot templates are only supported for UEFI firmware.");
}
if self.firmware.is_openhcl() {
self.ged.as_mut().unwrap().secure_boot_template =
get_resources::ged::GuestSecureBootTemplateType::MicrosoftUefiCertificateAuthoritiy;
} else {
self.config.custom_uefi_vars = hyperv_secure_boot_templates::x64::microsoft_uefi_ca();
}
self
}

/// Enable the battery for the VM.
pub fn with_battery(mut self) -> Self {
if self.firmware.is_openhcl() {
Expand Down
63 changes: 63 additions & 0 deletions vmm_tests/vmm_tests/tests/tests/multiarch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,69 @@ async fn boot(config: Box<dyn PetriVmConfig>) -> anyhow::Result<()> {
Ok(())
}

/// Basic boot test with secure boot enabled.
#[vmm_test(
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have other tests with secure boot that can be removed/folded into this?

Copy link
Author

Choose a reason for hiding this comment

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

good point, ill have to look around for these

Copy link
Author

Choose a reason for hiding this comment

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

as far as I know, looks like we don't have any other tests with secure boot explicitly?

I did notice guest_test_uefi multiarch test has with_windows_secure_boot_template().

My new with_secure_boot() could be used instead? But I'm not sure if, based on the OsFlavor for guest_test_uefi_x64 or guest_test_uefi_aarch64 what the correct secure boot template would be in this case-- must it be the windows template?

Copy link
Member

Choose a reason for hiding this comment

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

Should we add a test that verifies Linux fails to boot with the Windows secure boot template?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, we should-- I'll add these negative tests next

openvmm_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
openvmm_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
openvmm_uefi_x64(vhd(ubuntu_2204_server_x64)),
openvmm_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
openvmm_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64)),
hyperv_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
hyperv_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
hyperv_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
hyperv_uefi_x64(vhd(ubuntu_2204_server_x64)),
hyperv_openhcl_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
hyperv_openhcl_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
hyperv_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
hyperv_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64))
)]
async fn secure_boot(config: Box<dyn PetriVmConfig>) -> anyhow::Result<()> {
let mut vm = config.with_secure_boot().run_without_agent().await?;
vm.wait_for_successful_boot_event().await?;
assert_eq!(vm.wait_for_teardown().await?, HaltReason::PowerOff);
Copy link
Contributor

Choose a reason for hiding this comment

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

What is telling these vms to shut down? You probably need to add a call to enlightened_shutdown

Ok(())
}

/// Verify that Windows UEFI guests fail with a mismatched secure boot template.
#[vmm_test(
openvmm_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
openvmm_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
hyperv_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
hyperv_uefi_x64(vhd(windows_datacenter_core_2022_x64)),
hyperv_openhcl_uefi_aarch64(vhd(windows_11_enterprise_aarch64)),
hyperv_openhcl_uefi_x64(vhd(windows_datacenter_core_2022_x64))
)]
async fn mismatched_secure_boot_template_windows(
config: Box<dyn PetriVmConfig>,
) -> anyhow::Result<()> {
let mut vm = config.with_uefi_ca_template().run_without_agent().await?;
assert_eq!(vm.wait_for_boot_event().await?, FirmwareEvent::BootFailed);
assert_eq!(vm.wait_for_teardown().await?, HaltReason::PowerOff);
Ok(())
}

/// Verify that Linux UEFI guests fail with a mismatched secure boot template.
#[vmm_test(
openvmm_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
openvmm_uefi_x64(vhd(ubuntu_2204_server_x64)),
openvmm_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64)),
hyperv_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
hyperv_uefi_x64(vhd(ubuntu_2204_server_x64)),
hyperv_openhcl_uefi_aarch64(vhd(ubuntu_2404_server_aarch64)),
hyperv_openhcl_uefi_x64(vhd(ubuntu_2204_server_x64))
)]
async fn mismatched_secure_boot_template_linux(
config: Box<dyn PetriVmConfig>,
) -> anyhow::Result<()> {
let mut vm = config
.with_windows_secure_boot_template()
.run_without_agent()
.await?;
assert_eq!(vm.wait_for_boot_event().await?, FirmwareEvent::BootFailed);
assert_eq!(vm.wait_for_teardown().await?, HaltReason::PowerOff);
Ok(())
}

/// Basic boot test for guests that are expected to reboot
// TODO: Remove this test and other enable Windows 11 ARM OpenVMM tests
// once we figure out how to get the guest to not reboot via IMC or other
Expand Down
Loading