Accept STS and IAM services from Ceph Obj Gateway

Ceph Object Gateway can use keystone for authenticating user requests
to its S3-compatible API, but recent versions also provide two other
AWS-compatible APIs for managing user access: Security Token Service
(STS) and Identity and Access Management (IAM). These attempt to
authenticate requests with Keystone but always receive 403 Access
Denied because _calculate_signature_v4() in api/s3tokens.py only
accepts "s3" as the service name. This patch accepts any of "s3" or
"sts" or "iam" as valid service names.

Change-Id: I69f16ed55dd9852859307b701a8391ba1e71c042
Closes-Bug: #1897280
This commit is contained in:
Stuart Grace 2020-09-25 15:10:22 +01:00 committed by Jonathan Rosser
parent 1e7ecca881
commit 36d6fc7f8f
3 changed files with 93 additions and 1 deletions

View File

@ -56,7 +56,10 @@ def _calculate_signature_v4(string_to_sign, secret_key):
if len(parts) != 4 or parts[0] != b'AWS4-HMAC-SHA256':
raise exception.Unauthorized(message=_('Invalid EC2 signature.'))
scope = parts[2].split(b'/')
if len(scope) != 4 or scope[2] != b's3' or scope[3] != b'aws4_request':
if len(scope) != 4 or scope[3] != b'aws4_request':
raise exception.Unauthorized(message=_('Invalid EC2 signature.'))
allowed_services = [b's3', b'iam', b'sts']
if scope[2] not in allowed_services:
raise exception.Unauthorized(message=_('Invalid EC2 signature.'))
def _sign(key, msg):

View File

@ -121,9 +121,40 @@ class S3ContribCore(test_v3.RestfulTestCase):
self.assertIsNone(s3tokens.S3Resource._check_signature(
creds_ref, credentials))
def test_good_iam_signature_v4(self):
creds_ref = {'secret':
u'e7a7a2240136494986991a6598d9fb9f'}
credentials = {'token':
'QVdTNC1ITUFDLVNIQTI1NgoyMDE1MDgyNFQxMTIwNDFaCjIw'
'MTUwODI0L1JlZ2lvbk9uZS9pYW0vYXdzNF9yZXF1ZXN0CmYy'
'MjE1NTgwZWViOWExNjczNTFiZDkzZTg2YzNiNmYwNGE5Mjhm'
'NWM1NTIwYTM5MzVhNDUzNTQwYTA5NTY0YjU=',
'signature':
'db4e15b3040f6afaa9d9d16002de2fc3425b'
'eea0c6ea8c1b2bb674f052030b7d'}
self.assertIsNone(s3tokens.S3Resource._check_signature(
creds_ref, credentials))
def test_good_sts_signature_v4(self):
creds_ref = {'secret':
u'e7a7a2240136494986991a6598d9fb9f'}
credentials = {'token':
'QVdTNC1ITUFDLVNIQTI1NgoyMDE1MDgyNFQxMTIwNDFaCjIw'
'MTUwODI0L1JlZ2lvbk9uZS9zdHMvYXdzNF9yZXF1ZXN0CmYy'
'MjE1NTgwZWViOWExNjczNTFiZDkzZTg2YzNiNmYwNGE5Mjhm'
'NWM1NTIwYTM5MzVhNDUzNTQwYTA5NTY0YjU=',
'signature':
'3aa0b6f1414b92b2a32584068f83c6d09b7f'
'daa11d4ea58912bbf1d8616ef56d'}
self.assertIsNone(s3tokens.S3Resource._check_signature(
creds_ref, credentials))
def test_bad_signature_v4(self):
creds_ref = {'secret':
u'e7a7a2240136494986991a6598d9fb9f'}
# the signature is wrong on an otherwise correctly formed token
credentials = {'token':
'QVdTNC1ITUFDLVNIQTI1NgoyMDE1MDgyNFQxMTIwNDFaCjIw'
'MTUwODI0L1JlZ2lvbk9uZS9zMy9hd3M0X3JlcXVlc3QKZjIy'
@ -135,6 +166,57 @@ class S3ContribCore(test_v3.RestfulTestCase):
s3tokens.S3Resource._check_signature,
creds_ref, credentials)
def test_bad_service_v4(self):
creds_ref = {'secret':
u'e7a7a2240136494986991a6598d9fb9f'}
# use 'bad' as the service scope instead of a recognised service
credentials = {'token':
'QVdTNC1ITUFDLVNIQTI1NgoyMDE1MDgyNFQxMTIwNDFaCjIw'
'MTUwODI0L1JlZ2lvbk9uZS9iYWQvYXdzNF9yZXF1ZXN0CmYy'
'MjE1NTgwZWViOWExNjczNTFiZDkzZTg2YzNiNmYwNGE5Mjhm'
'NWM1NTIwYTM5MzVhNDUzNTQwYTA5NTY0YjU=',
'signature':
'1a2dec50eb1bba97887d1103c2ead6a39911'
'98c4be2537cf14d40b64cceb888b'}
self.assertRaises(exception.Unauthorized,
s3tokens.S3Resource._check_signature,
creds_ref, credentials)
def test_bad_signing_key_v4(self):
creds_ref = {'secret':
u'e7a7a2240136494986991a6598d9fb9f'}
# signed with aws4_badrequest instead of aws4_request
credentials = {'token':
'QVdTNC1ITUFDLVNIQTI1NgoyMDE1MDgyNFQxMTIwNDFaCjIw'
'MTUwODI0L1JlZ2lvbk9uZS9zMy9hd3M0X3JlcXVlc3QKZjIy'
'MTU1ODBlZWI5YTE2NzM1MWJkOTNlODZjM2I2ZjA0YTkyOGY1'
'YzU1MjBhMzkzNWE0NTM1NDBhMDk1NjRiNQ==',
'signature':
'52d02211a3767d00b2104ab28c9859003b0e'
'9c8735cd10de7975f3b1212cca41'}
self.assertRaises(exception.Unauthorized,
s3tokens.S3Resource._check_signature,
creds_ref, credentials)
def test_bad_short_scope_v4(self):
creds_ref = {'secret':
u'e7a7a2240136494986991a6598d9fb9f'}
# credential scope has too few parts, missing final /aws4_request
credentials = {'token':
'QVdTNC1ITUFDLVNIQTI1NgoyMDE1MDgyNFQxMTIwNDFaCjIw'
'MTUwODI0L1JlZ2lvbk9uZS9zMwpmMjIxNTU4MGVlYjlhMTY3'
'MzUxYmQ5M2U4NmMzYjZmMDRhOTI4ZjVjNTUyMGEzOTM1YTQ1'
'MzU0MGEwOTU2NGI1',
'signature':
'28a075f1ee41e96c431153914998443ff0f5'
'5fe93d31b37181f13ff4865942a2'}
self.assertRaises(exception.Unauthorized,
s3tokens.S3Resource._check_signature,
creds_ref, credentials)
def test_bad_token_v4(self):
creds_ref = {'secret':
u'e7a7a2240136494986991a6598d9fb9f'}

View File

@ -0,0 +1,7 @@
---
fixes:
- |
[ `Bug 1897230 <https://launchpad.net/bugs/1897280>`_]
Allows s3 tokens with service types sts and iam to authenticate. This
is necessary when using assumed role features of Ceph object storage and
keystone is providing the authentication service for Rados Gateway.