summaryrefslogtreecommitdiff
path: root/glanceclient
diff options
context:
space:
mode:
authorBrian Waldon <bcwaldon@gmail.com>2012-08-02 14:16:13 -0700
committerBrian Waldon <bcwaldon@gmail.com>2012-08-04 12:09:49 -0700
commitff34cfc50f3e030671362f42fbba58e11e5fb23d (patch)
treecfab25260ef1763dcb6a583a315d5c15b7088c83 /glanceclient
parent18543b1a46cad3e01613c4b5c933b7dfb68e048b (diff)
downloadpython-glanceclient-ff34cfc50f3e030671362f42fbba58e11e5fb23d.tar.gz
SSL Certificate Validation
This adds support for validation of ssl certs returned by remote servers over SSL. The --ca-file param represents the CA cert used to sign the remote server's cert. Use --insecure if the remote server is using a self-signed cert or you don't have the CA cert. Related to bp glance-client-parity Change-Id: I45253a6e2d88da599addfcc464571e62ae920166
Diffstat (limited to 'glanceclient')
-rw-r--r--glanceclient/common/http.py84
-rw-r--r--glanceclient/shell.py17
-rw-r--r--glanceclient/v1/client.py5
-rw-r--r--glanceclient/v2/client.py5
4 files changed, 86 insertions, 25 deletions
diff --git a/glanceclient/common/http.py b/glanceclient/common/http.py
index ce2140a..6414c5c 100644
--- a/glanceclient/common/http.py
+++ b/glanceclient/common/http.py
@@ -5,9 +5,15 @@ OpenStack Client interface. Handles the REST calls and responses.
import copy
import httplib
import logging
+import socket
import StringIO
import urlparse
+try:
+ import ssl
+except ImportError:
+ #TODO(bcwaldon): Handle this failure more gracefully
+ pass
try:
import json
@@ -30,23 +36,33 @@ CHUNKSIZE = 1024 * 64 # 64kB
class HTTPClient(object):
- def __init__(self, endpoint, token=None, timeout=600, insecure=False):
- parts = urlparse.urlparse(endpoint)
- self.connection_class = self.get_connection_class(parts.scheme)
- self.endpoint = (parts.hostname, parts.port)
- self.scheme = parts.scheme
- self.auth_token = token
+ def __init__(self, endpoint, **kwargs):
+ self.endpoint = endpoint
+ self.auth_token = kwargs.get('token')
+ self.connection_params = self.get_connection_params(endpoint, **kwargs)
@staticmethod
- def get_connection_class(scheme):
- try:
- return getattr(httplib, '%sConnection' % scheme.upper())
- except AttributeError:
- msg = 'Unsupported scheme: %s' % scheme
+ def get_connection_params(endpoint, **kwargs):
+ parts = urlparse.urlparse(endpoint)
+
+ _args = (parts.hostname, parts.port)
+ _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)
+ return (_class, _args, _kwargs)
+
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):
curl = ['curl -i -X %s' % method]
@@ -58,8 +74,7 @@ class HTTPClient(object):
if 'body' in kwargs:
curl.append('-d \'%s\'' % kwargs['body'])
- endpoint_parts = (self.scheme, self.endpoint[0], self.endpoint[1], url)
- curl.append('%s://%s:%s%s' % endpoint_parts)
+ curl.append('%s%s' % (self.endpoint, url))
LOG.debug(' '.join(curl))
@staticmethod
@@ -136,6 +151,47 @@ class HTTPClient(object):
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):
"""A class that acts as an iterator over an HTTP response."""
diff --git a/glanceclient/shell.py b/glanceclient/shell.py
index 6ec6fff..ca8b606 100644
--- a/glanceclient/shell.py
+++ b/glanceclient/shell.py
@@ -64,6 +64,10 @@ class OpenStackImagesShell(object):
"not be verified against any certificate authorities. "
"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',
default=600,
help='Number of seconds to wait for a response')
@@ -375,11 +379,14 @@ class OpenStackImagesShell(object):
endpoint = args.os_image_url or \
self._get_endpoint(_ksclient, **kwargs)
- client = glanceclient.Client(api_version,
- endpoint,
- token,
- insecure=args.insecure,
- timeout=args.timeout)
+ kwargs = {
+ 'token': token,
+ 'insecure': args.insecure,
+ 'timeout': args.timeout,
+ 'ca_file': args.ca_file,
+ }
+
+ client = glanceclient.Client(api_version, endpoint, **kwargs)
try:
args.func(client, args)
diff --git a/glanceclient/v1/client.py b/glanceclient/v1/client.py
index bf3f3a1..0f5980e 100644
--- a/glanceclient/v1/client.py
+++ b/glanceclient/v1/client.py
@@ -28,9 +28,8 @@ class Client(http.HTTPClient):
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. """
- super(Client, self).__init__(endpoint, token=token,
- timeout=timeout, insecure=insecure)
+ super(Client, self).__init__(*args, **kwargs)
self.images = images.ImageManager(self)
self.image_members = image_members.ImageMemberManager(self)
diff --git a/glanceclient/v2/client.py b/glanceclient/v2/client.py
index 08c8141..33f4ae5 100644
--- a/glanceclient/v2/client.py
+++ b/glanceclient/v2/client.py
@@ -30,9 +30,8 @@ class Client(object):
http requests. (optional)
"""
- def __init__(self, endpoint, token=None, timeout=600, **kwargs):
- self.http_client = http.HTTPClient(
- endpoint, token=token, timeout=timeout)
+ def __init__(self, *args, **kwargs):
+ self.http_client = http.HTTPClient(*args, **kwargs)
self.schemas = schemas.Controller(self.http_client)
self.images = images.Controller(self.http_client,
self._get_image_model())