Emulated Trusted Platform Module (vTPM) in OpenStack 🔐

Work is ongoing in nova to provide support for attaching virtual Trusted Platform Modules (vTPMs) to instances. The below guide demonstrates how you can go about testing this feature for yourself. This work was conducted on a Fedora 31 VM (with nested virt, though that’s not relevant) using DevStack master.

Initial Steps

We’re going to use DevStack on Fedora 31 to test this. Fedora 31 is necessary since Ubuntu 18.04 (Bionic) does not provide new enough versions of libvirt or QEMU, while Ubuntu 20.04 (Focal) is not supported by DevStack at the time of writing and was affected by a bug in barbican. With a Fedora 31 installation at the ready, let’s update and get DevStack:

$ sudo dnf upgrade -y
$ git clone https://opendev.org/openstack/devstack
$ cd devstack

The DevStack installation is pretty bog standard with the key differences being that you need to enable the OpenStack Key Manager service, barbican, for storing secrets along with the virt preview repo to get new versions of libvirt and QEMU. Here’s a sample local.conf, to be placed into the devstack directory:


## Passwords


## Additional plugins and configuration

enable_plugin barbican https://opendev.org/openstack/barbican
enable_service rabbit mysql key


There’s also a minor tweak necessary to work around pip 10 refusing to uninstall system-provided Python packages:

diff --git inc/python inc/python
index dd772960..63a3dc19 100644
--- inc/python
+++ inc/python
@@ -196,7 +196,7 @@ function pip_install {
         no_proxy="${no_proxy:-}" \
-        $cmd_pip $upgrade \
+        $cmd_pip --ignore-installed $upgrade \
         [email protected]

We also need to install the swtpm and swtpm_setup binaries. These are provided in the Fedora repos (yay!):

$ sudo dnf install swtpm swtpm-tools

Finally, crudini makes working with INI files a breeze. Let’s install that too:

$ sudo dnf install crudini

With all this done, you can stack:


Now go make a cup of tea. ☕

Configuring nova

With the stack (hopefully) complete, we need to configure nova appropriately. This is a simple, single-node “can I boot an instance” test so we don’t need to do too much. The key steps are to check out the correct code, given that it’s not yet merged, add some nova.conf configuration options and create a new flavor. First up, let’s checkout the correct nova code. You can use the checkout link from the Gerrit review for this purpose:

$ cd /opt/stack/nova
# checkout using the "Checkout" link in the "Download" dropdown on the review,
# which I'm not providing here since it won't age well

Now, let’s modify the nova.conf file the nova-compute service. We’ll use crudini for this:

$ crudini --set /etc/nova/nova-cpu.conf libvirt swtpm_enabled True

Let’s restart the various nova services to load both the correct code and the configuration changes:

$ sudo systemctl restart [email protected]*

With everything restarted, we should be able to see the relevant COMPUTE_SECURITY_TPM_* traits reported by our sole compute node:

$ openstack --os-placement-api-version 1.20 resource provider trait list \
    --format value $RP_UUID | grep TPM

(where $RP_UUID is the UUID of the resource provider of the compute node, which can be found via openstack resource provider list)

This should return the following:


With that done, let’s create a suitable flavor. The key feature here is the configuration of the hw:tpm_version and hw:tpm_model extra specs. hw:tpm_model is optional but hw:tpm_version is required to enable the feature:

$ openstack flavor create test.vtpm \
    --ram 512 --disk 1 --vcpus 1 --wait \
    --property hw:tpm_version=1.2 \
    --property hw:tpm_model=tpm-tis

Create an instance with vTPM

With configuration complete, we can finally proceed to creating an instance. Nothing to funky here: simply create an instance using the flavor we created previously.

$ openstack --os-compute-api-version 2.latest server create test.server \
    --image cirros-0.5.1-x86_64-disk --flavor test.vtpm \
    --nic none --wait test.server

Once booted, let’s check if the generated XML includes the <tpm> device as expected:

$ sudo virsh list
 Id   Name                State
 10   instance-0000000d   running

$ sudo virsh dumpxml instance-0000000d | xmllint --xpath '/domain/devices/tpm' -
<tpm model="tpm-tis">
      <backend type="emulator" version="1.2">
        <encryption secret="8cc4e70c-d805-4d48-9302-e3c970d1217b"/>
      <alias name="tpm0"/>

We can also query barbican to see if nova correctly stored keys as expected:

$ openstack secret list --format yaml
- Algorithm: null
  Bit length: null
  Content types:
    default: application/octet-stream
  Created: '2020-05-28T15:41:05+00:00'
  Expiration: null
  Mode: null
  Name: vTPM secret for instance bbe8bc62-8403-490b-bce3-bd9c8267147e
  Secret href:
  Secret type: passphrase
  Status: ACTIVE

We can also validate that the rebuild operation works as expected:

$ openstack server rebuild --wait test.server

Finally, we can ensure that things are properly cleaned up once we’re finished:

$ openstack server delete test.server
$ openstack secret list --format yaml

QED. 🎉

Further reading

The spec and WIP code are well worth a read and contain background information on most of the topics discussed here.

Package versions

The following software versions were used. In summary, these correspond to the master versions of various OpenStack projects and latest Fedora package versions on the day of test:

  • Distro: Fedora 31 (with virt preview repo via DevStack)
  • Kernel: 5.3.7-301.fc31.x86_64
  • Libvirt: 6.3.0
  • QEMU: 5.0.0
  • DevStack: 9a6ae3419c6412a55456aa87b7a790c255f01028 (master)
  • Nova: de42f9e983cb4d4e94977697f86abf0a05e61cb4 (master) (before checking out in-progress vTPM changes)
  • Barbican: 1ad43597352b225b6f3a21ef6c4186330cadf660 (master)
comments powered by Disqus