Create mistral action to rotate fernet keys from passwords variable
This grabs the fernet keys from the passwords environment variable and performs a rotation based on those keys. Subsequently, this parameter will be passed to t-h-t in order for puppet to persist the rotation on a stack update. Also, further work will be done in order for the deployer to be able to do this without having to do a full stack update. bp keystone-fernet-rotation Change-Id: I18a3669e04021ad499973073a91e6bf78741ed20
This commit is contained in:
parent
017a6da817
commit
4bc5c71a3e
@ -454,3 +454,67 @@ class GetProfileOfFlavorAction(base.TripleOAction):
|
||||
except exception.DeriveParamsError as err:
|
||||
LOG.error('Derive Params Error: %s', err)
|
||||
return mistral_workflow_utils.Result(error=str(err))
|
||||
|
||||
|
||||
class RotateFernetKeysAction(GetPasswordsAction):
|
||||
"""Rotate fernet keys from the environment
|
||||
|
||||
This method rotates the fernet keys that are saved in the environment, in
|
||||
the passwords parameter.
|
||||
"""
|
||||
|
||||
def __init__(self, container=constants.DEFAULT_CONTAINER_NAME):
|
||||
super(RotateFernetKeysAction, self).__init__()
|
||||
self.container = container
|
||||
|
||||
def run(self, context):
|
||||
swift = self.get_object_client(context)
|
||||
|
||||
try:
|
||||
env = plan_utils.get_env(swift, self.container)
|
||||
except swiftexceptions.ClientException as err:
|
||||
err_msg = ("Error retrieving environment for plan %s: %s" % (
|
||||
self.container, err))
|
||||
LOG.exception(err_msg)
|
||||
return mistral_workflow_utils.Result(error=err_msg)
|
||||
|
||||
parameter_defaults = env.get('parameter_defaults', {})
|
||||
passwords = env.get('passwords', {})
|
||||
for name in constants.PASSWORD_PARAMETER_NAMES:
|
||||
if name in parameter_defaults:
|
||||
passwords[name] = parameter_defaults[name]
|
||||
|
||||
next_index = self.get_next_index(passwords['KeystoneFernetKeys'])
|
||||
keys_map = self.rotate_keys(passwords['KeystoneFernetKeys'],
|
||||
next_index)
|
||||
|
||||
env['passwords']['KeystoneFernetKeys'] = keys_map
|
||||
|
||||
try:
|
||||
plan_utils.put_env(swift, env)
|
||||
except swiftexceptions.ClientException as err:
|
||||
err_msg = "Error uploading to container: %s" % err
|
||||
LOG.exception(err_msg)
|
||||
return mistral_workflow_utils.Result(error=err_msg)
|
||||
|
||||
self.cache_delete(context,
|
||||
self.container,
|
||||
"tripleo.parameters.get")
|
||||
|
||||
return keys_map
|
||||
|
||||
def get_next_index(self, keys_map):
|
||||
def get_index(path):
|
||||
return int(path[path.rfind('/') + 1:])
|
||||
return get_index(max(keys_map, key=get_index)) + 1
|
||||
|
||||
def rotate_keys(self, keys_map, next_index):
|
||||
next_index_path = password_utils.KEYSTONE_FERNET_REPO + str(next_index)
|
||||
zero_index_path = password_utils.KEYSTONE_FERNET_REPO + '0'
|
||||
|
||||
# promote staged key to be new primary
|
||||
keys_map[next_index_path] = keys_map[zero_index_path]
|
||||
# Set new staged key
|
||||
keys_map[zero_index_path] = {
|
||||
'content': password_utils.create_keystone_credential()}
|
||||
return keys_map
|
||||
|
@ -21,6 +21,7 @@ from tripleo_common.actions import parameters
|
||||
from tripleo_common import constants
|
||||
from tripleo_common import exception
|
||||
from tripleo_common.tests import base
|
||||
from tripleo_common.utils import passwords as password_utils
|
||||
|
||||
_EXISTING_PASSWORDS = {
|
||||
'MistralPassword': 'VFJeqBKbatYhQm9jja67hufft',
|
||||
@ -879,3 +880,40 @@ class GetProfileOfFlavorActionTest(base.TestCase):
|
||||
result = action.run(mock_ctx)
|
||||
self.assertTrue(result.is_error())
|
||||
mock_get_profile_of_flavor.assert_called_once()
|
||||
|
||||
|
||||
class RotateFernetKeysActionTest(base.TestCase):
|
||||
|
||||
def test_get_next_index(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'Some key'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'Some other key'},
|
||||
}
|
||||
next_index = action.get_next_index(keys_map)
|
||||
self.assertEqual(next_index, 2)
|
||||
|
||||
@mock.patch('tripleo_common.utils.passwords.'
|
||||
'create_keystone_credential')
|
||||
def test_rotate_keys(self, mock_keystone_creds):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
mock_keystone_creds.return_value = 'Some new key'
|
||||
|
||||
staged_key_index = password_utils.KEYSTONE_FERNET_REPO + '0'
|
||||
new_primary_key_index = password_utils.KEYSTONE_FERNET_REPO + '2'
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'Some key'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'Some other key'},
|
||||
}
|
||||
new_keys_map = action.rotate_keys(keys_map, 2)
|
||||
|
||||
# Staged key should be the new key
|
||||
self.assertEqual('Some new key',
|
||||
new_keys_map[staged_key_index]['content'])
|
||||
# primary key should be the previous staged key
|
||||
self.assertEqual('Some key',
|
||||
new_keys_map[new_primary_key_index]['content'])
|
||||
|
@ -27,6 +27,7 @@ from tripleo_common import constants
|
||||
|
||||
|
||||
_MIN_PASSWORD_SIZE = 25
|
||||
KEYSTONE_FERNET_REPO = '/etc/keystone/fernet-keys/'
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -80,9 +81,9 @@ def generate_passwords(mistralclient=None, stack_env=None):
|
||||
|
||||
def create_fernet_keys_repo_structure_and_keys():
|
||||
return {
|
||||
'/etc/keystone/fernet-keys/0': {
|
||||
KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': create_keystone_credential()},
|
||||
'/etc/keystone/fernet-keys/1': {
|
||||
KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': create_keystone_credential()}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user