[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