Add support for using thumbprint to verify Manager certificate

This patch adds a new config option "thumbprint".
It will be used to verify Manager server certificate
when "insecure" is false and "ca_file" is unset.


Change-Id: Idfb654c5b502cd6df12275e0a88cf10c546d819d
This commit is contained in:
Shih-Hao Li 2019-11-26 21:03:09 -08:00 committed by Adit Sarfaty
parent 20996b400d
commit 56ae89a6fc
3 changed files with 94 additions and 10 deletions

View File

@ -125,6 +125,44 @@ class RequestsHTTPProviderTestCase(unittest.TestCase):
mock_get_def_headers.assert_called_once_with(
mock.ANY, cluster_provider, False, token_provider_inst)
@mock.patch("vmware_nsxlib.v3.cluster.NSXHTTPAdapter.__init__")
def test_new_connection_with_ca_file(self, mock_adaptor_init):
mock_api = mock.Mock()
mock_api.nsxlib_config = mock.Mock()
mock_api.nsxlib_config.retries = 100
mock_api.nsxlib_config.insecure = False
mock_adaptor_init.return_value = None
provider = cluster.NSXRequestsHTTPProvider()
with mock.patch.object(cluster.TimeoutSession, 'request',
return_value=get_sess_create_resp()):
session = provider.new_connection(
mock_api, cluster.Provider('9.8.7.6', 'https://9.8.7.6',
None, None, "ca_file"))
self.assertEqual("ca_file", session.verify)
mock_adaptor_init.assert_called_once_with(
pool_connections=1, pool_maxsize=1,
max_retries=100, pool_block=False, thumbprint=None)
@mock.patch("vmware_nsxlib.v3.cluster.NSXHTTPAdapter.__init__")
def test_new_connection_with_thumbprint(self, mock_adaptor_init):
mock_api = mock.Mock()
mock_api.nsxlib_config = mock.Mock()
mock_api.nsxlib_config.retries = 100
mock_api.nsxlib_config.insecure = False
mock_adaptor_init.return_value = None
provider = cluster.NSXRequestsHTTPProvider()
with mock.patch.object(cluster.TimeoutSession, 'request',
return_value=get_sess_create_resp()):
session = provider.new_connection(
mock_api, cluster.Provider('9.8.7.6', 'https://9.8.7.6',
None, None, None, "thumbprint"))
self.assertIsNone(session.verify)
mock_adaptor_init.assert_called_once_with(
pool_connections=1, pool_maxsize=1,
max_retries=100, pool_block=False, thumbprint="thumbprint")
def test_validate_connection_keep_alive(self):
mock_conn = mocks.MockRequestSessionApi()
mock_conn.default_headers = {}
@ -189,6 +227,16 @@ class RequestsHTTPProviderTestCase(unittest.TestCase):
provider.validate_connection(mock_cluster, mock_ep, mock_conn)
class NSXHTTPAdapterTestCase(nsxlib_testcase.NsxClientTestCase):
@mock.patch("requests.adapters.HTTPAdapter.init_poolmanager")
def test_init_poolmanager(self, mock_init_poolmanager):
cluster.NSXHTTPAdapter(thumbprint="thumbprint")
mock_init_poolmanager.assert_called_once_with(
mock.ANY, mock.ANY, block=mock.ANY,
assert_fingerprint="thumbprint")
class NsxV3ClusteredAPITestCase(nsxlib_testcase.NsxClientTestCase):
def _assert_providers(self, cluster_api, provider_tuples):

View File

