summaryrefslogtreecommitdiffstats
path: root/client/ipa-client-automount
diff options
context:
space:
mode:
authorPetr Viktorin <pviktori@redhat.com>2016-01-14 14:15:49 +0100
committerJan Cholasta <jcholast@redhat.com>2016-01-27 12:09:02 +0100
commit840de9bb48b37508e11fc0514761161e7cd0f9ef (patch)
tree2be322c04c238096923b2216a48249afa5d52bd7 /client/ipa-client-automount
parent7dae5c09d5a6bf084661511bef4811223da64252 (diff)
downloadfreeipa-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-xclient/ipa-client-automount505
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)