[pkg-eucalyptus-commits] [SCM] managing cloud instances for Eucalyptus branch, experimental, updated. debian/2.1.3-1_experimental1

Garrett Holmstrom gholms at fedoraproject.org
Sat Mar 16 03:26:41 UTC 2013


The following commit has been merged in the experimental branch:
commit 69f01e4690a7bbf10518dcf609ed70d89fd6e86b
Author: Garrett Holmstrom <gholms at fedoraproject.org>
Date:   Thu Aug 9 10:58:42 2012 -0700

    Implement EUCA2-RSA-SHA256 signature
    
    Fixes TOOLS-111

diff --git a/euca2ools/nc/auth.py b/euca2ools/nc/auth.py
index 089a715..eaf6bd2 100644
--- a/euca2ools/nc/auth.py
+++ b/euca2ools/nc/auth.py
@@ -30,24 +30,27 @@
 #
 # Author: Mitch Garnaat mgarnaat at eucalyptus.com
 
-import urllib, base64
-import time
+import M2Crypto
+import base64
 import boto.auth_handler
-from boto.exception import BotoClientError
-from hashlib import sha1 as sha
+import datetime
+import hashlib
 import hmac
-from M2Crypto import RSA
-        
-class EucaNCAuthHandler(boto.auth_handler.AuthHandler):
+import time
+import urllib
+import warnings
+from boto.exception import BotoClientError
+
+class EucaRsaAuthV1Handler(boto.auth_handler.AuthHandler):
     """Provides Eucalyptus NC Authentication."""
 
-    capability = ['euca-nc']
+    capability = ['euca-rsa-v1', 'euca-nc']
 
     def __init__(self, host, config, provider):
         boto.auth_handler.AuthHandler.__init__(self, host, config, provider)
-        self.hmac = hmac.new(provider.secret_key, digestmod=sha)
+        self.hmac = hmac.new(provider.secret_key, digestmod=hashlib.sha1)
         self.private_key_path = None
-        
+
     def _calc_signature(self, params, headers, verb, path):
         boto.log.debug('using euca_signature')
         string_to_sign = '%s\n%s\n%s\n' % (verb, headers['Date'], path)
@@ -62,9 +65,9 @@ class EucaNCAuthHandler(boto.auth_handler.AuthHandler):
         string_to_sign += qs
         hmac = self.hmac.copy()
         hmac.update(string_to_sign)
-        sha_manifest = sha()
+        sha_manifest = hashlib.sha1()
         sha_manifest.update(string_to_sign)
-        private_key = RSA.load_key(self.private_key_path)
+        private_key = M2Crypto.RSA.load_key(self.private_key_path)
         signature_value = private_key.sign(sha_manifest.digest())
         b64 = base64.b64encode(signature_value)
         boto.log.debug('len(b64)=%d' % len(b64))
@@ -86,3 +89,92 @@ class EucaNCAuthHandler(boto.auth_handler.AuthHandler):
         else:
             http_request.body = ''
 
+
+class EucaNCAuthHandler(EucaRsaAuthV1Handler):
+    # For API compatibility
+
+    def __init__(self, host, config, provider):
+        warnings.warn(('EucaNCAuthHandler has been renamed to '
+                       'EucaRsaAuthV1Handler'), DeprecationWarning)
+        EucaRsaAuthV1Handler.__init__(self, host, config, provider)
+
+
+class EucaRsaAuthV2Handler(boto.auth_handler.AuthHandler):
+    '''Provides authentication for inter-component requests'''
+
+    capability = ['euca-rsa-v2']
+
+    def __init__(self, host, config, provider):
+        boto.auth_handler.AuthHandler.__init__(self, host, config, provider)
+        self.cert_path        = None
+        self.private_key_path = None
+
+    def add_auth(self, http_request, **kwargs):
+        if 'Authorization' in http_request.headers:
+            del http_request.headers['Authorization']
+        now = datetime.datetime.utcnow()
+        http_request.headers['Date'] = now.strftime('%Y%m%dT%H%M%SZ')
+
+        cert_fp = self._get_fingerprint()
+
+        headers_to_sign = self._get_headers_to_sign(http_request)
+        signed_headers  = self._get_signed_headers(headers_to_sign)
+        boto.log.debug('SignedHeaders:%s', signed_headers)
+
+        canonical_request = self._get_canonical_request(http_request)
+        boto.log.debug('CanonicalRequest:\n%s', canonical_request)
+        signature = self._sign(canonical_request)
+        boto.log.debug('Signature:%s', signature)
+
+        auth_header = ' '.join(('EUCA2-RSA-SHA256', cert_fp, signed_headers,
+                                signature))
+        http_request.headers['Authorization'] = auth_header
+
+    def _get_fingerprint(self):
+        cert = M2Crypto.X509.load_cert(self.cert_path)
+        return cert.get_fingerprint().lower()
+
+    def _sign(self, canonical_request):
+        privkey = M2Crypto.RSA.load_key(self.private_key_path)
+        digest  = hashlib.sha256(canonical_request).digest()
+        return base64.b64encode(privkey.sign(digest, algo='sha256'))
+
+    def _get_canonical_request(self, http_request):
+        # 1.  request method
+        method = http_request.method.upper()
+        # 2.  CanonicalURI
+        c_uri  = self._get_canonical_uri(http_request)
+        # 3.  CanonicalQueryString
+        c_querystr = self._get_canonical_querystr(http_request)
+        # 4.  CanonicalHeaders
+        headers_to_sign = self._get_headers_to_sign(http_request)
+        c_headers = self._get_canonical_headers(headers_to_sign)
+        # 5.  SignedHeaders
+        s_headers = self._get_signed_headers(headers_to_sign)
+
+        return '\n'.join((method, c_uri, c_querystr, c_headers, s_headers))
+
+    def _get_canonical_uri(self, http_request):
+        return http_request.path or '/'
+
+    def _get_canonical_querystr(self, http_request):
+        params = []
+        for key, val in http_request.params.iteritems():
+            params.append(urllib.quote(param,    safe='/~') + '=' +
+                          urllib.quote(str(val), safe='/~'))
+        return '&'.join(sorted(params))
+
+    def _get_headers_to_sign(self, http_request):
+        headers = {'Host': http_request.host}
+        for key, val in http_request.headers.iteritems():
+            if key.lower() != 'authorization':
+                headers[key] = val
+        return headers
+
+    def _get_canonical_headers(self, headers):
+        header_strs = [key.lower().strip() + ':' + val.strip()
+                       for key, val in headers.iteritems()]
+        return '\n'.join(sorted(header_strs))
+
+    def _get_signed_headers(self, headers):
+        return ';'.join(sorted(header.lower().strip() for header in headers))
diff --git a/euca2ools/nc/connection.py b/euca2ools/nc/connection.py
index 908e972..0300fff 100644
--- a/euca2ools/nc/connection.py
+++ b/euca2ools/nc/connection.py
@@ -30,14 +30,15 @@
 #
 # Author: Mitch Garnaat mgarnaat at eucalyptus.com
 