@ -221,16 +221,28 @@ class NSXRequestsHTTPProvider(AbstractHTTPProvider):
# NSX v3 doesn't use redirects
session.max_redirects = 0
session.verify = not config.insecure
if session.verify and provider.ca_file:
if config.insecure:
# no verification on server certificate
session.verify = False
thumbprint = None
elif provider.ca_file:
# verify using the said ca bundle path
session.verify = provider.ca_file
thumbprint = None
elif provider.thumbprint:
# verify using the thumbprint
session.verify = None
thumbprint = provider.thumbprint
else:
# verify using the default system root CAs
session.verify = True
thumbprint = None
# we are pooling with eventlet in the cluster class
adapter = adapters.HTTPAdapter(
adapter = NSXHTTPAdapter(
pool_connections=1, pool_maxsize=1,
max_retries=config.retries,
pool_block=False)
pool_block=False, thumbprint=thumbprint)
session.mount('http://', adapter)
session.mount('https://', adapter)
@ -314,6 +326,17 @@ class NSXRequestsHTTPProvider(AbstractHTTPProvider):
{'url': provider.url, 'hdr': session.default_headers})
class NSXHTTPAdapter(adapters.HTTPAdapter):
def __init__(self, *args, **kwargs):
self.thumbprint = kwargs.pop("thumbprint", None)
super(NSXHTTPAdapter, self).__init__(*args, **kwargs)
def init_poolmanager(self, *args, **kwargs):
if self.thumbprint:
kwargs["assert_fingerprint"] = self.thumbprint
super(NSXHTTPAdapter, self).init_poolmanager(*args, **kwargs)
class ClusterHealth(object):
"""Indicator of overall cluster health.
@ -343,12 +366,14 @@ class Provider(object):
Which has a unique id a connection URL, and the credential details.
"""
def __init__(self, provider_id, provider_url, username, password, ca_file):
def __init__(self, provider_id, provider_url, username, password, ca_file,
thumbprint=None):
self.id = provider_id
self.url = provider_url
self.username = username
self.password = password
self.ca_file = ca_file
self.thumbprint = thumbprint
def __str__(self):
return str(self.url)
@ -717,5 +742,6 @@ class NSXClusteredAPI(ClusteredAPI):
urlparse.urlunparse(conf_url),
self.nsxlib_config.username(provider_index),
self.nsxlib_config.password(provider_index),
self.nsxlib_config.ca_file(provider_index)))
self.nsxlib_config.ca_file(provider_index),
self.nsxlib_config.thumbprint(provider_index)))
return providers

View File

@ -35,13 +35,18 @@ class NsxLibConfig(object):
instead of basic authentication.
:param insecure: If true, the NSX Manager server certificate is not
verified. If false the CA bundle specified via "ca_file"
will be used or if unset the default system root CAs
will be used or if unset the "thumbprint" will be used.
If "thumbprint" is unset, the default system root CAs
will be used.
:param ca_file: Specify a CA bundle file to use in verifying the NSX
Manager server certificate. This option is ignored if
"insecure" is set to True. If "insecure" is set to
False and ca_file is unset, the system root CAs will
be used to verify the server certificate.
"insecure" is set to True. If "insecure" is set to False
and "ca_file" is unset, the "thumbprint" will be used.
If "thumbprint" is unset, the system root CAs will be
used to verify the server certificate.
:param thumbprint: Specify a thumbprint string to use in verifying the
NSX Manager server certificate. This option is ignored
if "insecure" is set to True or "ca_file" is defined.
:param token_provider: None, or instance of implemented AbstractJWTProvider
which will return the JSON Web Token used in the
requests in NSX for authorization.
@ -98,6 +103,7 @@ class NsxLibConfig(object):
client_cert_provider=None,
insecure=True,
ca_file=None,
thumbprint=None,
token_provider=None,
concurrent_connections=10,
retries=3,
@ -123,6 +129,7 @@ class NsxLibConfig(object):
self._username = username
self._password = password
self._ca_file = ca_file
self._thumbprint = thumbprint
self.insecure = insecure
self.concurrent_connections = concurrent_connections
self.retries = retries
@ -178,3 +185,6 @@ class NsxLibConfig(object):
def ca_file(self, index):
return self._attribute_by_index(self._ca_file, index)
def thumbprint(self, index):
return self._attribute_by_index(self._thumbprint, index)