From 615cc81051164c8e53c4237a28563264d1edc768 Mon Sep 17 00:00:00 2001 From: Patrick East Date: Tue, 16 Feb 2016 20:38:22 -0800 Subject: [PATCH] Allow for Pure drivers to verify HTTPS requests This pipes in the config options (driver_ssl_cert_verify and driver_ssl_cert_path) for verifying https requests to the purestorage python module. This will allow for the underlying https management API requests to the array to be verified. To use this feature a newer (>1.4.0) version of the pure storage python module will be required. DocImpact: Need to update Pure Storage Volume Driver config reference to mention that it can use the new config options and the python module version requirements that go along with it. Change-Id: Id4d73e76ec64e4ac00291ed9b7377c47e40bc551 Closes-Bug: #1546655 --- cinder/tests/unit/test_pure.py | 56 +++++++++++++++++++ cinder/volume/drivers/pure.py | 48 +++++++++++++--- ...erify-https-requests-464320c97ba77a1f.yaml | 5 ++ 3 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 releasenotes/notes/pure-verify-https-requests-464320c97ba77a1f.yaml diff --git a/cinder/tests/unit/test_pure.py b/cinder/tests/unit/test_pure.py index 46ca32bb6..d80a81608 100644 --- a/cinder/tests/unit/test_pure.py +++ b/cinder/tests/unit/test_pure.py @@ -343,6 +343,8 @@ class PureDriverTestCase(test.TestCase): self.mock_config.volume_backend_name = VOLUME_BACKEND_NAME self.mock_config.safe_get.return_value = None self.mock_config.pure_eradicate_on_delete = False + self.mock_config.driver_ssl_cert_verify = False + self.mock_config.driver_ssl_cert_path = None self.array = mock.Mock() self.array.get.return_value = GET_ARRAY_PRIMARY self.array.array_name = GET_ARRAY_PRIMARY["array_name"] @@ -352,6 +354,7 @@ class PureDriverTestCase(test.TestCase): self.array2.array_id = GET_ARRAY_SECONDARY["id"] self.array2.get.return_value = GET_ARRAY_SECONDARY self.purestorage_module = pure.purestorage + self.purestorage_module.VERSION = '1.4.0' self.purestorage_module.PureHTTPError = FakePureStorageHTTPError def fake_get_array(*args, **kwargs): @@ -392,6 +395,7 @@ class PureBaseSharedDriverTestCase(PureDriverTestCase): super(PureBaseSharedDriverTestCase, self).tearDown() +@ddt.ddt class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): def setUp(self): super(PureBaseVolumeDriverTestCase, self).setUp() @@ -1885,6 +1889,58 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase): remvollist=[VOLUME_PURITY_NAME] ) + @ddt.data(dict(version='1.5.0'), dict(version='2.0.0')) + @ddt.unpack + def test_get_flasharray_verify_https(self, version): + self.purestorage_module.VERSION = version + san_ip = '1.2.3.4' + api_token = 'abcdef' + cert_path = '/my/ssl/certs' + self.purestorage_module.FlashArray.return_value = mock.MagicMock() + + self.driver._get_flasharray(san_ip, + api_token, + verify_https=True, + ssl_cert_path=cert_path) + self.purestorage_module.FlashArray.assert_called_with( + san_ip, + api_token=api_token, + rest_version=None, + verify_https=True, + ssl_cert=cert_path + ) + + def test_get_flasharray_dont_verify_https_version_too_old(self): + self.purestorage_module.VERSION = '1.4.0' + san_ip = '1.2.3.4' + api_token = 'abcdef' + self.purestorage_module.FlashArray.return_value = mock.MagicMock() + + self.driver._get_flasharray(san_ip, + api_token, + verify_https=False, + ssl_cert_path=None) + self.purestorage_module.FlashArray.assert_called_with( + san_ip, + api_token=api_token, + rest_version=None + ) + + def test_get_flasharray_verify_https_version_too_old(self): + self.purestorage_module.VERSION = '1.4.0' + san_ip = '1.2.3.4' + api_token = 'abcdef' + self.purestorage_module.FlashArray.return_value = mock.MagicMock() + + self.assertRaises( + exception.PureDriverException, + self.driver._get_flasharray, + san_ip, + api_token, + verify_https=True, + ssl_cert_path='/my/ssl/certs' + ) + class PureISCSIDriverTestCase(PureDriverTestCase): diff --git a/cinder/volume/drivers/pure.py b/cinder/volume/drivers/pure.py index b2f0fc06a..1b48157b6 100644 --- a/cinder/volume/drivers/pure.py +++ b/cinder/volume/drivers/pure.py @@ -168,7 +168,14 @@ class PureBaseVolumeDriver(san.SanDriver): backend_id = replication_device["backend_id"] san_ip = replication_device["san_ip"] api_token = replication_device["api_token"] - target_array = self._get_flasharray(san_ip, api_token) + verify_https = replication_device.get("ssl_cert_verify", False) + ssl_cert_path = replication_device.get("ssl_cert_path", None) + target_array = self._get_flasharray( + san_ip, + api_token, + verify_https=verify_https, + ssl_cert_path=ssl_cert_path + ) target_array._backend_id = backend_id LOG.debug("Adding san_ip %(san_ip)s to replication_targets.", {"san_ip": san_ip}) @@ -210,8 +217,10 @@ class PureBaseVolumeDriver(san.SanDriver): self.SUPPORTED_REST_API_VERSIONS self._array = self._get_flasharray( self.configuration.san_ip, - api_token=self.configuration.pure_api_token) - + api_token=self.configuration.pure_api_token, + verify_https=self.configuration.driver_ssl_cert_verify, + ssl_cert_path=self.configuration.driver_ssl_cert_path + ) self._array._backend_id = self._backend_name LOG.debug("Primary array backend_id: %s", self.configuration.config_group) @@ -942,10 +951,31 @@ class PureBaseVolumeDriver(san.SanDriver): self._rename_volume_object(snap_name, unmanaged_snap_name) @staticmethod - def _get_flasharray(san_ip, api_token, rest_version=None): - array = purestorage.FlashArray(san_ip, - api_token=api_token, - rest_version=rest_version) + def _get_flasharray(san_ip, api_token, rest_version=None, + verify_https=None, ssl_cert_path=None): + # Older versions of the module (1.4.0) do not support setting ssl certs + # TODO(patrickeast): In future releases drop support for 1.4.0 + module_version = purestorage.VERSION.split('.') + major_version = int(module_version[0]) + minor_version = int(module_version[1]) + if major_version > 1 or (major_version == 1 and minor_version > 4): + array = purestorage.FlashArray(san_ip, + api_token=api_token, + rest_version=rest_version, + verify_https=verify_https, + ssl_cert=ssl_cert_path) + else: + if verify_https or ssl_cert_path is not None: + msg = _('HTTPS certificate verification was requested ' + 'but cannot be enabled with purestorage ' + 'module version %(version)s. Upgrade to a ' + 'newer version to enable this feature.') % { + 'version': purestorage.VERSION + } + raise exception.PureDriverException(reason=msg) + array = purestorage.FlashArray(san_ip, + api_token=api_token, + rest_version=rest_version) array_info = array.get() array.array_name = array_info["array_name"] array.array_id = array_info["id"] @@ -1135,7 +1165,9 @@ class PureBaseVolumeDriver(san.SanDriver): target_array = self._get_flasharray( secondary_array._target, api_token=secondary_array._api_token, - rest_version='1.3' + rest_version='1.3', + verify_https=secondary_array._verify_https, + ssl_cert_path=secondary_array._ssl_cert ) else: target_array = secondary_array diff --git a/releasenotes/notes/pure-verify-https-requests-464320c97ba77a1f.yaml b/releasenotes/notes/pure-verify-https-requests-464320c97ba77a1f.yaml new file mode 100644 index 000000000..3c227aaa6 --- /dev/null +++ b/releasenotes/notes/pure-verify-https-requests-464320c97ba77a1f.yaml @@ -0,0 +1,5 @@ +--- +security: + - Pure Storage Volume Drivers can now utilize driver_ssl_cert_verify and + driver_ssl_cert_path config options to allow for secure https requests to + the FlashArray. -- 2.45.2