mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #25420 from techhat/s3sig4
Move S3 to use AWS Signature Version 4
This commit is contained in:
commit
9313804e27
2 changed files with 30 additions and 75 deletions
|
@ -139,8 +139,9 @@ def sig2(method, endpoint, params, provider, aws_api_version):
|
|||
return params_with_headers
|
||||
|
||||
|
||||
def sig4(method, endpoint, params, prov_dict, aws_api_version, location,
|
||||
product='ec2', uri='/', requesturl=None):
|
||||
def sig4(method, endpoint, params, prov_dict,
|
||||
aws_api_version=DEFAULT_AWS_API_VERSION, location=DEFAULT_LOCATION,
|
||||
product='ec2', uri='/', requesturl=None, data=''):
|
||||
'''
|
||||
Sign a query against AWS services using Signature Version 4 Signing
|
||||
Process. This is documented at:
|
||||
|
@ -155,7 +156,8 @@ def sig4(method, endpoint, params, prov_dict, aws_api_version, location,
|
|||
access_key_id, secret_access_key, token = creds(prov_dict)
|
||||
|
||||
params_with_headers = params.copy()
|
||||
params_with_headers['Version'] = aws_api_version
|
||||
if product != 's3':
|
||||
params_with_headers['Version'] = aws_api_version
|
||||
keys = sorted(params_with_headers.keys())
|
||||
values = list(map(params_with_headers.get, keys))
|
||||
querystring = urlencode(list(zip(keys, values))).replace('+', '%20')
|
||||
|
@ -173,7 +175,7 @@ def sig4(method, endpoint, params, prov_dict, aws_api_version, location,
|
|||
|
||||
# Create payload hash (hash of the request body content). For GET
|
||||
# requests, the payload is an empty string ('').
|
||||
payload_hash = hashlib.sha256('').hexdigest()
|
||||
payload_hash = hashlib.sha256(data).hexdigest()
|
||||
|
||||
# Combine elements to create create canonical request
|
||||
canonical_request = '\n'.join((
|
||||
|
@ -223,7 +225,8 @@ def sig4(method, endpoint, params, prov_dict, aws_api_version, location,
|
|||
|
||||
headers = {
|
||||
'x-amz-date': amzdate,
|
||||
'Authorization': authorization_header
|
||||
'x-amz-content-sha256': payload_hash,
|
||||
'Authorization': authorization_header,
|
||||
}
|
||||
|
||||
# Add in security token if we have one
|
||||
|
|
|
@ -7,10 +7,6 @@ Connection library for Amazon S3
|
|||
from __future__ import absolute_import
|
||||
|
||||
# Import Python libs
|
||||
import binascii
|
||||
import datetime
|
||||
import hashlib
|
||||
import hmac
|
||||
import logging
|
||||
|
||||
# Import 3rd-party libs
|
||||
|
@ -19,21 +15,22 @@ try:
|
|||
HAS_REQUESTS = True # pylint: disable=W0612
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False # pylint: disable=W0612
|
||||
from salt.ext.six.moves.urllib.parse import urlencode # pylint: disable=no-name-in-module,import-error
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
import salt.utils.aws
|
||||
import salt.utils.xmlutil as xml
|
||||
import salt.utils.iam as iam
|
||||
from salt._compat import ElementTree as ET
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
DEFAULT_LOCATION = 'us-east-1'
|
||||
|
||||
|
||||
def query(key, keyid, method='GET', params=None, headers=None,
|
||||
requesturl=None, return_url=False, bucket=None, service_url=None,
|
||||
path=None, return_bin=False, action=None, local_file=None,
|
||||
verify_ssl=True):
|
||||
path='', return_bin=False, action=None, local_file=None,
|
||||
verify_ssl=True, location=DEFAULT_LOCATION):
|
||||
'''
|
||||
Perform a query against an S3-like API. This function requires that a
|
||||
secret key and the id for that key are passed in. For instance:
|
||||
|
@ -71,9 +68,6 @@ def query(key, keyid, method='GET', params=None, headers=None,
|
|||
if not params:
|
||||
params = {}
|
||||
|
||||
if path is None:
|
||||
path = ''
|
||||
|
||||
if not service_url:
|
||||
service_url = 's3.amazonaws.com'
|
||||
|
||||
|
@ -90,75 +84,33 @@ def query(key, keyid, method='GET', params=None, headers=None,
|
|||
keyid = iam_creds['access_key']
|
||||
token = iam_creds['security_token']
|
||||
|
||||
if not requesturl:
|
||||
x_amz_date = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
content_type = 'text/plain'
|
||||
if method == 'GET':
|
||||
if bucket:
|
||||
can_resource = '/{0}/{1}'.format(bucket, path)
|
||||
else:
|
||||
can_resource = '/'
|
||||
elif method == 'PUT' or method == 'HEAD' or method == 'DELETE':
|
||||
if path:
|
||||
can_resource = '/{0}/{1}'.format(bucket, path)
|
||||
else:
|
||||
can_resource = '/{0}/'.format(bucket)
|
||||
|
||||
if action:
|
||||
can_resource += '?{0}'.format(action)
|
||||
|
||||
log.debug('CanonicalizedResource: {0}'.format(can_resource))
|
||||
|
||||
headers['Host'] = endpoint
|
||||
headers['Content-type'] = content_type
|
||||
headers['Date'] = x_amz_date
|
||||
if token:
|
||||
headers['x-amz-security-token'] = token
|
||||
|
||||
string_to_sign = '{0}\n'.format(method)
|
||||
|
||||
new_headers = []
|
||||
for header in sorted(headers):
|
||||
if header.lower().startswith('x-amz'):
|
||||
log.debug(header.lower())
|
||||
new_headers.append('{0}:{1}'.format(header.lower(),
|
||||
headers[header]))
|
||||
can_headers = '\n'.join(new_headers)
|
||||
log.debug('CanonicalizedAmzHeaders: {0}'.format(can_headers))
|
||||
|
||||
string_to_sign += '\n{0}'.format(content_type)
|
||||
string_to_sign += '\n{0}'.format(x_amz_date)
|
||||
if can_headers:
|
||||
string_to_sign += '\n{0}'.format(can_headers)
|
||||
string_to_sign += '\n{0}'.format(can_resource)
|
||||
log.debug('String To Sign:: \n{0}'.format(string_to_sign))
|
||||
|
||||
hashed = hmac.new(key, string_to_sign, hashlib.sha1)
|
||||
sig = binascii.b2a_base64(hashed.digest())
|
||||
headers['Authorization'] = 'AWS {0}:{1}'.format(keyid, sig.strip())
|
||||
|
||||
querystring = urlencode(params)
|
||||
if action:
|
||||
if querystring:
|
||||
querystring = '{0}&{1}'.format(action, querystring)
|
||||
else:
|
||||
querystring = action
|
||||
requesturl = 'https://{0}/'.format(endpoint)
|
||||
if path:
|
||||
requesturl += path
|
||||
if querystring:
|
||||
requesturl += '?{0}'.format(querystring)
|
||||
|
||||
data = None
|
||||
data = ''
|
||||
if method == 'PUT':
|
||||
if local_file:
|
||||
with salt.utils.fopen(local_file, 'r') as ifile:
|
||||
data = ifile.read()
|
||||
|
||||
if not requesturl:
|
||||
requesturl = 'https://{0}/{1}'.format(endpoint, path)
|
||||
headers, requesturl = salt.utils.aws.sig4(
|
||||
method,
|
||||
endpoint,
|
||||
params,
|
||||
data=data,
|
||||
uri='/{0}'.format(path),
|
||||
prov_dict={'id': keyid, 'key': key},
|
||||
location=location,
|
||||
product='s3',
|
||||
requesturl=requesturl,
|
||||
)
|
||||
|
||||
log.debug('S3 Request: {0}'.format(requesturl))
|
||||
log.debug('S3 Headers::')
|
||||
log.debug(' Authorization: {0}'.format(headers['Authorization']))
|
||||
|
||||
if not data:
|
||||
data = None
|
||||
|
||||
try:
|
||||
result = requests.request(method, requesturl, headers=headers,
|
||||
data=data,
|
||||
|
|
Loading…
Add table
Reference in a new issue