summaryrefslogtreecommitdiffstats
path: root/keystone/contrib/kds/backends/sql.py
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-06-27 22:16:25 -0400
committerSimo Sorce <simo@redhat.com>2013-08-20 11:54:39 -0400
commit86cf469e4feed55f5b6dfc8ab0f139b39afb75b8 (patch)
tree0f067718c9bb1ca7e4e80ce8bfd7406b78c6821f /keystone/contrib/kds/backends/sql.py
parentffa55f7a8cbc824b03cec8cbfbb380b42f9c3e70 (diff)
downloadkeystone-86cf469e4feed55f5b6dfc8ab0f139b39afb75b8.tar.gz
keystone-86cf469e4feed55f5b6dfc8ab0f139b39afb75b8.tar.xz
keystone-86cf469e4feed55f5b6dfc8ab0f139b39afb75b8.zip
Add group key support
A requestor asking for a key for a target identified as a group object will receive a group_key ticket. Group keys are temporary keys with a limited timelife and are released together with a generation number. Multiple keys with different generation numbers may exist at the same time. When no valid keys are found or if the only valid key has less than 10 minutes of lifetime a new key is generated using the next available generation number. Generation numbers grow monotonically. Group keys can be retrieved using the get_group_key call only by requestors belonging to the group. A requestor is considered as belonging to a group if the first part of the name is the same as the group. Requestors must specify a valid generation number when requesting a group key. The generation number is used to create the destination name by postfixing it to the group name after a colon. Example: requestor: scheduler.xyz.example.com destination: scheduler:123 The requestor is considered part of the scheduler group and asks for a key of generation number 123. If that key exist it will be returned encrypted with the requestor's key. blueprint key-distribution-server Change-Id: I013ae466d626c0a4737d475e1b42b183a88dbe83 Signed-off-by: Simo Sorce <simo@redhat.com>
Diffstat (limited to 'keystone/contrib/kds/backends/sql.py')
-rw-r--r--keystone/contrib/kds/backends/sql.py111
1 files changed, 110 insertions, 1 deletions
diff --git a/keystone/contrib/kds/backends/sql.py b/keystone/contrib/kds/backends/sql.py
index b7878f2a..36dec215 100644
--- a/keystone/contrib/kds/backends/sql.py
+++ b/keystone/contrib/kds/backends/sql.py
@@ -15,10 +15,43 @@
# under the License.
import base64
+import copy
import hashlib
+import time
from keystone.common import sql
+'''
+There are 2 types of keys we save here.
+Individual keys and group keys.
+
+Individual keys are identified by an id string and in 'key' is a
+dictionary the contains only the base64-encoded encrypted key in a
+field named 'key_v1'.
+Example:
+ id = d22b35312b77798eef75f195c2ed8ea78d2c26a98dedfa1c87ff0d4660636cc9
+ name = 'compute.abc.example.com'
+ key = {'key_v1': 'ERWERewre.....'}
+
+Group keys use a more complex key payload, where multiple
+'generations' of keys are stored with expiration times, as well as a
+generation counter.
+Example:
+ id = a02ba163f3a02db22fdd14b310119f1a4c2f9e4a773a573f757904dd5433d4dd
+ name = 'scheduler'
+ key = {'generation': 10,
+ 'group_keys': {'9': {'key_v1': 'ERWERewre.....',
+ 'expiration': 1234567890
+ },
+ '10': {'key_v1': 'GFDSAFhjhkd.....',
+ 'expiration': 1234509876
+ }
+ }
+
+'''
+
+EXPIRATION_TIME = 600
+
class Keys(sql.ModelBase, sql.DictBase):
__tablename__ = 'kds_keys'
@@ -58,6 +91,82 @@ class KDS(sql.Base):
session.add(key_ref)
session.flush()
+ def set_group_key(self, kds_id, expiration=None, key=None):
+ """Creates a new group of keys or sets a new key
+
+ If key is empty this function will create a abre entry in the
+ database or fail if one exists, in this case we return 0 on
+ success or an exception.
+ When a key is provided the database entry is updated with the
+ new key. Additionally any expired keys are removed.
+ We return the generation number associated to the new key on
+ success or an exception.
+
+ :param kds_id: the name/id of the key
+ :param expiration: the requested expiration time for the key
+ :param key: the actual key material (will be base64 encoded
+ before being stored in the db)
+ :returns: 0 when crating a new key, or the generation number
+ """
+
+ session = self.get_session()
+
+ if key is None:
+ # Create new group
+ with session.begin():
+ new_group = {'id': self._id_from_name(kds_id),
+ 'name': kds_id,
+ 'key': {'generation': 0, 'group_keys': dict()}}
+ key_ref = Keys.from_dict(new_group)
+ session.add(key_ref)
+ session.flush()
+ return 0
+
+ key_ref = None
+ cur_val = None
+ new_val = None
+ gen = 0
+ with session.begin():
+ id = self._id_from_name(kds_id)
+ key_ref = session.query(Keys).filter_by(id=id).first()
+ cur_val = key_ref.to_dict()
+
+ # get current entry if any, parse it and retrieve each
+ # referenced generation key, then delete all the keys expired
+ # by more than 10 minutes in a deepcopy
+ k = cur_val['key']
+ if 'generation' in k:
+ gen = k['generation']
+ if 'group_keys' in k:
+ purge_time = time.time() - EXPIRATION_TIME
+ ks = k['group_keys']
+ for g in ks:
+ if ks[g]['expiration'] < purge_time:
+ if new_val is None:
+ new_val = copy.deepcopy(cur_val)
+ del new_val['key']['group_keys'][g]
+
+ if new_val is None:
+ new_val = copy.deepcopy(cur_val)
+
+ gen = gen + 1
+ bkey = base64.b64encode(key)
+ new_val['key']['generation'] = gen
+ new_val['key']['group_keys'][gen] = {'expiration': expiration,
+ 'key_v1': bkey}
+
+ # first delete old if any
+ if key_ref is not None:
+ session.delete(key_ref)
+
+ # now store new generation list
+ key_ref = Keys.from_dict(new_val)
+ session.add(key_ref)
+
+ session.flush()
+
+ return gen
+
def get_shared_key(self, kds_id):
session = self.get_session()
id = self._id_from_name(kds_id)
@@ -65,4 +174,4 @@ class KDS(sql.Base):
if not key_ref:
return None
d = key_ref.to_dict()
- return base64.b64decode(d['key']['key_v1'])
+ return d['key']