Skip to content

Commit e4e732b

Browse files
committed
WIP: Add efi and secureboot support
- secure boot support is currently limited to redhat derived distros as the ubuntu packages don't provide UefiShell.iso. - Although the upgrading of qemu packages is more suited to the ansible-role-libvirt-host role, we need to know the location of the UEFI firmware images to setup the virtual machines. As we somtimes require custom packages this makes it hard to know upfront what the path will be.
1 parent 435ea81 commit e4e732b

File tree

9 files changed

+188
-0
lines changed

9 files changed

+188
-0
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ Role Variables
2727

2828
- `libvirt_vm_arch`: CPU architecture, default is `x86_64`.
2929

30+
- `libvirt_vm_enable_efi_support`: Whether to enable EFI support. By default this
31+
will enabled if any of the declared VMs specify the boot_firmware as efi. The
32+
certificates and keys needed to enable secure boot are currently only installed
33+
on RedHat derived distributions. See: [qemu-ovmf-secureboot](https://github.com/puiterwijk/qemu-ovmf-secureboot)
34+
for details of the certificates and keys that are installed.
35+
3036
- `libvirt_vms`: list of VMs to be created/destroyed. Each one may have the
3137
following attributes:
3238

@@ -91,6 +97,8 @@ Role Variables
9197

9298
- `autostart`: Whether to start the VM when the host starts up. Default is
9399
`true`.
100+
101+
- `boot_firmware`: Can be one of: `bios`, or `efi`. Defaults to `bios`.
94102

95103

96104
N.B. the following variables are deprecated: `libvirt_vm_state`,

defaults/main.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ libvirt_vm_engine:
2424
# correct emulator to use.
2525
libvirt_vm_emulator:
2626

27+
# This is where https://github.com/puiterwijk/qemu-ovmf-secureboot will be
28+
# checked out to.
29+
libvirt_ovmf_vars_generator_checkout_path: "/opt/qemu-ovmf-secureboot"
30+
31+
# Where to output the generated variable store
32+
libvirt_ovmf_vars_generator_output_path: "{{ libvirt_ovmf_vars_generator_checkout_path }}"
33+
34+
# Prefix of generated variable file name. The checksum of the input will be appended.
35+
libvirt_ovmf_vars_generator_output_prefix: ovmf_vars_enrolled_
36+
37+
# Whether to enable EFI support.
38+
libvirt_vm_enable_efi_support: "{{ 'efi' in (libvirt_vms | map(attribute='boot_firmware') | map('lower')) }}"
39+
2740
# A list of specifications of VMs to be created.
2841
# For backwards compatibility, libvirt_vms defaults to a singleton list using
2942
# the values of the deprecated variables below.
@@ -56,6 +69,9 @@ libvirt_vms:
5669
# Path to console log file.
5770
console_log_path: "{{ libvirt_vm_console_log_path }}"
5871

72+
# May be one of: bios, or efi.
73+
boot_firmware: bios
74+
5975

6076
### DEPRECATED ###
6177
# Use the above settings for each item within `libvirt_vms`, instead of the

tasks/main.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
---
2+
3+
- include_tasks: setup.yml
4+
25
- include_tasks: autodetect.yml
36
# We don't need to know the engine and emulator if we're not creating any new
47
# VMs.
@@ -26,6 +29,9 @@
2629
interfaces: "{{ vm.interfaces | default([], true) }}"
2730
start: "{{ vm.start | default(true) }}"
2831
autostart: "{{ vm.autostart | default(true) }}"
32+
boot_firmware: "{{ vm.boot_firmware | default('bios', true) | lower }}"
33+
enable_feature_acpi_default: "{{ true if boot_firmware == 'efi' else false }}"
34+
enable_feature_acpi: "{{ vm.enable_feature_acpi | default(enable_feature_acpi_default, true) }}"
2935
with_items: "{{ libvirt_vms }}"
3036
loop_control:
3137
loop_var: vm

tasks/prepare_secure_boot.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
3+
- name: Gather os specific variables
4+
include_vars: "{{ item }}"
5+
with_first_found:
6+
- files:
7+
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml"
8+
- "{{ ansible_distribution }}.yml"
9+
- "{{ ansible_os_family }}.yml"
10+
skip: true
11+
tags: vars
12+
13+
- name: Ensure ovmf generator checkout directory is owned by ansible_user
14+
file:
15+
path: "{{ libvirt_ovmf_vars_generator_checkout_path }}"
16+
owner: "{{ ansible_user }}"
17+
state: directory
18+
become: true
19+
20+
- name: Clone ovfm-vars generator
21+
git:
22+
repo: 'https://github.com/puiterwijk/qemu-ovmf-secureboot'
23+
dest: "{{ libvirt_ovmf_vars_generator_checkout_path }}"
24+
update: yes
25+
26+
- name: Get checksum of template OVMF vars
27+
# We need to keep the generated vars in sync with templated version.
28+
# if the OVMF package is updated - we should update a new version with
29+
# the signing keys enrolled.
30+
stat:
31+
path: "{{ libvirt_vm_ovmf_efi_variable_store_path }}"
32+
get_checksum: true
33+
checksum_algorithm: sha256
34+
register: ovmf_template
35+
36+
- name: Register path of generated variables
37+
set_fact:
38+
ovmf_enrolled_variables_path: "\
39+
{{ libvirt_ovmf_vars_generator_output_path }}/\
40+
{{ libvirt_ovmf_vars_generator_output_prefix }}\
41+
{{ ovmf_template.stat.checksum }}"
42+
43+
- name: Check to see if we have generated these vars before
44+
stat:
45+
path: "{{ ovmf_enrolled_variables_path }}"
46+
register: generated_ovmf
47+
48+
- name: Run OVMF vars generator
49+
command: >
50+
python {{ libvirt_ovmf_vars_generator_checkout_path}}/ovmf-vars-generator
51+
--ovmf-binary {{ libvirt_vm_ovmf_efi_firmware_path }}
52+
--uefi-shell-iso {{ libvirt_vm_ovmf_uefi_shell_iso_path }}
53+
--ovmf-template-vars {{ libvirt_vm_ovmf_efi_variable_store_path }}
54+
--qemu-binary {{ libvirt_vm_emulator }}
55+
{% if libvirt_vm_engine == 'kvm' %}--enable-kvm{% endif %}
56+
--skip-testing
57+
--no-download
58+
{{ ovmf_enrolled_variables_path }}
59+
when: not generated_ovmf.stat.exists

tasks/setup.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
3+
- name: Gather os specific variables
4+
include_vars: "{{ item }}"
5+
with_first_found:
6+
- files:
7+
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml"
8+
- "{{ ansible_distribution }}.yml"
9+
- "{{ ansible_os_family }}.yml"
10+
skip: true
11+
tags: vars
12+
13+
- name: Install custom yum repositories
14+
# Although argument splatting is marked as deprecated:
15+
#
16+
# [DEPRECATION WARNING]: Using variables for task params is unsafe,
17+
# especially if the variables come from an external source like facts. This
18+
# feature will be removed in a future release.
19+
#
20+
# The core team had a a change of heart and it is actually being preserved:
21+
# https://github.com/ansible/ansible/pull/43798
22+
yum_repository: "{{ item }}"
23+
loop: "{{ libvirt_vm_custom_yum_repos | default({}) }}"
24+
become: true
25+
26+
- name: Install custom packages
27+
package:
28+
name: "{{ item }}"
29+
state: present
30+
loop: "{{ libvirt_vm_extra_packages }}"
31+

tasks/vm.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
interface: "{{ item }}"
2727
with_items: "{{ interfaces }}"
2828

29+
- name: Create secure boot template variables
30+
include_tasks: prepare_secure_boot.yml
31+
when:
32+
- boot_firmware == "efi"
33+
- libvirt_vm_ovmf_uefi_shell_iso_path is defined
34+
2935
- name: Ensure the VM is defined
3036
virt:
3137
name: "{{ vm.name }}"

templates/vm.xml.j2

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,18 @@
1212
<boot dev='hd'/>
1313
<boot dev='network'/>
1414
<bios useserial='yes'/>
15+
{% if boot_firmware == "efi" %}
16+
{# NOTE: pflash requires qemu 1.6 or newer. There are alternatives for older versions, but
17+
they do not work with secure boot. See OVMF readme for an overview #}
18+
<loader readonly='yes' type='pflash'>{{ libvirt_vm_ovmf_efi_firmware_path }}</loader>
19+
<loader readonly='no' type='pflash'>{{ ovmf_enrolled_variables_path | default(libvirt_vm_ovmf_efi_variable_store_path) }}</loader>
20+
{% endif %}
1521
</os>
22+
<features>
23+
{% if enable_feature_acpi %}
24+
<acpi/>
25+
{% endif %}
26+
</features>
1627
<cpu{% if cpu_mode is not none %} mode='{{ cpu_mode }}'{% endif %}>
1728
<model fallback='allow'/>
1829
</cpu>

vars/Debian.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,18 @@ libvirt_vm_log_owner: libvirt-qemu
55

66
# The environment passed to virt_volume.sh
77
libvirt_vm_volume_creation_env: {}
8+
9+
# Packages that are only necessary if you require EFI support
10+
libvirt_vm_extra_packages_efi:
11+
- ovmf
12+
13+
# List of extra packages to install
14+
libvirt_vm_extra_packages: "{{ [] + (libvirt_vm_extra_packages_efi if libvirt_vm_enable_efi_support else []) | unique }}"
15+
16+
# Path to template OVMF efi variable store. A copy will be created
17+
# for each VM created.
18+
libvirt_vm_ovmf_efi_variable_store_path: /usr/share/OVMF/OVMF_VARS.fd
19+
20+
# Path to OVMF efi firmware
21+
libvirt_vm_ovmf_efi_firmware_path: /usr/share/OVMF/OVMF_CODE.fd
22+

vars/RedHat.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,39 @@ libvirt_vm_log_owner: qemu
77
libvirt_vm_volume_creation_env:
88
VOLUME_GROUP: qemu
99
VOLUME_OWNER: qemu
10+
11+
# Packages that are only necessary if you require EFI support
12+
libvirt_vm_extra_packages_efi:
13+
- edk2.git-ovmf-x64 # Official OVMF package doesn't boot (CentOS 7.5)
14+
- qemu-kvm-ev # Need smm support for secure boot
15+
16+
# List of extra packages to install
17+
libvirt_vm_extra_packages: "{{ [] + (libvirt_vm_extra_packages_efi if libvirt_vm_enable_efi_support else []) | unique }}"
18+
19+
# Path to template OVMF efi variable store. A copy will be created
20+
# for each VM created.
21+
# note(wszumski): official package path is /usr/share/OVMF/OVMF_VARS.fd
22+
libvirt_vm_ovmf_efi_variable_store_path: /usr/share/edk2.git/ovmf-x64/OVMF_VARS-need-smm.fd
23+
24+
# Path to OVMF efi firmware
25+
# note(wszumski): official package path is /usr/share/OVMF/OVMF_CODE.secboot.fd
26+
libvirt_vm_ovmf_efi_firmware_path: /usr/share/edk2.git/ovmf-x64/OVMF_CODE-need-smm.fd
27+
28+
# Path to iso containing signing keys
29+
# note(wszumski): official package path is /usr/share/OVMF/UefiShell.iso
30+
libvirt_vm_ovmf_uefi_shell_iso_path: /usr/share/edk2.git/ovmf-x64/UefiShell.iso
31+
32+
# Add custom repository as OVMF package seems to be broken
33+
libvirt_vm_custom_yum_repos_efi:
34+
- name: qemu-firmware-jenkins
35+
description: upstream OVMF firmware images
36+
baseurl: https://www.kraxel.org/repos/jenkins/
37+
gpgcheck: no
38+
# Need an updated version of qemu with smm support
39+
- name: centos-qemu-ev
40+
description: CentOS-$releasever - QEMU EV
41+
baseurl: http://mirror.centos.org/$contentdir/$releasever/virt/$basearch/kvm-common/
42+
gpgcheck: yes
43+
44+
libvirt_vm_custom_yum_repos: "{{ [] + (libvirt_vm_custom_yum_repos_efi if libvirt_vm_enable_efi_support else []) | unique }}"
45+

0 commit comments

Comments
 (0)