summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam King <47704447+adk3798@users.noreply.github.com>2024-10-24 16:45:42 +0200
committerGitHub <noreply@github.com>2024-10-24 16:45:42 +0200
commitbd0160de81e216e42d835a3d4ce920c3bef81b16 (patch)
tree9fe5f6fc52413ef7b8bc43eb8c3419ff5ad56c1b
parentMerge pull request #58898 from neesingh-rh/wip-fix-strict-iec-cast (diff)
parentdoc/cephadm: adding documentation for mgmt-gateway HA setup (diff)
downloadceph-bd0160de81e216e42d835a3d4ce920c3bef81b16.tar.xz
ceph-bd0160de81e216e42d835a3d4ce920c3bef81b16.zip
Merge pull request #59982 from rkachach/fix_issue_mgmt_gw_high_availability
Adding HA support for mgmt-gateway and oauth2-proxy services Reviewed-by: Adam king <adking@redhat.com> Reviewed-by: Anthony D'Atri <anthonyeleven@users.noreply.github.com> Reviewed-by: Juan Miguel Olmo Martínez <jolmomar@redhat.com>
-rw-r--r--doc/cephadm/services/mgmt-gateway.rst52
-rw-r--r--doc/cephadm/services/oauth2-proxy.rst9
-rw-r--r--src/pybind/mgr/cephadm/module.py82
-rw-r--r--src/pybind/mgr/cephadm/services/ingress.py11
-rw-r--r--src/pybind/mgr/cephadm/services/mgmt_gateway.py66
-rw-r--r--src/pybind/mgr/cephadm/services/monitoring.py55
-rw-r--r--src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j22
-rw-r--r--src/pybind/mgr/cephadm/templates/services/mgmt-gateway/external_server.conf.j216
-rw-r--r--src/pybind/mgr/cephadm/templates/services/mgmt-gateway/internal_server.conf.j210
-rw-r--r--src/pybind/mgr/cephadm/templates/services/mgmt-gateway/nginx.conf.j217
-rw-r--r--src/pybind/mgr/cephadm/tests/test_services.py84
-rw-r--r--src/pybind/mgr/cephadm/utils.py7
-rw-r--r--src/python-common/ceph/deployment/service_spec.py4
13 files changed, 303 insertions, 112 deletions
diff --git a/doc/cephadm/services/mgmt-gateway.rst b/doc/cephadm/services/mgmt-gateway.rst
index 60129b28621..2b88d55952e 100644
--- a/doc/cephadm/services/mgmt-gateway.rst
+++ b/doc/cephadm/services/mgmt-gateway.rst
@@ -49,6 +49,55 @@ monitoring `mgmt-gateway` takes care of handling HA when several instances of Pr
available. The reverse proxy will automatically detect healthy instances and use them to process user requests.
+High Availability for mgmt-gateway service
+==========================================
+
+In addition to providing high availability for the underlying backend services, the mgmt-gateway
+service itself can be configured for high availability, ensuring that the system remains resilient
+even if certain core components for the service fail.
+
+Multiple mgmt-gateway instances can be deployed in an active/standby configuration using keepalived
+for seamless failover. The `oauth2-proxy` service can be deployed as multiple stateless instances,
+with nginx acting as a load balancer across them using round-robin strategy. This setup removes
+single points of failure and enhances the resilience of the entire system.
+
+In this setup, the underlying internal services follow the same high availability mechanism. Instead of
+directly accessing the `mgmt-gateway` internal endpoint, services use the virtual IP specified in the spec.
+This ensures that the high availability mechanism for `mgmt-gateway` is transparent to other services.
+
+Example Configuration for High Availability
+
+To deploy the mgmt-gateway in a high availability setup, here is an example of the specification files required:
+
+`mgmt-gateway` Configuration:
+
+.. code-block:: yaml
+
+ service_type: mgmt-gateway
+ placement:
+ label: mgmt
+ spec:
+ enable_auth: true
+ virtual_ip: 192.168.100.220
+
+`Ingress` Configuration for Keepalived:
+
+.. code-block:: yaml
+
+ service_type: ingress
+ service_id: ingress-mgmt-gw
+ placement:
+ label: mgmt
+ virtual_ip: 192.168.100.220
+ backend_service: mgmt-gateway
+ keepalive_only: true
+
+The number of deployed instances is determined by the number of hosts with the mgmt label.
+The ingress is configured in `keepalive_only` mode, with labels ensuring that any changes to
+the mgmt-gateway daemons are replicated to the corresponding keepalived instances. Additionally,
+the `virtual_ip` parameter must be identical in both specifications.
+
+
Accessing services with mgmt-gateway
====================================
@@ -123,9 +172,6 @@ The specification can then be applied by running the following command:
Limitations
===========
-A non-exhaustive list of important limitations for the mgmt-gateway service follows:
-
-* High-availability configurations and clustering for the mgmt-gateway service itself are currently not supported.
* Services must bind to the appropriate ports based on the applications being proxied. Ensure that there
are no port conflicts that might disrupt service availability.
diff --git a/doc/cephadm/services/oauth2-proxy.rst b/doc/cephadm/services/oauth2-proxy.rst
index d1afb515ca2..a941b11e555 100644
--- a/doc/cephadm/services/oauth2-proxy.rst
+++ b/doc/cephadm/services/oauth2-proxy.rst
@@ -42,8 +42,10 @@ a secure and flexible authentication mechanism.
High availability
==============================
-`oauth2-proxy` is designed to integrate with an external IDP hence login high availability is not the responsibility of this
-service. In squid release high availability for the service itself is not supported yet.
+In general, `oauth2-proxy` is used in conjunction with the `mgmt-gateway`. The `oauth2-proxy` service can be deployed as multiple
+stateless instances, with the `mgmt-gateway` (nginx reverse-proxy) handling load balancing across these instances using a round-robin strategy.
+Since oauth2-proxy integrates with an external identity provider (IDP), ensuring high availability for login is managed externally
+and not the responsibility of this service.
Accessing services with oauth2-proxy
@@ -70,8 +72,7 @@ An `oauth2-proxy` service can be applied using a specification. An example in YA
service_type: oauth2-proxy
service_id: auth-proxy
placement:
- hosts:
- - ceph0
+ label: mgmt
spec:
https_address: "0.0.0.0:4180"
provider_display_name: "My OIDC Provider"
diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py
index 7bf65b532fa..1acc2ad2f2d 100644
--- a/src/pybind/mgr/cephadm/module.py
+++ b/src/pybind/mgr/cephadm/module.py
@@ -822,30 +822,33 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule,
security_enabled = self.secure_monitoring_stack or mgmt_gw_enabled
return security_enabled, mgmt_gw_enabled, oauth2_proxy_enabled
- def get_mgmt_gw_internal_endpoint(self) -> Optional[str]:
+ def _get_mgmt_gw_endpoint(self, is_internal: bool) -> Optional[str]:
mgmt_gw_daemons = self.cache.get_daemons_by_service('mgmt-gateway')
if not mgmt_gw_daemons:
return None
dd = mgmt_gw_daemons[0]
assert dd.hostname is not None
- mgmt_gw_addr = self.get_fqdn(dd.hostname)
- mgmt_gw_internal_endpoint = build_url(scheme='https', host=mgmt_gw_addr, port=MgmtGatewayService.INTERNAL_SERVICE_PORT)
- return f'{mgmt_gw_internal_endpoint}/internal'
+ mgmt_gw_spec = cast(MgmtGatewaySpec, self.spec_store['mgmt-gateway'].spec)
+ mgmt_gw_addr = mgmt_gw_spec.virtual_ip if mgmt_gw_spec.virtual_ip is not None else self.get_fqdn(dd.hostname)
- def get_mgmt_gw_external_endpoint(self) -> Optional[str]:
- mgmt_gw_daemons = self.cache.get_daemons_by_service('mgmt-gateway')
- if not mgmt_gw_daemons:
- return None
+ if is_internal:
+ mgmt_gw_port: Optional[int] = MgmtGatewayService.INTERNAL_SERVICE_PORT
+ protocol = 'https'
+ endpoint_suffix = '/internal'
+ else:
+ mgmt_gw_port = dd.ports[0] if dd.ports else None
+ protocol = 'http' if mgmt_gw_spec.disable_https else 'https'
+ endpoint_suffix = ''
- dd = mgmt_gw_daemons[0]
- assert dd.hostname is not None
- mgmt_gw_port = dd.ports[0] if dd.ports else None
- mgmt_gw_addr = self.get_fqdn(dd.hostname)
- mgmt_gw_spec = cast(MgmtGatewaySpec, self.spec_store['mgmt-gateway'].spec)
- protocol = 'http' if mgmt_gw_spec.disable_https else 'https'
- mgmt_gw_external_endpoint = build_url(scheme=protocol, host=mgmt_gw_addr, port=mgmt_gw_port)
- return mgmt_gw_external_endpoint
+ mgmt_gw_endpoint = build_url(scheme=protocol, host=mgmt_gw_addr, port=mgmt_gw_port)
+ return f'{mgmt_gw_endpoint}{endpoint_suffix}'
+
+ def get_mgmt_gw_internal_endpoint(self) -> Optional[str]:
+ return self._get_mgmt_gw_endpoint(is_internal=True)
+
+ def get_mgmt_gw_external_endpoint(self) -> Optional[str]:
+ return self._get_mgmt_gw_endpoint(is_internal=False)
def _get_cephadm_binary_path(self) -> str:
import hashlib
@@ -3004,8 +3007,16 @@ Then run the following:
daemon_names.append(dd.name())
return daemon_names
- alertmanager_user, alertmanager_password = self._get_alertmanager_credentials()
- prometheus_user, prometheus_password = self._get_prometheus_credentials()
+ prom_cred_hash = None
+ alertmgr_cred_hash = None
+ security_enabled, mgmt_gw_enabled, _ = self._get_security_config()
+ if security_enabled:
+ alertmanager_user, alertmanager_password = self._get_alertmanager_credentials()
+ prometheus_user, prometheus_password = self._get_prometheus_credentials()
+ if prometheus_user and prometheus_password:
+ prom_cred_hash = f'{utils.md5_hash(prometheus_user + prometheus_password)}'
+ if alertmanager_user and alertmanager_password:
+ alertmgr_cred_hash = f'{utils.md5_hash(alertmanager_user + alertmanager_password)}'
deps = []
if daemon_type == 'haproxy':
@@ -3052,9 +3063,10 @@ Then run the following:
else:
deps = [self.get_mgr_ip()]
elif daemon_type == 'prometheus':
- # for prometheus we add the active mgr as an explicit dependency,
- # this way we force a redeploy after a mgr failover
- deps.append(self.get_active_mgr().name())
+ if not mgmt_gw_enabled:
+ # for prometheus we add the active mgr as an explicit dependency,
+ # this way we force a redeploy after a mgr failover
+ deps.append(self.get_active_mgr().name())
deps.append(str(self.get_module_option_ex('prometheus', 'server_port', 9283)))
deps.append(str(self.service_discovery_port))
# prometheus yaml configuration file (generated by prometheus.yml.j2) contains
@@ -3071,22 +3083,20 @@ Then run the following:
deps += [d.name() for d in self.cache.get_daemons_by_service('ceph-exporter')]
deps += [d.name() for d in self.cache.get_daemons_by_service('mgmt-gateway')]
deps += [d.name() for d in self.cache.get_daemons_by_service('oauth2-proxy')]
- security_enabled, _, _ = self._get_security_config()
- if security_enabled:
- if prometheus_user and prometheus_password:
- deps.append(f'{hash(prometheus_user + prometheus_password)}')
- if alertmanager_user and alertmanager_password:
- deps.append(f'{hash(alertmanager_user + alertmanager_password)}')
+ if prom_cred_hash is not None:
+ deps.append(prom_cred_hash)
+ if alertmgr_cred_hash is not None:
+ deps.append(alertmgr_cred_hash)
elif daemon_type == 'grafana':
deps += get_daemon_names(['prometheus', 'loki', 'mgmt-gateway', 'oauth2-proxy'])
- security_enabled, _, _ = self._get_security_config()
- if security_enabled and prometheus_user and prometheus_password:
- deps.append(f'{hash(prometheus_user + prometheus_password)}')
+ if prom_cred_hash is not None:
+ deps.append(prom_cred_hash)
elif daemon_type == 'alertmanager':
- deps += get_daemon_names(['mgr', 'alertmanager', 'snmp-gateway', 'mgmt-gateway', 'oauth2-proxy'])
- security_enabled, _, _ = self._get_security_config()
- if security_enabled and alertmanager_user and alertmanager_password:
- deps.append(f'{hash(alertmanager_user + alertmanager_password)}')
+ deps += get_daemon_names(['alertmanager', 'snmp-gateway', 'mgmt-gateway', 'oauth2-proxy'])
+ if not mgmt_gw_enabled:
+ deps += get_daemon_names(['mgr'])
+ if alertmgr_cred_hash is not None:
+ deps.append(alertmgr_cred_hash)
elif daemon_type == 'promtail':
deps += get_daemon_names(['loki'])
elif daemon_type in ['ceph-exporter', 'node-exporter']:
@@ -3098,9 +3108,7 @@ Then run the following:
deps.append(build_url(host=dd.hostname, port=port).lstrip('/'))
deps = sorted(deps)
elif daemon_type == 'mgmt-gateway':
- # url_prefix for monitoring daemons depends on the presence of mgmt-gateway
- # while dashboard urls depend on the mgr daemons
- deps += get_daemon_names(['mgr', 'grafana', 'prometheus', 'alertmanager', 'oauth2-proxy'])
+ deps = MgmtGatewayService.get_dependencies(self)
else:
# this daemon type doesn't need deps mgmt
pass
diff --git a/src/pybind/mgr/cephadm/services/ingress.py b/src/pybind/mgr/cephadm/services/ingress.py
index a17000cd632..7381ef67d7e 100644
--- a/src/pybind/mgr/cephadm/services/ingress.py
+++ b/src/pybind/mgr/cephadm/services/ingress.py
@@ -241,7 +241,12 @@ class IngressService(CephService):
if spec.keepalived_password:
password = spec.keepalived_password
- daemons = self.mgr.cache.get_daemons_by_service(spec.service_name())
+ if spec.keepalive_only:
+ # when keepalive_only instead of haproxy, we have to monitor the backend service daemons
+ if spec.backend_service is not None:
+ daemons = self.mgr.cache.get_daemons_by_service(spec.backend_service)
+ else:
+ daemons = self.mgr.cache.get_daemons_by_service(spec.service_name())
if not daemons and not spec.keepalive_only:
raise OrchestratorError(
@@ -297,6 +302,10 @@ class IngressService(CephService):
port = d.ports[1] # monitoring port
host_ip = d.ip or self.mgr.inventory.get_addr(d.hostname)
script = f'/usr/bin/curl {build_url(scheme="http", host=host_ip, port=port)}/health'
+ elif d.daemon_type == 'mgmt-gateway':
+ mgmt_gw_port = d.ports[0] if d.ports else None
+ host_ip = d.ip or self.mgr.inventory.get_addr(d.hostname)
+ script = f'/usr/bin/curl -k {build_url(scheme="https", host=host_ip, port=mgmt_gw_port)}/health'
assert script
states = []
diff --git a/src/pybind/mgr/cephadm/services/mgmt_gateway.py b/src/pybind/mgr/cephadm/services/mgmt_gateway.py
index 1943264025e..0897ce99ff7 100644
--- a/src/pybind/mgr/cephadm/services/mgmt_gateway.py
+++ b/src/pybind/mgr/cephadm/services/mgmt_gateway.py
@@ -1,10 +1,12 @@
import logging
-from typing import List, Any, Tuple, Dict, cast, Optional
+from typing import List, Any, Tuple, Dict, cast, TYPE_CHECKING
from orchestrator import DaemonDescription
from ceph.deployment.service_spec import MgmtGatewaySpec, GrafanaSpec
from cephadm.services.cephadmservice import CephadmService, CephadmDaemonDeploySpec, get_dashboard_endpoints
+if TYPE_CHECKING:
+ from ..module import CephadmOrchestrator
logger = logging.getLogger(__name__)
@@ -36,10 +38,11 @@ class MgmtGatewayService(CephadmService):
# if empty list provided, return empty Daemon Desc
return DaemonDescription()
- def get_oauth2_service_url(self) -> Optional[str]:
- # TODO(redo): check how can we create several servers for HA
- oauth2_servers = self.get_service_endpoints('oauth2-proxy')
- return f'https://{oauth2_servers[0]}' if oauth2_servers else None
+ def get_mgmt_gw_ips(self, svc_spec: MgmtGatewaySpec, daemon_spec: CephadmDaemonDeploySpec) -> List[str]:
+ mgmt_gw_ips = [self.mgr.inventory.get_addr(daemon_spec.host)]
+ if svc_spec.virtual_ip is not None:
+ mgmt_gw_ips.append(svc_spec.virtual_ip)
+ return mgmt_gw_ips
def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None:
# we adjust the standby behaviour so rev-proxy can pick correctly the active instance
@@ -56,9 +59,9 @@ class MgmtGatewayService(CephadmService):
key = svc_spec.ssl_certificate_key
else:
# not provided on the spec, let's generate self-sigend certificates
- addr = self.mgr.inventory.get_addr(daemon_spec.host)
+ ips = self.get_mgmt_gw_ips(svc_spec, daemon_spec)
host_fqdn = self.mgr.get_fqdn(daemon_spec.host)
- cert, key = self.mgr.cert_mgr.generate_cert(host_fqdn, addr)
+ cert, key = self.mgr.cert_mgr.generate_cert(host_fqdn, ips)
# save certificates
if cert and key:
self.mgr.cert_key_store.save_cert('mgmt_gw_cert', cert)
@@ -67,23 +70,33 @@ class MgmtGatewayService(CephadmService):
logger.error("Failed to obtain certificate and key from mgmt-gateway.")
return cert, key
- def get_internal_certificates(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[str, str]:
- node_ip = self.mgr.inventory.get_addr(daemon_spec.host)
+ def get_internal_certificates(self, svc_spec: MgmtGatewaySpec, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[str, str]:
+ ips = self.get_mgmt_gw_ips(svc_spec, daemon_spec)
host_fqdn = self.mgr.get_fqdn(daemon_spec.host)
- return self.mgr.cert_mgr.generate_cert(host_fqdn, node_ip)
+ return self.mgr.cert_mgr.generate_cert(host_fqdn, ips)
- def get_mgmt_gateway_deps(self) -> List[str]:
- # url_prefix for the following services depends on the presence of mgmt-gateway
- deps: List[str] = []
- deps += [d.name() for d in self.mgr.cache.get_daemons_by_service('prometheus')]
- deps += [d.name() for d in self.mgr.cache.get_daemons_by_service('alertmanager')]
- deps += [d.name() for d in self.mgr.cache.get_daemons_by_service('grafana')]
- deps += [d.name() for d in self.mgr.cache.get_daemons_by_service('oauth2-proxy')]
+ def get_service_discovery_endpoints(self) -> List[str]:
+ sd_endpoints = []
for dd in self.mgr.cache.get_daemons_by_service('mgr'):
- # we consider mgr a dep even if the dashboard is disabled
- # in order to be consistent with _calc_daemon_deps().
- deps.append(dd.name())
+ assert dd.hostname is not None
+ addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname)
+ sd_endpoints.append(f"{addr}:{self.mgr.service_discovery_port}")
+ return sd_endpoints
+ @staticmethod
+ def get_dependencies(mgr: "CephadmOrchestrator") -> List[str]:
+ # url_prefix for the following services depends on the presence of mgmt-gateway
+ deps = [
+ f'{d.name()}:{d.ports[0]}' if d.ports else d.name()
+ for service in ['prometheus', 'alertmanager', 'grafana', 'oauth2-proxy']
+ for d in mgr.cache.get_daemons_by_service(service)
+ ]
+ # dashboard and service discovery urls depend on the mgr daemons
+ deps += [
+ f'{d.name()}'
+ for service in ['mgr']
+ for d in mgr.cache.get_daemons_by_service(service)
+ ]
return deps
def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]:
@@ -94,6 +107,8 @@ class MgmtGatewayService(CephadmService):
prometheus_endpoints = self.get_service_endpoints('prometheus')
alertmanager_endpoints = self.get_service_endpoints('alertmanager')
grafana_endpoints = self.get_service_endpoints('grafana')
+ oauth2_proxy_endpoints = self.get_service_endpoints('oauth2-proxy')
+ service_discovery_endpoints = self.get_service_discovery_endpoints()
try:
grafana_spec = cast(GrafanaSpec, self.mgr.spec_store['grafana'].spec)
grafana_protocol = grafana_spec.protocol
@@ -104,7 +119,9 @@ class MgmtGatewayService(CephadmService):
'dashboard_endpoints': dashboard_endpoints,
'prometheus_endpoints': prometheus_endpoints,
'alertmanager_endpoints': alertmanager_endpoints,
- 'grafana_endpoints': grafana_endpoints
+ 'grafana_endpoints': grafana_endpoints,
+ 'oauth2_proxy_endpoints': oauth2_proxy_endpoints,
+ 'service_discovery_endpoints': service_discovery_endpoints
}
server_context = {
'spec': svc_spec,
@@ -117,11 +134,12 @@ class MgmtGatewayService(CephadmService):
'prometheus_endpoints': prometheus_endpoints,
'alertmanager_endpoints': alertmanager_endpoints,
'grafana_endpoints': grafana_endpoints,
- 'oauth2_proxy_url': self.get_oauth2_service_url(),
+ 'service_discovery_endpoints': service_discovery_endpoints,
+ 'enable_oauth2_proxy': bool(oauth2_proxy_endpoints),
}
cert, key = self.get_external_certificates(svc_spec, daemon_spec)
- internal_cert, internal_pkey = self.get_internal_certificates(daemon_spec)
+ internal_cert, internal_pkey = self.get_internal_certificates(svc_spec, daemon_spec)
daemon_config = {
"files": {
"nginx.conf": self.mgr.template.render(self.SVC_TEMPLATE_PATH, main_context),
@@ -136,7 +154,7 @@ class MgmtGatewayService(CephadmService):
daemon_config["files"]["nginx.crt"] = cert
daemon_config["files"]["nginx.key"] = key
- return daemon_config, sorted(self.get_mgmt_gateway_deps())
+ return daemon_config, sorted(MgmtGatewayService.get_dependencies(self.mgr))
def pre_remove(self, daemon: DaemonDescription) -> None:
"""
diff --git a/src/pybind/mgr/cephadm/services/monitoring.py b/src/pybind/mgr/cephadm/services/monitoring.py
index 6a57e3b31ef..1b9cf618570 100644
--- a/src/pybind/mgr/cephadm/services/monitoring.py
+++ b/src/pybind/mgr/cephadm/services/monitoring.py
@@ -8,10 +8,11 @@ from mgr_module import HandleCommandResult
from orchestrator import DaemonDescription
from ceph.deployment.service_spec import AlertManagerSpec, GrafanaSpec, ServiceSpec, \
- SNMPGatewaySpec, PrometheusSpec
+ SNMPGatewaySpec, PrometheusSpec, MgmtGatewaySpec
from cephadm.services.cephadmservice import CephadmService, CephadmDaemonDeploySpec, get_dashboard_urls
from mgr_util import verify_tls, ServerConfigException, build_url, get_cert_issuer_info, password_hash
from ceph.deployment.utils import wrap_ipv6
+from .. import utils
logger = logging.getLogger(__name__)
@@ -57,15 +58,17 @@ class GrafanaService(CephadmService):
daemon_spec.port_ips = {str(grafana_port): ip_to_bind_to}
grafana_ip = ip_to_bind_to
- mgmt_gw_ip = None
domain = self.mgr.get_fqdn(daemon_spec.host)
+ mgmt_gw_ips = []
if mgmt_gw_enabled:
mgmt_gw_daemons = self.mgr.cache.get_daemons_by_service('mgmt-gateway')
if mgmt_gw_daemons:
dd = mgmt_gw_daemons[0]
assert dd.hostname
- domain = self.mgr.get_fqdn(dd.hostname)
- mgmt_gw_ip = self.mgr.inventory.get_addr(dd.hostname)
+ mgmt_gw_spec = cast(MgmtGatewaySpec, self.mgr.spec_store['mgmt-gateway'].spec)
+ # TODO(redo): should we resolve the virtual_ip to a name if possible?
+ domain = mgmt_gw_spec.virtual_ip or self.mgr.get_fqdn(dd.hostname) # give prio to VIP if configured
+ mgmt_gw_ips = [self.mgr.inventory.get_addr(dd.hostname) for dd in mgmt_gw_daemons] # type: ignore
return self.mgr.template.render('services/grafana/grafana.ini.j2', {
'anonymous_access': spec.anonymous_access,
@@ -76,7 +79,7 @@ class GrafanaService(CephadmService):
'domain': domain,
'mgmt_gw_enabled': mgmt_gw_enabled,
'oauth2_enabled': oauth2_enabled,
- 'mgmt_gw_ip': mgmt_gw_ip,
+ 'mgmt_gw_ips': ','.join(mgmt_gw_ips),
})
def calculate_grafana_deps(self, security_enabled: bool) -> List[str]:
@@ -87,7 +90,7 @@ class GrafanaService(CephadmService):
# in case security is enabled we have to reconfig when prom user/pass changes
prometheus_user, prometheus_password = self.mgr._get_prometheus_credentials()
if security_enabled and prometheus_user and prometheus_password:
- deps.append(f'{hash(prometheus_user + prometheus_password)}')
+ deps.append(f'{utils.md5_hash(prometheus_user + prometheus_password)}')
# adding a dependency for mgmt-gateway because the usage of url_prefix relies on its presence.
# another dependency is added for oauth-proxy as Grafana login is delegated to this service when enabled.
@@ -311,17 +314,18 @@ class AlertmanagerService(CephadmService):
# add a dependency since enbling basic-auth (or not) depends on the existence of 'oauth2-proxy'
deps += [d.name() for d in self.mgr.cache.get_daemons_by_service('oauth2-proxy')]
- # scan all mgrs to generate deps and to get standbys too.
- for dd in self.mgr.cache.get_daemons_by_service('mgr'):
- # we consider mgr a dep even if the dashboard is disabled
- # in order to be consistent with _calc_daemon_deps().
- deps.append(dd.name())
-
security_enabled, mgmt_gw_enabled, oauth2_enabled = self.mgr._get_security_config()
if mgmt_gw_enabled:
dashboard_urls = [f'{self.mgr.get_mgmt_gw_internal_endpoint()}/dashboard']
else:
dashboard_urls = get_dashboard_urls(self)
+ # scan all mgrs to generate deps and to get standbys too.
+ for dd in self.mgr.cache.get_daemons_by_service('mgr'):
+ # we consider mgr a dep even if the dashboard is disabled
+ # in order to be consistent with _calc_daemon_deps().
+ # when mgmt_gw is enabled there's no need for mgr dep as
+ # mgmt-gw wil route to the active mgr automatically
+ deps.append(dd.name())
snmp_gateway_urls: List[str] = []
for dd in self.mgr.cache.get_daemons_by_service('snmp-gateway'):
@@ -354,7 +358,7 @@ class AlertmanagerService(CephadmService):
if security_enabled:
alertmanager_user, alertmanager_password = self.mgr._get_alertmanager_credentials()
if alertmanager_user and alertmanager_password:
- deps.append(f'{hash(alertmanager_user + alertmanager_password)}')
+ deps.append(f'{utils.md5_hash(alertmanager_user + alertmanager_password)}')
cert, key = self.get_alertmanager_certificates(daemon_spec)
context = {
'enable_mtls': mgmt_gw_enabled,
@@ -489,8 +493,14 @@ class PrometheusService(CephadmService):
security_enabled, mgmt_gw_enabled, oauth2_enabled = self.mgr._get_security_config()
port = self.mgr.service_discovery_port
mgr_addr = wrap_ipv6(self.mgr.get_mgr_ip())
+
protocol = 'https' if security_enabled else 'http'
- srv_end_point = f'{protocol}://{mgr_addr}:{port}/sd/prometheus/sd-config?'
+ self.mgr.get_mgmt_gw_internal_endpoint()
+ if mgmt_gw_enabled:
+ service_discovery_url_prefix = f'{self.mgr.get_mgmt_gw_internal_endpoint()}'
+ else:
+ service_discovery_url_prefix = f'{protocol}://{mgr_addr}:{port}'
+ srv_end_point = f'{service_discovery_url_prefix}/sd/prometheus/sd-config?'
node_exporter_cnt = len(self.mgr.cache.get_daemons_by_service('node-exporter'))
alertmgr_cnt = len(self.mgr.cache.get_daemons_by_service('alertmanager'))
@@ -617,18 +627,23 @@ class PrometheusService(CephadmService):
port = cast(int, self.mgr.get_module_option_ex('prometheus', 'server_port', self.DEFAULT_MGR_PROMETHEUS_PORT))
deps.append(str(port))
deps.append(str(self.mgr.service_discovery_port))
- # add an explicit dependency on the active manager. This will force to
- # re-deploy prometheus if the mgr has changed (due to a fail-over i.e).
- deps.append(self.mgr.get_active_mgr().name())
deps.append(f'secure_monitoring_stack:{self.mgr.secure_monitoring_stack}')
- security_enabled, _, _ = self.mgr._get_security_config()
+ security_enabled, mgmt_gw_enabled, _ = self.mgr._get_security_config()
+
+ if not mgmt_gw_enabled:
+ # add an explicit dependency on the active manager. This will force to
+ # re-deploy prometheus if the mgr has changed (due to a fail-over i.e).
+ # when mgmt_gw is enabled there's no need for such dep as mgmt-gw wil
+ # route to the active mgr automatically
+ deps.append(self.mgr.get_active_mgr().name())
+
if security_enabled:
alertmanager_user, alertmanager_password = self.mgr._get_alertmanager_credentials()
prometheus_user, prometheus_password = self.mgr._get_prometheus_credentials()
if prometheus_user and prometheus_password:
- deps.append(f'{hash(prometheus_user + prometheus_password)}')
+ deps.append(f'{utils.md5_hash(prometheus_user + prometheus_password)}')
if alertmanager_user and alertmanager_password:
- deps.append(f'{hash(alertmanager_user + alertmanager_password)}')
+ deps.append(f'{utils.md5_hash(alertmanager_user + alertmanager_password)}')
# add a dependency since url_prefix depends on the existence of mgmt-gateway
deps += [d.name() for d in self.mgr.cache.get_daemons_by_service('mgmt-gateway')]
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 967f1355af1..c767baddbb7 100644
--- a/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2
+++ b/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2
@@ -39,7 +39,7 @@
header_property = username
auto_sign_up = true
sync_ttl = 15
- whitelist = {{ mgmt_gw_ip }}
+ whitelist = {{ mgmt_gw_ips }}
headers_encoded = false
enable_login_token = false
headers = Role:X-WEBAUTH-ROLE
diff --git a/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/external_server.conf.j2 b/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/external_server.conf.j2
index 594582e7ee4..50a61f843d1 100644
--- a/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/external_server.conf.j2
+++ b/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/external_server.conf.j2
@@ -46,15 +46,15 @@ server {
# add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'; require-trusted-types-for 'script'; frame-ancestors 'self';";
{% endif %}
-{% if spec.enable_health_check_endpoint %}
+{% if spec.enable_health_check_endpoint or spec.virtual_ip %}
location /health {
return 200 'OK';
add_header Content-Type text/plain;
}
{% endif %}
-{% if oauth2_proxy_url %}
+{% if enable_oauth2_proxy %}
location /oauth2/ {
- proxy_pass {{ oauth2_proxy_url }};
+ proxy_pass https://oauth2_proxy_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
@@ -64,7 +64,7 @@ server {
location = /oauth2/auth {
internal;
- proxy_pass {{ oauth2_proxy_url }};
+ proxy_pass https://oauth2_proxy_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
@@ -78,7 +78,7 @@ server {
location / {
proxy_pass {{ dashboard_scheme }}://dashboard_servers;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
- {% if oauth2_proxy_url %}
+ {% if enable_oauth2_proxy %}
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
@@ -120,7 +120,7 @@ server {
# will send this header if Grafana is running on the same node as one of those services
proxy_set_header Authorization "";
proxy_buffering off;
- {% if oauth2_proxy_url %}
+ {% if enable_oauth2_proxy %}
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
@@ -156,7 +156,7 @@ server {
proxy_ssl_trusted_certificate /etc/nginx/ssl/ca.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
- {% if oauth2_proxy_url %}
+ {% if enable_oauth2_proxy %}
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
@@ -180,7 +180,7 @@ server {
proxy_ssl_trusted_certificate /etc/nginx/ssl/ca.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
- {% if oauth2_proxy_url %}
+ {% if enable_oauth2_proxy %}
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
diff --git a/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/internal_server.conf.j2 b/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/internal_server.conf.j2
index 9148ddc4a14..2abb24b2eba 100644
--- a/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/internal_server.conf.j2
+++ b/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/internal_server.conf.j2
@@ -12,12 +12,20 @@ server {
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
-{% if spec.enable_health_check_endpoint %}
+{% if spec.enable_health_check_endpoint or spec.virtual_ip %}
location /health {
return 200 'OK';
add_header Content-Type text/plain;
}
{% endif %}
+{% if service_discovery_endpoints %}
+ location /internal/sd {
+ rewrite ^/internal/(.*) /$1 break;
+ proxy_pass https://service_discovery_servers;
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ }
+{% endif %}
+
{% if dashboard_endpoints %}
location /internal/dashboard {
rewrite ^/internal/dashboard/(.*) /$1 break;
diff --git a/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/nginx.conf.j2 b/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/nginx.conf.j2
index 0c2a6b98c3b..b9773ceeeb3 100644
--- a/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/nginx.conf.j2
+++ b/src/pybind/mgr/cephadm/templates/services/mgmt-gateway/nginx.conf.j2
@@ -8,6 +8,7 @@ events {
http {
+ #access_log /dev/stdout;
client_header_buffer_size 32K;
large_client_header_buffers 4 32k;
proxy_busy_buffers_size 512k;
@@ -16,6 +17,22 @@ http {
proxy_headers_hash_max_size 1024;
proxy_headers_hash_bucket_size 128;
+{% if oauth2_proxy_endpoints %}
+ upstream oauth2_proxy_servers {
+ {% for ep in oauth2_proxy_endpoints %}
+ server {{ ep }};
+ {% endfor %}
+ }
+{% endif %}
+
+{% if service_discovery_endpoints %}
+ upstream service_discovery_servers {
+ {% for ep in service_discovery_endpoints %}
+ server {{ ep }};
+ {% endfor %}
+ }
+{% endif %}
+
{% if dashboard_endpoints %}
upstream dashboard_servers {
{% for ep in dashboard_endpoints %}
diff --git a/src/pybind/mgr/cephadm/tests/test_services.py b/src/pybind/mgr/cephadm/tests/test_services.py
index e82471c67ce..84d7c8f5b13 100644
--- a/src/pybind/mgr/cephadm/tests/test_services.py
+++ b/src/pybind/mgr/cephadm/tests/test_services.py
@@ -3771,14 +3771,19 @@ class TestSMB:
class TestMgmtGateway:
@patch("cephadm.serve.CephadmServe._run_cephadm")
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_service_endpoints")
+ @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_service_discovery_endpoints")
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_external_certificates",
lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_internal_certificates",
- lambda instance, dspec: (ceph_generated_cert, ceph_generated_key))
+ lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
@patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '::1')
@patch('cephadm.cert_mgr.CertMgr.get_root_ca', lambda instance: cephadm_root_ca)
@patch("cephadm.services.mgmt_gateway.get_dashboard_endpoints", lambda _: (["ceph-node-2:8443", "ceph-node-2:8443"], "https"))
- def test_mgmt_gw_config_no_auth(self, get_service_endpoints_mock: List[str], _run_cephadm, cephadm_module: CephadmOrchestrator):
+ def test_mgmt_gateway_config_no_auth(self,
+ get_service_discovery_endpoints_mock: List[str],
+ get_service_endpoints_mock: List[str],
+ _run_cephadm,
+ cephadm_module: CephadmOrchestrator):
def get_services_endpoints(name):
if name == 'prometheus':
@@ -3791,6 +3796,7 @@ class TestMgmtGateway:
_run_cephadm.side_effect = async_side_effect(('{}', '', 0))
get_service_endpoints_mock.side_effect = get_services_endpoints
+ get_service_discovery_endpoints_mock.side_effect = lambda: ["ceph-node-0:8765", "ceph-node-2:8765"]
server_port = 5555
spec = MgmtGatewaySpec(port=server_port,
@@ -3825,6 +3831,7 @@ class TestMgmtGateway:
http {
+ #access_log /dev/stdout;
client_header_buffer_size 32K;
large_client_header_buffers 4 32k;
proxy_busy_buffers_size 512k;
@@ -3833,6 +3840,12 @@ class TestMgmtGateway:
proxy_headers_hash_max_size 1024;
proxy_headers_hash_bucket_size 128;
+
+ upstream service_discovery_servers {
+ server ceph-node-0:8765;
+ server ceph-node-2:8765;
+ }
+
upstream dashboard_servers {
server ceph-node-2:8443;
server ceph-node-2:8443;
@@ -3940,6 +3953,12 @@ class TestMgmtGateway:
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
+ location /internal/sd {
+ rewrite ^/internal/(.*) /$1 break;
+ proxy_pass https://service_discovery_servers;
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ }
+
location /internal/dashboard {
rewrite ^/internal/dashboard/(.*) /$1 break;
proxy_pass https://dashboard_servers;
@@ -3995,15 +4014,19 @@ class TestMgmtGateway:
@patch("cephadm.serve.CephadmServe._run_cephadm")
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_service_endpoints")
+ @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_service_discovery_endpoints")
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_external_certificates",
lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_internal_certificates",
- lambda instance, dspec: (ceph_generated_cert, ceph_generated_key))
+ lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
@patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '::1')
@patch('cephadm.cert_mgr.CertMgr.get_root_ca', lambda instance: cephadm_root_ca)
@patch("cephadm.services.mgmt_gateway.get_dashboard_endpoints", lambda _: (["ceph-node-2:8443", "ceph-node-2:8443"], "https"))
- @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_oauth2_service_url", lambda _: "https://192.168.100.102:4180")
- def test_mgmt_gw_config_with_auth(self, get_service_endpoints_mock: List[str], _run_cephadm, cephadm_module: CephadmOrchestrator):
+ def test_mgmt_gateway_config_with_auth(self,
+ get_service_discovery_endpoints_mock: List[str],
+ get_service_endpoints_mock: List[str],
+ _run_cephadm,
+ cephadm_module: CephadmOrchestrator):
def get_services_endpoints(name):
if name == 'prometheus':
@@ -4012,10 +4035,13 @@ class TestMgmtGateway:
return ["ceph-node-2:3000", "ceph-node-2:3000"]
elif name == 'alertmanager':
return ["192.168.100.100:9093", "192.168.100.102:9093"]
+ elif name == 'oauth2-proxy':
+ return ["192.168.100.101:4180", "192.168.100.102:4180"]
return []
_run_cephadm.side_effect = async_side_effect(('{}', '', 0))
get_service_endpoints_mock.side_effect = get_services_endpoints
+ get_service_discovery_endpoints_mock.side_effect = lambda: ["ceph-node-0:8765", "ceph-node-2:8765"]
server_port = 5555
spec = MgmtGatewaySpec(port=server_port,
@@ -4051,6 +4077,7 @@ class TestMgmtGateway:
http {
+ #access_log /dev/stdout;
client_header_buffer_size 32K;
large_client_header_buffers 4 32k;
proxy_busy_buffers_size 512k;
@@ -4059,6 +4086,16 @@ class TestMgmtGateway:
proxy_headers_hash_max_size 1024;
proxy_headers_hash_bucket_size 128;
+ upstream oauth2_proxy_servers {
+ server 192.168.100.101:4180;
+ server 192.168.100.102:4180;
+ }
+
+ upstream service_discovery_servers {
+ server ceph-node-0:8765;
+ server ceph-node-2:8765;
+ }
+
upstream dashboard_servers {
server ceph-node-2:8443;
server ceph-node-2:8443;
@@ -4119,7 +4156,7 @@ class TestMgmtGateway:
# add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'; require-trusted-types-for 'script'; frame-ancestors 'self';";
location /oauth2/ {
- proxy_pass https://192.168.100.102:4180;
+ proxy_pass https://oauth2_proxy_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
@@ -4129,7 +4166,7 @@ class TestMgmtGateway:
location = /oauth2/auth {
internal;
- proxy_pass https://192.168.100.102:4180;
+ proxy_pass https://oauth2_proxy_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
@@ -4257,6 +4294,12 @@ class TestMgmtGateway:
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
+ location /internal/sd {
+ rewrite ^/internal/(.*) /$1 break;
+ proxy_pass https://service_discovery_servers;
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ }
+
location /internal/dashboard {
rewrite ^/internal/dashboard/(.*) /$1 break;
proxy_pass https://dashboard_servers;
@@ -4315,12 +4358,26 @@ class TestMgmtGateway:
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_external_certificates",
lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
@patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_internal_certificates",
- lambda instance, dspec: (ceph_generated_cert, ceph_generated_key))
+ lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
@patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '::1')
@patch('cephadm.cert_mgr.CertMgr.get_root_ca', lambda instance: cephadm_root_ca)
@patch("cephadm.services.mgmt_gateway.get_dashboard_endpoints", lambda _: (["ceph-node-2:8443", "ceph-node-2:8443"], "https"))
- def test_oauth2_proxy_service(self, get_service_endpoints_mock: List[str], _run_cephadm, cephadm_module: CephadmOrchestrator):
+ def test_oauth2_proxy_service(self, get_service_endpoints_mock, _run_cephadm, cephadm_module):
+ self.oauth2_proxy_service_common(get_service_endpoints_mock, _run_cephadm, cephadm_module, virtual_ip=None)
+ @patch("cephadm.serve.CephadmServe._run_cephadm")
+ @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_service_endpoints")
+ @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_external_certificates",
+ lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
+ @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_internal_certificates",
+ lambda instance, svc_spec, dspec: (ceph_generated_cert, ceph_generated_key))
+ @patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '::1')
+ @patch('cephadm.cert_mgr.CertMgr.get_root_ca', lambda instance: cephadm_root_ca)
+ @patch("cephadm.services.mgmt_gateway.get_dashboard_endpoints", lambda _: (["ceph-node-2:8443", "ceph-node-2:8443"], "https"))
+ def test_oauth2_proxy_service_with_ha(self, get_service_endpoints_mock, _run_cephadm, cephadm_module):
+ self.oauth2_proxy_service_common(get_service_endpoints_mock, _run_cephadm, cephadm_module, virtual_ip="192.168.100.200")
+
+ def oauth2_proxy_service_common(self, get_service_endpoints_mock, _run_cephadm, cephadm_module: CephadmOrchestrator, virtual_ip=None):
def get_services_endpoints(name):
if name == 'prometheus':
return ["192.168.100.100:9095", "192.168.100.101:9095"]
@@ -4337,7 +4394,8 @@ class TestMgmtGateway:
mgmt_gw_spec = MgmtGatewaySpec(port=server_port,
ssl_certificate=ceph_generated_cert,
ssl_certificate_key=ceph_generated_key,
- enable_auth=True)
+ enable_auth=True,
+ virtual_ip=virtual_ip)
oauth2_spec = OAuth2ProxySpec(provider_display_name='my_idp_provider',
client_id='my_client_id',
@@ -4346,6 +4404,8 @@ class TestMgmtGateway:
cookie_secret='kbAEM9opAmuHskQvt0AW8oeJRaOM2BYy5Loba0kZ0SQ=',
ssl_certificate=ceph_generated_cert,
ssl_certificate_key=ceph_generated_key)
+
+ redirect_url = f"https://{virtual_ip if virtual_ip else 'host_fqdn'}:5555/oauth2/callback"
expected = {
"fsid": "fsid",
"name": "oauth2-proxy.ceph-node",
@@ -4364,7 +4424,7 @@ class TestMgmtGateway:
},
"config_blobs": {
"files": {
- "oauth2-proxy.conf": dedent("""
+ "oauth2-proxy.conf": dedent(f"""
# Listen on port 4180 for incoming HTTP traffic.
https_address= "0.0.0.0:4180"
@@ -4377,7 +4437,7 @@ class TestMgmtGateway:
client_id= "my_client_id"
client_secret= "my_client_secret"
oidc_issuer_url= "http://192.168.10.10:8888/dex"
- redirect_url= "https://host_fqdn:5555/oauth2/callback"
+ redirect_url= "{redirect_url}"
ssl_insecure_skip_verify=true
diff --git a/src/pybind/mgr/cephadm/utils.py b/src/pybind/mgr/cephadm/utils.py
index 3673fbf621c..edd775aa178 100644
--- a/src/pybind/mgr/cephadm/utils.py
+++ b/src/pybind/mgr/cephadm/utils.py
@@ -5,6 +5,7 @@ from enum import Enum
from functools import wraps
from typing import Optional, Callable, TypeVar, List, NewType, TYPE_CHECKING, Any, NamedTuple
from orchestrator import OrchestratorError
+import hashlib
if TYPE_CHECKING:
from cephadm import CephadmOrchestrator
@@ -154,3 +155,9 @@ def file_mode_to_str(mode: int) -> str:
f'{"x" if (mode >> shift) & 1 else "-"}'
) + r
return r
+
+
+def md5_hash(input_value: str) -> str:
+ input_str = str(input_value).encode('utf-8')
+ hash_object = hashlib.md5(input_str)
+ return hash_object.hexdigest()
diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py
index 7002cd58947..979c14f7d00 100644
--- a/src/python-common/ceph/deployment/service_spec.py
+++ b/src/python-common/ceph/deployment/service_spec.py
@@ -1787,7 +1787,7 @@ class IngressSpec(ServiceSpec):
if not self.keepalive_only and not self.frontend_port:
raise SpecValidationError(
'Cannot add ingress: No frontend_port specified')
- if not self.monitor_port:
+ if not self.keepalive_only and not self.monitor_port:
raise SpecValidationError(
'Cannot add ingress: No monitor_port specified')
if not self.virtual_ip and not self.virtual_ips_list:
@@ -1830,6 +1830,7 @@ class MgmtGatewaySpec(ServiceSpec):
ssl_protocols: Optional[List[str]] = None,
ssl_ciphers: Optional[List[str]] = None,
enable_health_check_endpoint: bool = False,
+ virtual_ip: Optional[str] = None,
preview_only: bool = False,
unmanaged: bool = False,
extra_container_args: Optional[GeneralArgList] = None,
@@ -1876,6 +1877,7 @@ class MgmtGatewaySpec(ServiceSpec):
#: List of supported secure SSL ciphers. Changing this list may reduce system security.
self.ssl_ciphers = ssl_ciphers
self.enable_health_check_endpoint = enable_health_check_endpoint
+ self.virtual_ip = virtual_ip
def get_port_start(self) -> List[int]:
ports = []