From 41ef8fba31ddbb32e2e5b7cccdc9b582a0809111 Mon Sep 17 00:00:00 2001 From: Rob Critenden Date: Fri, 16 Aug 2019 18:10:15 +0000 Subject: [PATCH] Use tasks to configure automount nsswitch settings authselect doesn't allow one to directly write to /etc/nsswitch.conf. It will complain bitterly if it detects it and will refuse to work until reset. Instead it wants the user to write to /etc/authselect/user-nsswitch.conf and then it will handle merging in any differences. To complicate matters some databases are not user configurable like passwd, group and of course, automount. There are some undocumented options to allow one to override these though so we utilize that. tasks are used so that authselect-based installations can still write directly to /etc/nsswitch.conf and operate as it used to. Reviewed-By: Francois Cami Reviewed-By: Rob Crittenden --- ipaclient/install/client.py | 72 +--------- ipaclient/install/ipa_client_automount.py | 80 +---------- ipaplatform/base/tasks.py | 153 ++++++++++++++++++++++ ipaplatform/redhat/paths.py | 1 + ipaplatform/redhat/tasks.py | 19 +++ 5 files changed, 180 insertions(+), 145 deletions(-) diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py index 9492ca418..1e88ba1d4 100644 --- a/ipaclient/install/client.py +++ b/ipaclient/install/client.py @@ -66,7 +66,7 @@ from ipapython import version from . import automount, timeconf, sssd from ipaclient import discovery -from .ipachangeconf import IPAChangeConf +from ipapython.ipachangeconf import IPAChangeConf NoneType = type(None) @@ -281,72 +281,6 @@ def is_ipa_client_installed(fstore, on_master=False): return installed -def configure_nsswitch_database(fstore, database, services, preserve=True, - append=True, default_value=()): - """ - Edits the specified nsswitch.conf database (e.g. passwd, group, sudoers) - to use the specified service(s). - - Arguments: - fstore - FileStore to backup the nsswitch.conf - database - database configuration that should be ammended, - e.g. 'sudoers' - service - list of services that should be added, e.g. ['sss'] - preserve - if True, the already configured services will be preserved - - The next arguments modify the behaviour if preserve=True: - append - if True, the services will be appended, if False, prepended - default_value - list of services that are considered as default (if - the database is not mentioned in nsswitch.conf), e.g. - ['files'] - """ - - # Backup the original version of nsswitch.conf, we're going to edit it now - if not fstore.has_file(paths.NSSWITCH_CONF): - fstore.backup_file(paths.NSSWITCH_CONF) - - conf = IPAChangeConf("IPA Installer") - conf.setOptionAssignment(':') - - if preserve: - # Read the existing configuration - with open(paths.NSSWITCH_CONF, 'r') as f: - opts = conf.parse(f) - raw_database_entry = conf.findOpts(opts, 'option', database)[1] - - # Detect the list of already configured services - if not raw_database_entry: - # If there is no database entry, database is not present in - # the nsswitch.conf. Set the list of services to the - # default list, if passed. - configured_services = list(default_value) - else: - configured_services = raw_database_entry['value'].strip().split() - - # Make sure no service is added if already mentioned in the list - added_services = [s for s in services - if s not in configured_services] - - # Prepend / append the list of new services - if append: - new_value = ' ' + ' '.join(configured_services + added_services) - else: - new_value = ' ' + ' '.join(added_services + configured_services) - - else: - # Preserve not set, let's rewrite existing configuration - new_value = ' ' + ' '.join(services) - - # Set new services as sources for database - opts = [ - conf.setOption(database, new_value), - conf.emptyLine(), - ] - - conf.changeConf(paths.NSSWITCH_CONF, opts) - logger.info("Configured %s in %s", database, paths.NSSWITCH_CONF) - - def configure_ipa_conf( fstore, cli_basedn, cli_realm, cli_domain, cli_server, hostname): ipaconf = IPAChangeConf("IPA Installer") @@ -948,9 +882,7 @@ def configure_sssd_conf( "Unable to activate the SUDO service in SSSD config.") sssdconfig.activate_service('sudo') - configure_nsswitch_database( - fstore, 'sudoers', ['sss'], - default_value=['files']) + tasks.enable_sssd_sudo(fstore) domain.add_provider('ipa', 'id') diff --git a/ipaclient/install/ipa_client_automount.py b/ipaclient/install/ipa_client_automount.py index a1dc2a18c..3a0896be3 100644 --- a/ipaclient/install/ipa_client_automount.py +++ b/ipaclient/install/ipa_client_automount.py @@ -41,7 +41,8 @@ from six.moves.urllib.parse import urlsplit # pylint: enable=import-error from optparse import OptionParser # pylint: disable=deprecated-module -from ipaclient.install import ipachangeconf, ipadiscovery +from ipapython import ipachangeconf +from ipaclient.install import ipadiscovery from ipaclient.install.client import ( CLIENT_NOT_CONFIGURED, CLIENT_ALREADY_CONFIGURED, @@ -177,44 +178,6 @@ def configure_xml(fstore): print("Configured %s" % authconf) -def configure_nsswitch(statestore, options): - """ - Point automount to ldap in nsswitch.conf. - This function is for non-SSSD setups only. - """ - conf = ipachangeconf.IPAChangeConf("IPA Installer") - conf.setOptionAssignment(':') - - with open(paths.NSSWITCH_CONF, 'r') as f: - current_opts = conf.parse(f) - current_nss_value = conf.findOpts( - current_opts, name='automount', type='option' - )[1] - if current_nss_value is None: - # no automount database present - current_nss_value = False # None cannot be backed up - else: - current_nss_value = current_nss_value['value'] - statestore.backup_state( - 'ipa-client-automount-nsswitch', 'previous-automount', - current_nss_value - ) - - nss_value = ' files ldap' - opts = [ - { - 'name': 'automount', - 'type': 'option', - 'action': 'set', - 'value': nss_value, - }, - {'name': 'empty', 'type': 'empty'}, - ] - conf.changeConf(paths.NSSWITCH_CONF, opts) - - print("Configured %s" % paths.NSSWITCH_CONF) - - def configure_autofs_sssd(fstore, statestore, autodiscover, options): try: sssdconfig = SSSDConfig.SSSDConfig() @@ -339,41 +302,8 @@ def uninstall(fstore, statestore): ] STATES = ['autofs', 'rpcidmapd', 'rpcgssd'] - if statestore.get_state( - 'ipa-client-automount-nsswitch', 'previous-automount' - ) is False: - # Previous nsswitch.conf had no automount database configured - # so remove it. - conf = ipachangeconf.IPAChangeConf("IPA automount installer") - conf.setOptionAssignment(':') - changes = [conf.rmOption('automount')] - conf.changeConf(paths.NSSWITCH_CONF, changes) - tasks.restore_context(paths.NSSWITCH_CONF) - statestore.delete_state( - 'ipa-client-automount-nsswitch', 'previous-automount' - ) - elif statestore.get_state( - 'ipa-client-automount-nsswitch', 'previous-automount' - ) is not None: - nss_value = statestore.get_state( - 'ipa-client-automount-nsswitch', 'previous-automount' - ) - opts = [ - { - 'name': 'automount', - 'type': 'option', - 'action': 'set', - 'value': nss_value, - }, - {'name': 'empty', 'type': 'empty'}, - ] - conf = ipachangeconf.IPAChangeConf("IPA automount installer") - conf.setOptionAssignment(':') - conf.changeConf(paths.NSSWITCH_CONF, opts) - tasks.restore_context(paths.NSSWITCH_CONF) - statestore.delete_state( - 'ipa-client-automount-nsswitch', 'previous-automount' - ) + if not statestore.get_state('autofs', 'sssd'): + tasks.disable_ldap_automount(statestore) if not any(fstore.has_file(f) for f in RESTORE_FILES) or not any( statestore.has_state(s) for s in STATES @@ -627,7 +557,7 @@ def configure_automount(): try: if not options.sssd: - configure_nsswitch(statestore, options) + tasks.enable_ldap_automount(statestore) configure_nfs(fstore, statestore, options) if options.sssd: configure_autofs_sssd(fstore, statestore, autodiscover, options) diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py index 8aa9c5cd0..7fd7d5765 100644 --- a/ipaplatform/base/tasks.py +++ b/ipaplatform/base/tasks.py @@ -32,6 +32,7 @@ from pkg_resources import parse_version from ipaplatform.constants import constants from ipaplatform.paths import paths from ipapython import ipautil +from ipapython.ipachangeconf import IPAChangeConf logger = logging.getLogger(__name__) @@ -337,5 +338,157 @@ class BaseTaskNamespace: """ raise NotImplementedError + def configure_nsswitch_database(self, fstore, database, services, + preserve=True, append=True, + default_value=()): + """ + Edits the specified nsswitch.conf database (e.g. passwd, group, + sudoers) to use the specified service(s). + + Arguments: + fstore - FileStore to backup the nsswitch.conf + database - database configuration that should be ammended, + e.g. 'sudoers' + service - list of services that should be added, e.g. ['sss'] + preserve - if True, the already configured services will be + preserved + + The next arguments modify the behaviour if preserve=True: + append - if True, the services will be appended, if False, + prepended + default_value - list of services that are considered as default (if + the database is not mentioned in nsswitch.conf), + e.g. ['files'] + """ + + # Backup the original version of nsswitch.conf, we're going to edit it + # now + if not fstore.has_file(paths.NSSWITCH_CONF): + fstore.backup_file(paths.NSSWITCH_CONF) + + conf = IPAChangeConf("IPA Installer") + conf.setOptionAssignment(':') + + if preserve: + # Read the existing configuration + with open(paths.NSSWITCH_CONF, 'r') as f: + opts = conf.parse(f) + raw_database_entry = conf.findOpts(opts, 'option', database)[1] + + # Detect the list of already configured services + if not raw_database_entry: + # If there is no database entry, database is not present in + # the nsswitch.conf. Set the list of services to the + # default list, if passed. + configured_services = list(default_value) + else: + configured_services = raw_database_entry[ + 'value'].strip().split() + + # Make sure no service is added if already mentioned in the list + added_services = [s for s in services + if s not in configured_services] + + # Prepend / append the list of new services + if append: + new_value = ' ' + ' '.join(configured_services + + added_services) + else: + new_value = ' ' + ' '.join(added_services + + configured_services) + + else: + # Preserve not set, let's rewrite existing configuration + new_value = ' ' + ' '.join(services) + + # Set new services as sources for database + opts = [ + conf.setOption(database, new_value), + conf.emptyLine(), + ] + + conf.changeConf(paths.NSSWITCH_CONF, opts) + logger.info("Configured %s in %s", database, paths.NSSWITCH_CONF) + + def enable_sssd_sudo(self, fstore): + """Configure nsswitch.conf to use sssd for sudo""" + self.configure_nsswitch_database( + fstore, 'sudoers', ['sss'], + default_value=['files']) + + def enable_ldap_automount(self, statestore): + """ + Point automount to ldap in nsswitch.conf. + This function is for non-SSSD setups only. + """ + conf = IPAChangeConf("IPA Installer") + conf.setOptionAssignment(':') + + with open(paths.NSSWITCH_CONF, 'r') as f: + current_opts = conf.parse(f) + current_nss_value = conf.findOpts( + current_opts, name='automount', type='option' + )[1] + if current_nss_value is None: + # no automount database present + current_nss_value = False # None cannot be backed up + else: + current_nss_value = current_nss_value['value'] + statestore.backup_state( + 'ipa-client-automount-nsswitch', 'previous-automount', + current_nss_value + ) + + nss_value = ' files ldap' + opts = [ + { + 'name': 'automount', + 'type': 'option', + 'action': 'set', + 'value': nss_value, + }, + {'name': 'empty', 'type': 'empty'}, + ] + conf.changeConf(paths.NSSWITCH_CONF, opts) + + logger.info("Configured %s", paths.NSSWITCH_CONF) + + def disable_ldap_automount(self, statestore): + """Disable automount using LDAP""" + if statestore.get_state( + 'ipa-client-automount-nsswitch', 'previous-automount' + ) is False: + # Previous nsswitch.conf had no automount database configured + # so remove it. + conf = IPAChangeConf("IPA automount installer") + conf.setOptionAssignment(':') + changes = [conf.rmOption('automount')] + conf.changeConf(paths.NSSWITCH_CONF, changes) + self.restore_context(paths.NSSWITCH_CONF) + statestore.delete_state( + 'ipa-client-automount-nsswitch', 'previous-automount' + ) + elif statestore.get_state( + 'ipa-client-automount-nsswitch', 'previous-automount' + ) is not None: + nss_value = statestore.get_state( + 'ipa-client-automount-nsswitch', 'previous-automount' + ) + opts = [ + { + 'name': 'automount', + 'type': 'option', + 'action': 'set', + 'value': nss_value, + }, + {'name': 'empty', 'type': 'empty'}, + ] + conf = IPAChangeConf("IPA automount installer") + conf.setOptionAssignment(':') + conf.changeConf(paths.NSSWITCH_CONF, opts) + self.restore_context(paths.NSSWITCH_CONF) + statestore.delete_state( + 'ipa-client-automount-nsswitch', 'previous-automount' + ) tasks = BaseTaskNamespace() diff --git a/ipaplatform/redhat/paths.py b/ipaplatform/redhat/paths.py index 8ccd04bb5..15bdef60f 100644 --- a/ipaplatform/redhat/paths.py +++ b/ipaplatform/redhat/paths.py @@ -39,6 +39,7 @@ class RedHatPathNamespace(BasePathNamespace): AUTHCONFIG = '/usr/sbin/authconfig' AUTHSELECT = '/usr/bin/authselect' SYSCONF_NETWORK = '/etc/sysconfig/network' + NSSWITCH_CONF = '/etc/authselect/user-nsswitch.conf' paths = RedHatPathNamespace() diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py index be0b6414a..e18f6fad5 100644 --- a/ipaplatform/redhat/tasks.py +++ b/ipaplatform/redhat/tasks.py @@ -744,4 +744,23 @@ class RedHatTaskNamespace(BaseTaskNamespace): return filenames + def enable_ldap_automount(self, statestore): + """ + Point automount to ldap in nsswitch.conf. + This function is for non-SSSD setups only. + """ + super(RedHatTaskNamespace, self).enable_ldap_automount(statestore) + + authselect_cmd = [paths.AUTHSELECT, "enable-feature", + "with-custom-automount"] + ipautil.run(authselect_cmd) + + def disable_ldap_automount(self, statestore): + """Disable ldap-based automount""" + super(RedHatTaskNamespace, self).disable_ldap_automount(statestore) + + authselect_cmd = [paths.AUTHSELECT, "disable-feature", + "with-custom-automount"] + ipautil.run(authselect_cmd) + tasks = RedHatTaskNamespace()