Skip to content
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

EFI & Secure Boot #155

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ Style/SignalException:

Metrics/ClassLength:
Enabled: false

Metrics/BlockLength:
Exclude:
- tests/**/*.rb
24 changes: 23 additions & 1 deletion lib/fog/libvirt/models/compute/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class Server < Fog::Compute::Server

attribute :cpus
attribute :cputime
attribute :firmware
attribute :firmware_features
attribute :secure_boot
attribute :loader

Choose a reason for hiding this comment

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

Note to myself:
The loader element points to a specific boot binary (BIOS/UEFI), while firmware refers to the type or particular features used to configure or load the guest VM.

attribute :os_type
attribute :memory_size
attribute :max_memory_size
Expand Down Expand Up @@ -287,14 +291,31 @@ def to_xml
end

xml.vcpu(cpus)
xml.os do
os_tags = {}

os_tags[:firmware] = firmware if firmware == 'efi'

xml.os(**os_tags) do
type = xml.type(os_type, :arch => arch)
type[:machine] = "q35" if ["i686", "x86_64"].include?(arch)

boot_order.each do |dev|
xml.boot(:dev => dev)
end

loader&.each do |key, value|
xml.loader(key => value)
end

if firmware == "efi" && firmware_features&.any?
xml.firmware do
firmware_features.each_pair do |key, value|
xml.feature(:name => key, :enabled => value)
end
end
end
end

xml.features do
xml.acpi
xml.apic
Expand Down Expand Up @@ -539,6 +560,7 @@ def defaults
:guest_agent => true,
:video => {:type => "cirrus", :vram => 9216, :heads => 1},
:virtio_rng => {},
:firmware_features => { "secure-boot" => "no" },
}
end

Expand Down
22 changes: 21 additions & 1 deletion lib/fog/libvirt/requests/compute/list_domains.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ def boot_order xml
xml_elements(xml, "domain/os/boot", "dev")
end

def firmware(xml)
firmware_from_loader = xml_elements(xml, "domain/os/loader", "type").first

case firmware_from_loader
when 'pflash'
'efi'
when 'rom'
'bios'
else
xml_elements(xml, "domain/os", "firmware").first || 'bios'
end
end

# we rely on the fact that the secure attribute is only present when secure boot is enabled
def secure_boot_enabled?(xml)
Copy link
Contributor

Choose a reason for hiding this comment

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

Another thought I had: you can implement secure_boot? on the Server object that inspects the attributes.

xml_elements(xml, "domain/os/loader", "secure").first == 'yes'
end

def domain_interfaces xml
ifs = xml_elements(xml, "domain/devices/interface")
ifs.map { |i|
Expand Down Expand Up @@ -78,7 +96,9 @@ def domain_to_attributes(dom)
:boot_order => boot_order(dom.xml_desc),
:nics => domain_interfaces(dom.xml_desc),
:volumes_path => domain_volumes(dom.xml_desc),
:state => states[dom.info.state]
:state => states[dom.info.state],
:firmware => firmware(dom.xml_desc),
:secure_boot => secure_boot_enabled?(dom.xml_desc),
}
rescue ::Libvirt::RetrieveError, ::Libvirt::Error
# Catch libvirt exceptions to avoid race conditions involving
Expand Down
43 changes: 43 additions & 0 deletions tests/libvirt/models/compute/server_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
attributes = [ :id,
:cpus,
:cputime,
:firmware,
:firmware_features,
:secure_boot,
:loader,
:os_type,
:memory_size,
:max_memory_size,
Expand Down Expand Up @@ -67,6 +71,7 @@

test('be a kind of Fog::Libvirt::Compute::Server') { server.kind_of? Fog::Libvirt::Compute::Server }
tests("serializes to xml") do
test("without firmware") { server.to_xml.include?("<os>") }
test("with memory") { server.to_xml.match?(%r{<memory>\d+</memory>}) }
test("with disk of type file") do
xml = server.to_xml
Expand All @@ -86,5 +91,43 @@
end
test("with q35 machine type on x86_64") { server.to_xml.match?(%r{<type arch="x86_64" machine="q35">hvm</type>}) }
end
test("with efi firmware") do
server = Fog::Libvirt::Compute::Server.new(
{
:firmware => "efi",
:nics => [],
:volumes => []
}
)
xml = server.to_xml

os_firmware = xml.include?('<os firmware="efi">')
secure_boot = xml.include?('<feature name="secure-boot" enabled="no"/>')
loader = !xml.include?('<loader secure="yes"/>')

os_firmware && secure_boot && loader
end
test("with secure boot enabled") do
server = Fog::Libvirt::Compute::Server.new(
{
:firmware => "efi",
:firmware_features => {
"secure-boot" => "yes",
"enrolled-keys" => "yes"
},
:loader => { "secure" => "yes" },
:nics => [],
:volumes => []
}
)
xml = server.to_xml

os_firmware = xml.include?('<os firmware="efi">')
secure_boot = xml.include?('<feature name="secure-boot" enabled="yes"/>')
enrolled_keys = xml.include?('<feature name="enrolled-keys" enabled="yes"/>')
loader = xml.include?('<loader secure="yes"/>')

os_firmware && secure_boot && enrolled_keys && loader
end
end
end