From: Guillaume Abrioux Date: Wed, 10 Jul 2024 13:50:59 +0000 (+0200) Subject: mgr/cephadm: add SSL support to ceph-exporter X-Git-Tag: v20.0.0~1356^2~1 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=9dc630b00db26abfb72f9c884950e739c121aa5e;p=ceph.git mgr/cephadm: add SSL support to ceph-exporter This commit adds SSL support to the ceph-exporter deployment made by cephadm. When `secure_monitoring_stack` is set to `True`, the `ceph-exporter` container is restarted with SSL enabled. Signed-off-by: Guillaume Abrioux Signed-off-by: Redouane Kachach --- diff --git a/src/cephadm/cephadm.py b/src/cephadm/cephadm.py index 75ac3045c1e31..5c0762f8bf331 100755 --- a/src/cephadm/cephadm.py +++ b/src/cephadm/cephadm.py @@ -167,6 +167,7 @@ from cephadmlib import templating from cephadmlib.daemons.ceph import get_ceph_mounts_for_type, ceph_daemons from cephadmlib.daemons import ( Ceph, + CephExporter, CephIscsi, CephNvmeof, CustomContainer, @@ -867,6 +868,10 @@ def create_daemon_dirs( node_proxy = NodeProxy.init(ctx, fsid, ident.daemon_id) node_proxy.create_daemon_dirs(data_dir, uid, gid) + elif daemon_type == CephExporter.daemon_type: + ceph_exporter = CephExporter.init(ctx, fsid, ident.daemon_id) + ceph_exporter.create_daemon_dirs(data_dir, uid, gid) + else: daemon = daemon_form_create(ctx, ident) if isinstance(daemon, ContainerDaemonForm): diff --git a/src/cephadm/cephadmlib/daemons/ceph.py b/src/cephadm/cephadmlib/daemons/ceph.py index d5e87ad94847b..efb013c7e09c1 100644 --- a/src/cephadm/cephadmlib/daemons/ceph.py +++ b/src/cephadm/cephadmlib/daemons/ceph.py @@ -16,7 +16,14 @@ from ..constants import DEFAULT_IMAGE from ..context import CephadmContext from ..deployment_utils import to_deployment_container from ..exceptions import Error -from ..file_utils import make_run_dir, pathify +from ..file_utils import ( + make_run_dir, + pathify, + populate_files, + makedirs, + recursive_chown, +) +from ..data_utils import dict_get from ..host_facts import HostFacts from ..logging import Highlight from ..net_utils import get_hostname, get_ip_addresses @@ -298,6 +305,8 @@ class CephExporter(ContainerDaemonForm): self.port = config_json.get('port', self.DEFAULT_PORT) self.prio_limit = config_json.get('prio-limit', 5) self.stats_period = config_json.get('stats-period', 5) + self.https_enabled: bool = config_json.get('https_enabled', False) + self.files = dict_get(config_json, 'files', {}) @classmethod def init( @@ -323,6 +332,15 @@ class CephExporter(ContainerDaemonForm): f'--prio-limit={self.prio_limit}', f'--stats-period={self.stats_period}', ] + if self.https_enabled: + args.extend( + [ + '--cert-file', + '/etc/certs/ceph-exporter.crt', + '--key-file', + '/etc/certs/ceph-exporter.key', + ] + ) return args def validate(self) -> None: @@ -348,6 +366,9 @@ class CephExporter(ContainerDaemonForm): ) -> None: cm = Ceph.get_ceph_mounts(ctx, self.identity) mounts.update(cm) + if self.https_enabled: + data_dir = self.identity.data_dir(ctx.data_dir) + mounts.update({os.path.join(data_dir, 'etc/certs'): '/etc/certs'}) def customize_process_args( self, ctx: CephadmContext, args: List[str] @@ -376,6 +397,23 @@ class CephExporter(ContainerDaemonForm): # it until now self.validate() + def create_daemon_dirs(self, data_dir: str, uid: int, gid: int) -> None: + """Create files under the container data dir""" + if not os.path.isdir(data_dir): + raise OSError('data_dir is not a directory: %s' % (data_dir)) + logger.info('Writing ceph-exporter config...') + config_dir = os.path.join(data_dir, 'etc/') + ssl_dir = os.path.join(data_dir, 'etc/certs') + for ddir in [config_dir, ssl_dir]: + makedirs(ddir, uid, gid, 0o755) + recursive_chown(ddir, uid, gid) + cert_files = { + fname: content + for fname, content in self.files.items() + if fname.endswith('.crt') or fname.endswith('.key') + } + populate_files(ssl_dir, cert_files, uid, gid) + def get_ceph_mounts_for_type( ctx: CephadmContext, fsid: str, daemon_type: str diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index 51d677ec6cd6d..392c62ae9acd9 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -2992,7 +2992,8 @@ Then run the following: # this daemon type doesn't need deps mgmt pass - if daemon_type in ['prometheus', 'node-exporter', 'alertmanager', 'grafana']: + if daemon_type in ['prometheus', 'node-exporter', 'alertmanager', 'grafana', + 'ceph-exporter']: deps.append(f'secure_monitoring_stack:{self.secure_monitoring_stack}') return sorted(deps) diff --git a/src/pybind/mgr/cephadm/serve.py b/src/pybind/mgr/cephadm/serve.py index 440cc81ec44b9..dffd80b979b1e 100644 --- a/src/pybind/mgr/cephadm/serve.py +++ b/src/pybind/mgr/cephadm/serve.py @@ -1095,7 +1095,7 @@ class CephadmServe: action = 'reconfig' # we need only redeploy if secure_monitoring_stack or mgmt-gateway value has changed: # TODO(redo): check if we should just go always with redeploy (it's fast enough) - if dd.daemon_type in ['prometheus', 'node-exporter', 'alertmanager']: + if dd.daemon_type in ['prometheus', 'node-exporter', 'alertmanager', 'ceph-exporter']: diff = list(set(last_deps).symmetric_difference(set(deps))) REDEPLOY_TRIGGERS = ['secure_monitoring_stack', 'mgmt-gateway'] if any(svc in e for e in diff for svc in REDEPLOY_TRIGGERS): diff --git a/src/pybind/mgr/cephadm/services/cephadmservice.py b/src/pybind/mgr/cephadm/services/cephadmservice.py index 2964a44e2c3b1..d4b9ea262bb07 100644 --- a/src/pybind/mgr/cephadm/services/cephadmservice.py +++ b/src/pybind/mgr/cephadm/services/cephadmservice.py @@ -1263,7 +1263,7 @@ class CephExporterService(CephService): 'mon', 'allow r', 'mgr', 'allow r', 'osd', 'allow r']) - exporter_config = {} + exporter_config: Dict[str, Any] = {} if spec.sock_dir: exporter_config.update({'sock-dir': spec.sock_dir}) if spec.port: @@ -1297,6 +1297,7 @@ class CephExporterService(CephService): host_fqdn = self.mgr.get_fqdn(daemon_spec.host) return self.mgr.cert_mgr.generate_cert(host_fqdn, node_ip) + class CephfsMirrorService(CephService): TYPE = 'cephfs-mirror' diff --git a/src/pybind/mgr/cephadm/tests/test_services.py b/src/pybind/mgr/cephadm/tests/test_services.py index 93768ff1f8fda..179f117605958 100644 --- a/src/pybind/mgr/cephadm/tests/test_services.py +++ b/src/pybind/mgr/cephadm/tests/test_services.py @@ -6,7 +6,7 @@ from mgr_util import build_url import pytest -from unittest.mock import MagicMock, call, patch, ANY +from unittest.mock import Mock, MagicMock, call, patch, ANY from cephadm.serve import CephadmServe from cephadm.services.cephadmservice import MonService, MgrService, MdsService, RgwService, \ @@ -693,6 +693,50 @@ class TestMonitoring: use_current_daemon_image=False, ) + @patch("cephadm.serve.CephadmServe._run_cephadm") + @patch("socket.getfqdn") + @patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '::1') + @patch('cephadm.cert_mgr.CertMgr.get_root_ca', lambda instance: 'cephadm_root_cert') + @patch('cephadm.cert_mgr.CertMgr.generate_cert', lambda instance, fqdn, ip: ('mycert', 'mykey')) + @patch('cephadm.services.cephadmservice.CephExporterService.get_keyring_with_caps', Mock(return_value='[client.ceph-exporter.test]\nkey = fake-secret\n')) + def test_ceph_exporter_config_security_enabled(self, _get_fqdn, _run_cephadm, cephadm_module: CephadmOrchestrator): + _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) + + fqdn = 'host1.test' + _get_fqdn.return_value = fqdn + + with with_host(cephadm_module, 'test'): + cephadm_module.secure_monitoring_stack = True + with with_service(cephadm_module, CephExporterSpec()): + _run_cephadm.assert_called_with('test', 'ceph-exporter.test', + ['_orch', 'deploy'], [], + stdin=json.dumps({ + "fsid": "fsid", + "name": "ceph-exporter.test", + "image": "", + "deploy_arguments": [], + "params": {}, + "meta": { + "service_name": "ceph-exporter", + "ports": [], + "ip": None, + "deployed_by": [], + "rank": None, + "rank_generation": None, + "extra_container_args": None, + "extra_entrypoint_args": None + }, + "config_blobs": { + "config": "", + "keyring": "[client.ceph-exporter.test]\nkey = fake-secret\n", + "prio-limit": "5", + "stats-period": "5", + "https_enabled": True, + "files": { + "ceph-exporter.crt": "mycert", + "ceph-exporter.key": "mykey"}}}), + use_current_daemon_image=False) + @patch("cephadm.serve.CephadmServe._run_cephadm") @patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '::1') def test_prometheus_config_security_disabled(self, _run_cephadm, cephadm_module: CephadmOrchestrator):