Transition to 'keymaster'
Transitioned from 'trivial_keymaster' with constant root key to 'keymaster' with root key set in proxy-server.conf. Change-Id: Ib5051bc402d5758f149c6afbaee7ccf06c44bbca
This commit is contained in:
parent
19226e4449
commit
8cb5505bf1
|
@ -717,13 +717,17 @@ use = egg:swift#versioned_writes
|
|||
|
||||
# Note: To enable encryption, add the following 3 dependent pieces of
|
||||
# crypto middleware to the proxy-server pipeline as follows:
|
||||
# ... decrypter trivial_keymaster encrypter proxy-logging (end of pipeline)
|
||||
# ... decrypter keymaster encrypter proxy-logging (end of pipeline)
|
||||
|
||||
[filter:decrypter]
|
||||
use = egg:swift#decrypter
|
||||
|
||||
[filter:trivial_keymaster]
|
||||
use = egg:swift#trivial_keymaster
|
||||
[filter:keymaster]
|
||||
use = egg:swift#keymaster
|
||||
|
||||
# Sets the root key from which encryption keys are derived. Change before
|
||||
# first use. After that, changing the key may result in data loss.
|
||||
encryption_root_secret = change_before_use
|
||||
|
||||
[filter:encrypter]
|
||||
use = egg:swift#encrypter
|
||||
|
|
|
@ -97,7 +97,7 @@ paste.filter_factory =
|
|||
xprofile = swift.common.middleware.xprofile:filter_factory
|
||||
versioned_writes = swift.common.middleware.versioned_writes:filter_factory
|
||||
fake_footers = swift.common.middleware.fake_footers:filter_factory
|
||||
trivial_keymaster = swift.common.middleware.trivial_keymaster:filter_factory
|
||||
keymaster = swift.common.middleware.keymaster:filter_factory
|
||||
decrypter = swift.common.middleware.decrypter:filter_factory
|
||||
encrypter = swift.common.middleware.encrypter:filter_factory
|
||||
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
# limitations under the License.
|
||||
|
||||
"""
|
||||
The simple scheme used here for testing the encryption feature in swift is as
|
||||
follows: every path is associated with a key, where the key is derived from the
|
||||
The simple scheme for key derivation is as follows:
|
||||
every path is associated with a key, where the key is derived from the
|
||||
path itself in a deterministic fashion such that the key does not need to be
|
||||
stored. Specifically, the key for any path is an HMAC of a root key and the
|
||||
path itself, calculated using an SHA256 hash function::
|
||||
|
||||
<path_key> = HMAC_SHA256(<root_key>, <path>)
|
||||
<path_key> = HMAC_SHA256(<root_secret>, <path>)
|
||||
"""
|
||||
|
||||
import base64
|
||||
|
@ -35,9 +35,9 @@ from swift.common.wsgi import WSGIContext
|
|||
from swift.common.swob import Request, HTTPException, HTTPUnprocessableEntity
|
||||
|
||||
|
||||
class TrivialKeyMasterContext(WSGIContext):
|
||||
class KeyMasterContext(WSGIContext):
|
||||
def __init__(self, keymaster, account, container, obj):
|
||||
super(TrivialKeyMasterContext, self).__init__(keymaster.app)
|
||||
super(KeyMasterContext, self).__init__(keymaster.app)
|
||||
self.keymaster = keymaster
|
||||
self.logger = keymaster.logger
|
||||
self.account = account
|
||||
|
@ -169,15 +169,16 @@ class TrivialKeyMasterContext(WSGIContext):
|
|||
return self.keys
|
||||
|
||||
|
||||
class TrivialKeyMaster(object):
|
||||
"""
|
||||
Encryption keymaster middleware for testing. Don't use in production.
|
||||
"""
|
||||
class KeyMaster(object):
|
||||
|
||||
def __init__(self, app, conf):
|
||||
self.app = app
|
||||
self.logger = get_logger(conf, log_route="trivial_keymaster")
|
||||
# TODO: consider optionally loading root key from conf
|
||||
self.root_key = 'secret'.encode('utf-8')
|
||||
self.logger = get_logger(conf, log_route="keymaster")
|
||||
self.root_secret = conf.get('encryption_root_secret', None)
|
||||
if not self.root_secret:
|
||||
raise ValueError('encryption_root_secret not set in '
|
||||
'proxy-server.conf')
|
||||
self.root_secret = self.root_secret.encode('utf-8')
|
||||
|
||||
def __call__(self, env, start_response):
|
||||
req = Request(env)
|
||||
|
@ -187,9 +188,9 @@ class TrivialKeyMaster(object):
|
|||
except ValueError:
|
||||
return self.app(env, start_response)
|
||||
|
||||
if hasattr(TrivialKeyMasterContext, req.method):
|
||||
if hasattr(KeyMasterContext, req.method):
|
||||
# handle only those request methods that may require keys
|
||||
km_context = TrivialKeyMasterContext(self, *parts[1:])
|
||||
km_context = KeyMasterContext(self, *parts[1:])
|
||||
try:
|
||||
return getattr(km_context, req.method)(req, start_response)
|
||||
except HTTPException as err_resp:
|
||||
|
@ -199,7 +200,11 @@ class TrivialKeyMaster(object):
|
|||
return self.app(env, start_response)
|
||||
|
||||
def create_key(self, key_id):
|
||||
return hmac.new(self.root_key, key_id,
|
||||
key_id = 'fixed'
|
||||
# TODO: setting key_id to 'fixed' is a temporary workaround for
|
||||
# problems caused by the lack of copy middleware. Once that is merged,
|
||||
# we can remove the above line.
|
||||
return hmac.new(self.root_secret, key_id,
|
||||
digestmod=hashlib.sha256).digest()
|
||||
|
||||
|
||||
|
@ -207,7 +212,7 @@ def filter_factory(global_conf, **local_conf):
|
|||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
|
||||
def trivial_keymaster_filter(app):
|
||||
return TrivialKeyMaster(app, conf)
|
||||
def keymaster_filter(app):
|
||||
return KeyMaster(app, conf)
|
||||
|
||||
return trivial_keymaster_filter
|
||||
return keymaster_filter
|
|
@ -16,7 +16,7 @@ import base64
|
|||
|
||||
import unittest
|
||||
|
||||
from swift.common.middleware import trivial_keymaster
|
||||
from swift.common.middleware import keymaster
|
||||
from swift.common import swob
|
||||
Request = swob.Request
|
||||
|
||||
|
@ -31,10 +31,10 @@ def capture_start_response():
|
|||
return start_response, calls
|
||||
|
||||
|
||||
class TestTrivialKeymaster(unittest.TestCase):
|
||||
class TestKeymaster(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestTrivialKeymaster, self).setUp()
|
||||
super(TestKeymaster, self).setUp()
|
||||
self.swift = FakeSwift()
|
||||
|
||||
def test_object_path(self):
|
||||
|
@ -48,7 +48,8 @@ class TestTrivialKeymaster(unittest.TestCase):
|
|||
|
||||
def verify_keys_for_path(self, path, expected_keys, key_id=None):
|
||||
put_keys = None
|
||||
app = trivial_keymaster.TrivialKeyMaster(self.swift, {})
|
||||
app = keymaster.KeyMaster(self.swift,
|
||||
{'encryption_root_secret': 'secret'})
|
||||
for method, resp_class, status in (
|
||||
('PUT', swob.HTTPCreated, '201'),
|
||||
('POST', swob.HTTPAccepted, '202'),
|
||||
|
@ -85,7 +86,8 @@ class TestTrivialKeymaster(unittest.TestCase):
|
|||
# first get keys when path matches key_id
|
||||
method = 'HEAD'
|
||||
self.swift.register(method, path, swob.HTTPOk, resp_headers, '')
|
||||
app = trivial_keymaster.TrivialKeyMaster(self.swift, {})
|
||||
app = keymaster.KeyMaster(self.swift,
|
||||
{'encryption_root_secret': 'secret'})
|
||||
req = Request.blank(path, environ={'REQUEST_METHOD': method})
|
||||
start_response, calls = capture_start_response()
|
||||
app(req.environ, start_response)
|
||||
|
@ -98,7 +100,8 @@ class TestTrivialKeymaster(unittest.TestCase):
|
|||
path = '/v1/a/got/relocated'
|
||||
for method in ('HEAD', 'GET'):
|
||||
self.swift.register(method, path, swob.HTTPOk, resp_headers, '')
|
||||
app = trivial_keymaster.TrivialKeyMaster(self.swift, {})
|
||||
app = keymaster.KeyMaster(self.swift,
|
||||
{'encryption_root_secret': 'secret'})
|
||||
req = Request.blank(path, environ={'REQUEST_METHOD': method})
|
||||
start_response, calls = capture_start_response()
|
||||
app(req.environ, start_response)
|
||||
|
@ -113,7 +116,8 @@ class TestTrivialKeymaster(unittest.TestCase):
|
|||
for method in ('HEAD', 'GET'):
|
||||
path = '/v1/a/c/o'
|
||||
self.swift.register(method, path, swob.HTTPOk, {}, '')
|
||||
app = trivial_keymaster.TrivialKeyMaster(self.swift, {})
|
||||
app = keymaster.KeyMaster(self.swift,
|
||||
{'encryption_root_secret': 'secret'})
|
||||
req = Request.blank(path, environ={'REQUEST_METHOD': method})
|
||||
start_response, calls = capture_start_response()
|
||||
app(req.environ, start_response)
|
||||
|
@ -130,13 +134,14 @@ class TestTrivialKeymaster(unittest.TestCase):
|
|||
self.swift.register(method, path, swob.HTTPOk,
|
||||
{'x-object-sysmeta-crypto-meta-foo': 'gotcha'},
|
||||
'')
|
||||
app = trivial_keymaster.TrivialKeyMaster(self.swift, {})
|
||||
app = keymaster.KeyMaster(self.swift,
|
||||
{'encryption_root_secret': 'secret'})
|
||||
req = Request.blank(path, environ={'REQUEST_METHOD': method})
|
||||
start_response, calls = capture_start_response()
|
||||
app(req.environ, start_response)
|
||||
self.assertEqual(1, len(calls))
|
||||
# TODO change to expect 422 once FakeFooters is removed.
|
||||
# error_if_need_keys is currently disabled in trivial_keymaster
|
||||
# error_if_need_keys is currently disabled in keymaster
|
||||
# because of how FakeFooters works. So 422's will not currently be
|
||||
# returned when keys are 'missing' and crypto-meta is found.
|
||||
self.assertEqual('200 OK', calls[0][0])
|
||||
|
@ -151,7 +156,8 @@ class TestTrivialKeymaster(unittest.TestCase):
|
|||
'x-object-meta-crypto-meta': 'no probs',
|
||||
'crypto-meta': 'pas de problem'},
|
||||
'')
|
||||
app = trivial_keymaster.TrivialKeyMaster(self.swift, {})
|
||||
app = keymaster.KeyMaster(self.swift,
|
||||
{'encryption_root_secret': 'secret'})
|
||||
req = Request.blank(path, environ={'REQUEST_METHOD': method})
|
||||
start_response, calls = capture_start_response()
|
||||
app(req.environ, start_response)
|
||||
|
@ -159,17 +165,30 @@ class TestTrivialKeymaster(unittest.TestCase):
|
|||
self.assertEqual('200 OK', calls[0][0])
|
||||
|
||||
def test_filter(self):
|
||||
factory = trivial_keymaster.filter_factory({})
|
||||
factory = keymaster.filter_factory(
|
||||
{'encryption_root_secret': 'secret'})
|
||||
self.assertTrue(callable(factory))
|
||||
self.assertTrue(callable(factory(self.swift)))
|
||||
|
||||
def test_app_exception(self):
|
||||
app = trivial_keymaster.TrivialKeyMaster(
|
||||
FakeAppThatExcepts(), {})
|
||||
app = keymaster.KeyMaster(
|
||||
FakeAppThatExcepts(), {'encryption_root_secret': 'secret'})
|
||||
req = Request.blank('/', environ={'REQUEST_METHOD': 'PUT'})
|
||||
start_response, _ = capture_start_response()
|
||||
self.assertRaises(Exception, app, req.environ, start_response)
|
||||
|
||||
def test_key_loaded(self):
|
||||
app = keymaster.KeyMaster(self.swift,
|
||||
{'encryption_root_secret': 'secret'})
|
||||
self.assertEqual(app.root_secret, 'secret')
|
||||
|
||||
def test_no_root_secret_error(self):
|
||||
with self.assertRaises(ValueError) as err:
|
||||
keymaster.KeyMaster(self.swift, {})
|
||||
|
||||
self.assertEqual(err.exception.message,
|
||||
'encryption_root_secret not set in proxy-server.conf')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue