Add fernet keys purging based no t-h-t parameter
When fernet key rotations are done via the mistral action, the action will check the KeystoneFernetMaxActiveKeys and and purge the excess keys from the mistral environment or swift container. If the parameter is not present, a default value of 5 is taken. And if for some reason the user gives a smaller value, a minimum of 3 is taken into account. bp keystone-fernet-rotation Depends-On: I9c6b0708c2c03ad9918222599f8b6aad397d8089 Change-Id: I4a28073c93c210703871daa8fe660fc1914464e8
This commit is contained in:
parent
dea4a8264c
commit
fcb992a6b9
|
@ -263,11 +263,14 @@ class GetPasswordsAction(base.TripleOAction):
|
|||
|
||||
parameter_defaults = env.get('parameter_defaults', {})
|
||||
passwords = env.get('passwords', {})
|
||||
|
||||
return self._get_overriden_passwords(passwords, parameter_defaults)
|
||||
|
||||
def _get_overriden_passwords(self, env_passwords, parameter_defaults):
|
||||
for name in constants.PASSWORD_PARAMETER_NAMES:
|
||||
if name in parameter_defaults:
|
||||
passwords[name] = parameter_defaults[name]
|
||||
|
||||
return passwords
|
||||
env_passwords[name] = parameter_defaults[name]
|
||||
return env_passwords
|
||||
|
||||
|
||||
class GenerateFencingParametersAction(base.TripleOAction):
|
||||
|
@ -479,14 +482,14 @@ class RotateFernetKeysAction(GetPasswordsAction):
|
|||
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]
|
||||
passwords = self._get_overriden_passwords(env.get('passwords', {}),
|
||||
parameter_defaults)
|
||||
|
||||
next_index = self.get_next_index(passwords['KeystoneFernetKeys'])
|
||||
keys_map = self.rotate_keys(passwords['KeystoneFernetKeys'],
|
||||
next_index)
|
||||
max_keys = self.get_max_keys_value(parameter_defaults)
|
||||
keys_map = self.purge_excess_keys(max_keys, keys_map)
|
||||
|
||||
env['passwords']['KeystoneFernetKeys'] = keys_map
|
||||
|
||||
|
@ -503,14 +506,20 @@ class RotateFernetKeysAction(GetPasswordsAction):
|
|||
|
||||
return keys_map
|
||||
|
||||
@staticmethod
|
||||
def get_key_index_from_path(path):
|
||||
return int(path[path.rfind('/') + 1:])
|
||||
|
||||
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
|
||||
return self.get_key_index_from_path(
|
||||
max(keys_map, key=self.get_key_index_from_path)) + 1
|
||||
|
||||
def get_key_path(self, index):
|
||||
return password_utils.KEYSTONE_FERNET_REPO + str(index)
|
||||
|
||||
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'
|
||||
next_index_path = self.get_key_path(next_index)
|
||||
zero_index_path = self.get_key_path(0)
|
||||
|
||||
# promote staged key to be new primary
|
||||
keys_map[next_index_path] = keys_map[zero_index_path]
|
||||
|
@ -518,3 +527,20 @@ class RotateFernetKeysAction(GetPasswordsAction):
|
|||
keys_map[zero_index_path] = {
|
||||
'content': password_utils.create_keystone_credential()}
|
||||
return keys_map
|
||||
|
||||
def get_max_keys_value(self, parameter_defaults):
|
||||
# The number of max keys should always be positive. The minimum amount
|
||||
# of keys is 3.
|
||||
return max(parameter_defaults.get('KeystoneFernetMaxActiveKeys', 5), 3)
|
||||
|
||||
def purge_excess_keys(self, max_keys, keys_map):
|
||||
current_repo_size = len(keys_map)
|
||||
if current_repo_size <= max_keys:
|
||||
return keys_map
|
||||
key_paths = sorted(keys_map.keys(), key=self.get_key_index_from_path)
|
||||
|
||||
keys_to_be_purged = current_repo_size - max_keys
|
||||
|
||||
for key_path in key_paths[1:keys_to_be_purged + 1]:
|
||||
del keys_map[key_path]
|
||||
return keys_map
|
||||
|
|
|
@ -917,3 +917,54 @@ class RotateFernetKeysActionTest(base.TestCase):
|
|||
# primary key should be the previous staged key
|
||||
self.assertEqual('Some key',
|
||||
new_keys_map[new_primary_key_index]['content'])
|
||||
|
||||
def test_purge_excess_keys_should_purge(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '2': {
|
||||
'content': 'key2'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '3': {
|
||||
'content': 'key3'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '4': {
|
||||
'content': 'key4'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = action.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(max_keys, len(keys_map))
|
||||
# It should keep index 0, 3 and 4
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '0', keys_map)
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '3', keys_map)
|
||||
self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '4', keys_map)
|
||||
# It sould have removed index 1 and 2
|
||||
self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '1', keys_map)
|
||||
self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '2', keys_map)
|
||||
|
||||
def test_purge_excess_keys_should_not_purge_if_equal_to_max(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '2': {
|
||||
'content': 'key2'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = action.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(max_keys, len(keys_map))
|
||||
|
||||
def test_purge_excess_keys_should_not_purge_if_less_than_max(self):
|
||||
action = parameters.RotateFernetKeysAction()
|
||||
keys_map = {
|
||||
password_utils.KEYSTONE_FERNET_REPO + '0': {
|
||||
'content': 'key0'},
|
||||
password_utils.KEYSTONE_FERNET_REPO + '1': {
|
||||
'content': 'key1'},
|
||||
}
|
||||
max_keys = 3
|
||||
keys_map = action.purge_excess_keys(max_keys, keys_map)
|
||||
self.assertEqual(2, len(keys_map))
|
||||
|
|
Loading…
Reference in New Issue