diff options
author | Petr Viktorin <pviktori@redhat.com> | 2016-01-14 14:15:49 +0100 |
---|---|---|
committer | Jan Cholasta <jcholast@redhat.com> | 2016-01-27 12:09:02 +0100 |
commit | 840de9bb48b37508e11fc0514761161e7cd0f9ef (patch) | |
tree | 2be322c04c238096923b2216a48249afa5d52bd7 /client/ipa-client-automount | |
parent | 7dae5c09d5a6bf084661511bef4811223da64252 (diff) | |
download | freeipa-840de9bb48b37508e11fc0514761161e7cd0f9ef.tar.gz freeipa-840de9bb48b37508e11fc0514761161e7cd0f9ef.tar.xz freeipa-840de9bb48b37508e11fc0514761161e7cd0f9ef.zip |
Split ipa-client/ into ipaclient/ (Python library) and client/ (C, scripts)
Make ipaclient a Python library like ipapython, ipalib, etc.
Use setup.py instead of autotools for installing it.
Move C client tools, Python scripts, and man pages, to client/.
Remove old, empty or outdated, boilerplate files (NEWS, README, AUTHORS).
Remove /setup-client.py (ipalib/setup.py should be used instead).
Update Makefiles and the spec file accordingly.
https://fedorahosted.org/freeipa/ticket/5638
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Diffstat (limited to 'client/ipa-client-automount')
-rwxr-xr-x | client/ipa-client-automount | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/client/ipa-client-automount b/client/ipa-client-automount new file mode 100755 index 000000000..f06aa7f8d --- /dev/null +++ b/client/ipa-client-automount @@ -0,0 +1,505 @@ +#!/usr/bin/python2 -E +# +# Authors: +# Rob Crittenden <rcritten@redhat.com> +# +# Copyright (C) 2012 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Configure the automount client for ldap. + +from __future__ import print_function + +import sys +import os +import time +import tempfile +import gssapi + +import SSSDConfig +from six.moves.urllib.parse import urlsplit + +from optparse import OptionParser +from ipalib import api, errors +from ipapython import sysrestore +from ipapython import ipautil +from ipaclient import ipadiscovery +from ipaclient import ipachangeconf +from ipapython.ipa_log_manager import root_logger, standard_logging_setup +from ipapython.dn import DN +from ipaplatform.constants import constants +from ipaplatform.tasks import tasks +from ipaplatform import services +from ipaplatform.paths import paths + + +def parse_options(): + usage = "%prog [options]\n" + parser = OptionParser(usage=usage) + parser.add_option("--server", dest="server", help="IPA server") + parser.add_option("--location", dest="location", help="Automount location", + default="default") + parser.add_option("-S", "--no-sssd", dest="sssd", + action="store_false", default=True, + help="Do not configure the client to use SSSD for automount") + parser.add_option("--debug", dest="debug", action="store_true", + default=False, help="enable debugging") + parser.add_option("-U", "--unattended", dest="unattended", + action="store_true", default=False, + help="unattended installation never prompts the user") + parser.add_option("--uninstall", dest="uninstall", action="store_true", + default=False, help="Unconfigure automount") + + options, args = parser.parse_args() + return options, args + +def wait_for_sssd(): + """ + It takes a bit for sssd to get going, lets loop until it is + serving data. + + This function returns nothing. + """ + n = 0 + found = False + time.sleep(1) + while n < 10 and not found: + try: + ipautil.run(["getent", "passwd", "admin@%s" % api.env.realm]) + found = True + except Exception as e: + time.sleep(1) + n = n + 1 + + # This should never happen but if it does, may as well warn the user + if not found: + err_msg = ("Unable to find 'admin' user with " + "'getent passwd admin@%s'!" % api.env.realm) + root_logger.debug(err_msg) + print(err_msg) + print("This may mean that sssd didn't re-start properly after the configuration changes.") + +def configure_xml(fstore): + from lxml import etree + + fstore.backup_file(paths.AUTOFS_LDAP_AUTH_CONF) + + try: + f = open(paths.AUTOFS_LDAP_AUTH_CONF, 'r') + lines = f.read() + f.close() + + saslconf = etree.fromstring(lines) + element = saslconf.xpath('//autofs_ldap_sasl_conf') + root = saslconf.getroottree() + except IOError as e: + root_logger.debug('Unable to open file %s' % e) + root_logger.debug('Creating new from template') + element = [etree.Element('autofs_ldap_sasl_conf')] + root = element[0].getroottree() + + if len(element) != 1: + raise RuntimeError('Unable to parse %s' % paths.AUTOFS_LDAP_AUTH_CONF) + + element[0].set('usetls', 'no') + element[0].set('tlsrequired', 'no') + element[0].set('authrequired', 'yes') + element[0].set('authtype', 'GSSAPI') + element[0].set('clientprinc', 'host/%s@%s' % (api.env.host, api.env.realm)) + + newconf = open(paths.AUTOFS_LDAP_AUTH_CONF, 'w') + try: + root.write(newconf, pretty_print=True, xml_declaration=True, encoding='UTF-8') + newconf.close() + except IOError as e: + print("Unable to write %s: %s" % (paths.AUTOFS_LDAP_AUTH_CONF, e)) + print("Configured %s" % paths.AUTOFS_LDAP_AUTH_CONF) + +def configure_nsswitch(fstore, options): + """ + Point automount to ldap in nsswitch.conf. This function is for non-SSSD + setups only + """ + fstore.backup_file(paths.NSSWITCH_CONF) + + conf = ipachangeconf.IPAChangeConf("IPA Installer") + conf.setOptionAssignment(':') + + 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() + sssdconfig.import_config() + domains = sssdconfig.list_active_domains() + except Exception as e: + sys.exit(e) + + try: + sssdconfig.new_service('autofs') + except SSSDConfig.ServiceAlreadyExists: + pass + except SSSDConfig.ServiceNotRecognizedError: + root_logger.error("Unable to activate the Autofs service in SSSD config.") + root_logger.info( + "Please make sure you have SSSD built with autofs support installed.") + root_logger.info( + "Configure autofs support manually in /etc/sssd/sssd.conf.") + sys.exit("Cannot create the autofs service in sssd.conf") + + sssdconfig.activate_service('autofs') + + domain = None + for name in domains: + domain = sssdconfig.get_domain(name) + try: + provider = domain.get_option('id_provider') + except SSSDConfig.NoOptionError: + continue + if provider == "ipa": + domain.add_provider('ipa', 'autofs') + try: + location = domain.get_option('ipa_automount_location') + sys.exit('An automount location is already configured') + except SSSDConfig.NoOptionError: + domain.set_option('ipa_automount_location', options.location) + break + + if domain is None: + sys.exit('SSSD is not configured.') + + sssdconfig.save_domain(domain) + sssdconfig.write(paths.SSSD_CONF) + statestore.backup_state('autofs', 'sssd', True) + + sssd = services.service('sssd') + sssd.restart() + print("Restarting sssd, waiting for it to become available.") + wait_for_sssd() + +def configure_autofs(fstore, statestore, autodiscover, server, options): + """ + fstore: the FileStore to back up files in + options.server: the IPA server to use + options.location: the Automount location to use + """ + if not autodiscover: + ldap_uri = "ldap://%s" % server + else: + ldap_uri = "ldap:///%s" % api.env.basedn + + search_base = str(DN(('cn', options.location), api.env.container_automount, api.env.basedn)) + replacevars = { + 'MAP_OBJECT_CLASS': 'automountMap', + 'ENTRY_OBJECT_CLASS': 'automount', + 'MAP_ATTRIBUTE': 'automountMapName', + 'ENTRY_ATTRIBUTE': 'automountKey', + 'VALUE_ATTRIBUTE': 'automountInformation', + 'SEARCH_BASE': search_base, + 'LDAP_URI': ldap_uri, + } + + ipautil.backup_config_and_replace_variables(fstore, + paths.SYSCONFIG_AUTOFS, replacevars=replacevars) + tasks.restore_context(paths.SYSCONFIG_AUTOFS) + statestore.backup_state('autofs', 'sssd', False) + + print("Configured %s" % paths.SYSCONFIG_AUTOFS) + +def configure_autofs_common(fstore, statestore, options): + autofs = services.knownservices.autofs + statestore.backup_state('autofs', 'enabled', autofs.is_enabled()) + statestore.backup_state('autofs', 'running', autofs.is_running()) + try: + autofs.restart() + print("Started %s" % autofs.service_name) + except Exception as e: + root_logger.error("%s failed to restart: %s", autofs.service_name, e) + try: + autofs.enable() + except Exception as e: + print("Failed to configure automatic startup of the %s daemon" % (autofs.service_name)) + root_logger.error("Failed to enable automatic startup of the %s daemon: %s" % (autofs.service_name, str(e))) + +def uninstall(fstore, statestore): + print("Restoring configuration") + if fstore.has_file(paths.SYSCONFIG_AUTOFS): + fstore.restore_file(paths.SYSCONFIG_AUTOFS) + if fstore.has_file(paths.NSSWITCH_CONF): + fstore.restore_file(paths.NSSWITCH_CONF) + if fstore.has_file(paths.AUTOFS_LDAP_AUTH_CONF): + fstore.restore_file(paths.AUTOFS_LDAP_AUTH_CONF) + if fstore.has_file(paths.SYSCONFIG_NFS): + fstore.restore_file(paths.SYSCONFIG_NFS) + if fstore.has_file(paths.IDMAPD_CONF): + fstore.restore_file(paths.IDMAPD_CONF) + if statestore.has_state('autofs'): + enabled = statestore.restore_state('autofs', 'enabled') + running = statestore.restore_state('autofs', 'running') + sssd = statestore.restore_state('autofs', 'sssd') + autofs = services.knownservices.autofs + if not enabled: + autofs.disable() + if not running: + autofs.stop() + if sssd: + try: + sssdconfig = SSSDConfig.SSSDConfig() + sssdconfig.import_config() + sssdconfig.deactivate_service('autofs') + domains = sssdconfig.list_active_domains() + for name in domains: + domain = sssdconfig.get_domain(name) + try: + provider = domain.get_option('id_provider') + except SSSDConfig.NoOptionError: + continue + if provider == "ipa": + domain.remove_option('ipa_automount_location') + domain.remove_provider('autofs') + break + sssdconfig.save_domain(domain) + sssdconfig.write(paths.SSSD_CONF) + sssd = services.service('sssd') + sssd.restart() + wait_for_sssd() + except Exception as e: + print('Unable to restore SSSD configuration: %s' % str(e)) + root_logger.debug('Unable to restore SSSD configuration: %s' % str(e)) + if statestore.has_state('rpcidmapd'): + enabled = statestore.restore_state('rpcidmapd', 'enabled') + running = statestore.restore_state('rpcidmapd', 'running') + rpcidmapd = services.knownservices.rpcidmapd + if not enabled: + rpcidmapd.disable() + if not running: + rpcidmapd.stop() + if statestore.has_state('rpcgssd'): + enabled = statestore.restore_state('rpcgssd', 'enabled') + running = statestore.restore_state('rpcgssd', 'running') + rpcgssd = services.knownservices.rpcgssd + if not enabled: + rpcgssd.disable() + if not running: + rpcgssd.stop() + + return 0 + +def configure_nfs(fstore, statestore): + """ + Configure secure NFS + """ + replacevars = { + constants.SECURE_NFS_VAR: 'yes', + } + ipautil.backup_config_and_replace_variables(fstore, + paths.SYSCONFIG_NFS, replacevars=replacevars) + tasks.restore_context(paths.SYSCONFIG_NFS) + + print("Configured %s" % paths.SYSCONFIG_NFS) + + # Prepare the changes + # We need to use IPAChangeConf as simple regexp substitution + # does not cut it here + conf = ipachangeconf.IPAChangeConf("IPA automount installer") + conf.case_insensitive_sections = False + conf.setOptionAssignment(" = ") + conf.setSectionNameDelimiters(("[", "]")) + + changes = [conf.setOption('Domain', api.env.domain)] + section_with_changes = [conf.setSection('General', changes)] + + # Backup the file and apply the changes + fstore.backup_file(paths.IDMAPD_CONF) + conf.changeConf(paths.IDMAPD_CONF, section_with_changes) + + tasks.restore_context(paths.IDMAPD_CONF) + + print("Configured %s" % paths.IDMAPD_CONF) + + rpcidmapd = services.knownservices.rpcidmapd + statestore.backup_state('rpcidmapd', 'enabled', rpcidmapd.is_enabled()) + statestore.backup_state('rpcidmapd', 'running', rpcidmapd.is_running()) + try: + rpcidmapd.restart() + print("Started %s" % rpcidmapd.service_name) + except Exception as e: + root_logger.error("%s failed to restart: %s", rpcidmapd.service_name, e) + try: + rpcidmapd.enable() + except Exception as e: + print("Failed to configure automatic startup of the %s daemon" % (rpcidmapd.service_name)) + root_logger.error("Failed to enable automatic startup of the %s daemon: %s" % (rpcidmapd.service_name, str(e))) + + rpcgssd = services.knownservices.rpcgssd + statestore.backup_state('rpcgssd', 'enabled', rpcgssd.is_enabled()) + statestore.backup_state('rpcgssd', 'running', rpcgssd.is_running()) + try: + rpcgssd.restart() + print("Started %s" % rpcgssd.service_name) + except Exception as e: + root_logger.error("%s failed to restart: %s", rpcgssd.service_name, e) + try: + rpcgssd.enable() + except Exception as e: + print("Failed to configure automatic startup of the %s daemon" % (rpcgssd.service_name)) + root_logger.error("Failed to enable automatic startup of the %s daemon: %s" % (rpcgssd.service_name, str(e))) + +def main(): + + fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) + statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE) + if not fstore.has_files() and not os.path.exists(paths.IPA_DEFAULT_CONF): + sys.exit('IPA client is not configured on this system.\n') + + options, args = parse_options() + + standard_logging_setup( + paths.IPACLIENT_INSTALL_LOG, verbose=False, debug=options.debug, + filemode='a', console_format='%(message)s') + + cfg = dict( + context='cli_installer', + in_server=False, + debug=options.debug, + verbose=0, + ) + + api.bootstrap(**cfg) + api.finalize() + + ca_cert_path = None + if os.path.exists(paths.IPA_CA_CRT): + ca_cert_path = paths.IPA_CA_CRT + + if options.uninstall: + return uninstall(fstore, statestore) + + if statestore.has_state('autofs'): + sys.exit('automount is already configured on this system.\n') + + autodiscover = False + servers = [] + ds = ipadiscovery.IPADiscovery() + if not options.server: + print("Searching for IPA server...") + ret = ds.search(ca_cert_path=ca_cert_path) + root_logger.debug('Executing DNS discovery') + if ret == ipadiscovery.NO_LDAP_SERVER: + root_logger.debug('Autodiscovery did not find LDAP server') + s = urlsplit(api.env.xmlrpc_uri) + server = [s.netloc] + root_logger.debug('Setting server to %s' % s.netloc) + else: + autodiscover = True + if not ds.servers: + sys.exit('Autodiscovery was successful but didn\'t return a server') + root_logger.debug('Autodiscovery success, possible servers %s' % ','.join(ds.servers)) + server = ds.servers[0] + else: + server = options.server + root_logger.debug("Verifying that %s is an IPA server" % server) + ldapret = ds.ipacheckldap(server, api.env.realm, ca_cert_path) + if ldapret[0] == ipadiscovery.NO_ACCESS_TO_LDAP: + print("Anonymous access to the LDAP server is disabled.") + print("Proceeding without strict verification.") + print("Note: This is not an error if anonymous access has been explicitly restricted.") + elif ldapret[0] == ipadiscovery.NO_TLS_LDAP: + root_logger.warning("Unencrypted access to LDAP is not supported.") + elif ldapret[0] != 0: + sys.exit('Unable to confirm that %s is an IPA server' % server) + + if not autodiscover: + print("IPA server: %s" % server) + root_logger.debug('Using fixed server %s' % server) + else: + print("IPA server: DNS discovery") + root_logger.debug('Configuring to use DNS discovery') + + search_base = str(DN(('cn', options.location), api.env.container_automount, api.env.basedn)) + print("Location: %s" % options.location) + root_logger.debug('Using automount location %s' % options.location) + + ccache_dir = tempfile.mkdtemp() + ccache_name = os.path.join(ccache_dir, 'ccache') + try: + try: + host_princ = str('host/%s@%s' % (api.env.host, api.env.realm)) + ipautil.kinit_keytab(host_princ, paths.KRB5_KEYTAB, ccache_name) + os.environ['KRB5CCNAME'] = ccache_name + except gssapi.exceptions.GSSError as e: + sys.exit("Failed to obtain host TGT: %s" % e) + # Now we have a TGT, connect to IPA + try: + api.Backend.rpcclient.connect() + except errors.KerberosError as e: + sys.exit('Cannot connect to the server due to ' + str(e)) + try: + # Use the RPC directly so older servers are supported + result = api.Backend.rpcclient.forward( + 'automountlocation_show', + ipautil.fsdecode(options.location), + version=u'2.0', + ) + except errors.VersionError as e: + sys.exit('This client is incompatible: ' + str(e)) + except errors.NotFound: + sys.exit("Automount location '%s' does not exist" % options.location) + except errors.PublicError as e: + sys.exit("Cannot connect to the server due to generic error: %s" % str(e)) + finally: + os.remove(ccache_name) + os.rmdir(ccache_dir) + + if not options.unattended and not ipautil.user_input("Continue to configure the system with these values?", False): + sys.exit("Installation aborted") + + try: + if not options.sssd: + configure_nsswitch(fstore, options) + configure_nfs(fstore, statestore) + if options.sssd: + configure_autofs_sssd(fstore, statestore, autodiscover, options) + else: + configure_xml(fstore) + configure_autofs(fstore, statestore, autodiscover, server, options) + configure_autofs_common(fstore, statestore, options) + except Exception as e: + root_logger.debug('Raised exception %s' % e) + print("Installation failed. Rolling back changes.") + uninstall(fstore, statestore) + return 1 + + return 0 + +try: + if not os.geteuid()==0: + sys.exit("\nMust be run as root\n") + + sys.exit(main()) +except SystemExit as e: + sys.exit(e) +except RuntimeError as e: + sys.exit(e) +except (KeyboardInterrupt, EOFError): + sys.exit(1) |