libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject [07/16] libcloud git commit: Working example of SignatureVersion 4 for AWS EC2
Date Sat, 28 Mar 2015 14:51:24 GMT
Working example of SignatureVersion 4 for AWS EC2

Signed-off-by: Tomaz Muraus <tomaz@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/3caed8b0
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/3caed8b0
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/3caed8b0

Branch: refs/heads/trunk
Commit: 3caed8b02d97383e70dd6b9314f98506dc653493
Parents: 659d64c
Author: Gertjan Oude Lohuis <gertjan@byte.nl>
Authored: Mon Feb 2 15:00:57 2015 +0100
Committer: Tomaz Muraus <tomaz@apache.org>
Committed: Fri Mar 6 15:58:12 2015 +0100

----------------------------------------------------------------------
 libcloud/common/aws.py          | 74 ++++++++++++++++++++++++++++++++----
 libcloud/compute/drivers/ec2.py | 11 ++++++
 2 files changed, 78 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/3caed8b0/libcloud/common/aws.py
----------------------------------------------------------------------
diff --git a/libcloud/common/aws.py b/libcloud/common/aws.py
index 1d3dfb4..9e5a19e 100644
--- a/libcloud/common/aws.py
+++ b/libcloud/common/aws.py
@@ -14,6 +14,8 @@
 # limitations under the License.
 
 import base64
+from datetime import datetime
+import hashlib
 import hmac
 import time
 from hashlib import sha256
@@ -133,16 +135,74 @@ class AWSTokenConnection(ConnectionUserAndKey):
 class SignedAWSConnection(AWSTokenConnection):
 
     def add_default_params(self, params):
-        params['SignatureVersion'] = '2'
-        params['SignatureMethod'] = 'HmacSHA256'
-        params['AWSAccessKeyId'] = self.user_id
         params['Version'] = self.version
-        params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
-                                            time.gmtime())
-        params['Signature'] = self._get_aws_auth_param(params, self.key,
-                                                       self.action)
+
+        if self.signature_version == 2:
+            params['SignatureVersion'] = '2'
+            params['SignatureMethod'] = 'HmacSHA256'
+            params['AWSAccessKeyId'] = self.user_id
+            params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
+                                                time.gmtime())
+            params['Signature'] = self._get_aws_auth_param(params, self.key,
+                                                           self.action)
         return params
 
+    def pre_connect_hook(self, params, headers):
+        if self.signature_version == 4:
+            now = datetime.utcnow()
+            headers['X-AMZ-Date'] = now.strftime('%Y%m%dT%H%M%SZ')
+            headers['Authorization'] = self._get_authorization_v4_header(params, headers,
now)
+
+        return params, headers
+
+    def _get_authorization_v4_header(self, params, headers, dt):
+        # TODO: according to AWS spec (and RFC 2616 Section 4.2.) excess whitespace
+        # from inside non-quoted strings should be stripped. Now we only strip the
+        # start and end of the string. See http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
+        canonical_headers = '\n'.join([':'.join([k.lower(), v.strip()])
+                                       for k, v in sorted(headers.items())])
+        canonical_headers += '\n'
+
+        signed_headers = ';'.join([k.lower() for k in sorted(headers.keys())])
+
+        # For self.method == GET
+        request_params = '&'.join(["%s=%s" % (urlquote(k, safe=''), urlquote(v, safe='-_~'))
+                                   for k, v in sorted(params.items())])
+        payload_hash = hashlib.sha256('').hexdigest()
+
+        canonical_request = '\n'.join([self.method,
+                                       self.action,
+                                       request_params,
+                                       canonical_headers,
+                                       signed_headers,
+                                       payload_hash])
+
+        credential_scope = '/'.join([dt.strftime('%Y%m%d'),
+                                     self.driver.region_name,
+                                     self.service_name,
+                                     'aws4_request'])
+        string_to_sign = '\n'.join(['AWS4-HMAC-SHA256',
+                                    dt.strftime('%Y%m%dT%H%M%SZ'),
+                                    credential_scope,
+                                    hashlib.sha256(canonical_request).hexdigest()])
+
+        # Key derivation functions. See:
+        # http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python
+        def getSignatureKey(key, date_stamp, regionName, serviceName):
+            def sign(key, msg):
+                return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
+
+            signed_date = sign(('AWS4' + key).encode('utf-8'), date_stamp)
+            signed_region = sign(signed_date, regionName)
+            signed_service = sign(signed_region, serviceName)
+            return sign(signed_service, 'aws4_request')
+
+        signing_key = getSignatureKey(self.key, dt.strftime('%Y%m%d'), self.driver.region_name,
self.service_name)
+        signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
+
+        return 'AWS4-HMAC-SHA256 Credential=%s/%s, SignedHeaders=%s, Signature=%s' % \
+               (self.user_id, credential_scope, signed_headers, signature)
+
     def _get_aws_auth_param(self, params, secret_key, path='/'):
         """
         Creates the signature required for AWS, per

http://git-wip-us.apache.org/repos/asf/libcloud/blob/3caed8b0/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py
index b32fcc7..d05e74f 100644
--- a/libcloud/compute/drivers/ec2.py
+++ b/libcloud/compute/drivers/ec2.py
@@ -1674,6 +1674,15 @@ class EC2Connection(SignedAWSConnection):
     version = API_VERSION
     host = REGION_DETAILS['us-east-1']['endpoint']
     responseCls = EC2Response
+    signature_version = 2
+    service_name = 'ec2'
+
+
+class EC2V4Connection(EC2Connection):
+    """
+    Represents a single connection to an EC2 Endpoint using signature version 4.
+    """
+    signature_version = 4
 
 
 class ExEC2AvailabilityZone(object):
@@ -5541,6 +5550,8 @@ class EC2EUNodeDriver(EC2NodeDriver):
     """
     Driver class for EC2 in the Western Europe Region.
     """
+
+    connectionCls = EC2V4Connection
     name = 'Amazon EC2 (eu-west-1)'
     _region = 'eu-west-1'
 


Mime
View raw message