Support passing client certificates for server version requests
Using the cinderclient to fetch server versions will fail with error `OpenSSL.SSL.Error: [sslv3 alert handshake failure]` when the server requires client certificates to be passed with these requests. Added the optional parameter `cert` to both get_server_version get_highest_client_server_version and methods so that users can have the option to pass client certificates while fetching server versions. Also support passing mTLS certificate/key to HTTPClient Closes-Bug: #1915996 Change-Id: I57c665dd9d4b8c32e5f10994d891d1e0f5315548 Signed-off-by: sri harsha mekala <smekala@oath.com>
This commit is contained in:
parent
8e804292db
commit
9c2e8df948
@ -70,7 +70,7 @@ for svc in ('volume', 'volumev2', 'volumev3'):
|
|||||||
discover.add_catalog_discover_hack(svc, re.compile(r'/v[12]/\w+/?$'), '/')
|
discover.add_catalog_discover_hack(svc, re.compile(r'/v[12]/\w+/?$'), '/')
|
||||||
|
|
||||||
|
|
||||||
def get_server_version(url, insecure=False, cacert=None):
|
def get_server_version(url, insecure=False, cacert=None, cert=None):
|
||||||
"""Queries the server via the naked endpoint and gets version info.
|
"""Queries the server via the naked endpoint and gets version info.
|
||||||
|
|
||||||
:param url: url of the cinder endpoint
|
:param url: url of the cinder endpoint
|
||||||
@ -78,6 +78,10 @@ def get_server_version(url, insecure=False, cacert=None):
|
|||||||
(https) requests
|
(https) requests
|
||||||
:param cacert: Specify a CA bundle file to use in verifying a TLS
|
:param cacert: Specify a CA bundle file to use in verifying a TLS
|
||||||
(https) server certificate
|
(https) server certificate
|
||||||
|
:param cert: A client certificate to pass to requests. These are of the
|
||||||
|
same form as requests expects. Either a single filename
|
||||||
|
containing both the certificate and key or a tuple containing
|
||||||
|
the path to the certificate then a path to the key. (optional)
|
||||||
:returns: APIVersion object for min and max version supported by
|
:returns: APIVersion object for min and max version supported by
|
||||||
the server
|
the server
|
||||||
"""
|
"""
|
||||||
@ -115,7 +119,7 @@ def get_server_version(url, insecure=False, cacert=None):
|
|||||||
verify_cert = cacert
|
verify_cert = cacert
|
||||||
else:
|
else:
|
||||||
verify_cert = True
|
verify_cert = True
|
||||||
response = requests.get(version_url, verify=verify_cert)
|
response = requests.get(version_url, verify=verify_cert, cert=cert)
|
||||||
data = json.loads(response.text)
|
data = json.loads(response.text)
|
||||||
versions = data['versions']
|
versions = data['versions']
|
||||||
for version in versions:
|
for version in versions:
|
||||||
@ -135,9 +139,10 @@ def get_server_version(url, insecure=False, cacert=None):
|
|||||||
api_versions.APIVersion(current_version))
|
api_versions.APIVersion(current_version))
|
||||||
|
|
||||||
|
|
||||||
def get_highest_client_server_version(url, insecure=False, cacert=None):
|
def get_highest_client_server_version(url, insecure=False,
|
||||||
|
cacert=None, cert=None):
|
||||||
"""Returns highest supported version by client and server as a string."""
|
"""Returns highest supported version by client and server as a string."""
|
||||||
min_server, max_server = get_server_version(url, insecure, cacert)
|
min_server, max_server = get_server_version(url, insecure, cacert, cert)
|
||||||
max_client = api_versions.APIVersion(api_versions.MAX_VERSION)
|
max_client = api_versions.APIVersion(api_versions.MAX_VERSION)
|
||||||
return min(max_server, max_client).get_string()
|
return min(max_server, max_client).get_string()
|
||||||
|
|
||||||
@ -286,7 +291,7 @@ class HTTPClient(object):
|
|||||||
endpoint_type='publicURL', service_type=None,
|
endpoint_type='publicURL', service_type=None,
|
||||||
service_name=None, volume_service_name=None,
|
service_name=None, volume_service_name=None,
|
||||||
os_endpoint=None, retries=None,
|
os_endpoint=None, retries=None,
|
||||||
http_log_debug=False, cacert=None,
|
http_log_debug=False, cacert=None, cert=None,
|
||||||
auth_system='keystone', auth_plugin=None, api_version=None,
|
auth_system='keystone', auth_plugin=None, api_version=None,
|
||||||
logger=None, user_domain_name='Default',
|
logger=None, user_domain_name='Default',
|
||||||
project_domain_name='Default', global_request_id=None):
|
project_domain_name='Default', global_request_id=None):
|
||||||
@ -324,7 +329,7 @@ class HTTPClient(object):
|
|||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.user_domain_name = user_domain_name
|
self.user_domain_name = user_domain_name
|
||||||
self.project_domain_name = project_domain_name
|
self.project_domain_name = project_domain_name
|
||||||
|
self.cert = cert
|
||||||
if insecure:
|
if insecure:
|
||||||
self.verify_cert = False
|
self.verify_cert = False
|
||||||
else:
|
else:
|
||||||
@ -405,6 +410,7 @@ class HTTPClient(object):
|
|||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
verify=self.verify_cert,
|
verify=self.verify_cert,
|
||||||
|
cert=self.cert,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
self.http_log_resp(resp)
|
self.http_log_resp(resp)
|
||||||
|
|
||||||
@ -701,7 +707,7 @@ def _construct_http_client(username=None, password=None, project_id=None,
|
|||||||
os_endpoint=None, retries=None,
|
os_endpoint=None, retries=None,
|
||||||
http_log_debug=False,
|
http_log_debug=False,
|
||||||
auth_system='keystone', auth_plugin=None,
|
auth_system='keystone', auth_plugin=None,
|
||||||
cacert=None, tenant_id=None,
|
cacert=None, cert=None, tenant_id=None,
|
||||||
session=None,
|
session=None,
|
||||||
auth=None, api_version=None,
|
auth=None, api_version=None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
@ -741,6 +747,7 @@ def _construct_http_client(username=None, password=None, project_id=None,
|
|||||||
retries=retries,
|
retries=retries,
|
||||||
http_log_debug=http_log_debug,
|
http_log_debug=http_log_debug,
|
||||||
cacert=cacert,
|
cacert=cacert,
|
||||||
|
cert=cert,
|
||||||
auth_system=auth_system,
|
auth_system=auth_system,
|
||||||
auth_plugin=auth_plugin,
|
auth_plugin=auth_plugin,
|
||||||
logger=logger,
|
logger=logger,
|
||||||
|
@ -365,7 +365,9 @@ class GetAPIVersionTestCase(utils.TestCase):
|
|||||||
|
|
||||||
cinderclient.client.get_server_version(url, True)
|
cinderclient.client.get_server_version(url, True)
|
||||||
|
|
||||||
mock_request.assert_called_once_with(expected_url, verify=False)
|
mock_request.assert_called_once_with(expected_url,
|
||||||
|
verify=False,
|
||||||
|
cert=None)
|
||||||
|
|
||||||
@mock.patch('cinderclient.client.requests.get')
|
@mock.patch('cinderclient.client.requests.get')
|
||||||
def test_get_server_version_cacert(self, mock_request):
|
def test_get_server_version_cacert(self, mock_request):
|
||||||
@ -383,7 +385,29 @@ class GetAPIVersionTestCase(utils.TestCase):
|
|||||||
cacert = '/path/to/cert'
|
cacert = '/path/to/cert'
|
||||||
cinderclient.client.get_server_version(url, cacert=cacert)
|
cinderclient.client.get_server_version(url, cacert=cacert)
|
||||||
|
|
||||||
mock_request.assert_called_once_with(expected_url, verify=cacert)
|
mock_request.assert_called_once_with(expected_url,
|
||||||
|
verify=cacert,
|
||||||
|
cert=None)
|
||||||
|
|
||||||
|
@mock.patch('cinderclient.client.requests.get')
|
||||||
|
def test_get_server_version_cert(self, mock_request):
|
||||||
|
mock_response = utils.TestResponse({
|
||||||
|
"status_code": 200,
|
||||||
|
"text": json.dumps(fakes.fake_request_get_no_v3())
|
||||||
|
})
|
||||||
|
|
||||||
|
mock_request.return_value = mock_response
|
||||||
|
|
||||||
|
url = (
|
||||||
|
"https://192.168.122.127:8776/v3/e5526285ebd741b1819393f772f11fc3")
|
||||||
|
expected_url = "https://192.168.122.127:8776/"
|
||||||
|
|
||||||
|
client_cert = '/path/to/cert'
|
||||||
|
cinderclient.client.get_server_version(url, cert=client_cert)
|
||||||
|
|
||||||
|
mock_request.assert_called_once_with(expected_url,
|
||||||
|
verify=True,
|
||||||
|
cert=client_cert)
|
||||||
|
|
||||||
@mock.patch('cinderclient.client.requests.get')
|
@mock.patch('cinderclient.client.requests.get')
|
||||||
@ddt.data('3.12', '3.40')
|
@ddt.data('3.12', '3.40')
|
||||||
|
@ -27,6 +27,7 @@ REQUEST_ID = ['req-test-request-id']
|
|||||||
class TestCase(testtools.TestCase):
|
class TestCase(testtools.TestCase):
|
||||||
TEST_REQUEST_BASE = {
|
TEST_REQUEST_BASE = {
|
||||||
'verify': True,
|
'verify': True,
|
||||||
|
'cert': None
|
||||||
}
|
}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -56,9 +56,9 @@ class Client(object):
|
|||||||
endpoint_type='publicURL', extensions=None,
|
endpoint_type='publicURL', extensions=None,
|
||||||
service_type='volumev2', service_name=None,
|
service_type='volumev2', service_name=None,
|
||||||
volume_service_name=None, os_endpoint=None, retries=0,
|
volume_service_name=None, os_endpoint=None, retries=0,
|
||||||
http_log_debug=False, cacert=None, auth_system='keystone',
|
http_log_debug=False, cacert=None, cert=None,
|
||||||
auth_plugin=None, session=None, api_version=None,
|
auth_system='keystone', auth_plugin=None, session=None,
|
||||||
logger=None, **kwargs):
|
api_version=None, logger=None, **kwargs):
|
||||||
# FIXME(comstud): Rename the api_key argument above when we
|
# FIXME(comstud): Rename the api_key argument above when we
|
||||||
# know it's not being used as keyword argument
|
# know it's not being used as keyword argument
|
||||||
password = api_key
|
password = api_key
|
||||||
@ -118,6 +118,7 @@ class Client(object):
|
|||||||
retries=retries,
|
retries=retries,
|
||||||
http_log_debug=http_log_debug,
|
http_log_debug=http_log_debug,
|
||||||
cacert=cacert,
|
cacert=cacert,
|
||||||
|
cert=cert,
|
||||||
auth_system=auth_system,
|
auth_system=auth_system,
|
||||||
auth_plugin=auth_plugin,
|
auth_plugin=auth_plugin,
|
||||||
session=session,
|
session=session,
|
||||||
|
@ -63,9 +63,9 @@ class Client(object):
|
|||||||
endpoint_type='publicURL', extensions=None,
|
endpoint_type='publicURL', extensions=None,
|
||||||
service_type='volumev3', service_name=None,
|
service_type='volumev3', service_name=None,
|
||||||
volume_service_name=None, os_endpoint=None, retries=0,
|
volume_service_name=None, os_endpoint=None, retries=0,
|
||||||
http_log_debug=False, cacert=None, auth_system='keystone',
|
http_log_debug=False, cacert=None, cert=None,
|
||||||
auth_plugin=None, session=None, api_version=None,
|
auth_system='keystone', auth_plugin=None, session=None,
|
||||||
logger=None, **kwargs):
|
api_version=None, logger=None, **kwargs):
|
||||||
# FIXME(comstud): Rename the api_key argument above when we
|
# FIXME(comstud): Rename the api_key argument above when we
|
||||||
# know it's not being used as keyword argument
|
# know it's not being used as keyword argument
|
||||||
password = api_key
|
password = api_key
|
||||||
@ -131,6 +131,7 @@ class Client(object):
|
|||||||
retries=retries,
|
retries=retries,
|
||||||
http_log_debug=http_log_debug,
|
http_log_debug=http_log_debug,
|
||||||
cacert=cacert,
|
cacert=cacert,
|
||||||
|
cert=cert,
|
||||||
auth_system=auth_system,
|
auth_system=auth_system,
|
||||||
auth_plugin=auth_plugin,
|
auth_plugin=auth_plugin,
|
||||||
session=session,
|
session=session,
|
||||||
|
7
releasenotes/notes/bug-1915996-3aaa5e2548eb7c93.yaml
Normal file
7
releasenotes/notes/bug-1915996-3aaa5e2548eb7c93.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
`Bug #1915996 <https://bugs.launchpad.net/python-cinderclient/+bug/1915996>`_:
|
||||||
|
Passing client certificates for mTLS connections was not supported
|
||||||
|
and now has been fixed.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user