From 3625f307f99042c18acac70a7c0f045dfd9e55a1 Mon Sep 17 00:00:00 2001 From: Michael McCune Date: Mon, 11 Jan 2016 14:16:40 -0500 Subject: [PATCH] add developer documentation about the key manager This change adds a section to the developer guidelines about using the key manager within the sahara codebase. It also adds some minor cleanup to the sahara_key_manager docstrings. * add a section to the development guidelines about the key manager * add an anchor in the advanced configuration guide for the key manager * improve the docstrings in the sahara.service.castellan.utils module * cleanup the docstrings in the sahara.service.castellan.sahara_key_manager module Change-Id: Ied9278113deab5af0bbd80d229810e0636bb2b72 Partial-Implements: blueprint improved-secret-storage --- doc/source/devref/development.guidelines.rst | 57 +++++++++++++++++++ .../userdoc/advanced.configuration.guide.rst | 2 + .../service/castellan/sahara_key_manager.py | 27 +++++---- sahara/service/castellan/utils.py | 27 +++++---- 4 files changed, 91 insertions(+), 22 deletions(-) diff --git a/doc/source/devref/development.guidelines.rst b/doc/source/devref/development.guidelines.rst index 724552cc5f..bf835bf482 100644 --- a/doc/source/devref/development.guidelines.rst +++ b/doc/source/devref/development.guidelines.rst @@ -181,3 +181,60 @@ keystone ``Session`` and auth plugin objects(for example, ``Token`` or methodology, where available. For more information on using sessions with keystone, please see http://docs.openstack.org/developer/python-keystoneclient/using-sessions.html + +Storing sensitive information +----------------------------- + +During the course of development, there is often cause to store sensitive +information (for example, login credentials) in the records for a cluster, +job, or some other record. Storing secret information this way is **not** +safe. To mitigate the risk of storing this information, sahara provides +access to the OpenStack Key Manager service (implemented by the +`barbican project `_) through +the `castellan library `_. + +To utilize the external key manager, the functions in +``sahara.service.castellan.utils`` are provided as wrappers around the +castellan library. These functions allow a developer to store, retrieve, and +delete secrets from the manager. Secrets that are managed through the key +manager have an identifier associated with them. These identifiers are +considered safe to store in the database. + +The following are some examples of working with secrets in the sahara +codebase. These examples are considered basic, any developer wishing to +learn more about the advanced features of storing secrets should look to +the code and docstrings contained in the ``sahara.service.castellan`` module. + +**Storing a secret** + +.. sourcecode:: python + + from sahara.service.castellan import utils as key_manager + + password = 'SooperSecretPassword' + identifier = key_manager.store_secret(password) + +**Retrieving a secret** + +.. sourcecode:: python + + from sahara.service.castellan import utils as key_manager + + password = key_manager.get_secret(identifier) + +**Deleting a secret** + +.. sourcecode:: python + + from sahara.service.castellan import utils as key_manager + + key_manager.delete_secret(identifier) + +When storing secrets through this interface it is important to remember that +if an external key manager is being used, each stored secret creates an +entry in an external service. When you are finished using the secret it is +good practice to delete it, as not doing so may leave artifacts in those +external services. + +For more information on configuring sahara to use the OpenStack Key +Manager service, see :ref:`external_key_manager_usage`. diff --git a/doc/source/userdoc/advanced.configuration.guide.rst b/doc/source/userdoc/advanced.configuration.guide.rst index 3799509b63..9436a970c6 100644 --- a/doc/source/userdoc/advanced.configuration.guide.rst +++ b/doc/source/userdoc/advanced.configuration.guide.rst @@ -193,6 +193,8 @@ These options will also be present in the generated sample configuration file. For instructions on creating the configuration file please see the :doc:`configuration.guide`. +.. _external_key_manager_usage: + External key manager usage -------------------------- diff --git a/sahara/service/castellan/sahara_key_manager.py b/sahara/service/castellan/sahara_key_manager.py index 572662acf7..8e042bf0d0 100644 --- a/sahara/service/castellan/sahara_key_manager.py +++ b/sahara/service/castellan/sahara_key_manager.py @@ -17,53 +17,60 @@ from castellan.common.objects import passphrase as key from castellan.key_manager import key_manager as km +"""sahara.service.castellan.sahara_key_manager + +This module contains the KeyManager class that will be used by the +castellan library, it is not meant for direct usage within sahara. +""" + + class SaharaKeyManager(km.KeyManager): - '''Sahara specific key manager + """Sahara specific key manager This manager is a thin wrapper around the secret being stored. It is intended for backward compatible use only. It will not store keys or generate UUIDs but instead return the secret that is being stored. This behavior allows Sahara to continue storing secrets in its database while using the Castellan key manager abstraction. - ''' + """ def __init__(self, configuration=None): pass def create_key(self, context, algorithm=None, length=0, expiration=None, **kwargs): - '''creates a key + """creates a key algorithm, length, and expiration are unused by sahara keys. - ''' + """ return key.Passphrase(passphrase=kwargs.get('passphrase', '')) def create_key_pair(self, *args, **kwargs): pass def store(self, context, key, expiration=None, **kwargs): - '''store a key + """store a key in normal usage a store_key will return the UUID of the key as dictated by the key manager. Sahara would then store this UUID in its database to use for retrieval. As sahara is not actually using a key manager in this context it will return the key's payload for storage. - ''' + """ return key.get_encoded() def get(self, context, key_id, **kwargs): - '''get a key + """get a key since sahara is not actually storing key UUIDs the key_id to this function should actually be the key payload. this function will simply return a new SaharaKey based on that value. - ''' + """ return key.Passphrase(passphrase=key_id) def delete(self, context, key_id, **kwargs): - '''delete a key + """delete a key as there is no external key manager, this function will not perform any external actions. therefore, it won't change anything. - ''' + """ pass diff --git a/sahara/service/castellan/utils.py b/sahara/service/castellan/utils.py index e14133e99f..ff83b9faf2 100644 --- a/sahara/service/castellan/utils.py +++ b/sahara/service/castellan/utils.py @@ -20,22 +20,24 @@ from sahara import context def delete_secret(id, ctx=None): - '''delete a secret from the external key manager + """delete a secret from the external key manager - if no context is provided, the current context is used. - - ''' + :param id: The identifier of the secret to delete + :param ctx: The context, and associated authentication, to use with + this operation (defaults to the current context) + """ if ctx is None: ctx = context.current() key_manager.API().delete(ctx, id) def get_secret(id, ctx=None): - '''get a secret associated with an id + """get a secret associated with an id - if no context is provided, the current context is used. - - ''' + :param id: The identifier of the secret to retrieve + :param ctx: The context, and associated authentication, to use with + this operation (defaults to the current context) + """ if ctx is None: ctx = context.current() key = key_manager.API().get(ctx, id) @@ -43,11 +45,12 @@ def get_secret(id, ctx=None): def store_secret(secret, ctx=None): - '''store a secret and return its identifier + """store a secret and return its identifier - if no context is provided, the current context is used. - - ''' + :param secret: The secret to store, this should be a string + :param ctx: The context, and associated authentication, to use with + this operation (defaults to the current context) + """ if ctx is None: ctx = context.current() key = passphrase.Passphrase(secret)