Merge "SSL Certificate Validation"

This commit is contained in:
Jenkins
2012-08-10 18:35:33 +00:00
committed by Gerrit Code Review
4 changed files with 86 additions and 25 deletions

View File

@@ -16,9 +16,15 @@
import copy import copy
import httplib import httplib
import logging import logging
import socket
import StringIO import StringIO
import urlparse import urlparse
try:
import ssl
except ImportError:
#TODO(bcwaldon): Handle this failure more gracefully
pass
try: try:
import json import json
@@ -41,23 +47,33 @@ CHUNKSIZE = 1024 * 64 # 64kB
class HTTPClient(object): class HTTPClient(object):
def __init__(self, endpoint, token=None, timeout=600, insecure=False): def __init__(self, endpoint, **kwargs):
parts = urlparse.urlparse(endpoint) self.endpoint = endpoint
self.connection_class = self.get_connection_class(parts.scheme) self.auth_token = kwargs.get('token')
self.endpoint = (parts.hostname, parts.port) self.connection_params = self.get_connection_params(endpoint, **kwargs)
self.scheme = parts.scheme
self.auth_token = token
@staticmethod @staticmethod
def get_connection_class(scheme): def get_connection_params(endpoint, **kwargs):
try: parts = urlparse.urlparse(endpoint)
return getattr(httplib, '%sConnection' % scheme.upper())
except AttributeError: _args = (parts.hostname, parts.port)
msg = 'Unsupported scheme: %s' % scheme _kwargs = {'timeout': float(kwargs.get('timeout', 600))}
if parts.scheme == 'https':
_class = VerifiedHTTPSConnection
_kwargs['ca_file'] = kwargs.get('ca_file', None)
_kwargs['insecure'] = kwargs.get('insecure', False)
elif parts.scheme == 'http':
_class = httplib.HTTPConnection
else:
msg = 'Unsupported scheme: %s' % parts.scheme
raise exc.InvalidEndpoint(msg) raise exc.InvalidEndpoint(msg)
return (_class, _args, _kwargs)
def get_connection(self): def get_connection(self):
return self.connection_class(*self.endpoint) _class = self.connection_params[0]
return _class(*self.connection_params[1], **self.connection_params[2])
def log_curl_request(self, method, url, kwargs): def log_curl_request(self, method, url, kwargs):
curl = ['curl -i -X %s' % method] curl = ['curl -i -X %s' % method]
@@ -69,8 +85,7 @@ class HTTPClient(object):
if 'body' in kwargs: if 'body' in kwargs:
curl.append('-d \'%s\'' % kwargs['body']) curl.append('-d \'%s\'' % kwargs['body'])
endpoint_parts = (self.scheme, self.endpoint[0], self.endpoint[1], url) curl.append('%s%s' % (self.endpoint, url))
curl.append('%s://%s:%s%s' % endpoint_parts)
LOG.debug(' '.join(curl)) LOG.debug(' '.join(curl))
@staticmethod @staticmethod
@@ -147,6 +162,47 @@ class HTTPClient(object):
return self._http_request(url, method, **kwargs) return self._http_request(url, method, **kwargs)
class VerifiedHTTPSConnection(httplib.HTTPSConnection):
"""httplib-compatibile connection using client-side SSL authentication
:see http://code.activestate.com/recipes/
577548-https-httplib-client-connection-with-certificate-v/
"""
def __init__(self, host, port, key_file=None, cert_file=None,
ca_file=None, timeout=None, insecure=False):
httplib.HTTPSConnection.__init__(self, host, port, key_file=key_file,
cert_file=cert_file)
self.key_file = key_file
self.cert_file = cert_file
self.ca_file = ca_file
self.timeout = timeout
self.insecure = insecure
def connect(self):
"""
Connect to a host on a given (SSL) port.
If ca_file is pointing somewhere, use it to check Server Certificate.
Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to
ssl.wrap_socket(), which forces SSL to check server certificate against
our client certificate.
"""
sock = socket.create_connection((self.host, self.port), self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
if self.insecure is True:
kwargs = {'cert_reqs': ssl.CERT_NONE}
else:
kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.ca_file}
self.sock = ssl.wrap_socket(sock, **kwargs)
class ResponseBodyIterator(object): class ResponseBodyIterator(object):
"""A class that acts as an iterator over an HTTP response.""" """A class that acts as an iterator over an HTTP response."""

View File

@@ -64,6 +64,10 @@ class OpenStackImagesShell(object):
"not be verified against any certificate authorities. " "not be verified against any certificate authorities. "
"This option should be used with caution.") "This option should be used with caution.")
parser.add_argument('--ca-file',
help='Path of CA SSL certificate(s) used to sign the remote '
'server\'s certificate.')
parser.add_argument('--timeout', parser.add_argument('--timeout',
default=600, default=600,
help='Number of seconds to wait for a response') help='Number of seconds to wait for a response')
@@ -375,11 +379,14 @@ class OpenStackImagesShell(object):
endpoint = args.os_image_url or \ endpoint = args.os_image_url or \
self._get_endpoint(_ksclient, **kwargs) self._get_endpoint(_ksclient, **kwargs)
client = glanceclient.Client(api_version, kwargs = {
endpoint, 'token': token,
token, 'insecure': args.insecure,
insecure=args.insecure, 'timeout': args.timeout,
timeout=args.timeout) 'ca_file': args.ca_file,
}
client = glanceclient.Client(api_version, endpoint, **kwargs)
try: try:
args.func(client, args) args.func(client, args)

View File

@@ -28,9 +28,8 @@ class Client(http.HTTPClient):
http requests. (optional) http requests. (optional)
""" """
def __init__(self, endpoint, token=None, timeout=600, insecure=False): def __init__(self, *args, **kwargs):
""" Initialize a new client for the Images v1 API. """ """ Initialize a new client for the Images v1 API. """
super(Client, self).__init__(endpoint, token=token, super(Client, self).__init__(*args, **kwargs)
timeout=timeout, insecure=insecure)
self.images = images.ImageManager(self) self.images = images.ImageManager(self)
self.image_members = image_members.ImageMemberManager(self) self.image_members = image_members.ImageMemberManager(self)

View File

@@ -30,9 +30,8 @@ class Client(object):
http requests. (optional) http requests. (optional)
""" """
def __init__(self, endpoint, token=None, timeout=600, **kwargs): def __init__(self, *args, **kwargs):
self.http_client = http.HTTPClient( self.http_client = http.HTTPClient(*args, **kwargs)
endpoint, token=token, timeout=timeout)
self.schemas = schemas.Controller(self.http_client) self.schemas = schemas.Controller(self.http_client)
self.images = images.Controller(self.http_client, self.images = images.Controller(self.http_client,
self._get_image_model()) self._get_image_model())