From 2a566b5e05c302efcfe8f138f6a6aa5ec524a8e5 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Wed, 4 Jul 2018 12:32:30 +0200 Subject: [PATCH] Fix keystone tls detection and verification At present the check for which transport to use references a configuration option that has been removed. Update check to test for current configuration option plus add a check for presence of `tls-certificates` relation. Also remove insecure option and add verify option to allow control of verification including providing own CA certificate bundle. Reference for verify option: https://docs.openstack.org/keystoneauth/latest/api/keystoneauth1.html#keystoneauth1.session.Session --- .../test_zaza_utilities_openstack.py | 77 ++++++++++++++++++- zaza/utilities/openstack.py | 38 +++++---- 2 files changed, 100 insertions(+), 15 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index b183e1b..e8e40be 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -150,6 +150,77 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): self.get_current_os_versions.return_value = {"keystone": "mitaka"} self.assertEqual(openstack_utils.get_keystone_scope(), "PROJECT") + def _test_get_overcloud_auth(self, tls_relation=False, ssl_cert=False, + v2_api=False): + self.patch_object(openstack_utils.model, 'get_relation_id') + self.patch_object(openstack_utils, 'get_application_config_option') + self.patch_object(openstack_utils, 'get_keystone_ip') + self.patch_object(openstack_utils, "get_current_os_versions") + + self.get_keystone_ip.return_value = '127.0.0.1' + self.get_relation_id.return_value = None + self.get_application_config_option.return_value = None + if tls_relation or ssl_cert: + port = 35357 + transport = 'https' + if tls_relation: + self.get_relation_id.return_value = 'tls-certificates:1' + if ssl_cert: + self.get_application_config_option.side_effect = [ + 'FAKECRTDATA', + None, + ] + else: + port = 5000 + transport = 'http' + if v2_api: + str_api = 'v2.0' + self.get_current_os_versions.return_value = {"keystone": "mitaka"} + expect = { + 'OS_AUTH_URL': '{}://127.0.0.1:{}/{}' + .format(transport, port, str_api), + 'OS_TENANT_NAME': 'admin', + 'OS_USERNAME': 'admin', + 'OS_PASSWORD': 'openstack', + 'OS_REGION_NAME': 'RegionOne', + 'API_VERSION': 2, + } + else: + str_api = 'v3' + self.get_current_os_versions.return_value = {"keystone": "queens"} + expect = { + 'OS_AUTH_URL': '{}://127.0.0.1:{}/{}' + .format(transport, port, str_api), + 'OS_USERNAME': 'admin', + 'OS_PASSWORD': 'openstack', + 'OS_REGION_NAME': 'RegionOne', + 'OS_DOMAIN_NAME': 'admin_domain', + 'OS_USER_DOMAIN_NAME': 'admin_domain', + 'OS_PROJECT_NAME': 'admin', + 'OS_PROJECT_DOMAIN_NAME': 'admin_domain', + 'API_VERSION': 3, + } + self.assertEqual(openstack_utils.get_overcloud_auth(), + expect) + + def test_get_overcloud_auth(self): + self._test_get_overcloud_auth() + + def test_get_overcloud_auth_v2(self): + self._test_get_overcloud_auth(v2_api=True) + + def test_get_overcloud_auth_tls_relation(self): + self._test_get_overcloud_auth(tls_relation=True) + + def test_get_overcloud_auth_tls_relation_v2(self): + self._test_get_overcloud_auth(v2_api=True, tls_relation=True) + + def test_get_overcloud_auth_ssl_cert(self): + self._test_get_overcloud_auth(ssl_cert=True) + + def test_get_overcloud_auth_ssl_cert_v2(self): + self._test_get_overcloud_auth(v2_api=True, ssl_cert=True) + def test_get_overcloud_keystone_session(self): self.patch_object(openstack_utils, "get_keystone_session") self.patch_object(openstack_utils, "get_keystone_scope") @@ -160,7 +231,8 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): self.get_overcloud_auth.return_value = _auth openstack_utils.get_overcloud_keystone_session() - self.get_keystone_session.assert_called_once_with(_auth, scope=_scope) + self.get_keystone_session.assert_called_once_with(_auth, scope=_scope, + verify=None) def test_get_undercloud_keystone_session(self): self.patch_object(openstack_utils, "get_keystone_session") @@ -172,7 +244,8 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): self.get_undercloud_auth.return_value = _auth openstack_utils.get_undercloud_keystone_session() - self.get_keystone_session.assert_called_once_with(_auth, scope=_scope) + self.get_keystone_session.assert_called_once_with(_auth, scope=_scope, + verify=None) def test_get_urllib_opener(self): self.patch_object(openstack_utils.urllib.request, "ProxyHandler") diff --git a/zaza/utilities/openstack.py b/zaza/utilities/openstack.py index 4d00ffb..f62c4bd 100644 --- a/zaza/utilities/openstack.py +++ b/zaza/utilities/openstack.py @@ -172,13 +172,16 @@ def get_keystone_scope(): return scope -def get_keystone_session(opentackrc_creds, insecure=True, scope='PROJECT'): +def get_keystone_session(opentackrc_creds, scope='PROJECT', verify=None): """Return keystone session. :param openrc_creds: Openstack RC credentials :type openrc_creds: dict - :param insecure: Allow insecure HTTPS connections - :type insecure: boolean + :param verify: Control TLS certificate verification behaviour + :type verify: any (True - use system certs, + False - do not verify, + None - defer to requests library to find certs, + str - path to a CA cert bundle) :param scope: Authentication scope: PROJECT or DOMAIN :type scope: string :returns: Keystone session object @@ -189,27 +192,33 @@ def get_keystone_session(opentackrc_creds, insecure=True, scope='PROJECT'): auth = v2.Password(**keystone_creds) else: auth = v3.Password(**keystone_creds) - return session.Session(auth=auth, verify=not insecure) + return session.Session(auth=auth, verify=verify) -def get_overcloud_keystone_session(): +def get_overcloud_keystone_session(verify=None): """Return Over cloud keystone session. + :param verify: Control TLS certificate verification behaviour + :type verify: any :returns keystone_session: keystoneauth1.session.Session object :rtype: keystoneauth1.session.Session """ return get_keystone_session(get_overcloud_auth(), - scope=get_keystone_scope()) + scope=get_keystone_scope(), + verify=verify) -def get_undercloud_keystone_session(): +def get_undercloud_keystone_session(verify=None): """Return Under cloud keystone session. + :param verify: Control TLS certificate verification behaviour + :type verify: any :returns keystone_session: keystoneauth1.session.Session object :rtype: keystoneauth1.session.Session """ return get_keystone_session(get_undercloud_auth(), - scope=get_keystone_scope()) + scope=get_keystone_scope(), + verify=verify) def get_keystone_session_client(session): @@ -223,17 +232,17 @@ def get_keystone_session_client(session): return keystoneclient_v3.Client(session=session) -def get_keystone_client(opentackrc_creds, insecure=True): +def get_keystone_client(opentackrc_creds, verify=None): """Return authenticated keystoneclient and set auth_ref for service_catalog. :param openrc_creds: Openstack RC credentials :type openrc_creds: dict - :param insecure: Allow insecure HTTPS connections - :type insecure: boolean + :param verify: Control TLS certificate verification behaviour + :type verify: any :returns: Authenticated keystoneclient :rtype: keystoneclient.v3.Client object """ - session = get_keystone_session(opentackrc_creds, insecure) + session = get_keystone_session(opentackrc_creds, verify=verify) client = get_keystone_session_client(session) keystone_creds = get_ks_creds(opentackrc_creds) if opentackrc_creds.get('API_VERSION', 2) == 2: @@ -1190,7 +1199,10 @@ def get_overcloud_auth(): :returns: Dictionary of authentication settings :rtype: dict """ - if get_application_config_option('keystone', 'use-https').lower() == 'yes': + tls_rid = model.get_relation_id('keystone', 'vault', + remote_interface_name='tls-certificates') + ssl_config = get_application_config_option('keystone', 'ssl_cert') + if tls_rid or ssl_config: transport = 'https' port = 35357 else: