Skip to content

Commit 8be392f

Browse files
committed
mgr/cepahdm: adjusting grafana custom code to handle certificates
Signed-off-by: Redouane Kachach <[email protected]>
1 parent b008856 commit 8be392f

File tree

4 files changed

+35
-97
lines changed

4 files changed

+35
-97
lines changed

src/pybind/mgr/cephadm/serve.py

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import datetime
12
import ipaddress
23
import hashlib
34
import json
@@ -30,7 +31,7 @@
3031
from cephadm.utils import forall_hosts, cephadmNoImage, is_repo_digest, \
3132
CephadmNoImage, CEPH_TYPES, ContainerInspectInfo, SpecialHostLabels
3233
from mgr_module import MonCommandFailed
33-
from mgr_util import format_bytes, verify_tls, get_cert_issuer_info, ServerConfigException
34+
from mgr_util import format_bytes
3435
from cephadm.services.service_registry import service_registry
3536

3637
from . import utils
@@ -63,6 +64,7 @@ class CephadmServe:
6364
def __init__(self, mgr: "CephadmOrchestrator"):
6465
self.mgr: "CephadmOrchestrator" = mgr
6566
self.log = logger
67+
self.last_certificates_check: Optional[datetime] = None
6668

6769
def serve(self) -> None:
6870
"""
@@ -111,10 +113,7 @@ def serve(self) -> None:
111113

112114
self._check_daemons()
113115

114-
services_to_reconfig, _ = self.mgr.cert_mgr.check_services_certificates(fix_issues=True)
115-
for svc in services_to_reconfig:
116-
logger.info(f'certmgr: certificate has changed, reconfiguring service {svc}')
117-
self.mgr.service_action('reconfig', svc)
116+
self._check_certificates()
118117

119118
self._purge_deleted_services()
120119

@@ -142,39 +141,24 @@ def serve(self) -> None:
142141
self.log.debug("serve exit")
143142

144143
def _check_certificates(self) -> None:
145-
for d in self.mgr.cache.get_daemons_by_type('grafana'):
146-
host = d.hostname
147-
assert host is not None
148-
cert = self.mgr.cert_mgr.get_cert('grafana_cert', host=host)
149-
key = self.mgr.cert_mgr.get_key('grafana_key', host=host)
150-
if not cert or not key:
151-
# certificate/key are empty... nothing to check
152-
return
153-
154-
try:
155-
get_cert_issuer_info(cert)
156-
verify_tls(cert, key)
157-
self.mgr.remove_health_warning('CEPHADM_CERT_ERROR')
158-
except ServerConfigException as e:
159-
err_msg = f"""
160-
Detected invalid grafana certificates. Please, use the following commands:
161-
162-
> ceph config-key set mgr/cephadm/{d.hostname}/grafana_crt -i <path-to-ctr-file>
163-
> ceph config-key set mgr/cephadm/{d.hostname}/grafana_key -i <path-to-key-file>
164144

