From 42528933dc09626777f93a0eb91a55fb53429a36 Mon Sep 17 00:00:00 2001 From: Adam King Date: Tue, 21 Mar 2023 14:12:03 -0400 Subject: [PATCH] mgr/cephadm: allow configuring anonymous access for grafana In case users want to not allow anonymous viewers to view the grafana dashboard but don't want to use a full custom config Fixes: https://tracker.ceph.com/issues/59117 Signed-off-by: Adam King (cherry picked from commit e6690dde7b384d7d13e1a17119c648acfadee638) --- doc/cephadm/services/monitoring.rst | 22 +++++++++ src/pybind/mgr/cephadm/services/monitoring.py | 1 + .../templates/services/grafana/grafana.ini.j2 | 2 + src/pybind/mgr/cephadm/tests/test_services.py | 48 +++++++++++++++++++ src/pybind/mgr/cephadm/tests/test_spec.py | 1 + .../ceph/deployment/service_spec.py | 8 ++++ .../ceph/tests/test_service_spec.py | 4 ++ 7 files changed, 86 insertions(+) diff --git a/doc/cephadm/services/monitoring.rst b/doc/cephadm/services/monitoring.rst index 560a95b5146..a17a5ba039b 100644 --- a/doc/cephadm/services/monitoring.rst +++ b/doc/cephadm/services/monitoring.rst @@ -467,6 +467,28 @@ Then apply this specification: Grafana will now create an admin user called ``admin`` with the given password. +Turning off anonymous access +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, cephadm allows anonymous users (users who have not provided any +login information) limited, viewer only access to the grafana dashboard. In +order to set up grafana to only allow viewing from logged in users, you can +set ``anonymous_access: False`` in your grafana spec. + +.. code-block:: yaml + + service_type: grafana + placement: + hosts: + - host1 + spec: + anonymous_access: False + initial_admin_password: "mypassword" + +Since deploying grafana with anonymous access set to false without an initial +admin password set would make the dashboard inaccessible, cephadm requires +setting the ``initial_admin_password`` when ``anonymous_access`` is set to false. + Setting up Alertmanager ----------------------- diff --git a/src/pybind/mgr/cephadm/services/monitoring.py b/src/pybind/mgr/cephadm/services/monitoring.py index 6c63ef6436a..e0c0640ae49 100644 --- a/src/pybind/mgr/cephadm/services/monitoring.py +++ b/src/pybind/mgr/cephadm/services/monitoring.py @@ -68,6 +68,7 @@ class GrafanaService(CephadmService): GrafanaSpec, self.mgr.spec_store.active_specs[daemon_spec.service_name]) grafana_ini = self.mgr.template.render( 'services/grafana/grafana.ini.j2', { + 'anonymous_access': spec.anonymous_access, 'initial_admin_password': spec.initial_admin_password, 'http_port': daemon_spec.ports[0] if daemon_spec.ports else self.DEFAULT_SERVICE_PORT, 'protocol': spec.protocol, diff --git a/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 b/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 index 9d74ccd039c..e6c7bce1524 100644 --- a/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 +++ b/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 @@ -1,10 +1,12 @@ # {{ cephadm_managed }} [users] default_theme = light +{% if anonymous_access %} [auth.anonymous] enabled = true org_name = 'Main Org.' org_role = 'Viewer' +{% endif %} [server] domain = 'bootstrap.storage.lab' protocol = {{ protocol }} diff --git a/src/pybind/mgr/cephadm/tests/test_services.py b/src/pybind/mgr/cephadm/tests/test_services.py index b6172e45af0..7d545431873 100644 --- a/src/pybind/mgr/cephadm/tests/test_services.py +++ b/src/pybind/mgr/cephadm/tests/test_services.py @@ -911,6 +911,54 @@ class TestMonitoring: 'certs/cert_file': ANY, 'certs/cert_key': ANY}}, ['secure_monitoring_stack:False']) + @patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) + def test_grafana_no_anon_access(self, cephadm_module: CephadmOrchestrator): + # with anonymous_access set to False, expecting the [auth.anonymous] section + # to not be present in the grafana config. Note that we require an initial_admin_password + # to be provided when anonymous_access is False + with with_host(cephadm_module, 'test'): + with with_service(cephadm_module, ServiceSpec('mgr')) as _, \ + with_service(cephadm_module, GrafanaSpec(anonymous_access=False, initial_admin_password='secure')): + out = cephadm_module.cephadm_services['grafana'].generate_config( + CephadmDaemonDeploySpec('test', 'daemon', 'grafana')) + assert out == ( + { + 'files': + { + 'grafana.ini': + '# This file is generated by cephadm.\n' + '[users]\n' + ' default_theme = light\n' + '[server]\n' + " domain = 'bootstrap.storage.lab'\n" + ' protocol = https\n' + ' cert_file = /etc/grafana/certs/cert_file\n' + ' cert_key = /etc/grafana/certs/cert_key\n' + ' http_port = 3000\n' + ' http_addr = \n' + '[snapshots]\n' + ' external_enabled = false\n' + '[security]\n' + ' admin_user = admin\n' + ' admin_password = secure\n' + ' cookie_secure = true\n' + ' cookie_samesite = none\n' + ' allow_embedding = true', + 'provisioning/datasources/ceph-dashboard.yml': + "# This file is generated by cephadm.\n" + "apiVersion: 1\n\n" + 'deleteDatasources:\n\n' + 'datasources:\n\n' + " - name: 'Loki'\n" + " type: 'loki'\n" + " access: 'proxy'\n" + " url: ''\n" + ' basicAuth: false\n' + ' isDefault: false\n' + ' editable: false', + 'certs/cert_file': ANY, + 'certs/cert_key': ANY}}, ['secure_monitoring_stack:False']) + @patch("cephadm.serve.CephadmServe._run_cephadm") def test_monitoring_ports(self, _run_cephadm, cephadm_module: CephadmOrchestrator): _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) diff --git a/src/pybind/mgr/cephadm/tests/test_spec.py b/src/pybind/mgr/cephadm/tests/test_spec.py index 5e2a479d9f5..78a2d73118f 100644 --- a/src/pybind/mgr/cephadm/tests/test_spec.py +++ b/src/pybind/mgr/cephadm/tests/test_spec.py @@ -117,6 +117,7 @@ def test_spec_octopus(spec_json): ] j_c.pop('objectstore', None) j_c.pop('filter_logic', None) + j_c.pop('anonymous_access', None) return j_c assert spec_json == convert_to_old_style_json(spec.to_json()) diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index a94704d8228..9773b6facc0 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -1292,6 +1292,7 @@ class GrafanaSpec(MonitoringSpec): port: Optional[int] = None, protocol: Optional[str] = 'https', initial_admin_password: Optional[str] = None, + anonymous_access: Optional[bool] = True, extra_container_args: Optional[List[str]] = None, extra_entrypoint_args: Optional[List[str]] = None, custom_configs: Optional[List[CustomConfig]] = None, @@ -1305,6 +1306,7 @@ class GrafanaSpec(MonitoringSpec): custom_configs=custom_configs) self.initial_admin_password = initial_admin_password + self.anonymous_access = anonymous_access self.protocol = protocol def validate(self) -> None: @@ -1313,6 +1315,12 @@ class GrafanaSpec(MonitoringSpec): err_msg = f"Invalid protocol '{self.protocol}'. Valid values are: 'http', 'https'." raise SpecValidationError(err_msg) + if not self.anonymous_access and not self.initial_admin_password: + err_msg = ('Either initial_admin_password must be set or anonymous_access ' + 'must be set to true. Otherwise the grafana dashboard will ' + 'be inaccessible.') + raise SpecValidationError(err_msg) + yaml.add_representer(GrafanaSpec, ServiceSpec.yaml_representer) diff --git a/src/python-common/ceph/tests/test_service_spec.py b/src/python-common/ceph/tests/test_service_spec.py index 041cbbbd4ec..a0e0be2aa81 100644 --- a/src/python-common/ceph/tests/test_service_spec.py +++ b/src/python-common/ceph/tests/test_service_spec.py @@ -53,6 +53,8 @@ def test_parse_host_placement_specs(test_input, expected, require_network): (GrafanaSpec(protocol='-https'), True, '^Invalid protocol'), (GrafanaSpec(protocol='http'), False, ''), (GrafanaSpec(protocol='https'), False, ''), + (GrafanaSpec(anonymous_access=False), True, '^Either initial'), # we require inital_admin_password if anonymous_access is False + (GrafanaSpec(anonymous_access=False, initial_admin_password='test'), False, ''), ]) def test_apply_grafana(spec: GrafanaSpec, raise_exception: bool, msg: str): if raise_exception: @@ -341,12 +343,14 @@ spec: service_type: grafana service_name: grafana spec: + anonymous_access: true port: 1234 protocol: https --- service_type: grafana service_name: grafana spec: + anonymous_access: true initial_admin_password: secure port: 1234 protocol: https -- 2.39.5