def check_signature(self, creds_ref, credentials): signer = ec2_utils.Ec2Signer(creds_ref['secret']) signature = signer.generate(credentials) # NOTE(davechen): credentials.get('signature') is not guaranteed to # exist, we need check it explicitly. if credentials.get('signature'): if utils.auth_str_equal(credentials['signature'], signature): return True # NOTE(vish): Some client libraries don't use the port when signing # requests, so try again without port. elif ':' in credentials['host']: hostname, _port = credentials['host'].split(':') credentials['host'] = hostname # NOTE(davechen): we need reinitialize 'signer' to avoid # contaminated status of signature, this is similar with # other programming language libraries, JAVA for example. signer = ec2_utils.Ec2Signer(creds_ref['secret']) signature = signer.generate(credentials) if utils.auth_str_equal(credentials['signature'], signature): return True raise exception.Unauthorized( message=_('Invalid EC2 signature.')) else: raise exception.Unauthorized( message=_('EC2 signature not supplied.')) # Raise the exception when credentials.get('signature') is None else: raise exception.Unauthorized( message=_('EC2 signature not supplied.'))
def test_authenticate_without_proper_secret_returns_unauthorized(self): cred_blob, credential = unit.new_ec2_credential( self.user_foo['id'], self.tenant_bar['id']) PROVIDERS.credential_api.create_credential(credential['id'], credential) signer = ec2_utils.Ec2Signer('totally not the secret') credentials = { 'access': cred_blob['access'], 'secret': 'totally not the secret', 'host': 'localhost', 'verb': 'GET', 'path': '/', 'params': { 'SignatureVersion': '2', 'Action': 'Test', 'Timestamp': '2007-01-31T23:59:59Z' }, } credentials['signature'] = signer.generate(credentials) self.public_request(method='POST', path='/v2.0/ec2tokens', body={'credentials': credentials}, expected_status=http_client.UNAUTHORIZED)
def test_generate_v4_port(self): """Test v4 generator with host:port format.""" # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.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} # 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(signature, expected)
def test_signature_validate_no_host_port(self): """Test signature validation with the access/secret provided.""" access = self.blob['access'] secret = self.blob['secret'] signer = ec2_utils.Ec2Signer(secret) params = { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access } request = { 'host': 'foo', 'verb': 'GET', 'path': '/bar', 'params': params } signature = signer.generate(request) sig_ref = { 'access': access, 'signature': signature, 'host': 'foo', 'verb': 'GET', 'path': '/bar', 'params': params } # Now validate the signature based on the dummy request self.assertTrue( self.controller.check_signature(self.creds_ref, sig_ref))
def test_ec2_auth(self): client = self.get_client() cred = client.ec2.create(user_id=self.user_foo['id'], tenant_id=self.tenant_bar['id']) from keystoneclient.contrib.ec2 import utils as ec2_utils signer = ec2_utils.Ec2Signer(cred.secret) credentials = { 'params': { 'SignatureVersion': '2' }, 'access': cred.access, 'verb': 'GET', 'host': 'localhost', 'path': '/thisisgoingtowork' } signature = signer.generate(credentials) credentials['signature'] = signature url = '%s/ec2tokens' % (client.auth_url) (resp, token) = client.request(url=url, method='POST', body={'credentials': credentials}) # make sure we have a v2 token self.assertEqual(resp.status_code, 200) self.assertIn('access', token)
def test_signature_validate_with_host_port(self): """Test signature validation when host is bound with port. Host is bound with a port, generally, the port here is not the standard port for the protocol, like '80' for HTTP and port 443 for HTTPS, the port is not omitted by the client library. """ access = self.blob['access'] secret = self.blob['secret'] signer = ec2_utils.Ec2Signer(secret) params = { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access } request = { 'host': 'foo:8181', 'verb': 'GET', 'path': '/bar', 'params': params } signature = signer.generate(request) sig_ref = { 'access': access, 'signature': signature, 'host': 'foo:8181', 'verb': 'GET', 'path': '/bar', 'params': params } # Now validate the signature based on the dummy request self.assertTrue( self.controller.check_signature(self.creds_ref, sig_ref))
def test_signature_validate_invalid_signature(self): """Signature is not signed on the correct data.""" access = self.blob['access'] secret = self.blob['secret'] signer = ec2_utils.Ec2Signer(secret) params = { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access } request = { 'host': 'bar', 'verb': 'GET', 'path': '/bar', 'params': params } signature = signer.generate(request) sig_ref = { 'access': access, 'signature': signature, 'host': 'foo:8080', 'verb': 'GET', 'path': '/bar', 'params': params } # Now validate the signature based on the dummy request self.assertRaises(exception.Unauthorized, self.controller.check_signature, self.creds_ref, sig_ref)
def _validate_signature(self, access, secret): """Test signature validation with the access/secret provided.""" signer = ec2_utils.Ec2Signer(secret) params = { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access } request = { 'host': 'foo', 'verb': 'GET', 'path': '/bar', 'params': params } signature = signer.generate(request) # Now make a request to validate the signed dummy request via the # ec2tokens API. This proves the v3 ec2 credentials actually work. sig_ref = { 'access': access, 'signature': signature, 'host': 'foo', 'verb': 'GET', 'path': '/bar', 'params': params } r = self.post('/ec2tokens', body={'ec2Credentials': sig_ref}, expected_status=http_client.OK) self.assertValidTokenResponse(r)
def test_valid_authentication_response_with_proper_secret(self): cred_blob, credential = unit.new_ec2_credential( self.user_foo['id'], self.tenant_bar['id']) self.credential_api.create_credential(credential['id'], credential) signer = ec2_utils.Ec2Signer(cred_blob['secret']) credentials = { 'access': cred_blob['access'], 'secret': cred_blob['secret'], 'host': 'localhost', 'verb': 'GET', 'path': '/', 'params': { 'SignatureVersion': '2', 'Action': 'Test', 'Timestamp': '2007-01-31T23:59:59Z' }, } credentials['signature'] = signer.generate(credentials) resp = self.public_request(method='POST', path='/v2.0/ec2tokens', body={'credentials': credentials}, expected_status=http_client.OK) self.assertValidAuthenticationResponse(resp)
def _validate_signature(self, access, secret): """Test signature validation with the access/secret provided.""" signer = ec2_utils.Ec2Signer(secret) params = {'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access} request = {'host': 'foo', 'verb': 'GET', 'path': '/bar', 'params': params} signature = signer.generate(request) # Now make a request to validate the signed dummy request via the # ec2tokens API. This proves the v3 ec2 credentials actually work. sig_ref = {'access': access, 'signature': signature, 'host': 'foo', 'verb': 'GET', 'path': '/bar', 'params': params} r = self.post( '/ec2tokens', body={'ec2Credentials': sig_ref}, expected_status=200) # FIXME(shardy): ec2tokens is available via both v3 and v2 # paths, but it returns a v2 token in both cases, so we can # only do a sanity assertion here for now. self.assertIsNotNone(r.result['access']['token']['id'])
def _generate_user_ec2_credentials(self, access, secret): signer = ec2_utils.Ec2Signer(secret) credentials = {'params': {'SignatureVersion': '2'}, 'access': access, 'verb': 'GET', 'host': 'localhost', 'path': '/service/cloud'} signature = signer.generate(credentials) return credentials, signature
def _get_signed_url(self, signal_type=SIGNAL): """Create properly formatted and pre-signed URL. This uses the created user for the credentials. See boto/auth.py::QuerySignatureV2AuthHandler :param signal_type: either WAITCONDITION or SIGNAL. """ try: stored = db_api.resource_data_get(self, 'ec2_signed_url') except exception.NotFound: stored = None if stored is not None: return stored try: access_key = db_api.resource_data_get(self, 'access_key') secret_key = db_api.resource_data_get(self, 'secret_key') except exception.NotFound: logger.warning( _('Cannot generate signed url, ' 'no stored access/secret key')) return waitcond_url = cfg.CONF.heat_waitcondition_server_url signal_url = waitcond_url.replace('/waitcondition', signal_type) host_url = urlutils.urlparse(signal_url) path = self.identifier().arn_url_path() # Note the WSGI spec apparently means that the webob request we end up # prcessing in the CFN API (ec2token.py) has an unquoted path, so we # need to calculate the signature with the path component unquoted, but # ensure the actual URL contains the quoted version... unquoted_path = urlutils.unquote(host_url.path + path) request = { 'host': host_url.netloc.lower(), 'verb': SIGNAL_VERB[signal_type], 'path': unquoted_path, 'params': { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access_key, 'Timestamp': self.created_time.strftime("%Y-%m-%dT%H:%M:%SZ") } } # Sign the request signer = ec2_utils.Ec2Signer(secret_key) request['params']['Signature'] = signer.generate(request) qs = urlutils.urlencode(request['params']) url = "%s%s?%s" % (signal_url.lower(), path, qs) db_api.resource_data_set(self, 'ec2_signed_url', url) return url
def test_generate_v4(self): """Test v4 generator with data from AWS docs example. see: http://docs.aws.amazon.com/general/latest/gr/ sigv4-create-canonical-request.html and http://docs.aws.amazon.com/general/latest/gr/ sigv4-signed-request-examples.html """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.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': 'iam.amazonaws.com', 'Authorization': auth_str} # 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 = {'Action': 'CreateUser', 'UserName': '******', 'Version': '2010-05-08', 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', 'X-Amz-Credential': 'AKIAEXAMPLE/20140611/' 'us-east-1/iam/aws4_request', 'X-Amz-Date': '20140611T231318Z', 'X-Amz-Expires': '30', 'X-Amz-SignedHeaders': 'host', 'X-Amz-Signature': 'ced6826de92d2bdeed8f846f0bf508e8' '559e98e4b0199114b84c54174deb456c'} credentials = {'host': 'iam.amazonaws.com', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('ced6826de92d2bdeed8f846f0bf508e8' '559e98e4b0199114b84c54174deb456c') self.assertEqual(signature, expected)
def check_signature(self, creds_ref, credentials): signer = ec2_utils.Ec2Signer(creds_ref['secret']) signature = signer.generate(credentials) if utils.auth_str_equal(credentials['signature'], signature): return # NOTE(vish): Some libraries don't use the port when signing # requests, so try again without port. elif ':' in credentials['signature']: hostname, _port = credentials['host'].split(':') credentials['host'] = hostname signature = signer.generate(credentials) if not utils.auth_str_equal(credentials.signature, signature): raise exception.Unauthorized(message='Invalid EC2 signature.') else: raise exception.Unauthorized(message='EC2 signature not supplied.')
def _generate_default_user_ec2_credentials(self): cred = self.default_client.ec2.create(user_id=self.user_foo['id'], tenant_id=self.tenant_bar['id']) signer = ec2_utils.Ec2Signer(cred.secret) credentials = { 'params': { 'SignatureVersion': '2' }, 'access': cred.access, 'verb': 'GET', 'host': 'localhost', 'path': '/service/cloud' } signature = signer.generate(credentials) return credentials, signature
def test_authenticate_without_proper_secret_returns_unauthorized(self): signer = ec2_utils.Ec2Signer('totally not the secret') credentials = { 'access': self.cred_blob['access'], 'secret': 'totally not the secret', 'host': 'localhost', 'verb': 'GET', 'path': '/', 'params': { 'SignatureVersion': '2', 'Action': 'Test', 'Timestamp': '2007-01-31T23:59:59Z' }, } credentials['signature'] = signer.generate(credentials) self.post('/ec2tokens', body={'credentials': credentials}, expected_status=http_client.UNAUTHORIZED)
def test_valid_authentication_response_with_proper_secret(self): signer = ec2_utils.Ec2Signer(self.cred_blob['secret']) credentials = { 'access': self.cred_blob['access'], 'secret': self.cred_blob['secret'], 'host': 'localhost', 'verb': 'GET', 'path': '/', 'params': { 'SignatureVersion': '2', 'Action': 'Test', 'Timestamp': '2007-01-31T23:59:59Z' }, } credentials['signature'] = signer.generate(credentials) resp = self.post('/ec2tokens', body={'credentials': credentials}, expected_status=http_client.OK) self.assertValidProjectScopedTokenResponse(resp, self.user)
def test_signature_validate_with_missed_host_port(self): """Test signature validation when host is bound with well-known port. Host is bound with a port, but the port is well-know port like '80' for HTTP and port 443 for HTTPS, sometimes, client library omit the port but then make the request with the port. see (How to create the string to sign): 'http://docs.aws.amazon.com/ general/latest/gr/signature-version-2.html'. Since "credentials['host']" is not set by client library but is taken from "req.host", so caused the differences. """ access = self.blob['access'] secret = self.blob['secret'] signer = ec2_utils.Ec2Signer(secret) params = { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access } # Omit the port to generate the signature. cnt_req = { 'host': 'foo', 'verb': 'GET', 'path': '/bar', 'params': params } signature = signer.generate(cnt_req) sig_ref = { 'access': access, 'signature': signature, 'host': 'foo:8080', 'verb': 'GET', 'path': '/bar', 'params': params } # Now validate the signature based on the dummy request # Check the signature again after omitting the port. self.assertTrue( self.controller.check_signature(self.creds_ref, sig_ref))
def _get_signed_url(self, signal_type=SIGNAL): """Create properly formatted and pre-signed URL. This uses the created user for the credentials. See http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/ restarter-signature.html Also see boto/auth.py::QuerySignatureV2AuthHandler :param signal_type: either WAITCONDITION or SIGNAL. """ waitcond_url = cfg.CONF.heat_waitcondition_server_url signal_url = waitcond_url.replace('/waitcondition', signal_type) host_url = urlparse.urlparse(signal_url) path = self.identifier().arn_url_path() credentials = self.keystone().get_ec2_keypair(self.resource_id) # Note the WSGI spec apparently means that the webob request we end up # prcessing in the CFN API (ec2token.py) has an unquoted path, so we # need to calculate the signature with the path component unquoted, but # ensure the actual URL contains the quoted version... unquoted_path = urllib.unquote(host_url.path + path) request = { 'host': host_url.netloc.lower(), 'verb': SIGNAL_VERB[signal_type], 'path': unquoted_path, 'params': { 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': credentials.access, 'Timestamp': self.created_time.strftime("%Y-%m-%dT%H:%M:%SZ") } } # Sign the requested signer = ec2_utils.Ec2Signer(credentials.secret) request['params']['Signature'] = signer.generate(request) qs = urllib.urlencode(request['params']) url = "%s%s?%s" % (signal_url.lower(), path, qs) return url
def test_generate_v4_port_strip(self): """Test v4 generator with host:port format for old boto version. Validate for 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 = utils.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_authenticate_expired_request(self): self.config_fixture.config(group='credential', auth_ttl=5) signer = ec2_utils.Ec2Signer(self.cred_blob['secret']) past = timeutils.utcnow() - datetime.timedelta(minutes=10) timestamp = utils.isotime(past) credentials = { 'access': self.cred_blob['access'], 'secret': self.cred_blob['secret'], 'host': 'localhost', 'verb': 'GET', 'path': '/', 'params': { 'SignatureVersion': '2', 'Action': 'Test', 'Timestamp': timestamp }, } credentials['signature'] = signer.generate(credentials) self.post('/ec2tokens', body={'credentials': credentials}, expected_status=http.client.UNAUTHORIZED)
def test_authenticate_expired_request_v4(self): self.config_fixture.config(group='credential', auth_ttl=5) signer = ec2_utils.Ec2Signer(self.cred_blob['secret']) past = timeutils.utcnow() - datetime.timedelta(minutes=10) timestamp = utils.isotime(past) hashed_payload = ( 'GET\n' '/\n' 'Action=Test\n' 'host:localhost\n' 'x-amz-date:' + timestamp + '\n' '\n' 'host;x-amz-date\n' 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') body_hash = hashlib.sha256(hashed_payload.encode()).hexdigest() amz_credential = ( 'AKIAIOSFODNN7EXAMPLE/%s/us-east-1/iam/aws4_request,' % timestamp[:8]) credentials = { 'access': self.cred_blob['access'], 'secret': self.cred_blob['secret'], 'host': 'localhost', 'verb': 'GET', 'path': '/', 'params': { 'Action': 'Test', 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', 'X-Amz-SignedHeaders': 'host,x-amz-date,', 'X-Amz-Credential': amz_credential }, 'headers': { 'X-Amz-Date': timestamp }, 'body_hash': body_hash } credentials['signature'] = signer.generate(credentials) self.post('/ec2tokens', body={'credentials': credentials}, expected_status=http.client.UNAUTHORIZED)
def test_valid_authentication_response_with_signature_v4(self): signer = ec2_utils.Ec2Signer(self.cred_blob['secret']) timestamp = utils.isotime(timeutils.utcnow()) hashed_payload = ( 'GET\n' '/\n' 'Action=Test\n' 'host:localhost\n' 'x-amz-date:' + timestamp + '\n' '\n' 'host;x-amz-date\n' 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') body_hash = hashlib.sha256(hashed_payload.encode()).hexdigest() amz_credential = ( 'AKIAIOSFODNN7EXAMPLE/%s/us-east-1/iam/aws4_request,' % timestamp[:8]) credentials = { 'access': self.cred_blob['access'], 'secret': self.cred_blob['secret'], 'host': 'localhost', 'verb': 'GET', 'path': '/', 'params': { 'Action': 'Test', 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', 'X-Amz-SignedHeaders': 'host,x-amz-date,', 'X-Amz-Credential': amz_credential }, 'headers': { 'X-Amz-Date': timestamp }, 'body_hash': body_hash } credentials['signature'] = signer.generate(credentials) resp = self.post('/ec2tokens', body={'credentials': credentials}, expected_status=http.client.OK) self.assertValidProjectScopedTokenResponse(resp, self.user)
def _get_ec2_signed_url(self, signal_type=SIGNAL): """Create properly formatted and pre-signed URL. This uses the created user for the credentials. See boto/auth.py::QuerySignatureV2AuthHandler :param signal_type: either WAITCONDITION or SIGNAL. """ stored = self.data().get('ec2_signed_url') if stored is not None: return stored access_key = self.data().get('access_key') secret_key = self.data().get('secret_key') if not access_key or not secret_key: if self.id is None: # it is too early return if self._get_user_id() is None: self._create_user() self._create_keypair() access_key = self.data().get('access_key') secret_key = self.data().get('secret_key') if not access_key or not secret_key: LOG.warn(_LW('Cannot generate signed url, ' 'unable to create keypair')) return config_url = cfg.CONF.heat_waitcondition_server_url if config_url: signal_url = config_url.replace('/waitcondition', signal_type) else: heat_client_plugin = self.stack.clients.client_plugin('heat') endpoint = heat_client_plugin.get_heat_cfn_url() signal_url = ''.join([endpoint, signal_type]) host_url = urlparse.urlparse(signal_url) path = self.identifier().arn_url_path() # Note the WSGI spec apparently means that the webob request we end up # processing in the CFN API (ec2token.py) has an unquoted path, so we # need to calculate the signature with the path component unquoted, but # ensure the actual URL contains the quoted version... unquoted_path = urlparse.unquote(host_url.path + path) request = {'host': host_url.netloc.lower(), 'verb': SIGNAL_VERB[signal_type], 'path': unquoted_path, 'params': {'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'AWSAccessKeyId': access_key, 'Timestamp': self.created_time.strftime("%Y-%m-%dT%H:%M:%SZ") }} # Sign the request signer = ec2_utils.Ec2Signer(secret_key) request['params']['Signature'] = signer.generate(request) qs = urlparse.urlencode(request['params']) url = "%s%s?%s" % (signal_url.lower(), path, qs) self.data_set('ec2_signed_url', url) return url
def setUp(self): super(Ec2SignerTest, self).setUp() self.access = '966afbde20b84200ae4e62e09acf46b2' self.secret = '89cdf9e94e2643cab35b8b8ac5a51f83' self.signer = utils.Ec2Signer(self.secret)
def collect(self): if CONF.cfn.metadata_url is None: if (CONF.cfn.heat_metadata_hint and os.path.exists(CONF.cfn.heat_metadata_hint)): with open(CONF.cfn.heat_metadata_hint) as hint: CONF.cfn.metadata_url = '%s/v1/' % hint.read().strip() else: logger.info('No metadata_url configured.') raise exc.CfnMetadataNotConfigured if CONF.cfn.access_key_id is None: logger.info('No Access Key ID configured.') raise exc.CfnMetadataNotConfigured if CONF.cfn.secret_access_key is None: logger.info('No Secret Access Key configured.') raise exc.CfnMetadataNotConfigured url = CONF.cfn.metadata_url stack_name = CONF.cfn.stack_name headers = {'Content-Type': 'application/json'} final_content = {} if CONF.cfn.path is None: logger.info('No path configured') raise exc.CfnMetadataNotConfigured signer = ec2_utils.Ec2Signer(secret_key=CONF.cfn.secret_access_key) for path in CONF.cfn.path: if '.' not in path: logger.error('Path not in format resource.field[.x.y] (%s)' % path) raise exc.CfnMetadataNotConfigured resource, field = path.split('.', 1) if '.' in field: field, sub_path = field.split('.', 1) else: sub_path = '' params = {'Action': 'DescribeStackResource', 'StackName': stack_name, 'LogicalResourceId': resource, 'AWSAccessKeyId': CONF.cfn.access_key_id, 'SignatureVersion': '2'} parsed_url = urlparse.urlparse(url) credentials = {'params': params, 'verb': 'GET', 'host': parsed_url.netloc, 'path': parsed_url.path} params['Signature'] = signer.generate(credentials) try: content = self._session.get( url, params=params, headers=headers, verify=CONF.cfn.ca_certificate, timeout=CONF.cfn.timeout) content.raise_for_status() except self._requests_impl.exceptions.RequestException as e: logger.warn(e) raise exc.CfnMetadataNotAvailable map_content = etree.fromstring(content.text) resource_detail = map_content.find( 'DescribeStackResourceResult').find('StackResourceDetail') sub_element = resource_detail.find(field) if sub_element is None: logger.warn('Path %s does not exist.' % (path)) raise exc.CfnMetadataNotAvailable try: value = json.loads(sub_element.text) except ValueError as e: logger.warn( 'Path %s failed to parse as json. (%s)' % (path, e)) raise exc.CfnMetadataNotAvailable if sub_path: for subkey in sub_path.split('.'): try: value = value[subkey] except KeyError: logger.warn( 'Sub-key %s does not exist. (%s)' % (subkey, path)) raise exc.CfnMetadataNotAvailable final_content.update(value) final_list = merger.merged_list_from_content( final_content, cfg.CONF.cfn.deployment_key, name) return final_list
def setUp(self): super(Ec2SignerTest, self).setUp() self.useFixture(client_fixtures.Deprecations()) self.access = '966afbde20b84200ae4e62e09acf46b2' self.secret = '89cdf9e94e2643cab35b8b8ac5a51f83' self.signer = utils.Ec2Signer(self.secret)