Skip to content

Commit b047ff8

Browse files
authored
Merge pull request ceph#62215 from rkachach/fix_mgmt_gw_ha
mgr/cephadm: refactor oauth2-proxy certs and cookie-secret handling Reviewed-by: Adam King <[email protected]>
2 parents a4739fd + 8b0d16f commit b047ff8

File tree

5 files changed

+38
-27
lines changed

5 files changed

+38
-27
lines changed

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ def get_active_daemon(self, daemon_descrs: List[DaemonDescription]) -> DaemonDes
4040
# if empty list provided, return empty Daemon Desc
4141
return DaemonDescription()
4242

43-
def get_mgmt_gw_ips(self, svc_spec: MgmtGatewaySpec, daemon_spec: CephadmDaemonDeploySpec) -> List[str]:
44-
mgmt_gw_ips = [self.mgr.inventory.get_addr(daemon_spec.host)]
43+
def get_mgmt_gw_ip(self, svc_spec: MgmtGatewaySpec, daemon_spec: CephadmDaemonDeploySpec) -> str:
4544
if svc_spec.virtual_ip is not None:
46-
mgmt_gw_ips.append(svc_spec.virtual_ip)
47-
return mgmt_gw_ips
45+
return svc_spec.virtual_ip
46+
else:
47+
return self.mgr.inventory.get_addr(daemon_spec.host)
4848