-import urllib, base64
+import base64
+import binascii
+import boto
+import hashlib
 import time
+import urllib
 from boto.connection import AWSAuthConnection
-from boto import handler
+from boto.exception import BotoClientError, S3ResponseError, S3CreateError
 from boto.resultset import ResultSet
-from boto.exception import BotoClientError
-from boto.exception import S3ResponseError
-from boto.exception import S3CreateError
 from boto.s3.bucket import Bucket
 
 class EucaConnection(AWSAuthConnection):
@@ -60,10 +61,11 @@ class EucaConnection(AWSAuthConnection):
                                    debug=debug,
                                    https_connection_factory=https_connection_factory,
                                    path=path)
+        self._auth_handler.cert_path        = self.cert_path
         self._auth_handler.private_key_path = self.private_key_path
 
     def _required_auth_capability(self):
-        return ['euca-nc']
+        return ['euca-rsa-v2']
 
     def make_request(self, method='GET', bucket='', key='', headers=None,
                      data='', query_args=None, sender=None,
@@ -75,19 +77,17 @@ class EucaConnection(AWSAuthConnection):
             params = {}
         if not effective_user_id:
             effective_user_id = self.aws_access_key_id
-        headers['EucaEffectiveUserId'] = effective_user_id
         if action:
             headers['EucaOperation'] = action
         headers['AWSAccessKeyId'] = effective_user_id
         cert_file = open(self.cert_path, 'r')
         cert_str = cert_file.read()
         cert_file.close()
-        headers['EucaCert'] = base64.b64encode(cert_str)
-        if not headers.has_key('Content-Length'):
+        if not 'Content-Length' in headers:
             headers['Content-Length'] = str(len(data))
-        if not headers.has_key('Date'):
-            headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
-                                            time.gmtime())
+        if not 'Content-MD5' in headers:
+            md5sum = hashlib.md5(data).digest()
+            headers['Content-MD5'] = binascii.hexlify(md5sum)
         utf8_params = {}
         for key in params:
             utf8_params[key] = self.get_utf8_value(params[key])
@@ -98,7 +98,25 @@ class EucaConnection(AWSAuthConnection):
                                                     utf8_params,
                                                     headers, data,
                                                     self.server_name())
-        return self._mexe(http_request, sender)
+        # Use EUCA2 signing
+        response = self._mexe(http_request, sender)
+        if response.status == 403:
+            # Use EUCA signing in case we're talking to older Eucalyptus
+            headers['EucaEffectiveUserId'] = effective_user_id
+            headers['EucaCert'] = base64.b64encode(cert_str)
+            headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
+                                            time.gmtime())
+            http_request = self.build_base_http_request(method, path, None,
+                                                        utf8_params,
+                                                        headers, data,
+                                                        self.server_name())
+            self._auth_handler = boto.auth.get_auth_handler(self.host,
+                                                            boto.config,
+                                                            self.provider,
+                                                            ['euca-rsa-v1'])
+            self._auth_handler.private_key_path = self.private_key_path
+            response = self._mexe(http_request, sender)
+        return response
 
     def get_bucket(self, bucket_name, validate=True, headers=None):
         bucket = Bucket(self, bucket_name)

-- 
managing cloud instances for Eucalyptus



More information about the pkg-eucalyptus-commits mailing list