165-
to set valid key and certificate or reset their value to an empty string
166-
in case you want cephadm to generate self-signed Grafana certificates.
167-
168-
Once done, run the following command to reconfig the daemon:
169-
170-
> ceph orch daemon reconfig grafana.{d.hostname}
171-
172-
"""
173-
self.log.error(f'Detected invalid grafana certificate on host {d.hostname}: {e}')
174-
self.mgr.set_health_warning('CEPHADM_CERT_ERROR',
175-
f'Invalid grafana certificate on host {d.hostname}: {e}',
176-
1, [err_msg])
177-
break
145+
# Check certificates if:
146+
# - This is the first time (startup, last_certificates_check is None)
147+
# - Or the elapsed time is greater than or equal to the configured check period
148+
check_certificates = False
149+
if self.last_certificates_check is None:
150+
check_certificates = True
151+
else:
152+
elapsed_time = datetime_now() - self.last_certificates_check
153+
check_certificates = elapsed_time.days >= self.mgr.certificate_check_period
154+
155+
if check_certificates:
156+
self.log.debug('_check_certificates')
157+
self.last_certificates_check = datetime_now()
158+
services_to_reconfig, _ = self.mgr.cert_mgr.check_services_certificates(fix_issues=True)
159+
for svc in services_to_reconfig:
160+
logger.info(f'certmgr: certificate has changed, reconfiguring service {svc}')
161+
self.mgr.service_action('reconfig', svc)
178162

179163
def _serve_sleep(self) -> None:
180164
sleep_interval = max(

src/pybind/mgr/cephadm/services/monitoring.py

Lines changed: 13 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ceph.deployment.service_spec import AlertManagerSpec, GrafanaSpec, ServiceSpec, \
1515
SNMPGatewaySpec, PrometheusSpec, MgmtGatewaySpec
1616
from cephadm.services.cephadmservice import CephadmService, CephadmDaemonDeploySpec, get_dashboard_urls
17-
from mgr_util import verify_tls, ServerConfigException, build_url, get_cert_issuer_info, password_hash
17+
from mgr_util import build_url, password_hash
1818
from ceph.deployment.utils import wrap_ipv6
1919
from .. import utils
2020

@@ -143,13 +143,24 @@ def get_loki_host(self) -> str:
143143
def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]:
144144
assert self.TYPE == daemon_spec.daemon_type
145145

146-
cert, pkey = self.prepare_certificates(daemon_spec)
146+
host_fqdns = [socket.getfqdn(daemon_spec.host), 'grafana_servers']
147+
host_ips = self.mgr.inventory.get_addr(daemon_spec.host)
148+
cert, pkey = self.mgr.cert_mgr.prepare_certificate('grafana_cert', 'grafana_key', host_fqdns, host_ips, target_host=daemon_spec.host)
149+
if not cert or not pkey:
150+
logger.error(f'Cannot generate the needed certificates to deploy Grafana on {daemon_spec.host}')
151+
cert, pkey = ('', '') # this will lead to an error in the daemon as certificates are needed
152+
147153
security_enabled, mgmt_gw_enabled, oauth2_enabled = self.mgr._get_security_config()
148154
grafana_ini = self.generate_grafana_ini(daemon_spec, mgmt_gw_enabled, oauth2_enabled)
149155
grafana_data_sources = self.generate_data_sources(security_enabled, mgmt_gw_enabled, cert, pkey)
150156
# the path of the grafana dashboards are assumed from the providers.yml.j2 file by grafana
151157
grafana_dashboards_path = self.mgr.grafana_dashboards_path or '/etc/grafana/dashboards/ceph-dashboard/'
152158

159+
if 'dashboard' in self.mgr.get('mgr_map')['modules']:
160+
self.mgr.check_mon_command({
161+
'prefix': 'dashboard set-grafana-api-ssl-verify',
162+
'value': 'false'})
163+
153164
config_file = {
154165
'files': {
155166
"grafana.ini": grafana_ini,
@@ -178,61 +189,6 @@ def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[st
178189

179190
return config_file, self.get_dependencies(self.mgr)
180191

181-
def prepare_certificates(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[str, str]:
182-
cert = self.mgr.cert_mgr.get_cert('grafana_cert', host=daemon_spec.host)
183-
pkey = self.mgr.cert_mgr.get_key('grafana_key', host=daemon_spec.host)
184-
certs_present = (cert and pkey)
185-
is_valid_certificate = False
186-
(org, cn) = (None, None)
187-
if certs_present:
188-
try:
189-
(org, cn) = get_cert_issuer_info(cert)
190-
verify_tls(cert, pkey)
191-
is_valid_certificate = True
192-
except ServerConfigException as e:
193-
logger.warning(f'Provided grafana TLS certificates are invalid: {e}')
194-
195-
if is_valid_certificate:
196-
# let's clear health error just in case it was set
197-
self.mgr.remove_health_warning('CEPHADM_CERT_ERROR')
198-
return cert, pkey
199-
200-
# certificate is not valid, to avoid overwriting user generated
201-
# certificates we only re-generate in case of self signed certificates
202-
# that were originally generated by cephadm or in case cert/key are empty.
203-
if not certs_present or (org == 'Ceph' and cn == 'cephadm'):
204-
logger.info('Regenerating cephadm self-signed grafana TLS certificates')
205-
host_fqdn = socket.getfqdn(daemon_spec.host)
206-
node_ip = self.mgr.inventory.get_addr(daemon_spec.host)
207-
cert, pkey = self.mgr.cert_mgr.generate_cert([host_fqdn, "grafana_servers"], node_ip)
208-
# cert, pkey = create_self_signed_cert('Ceph', host_fqdn)
209-
self.mgr.cert_mgr.save_cert('grafana_cert', cert, host=daemon_spec.host)
210-
self.mgr.cert_mgr.save_key('grafana_key', pkey, host=daemon_spec.host)
211-
if 'dashboard' in self.mgr.get('mgr_map')['modules']:
212-
self.mgr.check_mon_command({
213-
'prefix': 'dashboard set-grafana-api-ssl-verify',
214-
'value': 'false',
215-
})
216-
self.mgr.remove_health_warning('CEPHADM_CERT_ERROR') # clear if any
217-
else:
218-
# the certificate was not generated by cephadm, we cannot overwrite
219-
# it by new self-signed ones. Let's warn the user to fix the issue
220-
err_msg = """
221-
Detected invalid grafana certificates. Set mgr/cephadm/grafana_crt
222-
and mgr/cephadm/grafana_key to valid certificates or reset their value
223-
to an empty string in case you want cephadm to generate self-signed Grafana
224-
certificates.
225-
226-
Once done, run the following command to reconfig the daemon:
227-
228-
> ceph orch daemon reconfig <grafana-daemon>
229-
230-
"""
231-
self.mgr.set_health_warning(
232-
'CEPHADM_CERT_ERROR', 'Invalid grafana certificate: ', 1, [err_msg])
233-
234-
return cert, pkey
235-
236192
def get_active_daemon(self, daemon_descrs: List[DaemonDescription]) -> DaemonDescription:
237193
# Use the least-created one as the active daemon
238194
if daemon_descrs:

src/pybind/mgr/cephadm/tests/test_services.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,6 @@ def test_promtail_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator
15321532
@patch("cephadm.serve.CephadmServe._run_cephadm")
15331533
@patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '1::4')
15341534
@patch("cephadm.module.CephadmOrchestrator.get_fqdn", lambda a, b: 'host_fqdn')
1535-
@patch("cephadm.services.monitoring.verify_tls", lambda *_: None)
15361535
@patch('cephadm.cert_mgr.CertMgr.get_root_ca', lambda instance: cephadm_root_ca)
15371536
def test_grafana_config_with_mgmt_gw_and_ouath2_proxy(self, _run_cephadm, cephadm_module: CephadmOrchestrator):
15381537
_run_cephadm.side_effect = async_side_effect(("{}", "", 0))
@@ -1693,7 +1692,6 @@ def inline_certificate(multi_line_cert):
16931692
@patch("cephadm.serve.CephadmServe._run_cephadm")
16941693
@patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '1::4')
16951694
@patch("cephadm.module.CephadmOrchestrator.get_fqdn", lambda a, b: 'host_fqdn')
1696-
@patch("cephadm.services.monitoring.verify_tls", lambda *_: None)
16971695
@patch('cephadm.cert_mgr.CertMgr.get_root_ca', lambda instance: cephadm_root_ca)
16981696
def test_grafana_config_with_mgmt_gw(self, _run_cephadm, cephadm_module: CephadmOrchestrator):
16991697
_run_cephadm.side_effect = async_side_effect(("{}", "", 0))
@@ -1834,7 +1832,6 @@ def inline_certificate(multi_line_cert):
18341832
@patch("cephadm.serve.CephadmServe._run_cephadm")
18351833
@patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '1::4')
18361834
@patch("cephadm.module.CephadmOrchestrator.get_fqdn", lambda a, b: 'host_fqdn')
1837-
@patch("cephadm.services.monitoring.verify_tls", lambda *_: None)
18381835
def test_grafana_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator):
18391836
_run_cephadm.side_effect = async_side_effect(("{}", "", 0))
18401837

src/pybind/mgr/mgr_util.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,7 @@ def password_hash(password: Optional[str], salt_password: Optional[str] = None)
991991
salt = salt_password.encode('utf8')
992992
return bcrypt.hashpw(password.encode('utf8'), salt).decode('utf8')
993993

994+
994995
def parse_combined_pem_file(pem_data: str) -> Tuple[Optional[str], Optional[str]]:
995996

996997
# Extract the certificate

0 commit comments

Comments
 (0)