Skip to content
Snippets Groups Projects
Select Git revision
  • 45847ce0525c06d89066df13b9132acbcd22c0ec
  • master default protected
  • v2.28.0
  • v2.27.0
  • v2.25.1
  • v2.24.3
  • v2.26.0
  • v2.24.2
  • v2.25.0
  • v2.24.1
  • v2.22.2
  • v2.23.3
  • v2.24.0
  • v2.23.2
  • v2.23.1
  • v2.23.0
  • v2.22.1
  • v2.22.0
  • v2.21.0
  • v2.20.0
  • v2.19.1
  • v2.18.2
22 results

vagrant.md

Blame
  • ansible_ipa_replica.py 19.07 KiB
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    # Authors:
    #   Thomas Woerner <twoerner@redhat.com>
    #
    # Based on ipa-replica-install code
    #
    # Copyright (C) 2018  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/>.
    
    import os
    import sys
    import logging
    import inspect
    from contextlib import contextmanager as contextlib_contextmanager
    
    
    from ipapython.version import NUM_VERSION, VERSION
    
    if NUM_VERSION < 30201:
        # See ipapython/version.py
        IPA_MAJOR,IPA_MINOR,IPA_RELEASE = [ int(x) for x in VERSION.split(".", 2) ]
        IPA_PYTHON_VERSION = IPA_MAJOR*10000 + IPA_MINOR*100 + IPA_RELEASE
    else:
        IPA_PYTHON_VERSION = NUM_VERSION
    
    
    if NUM_VERSION >= 40600:
        # IPA version >= 4.6
    
        import contextlib
    
        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 tempfile
        import traceback
    
        from pkg_resources import parse_version
        import six
    
        from ipaclient.install.ipachangeconf import IPAChangeConf
        from ipalib.install import certstore, sysrestore
        from ipapython.ipautil import ipa_generate_password
        from ipalib.install.kinit import kinit_keytab
        from ipapython import ipaldap, ipautil, kernel_keyring
        from ipapython.certdb import IPA_CA_TRUST_FLAGS, EXTERNAL_CA_TRUST_FLAGS
        from ipapython.dn import DN
        from ipapython.admintool import ScriptError
        from ipapython.ipa_log_manager import standard_logging_setup
        from ipaplatform import services
        from ipaplatform.tasks import tasks
        from ipaplatform.paths import paths
        from ipalib import api, constants, create_api, errors, rpc, x509
        from ipalib.config import Env
        from ipalib.util import (
            validate_domain_name,
            no_matching_interface_for_ip_address_warning)
        from ipaclient.install.client import configure_krb5_conf, purge_host_keytab
        from ipaserver.install import (
            adtrust, bindinstance, ca, certs, dns, dsinstance, httpinstance,
            installutils, kra, krbinstance,
            otpdinstance, custodiainstance, service, upgradeinstance)
        try:
            from ipaserver.masters import (
                find_providing_servers, find_providing_server)
        except ImportError:
            from ipaserver.install.service import (
                find_providing_servers, find_providing_server)
        from ipaserver.install.installutils import (
            ReplicaConfig, load_pkcs12, is_ipa_configured)
        from ipaserver.install.replication import (
            ReplicationManager, replica_conn_check)
        from ipaserver.install.server.replicainstall import (
            make_pkcs12_info, install_replica_ds, install_krb, install_ca_cert,
            install_http, install_dns_records, create_ipa_conf, check_dirsrv,
            check_dns_resolution, configure_certmonger, remove_replica_info_dir,
            #common_cleanup,
            preserve_enrollment_state, uninstall_client,
            promote_sssd, promote_openldap_conf, rpc_client,
            check_remote_fips_mode, check_remote_version, common_check,
            current_domain_level, check_domain_level_is_supported,
            #enroll_dl0_replica,
            #ensure_enrolled,
            promotion_check_ipa_domain
        )
        import SSSDConfig
        from subprocess import CalledProcessError
    
        if six.PY3:
            unicode = str
    
        try:
            from ipaclient.install import timeconf
            time_service = "chronyd"
            ntpinstance = None
        except ImportError:
            try:
                from ipaclient.install import ntpconf as timeconf
            except ImportError:
                from ipaclient import ntpconf as timeconf
            from ipaserver.install import ntpinstance
            time_service = "ntpd"
    
    
    else:
        # IPA version < 4.6
    
        raise Exception("freeipa version '%s' is too old" % VERSION)
    
    
    logger = logging.getLogger("ipa-server-install")
    #logger.setLevel(logging.DEBUG)
    standard_logging_setup(
        paths.IPAREPLICA_INSTALL_LOG, verbose=False, debug=False,
        filemode='a', console_format='%(message)s')
    
    @contextlib_contextmanager
    def redirect_stdout(f):
        sys.stdout = f
        try:
            yield f
        finally:
            sys.stdout = sys.__stdout__
    
    
    class AnsibleModuleLog():
        def __init__(self, module):
            self.module = module
            _ansible_module_log = self
    
            class AnsibleLoggingHandler(logging.Handler):
                def emit(self, record):
                    _ansible_module_log.write(self.format(record))
    
            self.logging_handler = AnsibleLoggingHandler()
            logger.setLevel(logging.DEBUG)
            logger.root.addHandler(self.logging_handler)
    
        def close(self):
            self.flush()
    
        def flush(self):
            pass
    
        def log(self, msg):
            #self.write(msg+"\n")
            self.write(msg)
    
        def debug(self, msg):
            self.module.debug(msg)
    
        def info(self, msg):
            self.module.debug(msg)
    
        def write(self, msg):
            self.module.debug(msg)
            #self.module.warn(msg)
    
    
    class installer_obj(object):
        def __init__(self):
            # CompatServerReplicaInstall
            self.ca_cert_files = None
            self.all_ip_addresses = False
            self.no_wait_for_dns = True
            self.nisdomain = None
            self.no_nisdomain = False
            self.no_sudo = False
            self.request_cert = False
            self.ca_file = None
            self.zonemgr = None
            self.replica_file = None
            # ServerReplicaInstall
            self.subject_base = None
            self.ca_subject = None
            # others
            self._ccache = None
            self.password = None
            self.reverse_zones = [ ]
            #def _is_promote(self):
            #    return self.replica_file is None
            #self.skip_conncheck = False
            self._replica_install = False
            #self.dnssec_master = False # future unknown
            #self.disable_dnssec_master = False # future unknown
            #self.domainlevel = MAX_DOMAIN_LEVEL # deprecated
            #self.domain_level = self.domainlevel # deprecated
            self.interactive = False
            self.unattended = not self.interactive
            #self.promote = self.replica_file is None
            self.promote = True
    
        #def __getattribute__(self, attr):
        #    value = super(installer_obj, self).__getattribute__(attr)
        #    if not attr.startswith("--") and not attr.endswith("--"):
        #        logger.debug(
        #            "  <-- Accessing installer.%s (%s)" % (attr, repr(value)))
        #    return value
    
        def __getattr__(self, attr):
            logger.info("  --> ADDING missing installer.%s" % attr)
            setattr(self, attr, None)
            return getattr(self, attr)
    
        #def __setattr__(self, attr, value):
        #    logger.debug("  --> Setting installer.%s to %s" % (attr, repr(value)))
        #    return super(installer_obj, self).__setattr__(attr, value)
    
        def knobs(self):
            for name in self.__dict__:
                yield self, name
    
    
    installer = installer_obj()
    options = installer
    
    # DNSInstallInterface
    options.dnssec_master = False
    options.disable_dnssec_master = False
    options.kasp_db_file = None
    options.force = False
    
    # ServerMasterInstall
    options.add_sids = True
    options.add_agents = False
    
    # ServerReplicaInstall
    options.subject_base = None
    options.ca_subject = None
    
    
    def api_Backend_ldap2(host_name, setup_ca, connect=False):
        # we are sure we have the configuration file ready.
        cfg = dict(context='installer', confdir=paths.ETC_IPA, in_server=True,
                   host=host_name,
        )
        if setup_ca:
            # we have an IPA-integrated CA
            cfg['ca_host'] = host_name
    
        api.bootstrap(**cfg)
        api.finalize()
        if connect:
            api.Backend.ldap2.connect()
    
    
    def gen_env_boostrap_finalize_core(etc_ipa, default_config):
        env = Env()
        #env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None)
        #env._finalize_core(**dict(constants.DEFAULT_CONFIG))
        env._bootstrap(context='installer', confdir=etc_ipa, log=None)
        env._finalize_core(**dict(default_config))
        return env
    
    
    def api_bootstrap_finalize(env):
        # pylint: disable=no-member
        xmlrpc_uri = 'https://{}/ipa/xml'.format(ipautil.format_netloc(env.host))
        api.bootstrap(in_server=True,
                      context='installer',
                      confdir=paths.ETC_IPA,
                      ldap_uri=installutils.realm_to_ldapi_uri(env.realm),
                      xmlrpc_uri=xmlrpc_uri)
        # pylint: enable=no-member
        api.finalize()
    
    
    def gen_ReplicaConfig():
        class ExtendedReplicaConfig(ReplicaConfig):
            def __init__(self, top_dir=None):
                super(ExtendedReplicaConfig, self).__init__(top_dir)
    
            #def __getattribute__(self, attr):
            #    value = super(ExtendedReplicaConfig, self).__getattribute__(attr)
            #    if attr not in [ "__dict__", "knobs" ]:
            #        logger.debug("  <== Accessing config.%s (%s)" % (attr, repr(value)))
            #    return value
    
            def __getattr__(self, attr):
                logger.info("  ==> ADDING missing config.%s" % attr)
                setattr(self, attr, None)
                return getattr(self, attr)
    
            #def __setattr__(self, attr, value):
            #    logger.debug("  ==> Setting config.%s to %s" % (attr, repr(value)))
            #    return super(ExtendedReplicaConfig, self).__setattr__(attr, value)
    
            def knobs(self):
                for name in self.__dict__:
                    yield self, name
    
        #config = ReplicaConfig()
        config = ExtendedReplicaConfig()
        config.realm_name = api.env.realm
        config.host_name = api.env.host
        config.domain_name = api.env.domain
        config.master_host_name = api.env.server
        config.ca_host_name = api.env.ca_host
        config.kra_host_name = config.ca_host_name
        config.ca_ds_port = 389
        config.setup_ca = options.setup_ca
        config.setup_kra = options.setup_kra
        config.dir = options._top_dir
        config.basedn = api.env.basedn
        #config.subject_base = options.subject_base
    
        return config
    
    
    def ds_init_info(ansible_log, fstore, domainlevel, dirsrv_config_file,
                     realm_name, host_name, domain_name, dm_password,
                     idstart, idmax, subject_base, ca_subject,
                     #no_hbac_allow,
                     dirsrv_pkcs12_info, no_pkinit,
                     external_cert_files, dirsrv_cert_files):
    
        if not external_cert_files:
            ds = dsinstance.DsInstance(fstore=fstore, domainlevel=domainlevel,
                                       config_ldif=dirsrv_config_file)
            ds.set_output(ansible_log)
    
            if dirsrv_cert_files:
                _dirsrv_pkcs12_info = dirsrv_pkcs12_info
            else:
                _dirsrv_pkcs12_info = None
    
            with redirect_stdout(ansible_log):
                ds.init_info(realm_name, host_name, domain_name, dm_password,
                             subject_base, ca_subject, idstart, idmax,
                             #hbac_allow=not no_hbac_allow,
                             _dirsrv_pkcs12_info, setup_pkinit=not no_pkinit)
        else:
            ds = dsinstance.DsInstance(fstore=fstore, domainlevel=domainlevel)
            ds.set_output(ansible_log)
    
            with redirect_stdout(ansible_log):
                ds.init_info(realm_name, host_name, domain_name, dm_password,
                             subject_base, ca_subject, 1101, 1100, None,
                             setup_pkinit=not no_pkinit)
    
        return ds
    
    
    def replica_ds_init_info(ansible_log,
                             config, options, ca_is_configured, remote_api,
                             ds_ca_subject, ca_file,
                             promote=False, pkcs12_info=None):
    
        dsinstance.check_ports()
    
        # if we have a pkcs12 file, create the cert db from
        # that. Otherwise the ds setup will create the CA
        # cert
        if pkcs12_info is None:
            pkcs12_info = make_pkcs12_info(config.dir, "dscert.p12",
                                           "dirsrv_pin.txt")
    
        # during replica install, this gets invoked before local DS is
        # available, so use the remote api.
        #if ca_is_configured:
        #    ca_subject = ca.lookup_ca_subject(_api, config.subject_base)
        #else:
        #    ca_subject = installutils.default_ca_subject_dn(config.subject_base)
        ca_subject = ds_ca_subject
    
        ds = dsinstance.DsInstance(
            config_ldif=options.dirsrv_config_file)
        ds.set_output(ansible_log)
    
        # Source: ipaserver/install/dsinstance.py
    
        # idstart and idmax are configured so that the range is seen as
        # depleted by the DNA plugin and the replica will go and get a
        # new range from the master.
        # This way all servers use the initially defined range by default.
        idstart = 1101
        idmax = 1100
    
        with redirect_stdout(ansible_log):
            ds.init_info(
                realm_name=config.realm_name,
                fqdn=config.host_name,
                domain_name=config.domain_name,
                dm_password=config.dirman_password,
                subject_base=config.subject_base,
                ca_subject=ca_subject,
                idstart=idstart,
                idmax=idmax,
                pkcs12_info=pkcs12_info,
                ca_file=ca_file,
                setup_pkinit=not options.no_pkinit,
            )
        ds.master_fqdn = config.master_host_name
        if ca_is_configured is not None:
            ds.ca_is_configured = ca_is_configured
        ds.promote = promote
        ds.api = remote_api
    
        # from __setup_replica
    
        # Always connect to ds over ldapi
        ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=ds.realm)
        conn = ipaldap.LDAPClient(ldap_uri)
        conn.external_bind()
    
        return ds
    
    
    def krb_init_info(ansible_log, fstore, realm_name, host_name, no_pkinit,
                      subject_base):
        krb = krbinstance.KrbInstance(fstore)
        krb.set_output(ansible_log)
        with redirect_stdout(ansible_log):
            krb.init_info(realm_name, host_name, etup_pkinit=not no_pkinit,
                          subject_base=subject_base)
    
    
    def replica_krb_init_info(ansible_log, fstore, realm_name, master_host_name,
                              host_name, domain_name, admin_password,
                              no_pkinit, subject_base, pkcs12_info=None):
        # promote is not needed here
    
        # From replicainstall.install_krb
        krb = krbinstance.KrbInstance(fstore=fstore)
        krb.set_output(ansible_log)
    
        # pkinit files
        if pkcs12_info is None:
            pkcs12_info = make_pkcs12_info(config.dir, "pkinitcert.p12",
                                           "pkinit_pin.txt")
    
        #krb.create_replica(realm_name,
        #                   master_host_name, host_name,
        #                   domain_name, dirman_password,
        #                   setup_pkinit, pkcs12_info,
        #                   subject_base=subject_base,
        #                   promote=promote)
        with redirect_stdout(ansible_log):
            krb.init_info(realm_name, host_name, setup_pkinit=not no_pkinit,
                          subject_base=subject_base)
    
            # From ipaserver.install.krbinstance.create_replica
    
            krb.pkcs12_info = pkcs12_info
            krb.subject_base = subject_base
            krb.master_fqdn = master_host_name
            krb.config_pkinit = not no_pkinit
    
            #krb.__common_setup(realm_name, host_name, domain_name, admin_password)
            krb.fqdn = host_name
            krb.realm = realm_name.upper()
            krb.host = host_name.split(".")[0]
            krb.ip = socket.getaddrinfo(host_name, None, socket.AF_UNSPEC, socket.SOCK_STREAM)[0][4][0]
            krb.domain = domain_name
            krb.suffix = ipautil.realm_to_suffix(krb.realm)
            krb.kdc_password = ipautil.ipa_generate_password()
            krb.admin_password = admin_password
            krb.dm_password = admin_password
    
            #krb.__setup_sub_dict()
            if os.path.exists(paths.COMMON_KRB5_CONF_DIR):
                includes = 'includedir {}'.format(paths.COMMON_KRB5_CONF_DIR)
            else:
                includes = ''
    
            krb.sub_dict = dict(FQDN=krb.fqdn,
                                 IP=krb.ip,
                                 PASSWORD=krb.kdc_password,
                                 SUFFIX=krb.suffix,
                                 DOMAIN=krb.domain,
                                 HOST=krb.host,
                                 SERVER_ID=installutils.realm_to_serverid(krb.realm),
                                 REALM=krb.realm,
                                 KRB5KDC_KADM5_ACL=paths.KRB5KDC_KADM5_ACL,
                                 DICT_WORDS=paths.DICT_WORDS,
                                 KRB5KDC_KADM5_KEYTAB=paths.KRB5KDC_KADM5_KEYTAB,
                                 KDC_CERT=paths.KDC_CERT,
                                 KDC_KEY=paths.KDC_KEY,
                                 CACERT_PEM=paths.CACERT_PEM,
                                 KDC_CA_BUNDLE_PEM=paths.KDC_CA_BUNDLE_PEM,
                                 CA_BUNDLE_PEM=paths.CA_BUNDLE_PEM,
                                 INCLUDES=includes)
    
            # IPA server/KDC is not a subdomain of default domain
            # Proper domain-realm mapping needs to be specified
            domain = dnsname.from_text(krb.domain)
            fqdn = dnsname.from_text(krb.fqdn)
            if not fqdn.is_subdomain(domain):
                logger.debug("IPA FQDN '%s' is not located in default domain '%s'",
                             fqdn, domain)
                server_domain = fqdn.parent().to_unicode(omit_final_dot=True)
                logger.debug("Domain '%s' needs additional mapping in krb5.conf",
                             server_domain)
                dr_map = " .%(domain)s = %(realm)s\n %(domain)s = %(realm)s\n" \
                            % dict(domain=server_domain, realm=krb.realm)
            else:
                dr_map = ""
            krb.sub_dict['OTHER_DOMAIN_REALM_MAPS'] = dr_map
    
            # Configure KEYRING CCACHE if supported
            if kernel_keyring.is_persistent_keyring_supported():
                logger.debug("Enabling persistent keyring CCACHE")
                krb.sub_dict['OTHER_LIBDEFAULTS'] = \
                    " default_ccache_name = KEYRING:persistent:%{uid}\n"
            else:
                logger.debug("Persistent keyring CCACHE is not enabled")
                krb.sub_dict['OTHER_LIBDEFAULTS'] = ''
    
        return krb
    
    
    def ansible_module_get_parsed_ip_addresses(ansible_module,
                                               param='ip_addresses'):
        ip_addrs = [ ]
        for ip in ansible_module.params.get(param):
            try:
                ip_parsed = ipautil.CheckedIPAddress(ip)
            except Exception as e:
                ansible_module.fail_json(msg="Invalid IP Address %s: %s" % (ip, e))
            ip_addrs.append(ip_parsed)
        return ip_addrs
    
    
    def gen_remote_api(master_host_name, etc_ipa):
        ldapuri = 'ldaps://%s' % ipautil.format_netloc(master_host_name)
        xmlrpc_uri = 'https://{}/ipa/xml'.format(ipautil.format_netloc(master_host_name))
        remote_api = create_api(mode=None)
        remote_api.bootstrap(in_server=True,
                             context='installer',
                             confdir=etc_ipa,
                             ldap_uri=ldapuri,
                             xmlrpc_uri=xmlrpc_uri)
        remote_api.finalize()
        return remote_api