Enable rgw trust forwarded https when https proxy

This option is required for server-side encryption to be allowed
if radosgw is behind a reverse proxy,
such as here when certificates are configured and apache2 is running.

ref. https://docs.ceph.com/en/latest/radosgw/encryption/

It is safe to always enable when https is configured in the charm,
because it will be securely behind the reverse proxy in the unit.
This option must not be enabled when https is not configured in the charm,
because this would allow clients to spoof headers.

Closes-Bug: #2021560
Change-Id: I940f9b2f424a3d98936b5f185bf8f87b71091317
This commit is contained in:
Samuel Walladge 2023-05-31 12:23:11 +09:30
parent 4484b0f0ed
commit 541ceec401
3 changed files with 105 additions and 11 deletions

View File

@ -295,6 +295,7 @@ class MonContext(context.CephContext):
'rgw_swift_versioning': config('rgw-swift-versioning-enabled'),
'relaxed_s3_bucket_names': config('relaxed-s3-bucket-names'),
'frontend': http_frontend,
'behind_https_proxy': https(),
}
# NOTE(dosaboy): these sections must correspond to what is supported in

View File

@ -16,6 +16,9 @@ ms bind ipv6 = true
{% endif %}
rgw swift versioning enabled = {{ rgw_swift_versioning }}
rgw relaxed s3 bucket names = {{ relaxed_s3_bucket_names }}
{% if behind_https_proxy -%}
rgw trust forwarded https = true
{% endif %}
{% if global -%}
# The following are user-provided options provided via the config-flags charm option.
# User-provided [global] section config

View File

