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
This commit is contained in:
Michael McCune 2016-01-11 14:16:40 -05:00
parent 423d80498b
commit 3625f307f9
4 changed files with 91 additions and 22 deletions

View File

@ -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 <http://docs.openstack.org/developer/barbican/>`_) through
the `castellan library <http://docs.openstack.org/developer/castellan/>`_.
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`.

View File

@ -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
--------------------------

View File

@ -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

View File

@ -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)