summaryrefslogtreecommitdiff
path: root/nova/crypto.py
diff options
context:
space:
mode:
authorStephen Finucane <stephenfin@redhat.com>2020-07-03 11:06:11 +0100
committerStephen Finucane <stephenfin@redhat.com>2020-07-16 17:58:36 +0100
commit6ac228782651bc319fa149749e0d229f90f47adc (patch)
tree8e79879892bfa9b3a69b8c9a4d5d586665de34c4 /nova/crypto.py
parent5550f866237157e82cad8c6146b486c62f35a0d8 (diff)
downloadnova-6ac228782651bc319fa149749e0d229f90f47adc.tar.gz
crypto: Add support for creating, destroying vTPM secrets
Provide a method to communicate with the configured key manager service using Castellan, a generic key manager interface that supports multiple backends including - but not limited to - Barbican. Once again, there's nothing using this yet but tests, though this will change shortly. Part of blueprint add-emulated-virtual-tpm Change-Id: Iff6195d252b018f008bb9d137e4d80c54b70b2d1 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
Diffstat (limited to 'nova/crypto.py')
-rw-r--r--nova/crypto.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/nova/crypto.py b/nova/crypto.py
index b927f0c255..05d6196c41 100644
--- a/nova/crypto.py
+++ b/nova/crypto.py
@@ -25,6 +25,9 @@ import io
import os
import typing as ty
+from castellan.common import exception as castellan_exception
+from castellan.common.objects import passphrase
+from castellan import key_manager
from cryptography.hazmat import backends
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
@@ -35,8 +38,10 @@ from oslo_log import log as logging
import paramiko
import nova.conf
+from nova import context as nova_context
from nova import exception
from nova.i18n import _
+from nova import objects
from nova import utils
@@ -44,6 +49,17 @@ LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
+_KEYMGR = None
+
+_VTPM_SECRET_BYTE_LENGTH = 384
+
+
+def _get_key_manager():
+ global _KEYMGR
+ if _KEYMGR is None:
+ _KEYMGR = key_manager.API(configuration=CONF)
+ return _KEYMGR
+
def generate_fingerprint(public_key: str) -> str:
try:
@@ -148,3 +164,93 @@ def _create_x509_openssl_config(conffile: str, upn: str):
with open(conffile, 'w') as file:
file.write(content % upn)
+
+
+def ensure_vtpm_secret(
+ context: nova_context.RequestContext,
+ instance: 'objects.Instance',
+) -> ty.Tuple[str, str]:
+ """Communicates with the key manager service to retrieve or create a secret
+ for an instance's emulated TPM.
+
+ When creating a secret, its UUID is saved to the instance's system_metadata
+ as ``vtpm_secret_uuid``.
+
+ :param context: Nova auth context.
+ :param instance: Instance object.
+ :return: A tuple comprising (secret_uuid, passphrase).
+ :raise: castellan_exception.ManagedObjectNotFoundError if communication
+ with the key manager API fails, or if a vtpm_secret_uuid was present in
+ the instance's system metadata but could not be found in the key
+ manager service.
+ """
+ key_mgr = _get_key_manager()
+
+ secret_uuid = instance.system_metadata.get('vtpm_secret_uuid')
+ if secret_uuid is not None:
+ # Try to retrieve the secret from the key manager
+ try:
+ secret = key_mgr.get(context, secret_uuid)
+ # assert secret_uuid == secret.id ?
+ LOG.debug(
+ "Found existing vTPM secret with UUID %s.",
+ secret_uuid, instance=instance)
+ return secret.id, secret.get_encoded()
+ except castellan_exception.ManagedObjectNotFoundError:
+ LOG.warning(
+ "Despite being set on the instance, failed to find a vTPM "
+ "secret with UUID %s. This should only happen if the secret "
+ "was manually deleted from the key manager service. Your vTPM "
+ "is likely to be unrecoverable.",
+ secret_uuid, instance=instance)
+ raise
+
+ # If we get here, the instance has no vtpm_secret_uuid. Create a new one
+ # and register it with the key manager.
+ secret = base64.b64encode(os.urandom(_VTPM_SECRET_BYTE_LENGTH))
+ # Castellan ManagedObject
+ cmo = passphrase.Passphrase(
+ secret, name="vTPM secret for instance %s" % instance.uuid)
+ secret_uuid = key_mgr.store(context, cmo)
+ LOG.debug("Created vTPM secret with UUID %s",
+ secret_uuid, instance=instance)
+
+ instance.system_metadata['vtpm_secret_uuid'] = secret_uuid
+ instance.save()
+ return secret_uuid, secret
+
+
+def delete_vtpm_secret(
+ context: nova_context.RequestContext,
+ instance: 'objects.Instance',
+):
+ """Communicates with the key manager service to destroy the secret for an
+ instance's emulated TPM.
+
+ This operation is idempotent: if the instance never had a vTPM secret, OR
+ if the secret has already been deleted, it is a no-op.
+
+ The ``vtpm_secret_uuid`` member of the instance's system_metadata is
+ cleared as a side effect of this method.
+
+ :param context: Nova auth context.
+ :param instance: Instance object.
+ :return: None
+ :raise: castellan_exception.ManagedObjectNotFoundError if communication
+ with the key manager API.
+ """
+ secret_uuid = instance.system_metadata.get('vtpm_secret_uuid')
+ if not secret_uuid:
+ return
+
+ key_mgr = _get_key_manager()
+ try:
+ key_mgr.delete(context, secret_uuid)
+ LOG.debug("Deleted vTPM secret with UUID %s",
+ secret_uuid, instance=instance)
+ except castellan_exception.ManagedObjectNotFoundError:
+ LOG.debug("vTPM secret with UUID %s already deleted or never existed.",
+ secret_uuid, instance=instance)
+
+ del instance.system_metadata['vtpm_secret_uuid']
+ instance.save()