diff options
author | Kaitlin Farr <kaitlin.farr@jhuapl.edu> | 2016-04-21 14:58:46 -0400 |
---|---|---|
committer | Kaitlin Farr <kaitlin.farr@jhuapl.edu> | 2016-04-27 14:37:06 -0400 |
commit | f006ff4de70077b05c3e4e266499bda2fc22db17 (patch) | |
tree | cf5093f9adcd1279ba7c59bc89304e57d577cb10 /nova/keymgr | |
parent | 9702e5d2e9433c67067847c02fb100cd808a3596 (diff) | |
download | nova-f006ff4de70077b05c3e4e266499bda2fc22db17.tar.gz |
Replace key manager with Castellan
Because key manager code is duplicated across several projects, a key
manager interface was moved into its own library. This patch goes back
to replace the old code with the new library.
Change-Id: Ib563b0ea4b8b4bc1833bf52bf49a68546c384996
Implements: blueprint use-castellan-key-manager
Diffstat (limited to 'nova/keymgr')
-rw-r--r-- | nova/keymgr/__init__.py | 58 | ||||
-rw-r--r-- | nova/keymgr/barbican.py | 340 | ||||
-rw-r--r-- | nova/keymgr/conf_key_mgr.py | 92 | ||||
-rw-r--r-- | nova/keymgr/key.py | 90 | ||||
-rw-r--r-- | nova/keymgr/key_mgr.py | 102 | ||||
-rw-r--r-- | nova/keymgr/mock_key_mgr.py | 133 | ||||
-rw-r--r-- | nova/keymgr/not_implemented_key_mgr.py | 41 | ||||
-rw-r--r-- | nova/keymgr/single_key_mgr.py | 72 |
8 files changed, 138 insertions, 790 deletions
diff --git a/nova/keymgr/__init__.py b/nova/keymgr/__init__.py index 5e10b395b7..67dbb3ecc4 100644 --- a/nova/keymgr/__init__.py +++ b/nova/keymgr/__init__.py @@ -14,13 +14,65 @@ # under the License. +from castellan import options as castellan_opts +from oslo_config import cfg +from oslo_log import log as logging from oslo_utils import importutils import nova.conf +from nova.i18n import _LW +LOG = logging.getLogger(__name__) CONF = nova.conf.CONF -def API(): - cls = importutils.import_class(CONF.keymgr.api_class) - return cls() +castellan_opts.set_defaults(CONF) + +# NOTE(kfarr): This line can be removed when a value is assigned in DevStack +CONF.set_default('api_class', 'nova.keymgr.conf_key_mgr.ConfKeyManager', + group='key_manager') + +# NOTE(kfarr): For backwards compatibility, everything below this comment +# is deprecated for removal +api_class = None +try: + api_class = CONF.key_manager.api_class +except cfg.NoSuchOptError: + LOG.warning(_LW("key_manager.api_class is not set, will use deprecated " + "option keymgr.api_class if set")) + try: + api_class = CONF.keymgr.api_class + except cfg.NoSuchOptError: + LOG.warning(_LW("keymgr.api_class is not set")) + +deprecated_barbican = 'nova.keymgr.barbican.BarbicanKeyManager' +barbican = 'castellan.key_manager.barbican_key_manager.BarbicanKeyManager' +deprecated_mock = 'nova.tests.unit.keymgr.mock_key_mgr.MockKeyManager' +castellan_mock = ('castellan.tests.unit.key_manager.mock_key_manager.' + 'MockKeyManager') + + +def log_deprecated_warning(deprecated, castellan): + LOG.warning(_LW("key manager api_class set to use deprecated option " + "%(deprecated)s, using %(castellan)s instead"), + {'deprecated': deprecated, 'castellan': castellan}) + +if api_class == deprecated_barbican: + log_deprecated_warning(deprecated_barbican, barbican) + api_class = barbican +elif api_class == deprecated_mock: + log_deprecated_warning(deprecated_mock, castellan_mock) + api_class = castellan_mock +elif api_class is None: + # TODO(kfarr): key_manager.api_class should be set in DevStack, and this + # block can be removed + LOG.warning(_LW("key manager not set, using insecure default %s"), + castellan_mock) + api_class = castellan_mock + +CONF.set_override('api_class', api_class, 'key_manager') + + +def API(conf=CONF): + cls = importutils.import_class(CONF.key_manager.api_class) + return cls(conf) diff --git a/nova/keymgr/barbican.py b/nova/keymgr/barbican.py deleted file mode 100644 index 972d81f0a5..0000000000 --- a/nova/keymgr/barbican.py +++ /dev/null @@ -1,340 +0,0 @@ -# Copyright (c) 2015 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. - -""" -Key manager implementation for Barbican -""" - -import array -import base64 -import binascii - -from barbicanclient import client as barbican_client -from keystoneauth1 import loading as ks_loading -from keystoneauth1 import session -from oslo_log import log as logging -from oslo_utils import excutils - -import nova.conf - -from nova import exception -from nova.i18n import _ -from nova.i18n import _LE -from nova.keymgr import key as keymgr_key -from nova.keymgr import key_mgr - - -CONF = nova.conf.CONF - -LOG = logging.getLogger(__name__) - - -class BarbicanKeyManager(key_mgr.KeyManager): - """Key Manager Interface that wraps the Barbican client API.""" - - def __init__(self): - self._barbican_client = None - self._current_context = None - self._base_url = None - - def _get_barbican_client(self, ctxt): - """Creates a client to connect to the Barbican service. - - :param ctxt: the user context for authentication - :return: a Barbican Client object - :raises Forbidden: if the ctxt is None - """ - - # Confirm context is provided, if not raise forbidden - if not ctxt: - msg = _("User is not authorized to use key manager.") - LOG.error(msg) - raise exception.Forbidden(msg) - - if not hasattr(ctxt, 'project_id') or ctxt.project_id is None: - msg = _("Unable to create Barbican Client without project_id.") - LOG.error(msg) - raise exception.KeyManagerError(msg) - - # If same context, return cached barbican client - if self._barbican_client and self._current_context == ctxt: - return self._barbican_client - - try: - _SESSION = ks_loading.load_session_from_conf_options( - CONF, - nova.conf.barbican.barbican_group.name) - - auth = ctxt.get_auth_plugin() - service_type, service_name, interface = (CONF. - barbican. - catalog_info. - split(':')) - region_name = CONF.barbican.os_region_name - service_parameters = {'service_type': service_type, - 'service_name': service_name, - 'interface': interface, - 'region_name': region_name} - - if CONF.barbican.endpoint_template: - self._base_url = (CONF.barbican.endpoint_template % - ctxt.to_dict()) - else: - self._base_url = _SESSION.get_endpoint( - auth, **service_parameters) - - # the barbican endpoint can't have the '/v1' on the end - self._barbican_endpoint = self._base_url.rpartition('/')[0] - - sess = session.Session(auth=auth) - self._barbican_client = barbican_client.Client( - session=sess, - endpoint=self._barbican_endpoint) - self._current_context = ctxt - - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error creating Barbican client: %s"), e) - - return self._barbican_client - - def create_key(self, ctxt, expiration=None, name='Nova Compute Key', - payload_content_type='application/octet-stream', mode='CBC', - algorithm='AES', length=256): - """Creates a key. - - :param ctxt: contains information of the user and the environment - for the request (nova/context.py) - :param expiration: the date the key will expire - :param name: a friendly name for the secret - :param payload_content_type: the format/type of the secret data - :param mode: the algorithm mode (e.g. CBC or CTR mode) - :param algorithm: the algorithm associated with the secret - :param length: the bit length of the secret - - :return: the UUID of the new key - :raises Exception: if key creation fails - """ - barbican_client = self._get_barbican_client(ctxt) - - try: - key_order = barbican_client.orders.create_key( - name, - algorithm, - length, - mode, - payload_content_type, - expiration) - order_ref = key_order.submit() - order = barbican_client.orders.get(order_ref) - return self._retrieve_secret_uuid(order.secret_ref) - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error creating key: %s"), e) - - def store_key(self, ctxt, key, expiration=None, name='Nova Compute Key', - payload_content_type='application/octet-stream', - payload_content_encoding='base64', algorithm='AES', - bit_length=256, mode='CBC', from_copy=False): - """Stores (i.e., registers) a key with the key manager. - - :param ctxt: contains information of the user and the environment for - the request (nova/context.py) - :param key: the unencrypted secret data. Known as "payload" to the - barbicanclient api - :param expiration: the expiration time of the secret in ISO 8601 - format - :param name: a friendly name for the key - :param payload_content_type: the format/type of the secret data - :param payload_content_encoding: the encoding of the secret data - :param algorithm: the algorithm associated with this secret key - :param bit_length: the bit length of this secret key - :param mode: the algorithm mode used with this secret key - :param from_copy: establishes whether the function is being used - to copy a key. In case of the latter, it does not - try to decode the key - - :returns: the UUID of the stored key - :raises Exception: if key storage fails - """ - barbican_client = self._get_barbican_client(ctxt) - - try: - if key.get_algorithm(): - algorithm = key.get_algorithm() - if payload_content_type == 'text/plain': - payload_content_encoding = None - encoded_key = key.get_encoded() - elif (payload_content_type == 'application/octet-stream' and - not from_copy): - key_list = key.get_encoded() - string_key = ''.join(map(lambda byte: "%02x" % byte, key_list)) - encoded_key = base64.b64encode(binascii.unhexlify(string_key)) - else: - encoded_key = key.get_encoded() - secret = barbican_client.secrets.create(name, - encoded_key, - payload_content_type, - payload_content_encoding, - algorithm, - bit_length, - mode, - expiration) - secret_ref = secret.store() - return self._retrieve_secret_uuid(secret_ref) - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error storing key: %s"), e) - - def copy_key(self, ctxt, key_id): - """Copies (i.e., clones) a key stored by barbican. - - :param ctxt: contains information of the user and the environment for - the request (nova/context.py) - :param key_id: the UUID of the key to copy - :return: the UUID of the key copy - :raises Exception: if key copying fails - """ - - try: - secret = self._get_secret(ctxt, key_id) - con_type = secret.content_types['default'] - secret_data = self._get_secret_data(secret, - payload_content_type=con_type) - key = keymgr_key.SymmetricKey(secret.algorithm, secret_data) - copy_uuid = self.store_key(ctxt, key, secret.expiration, - secret.name, con_type, - 'base64', - secret.algorithm, secret.bit_length, - secret.mode, True) - return copy_uuid - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error copying key: %s"), e) - - def _create_secret_ref(self, key_id): - """Creates the URL required for accessing a secret. - - :param key_id: the UUID of the key to copy - - :return: the URL of the requested secret - """ - if not key_id: - msg = "Key ID is None" - raise exception.KeyManagerError(msg) - return self._base_url + "/secrets/" + key_id - - def _retrieve_secret_uuid(self, secret_ref): - """Retrieves the UUID of the secret from the secret_ref. - - :param secret_ref: the href of the secret - - :return: the UUID of the secret - """ - - # The secret_ref is assumed to be of a form similar to - # http://host:9311/v1/secrets/d152fa13-2b41-42ca-a934-6c21566c0f40 - # with the UUID at the end. This command retrieves everything - # after the last '/', which is the UUID. - return secret_ref.rpartition('/')[2] - - def _get_secret_data(self, - secret, - payload_content_type='application/octet-stream'): - """Retrieves the secret data given a secret and content_type. - - :param ctxt: contains information of the user and the environment for - the request (nova/context.py) - :param secret: the secret from barbican with the payload of data - :param payload_content_type: the format/type of the secret data - - :returns: the secret data - :raises Exception: if data cannot be retrieved - """ - try: - generated_data = secret.payload - if payload_content_type == 'application/octet-stream': - secret_data = base64.b64encode(generated_data) - else: - secret_data = generated_data - return secret_data - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error getting secret data: %s"), e) - - def _get_secret(self, ctxt, key_id): - """Returns the metadata of the secret. - - :param ctxt: contains information of the user and the environment for - the request (nova/context.py) - :param key_id: UUID of the secret - - :return: the secret's metadata - :raises Exception: if there is an error retrieving the data - """ - - barbican_client = self._get_barbican_client(ctxt) - - try: - secret_ref = self._create_secret_ref(key_id) - return barbican_client.secrets.get(secret_ref) - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error getting secret metadata: %s"), e) - - def get_key(self, ctxt, key_id, - payload_content_type='application/octet-stream'): - """Retrieves the specified key. - - :param ctxt: contains information of the user and the environment for - the request (nova/context.py) - :param key_id: the UUID of the key to retrieve - :param payload_content_type: The format/type of the secret data - - :return: SymmetricKey representation of the key - :raises Exception: if key retrieval fails - """ - try: - secret = self._get_secret(ctxt, key_id) - secret_data = self._get_secret_data(secret, - payload_content_type) - if payload_content_type == 'application/octet-stream': - # convert decoded string to list of unsigned ints for each byte - key_data = array.array('B', - base64.b64decode(secret_data)).tolist() - else: - key_data = secret_data - key = keymgr_key.SymmetricKey(secret.algorithm, key_data) - return key - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error getting key: %s"), e) - - def delete_key(self, ctxt, key_id): - """Deletes the specified key. - - :param ctxt: contains information of the user and the environment for - the request (nova/context.py) - :param key_id: the UUID of the key to delete - :raises Exception: if key deletion fails - """ - barbican_client = self._get_barbican_client(ctxt) - - try: - secret_ref = self._create_secret_ref(key_id) - barbican_client.secrets.delete(secret_ref) - except Exception as e: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error deleting key: %s"), e) diff --git a/nova/keymgr/conf_key_mgr.py b/nova/keymgr/conf_key_mgr.py index 1e0a1ce14e..209f3653e0 100644 --- a/nova/keymgr/conf_key_mgr.py +++ b/nova/keymgr/conf_key_mgr.py @@ -31,15 +31,22 @@ encrypted with a key provided by this key manager actually share the same encryption key so *any* volume can be decrypted once the fixed key is known. """ +import binascii + +from castellan.common.objects import symmetric_key as key +from castellan.key_manager import key_manager +from oslo_log import log as logging + import nova.conf -from nova.i18n import _ -from nova.keymgr import single_key_mgr +from nova import exception +from nova.i18n import _, _LW CONF = nova.conf.CONF +LOG = logging.getLogger(__name__) -class ConfKeyManager(single_key_mgr.SingleKeyManager): +class ConfKeyManager(key_manager.KeyManager): """This key manager implementation supports all the methods specified by the key manager interface. This implementation creates a single key in response to all invocations of create_key. Side effects @@ -47,11 +54,78 @@ class ConfKeyManager(single_key_mgr.SingleKeyManager): as specified by the key manager interface. """ - def __init__(self): - if CONF.keymgr.fixed_key is None: + def __init__(self, configuration): + LOG.warning(_LW('This key manager is insecure and is not recommended ' + 'for production deployments')) + super(ConfKeyManager, self).__init__(configuration) + + self.key_id = '00000000-0000-0000-0000-000000000000' + + self.conf = CONF if configuration is None else configuration + + if CONF.key_manager.fixed_key is None: raise ValueError(_('keymgr.fixed_key not defined')) - self._hex_key = CONF.keymgr.fixed_key - super(ConfKeyManager, self).__init__() + self._hex_key = CONF.key_manager.fixed_key + super(ConfKeyManager, self).__init__(configuration) + + def _get_key(self): + key_bytes = bytes(binascii.unhexlify(self._hex_key)) + return key.SymmetricKey('AES', len(key_bytes) * 8, key_bytes) + + def create_key(self, context, algorithm, length, **kwargs): + """Creates a symmetric key. + + This implementation returns a UUID for the key read from the + configuration file. A Forbidden exception is raised if the + specified context is None. + """ + if context is None: + raise exception.Forbidden() + + return self.key_id + + def create_key_pair(self, context, **kwargs): + raise NotImplementedError( + "ConfKeyManager does not support asymmetric keys") + + def store(self, context, managed_object, **kwargs): + """Stores (i.e., registers) a key with the key manager.""" + if context is None: + raise exception.Forbidden() + + if managed_object != self._get_key(): + raise exception.KeyManagerError( + reason="cannot store arbitrary keys") + + return self.key_id + + def get(self, context, managed_object_id): + """Retrieves the key identified by the specified id. + + This implementation returns the key that is associated with the + specified UUID. A Forbidden exception is raised if the specified + context is None; a KeyError is raised if the UUID is invalid. + """ + if context is None: + raise exception.Forbidden() + + if managed_object_id != self.key_id: + raise KeyError(str(managed_object_id) + " != " + str(self.key_id)) + + return self._get_key() + + def delete(self, context, managed_object_id): + """Represents deleting the key. + + Because the ConfKeyManager has only one key, which is read from the + configuration file, the key is not actually deleted when this is + called. + """ + if context is None: + raise exception.Forbidden() + + if managed_object_id != self.key_id: + raise exception.KeyManagerError( + reason="cannot delete non-existent key") - def _generate_hex_key(self, **kwargs): - return self._hex_key + LOG.warning(_LW("Not deleting key %s"), managed_object_id) diff --git a/nova/keymgr/key.py b/nova/keymgr/key.py deleted file mode 100644 index 54080ab64c..0000000000 --- a/nova/keymgr/key.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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. - -""" -Base Key and SymmetricKey Classes - -This module defines the Key and SymmetricKey classes. The Key class is the base -class to represent all encryption keys. The basis for this class was copied -from Java. -""" - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class Key(object): - """Base class to represent all keys.""" - - @abc.abstractmethod - def get_algorithm(self): - """Returns the key's algorithm. - - Returns the key's algorithm. For example, "DSA" indicates that this key - is a DSA key and "AES" indicates that this key is an AES key. - """ - pass - - @abc.abstractmethod - def get_format(self): - """Returns the encoding format. - - Returns the key's encoding format or None if this key is not encoded. - """ - pass - - @abc.abstractmethod - def get_encoded(self): - """Returns the key in the format specified by its encoding.""" - pass - - -class SymmetricKey(Key): - """This class represents symmetric keys.""" - - def __init__(self, alg, key): - """Create a new SymmetricKey object. - - The arguments specify the algorithm for the symmetric encryption and - the bytes for the key. - """ - self.alg = alg - self.key = key - - def get_algorithm(self): - """Returns the algorithm for symmetric encryption.""" - return self.alg - - def get_format(self): - """This method returns 'RAW'.""" - return "RAW" - - def get_encoded(self): - """Returns the key in its encoded format.""" - return self.key - - def __eq__(self, other): - if isinstance(other, SymmetricKey): - return (self.alg == other.alg and - self.key == other.key) - return NotImplemented - - def __ne__(self, other): - result = self.__eq__(other) - if result is NotImplemented: - return result - return not result diff --git a/nova/keymgr/key_mgr.py b/nova/keymgr/key_mgr.py deleted file mode 100644 index c020ca2474..0000000000 --- a/nova/keymgr/key_mgr.py +++ /dev/null @@ -1,102 +0,0 @@ -# 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. - -""" -Key manager API -""" - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class KeyManager(object): - """Base Key Manager Interface - - A Key Manager is responsible for managing encryption keys for volumes. A - Key Manager is responsible for creating, reading, and deleting keys. - """ - - @abc.abstractmethod - def create_key(self, ctxt, algorithm='AES', length=256, expiration=None, - **kwargs): - """Creates a key. - - This method creates a key and returns the key's UUID. If the specified - context does not permit the creation of keys, then a NotAuthorized - exception should be raised. - """ - pass - - @abc.abstractmethod - def store_key(self, ctxt, key, expiration=None, **kwargs): - """Stores (i.e., registers) a key with the key manager. - - This method stores the specified key and returns its UUID that - identifies it within the key manager. If the specified context does - not permit the creation of keys, then a NotAuthorized exception should - be raised. - """ - pass - - @abc.abstractmethod - def copy_key(self, ctxt, key_id, **kwargs): - """Copies (i.e., clones) a key stored by the key manager. - - This method copies the specified key and returns the copy's UUID. If - the specified context does not permit copying keys, then a - NotAuthorized error should be raised. - - Implementation note: This method should behave identically to:: - - store_key(context, get_key(context, <encryption key UUID>)) - - although it is preferable to perform this operation within the key - manager to avoid unnecessary handling of the key material. - """ - pass - - @abc.abstractmethod - def get_key(self, ctxt, key_id, **kwargs): - """Retrieves the specified key. - - Implementations should verify that the caller has permissions to - retrieve the key by checking the context object passed in as ctxt. If - the user lacks permission then a NotAuthorized exception is raised. - - If the specified key does not exist, then a KeyError should be raised. - Implementations should preclude users from discerning the UUIDs of - keys that belong to other users by repeatedly calling this method. - That is, keys that belong to other users should be considered "non- - existent" and completely invisible. - """ - pass - - @abc.abstractmethod - def delete_key(self, ctxt, key_id, **kwargs): - """Deletes the specified key. - - Implementations should verify that the caller has permission to delete - the key by checking the context object (ctxt). A NotAuthorized - exception should be raised if the caller lacks permission. - - If the specified key does not exist, then a KeyError should be raised. - Implementations should preclude users from discerning the UUIDs of - keys that belong to other users by repeatedly calling this method. - That is, keys that belong to other users should be considered "non- - existent" and completely invisible. - """ - pass diff --git a/nova/keymgr/mock_key_mgr.py b/nova/keymgr/mock_key_mgr.py deleted file mode 100644 index ab947c1cb2..0000000000 --- a/nova/keymgr/mock_key_mgr.py +++ /dev/null @@ -1,133 +0,0 @@ -# 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. - -""" -A mock implementation of a key manager that stores keys in a dictionary. - -This key manager implementation is primarily intended for testing. In -particular, it does not store keys persistently. Lack of a centralized key -store also makes this implementation unsuitable for use among different -services. - -Note: Instantiating this class multiple times will create separate key stores. -Keys created in one instance will not be accessible from other instances of -this class. -""" - -import array -import codecs - -from oslo_log import log as logging -from oslo_utils import uuidutils - -from nova import exception -from nova.i18n import _LW -from nova.keymgr import key -from nova.keymgr import key_mgr -from nova import utils - - -LOG = logging.getLogger(__name__) -decode_hex = codecs.getdecoder("hex_codec") - - -class MockKeyManager(key_mgr.KeyManager): - """This mock key manager implementation supports all the methods specified - by the key manager interface. This implementation stores keys within a - dictionary, and as a result, it is not acceptable for use across different - services. Side effects (e.g., raising exceptions) for each method are - handled as specified by the key manager interface. - - This key manager is not suitable for use in production deployments. - """ - - def __init__(self): - LOG.warning(_LW('This key manager is not suitable for use in ' - 'production deployments')) - - self.keys = {} - - def _generate_hex_key(self, **kwargs): - key_length = kwargs.get('key_length', 256) - # hex digit => 4 bits - hex_encoded = utils.generate_password(length=key_length // 4, - symbolgroups='0123456789ABCDEF') - return hex_encoded - - def _generate_key(self, **kwargs): - _hex = self._generate_hex_key(**kwargs) - return key.SymmetricKey('AES', - array.array('B', decode_hex(_hex)[0]).tolist()) - - def create_key(self, ctxt, **kwargs): - """Creates a key. - - This implementation returns a UUID for the created key. A - Forbidden exception is raised if the specified context is None. - """ - if ctxt is None: - raise exception.Forbidden() - - key = self._generate_key(**kwargs) - return self.store_key(ctxt, key) - - def _generate_key_id(self): - key_id = uuidutils.generate_uuid() - while key_id in self.keys: - key_id = uuidutils.generate_uuid() - - return key_id - - def store_key(self, ctxt, key, **kwargs): - """Stores (i.e., registers) a key with the key manager.""" - if ctxt is None: - raise exception.Forbidden() - - key_id = self._generate_key_id() - self.keys[key_id] = key - - return key_id - - def copy_key(self, ctxt, key_id, **kwargs): - if ctxt is None: - raise exception.Forbidden() - - copied_key_id = self._generate_key_id() - self.keys[copied_key_id] = self.keys[key_id] - - return copied_key_id - - def get_key(self, ctxt, key_id, **kwargs): - """Retrieves the key identified by the specified id. - - This implementation returns the key that is associated with the - specified UUID. A Forbidden exception is raised if the specified - context is None; a KeyError is raised if the UUID is invalid. - """ - if ctxt is None: - raise exception.Forbidden() - - return self.keys[key_id] - - def delete_key(self, ctxt, key_id, **kwargs): - """Deletes the key identified by the specified id. - - A Forbidden exception is raised if the context is None and a - KeyError is raised if the UUID is invalid. - """ - if ctxt is None: - raise exception.Forbidden() - - del self.keys[key_id] diff --git a/nova/keymgr/not_implemented_key_mgr.py b/nova/keymgr/not_implemented_key_mgr.py deleted file mode 100644 index a98e657df3..0000000000 --- a/nova/keymgr/not_implemented_key_mgr.py +++ /dev/null @@ -1,41 +0,0 @@ -# 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. - -""" -Key manager implementation that raises NotImplementedError -""" - -from nova.keymgr import key_mgr - - -class NotImplementedKeyManager(key_mgr.KeyManager): - """Key Manager Interface that raises NotImplementedError for all operations - """ - - def create_key(self, ctxt, algorithm='AES', length=256, expiration=None, - **kwargs): - raise NotImplementedError() - - def store_key(self, ctxt, key, expiration=None, **kwargs): - raise NotImplementedError() - - def copy_key(self, ctxt, key_id, **kwargs): - raise NotImplementedError() - - def get_key(self, ctxt, key_id, **kwargs): - raise NotImplementedError() - - def delete_key(self, ctxt, key_id, **kwargs): - raise NotImplementedError() diff --git a/nova/keymgr/single_key_mgr.py b/nova/keymgr/single_key_mgr.py deleted file mode 100644 index 6cf0e75670..0000000000 --- a/nova/keymgr/single_key_mgr.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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. - -""" -An implementation of a key manager that returns a single key in response to -all invocations of get_key. -""" - -from oslo_log import log as logging - -from nova import exception -from nova.i18n import _, _LW -from nova.keymgr import mock_key_mgr - - -LOG = logging.getLogger(__name__) - - -class SingleKeyManager(mock_key_mgr.MockKeyManager): - """This key manager implementation supports all the methods specified by - the key manager interface. This implementation creates a single key in - response to all invocations of create_key. Side effects - (e.g., raising exceptions) for each method are handled as specified by - the key manager interface. - """ - - def __init__(self): - LOG.warning(_LW('This key manager is insecure and is not recommended ' - 'for production deployments')) - super(SingleKeyManager, self).__init__() - - self.key_id = '00000000-0000-0000-0000-000000000000' - self.key = self._generate_key(key_length=256) - - # key should exist by default - self.keys[self.key_id] = self.key - - def _generate_hex_key(self, **kwargs): - key_length = kwargs.get('key_length', 256) - return b'0' * (key_length // 4) # hex digit => 4 bits - - def _generate_key_id(self): - return self.key_id - - def store_key(self, ctxt, key, **kwargs): - if key != self.key: - raise exception.KeyManagerError( - reason=_("cannot store arbitrary keys")) - - return super(SingleKeyManager, self).store_key(ctxt, key, **kwargs) - - def delete_key(self, ctxt, key_id, **kwargs): - if ctxt is None: - raise exception.Forbidden() - - if key_id != self.key_id: - raise exception.KeyManagerError( - reason=_("cannot delete non-existent key")) - - LOG.warning(_LW("Not deleting key %s"), key_id) |