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

WIP: Add efi and secureboot support #12

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ Role Variables

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

- `libvirt_vm_enable_efi_support`: Whether to enable EFI support. By default this
will enabled if any of the declared VMs specify the boot_firmware as efi. The
certificates and keys needed to enable secure boot are currently only installed
on RedHat derived distributions. See: [qemu-ovmf-secureboot](https://github.com/puiterwijk/qemu-ovmf-secureboot)
for details of the certificates and keys that are installed.

- `libvirt_vms`: list of VMs to be created/destroyed. Each one may have the
following attributes:

Expand Down Expand Up @@ -91,6 +97,8 @@ Role Variables

- `autostart`: Whether to start the VM when the host starts up. Default is
`true`.

- `boot_firmware`: Can be one of: `bios`, or `efi`. Defaults to `bios`.


N.B. the following variables are deprecated: `libvirt_vm_state`,
Expand Down
16 changes: 16 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ libvirt_vm_engine:
# correct emulator to use.
libvirt_vm_emulator:

# This is where https://github.com/puiterwijk/qemu-ovmf-secureboot will be
# checked out to.
libvirt_ovmf_vars_generator_checkout_path: "/opt/qemu-ovmf-secureboot"

# Where to output the generated variable store
libvirt_ovmf_vars_generator_output_path: "{{ libvirt_ovmf_vars_generator_checkout_path }}"

# Prefix of generated variable file name. The checksum of the input will be appended.
libvirt_ovmf_vars_generator_output_prefix: ovmf_vars_enrolled_

# Whether to enable EFI support.
libvirt_vm_enable_efi_support: "{{ 'efi' in (libvirt_vms | map(attribute='boot_firmware') | map('lower')) }}"

# A list of specifications of VMs to be created.
# For backwards compatibility, libvirt_vms defaults to a singleton list using
# the values of the deprecated variables below.
Expand Down Expand Up @@ -56,6 +69,9 @@ libvirt_vms:
# Path to console log file.
console_log_path: "{{ libvirt_vm_console_log_path }}"

# May be one of: bios, or efi.
boot_firmware: bios


### DEPRECATED ###
# Use the above settings for each item within `libvirt_vms`, instead of the
Expand Down
9 changes: 6 additions & 3 deletions tasks/destroy-vm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
become: yes

- name: Ensure the VM is undefined
virt:
name: "{{ vm.name }}"
command: undefine
# note(wszumski): the virt module does not seem to support
# removing vms with nvram defined - as a workaround, use the
# virsh cli directly. It may be better to detect if dumpxml
# actually contains an nvram element rather than relying on
# boot_firmware having the correct value.
command: virsh -c qemu:///system undefine{% if boot_firmware == 'efi' %} --nvram{% endif %} {{ vm.name }}
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit clunky :( Looks like it's necessary though

become: yes
when: vm.name in result.list_vms
8 changes: 8 additions & 0 deletions tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
---

- include_tasks: setup.yml

- include_tasks: autodetect.yml
# We don't need to know the engine and emulator if we're not creating any new
# VMs.
Expand Down Expand Up @@ -26,6 +29,9 @@
interfaces: "{{ vm.interfaces | default([], true) }}"
start: "{{ vm.start | default(true) }}"
autostart: "{{ vm.autostart | default(true) }}"
boot_firmware: "{{ vm.boot_firmware | default('bios', true) | lower }}"
enable_feature_acpi_default: "{{ true if boot_firmware == 'efi' else false }}"
enable_feature_acpi: "{{ vm.enable_feature_acpi | default(enable_feature_acpi_default, true) }}"
with_items: "{{ libvirt_vms }}"
loop_control:
loop_var: vm
Expand All @@ -40,6 +46,8 @@
when: (vm.state | default('present', true)) == 'absent'

- include_tasks: destroy-vm.yml
vars:
boot_firmware: "{{ vm.boot_firmware | default('bios', true) | lower }}"
with_items: "{{ libvirt_vms }}"
loop_control:
loop_var: vm
Expand Down
59 changes: 59 additions & 0 deletions tasks/prepare_secure_boot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
Copy link
Member

Choose a reason for hiding this comment

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

micronit: other task file names use dashes

Copy link
Member

Choose a reason for hiding this comment

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

This file could do with a comment to explain what's going on.


- name: Gather os specific variables
include_vars: "{{ item }}"
with_first_found:
- files:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml"
- "{{ ansible_distribution }}.yml"
- "{{ ansible_os_family }}.yml"
skip: true
tags: vars

- name: Ensure ovmf generator checkout directory is owned by ansible_user
file:
path: "{{ libvirt_ovmf_vars_generator_checkout_path }}"
owner: "{{ ansible_user }}"
state: directory
become: true

- name: Clone ovfm-vars generator
git:
repo: 'https://github.com/puiterwijk/qemu-ovmf-secureboot'
dest: "{{ libvirt_ovmf_vars_generator_checkout_path }}"
update: yes

- name: Get checksum of template OVMF vars
# We need to keep the generated vars in sync with templated version.
# if the OVMF package is updated - we should update a new version with
# the signing keys enrolled.
stat:
path: "{{ libvirt_vm_ovmf_efi_variable_store_path }}"
get_checksum: true
checksum_algorithm: sha256
register: ovmf_template

- name: Register path of generated variables
set_fact:
ovmf_enrolled_variables_path: "\
{{ libvirt_ovmf_vars_generator_output_path }}/\
{{ libvirt_ovmf_vars_generator_output_prefix }}\
{{ ovmf_template.stat.checksum }}"

- name: Check to see if we have generated these vars before
stat:
path: "{{ ovmf_enrolled_variables_path }}"
register: generated_ovmf

- name: Run OVMF vars generator
Copy link
Member

Choose a reason for hiding this comment

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

Might be good if this role was just provided with a path to this script, which could be installed/cloned by libvirt-host.

command: >
python {{ libvirt_ovmf_vars_generator_checkout_path}}/ovmf-vars-generator
--ovmf-binary {{ libvirt_vm_ovmf_efi_firmware_path }}
--uefi-shell-iso {{ libvirt_vm_ovmf_uefi_shell_iso_path }}
--ovmf-template-vars {{ libvirt_vm_ovmf_efi_variable_store_path }}
--qemu-binary {{ libvirt_vm_emulator }}
{% if libvirt_vm_engine == 'kvm' %}--enable-kvm{% endif %}
--skip-testing
--no-download
{{ ovmf_enrolled_variables_path }}
when: not generated_ovmf.stat.exists
31 changes: 31 additions & 0 deletions tasks/setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
Copy link
Member

Choose a reason for hiding this comment

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

This stuff probably belongs in libvirt-host.


- name: Gather os specific variables
include_vars: "{{ item }}"
with_first_found:
- files:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml"
- "{{ ansible_distribution }}.yml"
- "{{ ansible_os_family }}.yml"
skip: true
tags: vars
Copy link
Member

Choose a reason for hiding this comment

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

Probably nicer to do this in main.yml.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't sure if we should make it so the playbooks can be run separately.

Copy link
Member

Choose a reason for hiding this comment

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

Fair point.


- name: Install custom yum repositories
# Although argument splatting is marked as deprecated:
#
# [DEPRECATION WARNING]: Using variables for task params is unsafe,
# especially if the variables come from an external source like facts. This
# feature will be removed in a future release.
#
# The core team had a a change of heart and it is actually being preserved:
# https://github.com/ansible/ansible/pull/43798
yum_repository: "{{ item }}"
loop: "{{ libvirt_vm_custom_yum_repos | default({}) }}"
Copy link
Member

Choose a reason for hiding this comment

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

nit: default should be a list

become: true

- name: Install custom packages
package:
name: "{{ item }}"
state: present
loop: "{{ libvirt_vm_extra_packages }}"

6 changes: 6 additions & 0 deletions tasks/vm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
interface: "{{ item }}"
with_items: "{{ interfaces }}"

- name: Create secure boot template variables
include_tasks: prepare_secure_boot.yml
when:
- boot_firmware == "efi"
- libvirt_vm_ovmf_uefi_shell_iso_path is defined
Copy link
Member

Choose a reason for hiding this comment

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

Not supported on Debian?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Debian packages don't contain the UEFI shell ISO which contains the EnrollDefaultKeys.efi binary. WE would either need to build this from source or obtain it by some other means.


- name: Ensure the VM is defined
virt:
name: "{{ vm.name }}"
Expand Down
11 changes: 11 additions & 0 deletions templates/vm.xml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@
<boot dev='hd'/>
<boot dev='network'/>
<bios useserial='yes'/>
{% if boot_firmware == "efi" %}
{# NOTE: pflash requires qemu 1.6 or newer. There are alternatives for older versions, but
they do not work with secure boot. See OVMF readme for an overview #}
<loader readonly='yes' type='pflash'>{{ libvirt_vm_ovmf_efi_firmware_path }}</loader>
<loader readonly='no' type='pflash'>{{ ovmf_enrolled_variables_path | default(libvirt_vm_ovmf_efi_variable_store_path) }}</loader>
{% endif %}
</os>
<features>
{% if enable_feature_acpi %}
<acpi/>
{% endif %}
</features>
<cpu{% if cpu_mode is not none %} mode='{{ cpu_mode }}'{% endif %}>
<model fallback='allow'/>
</cpu>
Expand Down
15 changes: 15 additions & 0 deletions vars/Debian.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,18 @@ libvirt_vm_log_owner: libvirt-qemu

# The environment passed to virt_volume.sh
libvirt_vm_volume_creation_env: {}

# Packages that are only necessary if you require EFI support
libvirt_vm_extra_packages_efi:
- ovmf

# List of extra packages to install
libvirt_vm_extra_packages: "{{ [] + (libvirt_vm_extra_packages_efi if libvirt_vm_enable_efi_support else []) | unique }}"
Copy link
Member

Choose a reason for hiding this comment

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

Do you need the [] + here?


# Path to template OVMF efi variable store. A copy will be created
# for each VM created.
libvirt_vm_ovmf_efi_variable_store_path: /usr/share/OVMF/OVMF_VARS.fd

# Path to OVMF efi firmware
libvirt_vm_ovmf_efi_firmware_path: /usr/share/OVMF/OVMF_CODE.fd

36 changes: 36 additions & 0 deletions vars/RedHat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,39 @@ libvirt_vm_log_owner: qemu
libvirt_vm_volume_creation_env:
VOLUME_GROUP: qemu
VOLUME_OWNER: qemu

# Packages that are only necessary if you require EFI support
libvirt_vm_extra_packages_efi:
- edk2.git-ovmf-x64 # Official OVMF package doesn't boot (CentOS 7.5)
Copy link
Member

Choose a reason for hiding this comment

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

Interesting OVMF was just proposed for addition to the nova containers in kolla: https://review.openstack.org/608579

- qemu-kvm-ev # Need smm support for secure boot
Copy link
Member

Choose a reason for hiding this comment

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

Does this require hardware virtualisation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe SMM emulation, which is needed for secure boot, requires either KVM or TCG acceleration to be enabled. So, by enabling TCG we should be able to use secure-boot without KVM - but I will need to test this to be sure.


# List of extra packages to install
libvirt_vm_extra_packages: "{{ [] + (libvirt_vm_extra_packages_efi if libvirt_vm_enable_efi_support else []) | unique }}"

# Path to template OVMF efi variable store. A copy will be created
# for each VM created.
# note(wszumski): official package path is /usr/share/OVMF/OVMF_VARS.fd
libvirt_vm_ovmf_efi_variable_store_path: /usr/share/edk2.git/ovmf-x64/OVMF_VARS-need-smm.fd

# Path to OVMF efi firmware
# note(wszumski): official package path is /usr/share/OVMF/OVMF_CODE.secboot.fd
libvirt_vm_ovmf_efi_firmware_path: /usr/share/edk2.git/ovmf-x64/OVMF_CODE-need-smm.fd

# Path to iso containing signing keys
# note(wszumski): official package path is /usr/share/OVMF/UefiShell.iso
libvirt_vm_ovmf_uefi_shell_iso_path: /usr/share/edk2.git/ovmf-x64/UefiShell.iso

# Add custom repository as OVMF package seems to be broken
libvirt_vm_custom_yum_repos_efi:
- name: qemu-firmware-jenkins
description: upstream OVMF firmware images
baseurl: https://www.kraxel.org/repos/jenkins/
gpgcheck: no
# Need an updated version of qemu with smm support
- name: centos-qemu-ev
description: CentOS-$releasever - QEMU EV
baseurl: http://mirror.centos.org/$contentdir/$releasever/virt/$basearch/kvm-common/
gpgcheck: yes

libvirt_vm_custom_yum_repos: "{{ [] + (libvirt_vm_custom_yum_repos_efi if libvirt_vm_enable_efi_support else []) | unique }}"