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
This commit is contained in:
parent
18543b1a46
commit
ff34cfc50f
@ -5,9 +5,15 @@ OpenStack Client interface. Handles the REST calls and responses.
|
|||||||
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
|
||||||
@ -30,23 +36,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]
|
||||||
@ -58,8 +74,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
|
||||||
@ -136,6 +151,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."""
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user