From 3cb42e33c06fb66702ecfd34ddcad9b548f23132 Mon Sep 17 00:00:00 2001 From: Jan Cholasta Date: Thu, 28 May 2015 06:06:05 +0200 Subject: install: Move ipa-replica-install code into a module https://fedorahosted.org/freeipa/ticket/4468 Reviewed-By: Martin Basti --- install/tools/ipa-replica-install | 606 +-------------------------- ipaserver/install/server/__init__.py | 2 + ipaserver/install/server/replicainstall.py | 640 +++++++++++++++++++++++++++++ 3 files changed, 652 insertions(+), 596 deletions(-) create mode 100644 ipaserver/install/server/replicainstall.py diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install index 1df782b73..775312b87 100755 --- a/install/tools/ipa-replica-install +++ b/install/tools/ipa-replica-install @@ -19,46 +19,23 @@ # import sys -import socket - -import os, pwd, shutil +import os from optparse import OptionGroup -from contextlib import contextmanager - -import dns.resolver -import dns.reversename -import dns.exception from ipapython import ipautil - -from ipaserver.install import dsinstance, installutils, krbinstance, service -from ipaserver.install import bindinstance, httpinstance, ntpinstance -from ipaserver.install import memcacheinstance, dnskeysyncinstance -from ipaserver.install import otpdinstance -from ipaserver.install.replication import replica_conn_check, ReplicationManager -from ipaserver.install.installutils import ( - create_replica_config, private_ccache) -from ipaserver.plugins.ldap2 import ldap2 -from ipaserver.install import cainstance -from ipaserver.install import kra -from ipaserver.install import dns as dns_installer -from ipalib import api, create_api, errors, util, certstore, x509 -from ipalib import constants +from ipaserver.install import installutils +from ipaserver.install import server from ipapython import version from ipapython.config import IPAOptionParser -from ipapython import sysrestore -from ipapython.ipa_log_manager import * -from ipapython import dogtag +from ipapython.ipa_log_manager import root_logger, standard_logging_setup from ipapython.dn import DN -import ipaclient.ntpconf -from ipaplatform.tasks import tasks -from ipaplatform import services from ipaplatform.paths import paths log_file_name = paths.IPAREPLICA_INSTALL_LOG DIRMAN_DN = DN(('cn', 'directory manager')) REPLICA_INFO_TOP_DIR = None + def parse_options(): usage = "%prog [options] REPLICA_FILE" parser = IPAOptionParser(usage=usage, version=version.VERSION) @@ -156,256 +133,8 @@ def parse_options(): return safe_options, options, args[0] -def get_dirman_password(): - return installutils.read_password("Directory Manager (existing master)", confirm=False, validate=False) - -def set_owner(config, dir): - pw = pwd.getpwnam(dsinstance.DS_USER) - os.chown(dir, pw.pw_uid, pw.pw_gid) - - -def make_pkcs12_info(directory, cert_name, password_name): - """Make pkcs12_info - - :param directory: Base directory (config.dir) - :param cert_name: Cert filename (e.g. "dscert.p12") - :param password_name: Cert filename (e.g. "dirsrv_pin.txt") - :return: a (full cert path, password) tuple, or None if cert is not found - """ - cert_path = os.path.join(directory, cert_name) - if ipautil.file_exists(cert_path): - password_file = os.path.join(directory, password_name) - password = open(password_file).read().strip() - return cert_path, password - else: - return None - - -def install_replica_ds(config): - dsinstance.check_ports() - - # if we have a pkcs12 file, create the cert db from - # that. Otherwise the ds setup will create the CA - # cert - pkcs12_info = make_pkcs12_info(config.dir, "dscert.p12", "dirsrv_pin.txt") - - ds = dsinstance.DsInstance() - ds.create_replica( - realm_name=config.realm_name, - master_fqdn=config.master_host_name, - fqdn=config.host_name, - domain_name=config.domain_name, - dm_password=config.dirman_password, - subject_base=config.subject_base, - pkcs12_info=pkcs12_info, - ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12"), - ca_file=config.dir + "/ca.crt", - ) - - return ds - -def install_krb(config, setup_pkinit=False): - krb = krbinstance.KrbInstance() - - #pkinit files - pkcs12_info = make_pkcs12_info(config.dir, "pkinitcert.p12", - "pkinit_pin.txt") - - krb.create_replica(config.realm_name, - config.master_host_name, config.host_name, - config.domain_name, config.dirman_password, - setup_pkinit, pkcs12_info) - - return krb - - -def install_ca_cert(ldap, base_dn, realm, cafile): - try: - try: - certs = certstore.get_ca_certs(ldap, base_dn, realm, False) - except errors.NotFound: - shutil.copy(cafile, constants.CACERT) - else: - certs = [c[0] for c in certs if c[2] is not False] - x509.write_certificate_list(certs, constants.CACERT) - - os.chmod(constants.CACERT, 0444) - except Exception, e: - print "error copying files: " + str(e) - sys.exit(1) - -def install_http(config, auto_redirect): - # if we have a pkcs12 file, create the cert db from - # that. Otherwise the ds setup will create the CA - # cert - pkcs12_info = make_pkcs12_info(config.dir, "httpcert.p12", "http_pin.txt") - - memcache = memcacheinstance.MemcacheInstance() - memcache.create_instance('MEMCACHE', config.host_name, config.dirman_password, ipautil.realm_to_suffix(config.realm_name)) - - http = httpinstance.HTTPInstance() - http.create_instance( - config.realm_name, config.host_name, config.domain_name, - config.dirman_password, False, pkcs12_info, - auto_redirect=auto_redirect, ca_file = config.dir + "/ca.crt", - ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12")) - - # Now copy the autoconfiguration files - try: - if ipautil.file_exists(config.dir + "/preferences.html"): - shutil.copy(config.dir + "/preferences.html", - paths.PREFERENCES_HTML) - if ipautil.file_exists(config.dir + "/configure.jar"): - shutil.copy(config.dir + "/configure.jar", - paths.CONFIGURE_JAR) - except Exception, e: - print "error copying files: " + str(e) - sys.exit(1) - - http.setup_firefox_extension(config.realm_name, config.domain_name) - - return http - -def install_dns_records(config, options, remote_api): - - if not bindinstance.dns_container_exists(config.master_host_name, - ipautil.realm_to_suffix(config.realm_name), - dm_password=config.dirman_password): - return - - try: - bind = bindinstance.BindInstance(dm_password=config.dirman_password, - api=remote_api) - for ip in config.ips: - reverse_zone = bindinstance.find_reverse_zone(ip, remote_api) - - bind.add_master_dns_records(config.host_name, - str(ip), - config.realm_name, - config.domain_name, - reverse_zone, - options.conf_ntp, - options.setup_ca) - except errors.NotFound, e: - root_logger.debug('Replica DNS records could not be added ' - 'on master: %s', str(e)) - - # we should not fail here no matter what - except Exception, e: - root_logger.info('Replica DNS records could not be added ' - 'on master: %s', str(e)) - - -def check_dirsrv(): - (ds_unsecure, ds_secure) = dsinstance.check_ports() - if not ds_unsecure or not ds_secure: - print "IPA requires ports 389 and 636 for the Directory Server." - print "These are currently in use:" - if not ds_unsecure: - print "\t389" - if not ds_secure: - print "\t636" - sys.exit(1) - -def check_bind(): - if not (bindinstance.check_inst(unattended=True) and - dnskeysyncinstance.check_inst()): - print "Aborting installation" - sys.exit(1) - - -def check_dns_resolution(host_name, dns_servers): - """Check forward and reverse resolution of host_name using dns_servers - """ - # Point the resolver at specified DNS server - server_ips = [] - for dns_server in dns_servers: - try: - server_ips = list( - a[4][0] for a in socket.getaddrinfo(dns_server, None)) - except socket.error: - pass - else: - break - if not server_ips: - root_logger.error( - 'Could not resolve any DNS server hostname: %s', dns_servers) - return False - resolver = dns.resolver.Resolver() - resolver.nameservers = server_ips - - root_logger.debug('Search DNS server %s (%s) for %s', - dns_server, server_ips, host_name) - - # Get IP addresses of host_name - addresses = set() - for rtype in 'A', 'AAAA': - try: - result = resolver.query(host_name, rtype) - except dns.exception.DNSException: - rrset = [] - else: - rrset = result.rrset - if rrset: - addresses.update(r.address for r in result.rrset) - - if not addresses: - root_logger.error( - 'Could not resolve hostname %s using DNS. ' - 'Clients may not function properly. ' - 'Please check your DNS setup. ' - '(Note that this check queries IPA DNS directly and ' - 'ignores /etc/hosts.)', - host_name) - return False - - no_errors = True - - # Check each of the IP addresses - checked = set() - for address in addresses: - if address in checked: - continue - checked.add(address) - try: - root_logger.debug('Check reverse address %s (%s)', - address, host_name) - revname = dns.reversename.from_address(address) - rrset = resolver.query(revname, 'PTR').rrset - except Exception, e: - root_logger.debug('Check failed: %s %s', type(e).__name__, e) - root_logger.error( - 'Reverse DNS resolution of address %s (%s) failed. ' - 'Clients may not function properly. ' - 'Please check your DNS setup. ' - '(Note that this check queries IPA DNS directly and ' - 'ignores /etc/hosts.)', - address, host_name) - no_errors = False - else: - host_name_obj = dns.name.from_text(host_name) - if rrset: - names = [r.target.to_text() for r in rrset] - else: - names = [] - root_logger.debug( - 'Address %s resolves to: %s. ', address, ', '.join(names)) - if not rrset or not any( - r.target == host_name_obj for r in rrset): - root_logger.error( - 'The IP address %s of host %s resolves to: %s. ' - 'Clients may not function properly. ' - 'Please check your DNS setup. ' - '(Note that this check queries IPA DNS directly and ' - 'ignores /etc/hosts.)', - address, host_name, ', '.join(names)) - no_errors = False - - return no_errors - def main(): - tasks.check_selinux_status() safe_options, options, filename = parse_options() if os.geteuid() != 0: @@ -418,315 +147,9 @@ def main(): if not ipautil.file_exists(filename): sys.exit("Replica file %s does not exist" % filename) - client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) - if client_fstore.has_files(): - sys.exit("IPA client is already configured on this system.\n" + - "Please uninstall it first before configuring the replica, " + - "using 'ipa-client-install --uninstall'.") - - global sstore - sstore = sysrestore.StateFile(paths.SYSRESTORE) - - global fstore - fstore = sysrestore.FileStore(paths.SYSRESTORE) - - # Check to see if httpd is already configured to listen on 443 - if httpinstance.httpd_443_configured(): - sys.exit("Aborting installation") - - check_dirsrv() - - if options.setup_ca: - if not cainstance.check_port(): - print "IPA requires port 8443 for PKI but it is currently in use." - sys.exit("Aborting installation") - - if options.conf_ntp: - try: - ipaclient.ntpconf.check_timedate_services() - except ipaclient.ntpconf.NTPConflictingService, e: - print "WARNING: conflicting time&date synchronization service '%s'" \ - " will" % e.conflicting_service - print "be disabled in favor of ntpd" - print "" - except ipaclient.ntpconf.NTPConfigurationError: - pass - - # get the directory manager password - dirman_password = options.password - if not dirman_password: - try: - dirman_password = get_dirman_password() - except KeyboardInterrupt: - sys.exit(0) - if dirman_password is None: - sys.exit("Directory Manager password required") - - config = create_replica_config(dirman_password, filename, options) - global REPLICA_INFO_TOP_DIR - REPLICA_INFO_TOP_DIR = config.top_dir - config.setup_ca = options.setup_ca - - if config.setup_ca and not ipautil.file_exists(config.dir + "/cacert.p12"): - print 'CA cannot be installed in CA-less setup.' - sys.exit(1) - - config.setup_kra = options.setup_kra - if config.setup_kra: - try: - kra.install_check(config, options, False, - dogtag.install_constants.DOGTAG_VERSION) - except RuntimeError as e: - print str(e) - exit(1) - - installutils.verify_fqdn(config.master_host_name, options.no_host_dns) - - if options.setup_dns: - dns_installer.install_check(False, True, options, config.host_name) - else: - installutils.get_server_ip_address(config.host_name, fstore, - options.unattended, False, - options.ip_addresses) - - # check connection - if not options.skip_conncheck: - replica_conn_check( - config.master_host_name, config.host_name, config.realm_name, - options.setup_ca, config.ca_ds_port, options.admin_password) - - - # Create the management framework config file - # Note: We must do this before bootstraping and finalizing ipalib.api - old_umask = os.umask(022) # must be readable for httpd - try: - fd = open(paths.IPA_DEFAULT_CONF, "w") - fd.write("[global]\n") - fd.write("host=%s\n" % config.host_name) - fd.write("basedn=%s\n" % str(ipautil.realm_to_suffix(config.realm_name))) - fd.write("realm=%s\n" % config.realm_name) - fd.write("domain=%s\n" % config.domain_name) - fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % ipautil.format_netloc(config.host_name)) - fd.write("ldap_uri=ldapi://%%2fvar%%2frun%%2fslapd-%s.socket\n" % - installutils.realm_to_serverid(config.realm_name)) - if ipautil.file_exists(config.dir + "/cacert.p12"): - fd.write("enable_ra=True\n") - fd.write("ra_plugin=dogtag\n") - fd.write("dogtag_version=%s\n" % - dogtag.install_constants.DOGTAG_VERSION) - else: - fd.write("enable_ra=False\n") - fd.write("ra_plugin=none\n") - - fd.write("enable_kra=%s\n" % config.setup_kra) - - fd.write("mode=production\n") - fd.close() - finally: - os.umask(old_umask) - - api.bootstrap(in_server=True, context='installer') - api.finalize() - - # Create DS user/group if it doesn't exist yet - dsinstance.create_ds_user() - - #Automatically disable pkinit w/ dogtag until that is supported - options.setup_pkinit = False - - cafile = config.dir + "/ca.crt" - if not ipautil.file_exists(cafile): - raise RuntimeError("CA cert file is not available. Please run " - "ipa-replica-prepare to create a new replica file.") - - ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name) - remote_api = create_api(mode=None) - remote_api.bootstrap(in_server=True, context='installer', - ldap_uri=ldapuri, basedn=DN()) - remote_api.finalize() - conn = remote_api.Backend.ldap2 - replman = None - try: - try: - # Try out the password - conn.connect(bind_dn=DIRMAN_DN, bind_pw=config.dirman_password, - tls_cacertfile=cafile) - replman = ReplicationManager(config.realm_name, config.master_host_name, - config.dirman_password) - - # Check that we don't already have a replication agreement - try: - (agreement_cn, agreement_dn) = replman.agreement_dn(config.host_name) - entry = conn.get_entry(agreement_dn, ['*']) - except errors.NotFound: - pass - else: - root_logger.info('Error: A replication agreement for this host ' - 'already exists.') - print ('A replication agreement for this host already exists. ' - 'It needs to be removed.') - print "Run this on the master that generated the info file:" - print " %% ipa-replica-manage del %s --force" % config.host_name - exit(3) - - # Detect the current domain level - try: - current = remote_api.Command['domainlevel_get']()['result'] - except errors.NotFound: - # If we're joining an older master, domain entry is not - # available - current = 0 - - # Detect if current level 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.") - root_logger.error(message) - print(message) - exit(3) - - # Check pre-existing host entry - try: - entry = conn.find_entries(u'fqdn=%s' % config.host_name, ['fqdn'], DN(api.env.container_host, api.env.basedn)) - except errors.NotFound: - pass - else: - root_logger.info( - 'Error: Host %s already exists on the master server.' % config.host_name) - print 'The host %s already exists on the master server.' % config.host_name - print "You should remove it before proceeding:" - print " %% ipa host-del %s" % config.host_name - exit(3) - - # Install CA cert so that we can do SSL connections with ldap - install_ca_cert(conn, api.env.basedn, api.env.realm, cafile) - - dns_masters = remote_api.Object['dnsrecord'].get_dns_masters() - if dns_masters: - if not options.no_host_dns: - master = config.master_host_name - root_logger.debug('Check forward/reverse DNS resolution') - resolution_ok = ( - check_dns_resolution(master, dns_masters) and - check_dns_resolution(config.host_name, dns_masters)) - if not resolution_ok and not options.unattended: - if not ipautil.user_input("Continue?", False): - sys.exit(0) - else: - root_logger.debug('No IPA DNS servers, ' - 'skipping forward/reverse resolution check') - - except errors.ACIError: - sys.exit("\nThe password provided is incorrect for LDAP server %s" % config.master_host_name) - except errors.LDAPError: - sys.exit("\nUnable to connect to LDAP server %s" % config.master_host_name) - finally: - if replman and replman.conn: - replman.conn.unbind() - - if options.skip_schema_check: - root_logger.info("Skipping CA DS schema check") - else: - cainstance.replica_ca_install_check(config) - - # Configure ntpd - if options.conf_ntp: - ipaclient.ntpconf.force_ntpd(sstore) - ntp = ntpinstance.NTPInstance() - ntp.create_instance() - - # Configure dirsrv - ds = install_replica_ds(config) - - # Configure the CA if necessary - CA = cainstance.install_replica_ca(config) - - # Always try to install DNS records - install_dns_records(config, options, remote_api) - finally: - if conn.isconnected(): - conn.disconnect() - - # We need to ldap_enable the CA now that DS is up and running - if CA and config.setup_ca: - CA.ldap_enable('CA', config.host_name, config.dirman_password, - ipautil.realm_to_suffix(config.realm_name)) - - # This is done within stopped_service context, which restarts CA - CA.enable_client_auth_to_db(CA.dogtag_constants.CS_CFG_PATH) - - krb = install_krb(config, setup_pkinit=options.setup_pkinit) - http = install_http(config, auto_redirect=options.ui_redirect) - - otpd = otpdinstance.OtpdInstance() - otpd.create_instance('OTPD', config.host_name, config.dirman_password, - ipautil.realm_to_suffix(config.realm_name)) - - if CA: - CA.configure_certmonger_renewal() - CA.import_ra_cert(config.dir + "/ra.p12") - CA.fix_ra_perms() - - # The DS instance is created before the keytab, add the SSL cert we - # generated - ds.add_cert_to_service() - - # Apply any LDAP updates. Needs to be done after the replica is synced-up - service.print_msg("Applying LDAP updates") - ds.apply_updates() - - if options.setup_kra: - kra.install(config, options, dirman_password) - else: - service.print_msg("Restarting the directory server") - ds.restart() - - service.print_msg("Restarting the KDC") - krb.restart() - - if CA and config.setup_ca: - service.print_msg("Restarting the certificate server") - CA.restart(dogtag.configured_constants().PKI_INSTANCE_NAME) - - if options.setup_dns: - api.Backend.ldap2.connect(autobind=True) - dns_installer.install(False, True, options) - - # Restart httpd to pick up the new IPA configuration - service.print_msg("Restarting the web server") - http.restart() - - # Call client install script - try: - args = [paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended", "--domain", config.domain_name, "--server", config.host_name, "--realm", config.realm_name] - if not options.create_sshfp: - args.append("--no-dns-sshfp") - if options.trust_sshfp: - args.append("--ssh-trust-dns") - if not options.conf_ssh: - args.append("--no-ssh") - if not options.conf_sshd: - args.append("--no-sshd") - if options.mkhomedir: - args.append("--mkhomedir") - ipautil.run(args) - except Exception, e: - print "Configuration of client side components failed!" - print "ipa-client-install returned: " + str(e) - raise RuntimeError("Failed to configure the client") - - ds.replica_populate() + server.replica_install_check(filename, options) + server.replica_install(filename, options) - #Everything installed properly, activate ipa service. - services.knownservices.ipa.enable() fail_message = ''' Your system may be partly configured. @@ -734,15 +157,6 @@ Run /usr/sbin/ipa-server-install --uninstall to clean up. ''' if __name__ == '__main__': - try: - with private_ccache(): - installutils.run_script(main, log_file_name=log_file_name, - operation_name='ipa-replica-install', - fail_message=fail_message) - finally: - # always try to remove decrypted replica file - try: - if REPLICA_INFO_TOP_DIR: - shutil.rmtree(REPLICA_INFO_TOP_DIR) - except OSError: - pass + installutils.run_script(main, log_file_name=log_file_name, + operation_name='ipa-replica-install', + fail_message=fail_message) diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py index d4af4f248..fd0812418 100644 --- a/ipaserver/install/server/__init__.py +++ b/ipaserver/install/server/__init__.py @@ -3,6 +3,8 @@ # from .install import install_check, install, uninstall_check, uninstall +from .replicainstall import install_check as replica_install_check +from .replicainstall import install as replica_install from .install import validate_dm_password, validate_admin_password from .upgrade import upgrade_configuration diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py new file mode 100644 index 000000000..dc8efd48d --- /dev/null +++ b/ipaserver/install/server/replicainstall.py @@ -0,0 +1,640 @@ +# +# Copyright (C) 2015 FreeIPA Contributors see COPYING for license +# + +import dns.exception as dnsexception +import dns.name as dnsname +import dns.resolver as dnsresolver +import dns.reversename as dnsreversename +import os +import shutil +import socket +import sys + +from ipapython import dogtag, ipautil, sysrestore +from ipapython.dn import DN +from ipapython.ipa_log_manager import root_logger +from ipaplatform import services +from ipaplatform.tasks import tasks +from ipaplatform.paths import paths +from ipalib import api, certstore, constants, create_api, errors, x509 +import ipaclient.ntpconf +from ipaserver.install import ( + bindinstance, cainstance, dns, dsinstance, httpinstance, installutils, kra, + krbinstance, memcacheinstance, ntpinstance, otpdinstance, service) +from ipaserver.install.installutils import create_replica_config +from ipaserver.install.replication import ( + ReplicationManager, replica_conn_check) +from ipaserver.install.server.install import ( + init_private_ccache, destroy_private_ccache) + +DIRMAN_DN = DN(('cn', 'directory manager')) + + +def get_dirman_password(): + return installutils.read_password("Directory Manager (existing master)", + confirm=False, validate=False) + + +def make_pkcs12_info(directory, cert_name, password_name): + """Make pkcs12_info + + :param directory: Base directory (config.dir) + :param cert_name: Cert filename (e.g. "dscert.p12") + :param password_name: Cert filename (e.g. "dirsrv_pin.txt") + :return: a (full cert path, password) tuple, or None if cert is not found + """ + cert_path = os.path.join(directory, cert_name) + if ipautil.file_exists(cert_path): + password_file = os.path.join(directory, password_name) + password = open(password_file).read().strip() + return cert_path, password + else: + return None + + +def install_replica_ds(config): + dsinstance.check_ports() + + # if we have a pkcs12 file, create the cert db from + # that. Otherwise the ds setup will create the CA + # cert + pkcs12_info = make_pkcs12_info(config.dir, "dscert.p12", "dirsrv_pin.txt") + + ds = dsinstance.DsInstance() + ds.create_replica( + realm_name=config.realm_name, + master_fqdn=config.master_host_name, + fqdn=config.host_name, + domain_name=config.domain_name, + dm_password=config.dirman_password, + subject_base=config.subject_base, + pkcs12_info=pkcs12_info, + ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12"), + ca_file=config.dir + "/ca.crt", + ) + + return ds + + +def install_krb(config, setup_pkinit=False): + krb = krbinstance.KrbInstance() + + # pkinit files + pkcs12_info = make_pkcs12_info(config.dir, "pkinitcert.p12", + "pkinit_pin.txt") + + krb.create_replica(config.realm_name, + config.master_host_name, config.host_name, + config.domain_name, config.dirman_password, + setup_pkinit, pkcs12_info) + + return krb + + +def install_ca_cert(ldap, base_dn, realm, cafile): + try: + try: + certs = certstore.get_ca_certs(ldap, base_dn, realm, False) + except errors.NotFound: + shutil.copy(cafile, constants.CACERT) + else: + certs = [c[0] for c in certs if c[2] is not False] + x509.write_certificate_list(certs, constants.CACERT) + + os.chmod(constants.CACERT, 0444) + except Exception, e: + print "error copying files: " + str(e) + sys.exit(1) + + +def install_http(config, auto_redirect): + # if we have a pkcs12 file, create the cert db from + # that. Otherwise the ds setup will create the CA + # cert + pkcs12_info = make_pkcs12_info(config.dir, "httpcert.p12", "http_pin.txt") + + memcache = memcacheinstance.MemcacheInstance() + memcache.create_instance('MEMCACHE', config.host_name, + config.dirman_password, + ipautil.realm_to_suffix(config.realm_name)) + + http = httpinstance.HTTPInstance() + http.create_instance( + config.realm_name, config.host_name, config.domain_name, + config.dirman_password, False, pkcs12_info, + auto_redirect=auto_redirect, ca_file=config.dir + "/ca.crt", + ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12")) + + # Now copy the autoconfiguration files + try: + if ipautil.file_exists(config.dir + "/preferences.html"): + shutil.copy(config.dir + "/preferences.html", + paths.PREFERENCES_HTML) + if ipautil.file_exists(config.dir + "/configure.jar"): + shutil.copy(config.dir + "/configure.jar", + paths.CONFIGURE_JAR) + except Exception, e: + print "error copying files: " + str(e) + sys.exit(1) + + http.setup_firefox_extension(config.realm_name, config.domain_name) + + return http + + +def install_dns_records(config, options, remote_api): + + if not bindinstance.dns_container_exists( + config.master_host_name, + ipautil.realm_to_suffix(config.realm_name), + dm_password=config.dirman_password): + return + + try: + bind = bindinstance.BindInstance(dm_password=config.dirman_password, + api=remote_api) + for ip in config.ips: + reverse_zone = bindinstance.find_reverse_zone(ip, remote_api) + + bind.add_master_dns_records(config.host_name, + str(ip), + config.realm_name, + config.domain_name, + reverse_zone, + options.conf_ntp, + options.setup_ca) + except errors.NotFound, e: + root_logger.debug('Replica DNS records could not be added ' + 'on master: %s', str(e)) + + # we should not fail here no matter what + except Exception, e: + root_logger.info('Replica DNS records could not be added ' + 'on master: %s', str(e)) + + +def check_dirsrv(): + (ds_unsecure, ds_secure) = dsinstance.check_ports() + if not ds_unsecure or not ds_secure: + print "IPA requires ports 389 and 636 for the Directory Server." + print "These are currently in use:" + if not ds_unsecure: + print "\t389" + if not ds_secure: + print "\t636" + sys.exit(1) + + +def check_dns_resolution(host_name, dns_servers): + """Check forward and reverse resolution of host_name using dns_servers + """ + # Point the resolver at specified DNS server + server_ips = [] + for dns_server in dns_servers: + try: + server_ips = list( + a[4][0] for a in socket.getaddrinfo(dns_server, None)) + except socket.error: + pass + else: + break + if not server_ips: + root_logger.error( + 'Could not resolve any DNS server hostname: %s', dns_servers) + return False + resolver = dnsresolver.Resolver() + resolver.nameservers = server_ips + + root_logger.debug('Search DNS server %s (%s) for %s', + dns_server, server_ips, host_name) + + # Get IP addresses of host_name + addresses = set() + for rtype in 'A', 'AAAA': + try: + result = resolver.query(host_name, rtype) + except dnsexception.DNSException: + rrset = [] + else: + rrset = result.rrset + if rrset: + addresses.update(r.address for r in result.rrset) + + if not addresses: + root_logger.error( + 'Could not resolve hostname %s using DNS. ' + 'Clients may not function properly. ' + 'Please check your DNS setup. ' + '(Note that this check queries IPA DNS directly and ' + 'ignores /etc/hosts.)', + host_name) + return False + + no_errors = True + + # Check each of the IP addresses + checked = set() + for address in addresses: + if address in checked: + continue + checked.add(address) + try: + root_logger.debug('Check reverse address %s (%s)', + address, host_name) + revname = dnsreversename.from_address(address) + rrset = resolver.query(revname, 'PTR').rrset + except Exception, e: + root_logger.debug('Check failed: %s %s', type(e).__name__, e) + root_logger.error( + 'Reverse DNS resolution of address %s (%s) failed. ' + 'Clients may not function properly. ' + 'Please check your DNS setup. ' + '(Note that this check queries IPA DNS directly and ' + 'ignores /etc/hosts.)', + address, host_name) + no_errors = False + else: + host_name_obj = dnsname.from_text(host_name) + if rrset: + names = [r.target.to_text() for r in rrset] + else: + names = [] + root_logger.debug( + 'Address %s resolves to: %s. ', address, ', '.join(names)) + if not rrset or not any( + r.target == host_name_obj for r in rrset): + root_logger.error( + 'The IP address %s of host %s resolves to: %s. ' + 'Clients may not function properly. ' + 'Please check your DNS setup. ' + '(Note that this check queries IPA DNS directly and ' + 'ignores /etc/hosts.)', + address, host_name, ', '.join(names)) + no_errors = False + + return no_errors + + +def remove_replica_info_dir(): + # always try to remove decrypted replica file + try: + if REPLICA_INFO_TOP_DIR: + shutil.rmtree(REPLICA_INFO_TOP_DIR) + except OSError: + pass + + +def common_cleanup(func): + def decorated(*args, **kwargs): + try: + try: + func(*args, **kwargs) + except BaseException: + destroy_private_ccache() + remove_replica_info_dir() + raise + except KeyboardInterrupt: + sys.exit(1) + + return decorated + + +@common_cleanup +def install_check(filename, options): + global config + + init_private_ccache() + + tasks.check_selinux_status() + + client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) + if client_fstore.has_files(): + sys.exit("IPA client is already configured on this system.\n" + "Please uninstall it first before configuring the replica, " + "using 'ipa-client-install --uninstall'.") + + global sstore + sstore = sysrestore.StateFile(paths.SYSRESTORE) + + global fstore + fstore = sysrestore.FileStore(paths.SYSRESTORE) + + # Check to see if httpd is already configured to listen on 443 + if httpinstance.httpd_443_configured(): + sys.exit("Aborting installation") + + check_dirsrv() + + if options.setup_ca: + if not cainstance.check_port(): + print "IPA requires port 8443 for PKI but it is currently in use." + sys.exit("Aborting installation") + + if options.conf_ntp: + try: + ipaclient.ntpconf.check_timedate_services() + except ipaclient.ntpconf.NTPConflictingService, e: + print("WARNING: conflicting time&date synchronization service '%s'" + " will" % e.conflicting_service) + print "be disabled in favor of ntpd" + print "" + except ipaclient.ntpconf.NTPConfigurationError: + pass + + # get the directory manager password + dirman_password = options.password + if not dirman_password: + try: + dirman_password = get_dirman_password() + except KeyboardInterrupt: + sys.exit(0) + if dirman_password is None: + sys.exit("Directory Manager password required") + + config = create_replica_config(dirman_password, filename, options) + global REPLICA_INFO_TOP_DIR + REPLICA_INFO_TOP_DIR = config.top_dir + config.setup_ca = options.setup_ca + + if config.setup_ca and not ipautil.file_exists(config.dir + "/cacert.p12"): + print 'CA cannot be installed in CA-less setup.' + sys.exit(1) + + config.setup_kra = options.setup_kra + if config.setup_kra: + try: + kra.install_check(config, options, False, + dogtag.install_constants.DOGTAG_VERSION) + except RuntimeError as e: + print str(e) + sys.exit(1) + + installutils.verify_fqdn(config.master_host_name, options.no_host_dns) + + if options.setup_dns: + dns.install_check(False, True, options, config.host_name) + else: + installutils.get_server_ip_address(config.host_name, fstore, + options.unattended, False, + options.ip_addresses) + + # check connection + if not options.skip_conncheck: + replica_conn_check( + config.master_host_name, config.host_name, config.realm_name, + options.setup_ca, config.ca_ds_port, options.admin_password) + + # Automatically disable pkinit w/ dogtag until that is supported + options.setup_pkinit = False + + cafile = config.dir + "/ca.crt" + if not ipautil.file_exists(cafile): + raise RuntimeError("CA cert file is not available. Please run " + "ipa-replica-prepare to create a new replica file.") + + +@common_cleanup +def install(filename, options): + global config + + # Create the management framework config file + # Note: We must do this before bootstraping and finalizing ipalib.api + old_umask = os.umask(022) # must be readable for httpd + try: + fd = open(paths.IPA_DEFAULT_CONF, "w") + fd.write("[global]\n") + fd.write("host=%s\n" % config.host_name) + fd.write("basedn=%s\n" % + str(ipautil.realm_to_suffix(config.realm_name))) + fd.write("realm=%s\n" % config.realm_name) + fd.write("domain=%s\n" % config.domain_name) + fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % + ipautil.format_netloc(config.host_name)) + fd.write("ldap_uri=ldapi://%%2fvar%%2frun%%2fslapd-%s.socket\n" % + installutils.realm_to_serverid(config.realm_name)) + if ipautil.file_exists(config.dir + "/cacert.p12"): + fd.write("enable_ra=True\n") + fd.write("ra_plugin=dogtag\n") + fd.write("dogtag_version=%s\n" % + dogtag.install_constants.DOGTAG_VERSION) + else: + fd.write("enable_ra=False\n") + fd.write("ra_plugin=none\n") + + fd.write("enable_kra=%s\n" % config.setup_kra) + + fd.write("mode=production\n") + fd.close() + finally: + os.umask(old_umask) + + api.bootstrap(in_server=True, context='installer') + api.finalize() + + # Create DS user/group if it doesn't exist yet + dsinstance.create_ds_user() + + cafile = config.dir + "/ca.crt" + + ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name) + remote_api = create_api(mode=None) + remote_api.bootstrap(in_server=True, context='installer', + ldap_uri=ldapuri, basedn=DN()) + remote_api.finalize() + conn = remote_api.Backend.ldap2 + replman = None + try: + try: + # Try out the password + conn.connect(bind_dn=DIRMAN_DN, bind_pw=config.dirman_password, + tls_cacertfile=cafile) + replman = ReplicationManager(config.realm_name, + config.master_host_name, + config.dirman_password) + + # Check that we don't already have a replication agreement + try: + (agreement_cn, agreement_dn) = replman.agreement_dn( + config.host_name) + entry = conn.get_entry(agreement_dn, ['*']) + except errors.NotFound: + pass + else: + root_logger.info('Error: A replication agreement for this ' + 'host already exists.') + print('A replication agreement for this host already exists. ' + 'It needs to be removed.') + print "Run this on the master that generated the info file:" + print(" %% ipa-replica-manage del %s --force" % + config.host_name) + sys.exit(3) + + # Detect the current domain level + try: + current = remote_api.Command['domainlevel_get']()['result'] + except errors.NotFound: + # If we're joining an older master, domain entry is not + # available + current = 0 + + # Detect if current level 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.") + root_logger.error(message) + print(message) + sys.exit(3) + + # Check pre-existing host entry + try: + entry = conn.find_entries(u'fqdn=%s' % config.host_name, + ['fqdn'], DN(api.env.container_host, + api.env.basedn)) + except errors.NotFound: + pass + else: + root_logger.info('Error: Host %s already exists on the master ' + 'server.' % config.host_name) + print('The host %s already exists on the master server.' % + config.host_name) + print "You should remove it before proceeding:" + print " %% ipa host-del %s" % config.host_name + sys.exit(3) + + # Install CA cert so that we can do SSL connections with ldap + install_ca_cert(conn, api.env.basedn, api.env.realm, cafile) + + dns_masters = remote_api.Object['dnsrecord'].get_dns_masters() + if dns_masters: + if not options.no_host_dns: + master = config.master_host_name + root_logger.debug('Check forward/reverse DNS resolution') + resolution_ok = ( + check_dns_resolution(master, dns_masters) and + check_dns_resolution(config.host_name, dns_masters)) + if not resolution_ok and not options.unattended: + if not ipautil.user_input("Continue?", False): + sys.exit(0) + else: + root_logger.debug('No IPA DNS servers, ' + 'skipping forward/reverse resolution check') + + except errors.ACIError: + sys.exit("\nThe password provided is incorrect for LDAP server " + "%s" % config.master_host_name) + except errors.LDAPError: + sys.exit("\nUnable to connect to LDAP server %s" % + config.master_host_name) + finally: + if replman and replman.conn: + replman.conn.unbind() + + if options.skip_schema_check: + root_logger.info("Skipping CA DS schema check") + else: + cainstance.replica_ca_install_check(config) + + # Configure ntpd + if options.conf_ntp: + ipaclient.ntpconf.force_ntpd(sstore) + ntp = ntpinstance.NTPInstance() + ntp.create_instance() + + # Configure dirsrv + ds = install_replica_ds(config) + + # Configure the CA if necessary + CA = cainstance.install_replica_ca(config) + + # Always try to install DNS records + install_dns_records(config, options, remote_api) + finally: + if conn.isconnected(): + conn.disconnect() + + # We need to ldap_enable the CA now that DS is up and running + if CA and config.setup_ca: + CA.ldap_enable('CA', config.host_name, config.dirman_password, + ipautil.realm_to_suffix(config.realm_name)) + + # This is done within stopped_service context, which restarts CA + CA.enable_client_auth_to_db(CA.dogtag_constants.CS_CFG_PATH) + + krb = install_krb(config, setup_pkinit=options.setup_pkinit) + http = install_http(config, auto_redirect=options.ui_redirect) + + otpd = otpdinstance.OtpdInstance() + otpd.create_instance('OTPD', config.host_name, config.dirman_password, + ipautil.realm_to_suffix(config.realm_name)) + + if CA: + CA.configure_certmonger_renewal() + CA.import_ra_cert(config.dir + "/ra.p12") + CA.fix_ra_perms() + + # The DS instance is created before the keytab, add the SSL cert we + # generated + ds.add_cert_to_service() + + # Apply any LDAP updates. Needs to be done after the replica is synced-up + service.print_msg("Applying LDAP updates") + ds.apply_updates() + + if options.setup_kra: + kra.install(config, options, config.dirman_password) + else: + service.print_msg("Restarting the directory server") + ds.restart() + + service.print_msg("Restarting the KDC") + krb.restart() + + if CA and config.setup_ca: + service.print_msg("Restarting the certificate server") + CA.restart(dogtag.configured_constants().PKI_INSTANCE_NAME) + + if options.setup_dns: + api.Backend.ldap2.connect(autobind=True) + dns.install(False, True, options) + + # Restart httpd to pick up the new IPA configuration + service.print_msg("Restarting the web server") + http.restart() + + # Call client install script + try: + args = [paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended", + "--domain", config.domain_name, "--server", config.host_name, + "--realm", config.realm_name] + if not options.create_sshfp: + args.append("--no-dns-sshfp") + if options.trust_sshfp: + args.append("--ssh-trust-dns") + if not options.conf_ssh: + args.append("--no-ssh") + if not options.conf_sshd: + args.append("--no-sshd") + if options.mkhomedir: + args.append("--mkhomedir") + ipautil.run(args) + except Exception, e: + print "Configuration of client side components failed!" + print "ipa-client-install returned: " + str(e) + raise RuntimeError("Failed to configure the client") + + ds.replica_populate() + + # Everything installed properly, activate ipa service. + services.knownservices.ipa.enable() + + destroy_private_ccache() + remove_replica_info_dir() -- cgit