@ -74,6 +74,7 @@ class HAProxyContextTests(CharmTestCase):
class MonContextTest(CharmTestCase):
maxDiff = None
def setUp(self):
super(MonContextTest, self).setUp(context, TO_PATCH)
@ -95,10 +96,16 @@ class MonContextTest(CharmTestCase):
else:
return []
@patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
@patch('charmhelpers.contrib.hahelpers.cluster.config_get')
@patch.object(ceph, 'config', lambda *args:
'{"client.radosgw.gateway": {"rgw init timeout": 60}}')
@patch.object(context, 'ensure_host_resolvable_v6')
def test_ctxt(self, mock_ensure_rsv_v6):
def test_ctxt(
self, mock_ensure_rsv_v6, mock_config_get, mock_relation_ids
):
mock_relation_ids.return_value = []
mock_config_get.side_effect = self.test_config.get
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
@ -136,7 +143,8 @@ class MonContextTest(CharmTestCase):
'frontend': 'beast',
'relaxed_s3_bucket_names': False,
'rgw_zonegroup': 'zonegroup1',
'rgw_realm': 'realmX'
'rgw_realm': 'realmX',
'behind_https_proxy': False,
}
self.assertEqual(expect, mon_ctxt())
self.assertFalse(mock_ensure_rsv_v6.called)
@ -148,10 +156,72 @@ class MonContextTest(CharmTestCase):
self.assertEqual(expect, mon_ctxt())
self.assertTrue(mock_ensure_rsv_v6.called)
@patch('ceph_radosgw_context.https')
@patch.object(ceph, 'config', lambda *args:
'{"client.radosgw.gateway": {"rgw init timeout": 60}}')
@patch.object(context, 'ensure_host_resolvable_v6')
def test_list_of_addresses_from_ceph_proxy(self, mock_ensure_rsv_v6):
def test_ctxt_with_https_proxy(self, mock_ensure_rsv_v6, mock_https):
mock_https.return_value = True
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
def _relation_get(attr, unit, rid):
if attr == 'ceph-public-address':
return addresses.pop()
elif attr == 'auth':
return 'cephx'
elif attr == 'rgw.testhost_key':
return 'testkey'
elif attr == 'fsid':
return 'testfsid'
self.relation_get.side_effect = _relation_get
self.relation_ids.return_value = ['mon:6']
self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
self.multisite.plain_list = self.plain_list_stub
self.determine_api_port.return_value = 70
expect = {
'auth_supported': 'cephx',
'hostname': 'testhost',
'mon_hosts': '10.5.4.1 10.5.4.2 10.5.4.3',
'old_auth': False,
'systemd_rgw': True,
'unit_public_ip': '10.255.255.255',
'use_syslog': 'false',
'loglevel': 1,
'port': 70,
'client_radosgw_gateway': {'rgw init timeout': 60},
'ipv6': False,
'rgw_zone': 'default',
'fsid': 'testfsid',
'rgw_swift_versioning': False,
'frontend': 'beast',
'relaxed_s3_bucket_names': False,
'rgw_zonegroup': 'zonegroup1',
'rgw_realm': 'realmX',
'behind_https_proxy': True,
}
self.assertEqual(expect, mon_ctxt())
self.assertFalse(mock_ensure_rsv_v6.called)
self.test_config.set('prefer-ipv6', True)
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
expect['ipv6'] = True
expect['port'] = "[::]:%s" % (70)
self.assertEqual(expect, mon_ctxt())
self.assertTrue(mock_ensure_rsv_v6.called)
@patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
@patch('charmhelpers.contrib.hahelpers.cluster.config_get')
@patch.object(ceph, 'config', lambda *args:
'{"client.radosgw.gateway": {"rgw init timeout": 60}}')
@patch.object(context, 'ensure_host_resolvable_v6')
def test_list_of_addresses_from_ceph_proxy(
self, mock_ensure_rsv_v6, mock_config_get, mock_relation_ids
):
mock_relation_ids.return_value = []
mock_config_get.side_effect = self.test_config.get
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
addresses = ['10.5.4.1 10.5.4.2 10.5.4.3']
@ -190,7 +260,8 @@ class MonContextTest(CharmTestCase):
'frontend': 'beast',
'relaxed_s3_bucket_names': False,
'rgw_zonegroup': 'zonegroup1',
'rgw_realm': 'realmX'
'rgw_realm': 'realmX',
'behind_https_proxy': False,
}
self.assertEqual(expect, mon_ctxt())
self.assertFalse(mock_ensure_rsv_v6.called)
@ -202,9 +273,13 @@ class MonContextTest(CharmTestCase):
self.assertEqual(expect, mon_ctxt())
self.assertTrue(mock_ensure_rsv_v6.called)
@patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
@patch('charmhelpers.contrib.hahelpers.cluster.config_get')
@patch.object(ceph, 'config', lambda *args:
'{"client.radosgw.gateway": {"rgw init timeout": 60}}')
def test_ctxt_missing_data(self):
def test_ctxt_missing_data(self, mock_config_get, mock_relation_ids):
mock_relation_ids.return_value = []
mock_config_get.side_effect = self.test_config.get
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
self.relation_get.return_value = None
@ -212,9 +287,13 @@ class MonContextTest(CharmTestCase):
self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
self.assertEqual({}, mon_ctxt())
@patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
@patch('charmhelpers.contrib.hahelpers.cluster.config_get')
@patch.object(ceph, 'config', lambda *args:
'{"client.radosgw.gateway": {"rgw init timeout": 60}}')
def test_ctxt_inconsistent_auths(self):
def test_ctxt_inconsistent_auths(self, mock_config_get, mock_relation_ids):
mock_relation_ids.return_value = []
mock_config_get.side_effect = self.test_config.get
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
@ -253,13 +332,18 @@ class MonContextTest(CharmTestCase):
'frontend': 'beast',
'relaxed_s3_bucket_names': False,
'rgw_zonegroup': 'zonegroup1',
'rgw_realm': 'realmX'
'rgw_realm': 'realmX',
'behind_https_proxy': False,
}
self.assertEqual(expect, mon_ctxt())
@patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
@patch('charmhelpers.contrib.hahelpers.cluster.config_get')
@patch.object(ceph, 'config', lambda *args:
'{"client.radosgw.gateway": {"rgw init timeout": 60}}')
def test_ctxt_consistent_auths(self):
def test_ctxt_consistent_auths(self, mock_config_get, mock_relation_ids):
mock_relation_ids.return_value = []
mock_config_get.side_effect = self.test_config.get
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
@ -298,7 +382,8 @@ class MonContextTest(CharmTestCase):
'frontend': 'beast',
'relaxed_s3_bucket_names': False,
'rgw_zonegroup': 'zonegroup1',
'rgw_realm': 'realmX'
'rgw_realm': 'realmX',
'behind_https_proxy': False,
}
self.assertEqual(expect, mon_ctxt())
@ -360,9 +445,13 @@ class MonContextTest(CharmTestCase):
_test_version = '16.2.0'
context.validate_http_frontend('beast')
@patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
@patch('charmhelpers.contrib.hahelpers.cluster.config_get')
@patch.object(ceph, 'config', lambda *args:
'{"client.radosgw.gateway": {"rgw init timeout": 60}}')
def test_ctxt_inconsistent_fsids(self):
def test_ctxt_inconsistent_fsids(self, mock_config_get, mock_relation_ids):
mock_relation_ids.return_value = []
mock_config_get.side_effect = self.test_config.get
self.socket.gethostname.return_value = 'testhost'
mon_ctxt = context.MonContext()
addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
@ -401,7 +490,8 @@ class MonContextTest(CharmTestCase):
'frontend': 'beast',
'relaxed_s3_bucket_names': False,
'rgw_zonegroup': 'zonegroup1',
'rgw_realm': 'realmX'
'rgw_realm': 'realmX',
'behind_https_proxy': False,
}
self.assertEqual(expect, mon_ctxt())