os-brick/os_brick/encryptors/__init__.py
Lee Yarwood 6a01bacda7 encryptors: Introduce support for LUKS2
LUKS2 support was introduced into cryptsetup 2.0.0 [1] and offers various
improvements over the original format now referred to as LUKS1.

This change introduces an encryptor to os-brick mostly using the
existing LuksEncryptor class with the only difference being the `--type`
switch supplied to cryptsetup when formatting a volume. As such the bulk
of the _format_volume method from the original class has been extracted
into a new _format_luks_volume method both the original and new
Luks2Encryptor class can now reuse.

[1] https://www.saout.de/pipermail/dm-crypt/2017-December/005771.html

Change-Id: I09fb2b2be1e376f8ec0f49741c855cfd54ee27f0
2019-09-03 18:44:41 +00:00

130 lines
5.1 KiB
Python

# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from os_brick.encryptors import nop
from oslo_log import log as logging
from oslo_utils import importutils
from oslo_utils import strutils
LOG = logging.getLogger(__name__)
LUKS = "luks"
LUKS2 = "luks2"
PLAIN = "plain"
FORMAT_TO_FRONTEND_ENCRYPTOR_MAP = {
LUKS: 'os_brick.encryptors.luks.LuksEncryptor',
LUKS2: 'os_brick.encryptors.luks.Luks2Encryptor',
PLAIN: 'os_brick.encryptors.cryptsetup.CryptsetupEncryptor'
}
LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP = {
"nova.volume.encryptors.luks.LuksEncryptor": LUKS,
"nova.volume.encryptors.cryptsetup.CryptsetupEncryptor": PLAIN,
"nova.volume.encryptors.nop.NoopEncryptor": None,
"os_brick.encryptors.luks.LuksEncryptor": LUKS,
"os_brick.encryptors.cryptsetup.CryptsetupEncryptor": PLAIN,
"os_brick.encryptors.nop.NoopEncryptor": None,
"LuksEncryptor": LUKS,
"CryptsetupEncryptor": PLAIN,
"NoOpEncryptor": None,
}
def get_volume_encryptor(root_helper,
connection_info,
keymgr,
execute=None,
*args, **kwargs):
"""Creates a VolumeEncryptor used to encrypt the specified volume.
:param: the connection information used to attach the volume
:returns VolumeEncryptor: the VolumeEncryptor for the volume
"""
encryptor = nop.NoOpEncryptor(root_helper=root_helper,
connection_info=connection_info,
keymgr=keymgr,
execute=execute,
*args, **kwargs)
location = kwargs.get('control_location', None)
if location and location.lower() == 'front-end': # case insensitive
provider = kwargs.get('provider')
# TODO(lyarwood): Remove the following in Queens and raise an
# ERROR if provider is not a key in SUPPORTED_ENCRYPTION_PROVIDERS.
# Until then continue to allow both the class name and path to be used.
if provider in LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP:
LOG.warning("Use of the in tree encryptor class %(provider)s"
" by directly referencing the implementation class"
" will be blocked in the Queens release of"
" os-brick.", {'provider': provider})
provider = LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP[provider]
if provider in FORMAT_TO_FRONTEND_ENCRYPTOR_MAP:
provider = FORMAT_TO_FRONTEND_ENCRYPTOR_MAP[provider]
elif provider is None:
provider = "os_brick.encryptors.nop.NoOpEncryptor"
else:
LOG.warning("Use of the out of tree encryptor class "
"%(provider)s will be blocked with the Queens "
"release of os-brick.", {'provider': provider})
try:
encryptor = importutils.import_object(
provider,
root_helper,
connection_info,
keymgr,
execute,
**kwargs)
except Exception as e:
LOG.error("Error instantiating %(provider)s: %(exception)s",
{'provider': provider, 'exception': e})
raise
msg = ("Using volume encryptor '%(encryptor)s' for connection: "
"%(connection_info)s" %
{'encryptor': encryptor, 'connection_info': connection_info})
LOG.debug(strutils.mask_password(msg))
return encryptor
def get_encryption_metadata(context, volume_api, volume_id, connection_info):
metadata = {}
if ('data' in connection_info and
connection_info['data'].get('encrypted', False)):
try:
metadata = volume_api.get_volume_encryption_metadata(context,
volume_id)
if not metadata:
LOG.warning('Volume %s should be encrypted but there is no '
'encryption metadata.', volume_id)
except Exception as e:
LOG.error("Failed to retrieve encryption metadata for "
"volume %(volume_id)s: %(exception)s",
{'volume_id': volume_id, 'exception': e})
raise
if metadata:
msg = ("Using volume encryption metadata '%(metadata)s' for "
"connection: %(connection_info)s" %
{'metadata': metadata, 'connection_info': connection_info})
LOG.debug(strutils.mask_password(msg))
return metadata