Ec2Signer : Allow signature verification for older boto versions
Since the fix for bug #1197553, verification for older clients (which strip the port when formatting the request) fails. This conditionally reverts to the original behavior, by detecting the boto version via the User-Agent header, the default behavior will be the new behavior (which doesn't strip the port), but this will allow a less painful transition for clients/distros to the new boto version. Fixes bug #1205281 Change-Id: I54ac9c5ba91e697004f1346a8f2d685da488992a
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
|
|
||||||
@@ -211,11 +212,25 @@ class Ec2Signer(object):
|
|||||||
# - the X-Amz-SignedHeaders query parameter
|
# - the X-Amz-SignedHeaders query parameter
|
||||||
headers_lower = dict((k.lower().strip(), v.strip())
|
headers_lower = dict((k.lower().strip(), v.strip())
|
||||||
for (k, v) in headers.iteritems())
|
for (k, v) in headers.iteritems())
|
||||||
|
|
||||||
|
# Boto versions < 2.9.3 strip the port component of the host:port
|
||||||
|
# header, so detect the user-agent via the header and strip the
|
||||||
|
# port if we detect an old boto version. FIXME: remove when all
|
||||||
|
# distros package boto >= 2.9.3, this is a transitional workaround
|
||||||
|
user_agent = headers_lower.get('user-agent', '')
|
||||||
|
strip_port = re.match('Boto/2.[0-9].[0-2]', user_agent)
|
||||||
|
|
||||||
header_list = []
|
header_list = []
|
||||||
sh_str = auth_param('SignedHeaders')
|
sh_str = auth_param('SignedHeaders')
|
||||||
for h in sh_str.split(';'):
|
for h in sh_str.split(';'):
|
||||||
if h not in headers_lower:
|
if h not in headers_lower:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if h == 'host' and strip_port:
|
||||||
|
header_list.append('%s:%s' %
|
||||||
|
(h, headers_lower[h].split(':')[0]))
|
||||||
|
continue
|
||||||
|
|
||||||
header_list.append('%s:%s' % (h, headers_lower[h]))
|
header_list.append('%s:%s' % (h, headers_lower[h]))
|
||||||
return '\n'.join(header_list) + '\n'
|
return '\n'.join(header_list) + '\n'
|
||||||
|
|
||||||
|
@@ -179,3 +179,80 @@ class Ec2SignerTest(testtools.TestCase):
|
|||||||
expected = ('26dd92ea79aaa49f533d13b1055acdc'
|
expected = ('26dd92ea79aaa49f533d13b1055acdc'
|
||||||
'd7d7321460d64621f96cc79c4f4d4ab2b')
|
'd7d7321460d64621f96cc79c4f4d4ab2b')
|
||||||
self.assertEqual(signature, expected)
|
self.assertEqual(signature, expected)
|
||||||
|
|
||||||
|
def test_generate_v4_port_strip(self):
|
||||||
|
"""
|
||||||
|
Test v4 generator with host:port format, but for an old
|
||||||
|
(<2.9.3) version of boto, where the port should be stripped
|
||||||
|
to match boto behavior
|
||||||
|
"""
|
||||||
|
# Create a new signer object with the AWS example key
|
||||||
|
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
|
||||||
|
signer = Ec2Signer(secret)
|
||||||
|
|
||||||
|
body_hash = ('b6359072c78d70ebee1e81adcbab4f0'
|
||||||
|
'1bf2c23245fa365ef83fe8f1f955085e2')
|
||||||
|
auth_str = ('AWS4-HMAC-SHA256 '
|
||||||
|
'Credential=AKIAIOSFODNN7EXAMPLE/20110909/'
|
||||||
|
'us-east-1/iam/aws4_request,'
|
||||||
|
'SignedHeaders=content-type;host;x-amz-date,')
|
||||||
|
headers = {'Content-type':
|
||||||
|
'application/x-www-form-urlencoded; charset=utf-8',
|
||||||
|
'X-Amz-Date': '20110909T233600Z',
|
||||||
|
'Host': 'foo:8000',
|
||||||
|
'Authorization': auth_str,
|
||||||
|
'User-Agent': 'Boto/2.9.2 (linux2)'}
|
||||||
|
# Note the example in the AWS docs is inconsistent, previous
|
||||||
|
# examples specify no query string, but the final POST example
|
||||||
|
# does, apparently incorrectly since an empty parameter list
|
||||||
|
# aligns all steps and the final signature with the examples
|
||||||
|
params = {}
|
||||||
|
credentials = {'host': 'foo:8000',
|
||||||
|
'verb': 'POST',
|
||||||
|
'path': '/',
|
||||||
|
'params': params,
|
||||||
|
'headers': headers,
|
||||||
|
'body_hash': body_hash}
|
||||||
|
signature = signer.generate(credentials)
|
||||||
|
|
||||||
|
expected = ('9a4b2276a5039ada3b90f72ea8ec1745'
|
||||||
|
'14b92b909fb106b22ad910c5d75a54f4')
|
||||||
|
self.assertEqual(expected, signature)
|
||||||
|
|
||||||
|
def test_generate_v4_port_nostrip(self):
|
||||||
|
"""
|
||||||
|
Test v4 generator with host:port format, but for an new
|
||||||
|
(>=2.9.3) version of boto, where the port should not be stripped
|
||||||
|
"""
|
||||||
|
# Create a new signer object with the AWS example key
|
||||||
|
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
|
||||||
|
signer = Ec2Signer(secret)
|
||||||
|
|
||||||
|
body_hash = ('b6359072c78d70ebee1e81adcbab4f0'
|
||||||
|
'1bf2c23245fa365ef83fe8f1f955085e2')
|
||||||
|
auth_str = ('AWS4-HMAC-SHA256 '
|
||||||
|
'Credential=AKIAIOSFODNN7EXAMPLE/20110909/'
|
||||||
|
'us-east-1/iam/aws4_request,'
|
||||||
|
'SignedHeaders=content-type;host;x-amz-date,')
|
||||||
|
headers = {'Content-type':
|
||||||
|
'application/x-www-form-urlencoded; charset=utf-8',
|
||||||
|
'X-Amz-Date': '20110909T233600Z',
|
||||||
|
'Host': 'foo:8000',
|
||||||
|
'Authorization': auth_str,
|
||||||
|
'User-Agent': 'Boto/2.9.3 (linux2)'}
|
||||||
|
# Note the example in the AWS docs is inconsistent, previous
|
||||||
|
# examples specify no query string, but the final POST example
|
||||||
|
# does, apparently incorrectly since an empty parameter list
|
||||||
|
# aligns all steps and the final signature with the examples
|
||||||
|
params = {}
|
||||||
|
credentials = {'host': 'foo:8000',
|
||||||
|
'verb': 'POST',
|
||||||
|
'path': '/',
|
||||||
|
'params': params,
|
||||||
|
'headers': headers,
|
||||||
|
'body_hash': body_hash}
|
||||||
|
signature = signer.generate(credentials)
|
||||||
|
|
||||||
|
expected = ('26dd92ea79aaa49f533d13b1055acdc'
|
||||||
|
'd7d7321460d64621f96cc79c4f4d4ab2b')
|
||||||
|
self.assertEqual(expected, signature)
|
||||||
|
Reference in New Issue
Block a user