Skip to content

Commit eda11a4

Browse files
committed
libvirt: Skip encryption metadata lookups if secret already exists on host
When connecting an encrypted volume to a host the _attach_encryptor method will be called in order to either call a legacy os-brick encryptor *or* configure a libvirt secret used by libvirt and QEMU to natively decrypt LUKSv1 encrypted volumes. To create this libvirt secret the configured key manager will be queried to provide and then decode the associated secret before this is stashed within libvirt. This change simply skips the above when an existing libvirt secret associated with the target volume is found on the host already. While this obviously optimises basic instance lifecycle flows such as a simple power off and on it additionally resolves a more convoluted use case when the ``[DEFAULT]/resume_guests_state_on_host_boot`` configurable is enabled. In this case the compute service has no request context with which to query the key manager when attempting to restart instances with encrypted volumes attached. As a result any attempt by the compute service to restart an instance with an attached encrypted volume would previously fail. Closes-Bug: #1905701 Change-Id: Ia2007bc63ef09931ea0197cef29d6a5614ed821a (cherry picked from commit a107a50)
1 parent 81ad810 commit eda11a4

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

nova/tests/unit/virt/libvirt/test_driver.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9136,6 +9136,9 @@ def test_connect_volume_luks(self, mock_is_volume_luks, mock_host,
91369136
'encryption_key_id': uuids.encryption_key_id}
91379137
instance = mock.sentinel.instance
91389138

9139+
# Mock out find_secret so we don't skip ahead
9140+
drvr._host.find_secret.return_value = None
9141+
91399142
# Mock out the encryptors
91409143
mock_encryptor = mock.Mock()
91419144
mock_get_volume_encryptor.return_value = mock_encryptor
@@ -10196,6 +10199,21 @@ def test_attach_encryptor_encrypted_native_luks_serial(self,
1019610199
crt_scrt.assert_called_once_with(
1019710200
'volume', uuids.serial, password=key)
1019810201

10202+
@mock.patch.object(key_manager, 'API')
10203+
def test_attach_encryptor_secret_exists(self, mock_key_manager_api):
10204+
connection_info = {'data': {'volume_id': uuids.volume_id}}
10205+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
10206+
with test.nested(
10207+
mock.patch.object(drvr, '_get_volume_encryption'),
10208+
mock.patch.object(drvr._host, 'find_secret')
10209+
) as (mock_get_volume_encryption, mock_find_secret):
10210+
drvr._attach_encryptor(self.context, connection_info, None)
10211+
10212+
# Assert we called find_secret and nothing else
10213+
mock_find_secret.assert_called_once_with('volume', uuids.volume_id)
10214+
mock_get_volume_encryption.assert_not_called()
10215+
mock_key_manager_api.assert_not_called()
10216+
1019910217
@mock.patch('os_brick.encryptors.get_encryption_metadata')
1020010218
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
1020110219
def test_detach_encryptor_connection_info_incomplete(self,

nova/virt/libvirt/driver.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,17 @@ def _attach_encryptor(self, context, connection_info, encryption):
17911791
to determine if an attempt to attach the encryptor should be made.
17921792

17931793
"""
1794+
# NOTE(lyarwood): Skip any attempt to fetch encryption metadata or the
1795+
# actual passphrase from the key manager if a libvirt secert already
1796+
# exists locally for the volume. This suggests that the instance was
1797+
# only powered off or the underlying host rebooted.
1798+
volume_id = driver_block_device.get_volume_id(connection_info)
1799+
if self._host.find_secret('volume', volume_id):
1800+
LOG.debug("A libvirt secret for volume %s has been found on the "
1801+
"host, skipping any attempt to create another or attach "
1802+
"an os-brick encryptor.", volume_id)
1803+
return
1804+
17941805
if encryption is None:
17951806
encryption = self._get_volume_encryption(context, connection_info)
17961807

@@ -1822,7 +1833,6 @@ def _attach_encryptor(self, context, connection_info, encryption):
18221833
# NOTE(lyarwood): Store the passphrase as a libvirt secret locally
18231834
# on the compute node. This secret is used later when generating
18241835
# the volume config.
1825-
volume_id = driver_block_device.get_volume_id(connection_info)
18261836
self._host.create_secret('volume', volume_id, password=passphrase)
18271837
elif encryption:
18281838
encryptor = self._get_volume_encryptor(connection_info,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
fixes:
3+
- |
4+
The libvirt virt driver will no longer attempt to fetch volume
5+
encryption metadata or the associated secret key when attaching ``LUKSv1``
6+
encrypted volumes if a libvirt secret already exists on the host.
7+
8+
This resolves `bug 1905701`_ where instances with ``LUKSv1`` encrypted
9+
volumes could not be restarted automatically by the ``nova-compute``
10+
service after a host reboot when the
11+
``[DEFAULT]/resume_guests_state_on_host_boot`` configurable was enabled.
12+
13+
.. _bug 1905701: https://launchpad.net/bugs/1905701

0 commit comments

Comments
 (0)