]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
cephadm: Allow users to use a custom dashboard ssl port
authorJuan Miguel Olmo Martínez <jolmomar@redhat.com>
Wed, 19 Aug 2020 10:11:28 +0000 (12:11 +0200)
committerJuan Miguel Olmo Martínez <jolmomar@redhat.com>
Thu, 20 Aug 2020 11:56:59 +0000 (13:56 +0200)
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 <jolmomar@redhat.com>
doc/man/8/cephadm.rst
src/cephadm/cephadm
src/pybind/mgr/cephadm/module.py
src/pybind/mgr/cephadm/services/cephadmservice.py

index 60b4535f11b751f8cc7eb59bfc03f196ac1fba47..72bbf22edab0015a299e9eb6d2a441291513fb13 100644 (file)
@@ -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
 
index 540dc999818aa2e8fb965457a258ab0e75901851..abb2400c0f2440b0c113a553a35681aea45f6c37 100755 (executable)
@@ -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',
index 07e6c91c3c487505f83bb32290bb8b973d517564..d5ffd2a6a9c3fb1490888e5641f259be8413c9a2 100644 (file)
@@ -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:
index ec4cba0bbb3234a9926ba5995fb02e5bc7b79010..7582d1b73e43f0284b9fff93f9bffe6fa26808d3 100644 (file)
@@ -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)