4949
def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None:
5050
# we adjust the standby behaviour so rev-proxy can pick correctly the active instance
@@ -63,9 +63,12 @@ def get_external_certificates(self, svc_spec: MgmtGatewaySpec, daemon_spec: Ceph
6363
key = svc_spec.ssl_certificate_key
6464
else:
6565
# not provided on the spec, let's generate self-sigend certificates
66-
ips = self.get_mgmt_gw_ips(svc_spec, daemon_spec)
67-
host_fqdn = self.mgr.get_fqdn(daemon_spec.host)
68-
cert, key = self.mgr.cert_mgr.generate_cert(host_fqdn, ips)
66+
ip = self.get_mgmt_gw_ip(svc_spec, daemon_spec)
67+
# we don't include the host_fqdn in case of using a virtual_ip
68+
# because we may have several instances of the mgmt-gateway running
69+
# on different hosts
70+
host_fqdn = [] if svc_spec.virtual_ip else [self.mgr.get_fqdn(daemon_spec.host)]
71+
cert, key = self.mgr.cert_mgr.generate_cert(host_fqdn, ip)
6972
# save certificates
7073
if cert and key:
7174
self.mgr.cert_mgr.save_cert('mgmt_gw_cert', cert, user_made=user_made)
@@ -75,9 +78,9 @@ def get_external_certificates(self, svc_spec: MgmtGatewaySpec, daemon_spec: Ceph
7578
return cert, key
7679

7780
def get_internal_certificates(self, svc_spec: MgmtGatewaySpec, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[str, str]:
78-
ips = self.get_mgmt_gw_ips(svc_spec, daemon_spec)
81+
ip = self.get_mgmt_gw_ip(svc_spec, daemon_spec)
7982
host_fqdn = self.mgr.get_fqdn(daemon_spec.host)
80-
return self.mgr.cert_mgr.generate_cert(host_fqdn, ips)
83+
return self.mgr.cert_mgr.generate_cert(host_fqdn, ip)
8184

8285
def get_service_discovery_endpoints(self) -> List[str]:
8386
sd_endpoints = []

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

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import logging
22
from typing import List, Any, Tuple, Dict, cast, Optional
3-
import os
4-
import base64
3+
from copy import copy
54

65
from orchestrator import DaemonDescription
7-
from ceph.deployment.service_spec import OAuth2ProxySpec
6+
from ceph.deployment.service_spec import OAuth2ProxySpec, MgmtGatewaySpec
87
from cephadm.services.cephadmservice import CephadmService, CephadmDaemonDeploySpec
98
from .service_registry import register_cephadm_service
109

@@ -23,6 +22,9 @@ def prepare_create(self, daemon_spec: CephadmDaemonDeploySpec) -> CephadmDaemonD
2322

2423
def get_service_ips_and_hosts(self, service_name: str) -> List[str]:
2524
entries = set()
25+
mgmt_gw_spec = cast(MgmtGatewaySpec, self.mgr.spec_store['mgmt-gateway'].spec)
26+
if mgmt_gw_spec.virtual_ip is not None:
27+
entries.add(mgmt_gw_spec.virtual_ip)
2628
for dd in self.mgr.cache.get_daemons_by_service(service_name):
2729
assert dd.hostname is not None
2830
addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname)
@@ -43,9 +45,11 @@ def get_active_daemon(self, daemon_descrs: List[DaemonDescription]) -> DaemonDes
4345
def get_certificates(self, svc_spec: OAuth2ProxySpec, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[str, str]:
4446
cert = self.mgr.cert_mgr.get_cert('oauth2_proxy_cert')
4547
key = self.mgr.cert_mgr.get_key('oauth2_proxy_key')
48+
user_made = False
4649
if not (cert and key):
4750
# not available on store, check if provided on the spec
4851
if svc_spec.ssl_certificate and svc_spec.ssl_certificate_key:
52+
user_made = True
4953
cert = svc_spec.ssl_certificate
5054
key = svc_spec.ssl_certificate_key
5155
else:
@@ -55,25 +59,20 @@ def get_certificates(self, svc_spec: OAuth2ProxySpec, daemon_spec: CephadmDaemon
5559
cert, key = self.mgr.cert_mgr.generate_cert(host_fqdn, addr)
5660
# save certificates
5761
if cert and key:
58-
self.mgr.cert_mgr.save_cert('oauth2_proxy_cert', cert)
59-
self.mgr.cert_mgr.save_key('oauth2_proxy_key', key)
62+
self.mgr.cert_mgr.save_cert('oauth2_proxy_cert', cert, user_made=user_made)
63+
self.mgr.cert_mgr.save_key('oauth2_proxy_key', key, user_made=user_made)
6064
else:
6165
logger.error("Failed to obtain certificate and key from mgmt-gateway.")
6266
return cert, key
6367

64-
def generate_random_secret(self) -> str:
65-
random_bytes = os.urandom(32)
66-
base64_secret = base64.urlsafe_b64encode(random_bytes).rstrip(b'=').decode('utf-8')
67-
return base64_secret
68-
6968
def generate_config(self, daemon_spec: CephadmDaemonDeploySpec) -> Tuple[Dict[str, Any], List[str]]:
7069
assert self.TYPE == daemon_spec.daemon_type
7170
svc_spec = cast(OAuth2ProxySpec, self.mgr.spec_store[daemon_spec.service_name].spec)
72-
allowlist_domains = svc_spec.allowlist_domains or []
71+
allowlist_domains = copy(svc_spec.allowlist_domains) or []
7372
allowlist_domains += self.get_service_ips_and_hosts('mgmt-gateway')
7473
context = {
7574
'spec': svc_spec,
76-
'cookie_secret': svc_spec.cookie_secret or self.generate_random_secret(),
75+
'cookie_secret': svc_spec.cookie_secret,
7776
'allowlist_domains': allowlist_domains,
7877
'redirect_url': svc_spec.redirect_url or self.get_redirect_url()
7978
}

src/pybind/mgr/cephadm/templates/services/mgmt-gateway/nginx.conf.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ events {
99
http {
1010

1111
#access_log /dev/stdout;
12-
error_log /dev/stderr info;
12+
error_log /dev/stderr warn;
1313
client_header_buffer_size 32K;
1414
large_client_header_buffers 4 32k;
1515
proxy_busy_buffers_size 512k;

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4037,7 +4037,7 @@ def get_services_endpoints(name):
40374037
http {
40384038
40394039
#access_log /dev/stdout;
4040-
error_log /dev/stderr info;
4040+
error_log /dev/stderr warn;
40414041
client_header_buffer_size 32K;
40424042
large_client_header_buffers 4 32k;
40434043
proxy_busy_buffers_size 512k;
@@ -4286,7 +4286,7 @@ def get_services_endpoints(name):
42864286
http {
42874287
42884288
#access_log /dev/stdout;
4289-
error_log /dev/stderr info;
4289+
error_log /dev/stderr warn;
42904290
client_header_buffer_size 32K;
42914291
large_client_header_buffers 4 32k;
42924292
proxy_busy_buffers_size 512k;
@@ -4608,14 +4608,17 @@ def get_services_endpoints(name):
46084608
enable_auth=True,
46094609
virtual_ip=virtual_ip)
46104610

4611+
allowed_domain = '192.168.100.1:8080'
46114612
oauth2_spec = OAuth2ProxySpec(provider_display_name='my_idp_provider',
46124613
client_id='my_client_id',
46134614
client_secret='my_client_secret',
46144615
oidc_issuer_url='http://192.168.10.10:8888/dex',
46154616
cookie_secret='kbAEM9opAmuHskQvt0AW8oeJRaOM2BYy5Loba0kZ0SQ=',
46164617
ssl_certificate=ceph_generated_cert,
4617-
ssl_certificate_key=ceph_generated_key)
4618+
ssl_certificate_key=ceph_generated_key,
4619+
allowlist_domains=[allowed_domain])
46184620

4621+
whitelist_domains = f"{allowed_domain},1::4,ceph-node" if virtual_ip is None else f"{allowed_domain},{virtual_ip},1::4,ceph-node"
46194622
redirect_url = f"https://{virtual_ip if virtual_ip else 'host_fqdn'}:5555/oauth2/callback"
46204623
expected = {
46214624
"fsid": "fsid",
@@ -4669,7 +4672,7 @@ def get_services_endpoints(name):
46694672
# Secret value for encrypting cookies.
46704673
cookie_secret= "kbAEM9opAmuHskQvt0AW8oeJRaOM2BYy5Loba0kZ0SQ="
46714674
email_domains= "*"
4672-
whitelist_domains= "1::4,ceph-node\""""),
4675+
whitelist_domains= "{whitelist_domains}\""""),
46734676
"oauth2-proxy.crt": f"{ceph_generated_cert}",
46744677
"oauth2-proxy.key": f"{ceph_generated_key}",
46754678
}

src/python-common/ceph/deployment/service_spec.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2030,7 +2030,7 @@ def __init__(self,
20302030
self.redirect_url = redirect_url
20312031
#: The secret key used for signing cookies. Its length must be 16,
20322032
# 24, or 32 bytes to create an AES cipher.
2033-
self.cookie_secret = cookie_secret
2033+
self.cookie_secret = cookie_secret or self.generate_random_secret()
20342034
#: The multi-line SSL certificate for encrypting communications.
20352035
self.ssl_certificate = ssl_certificate
20362036
#: The multi-line SSL certificate private key for decrypting communications.
@@ -2040,6 +2040,12 @@ def __init__(self,
20402040
self.allowlist_domains = allowlist_domains
20412041
self.unmanaged = unmanaged
20422042

2043+
def generate_random_secret(self) -> str:
2044+
import base64
2045+
random_bytes = os.urandom(32)
2046+
base64_secret = base64.urlsafe_b64encode(random_bytes).decode('utf-8')
2047+
return base64_secret
2048+
20432049
def get_port_start(self) -> List[int]:
20442050
ports = [4180]
20452051
return ports

0 commit comments

Comments
 (0)