From: Juan Miguel Olmo Martínez Date: Wed, 19 Aug 2020 10:11:28 +0000 (+0200) Subject: cephadm: Allow users to use a custom dashboard ssl port X-Git-Tag: wip-pdonnell-testing-20200918.022351~309^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=08c4a53e79a7ed63912f765fbbf86e3364d8f9be;p=ceph-ci.git cephadm: Allow users to use a custom dashboard ssl port This modification allows the user to create a new bootstrap cluster using a predefined SSl port for the dashboard. If firewall is enabled, any new manager daemon deployed in new hosts will take care of open the required ports for all the services enabled in the manager. Two new parameters for cephadm tool (aka binary or standalone): Command bootstrap: --ssl-dashboard-port SSL_DASHBOARD_PORT Port number used to connect with dashboard using SSL Command deploy: --tcp-ports TCP_PORTS List of tcp ports to open in the host firewall Signed-off-by: Juan Miguel Olmo Martínez --- diff --git a/doc/man/8/cephadm.rst b/doc/man/8/cephadm.rst index 60b4535f11b..72bbf22edab 100644 --- a/doc/man/8/cephadm.rst +++ b/doc/man/8/cephadm.rst @@ -60,6 +60,7 @@ Synopsis | [--skip-ssh] | [--initial-dashboard-user INITIAL_DASHBOARD_USER] | [--initial-dashboard-password INITIAL_DASHBOARD_PASSWORD] +| [--ssl-dashboard-port SSL_DASHBOARD_PORT] | [--dashboard-key DASHBOARD_KEY] | [--dashboard-crt DASHBOARD_CRT] | [--ssh-config SSH_CONFIG] @@ -82,7 +83,7 @@ Synopsis | **cephadm** **deploy** [-h] --name NAME --fsid FSID [--config CONFIG] | [--config-json CONFIG_JSON] [--keyring KEYRING] | [--key KEY] [--osd-fsid OSD_FSID] [--skip-firewalld] -| [--reconfig] [--allow-ptrace] +| [--tcp-ports TCP_PORTS] [--reconfig] [--allow-ptrace] | **cephadm** **check-host** [-h] [--expect-hostname EXPECT_HOSTNAME] @@ -210,6 +211,7 @@ Arguments: * [--skip-ssh skip setup of ssh key on local host * [--initial-dashboard-user INITIAL_DASHBOARD_USER] Initial user for the dashboard * [--initial-dashboard-password INITIAL_DASHBOARD_PASSWORD] Initial password for the initial dashboard user +* [--ssl-dashboard-port SSL_DASHBOARD_PORT] Port number used to connect with dashboard using SSL * [--dashboard-key DASHBOARD_KEY] Dashboard key * [--dashboard-crt DASHBOARD_CRT] Dashboard certificate * [--ssh-config SSH_CONFIG] SSH config @@ -279,6 +281,7 @@ Arguments: * [--key KEY] key for new daemon * [--osd-fsid OSD_FSID] OSD uuid, if creating an OSD container * [--skip-firewalld] Do not configure firewalld +* [--tcp-ports List of tcp ports to open in the host firewall * [--reconfig] Reconfigure a previously deployed daemon * [--allow-ptrace] Allow SYS_PTRACE on daemon container diff --git a/src/cephadm/cephadm b/src/cephadm/cephadm index 540dc999818..abb2400c0f2 100755 --- a/src/cephadm/cephadm +++ b/src/cephadm/cephadm @@ -220,14 +220,6 @@ class NFSGanesha(object): # type: (str, Union[int, str]) -> NFSGanesha return cls(fsid, daemon_id, get_parm(args.config_json), args.image) - @staticmethod - def port_in_use(): - # type () -> None - for (srv, port) in NFSGanesha.port_map.items(): - if port_in_use(port): - msg = 'TCP port {} required for {} is already in use'.format(port, srv) - raise Error(msg) - @staticmethod def get_container_mounts(data_dir): # type: (str) -> Dict[str, str] @@ -1865,8 +1857,14 @@ def extract_uid_gid(img='', file_path='/var/lib/ceph'): def deploy_daemon(fsid, daemon_type, daemon_id, c, uid, gid, config=None, keyring=None, osd_fsid=None, - reconfig=False): - # type: (str, str, Union[int, str], CephContainer, int, int, Optional[str], Optional[str], Optional[str], Optional[bool]) -> None + reconfig=False, + ports=None): + # type: (str, str, Union[int, str], CephContainer, int, int, Optional[str], Optional[str], Optional[str], Optional[bool], Optional[List[int]]) -> None + + ports = ports or [] + if any([port_in_use(port) for port in ports]): + raise Error("TCP Port(s) '{}' required for {} already in use".format(",".join(map(str, ports)), daemon_type)) + data_dir = get_data_dir(fsid, daemon_type, daemon_id) if reconfig and not os.path.exists(data_dir): raise Error('cannot reconfig, data path %s does not exist' % data_dir) @@ -1929,6 +1927,12 @@ def deploy_daemon(fsid, daemon_type, daemon_id, c, uid, gid, update_firewalld(daemon_type) + # Open ports explicitly required for the daemon + if ports: + fw = Firewalld() + fw.open_ports(ports) + fw.apply_rules() + if reconfig and daemon_type not in Ceph.daemons: # ceph daemons do not need a restart; others (presumably) do to pick # up the new config @@ -2149,11 +2153,7 @@ def update_firewalld(daemon_type): fw_ports = [] - if daemon_type == 'mgr': - fw_ports.append(8080) # dashboard - fw_ports.append(8443) # dashboard - fw_ports.append(9283) # mgr/prometheus exporter - elif daemon_type in Monitoring.port_map.keys(): + if daemon_type in Monitoring.port_map.keys(): fw_ports.extend(Monitoring.port_map[daemon_type]) # prometheus etc firewall.open_ports(fw_ports) @@ -2787,8 +2787,9 @@ def command_bootstrap(): logger.info('Creating mgr...') mgr_keyring = '[mgr.%s]\n\tkey = %s\n' % (mgr_id, mgr_key) mgr_c = get_container(fsid, 'mgr', mgr_id) + # Note:the default port used by the Prometheus node exporter is opened in fw deploy_daemon(fsid, 'mgr', mgr_id, mgr_c, uid, gid, - config=config, keyring=mgr_keyring) + config=config, keyring=mgr_keyring, ports=[9283]) # output files with open(args.output_keyring, 'w') as f: @@ -2924,6 +2925,11 @@ def command_bootstrap(): cli(['config', 'set', 'mgr', 'mgr/cephadm/registry_password', args.registry_password, '--force']) if not args.skip_dashboard: + # Configure SSL port (cephadm only allows to configure dashboard SSL port) + # if the user does not want to use SSL he can change this setting once the cluster is up + cli(["config", "set", "mgr", "mgr/dashboard/ssl_server_port" , str(args.ssl_dashboard_port)]) + + # configuring dashboard parameters logger.info('Enabling the dashboard module...') cli(['mgr', 'module', 'enable', 'dashboard']) wait_for_mgr_restart() @@ -2951,6 +2957,11 @@ def command_bootstrap(): out = cli(['config', 'get', 'mgr', 'mgr/dashboard/ssl_server_port']) port = int(out) + # Open dashboard port + fw = Firewalld() + fw.open_ports([port]) + fw.apply_rules() + logger.info('Ceph Dashboard is now available at:\n\n' '\t URL: https://%s:%s/\n' '\t User: %s\n' @@ -3074,24 +3085,29 @@ def command_deploy(): else: logger.info('%s daemon %s ...' % ('Deploy', args.name)) + # Get and check ports explicitly required to be opened + daemon_ports = [] # type: List[int] + if args.tcp_ports: + daemon_ports = list(map(int, args.tcp_ports.split())) + if daemon_type in Ceph.daemons: config, keyring = get_config_and_keyring() uid, gid = extract_uid_gid() make_var_run(args.fsid, uid, gid) + c = get_container(args.fsid, daemon_type, daemon_id, ptrace=args.allow_ptrace) deploy_daemon(args.fsid, daemon_type, daemon_id, c, uid, gid, config=config, keyring=keyring, osd_fsid=args.osd_fsid, - reconfig=args.reconfig) + reconfig=args.reconfig, + ports=daemon_ports) elif daemon_type in Monitoring.components: # monitoring daemon - prometheus, grafana, alertmanager, node-exporter # Default Checks if not args.reconfig and not redeploy: - daemon_ports = Monitoring.port_map[daemon_type] # type: List[int] - if any([port_in_use(port) for port in daemon_ports]): - raise Error("TCP Port(s) '{}' required for {} is already in use".format(",".join(map(str, daemon_ports)), daemon_type)) + daemon_ports.extend(Monitoring.port_map[daemon_type]) # make sure provided config-json is sufficient config = get_parm(args.config_json) # type: ignore @@ -3109,18 +3125,21 @@ def command_deploy(): uid, gid = extract_uid_gid_monitoring(daemon_type) c = get_container(args.fsid, daemon_type, daemon_id) deploy_daemon(args.fsid, daemon_type, daemon_id, c, uid, gid, - reconfig=args.reconfig) + reconfig=args.reconfig, + ports=daemon_ports) elif daemon_type == NFSGanesha.daemon_type: if not args.reconfig and not redeploy: - NFSGanesha.port_in_use() + daemon_ports.extend(NFSGanesha.port_map.values()) + config, keyring = get_config_and_keyring() # TODO: extract ganesha uid/gid (997, 994) ? uid, gid = extract_uid_gid() c = get_container(args.fsid, daemon_type, daemon_id) deploy_daemon(args.fsid, daemon_type, daemon_id, c, uid, gid, config=config, keyring=keyring, - reconfig=args.reconfig) + reconfig=args.reconfig, + ports=daemon_ports) elif daemon_type == CephIscsi.daemon_type: config, keyring = get_config_and_keyring() @@ -3128,7 +3147,8 @@ def command_deploy(): c = get_container(args.fsid, daemon_type, daemon_id) deploy_daemon(args.fsid, daemon_type, daemon_id, c, uid, gid, config=config, keyring=keyring, - reconfig=args.reconfig) + reconfig=args.reconfig, + ports=daemon_ports) else: raise Error("{} not implemented in command_deploy function".format(daemon_type)) @@ -4903,7 +4923,11 @@ def _get_parser(): parser_bootstrap.add_argument( '--initial-dashboard-password', help='Initial password for the initial dashboard user') - + parser_bootstrap.add_argument( + '--ssl-dashboard-port', + type=int, + default = 8443, + help='Port number used to connect with dashboard using SSL') parser_bootstrap.add_argument( '--dashboard-key', type=argparse.FileType('r'), @@ -5031,6 +5055,9 @@ def _get_parser(): '--skip-firewalld', action='store_true', help='Do not configure firewalld') + parser_deploy.add_argument( + '--tcp-ports', + help='List of tcp ports to open in the host firewall') parser_deploy.add_argument( '--reconfig', action='store_true', diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index 07e6c91c3c4..d5ffd2a6a9c 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -482,7 +482,7 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, The main loop of cephadm. A command handler will typically change the declarative state - of cephadm. This loop will then attempt to apply this new state. + of cephadm. This loop will then attempt to apply this new state. """ self.log.debug("serve starting") while self.run: @@ -1890,6 +1890,10 @@ you may want to run: daemon_spec.extra_args.extend(['--config-json', '-']) + # TCP port to open in the host firewall + if daemon_spec.ports: + daemon_spec.extra_args.extend(['--tcp-ports', ' '.join(map(str,daemon_spec.ports))]) + # osd deployments needs an --osd-uuid arg if daemon_spec.daemon_type == 'osd': if not osd_uuid_map: diff --git a/src/pybind/mgr/cephadm/services/cephadmservice.py b/src/pybind/mgr/cephadm/services/cephadmservice.py index ec4cba0bbb3..7582d1b73e4 100644 --- a/src/pybind/mgr/cephadm/services/cephadmservice.py +++ b/src/pybind/mgr/cephadm/services/cephadmservice.py @@ -1,4 +1,5 @@ import json +import re import logging import subprocess from abc import ABCMeta, abstractmethod @@ -26,7 +27,8 @@ class CephadmDaemonSpec(Generic[ServiceSpecs]): keyring: Optional[str]=None, extra_args: Optional[List[str]]=None, extra_config: Optional[Dict[str, Any]]=None, - daemon_type: Optional[str]=None): + daemon_type: Optional[str]=None, + ports: Optional[List[int]]=None,): """ Used for * deploying new daemons. then everything is set @@ -53,6 +55,9 @@ class CephadmDaemonSpec(Generic[ServiceSpecs]): self.extra_args: List[str] = extra_args or [] self.extra_config: Dict[str, Any] = extra_config or {} + # TCP ports used by the daemon + self.ports: List[int] = ports or [] + def name(self) -> str: return '%s.%s' % (self.daemon_type, self.daemon_id) @@ -323,6 +328,28 @@ class MgrService(CephadmService): 'mds', 'allow *'], }) + + # Retrieve ports used by manager modules + # In the case of the dashboard port and with several manager daemons + # running in different hosts, it exists the possibility that the + # user has decided to use different dashboard ports in each server + # If this is the case then the dashboard port opened will be only the used + # as default. + ports = [] + config_ports = '' + ret, mgr_services, err = self.mgr.check_mon_command({ + 'prefix': 'mgr services', + }) + if mgr_services: + mgr_endpoints = json.loads(mgr_services) + for end_point in mgr_endpoints.values(): + port = re.search('\:\d+\/', end_point) + if port: + ports.append(int(port[0][1:-1])) + + if ports: + daemon_spec.ports = ports + daemon_spec.keyring = keyring return self.mgr._create_daemon(daemon_spec)