From: John Mulligan Date: Fri, 22 Sep 2023 17:34:58 +0000 (-0400) Subject: cephadm: move firewalld related items to firewalld.py X-Git-Tag: v19.0.0~330^2~7 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=33f2606935e0813ba8e199d55c2819eecdbbf51d;p=ceph.git cephadm: move firewalld related items to firewalld.py Signed-off-by: John Mulligan --- diff --git a/src/cephadm/cephadm.py b/src/cephadm/cephadm.py index 2c7b5602d5a64..b6692b399eeb3 100755 --- a/src/cephadm/cephadm.py +++ b/src/cephadm/cephadm.py @@ -165,11 +165,11 @@ from cephadmlib.host_facts import HostFacts, list_networks from cephadmlib.ssh import authorize_ssh_key, check_ssh_connectivity from cephadmlib.daemon_form import ( DaemonForm, - FirewalledServiceDaemonForm, create as daemon_form_create, register as register_daemon_form, ) from cephadmlib.sysctl import install_sysctl, migrate_sysctl_dir +from cephadmlib.firewalld import Firewalld, update_firewalld FuncT = TypeVar('FuncT', bound=Callable) @@ -3102,127 +3102,6 @@ def _write_iscsi_unit_poststop_commands( f.write(' '.join(CephIscsi.configfs_mount_umount(data_dir, mount=False)) + '\n') -class Firewalld(object): - - # for specifying ports we should always open when opening - # ports for a daemon of that type. Main use case is for ports - # that we should open when deploying the daemon type but that - # the daemon itself may not necessarily need to bind to the port. - # This needs to be handed differently as we don't want to fail - # deployment if the port cannot be bound to but we still want to - # open the port in the firewall. - external_ports: Dict[str, List[int]] = { - 'iscsi': [3260] # 3260 is the well known iSCSI port - } - - def __init__(self, ctx): - # type: (CephadmContext) -> None - self.ctx = ctx - self.available = self.check() - - def check(self): - # type: () -> bool - self.cmd = find_executable('firewall-cmd') - if not self.cmd: - logger.debug('firewalld does not appear to be present') - return False - (enabled, state, _) = check_unit(self.ctx, 'firewalld.service') - if not enabled: - logger.debug('firewalld.service is not enabled') - return False - if state != 'running': - logger.debug('firewalld.service is not running') - return False - - logger.info('firewalld ready') - return True - - def enable_service_for(self, svc: str) -> None: - assert svc, 'service name not provided' - if not self.available: - logger.debug('Not possible to enable service <%s>. firewalld.service is not available' % svc) - return - - if not self.cmd: - raise RuntimeError('command not defined') - - out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--query-service', svc], verbosity=CallVerbosity.DEBUG) - if ret: - logger.info('Enabling firewalld service %s in current zone...' % svc) - out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--add-service', svc]) - if ret: - raise RuntimeError( - 'unable to add service %s to current zone: %s' % (svc, err)) - else: - logger.debug('firewalld service %s is enabled in current zone' % svc) - - def open_ports(self, fw_ports): - # type: (List[int]) -> None - if not self.available: - logger.debug('Not possible to open ports <%s>. firewalld.service is not available' % fw_ports) - return - - if not self.cmd: - raise RuntimeError('command not defined') - - for port in fw_ports: - tcp_port = str(port) + '/tcp' - out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--query-port', tcp_port], verbosity=CallVerbosity.DEBUG) - if ret: - logger.info('Enabling firewalld port %s in current zone...' % tcp_port) - out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--add-port', tcp_port]) - if ret: - raise RuntimeError('unable to add port %s to current zone: %s' % - (tcp_port, err)) - else: - logger.debug('firewalld port %s is enabled in current zone' % tcp_port) - - def close_ports(self, fw_ports): - # type: (List[int]) -> None - if not self.available: - logger.debug('Not possible to close ports <%s>. firewalld.service is not available' % fw_ports) - return - - if not self.cmd: - raise RuntimeError('command not defined') - - for port in fw_ports: - tcp_port = str(port) + '/tcp' - out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--query-port', tcp_port], verbosity=CallVerbosity.DEBUG) - if not ret: - logger.info('Disabling port %s in current zone...' % tcp_port) - out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--remove-port', tcp_port]) - if ret: - raise RuntimeError('unable to remove port %s from current zone: %s' % - (tcp_port, err)) - else: - logger.info(f'Port {tcp_port} disabled') - else: - logger.info(f'firewalld port {tcp_port} already closed') - - def apply_rules(self): - # type: () -> None - if not self.available: - return - - if not self.cmd: - raise RuntimeError('command not defined') - - call_throws(self.ctx, [self.cmd, '--reload']) - - -def update_firewalld(ctx: CephadmContext, daemon: DaemonForm) -> None: - if not ('skip_firewalld' in ctx and ctx.skip_firewalld) and isinstance( - daemon, FirewalledServiceDaemonForm - ): - svc = daemon.firewall_service_name() - if not svc: - return - firewall = Firewalld(ctx) - firewall.enable_service_for(svc) - firewall.apply_rules() - - def install_base_units(ctx, fsid): # type: (CephadmContext, str) -> None """ diff --git a/src/cephadm/cephadmlib/firewalld.py b/src/cephadm/cephadmlib/firewalld.py new file mode 100644 index 0000000000000..f47e7e71d4dce --- /dev/null +++ b/src/cephadm/cephadmlib/firewalld.py @@ -0,0 +1,134 @@ +# firewalld.py - functions and types for working with firewalld + +import logging + +from typing import List, Dict + +from .call_wrappers import call, call_throws, CallVerbosity +from .context import CephadmContext +from .daemon_form import DaemonForm, FirewalledServiceDaemonForm +from .exe_utils import find_executable +from .systemd import check_unit + +logger = logging.getLogger() + + +class Firewalld(object): + + # for specifying ports we should always open when opening + # ports for a daemon of that type. Main use case is for ports + # that we should open when deploying the daemon type but that + # the daemon itself may not necessarily need to bind to the port. + # This needs to be handed differently as we don't want to fail + # deployment if the port cannot be bound to but we still want to + # open the port in the firewall. + external_ports: Dict[str, List[int]] = { + 'iscsi': [3260] # 3260 is the well known iSCSI port + } + + def __init__(self, ctx): + # type: (CephadmContext) -> None + self.ctx = ctx + self.available = self.check() + + def check(self): + # type: () -> bool + self.cmd = find_executable('firewall-cmd') + if not self.cmd: + logger.debug('firewalld does not appear to be present') + return False + (enabled, state, _) = check_unit(self.ctx, 'firewalld.service') + if not enabled: + logger.debug('firewalld.service is not enabled') + return False + if state != 'running': + logger.debug('firewalld.service is not running') + return False + + logger.info('firewalld ready') + return True + + def enable_service_for(self, svc: str) -> None: + assert svc, 'service name not provided' + if not self.available: + logger.debug('Not possible to enable service <%s>. firewalld.service is not available' % svc) + return + + if not self.cmd: + raise RuntimeError('command not defined') + + out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--query-service', svc], verbosity=CallVerbosity.DEBUG) + if ret: + logger.info('Enabling firewalld service %s in current zone...' % svc) + out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--add-service', svc]) + if ret: + raise RuntimeError( + 'unable to add service %s to current zone: %s' % (svc, err)) + else: + logger.debug('firewalld service %s is enabled in current zone' % svc) + + def open_ports(self, fw_ports): + # type: (List[int]) -> None + if not self.available: + logger.debug('Not possible to open ports <%s>. firewalld.service is not available' % fw_ports) + return + + if not self.cmd: + raise RuntimeError('command not defined') + + for port in fw_ports: + tcp_port = str(port) + '/tcp' + out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--query-port', tcp_port], verbosity=CallVerbosity.DEBUG) + if ret: + logger.info('Enabling firewalld port %s in current zone...' % tcp_port) + out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--add-port', tcp_port]) + if ret: + raise RuntimeError('unable to add port %s to current zone: %s' % + (tcp_port, err)) + else: + logger.debug('firewalld port %s is enabled in current zone' % tcp_port) + + def close_ports(self, fw_ports): + # type: (List[int]) -> None + if not self.available: + logger.debug('Not possible to close ports <%s>. firewalld.service is not available' % fw_ports) + return + + if not self.cmd: + raise RuntimeError('command not defined') + + for port in fw_ports: + tcp_port = str(port) + '/tcp' + out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--query-port', tcp_port], verbosity=CallVerbosity.DEBUG) + if not ret: + logger.info('Disabling port %s in current zone...' % tcp_port) + out, err, ret = call(self.ctx, [self.cmd, '--permanent', '--remove-port', tcp_port]) + if ret: + raise RuntimeError('unable to remove port %s from current zone: %s' % + (tcp_port, err)) + else: + logger.info(f'Port {tcp_port} disabled') + else: + logger.info(f'firewalld port {tcp_port} already closed') + + def apply_rules(self): + # type: () -> None + if not self.available: + return + + if not self.cmd: + raise RuntimeError('command not defined') + + call_throws(self.ctx, [self.cmd, '--reload']) + + +def update_firewalld(ctx: CephadmContext, daemon: DaemonForm) -> None: + if not ('skip_firewalld' in ctx and ctx.skip_firewalld) and isinstance( + daemon, FirewalledServiceDaemonForm + ): + svc = daemon.firewall_service_name() + if not svc: + return + firewall = Firewalld(ctx) + firewall.enable_service_for(svc) + firewall.apply_rules() diff --git a/src/cephadm/tests/test_cephadm.py b/src/cephadm/tests/test_cephadm.py index a43ed5457c54e..ff474c23ccd9d 100644 --- a/src/cephadm/tests/test_cephadm.py +++ b/src/cephadm/tests/test_cephadm.py @@ -286,6 +286,7 @@ class TestCephAdm(object): for address, expected in tests: wrap_test(address, expected) + @mock.patch('cephadmlib.firewalld.Firewalld', mock_bad_firewalld) @mock.patch('cephadm.Firewalld', mock_bad_firewalld) @mock.patch('cephadm.logger') def test_skip_firewalld(self, _logger, cephadm_fs):