def decorated(installer): success = False try: func(installer) success = True except KeyboardInterrupt: ds = installer._ds print("\nCleaning up...") if ds: print("Removing configuration for %s instance" % ds.serverid) ds.stop() if ds.serverid: try: dsinstance.remove_ds_instance(ds.serverid) except ipautil.CalledProcessError: root_logger.error("Failed to remove DS instance. You " "may need to remove instance data " "manually") raise ScriptError() finally: if not success and installer._installation_cleanup: # Do a cautious clean up as we don't know what failed and # what is the state of the environment try: installer._fstore.restore_file(paths.HOSTS) except Exception: pass
def check_client_configuration(): """ Check if IPA client is configured on the system. """ fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) if not fstore.has_files() and not os.path.exists(paths.IPA_DEFAULT_CONF): raise ScriptError('IPA client is not configured on this system')
def uninstall_check(options): """Check if the host is CRL generation master""" # Skip the checks if the host is not a CA instance ca = cainstance.CAInstance(api.env.realm) if not (api.Command.ca_is_enabled()['result'] and cainstance.is_ca_installed_locally()): return # skip the checks if the host is the last master ipa_config = api.Command.config_show()['result'] ipa_masters = ipa_config.get('ipa_master_server', []) if len(ipa_masters) <= 1: return try: crlgen_enabled = ca.is_crlgen_enabled() except cainstance.InconsistentCRLGenConfigException: # If config is inconsistent, let's be safe and act as if # crl gen was enabled crlgen_enabled = True if crlgen_enabled: print("Deleting this server will leave your installation " "without a CRL generation master.") if (options.unattended and not options.ignore_last_of_role) or \ not (options.unattended or ipautil.user_input( "Are you sure you want to continue with the uninstall " "procedure?", False)): raise ScriptError("Aborting uninstall operation.")
def check_client_configuration(): """ Check if IPA client is configured on the system. """ if (not os.path.isfile(paths.IPA_DEFAULT_CONF) or not os.path.isdir(paths.IPA_CLIENT_SYSRESTORE) or not os.listdir(paths.IPA_CLIENT_SYSRESTORE)): raise ScriptError('IPA client is not configured on this system')
def check_reverse_zones(ip_addresses, reverse_zones, options, unattended, search_reverse_zones=False): checked_reverse_zones = [] if (not options.no_reverse and not reverse_zones and not options.auto_reverse): if unattended: options.no_reverse = True else: options.no_reverse = not create_reverse() # shortcut if options.no_reverse: return [] # verify zones passed in options for rz in reverse_zones: # isn't the zone managed by someone else if not options.allow_zone_overlap: try: dnsutil.check_zone_overlap(rz) except ValueError as e: msg = "Reverse zone %s will not be used: %s" % (rz, e) if unattended: raise ScriptError(msg) else: logger.warning('%s', msg) continue checked_reverse_zones.append(normalize_zone(rz)) # check that there is reverse zone for every IP ips_missing_reverse = [] for ip in ip_addresses: if search_reverse_zones and find_reverse_zone(str(ip)): # reverse zone is already in LDAP continue for rz in checked_reverse_zones: if verify_reverse_zone(rz, ip): # reverse zone was entered by user break else: ips_missing_reverse.append(ip) # create reverse zone for IP addresses that does not have one for (ip, rz) in get_auto_reverse_zones(ips_missing_reverse, options.allow_zone_overlap): if options.auto_reverse: logger.info("Reverse zone %s will be created", rz) checked_reverse_zones.append(rz) elif unattended: logger.warning("Missing reverse record for IP address %s", ip) else: if ipautil.user_input("Do you want to create reverse zone for IP " "%s" % ip, True): rz = read_reverse_zone(rz, str(ip), options.allow_zone_overlap) checked_reverse_zones.append(rz) return checked_reverse_zones
def ensure_enrolled(installer): args = [paths.IPA_CLIENT_INSTALL, "--unattended"] stdin = None nolog = [] if installer.domain_name: args.extend(["--domain", installer.domain_name]) if installer.server: args.extend(["--server", installer.server]) if installer.realm_name: args.extend(["--realm", installer.realm_name]) if installer.host_name: args.extend(["--hostname", installer.host_name]) if installer.password: args.extend(["--password", installer.password]) nolog.append(installer.password) else: if installer.admin_password: # Always set principal if password was set explicitly, # the password itself gets passed directly via stdin args.extend(["--principal", installer.principal or "admin"]) stdin = installer.admin_password if installer.keytab: args.extend(["--keytab", installer.keytab]) if installer.no_dns_sshfp: args.append("--no-dns-sshfp") if installer.ssh_trust_dns: args.append("--ssh-trust-dns") if installer.no_ssh: args.append("--no-ssh") if installer.no_sshd: args.append("--no-sshd") if installer.mkhomedir: args.append("--mkhomedir") if installer.force_join: args.append("--force-join") if installer.no_ntp: args.append("--no-ntp") if installer.ip_addresses: for ip in installer.ip_addresses: # installer.ip_addresses is of type [CheckedIPAddress] args.extend(("--ip-address", str(ip))) if installer.ntp_servers: for server in installer.ntp_servers: args.extend(("--ntp-server", server)) if installer.ntp_pool: args.extend(("--ntp-pool", installer.ntp_pool)) try: # Call client install script service.print_msg("Configuring client side components") installer._enrollment_performed = True ipautil.run(args, stdin=stdin, nolog=nolog, redirect_output=True) print() except ipautil.CalledProcessError: raise ScriptError("Configuration of client side components failed!")
def execute(self, update=True): if update: self.add_option("update") args = self.build_args() try: ipautil.run([paths.AUTHCONFIG] + args) except ipautil.CalledProcessError: raise ScriptError("Failed to execute authconfig command")
def create_replica_config(dirman_password, filename, options): top_dir = None try: top_dir, dir = expand_replica_info(filename, dirman_password) except Exception as e: logger.error("Failed to decrypt or open the replica file.") raise ScriptError( "ERROR: Failed to decrypt or open the replica file.\n" "Verify you entered the correct Directory Manager password.") config = ReplicaConfig(top_dir) read_replica_info(dir, config) logger.debug( 'Installing replica file with version %d ' '(0 means no version in prepared file).', config.version) if config.version and config.version > version.NUM_VERSION: logger.error( 'A replica file from a newer release (%d) cannot be installed on ' 'an older version (%d)', config.version, version.NUM_VERSION) raise ScriptError() config.dirman_password = dirman_password try: host = get_host_name(options.no_host_dns) except BadHostError as e: logger.error("%s", str(e)) raise ScriptError() if config.host_name != host: try: print( "This replica was created for '%s' but this machine is named '%s'" % (config.host_name, host)) if not ipautil.user_input("This may cause problems. Continue?", False): logger.debug( "Replica was created for %s but machine is named %s " "User chose to exit", config.host_name, host) sys.exit(0) config.host_name = host print("") except KeyboardInterrupt: logger.debug("Keyboard Interrupt") raise ScriptError(rval=0) config.dir = dir config.ca_ds_port = read_replica_info_dogtag_port(config.dir) return config
def check_remote_fips_mode(client, local_fips_mode): """ Verify remote server's fips-mode is the same as this server's fips-mode :param client: RPC client :param local_fips_mode: boolean indicating whether FIPS mode is turned on :raises: ScriptError: if the checks fails """ env = client.forward(u'env', u'fips_mode')['result'] remote_fips_mode = env.get('fips_mode', False) if local_fips_mode != remote_fips_mode: if local_fips_mode: raise ScriptError( "Cannot join FIPS-enabled replica into existing topology: " "FIPS is not enabled on the master server.") else: raise ScriptError( "Cannot join replica into existing FIPS-enabled topology: " "FIPS has to be enabled locally first.")
def get_group_info(self): assert api.isdone("finalize") group = self.safe_options.group if group is None: return None try: result = api.Command.group_show(group, no_members=True) return result["result"] except errors.NotFound: raise ScriptError(f"Unknown users group '{group}'.")
def check_dirsrv(unattended): (ds_unsecure, ds_secure) = dsinstance.check_ports() if not ds_unsecure or not ds_secure: msg = ("IPA requires ports 389 and 636 for the Directory Server.\n" "These are currently in use:\n") if not ds_unsecure: msg += "\t389\n" if not ds_secure: msg += "\t636\n" raise ScriptError(msg)
def check_client_configuration(): """ Check if IPA client is configured on the system. Hardcode return code to avoid recursive imports """ if (not os.path.isfile(paths.IPA_DEFAULT_CONF) or not os.path.isdir(paths.IPA_CLIENT_SYSRESTORE) or not os.listdir(paths.IPA_CLIENT_SYSRESTORE)): raise ScriptError('IPA client is not configured on this system', 2) # CLIENT_NOT_CONFIGURED
def configure_certmonger(): dbus = services.knownservices.dbus try: dbus.start() except Exception as e: raise ScriptError("dbus service unavailable: %s" % str(e), rval=3) # Ensure that certmonger has been started at least once to generate the # cas files in /var/lib/certmonger/cas. cmonger = services.knownservices.certmonger try: cmonger.restart() except Exception as e: raise ScriptError("Certmonger service unavailable: %s" % str(e), rval=3) try: cmonger.enable() except Exception as e: raise ScriptError("Failed to enable Certmonger: %s" % str(e), rval=3)
def restart(self, instance=''): api.Backend.ldap2.disconnect() try: super(DsInstance, self).restart(instance) if not is_ds_running(instance): root_logger.critical("Failed to restart the directory server. See the installation log for details.") raise ScriptError() except SystemExit as e: raise e except Exception as e: # TODO: roll back here? root_logger.critical("Failed to restart the directory server (%s). See the installation log for details." % e) api.Backend.ldap2.connect()
def configure_certmonger(): dbus = services.knownservices.dbus if not dbus.is_running(): # some platforms protect dbus with RefuseManualStart=True try: dbus.start() except Exception as e: raise ScriptError("dbus service unavailable: %s" % str(e), rval=3) # Ensure that certmonger has been started at least once to generate the # cas files in /var/lib/certmonger/cas. cmonger = services.knownservices.certmonger try: cmonger.restart() except Exception as e: raise ScriptError("Certmonger service unavailable: %s" % str(e), rval=3) try: cmonger.enable() except Exception as e: raise ScriptError("Failed to enable Certmonger: %s" % str(e), rval=3)
def decorated(installer): try: try: func(installer) except BaseException: remove_replica_info_dir(installer) raise except KeyboardInterrupt: raise ScriptError() except Exception: print( "Your system may be partly configured.\n" "Run /usr/sbin/ipa-server-install --uninstall to clean up.\n") raise
def check_remote_version(client, local_version): """ Verify remote server's version is not higher than this server's version :param client: RPC client :param local_version: API version of local server :raises: ScriptError: if the checks fails """ env = client.forward(u'env', u'version')['result'] remote_version = parse_version(env['version']) if remote_version > local_version: raise ScriptError( "Cannot install replica of a server of higher version ({}) than " "the local version ({})".format(remote_version, local_version))
def check_available_memory(ca=False): """ Raise an exception if there isn't enough memory for IPA to install. In a container then psutil will most likely return the host memory and not the container. If in a container use the cgroup values which also may not be constrained but it's the best approximation. 2GB is the rule-of-thumb minimum but the server is installable with less. The CA uses ~150MB in a fresh install. Use Kb instead of KiB to leave a bit of slush for the OS """ minimum_suggested = 1000 * 1000 * 1000 * 1.6 if not ca: minimum_suggested -= 150 * 1000 * 1000 if in_container(): if os.path.exists('/sys/fs/cgroup/memory/memory.limit_in_bytes' ) and os.path.exists( '/sys/fs/cgroup/memory/memory.usage_in_bytes'): with open('/sys/fs/cgroup/memory/memory.limit_in_bytes') as fd: limit = int(fd.readline()) with open('/sys/fs/cgroup/memory/memory.usage_in_bytes') as fd: used = int(fd.readline()) available = limit - used else: raise ScriptError( "Unable to determine the amount of available RAM") else: available = psutil.virtual_memory().available logger.debug("Available memory is %sB", available) if available < minimum_suggested: raise ScriptError("Less than the minimum 1.6GB of RAM is available, " "%.2fGB available" % (available / (1024 * 1024 * 1024)))
def install_replica_kra(config, postinstall=False): """ Install a KRA on a replica. There are two modes of doing this controlled: - While the replica is being installed - Post-replica installation config is a ReplicaConfig object Returns a KRA instance """ # note that the cacert.p12 file is regenerated during the # ipa-replica-prepare process and should include all the certs # for the CA and KRA krafile = config.dir + "/cacert.p12" if not ipautil.file_exists(krafile): raise RuntimeError("Unable to clone KRA." " cacert.p12 file not found in replica file") _kra = KRAInstance(config.realm_name) _kra.dm_password = config.dirman_password _kra.subject_base = config.subject_base if _kra.is_installed(): raise ScriptError("A KRA is already configured on this system.") _kra.configure_instance(config.realm_name, config.host_name, config.dirman_password, config.dirman_password, pkcs12_info=(krafile, ), master_host=config.master_host_name, subject_base=config.subject_base) # Restart httpd since we changed it's config and added ipa-pki-proxy.conf if postinstall: services.knownservices.httpd.restart() # The dogtag DS instance needs to be restarted after installation. # The procedure for this is: stop dogtag, stop DS, start DS, start # dogtag service.print_msg("Restarting the directory and KRA servers") _kra.stop('pki-tomcat') services.knownservices.dirsrv.restart() _kra.start('pki-tomcat') return _kra
def remove_master_from_managed_topology(api_instance, options): try: # we may force the removal server_del_options = dict( force=True, ignore_topology_disconnect=options.ignore_topology_disconnect, ignore_last_of_role=options.ignore_last_of_role) replication.run_server_del_as_cli(api_instance, api_instance.env.host, **server_del_options) except errors.ServerRemovalError as e: raise ScriptError(str(e)) except Exception as e: # if the master was already deleted we will just get a warning root_logger.warning("Failed to delete master: {}".format(e))
def run(self): api.bootstrap(in_server=True, confdir=paths.ETC_IPA) api.finalize() try: api.Backend.ldap2.connect() # ensure DS is up # If required, enable Schema compat plugin on users/groups if self.options.enable_compat: try: self._enable_compat_tree() except Exception as e: raise ScriptError("Enabling Schema Compatibility plugin " "failed: {}".format(e)) # Restart 389-ds and sssd logger.info("Restarting Directory Server") try: services.knownservices.dirsrv.restart() except Exception as e: raise ScriptError( "Directory Server restart was unsuccessful: {}".format(e)) logger.info("Restarting SSSD service") try: sssd = services.service('sssd', api) sssd.restart() except CalledProcessError as e: raise ScriptError( "SSSD service restart was unsuccessful: {}".format(e)) finally: if api.Backend.ldap2.isconnected(): api.Backend.ldap2.disconnect() return 0
def check_client_configuration(env=None): """ Check if IPA client is configured on the system. Hardcode return code to avoid recursive imports """ CLIENT_NOT_CONFIGURED = 2 if env is not None and env.confdir != paths.ETC_IPA: # custom IPA conf dir, check for custom conf_default if os.path.isfile(env.conf_default): return True else: raise ScriptError( f'IPA client is not configured on this system (confdir ' f'{env.confdir} is missing {env.conf_default})', CLIENT_NOT_CONFIGURED) elif (os.path.isfile(paths.IPA_DEFAULT_CONF) and os.path.isfile( os.path.join(paths.IPA_CLIENT_SYSRESTORE, 'sysrestore.state'))): # standard installation, check for config and client sysrestore state return True else: # client not configured raise ScriptError('IPA client is not configured on this system', CLIENT_NOT_CONFIGURED)
def backup(self, path): try: ipautil.run([paths.AUTHCONFIG, "--savebackup", path]) except ipautil.CalledProcessError: raise ScriptError("Failed to execute authconfig command") # do not backup these files since we don't want to mess with # users/groups during restore. Authconfig doesn't seem to mind about # having them deleted from backup dir files_to_remove = [os.path.join(path, f) for f in FILES_TO_NOT_BACKUP] for filename in files_to_remove: try: os.remove(filename) except OSError: pass
def check_server_configuration(): """ Check if IPA server is configured on the system. This is done by checking if there are system restore (uninstall) files present on the system. Note that this check can only be run with root privileges. When IPA is not configured, this function raises a RuntimeError exception. Most convenient use case for the function is in install tools that require configured IPA for its function. """ if not facts.is_ipa_configured(): raise ScriptError("IPA is not configured on this system.", rval=SERVER_NOT_CONFIGURED)
def install_ca_cert(ldap, base_dn, realm, cafile, destfile=paths.IPA_CA_CRT): try: try: certs = certstore.get_ca_certs(ldap, base_dn, realm, False) except errors.NotFound: try: shutil.copy(cafile, destfile) except shutil.Error: # cafile == IPA_CA_CRT pass else: certs = [c[0] for c in certs if c[2] is not False] x509.write_certificate_list(certs, destfile, mode=0o644) except Exception as e: raise ScriptError("error copying files: " + str(e)) return destfile
def check_domain_level_is_supported(current): """Check that the given domain level is supported by this server version. :raises: ScriptError if DL is out of supported range for this IPA version. """ under_lower_bound = current < constants.MIN_DOMAIN_LEVEL above_upper_bound = current > constants.MAX_DOMAIN_LEVEL if under_lower_bound or above_upper_bound: message = ("This version of FreeIPA does not support " "the Domain Level which is currently set for " "this domain. The Domain Level needs to be " "raised before installing a replica with " "this version is allowed to be installed " "within this domain.") logger.error("%s", message) raise ScriptError(message, rval=3)
def read_realm_name(domain_name, unattended): print("The kerberos protocol requires a Realm name to be defined.") print("This is typically the domain name converted to uppercase.") print("") if unattended: return domain_name.upper() realm_name = str( user_input("Please provide a realm name", domain_name.upper())) upper_dom = realm_name.upper() if upper_dom != realm_name: print("An upper-case realm name is required.") if not user_input( "Do you want to use " + upper_dom + " as realm name?", True): raise ScriptError( "An upper-case realm name is required. Unable to continue.") else: realm_name = upper_dom print("") return realm_name
def common_check(no_ntp): tasks.check_ipv6_stack_enabled() tasks.check_selinux_status() check_ldap_conf() if is_ipa_configured(): raise ScriptError( "IPA server is already configured on this system.\n" "If you want to reinstall the IPA server, please uninstall " "it first using 'ipa-server-install --uninstall'.") check_dirsrv() if not no_ntp: try: ipaclient.install.timeconf.check_timedate_services() except ipaclient.install.timeconf.NTPConflictingService as e: print("WARNING: conflicting time&date synchronization service " "'{svc}' will\nbe disabled in favor of chronyd\n".format( svc=e.conflicting_service)) except ipaclient.install.timeconf.NTPConfigurationError: pass
def set_and_check_netbios_name(netbios_name, unattended, api): """ Depending if trust in already configured or not a given NetBIOS domain name must be handled differently. If trust is not configured the given NetBIOS is used or the NetBIOS is generated if none was given on the command line. If trust is already configured the given NetBIOS name is used to reset the stored NetBIOS name it it differs from the current one. """ flat_name_attr = 'ipantflatname' cur_netbios_name = None gen_netbios_name = None reset_netbios_name = False entry = None try: entry = api.Backend.ldap2.get_entry( DN(('cn', api.env.domain), api.env.container_cifsdomains, ipautil.realm_to_suffix(api.env.realm)), [flat_name_attr]) except errors.NotFound: # trust not configured pass else: cur_netbios_name = entry.get(flat_name_attr)[0] if cur_netbios_name and not netbios_name: # keep the current NetBIOS name netbios_name = cur_netbios_name reset_netbios_name = False elif cur_netbios_name and cur_netbios_name != netbios_name: # change the NetBIOS name print("Current NetBIOS domain name is %s, new name is %s.\n" % (cur_netbios_name, netbios_name)) print("Please note that changing the NetBIOS name might " "break existing trust relationships.") if unattended: reset_netbios_name = True print("NetBIOS domain name will be changed to %s.\n" % netbios_name) else: print("Say 'yes' if the NetBIOS shall be changed and " "'no' if the old one shall be kept.") reset_netbios_name = ipautil.user_input( 'Do you want to reset the NetBIOS domain name?', default=False, allow_empty=False) if not reset_netbios_name: netbios_name = cur_netbios_name elif cur_netbios_name and cur_netbios_name == netbios_name: # keep the current NetBIOS name reset_netbios_name = False elif not cur_netbios_name: if not netbios_name: gen_netbios_name = adtrustinstance.make_netbios_name( api.env.domain) if entry is not None: # Fix existing trust configuration print("Trust is configured but no NetBIOS domain name found, " "setting it now.") reset_netbios_name = True else: # initial trust configuration reset_netbios_name = False else: # all possible cases should be covered above raise Exception('Unexpected state while checking NetBIOS domain name') if unattended and netbios_name is None and gen_netbios_name: netbios_name = gen_netbios_name if not adtrustinstance.check_netbios_name(netbios_name): if unattended: netbios_name_error(netbios_name) raise ScriptError("Aborting installation.") else: if netbios_name: netbios_name_error(netbios_name) netbios_name = None if not unattended and not netbios_name: netbios_name = read_netbios_name(gen_netbios_name) return (netbios_name, reset_netbios_name)
def install_check(standalone, options, api): global netbios_name global reset_netbios_name if standalone: check_for_installed_deps() realm_not_matching_domain = (api.env.domain.upper() != api.env.realm) if realm_not_matching_domain: print("WARNING: Realm name does not match the domain name.\n" "You will not be able to estabilish trusts with Active " "Directory unless\nthe realm name of the IPA server matches its " "domain name.\n\n") if not options.unattended: if not ipautil.user_input("Do you wish to continue?", default=False, allow_empty=False): raise ScriptError("Aborting installation.") # Check if /etc/samba/smb.conf already exists. In case it was not generated # by IPA, print a warning that we will break existing configuration. if adtrustinstance.ipa_smb_conf_exists(): if not options.unattended: print("IPA generated smb.conf detected.") if not ipautil.user_input( "Overwrite smb.conf?", default=False, allow_empty=False): raise ScriptError("Aborting installation.") elif os.path.exists(paths.SMB_CONF): print("WARNING: The smb.conf already exists. Running " "ipa-adtrust-install will break your existing samba " "configuration.\n\n") if not options.unattended: if not ipautil.user_input("Do you wish to continue?", default=False, allow_empty=False): raise ScriptError("Aborting installation.") elif os.path.exists(paths.SMB_CONF): print("WARNING: The smb.conf already exists. Running " "ipa-adtrust-install will break your existing samba " "configuration.\n\n") if not options.unattended: if not ipautil.user_input("Do you wish to continue?", default=False, allow_empty=False): raise ScriptError("Aborting installation.") if not options.unattended and not options.enable_compat: options.enable_compat = enable_compat_tree() netbios_name, reset_netbios_name = set_and_check_netbios_name( options.netbios_name, options.unattended, api) if not options.add_sids: # The filter corresponds to ipa_sidgen_task.c LDAP search filter filter = '(&(objectclass=ipaobject)(!(objectclass=mepmanagedentry))' \ '(|(objectclass=posixaccount)(objectclass=posixgroup)' \ '(objectclass=ipaidobject))(!(ipantsecurityidentifier=*)))' base_dn = api.env.basedn try: root_logger.debug( "Searching for objects with missing SID with " "filter=%s, base_dn=%s", filter, base_dn) entries, _truncated = api.Backend.ldap2.find_entries( filter=filter, base_dn=base_dn, attrs_list=['']) except errors.NotFound: # All objects have SIDs assigned pass except (errors.DatabaseError, errors.NetworkError) as e: print("Could not retrieve a list of objects that need a SID " "identifier assigned:") print(unicode(e)) else: object_count = len(entries) if object_count > 0: print("") print("WARNING: %d existing users or groups do not have " "a SID identifier assigned." % len(entries)) print("Installer can run a task to have ipa-sidgen " "Directory Server plugin generate") print("the SID identifier for all these users. Please note, " "the in case of a high") print("number of users and groups, the operation might " "lead to high replication") print("traffic and performance degradation. Refer to " "ipa-adtrust-install(1) man page") print("for details.") print("") if options.unattended: print("Unattended mode was selected, installer will " "NOT run ipa-sidgen task!") else: if ipautil.user_input( "Do you want to run the ipa-sidgen task?", default=False, allow_empty=False): options.add_sids = True