Make ec2 auth support v4 signature format
Extract the signature and access key via whatever method is needed
for the version of the request (e.g headers for v4), and add the
headers and hashed body, which is required for keystone to calculate
the correct v4 signature when validating the request.
Conflicts:
nova/api/ec2/__init__.py
Change-Id: I161eccc4ea48a21a80d689f6a328ca95cace2e6e
Closes-Bug: #1408987
(cherry picked from commit f7b1af9e13
)
This commit is contained in:
parent
d9bcfab1e9
commit
56e3dd3331
|
@ -18,6 +18,8 @@ Starting point for routing EC2 requests.
|
|||
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
|
||||
from eventlet.green import httplib
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
|
@ -180,15 +182,51 @@ class Lockout(wsgi.Middleware):
|
|||
class EC2KeystoneAuth(wsgi.Middleware):
|
||||
"""Authenticate an EC2 request with keystone and convert to context."""
|
||||
|
||||
def _get_signature(self, req):
|
||||
"""Extract the signature from the request.
|
||||
|
||||
This can be a get/post variable or for version 4 also in a header
|
||||
called 'Authorization'.
|
||||
- params['Signature'] == version 0,1,2,3
|
||||
- params['X-Amz-Signature'] == version 4
|
||||
- header 'Authorization' == version 4
|
||||
"""
|
||||
sig = req.params.get('Signature') or req.params.get('X-Amz-Signature')
|
||||
if sig is None and 'Authorization' in req.headers:
|
||||
auth_str = req.headers['Authorization']
|
||||
sig = auth_str.partition("Signature=")[2].split(',')[0]
|
||||
|
||||
return sig
|
||||
|
||||
def _get_access(self, req):
|
||||
"""Extract the access key identifier.
|
||||
|
||||
For version 0/1/2/3 this is passed as the AccessKeyId parameter, for
|
||||
version 4 it is either an X-Amz-Credential parameter or a Credential=
|
||||
field in the 'Authorization' header string.
|
||||
"""
|
||||
access = req.params.get('AWSAccessKeyId')
|
||||
if access is None:
|
||||
cred_param = req.params.get('X-Amz-Credential')
|
||||
if cred_param:
|
||||
access = cred_param.split("/")[0]
|
||||
|
||||
if access is None and 'Authorization' in req.headers:
|
||||
auth_str = req.headers['Authorization']
|
||||
cred_str = auth_str.partition("Credential=")[2].split(',')[0]
|
||||
access = cred_str.split("/")[0]
|
||||
|
||||
return access
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
request_id = context.generate_request_id()
|
||||
signature = req.params.get('Signature')
|
||||
signature = self._get_signature(req)
|
||||
if not signature:
|
||||
msg = _("Signature not provided")
|
||||
return faults.ec2_error_response(request_id, "AuthFailure", msg,
|
||||
status=400)
|
||||
access = req.params.get('AWSAccessKeyId')
|
||||
access = self._get_access(req)
|
||||
if not access:
|
||||
msg = _("Access key not provided")
|
||||
return faults.ec2_error_response(request_id, "AuthFailure", msg,
|
||||
|
@ -197,8 +235,9 @@ class EC2KeystoneAuth(wsgi.Middleware):
|
|||
# Make a copy of args for authentication and signature verification.
|
||||
auth_params = dict(req.params)
|
||||
# Not part of authentication args
|
||||
auth_params.pop('Signature')
|
||||
auth_params.pop('Signature', None)
|
||||
|
||||
body_hash = hashlib.sha256(req.body).hexdigest()
|
||||
cred_dict = {
|
||||
'access': access,
|
||||
'signature': signature,
|
||||
|
@ -206,6 +245,8 @@ class EC2KeystoneAuth(wsgi.Middleware):
|
|||
'verb': req.method,
|
||||
'path': req.path,
|
||||
'params': auth_params,
|
||||
'headers': req.headers,
|
||||
'body_hash': body_hash
|
||||
}
|
||||
if "ec2" in CONF.keystone_ec2_url:
|
||||
creds = {'ec2Credentials': cred_dict}
|
||||
|
@ -295,6 +336,9 @@ class Requestify(wsgi.Middleware):
|
|||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
# Not all arguments are mandatory with v4 signatures, as some data is
|
||||
# passed in the header, not query arguments.
|
||||
required_args = ['Action', 'Version']
|
||||
non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod',
|
||||
'SignatureVersion', 'Version', 'Timestamp']
|
||||
args = dict(req.params)
|
||||
|
@ -309,14 +353,18 @@ class Requestify(wsgi.Middleware):
|
|||
# Raise KeyError if omitted
|
||||
action = req.params['Action']
|
||||
# Fix bug lp:720157 for older (version 1) clients
|
||||
version = req.params['SignatureVersion']
|
||||
# If not present assume v4
|
||||
version = req.params.get('SignatureVersion', 4)
|
||||
if int(version) == 1:
|
||||
non_args.remove('SignatureMethod')
|
||||
if 'SignatureMethod' in args:
|
||||
args.pop('SignatureMethod')
|
||||
for non_arg in non_args:
|
||||
# Remove, but raise KeyError if omitted
|
||||
args.pop(non_arg)
|
||||
if non_arg in required_args:
|
||||
# Remove, but raise KeyError if omitted
|
||||
args.pop(non_arg)
|
||||
else:
|
||||
args.pop(non_arg, None)
|
||||
except KeyError:
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
except exception.InvalidRequest as err:
|
||||
|
|
Loading…
Reference in New Issue