mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2025-02-25 18:55:27 -06:00
Add ability to use emulated tpm (#1166)
Qemu has supported tpm 2 and the ability to start swtpm. Additionally
it expands the tests for the tpm configuration to ensure that only when
the options cause a change to the domain XML will the domain be updated
on a subsequent start. This change just allows passing through the
necessary config.
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.tpm_model = "tpm-crb"
libvirt.tpm_type = "emulator"
libvirt.tpm_version = "2.0"
end
end
closes #965
This commit is contained in:
28
README.md
28
README.md
@@ -504,6 +504,7 @@ end
|
||||
* `tpm_model` - The model of the TPM to which you wish to connect.
|
||||
* `tpm_type` - The type of TPM device to which you are connecting.
|
||||
* `tpm_path` - The path to the TPM device on the host system.
|
||||
* `tpm_version` - The TPM version to use.
|
||||
* `dtb` - The device tree blob file, mostly used for non-x86 platforms. In case
|
||||
the device tree isn't added in-line to the kernel, it can be manually
|
||||
specified here.
|
||||
@@ -579,6 +580,7 @@ defined domain:
|
||||
* `tpm_model` - Updated
|
||||
* `tpm_type` - Updated
|
||||
* `tpm_path` - Updated
|
||||
* `tpm_version` - Updated
|
||||
|
||||
## Networks
|
||||
|
||||
@@ -1408,13 +1410,14 @@ Modern versions of Libvirt support connecting to TPM devices on the host
|
||||
system. This allows you to enable Trusted Boot Extensions, among other
|
||||
features, on your guest VMs.
|
||||
|
||||
In general, you will only need to modify the `tpm_path` variable in your guest
|
||||
configuration. However, advanced usage, such as the application of a Software
|
||||
TPM, may require modifying the `tpm_model` and `tpm_type` variables.
|
||||
To passthrough a hardware TPM, you will generally only need to modify the
|
||||
`tpm_path` variable in your guest configuration. However, advanced usage,
|
||||
such as the application of a Software TPM, may require modifying the
|
||||
`tpm_model`, `tpm_type` and `tpm_version` variables.
|
||||
|
||||
The TPM options will only be used if you specify a TPM path. Declarations of
|
||||
any TPM options without specifying a path will result in those options being
|
||||
ignored.
|
||||
The TPM options will only be used if you specify a TPM path or version.
|
||||
Declarations of any TPM options without specifying a path or version will
|
||||
result in those options being ignored.
|
||||
|
||||
Here is an example of using the TPM options:
|
||||
|
||||
@@ -1428,6 +1431,19 @@ Vagrant.configure("2") do |config|
|
||||
end
|
||||
```
|
||||
|
||||
It's also possible for Libvirt to start an emulated TPM device on the host.
|
||||
Requires `swtpm` and `swtpm-tools`
|
||||
|
||||
```ruby
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.provider :libvirt do |libvirt|
|
||||
libvirt.tpm_model = "tpm-crb"
|
||||
libvirt.tpm_type = "emulator"
|
||||
libvirt.tpm_version = "2.0"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Libvirt communication channels
|
||||
|
||||
For certain functionality to be available within a guest, a private
|
||||
|
||||
@@ -81,6 +81,7 @@ module VagrantPlugins
|
||||
@tpm_model = config.tpm_model
|
||||
@tpm_type = config.tpm_type
|
||||
@tpm_path = config.tpm_path
|
||||
@tpm_version = config.tpm_version
|
||||
|
||||
# Boot order
|
||||
@boot_order = config.boot_order
|
||||
@@ -254,7 +255,13 @@ module VagrantPlugins
|
||||
env[:ui].info(" -- Video VRAM: #{@video_vram}")
|
||||
env[:ui].info(" -- Sound Type: #{@sound_type}")
|
||||
env[:ui].info(" -- Keymap: #{@keymap}")
|
||||
env[:ui].info(" -- TPM Backend: #{@tpm_type}")
|
||||
if @tpm_type == 'emulator'
|
||||
env[:ui].info(" -- TPM Model: #{@tpm_model}")
|
||||
env[:ui].info(" -- TPM Version: #{@tpm_version}")
|
||||
else
|
||||
env[:ui].info(" -- TPM Path: #{@tpm_path}")
|
||||
end
|
||||
|
||||
@boot_order.each do |device|
|
||||
env[:ui].info(" -- Boot device: #{device}")
|
||||
|
||||
@@ -193,36 +193,31 @@ module VagrantPlugins
|
||||
end
|
||||
|
||||
# TPM
|
||||
if [config.tpm_path, config.tpm_version].any?
|
||||
if config.tpm_path
|
||||
raise Errors::FogCreateServerError, 'The TPM Path must be fully qualified' unless config.tpm_path[0].chr == '/'
|
||||
end
|
||||
|
||||
tpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
|
||||
if tpm.nil?
|
||||
@logger.debug "tpm created from previously not defined"
|
||||
descr_changed = true
|
||||
tpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices'))
|
||||
tpm.attributes['model'] = config.tpm_model
|
||||
tpm_backend_type = tpm.add_element('backend')
|
||||
tpm_backend_type.attributes['type'] = config.tpm_type
|
||||
tpm_device_path = tpm_backend_type.add_element('device')
|
||||
tpm_device_path.attributes['path'] = config.tpm_path
|
||||
else
|
||||
if tpm.attributes['model'] != config.tpm_model
|
||||
@logger.debug "tpm model updated from '#{tpm.attributes['model']}' to '#{config.tpm_model}'"
|
||||
descr_changed = true
|
||||
tpm.attributes['model'] = config.tpm_model
|
||||
end
|
||||
backend = tpm.elements['backend']
|
||||
if backend.attributes['type'] != config.tpm_type
|
||||
@logger.debug "tpm type updated from '#{backend.attributes['type']}' to '#{config.tpm_type}'"
|
||||
descr_changed = true
|
||||
# just build the tpm element every time
|
||||
# check at the end if it is different
|
||||
oldtpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
|
||||
REXML::XPath.first(xml_descr, '/domain/devices').delete_element("tpm")
|
||||
newtpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices'))
|
||||
|
||||
newtpm.attributes['model'] = config.tpm_model
|
||||
backend = newtpm.add_element('backend')
|
||||
backend.attributes['type'] = config.tpm_type
|
||||
|
||||
case config.tpm_type
|
||||
when 'emulator'
|
||||
backend.attributes['version'] = config.tpm_version
|
||||
when 'passthrough'
|
||||
backend.add_element('device').attributes['path'] = config.tpm_path
|
||||
end
|
||||
if backend.elements['device'].attributes['path'] != config.tpm_path
|
||||
@logger.debug "tpm path updated from '#{backend.elements['device'].attributes['path']}' to '#{config.tpm_path}'"
|
||||
|
||||
unless "'#{newtpm}'".eql? "'#{oldtpm}'"
|
||||
@logger.debug "tpm config changed"
|
||||
descr_changed = true
|
||||
backend.elements['device'].attributes['path'] = config.tpm_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ module VagrantPlugins
|
||||
attr_accessor :tpm_model
|
||||
attr_accessor :tpm_type
|
||||
attr_accessor :tpm_path
|
||||
attr_accessor :tpm_version
|
||||
|
||||
# Sets the max number of NICs that can be created
|
||||
# Default set to 8. Don't change the default unless you know
|
||||
@@ -245,6 +246,7 @@ module VagrantPlugins
|
||||
@tpm_model = UNSET_VALUE
|
||||
@tpm_type = UNSET_VALUE
|
||||
@tpm_path = UNSET_VALUE
|
||||
@tpm_version = UNSET_VALUE
|
||||
|
||||
@nic_adapter_count = UNSET_VALUE
|
||||
|
||||
@@ -781,6 +783,7 @@ module VagrantPlugins
|
||||
@tpm_model = 'tpm-tis' if @tpm_model == UNSET_VALUE
|
||||
@tpm_type = 'passthrough' if @tpm_type == UNSET_VALUE
|
||||
@tpm_path = nil if @tpm_path == UNSET_VALUE
|
||||
@tpm_version = nil if @tpm_version == UNSET_VALUE
|
||||
@nic_adapter_count = 8 if @nic_adapter_count == UNSET_VALUE
|
||||
@emulator_path = nil if @emulator_path == UNSET_VALUE
|
||||
|
||||
|
||||
@@ -257,11 +257,13 @@
|
||||
<% end %>
|
||||
<% end -%>
|
||||
|
||||
<% if @tpm_path -%>
|
||||
<% if @tpm_path || @tpm_version -%>
|
||||
<%# TPM Device -%>
|
||||
<tpm model='<%= @tpm_model %>'>
|
||||
<backend type='<%= @tpm_type %>'>
|
||||
<backend type='<%= @tpm_type %>'<% if @tpm_version %> version='<%= @tpm_version %>'<% end %>>
|
||||
<% if @tpm_path -%>
|
||||
<device path='<%= @tpm_path %>'/>
|
||||
<% end -%>
|
||||
</backend>
|
||||
</tpm>
|
||||
<% end -%>
|
||||
|
||||
@@ -48,8 +48,19 @@ describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'tpm_path added' do
|
||||
context 'tpm' do
|
||||
let(:test_file) { 'default.xml' }
|
||||
|
||||
before do
|
||||
allow(libvirt_domain).to receive(:xml_desc).and_return(domain_xml)
|
||||
|
||||
allow(libvirt_domain).to receive(:max_memory).and_return(512*1024)
|
||||
allow(libvirt_domain).to receive(:num_vcpus).and_return(1)
|
||||
end
|
||||
|
||||
context 'passthrough tpm added' do
|
||||
let(:updated_test_file) { 'default_added_tpm_path.xml' }
|
||||
let(:vagrantfile_providerconfig) do
|
||||
<<-EOF
|
||||
@@ -61,7 +72,90 @@ describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
|
||||
|
||||
it 'should modify the domain tpm_path' do
|
||||
expect(libvirt_domain).to receive(:undefine)
|
||||
expect(logger).to receive(:debug).with('tpm created from previously not defined')
|
||||
expect(logger).to receive(:debug).with('tpm config changed')
|
||||
expect(servers).to receive(:create).with(xml: updated_domain_xml)
|
||||
expect(libvirt_domain).to receive(:autostart=)
|
||||
expect(domain).to receive(:start)
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'emulated tpm added' do
|
||||
let(:updated_test_file) { 'default_added_tpm_version.xml' }
|
||||
let(:vagrantfile_providerconfig) do
|
||||
<<-EOF
|
||||
libvirt.tpm_type = 'emulator'
|
||||
libvirt.tpm_model = 'tpm-crb'
|
||||
libvirt.tpm_version = '2.0'
|
||||
EOF
|
||||
end
|
||||
|
||||
it 'should modify the domain tpm_path' do
|
||||
expect(libvirt_domain).to receive(:undefine)
|
||||
expect(logger).to receive(:debug).with('tpm config changed')
|
||||
expect(servers).to receive(:create).with(xml: updated_domain_xml)
|
||||
expect(libvirt_domain).to receive(:autostart=)
|
||||
expect(domain).to receive(:start)
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'same passthrough tpm config' do
|
||||
let(:test_file) { 'default_added_tpm_path.xml' }
|
||||
let(:updated_test_file) { 'default_added_tpm_path.xml' }
|
||||
let(:vagrantfile_providerconfig) do
|
||||
<<-EOF
|
||||
libvirt.tpm_path = '/dev/tpm0'
|
||||
libvirt.tpm_type = 'passthrough'
|
||||
libvirt.tpm_model = 'tpm-tis'
|
||||
EOF
|
||||
end
|
||||
|
||||
it 'should execute without changing' do
|
||||
expect(logger).to_not receive(:debug)
|
||||
expect(libvirt_domain).to receive(:autostart=)
|
||||
expect(domain).to receive(:start)
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'same emulated tpm config' do
|
||||
let(:test_file) { 'default_added_tpm_version.xml' }
|
||||
let(:updated_test_file) { 'default_added_tpm_version.xml' }
|
||||
let(:vagrantfile_providerconfig) do
|
||||
<<-EOF
|
||||
libvirt.tpm_type = 'emulator'
|
||||
libvirt.tpm_model = 'tpm-crb'
|
||||
libvirt.tpm_version = '2.0'
|
||||
EOF
|
||||
end
|
||||
|
||||
it 'should execute without changing' do
|
||||
expect(logger).to_not receive(:debug)
|
||||
expect(libvirt_domain).to receive(:autostart=)
|
||||
expect(domain).to receive(:start)
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'change from passthrough to emulated' do
|
||||
let(:test_file) { 'default_added_tpm_path.xml' }
|
||||
let(:updated_test_file) { 'default_added_tpm_version.xml' }
|
||||
let(:vagrantfile_providerconfig) do
|
||||
<<-EOF
|
||||
libvirt.tpm_type = 'emulator'
|
||||
libvirt.tpm_model = 'tpm-crb'
|
||||
libvirt.tpm_version = '2.0'
|
||||
EOF
|
||||
end
|
||||
|
||||
it 'should modify the domain' do
|
||||
expect(libvirt_domain).to receive(:undefine)
|
||||
expect(logger).to receive(:debug).with('tpm config changed')
|
||||
expect(servers).to receive(:create).with(xml: updated_domain_xml)
|
||||
expect(libvirt_domain).to receive(:autostart=)
|
||||
expect(domain).to receive(:start)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<domain xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' type=''>
|
||||
<name/>
|
||||
<title/>
|
||||
<description/>
|
||||
<uuid/>
|
||||
<memory/>
|
||||
<vcpu>1</vcpu>
|
||||
|
||||
|
||||
<cpu mode='host-model'>
|
||||
<model fallback='allow'/>
|
||||
</cpu>
|
||||
|
||||
|
||||
<os>
|
||||
<type>hvm</type>
|
||||
<kernel/>
|
||||
<initrd/>
|
||||
<cmdline/>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<pae/>
|
||||
</features>
|
||||
<clock offset='utc'/>
|
||||
<devices>
|
||||
|
||||
|
||||
<serial type='pty'>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
<console type='pty'>
|
||||
<target port='0'/>
|
||||
</console>
|
||||
|
||||
|
||||
<input bus='ps2' type='mouse'/>
|
||||
|
||||
<graphics autoport='yes' keymap='en-us' listen='127.0.0.1' port='-1' type='vnc'/>
|
||||
<video>
|
||||
<model heads='1' type='cirrus' vram='9216'/>
|
||||
</video>
|
||||
|
||||
|
||||
<tpm model='tpm-crb'><backend type='emulator' version='2.0'/></tpm></devices>
|
||||
|
||||
</domain>
|
||||
@@ -105,4 +105,28 @@ describe 'templates/domain' do
|
||||
expect(domain.to_xml('domain')).to eq xml_expected
|
||||
end
|
||||
end
|
||||
|
||||
context 'when tpm 2.0 device is specified' do
|
||||
before do
|
||||
domain.tpm_version = '2.0'
|
||||
domain.tpm_type = 'emulator'
|
||||
domain.tpm_model = 'tpm-crb'
|
||||
end
|
||||
let(:test_file) { 'tpm/version_2.0.xml' }
|
||||
it 'renders template' do
|
||||
domain.finalize!
|
||||
expect(domain.to_xml('domain')).to eq xml_expected
|
||||
end
|
||||
end
|
||||
|
||||
context 'when tpm 1.2 device is implicitly used' do
|
||||
before do
|
||||
domain.tpm_path = '/dev/tpm0'
|
||||
end
|
||||
let(:test_file) { 'tpm/version_1.2.xml' }
|
||||
it 'renders template' do
|
||||
domain.finalize!
|
||||
expect(domain.to_xml('domain')).to eq xml_expected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
53
spec/unit/templates/tpm/version_1.2.xml
Normal file
53
spec/unit/templates/tpm/version_1.2.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<domain type='' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
|
||||
<name></name>
|
||||
<title></title>
|
||||
<description></description>
|
||||
<uuid></uuid>
|
||||
<memory></memory>
|
||||
<vcpu>1</vcpu>
|
||||
|
||||
|
||||
<cpu mode='host-model'>
|
||||
<model fallback='allow'></model>
|
||||
</cpu>
|
||||
|
||||
|
||||
<os>
|
||||
<type>hvm</type>
|
||||
<kernel></kernel>
|
||||
<initrd></initrd>
|
||||
<cmdline></cmdline>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<pae/>
|
||||
</features>
|
||||
<clock offset='utc'/>
|
||||
<devices>
|
||||
|
||||
|
||||
<serial type='pty'>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
<console type='pty'>
|
||||
<target port='0'/>
|
||||
</console>
|
||||
|
||||
|
||||
<input type='mouse' bus='ps2'/>
|
||||
|
||||
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us' />
|
||||
<video>
|
||||
<model type='cirrus' vram='9216' heads='1'/>
|
||||
</video>
|
||||
|
||||
|
||||
<tpm model='tpm-tis'>
|
||||
<backend type='passthrough'>
|
||||
<device path='/dev/tpm0'/>
|
||||
</backend>
|
||||
</tpm>
|
||||
</devices>
|
||||
|
||||
</domain>
|
||||
52
spec/unit/templates/tpm/version_2.0.xml
Normal file
52
spec/unit/templates/tpm/version_2.0.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<domain type='' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
|
||||
<name></name>
|
||||
<title></title>
|
||||
<description></description>
|
||||
<uuid></uuid>
|
||||
<memory></memory>
|
||||
<vcpu>1</vcpu>
|
||||
|
||||
|
||||
<cpu mode='host-model'>
|
||||
<model fallback='allow'></model>
|
||||
</cpu>
|
||||
|
||||
|
||||
<os>
|
||||
<type>hvm</type>
|
||||
<kernel></kernel>
|
||||
<initrd></initrd>
|
||||
<cmdline></cmdline>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<pae/>
|
||||
</features>
|
||||
<clock offset='utc'/>
|
||||
<devices>
|
||||
|
||||
|
||||
<serial type='pty'>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
<console type='pty'>
|
||||
<target port='0'/>
|
||||
</console>
|
||||
|
||||
|
||||
<input type='mouse' bus='ps2'/>
|
||||
|
||||
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us' />
|
||||
<video>
|
||||
<model type='cirrus' vram='9216' heads='1'/>
|
||||
</video>
|
||||
|
||||
|
||||
<tpm model='tpm-crb'>
|
||||
<backend type='emulator' version='2.0'>
|
||||
</backend>
|
||||
</tpm>
|
||||
</devices>
|
||||
|
||||
</domain>
|
||||
Reference in New Issue
Block a user