]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: pass RGW zonegroup hostnames as custom SANs 69984/head
authorRedouane Kachach <rkachach@ibm.com>
Tue, 7 Jul 2026 10:37:53 +0000 (12:37 +0200)
committerRedouane Kachach <rkachach@ibm.com>
Tue, 7 Jul 2026 10:37:53 +0000 (12:37 +0200)
Fix RGW certificate generation so `zonegroup_hostnames` and wildcard
SANs are passed to `get_certificates()` using the correct keyword
argument. Previously, RGW built the expected SAN list from
`zonegroup_hostnames` and `wildcard_enabled`, but passed it
positionally so it was interpreted as ips list.

Also add a unit test to cover the RGW `zonegroup_hostnames` +
`wildcard_enabled` path and ensure SANs are passed as `custom_sans`.

Fixed other calls to enforce the usage of keywords for arguments just
to avoid similar issues in the future.

Fixes: https://tracker.ceph.com/issues/77980
Signed-off-by: Redouane Kachach <rkachach@ibm.com>
src/pybind/mgr/cephadm/services/cephadmservice.py
src/pybind/mgr/cephadm/services/mgmt_gateway.py
src/pybind/mgr/cephadm/services/monitoring.py
src/pybind/mgr/cephadm/tests/services/test_rgw.py

index cdc254c86784c83fbec0b7e565a4fb80f874c670..f1b3a56da7aba09eef6f8f05e859c1bba996844c 100644 (file)
@@ -1568,7 +1568,7 @@ class RgwService(CephService):
         if spec.ssl:
             san_list = spec.zonegroup_hostnames or []
             custom_sans = san_list + [f"*.{h}" for h in san_list] if spec.wildcard_enabled else san_list
-            tls_creds = self.get_certificates(daemon_spec, custom_sans)
+            tls_creds = self.get_certificates(daemon_spec, custom_sans=custom_sans)
             pem = f'{tls_creds.key.rstrip()}\n{tls_creds.cert.lstrip()}'
             rgw_cert_name = daemon_spec.name() if spec.generate_cert else spec.service_name()
             ret, out, err = self.mgr.check_mon_command({
index 801a76b281e57f73bc3428cf218c4b9db155f5aa..8dfa7730041bc606ce7928991e42b3a27250b877 100644 (file)
@@ -143,7 +143,7 @@ class MgmtGatewayService(CephadmService):
 
         if svc_spec.ssl:
             ip = self.get_mgmt_gw_ip(svc_spec, daemon_spec)
-            tls_pair = self.get_certificates(daemon_spec, [ip])
+            tls_pair = self.get_certificates(daemon_spec, ips=[ip])
             daemon_config["files"]["nginx.crt"] = tls_pair.cert
             daemon_config["files"]["nginx.key"] = tls_pair.key
 
index 6d0008f0ef643009cf572167ef4ec73c1911acef..f80cab0bd7078156060bb5cfffaa753407ff4bc2 100644 (file)
@@ -159,7 +159,7 @@ class GrafanaService(CephadmService):
     def get_grafana_certificates(self, daemon_spec: CephadmDaemonDeploySpec) -> TLSCredentials:
         host_ips = [self.mgr.inventory.get_addr(daemon_spec.host)]
         host_fqdns = [self.mgr.get_fqdn(daemon_spec.host), 'grafana_servers']
-        return self.get_certificates(daemon_spec, host_ips, host_fqdns)
+        return self.get_certificates(daemon_spec, ips=host_ips, fqdns=host_fqdns)
 
     def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]:
         assert self.TYPE == daemon_spec.daemon_type
@@ -301,7 +301,7 @@ class AlertmanagerService(CephadmService):
     def get_alertmanager_certificates(self, daemon_spec: CephadmDaemonDeploySpec) -> TLSCredentials:
         host_ips = [self.mgr.inventory.get_addr(daemon_spec.host)]
         host_fqdns = [self.mgr.get_fqdn(daemon_spec.host), 'alertmanager_servers']
-        return self.get_certificates(daemon_spec, host_ips, host_fqdns)
+        return self.get_certificates(daemon_spec, ips=host_ips, fqdns=host_fqdns)
 
     @classmethod
     def get_dependencies(cls, mgr: "CephadmOrchestrator",
@@ -524,7 +524,7 @@ class PrometheusService(CephadmService):
     def get_prometheus_certificates(self, daemon_spec: CephadmDaemonDeploySpec) -> TLSCredentials:
         host_ips = [self.mgr.inventory.get_addr(daemon_spec.host)]
         host_fqdns = [self.mgr.get_fqdn(daemon_spec.host), 'prometheus_servers']
-        return self.get_certificates(daemon_spec, host_ips, host_fqdns)
+        return self.get_certificates(daemon_spec, ips=host_ips, fqdns=host_fqdns)
 
     def get_service_discovery_cfg(self, security_enabled: bool, mgmt_gw_enabled: bool) -> Dict[str, List[str]]:
         """
index ecf9510885b0a75508f6182dd3397e3a5623e107..cec0197d64d69de55c5456b5ef5150a9d2dcc460 100644 (file)
@@ -496,3 +496,39 @@ class TestRGWService:
             # Inline certs must still be present — the service is still running on host2
             assert cm.get_cert(rgw_svc.cert_name, service_name=svc_name) is not None
             assert cm.get_key(rgw_svc.key_name, service_name=svc_name) is not None
+
+    @patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}'))
+    def test_rgw_zonegroup_hostnames_are_passed_as_custom_sans(
+            self, cephadm_module: CephadmOrchestrator):
+        """RGW zonegroup hostnames must be passed as custom DNS SANs.
+
+        Verifies that zonegroup hostnames are routed to get_certificates() as
+        the custom_sans keyword argument, so that values like s3.example.com
+        and *.s3.example.com are registered as DNS SANs on the generated
+        certificate.
+        """
+        with with_host(cephadm_module, 'host1'):
+            s = RGWSpec(
+                service_id='foo',
+                ssl=True,
+                zonegroup_hostnames=['s3.example.com'],
+                wildcard_enabled=True,
+                rgw_frontend_type='beast',
+            )
+            rgw_svc = service_registry.get_service('rgw')
+            tls_creds = TLSCredentials(
+                cert=ceph_generated_cert,
+                key=ceph_generated_key,
+            )
+            with patch.object(rgw_svc, 'get_certificates',
+                              return_value=tls_creds) as get_certificates:
+                with with_service(cephadm_module, s):
+                    # The serve loop may invoke get_certificates more than once;
+                    # assert on the arguments of every call, not the count.
+                    get_certificates.assert_called()
+                    for args, kwargs in get_certificates.call_args_list:
+                        # custom_sans must be a kwarg, never the positional ips arg
+                        assert len(args) == 1
+                        assert kwargs == {
+                            'custom_sans': ['s3.example.com', '*.s3.example.com'],
+                        }