S3: add option to specify a custom CA cert bundle

Users can:
- set this option to the path to their CA cert bundle;
- use the default value to use the default CA cert bundle used by
  botocore.

We do not allow users to disable SSL certificates verification.

Closes-Bug: #2030825

Change-Id: I852beab63512f2469f92181a2ba427cbaa172ec2
This commit is contained in:
Cyril Roelandt 2023-09-06 22:08:55 +02:00
parent 086bc2058f
commit f9db33edc7
4 changed files with 67 additions and 6 deletions

View File

@ -230,7 +230,18 @@ Related Options:
* s3_store_large_object_size
* s3_store_large_object_chunk_size
""")
"""),
cfg.StrOpt('s3_store_cacert',
default='',
help="""
The path to the CA cert bundle to use. The default value (an empty string)
forces the use of the default CA cert bundle used by botocore.
Possible values:
* A path to the CA cert bundle to use
* An empty string to use the default CA cert bundle used by botocore
"""),
]
@ -464,6 +475,8 @@ class Store(glance_store.driver.Store):
return result
if param == 's3_store_region_name':
return result
if param == 's3_store_cacert':
return result
reason = _("Could not find %s in configuration options.") % param
LOG.error(reason)
raise exceptions.BadStoreConfiguration(store_name="s3",
@ -500,11 +513,14 @@ class Store(glance_store.driver.Store):
else:
endpoint_url = s3_host
return session.client(service_name='s3',
endpoint_url=endpoint_url,
region_name=region_name,
use_ssl=(loc.scheme == 's3+https'),
config=config)
store_cacert = self._option_get('s3_store_cacert')
return session.client(
service_name='s3',
endpoint_url=endpoint_url,
region_name=region_name,
use_ssl=(loc.scheme == 's3+https'),
verify=None if store_cacert == '' else store_cacert,
config=config)
def _operation_set(self, loc):
"""Objects and variables frequently used when operating S3 are

View File

@ -91,6 +91,7 @@ class TestMultiS3Store(base.MultiStoreBaseTest,
s3_store_secret_key='key',
s3_store_host='https://s3-region1.com',
s3_store_region_name='custom_region_name',
s3_store_cacert='path/to/cert/bundle.pem',
s3_store_bucket='glance',
s3_store_large_object_size=S3_CONF[
's3_store_large_object_size'
@ -147,6 +148,24 @@ class TestMultiS3Store(base.MultiStoreBaseTest,
region_name='custom_region_name',
service_name='s3',
use_ssl=False,
verify='path/to/cert/bundle.pem',
)
@mock.patch('glance_store.location.Location')
@mock.patch.object(boto3.session.Session, "client")
def test_client_custom_ca_cert_bundle(self, mock_client, mock_loc):
"""Test a custom s3_store_cacert in config"""
mock_loc.accesskey = 'abcd'
mock_loc.secretkey = 'efgh'
mock_loc.bucket = 'bucket1'
self.store._create_s3_client(mock_loc)
mock_client.assert_called_with(
config=mock.ANY,
endpoint_url='https://s3-region1.com',
region_name='custom_region_name',
service_name='s3',
use_ssl=False,
verify='path/to/cert/bundle.pem',
)
@mock.patch.object(boto3.session.Session, "client")

View File

@ -112,6 +112,7 @@ class OptsTestCase(base.StoreBaseTest):
's3_store_large_object_size',
's3_store_large_object_chunk_size',
's3_store_thread_pools',
's3_store_cacert',
'swift_store_expire_soon_interval',
'swift_store_admin_tenants',
'swift_store_auth_address',

View File

@ -106,6 +106,31 @@ class TestStore(base.StoreBaseTest,
region_name='regionOne',
service_name='s3',
use_ssl=False,
verify=None,
)
@mock.patch('glance_store.location.Location')
@mock.patch.object(boto3.session.Session, "client")
def test_client_custom_ca_cert_bundle(self, mock_client, mock_loc):
"""Test a custom s3_store_cacert in config"""
self.config(s3_store_host='http://example.com')
self.config(s3_store_cacert='path/to/cert/bundle.pem')
self.config(s3_store_bucket_url_format='path')
self.store.configure()
mock_loc.accesskey = 'abcd'
mock_loc.secretkey = 'efgh'
mock_loc.bucket = 'bucket1'
self.store._create_s3_client(mock_loc)
mock_client.assert_called_with(
config=mock.ANY,
endpoint_url='http://example.com',
region_name=None,
service_name='s3',
use_ssl=False,
verify='path/to/cert/bundle.pem',
)
@mock.patch.object(boto3.session.Session, "client")