summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2012-01-11 10:42:00 -0800
committerVishvananda Ishaya <vishvananda@gmail.com>2012-01-23 16:40:15 -0800
commita47b5276244dba9c0a9058e6868522cf3fb3cada (patch)
treeec56c0328d88407a66e1665390cb22060ec75b1d
parentaaa7f7ed456cb9103b07ae629492f3a343ace9d8 (diff)
Brings back keystone middleware
* Adds ec2 auth middleware * Adds keystonecontext middleware * Notes alternative configurations in paste config * Fixes nova portion of bug 917408 Change-Id: I9909d6d235445b1413d1cc0fb61e99e1ab819438
-rw-r--r--etc/nova/api-paste.ini36
-rw-r--r--nova/api/auth.py41
-rw-r--r--nova/api/ec2/__init__.py62
3 files changed, 137 insertions, 2 deletions
diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini
index e03697f8e..2dc37439b 100644
--- a/etc/nova/api-paste.ini
+++ b/etc/nova/api-paste.ini
@@ -38,12 +38,16 @@ use = egg:Paste#urlmap
[pipeline:ec2cloud]
pipeline = ec2faultwrap logrequest ec2noauth cloudrequest authorizer validator ec2executor
# NOTE(vish): use the following pipeline for deprecated auth
-#pipeline = ec2faultwrap logrequest authenticate cloudrequest authorizer validator ec2executor
+# pipeline = ec2faultwrap logrequest authenticate cloudrequest authorizer validator ec2executor
+# NOTE(vish): use the following pipeline for keystone auth
+# pipeline = ec2faultwrap logrequest totoken authtoken keystonecontext cloudrequest authorizer validator ec2executor
[pipeline:ec2admin]
pipeline = ec2faultwrap logrequest ec2noauth adminrequest authorizer ec2executor
# NOTE(vish): use the following pipeline for deprecated auth
-#pipeline = ec2faultwrap logrequest authenticate adminrequest authorizer ec2executor
+# pipeline = ec2faultwrap logrequest authenticate adminrequest authorizer ec2executor
+# NOTE(vish): use the following pipeline for keystone auth
+# pipeline = ec2faultwrap logrequest totoken authtoken keystonecontext adminrequest authorizer ec2executor
[filter:ec2faultwrap]
paste.filter_factory = nova.api.ec2:FaultWrapper.factory
@@ -54,6 +58,9 @@ paste.filter_factory = nova.api.ec2:RequestLogging.factory
[filter:ec2lockout]
paste.filter_factory = nova.api.ec2:Lockout.factory
+[filter:totoken]
+paste.filter_factory = nova.api.ec2:EC2Token.factory
+
[filter:ec2noauth]
paste.filter_factory = nova.api.ec2:NoAuth.factory
@@ -96,9 +103,15 @@ use = call:nova.api.openstack.urlmap:urlmap_factory
pipeline = faultwrap noauth ratelimit osapi_compute_app_v2
# NOTE(vish): use the following pipeline for deprecated auth
# pipeline = faultwrap auth ratelimit osapi_compute_app_v2
+# NOTE(vish): use the following pipeline for keystone auth
+# pipeline = faultwrap authtoken keystonecontext ratelimit osapi_compute_app_v2
[pipeline:openstack_volume_api_v1]
pipeline = faultwrap noauth ratelimit osapi_volume_app_v1
+# NOTE(vish): use the following pipeline for deprecated auth
+# pipeline = faultwrap auth ratelimit osapi_volume_app_v1
+# NOTE(vish): use the following pipeline for keystone auth
+# pipeline = faultwrap authtoken keystonecontext ratelimit osapi_volume_app_v1
[filter:faultwrap]
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
@@ -129,3 +142,22 @@ pipeline = faultwrap osvolumeversionapp
[app:osvolumeversionapp]
paste.app_factory = nova.api.openstack.volume.versions:Versions.factory
+
+##########
+# Shared #
+##########
+
+[filter:keystonecontext]
+paste.filter_factory = nova.api.auth.nova_keystone_context:NovaKeystoneContext.factory
+
+[filter:authtoken]
+paste.filter_factory = keystone.middleware.auth_token:filter_factory
+service_protocol = http
+service_host = 127.0.0.1
+service_port = 5000
+auth_host = 127.0.0.1
+auth_port = 35357
+auth_protocol = http
+auth_uri = http://127.0.0.1:5000/
+# NOTE(vish): you will have to replace the value below with an actual admin token
+admin_token = %SERVICE_TOKEN%
diff --git a/nova/api/auth.py b/nova/api/auth.py
index 6be22620c..54fff94df 100644
--- a/nova/api/auth.py
+++ b/nova/api/auth.py
@@ -21,7 +21,9 @@ Common Auth Middleware.
import webob.dec
import webob.exc
+from nova import context
from nova import flags
+from nova import log as logging
from nova import wsgi
@@ -42,3 +44,42 @@ class InjectContext(wsgi.Middleware):
def __call__(self, req):
req.environ['nova.context'] = self.context
return self.application
+
+
+class NovaKeystoneContext(wsgi.Middleware):
+ """Make a request context from keystone headers"""
+
+ @webob.dec.wsgify(RequestClass=wsgi.Request)
+ def __call__(self, req):
+ try:
+ user_id = req.headers['X_USER']
+ except KeyError:
+ logging.debug("X_USER not found in request")
+ return webob.exc.HTTPUnauthorized()
+ # get the roles
+ roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')]
+ if 'X_TENANT_ID' in req.headers:
+ # This is the new header since Keystone went to ID/Name
+ project_id = req.headers['X_TENANT_ID']
+ else:
+ # This is for legacy compatibility
+ project_id = req.headers['X_TENANT']
+
+ # Get the auth token
+ auth_token = req.headers.get('X_AUTH_TOKEN',
+ req.headers.get('X_STORAGE_TOKEN'))
+
+ # Build a context, including the auth_token...
+ remote_address = getattr(req, 'remote_address', '127.0.0.1')
+ remote_address = req.remote_addr
+ if FLAGS.use_forwarded_for:
+ remote_address = req.headers.get('X-Forwarded-For', remote_address)
+ ctx = context.RequestContext(user_id,
+ project_id,
+ roles=roles,
+ auth_token=auth_token,
+ strategy='keystone',
+ remote_address=remote_address)
+
+ req.environ['nova.context'] = ctx
+ return self.application
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index 0dd97f8de..b052f7010 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -20,7 +20,9 @@ Starting point for routing EC2 requests.
"""
+import urlparse
+from eventlet.green import httplib
import webob
import webob.dec
import webob.exc
@@ -45,6 +47,9 @@ flags.DEFINE_integer('lockout_minutes', 15,
'Number of minutes to lockout if triggered.')
flags.DEFINE_integer('lockout_window', 15,
'Number of minutes for lockout window.')
+flags.DEFINE_string('keystone_ec2_url',
+ 'http://localhost:5000/v2.0/ec2tokens',
+ 'URL to get token from ec2 request.')
flags.DECLARE('use_forwarded_for', 'nova.api.auth')
@@ -151,6 +156,63 @@ class Lockout(wsgi.Middleware):
return res
+class EC2Token(wsgi.Middleware):
+ """Authenticate an EC2 request with keystone and convert to token."""
+
+ @webob.dec.wsgify(RequestClass=wsgi.Request)
+ def __call__(self, req):
+ # Read request signature and access id.
+ try:
+ signature = req.params['Signature']
+ access = req.params['AWSAccessKeyId']
+ except KeyError, e:
+ LOG.exception(e)
+ raise webob.exc.HTTPBadRequest()
+
+ # Make a copy of args for authentication and signature verification.
+ auth_params = dict(req.params)
+ # Not part of authentication args
+ auth_params.pop('Signature')
+
+ # Authenticate the request.
+ creds = {'ec2Credentials': {'access': access,
+ 'signature': signature,
+ 'host': req.host,
+ 'verb': req.method,
+ 'path': req.path,
+ 'params': auth_params,
+ }}
+ creds_json = utils.dumps(creds)
+ headers = {'Content-Type': 'application/json'}
+
+ # Disable "has no x member" pylint error
+ # for httplib and urlparse
+ # pylint: disable-msg=E1101
+ o = urlparse.urlparse(FLAGS.keystone_ec2_url)
+ if o.scheme == "http":
+ conn = httplib.HTTPConnection(o.netloc)
+ else:
+ conn = httplib.HTTPSConnection(o.netloc)
+ conn.request('POST', o.path, body=creds_json, headers=headers)
+ response = conn.getresponse().read()
+ conn.close()
+
+ # NOTE(vish): We could save a call to keystone by
+ # having keystone return token, tenant,
+ # user, and roles from this call.
+
+ result = utils.loads(response)
+ try:
+ token_id = result['access']['token']['id']
+ except (AttributeError, KeyError), e:
+ LOG.exception(e)
+ raise webob.exc.HTTPBadRequest()
+
+ # Authenticated!
+ req.headers['X-Auth-Token'] = token_id
+ return self.application
+
+
class NoAuth(wsgi.Middleware):
"""Add user:project as 'nova.context' to WSGI environ."""