Example #1
0
    def test_ipaconf_env(self):
        ipa_confdir = os.environ.get('IPA_CONFDIR', None)
        try:
            with TempHome() as home:
                defaultconf = home.join('default.conf')
                with open(defaultconf, 'w') as f:
                    f.write(textwrap.dedent("""
                        [global]
                        basedn = dc=ipa,dc=test
                        realm = IPA.TEST
                        domain = ipa.test
                        """)
                    )
                os.environ['IPA_CONFDIR'] = home.path
                api = create_api(mode='unit_test')
                api.bootstrap()
                api.finalize()
                assert api.env.confdir == home.path
                assert api.env.conf_default == defaultconf
                assert api.env.realm == 'IPA.TEST'
                assert api.env.domain == 'ipa.test'

                os.environ['IPA_CONFDIR'] = home.join('invalid')
                api = create_api(mode='unit_test')
                with pytest.raises(errors.EnvironmentError):
                    api.bootstrap()
        finally:
            if ipa_confdir:
                os.environ['IPA_CONFDIR'] = ipa_confdir
            else:
                os.environ.pop('IPA_CONFDIR')
Example #2
0
    def test_ipaconf_env(self):
        ipa_confdir = os.environ.get('IPA_CONFDIR', None)
        try:
            with TempHome() as home:
                defaultconf = home.join('default.conf')
                with open(defaultconf, 'w') as f:
                    f.write(
                        textwrap.dedent("""
                        [global]
                        basedn = dc=ipa,dc=test
                        realm = IPA.TEST
                        domain = ipa.test
                        """))
                os.environ['IPA_CONFDIR'] = home.path
                api = create_api(mode='unit_test')
                api.bootstrap()
                api.finalize()
                assert api.env.confdir == home.path
                assert api.env.conf_default == defaultconf
                assert api.env.realm == 'IPA.TEST'
                assert api.env.domain == 'ipa.test'

                os.environ['IPA_CONFDIR'] = home.join('invalid')
                api = create_api(mode='unit_test')
                with pytest.raises(errors.EnvironmentError):
                    api.bootstrap()
        finally:
            if ipa_confdir:
                os.environ['IPA_CONFDIR'] = ipa_confdir
            else:
                os.environ.pop('IPA_CONFDIR')
Example #3
0
 def test_args_valid_option(self):
     sys.argv = ['/usr/bin/ipa', '-v', 'user-show']
     api = create_api(mode='unit_test')
     (_options,
      argv) = api.bootstrap_with_global_options(context='unit_test')
     assert argv == ['user-show']
     assert _options.verbose == 1
Example #4
0
    def test_Backend(self):
        """
        Test using the ldap2 Backend directly (ala ipa-server-install)
        """

        # Create our own api because the one generated for the tests is
        # a client-only api. Then we register in the commands and objects
        # we need for the test.
        myapi = create_api(mode=None)
        myapi.bootstrap(context='cli', in_server=True, in_tree=True)
        myapi.register(ldap2)
        myapi.register(host)
        myapi.register(service)
        myapi.register(service_show)
        myapi.finalize()

        pwfile = api.env.dot_ipa + os.sep + ".dmpw"
        if ipautil.file_exists(pwfile):
            fp = open(pwfile, "r")
            dm_password = fp.read().rstrip()
            fp.close()
        else:
            raise nose.SkipTest("No directory manager password in %s" % pwfile)
        myapi.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password)

        result = myapi.Command['service_show']('ldap/%s@%s' %  (api.env.host, api.env.realm,))
        entry_attrs = result['result']
        cert = entry_attrs.get('usercertificate')
        cert = cert[0]
        serial = unicode(x509.get_serial_number(cert, x509.DER))
        assert serial is not None
Example #5
0
def application(environ, start_response):
    if environ.get('REQUEST_METHOD', None) != 'POST':
        return wsgi_redirect(start_response, 'index.html')

    content_type = environ.get('CONTENT_TYPE', '').lower()
    if not content_type.startswith('application/x-www-form-urlencoded'):
        return bad_request(start_response)

    form_data = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
    if 'username' not in form_data or 'password' not in form_data:
        return bad_request(start_response)

    status = '200 Success'
    response_headers = []
    result = 'error'
    response = b''

    # API object only for configuration, finalize() not needed
    api = create_api(mode=None)
    api.bootstrap(context='server', confdir=paths.ETC_IPA, in_server=True)
    try:
        bind(api.env.ldap_uri, api.env.basedn,
             form_data['username'].value, form_data['password'].value)
    except IOError as err:
        if err.errno == errno.EPERM:
            result = 'invalid-password'
        if err.errno == errno.EIO:
            result = 'migration-error'
    else:
        result = 'ok'
    response_headers.append(('X-IPA-Migrate-Result', result))
    start_response(status, response_headers)
    return [response]
Example #6
0
    def test_Backend(self):
        """
        Test using the ldap2 Backend directly (ala ipa-server-install)
        """

        # Create our own api because the one generated for the tests is
        # a client-only api. Then we register in the commands and objects
        # we need for the test.
        myapi = create_api(mode=None)
        myapi.bootstrap(context='cli', in_server=True, in_tree=True)
        myapi.register(ldap2)
        myapi.register(host)
        myapi.register(service)
        myapi.register(service_show)
        myapi.finalize()

        pwfile = api.env.dot_ipa + os.sep + ".dmpw"
        if ipautil.file_exists(pwfile):
            fp = open(pwfile, "r")
            dm_password = fp.read().rstrip()
            fp.close()
        else:
            raise nose.SkipTest("No directory manager password in %s" % pwfile)
        myapi.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
                                    bind_pw=dm_password)

        result = myapi.Command['service_show']('ldap/%s@%s' % (
            api.env.host,
            api.env.realm,
        ))
        entry_attrs = result['result']
        cert = entry_attrs.get('usercertificate')
        cert = cert[0]
        serial = unicode(x509.get_serial_number(cert, x509.DER))
        assert serial is not None
Example #7
0
    def test_Backend(self):
        """
        Test using the ldap2 Backend directly (ala ipa-server-install)
        """

        # Create our own api because the one generated for the tests is
        # a client-only api. Then we register in the commands and objects
        # we need for the test.
        myapi = create_api(mode=None)
        myapi.bootstrap(context='cli', in_server=True, confdir=paths.ETC_IPA)
        myapi.finalize()

        pwfile = api.env.dot_ipa + os.sep + ".dmpw"
        if os.path.isfile(pwfile):
            with open(pwfile, "r") as fp:
                dm_password = fp.read().rstrip()
        else:
            raise unittest.SkipTest(
                "No directory manager password in %s" % pwfile
            )
        myapi.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password)

        result = myapi.Command['service_show']('ldap/%s@%s' %  (api.env.host, api.env.realm,))
        entry_attrs = result['result']
        cert = entry_attrs.get('usercertificate')[0]
        assert cert.serial_number is not None
Example #8
0
 def test_args_invalid_option(self):
     sys.argv = ['/usr/bin/ipa', '-verbose', 'user-show']
     api = create_api(mode='unit_test')
     try:
         api.bootstrap_with_global_options(context='unit_test')
     except errors.OptionError as e:
         assert e.msg == 'Unable to parse option rbose'
Example #9
0
def application(environ, start_response):
    if environ.get('REQUEST_METHOD', None) != 'POST':
        return wsgi_redirect(start_response, 'index.html')

    content_type = environ.get('CONTENT_TYPE', '').lower()
    if not content_type.startswith('application/x-www-form-urlencoded'):
        return bad_request(start_response)

    form_data = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
    if 'username' not in form_data or 'password' not in form_data:
        return bad_request(start_response)

    status = '200 Success'
    response_headers = []
    result = 'error'
    response = b''

    # API object only for configuration, finalize() not needed
    api = create_api(mode=None)
    api.bootstrap(context='server', confdir=paths.ETC_IPA, in_server=True)
    try:
        bind(api.env.ldap_uri, api.env.basedn, form_data['username'].value,
             form_data['password'].value)
    except IOError as err:
        if err.errno == errno.EPERM:
            result = 'invalid-password'
        if err.errno == errno.EIO:
            result = 'migration-error'
    else:
        result = 'ok'
    response_headers.append(('X-IPA-Migrate-Result', result))
    start_response(status, response_headers)
    return [response]
Example #10
0
 def test_no_args(self):
     sys.argv = ['/usr/bin/ipa']
     api = create_api(mode='unit_test')
     (_options,
      argv) = api.bootstrap_with_global_options(context='unit_test')
     assert len(argv) == 0
     assert _options.env is None
     assert _options.conf is None
     assert _options.debug is None
     assert _options.delegate is None
     assert _options.verbose is None
Example #11
0
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
Example #12
0
def create_test_api(**kw):
    """
    Returns (api, home) tuple.

    This function returns a tuple containing an `ipalib.plugable.API`
    instance and a `TempHome` instance.
    """
    home = TempHome()
    api = ipalib.create_api(mode='unit_test')
    api.env.in_tree = True
    for (key, value) in kw.items():
        api.env[key] = value
    return (api, home)
Example #13
0
def create_test_api(**kw):
    """
    Returns (api, home) tuple.

    This function returns a tuple containing an `ipalib.plugable.API`
    instance and a `TempHome` instance.
    """
    home = TempHome()
    api = ipalib.create_api(mode='unit_test')
    api.env.in_tree = True
    for (key, value) in kw.items():
        api.env[key] = value
    return (api, home)
Example #14
0
def mock_api(request):
    test_api = create_api(mode=None)
    test_api.bootstrap(in_server=True, ldap_uri=api.env.ldap_uri)
    test_api.finalize()

    if not test_api.Backend.ldap2.isconnected():
        test_api.Backend.ldap2.connect()

    def finalize():
        test_api.Backend.ldap2.disconnect()

    request.addfinalizer(finalize)

    return test_api
Example #15
0
def mock_api(request):
    test_api = create_api(mode=None)
    test_api.bootstrap(in_server=True, ldap_uri=api.env.ldap_uri)
    test_api.finalize()

    if not test_api.Backend.ldap2.isconnected():
        test_api.Backend.ldap2.connect()

    def finalize():
        test_api.Backend.ldap2.disconnect()

    request.addfinalizer(finalize)

    return test_api
Example #16
0
def application(environ, start_response):
    if environ.get("REQUEST_METHOD", None) != "POST":
        return wsgi_redirect(start_response, "index.html")

    form_data = cgi.FieldStorage(fp=environ["wsgi.input"], environ=environ)
    if "username" not in form_data or "password" not in form_data:
        return wsgi_redirect(start_response, "invalid.html")

    # API object only for configuration, finalize() not needed
    api = create_api(mode=None)
    api.bootstrap(context="server", in_server=True)
    try:
        bind(api.env.ldap_uri, api.env.basedn, form_data["username"].value, form_data["password"].value)
    except IOError as err:
        if err.errno == errno.EPERM:
            return wsgi_redirect(start_response, "invalid.html")
        if err.errno == errno.EIO:
            return wsgi_redirect(start_response, "error.html")

    ui_url = get_ui_url(environ)
    return wsgi_redirect(start_response, ui_url)
Example #17
0
def application(environ, start_response):
    if environ.get('REQUEST_METHOD', None) != 'POST':
        return wsgi_redirect(start_response, 'index.html')

    form_data = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
    if 'username' not in form_data or 'password' not in form_data:
        return wsgi_redirect(start_response, 'invalid.html')

    # API object only for configuration, finalize() not needed
    api = create_api(mode=None)
    api.bootstrap(context='server', confdir=paths.ETC_IPA, in_server=True)
    try:
        bind(api.env.ldap_uri, api.env.basedn,
             form_data['username'].value, form_data['password'].value)
    except IOError as err:
        if err.errno == errno.EPERM:
            return wsgi_redirect(start_response, 'invalid.html')
        if err.errno == errno.EIO:
            return wsgi_redirect(start_response, 'error.html')

    ui_url = get_ui_url(environ)
    return wsgi_redirect(start_response, ui_url)
Example #18
0
def application(environ, start_response):
    if environ.get('REQUEST_METHOD', None) != 'POST':
        return wsgi_redirect(start_response, 'index.html')

    form_data = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
    if 'username' not in form_data or 'password' not in form_data:
        return wsgi_redirect(start_response, 'invalid.html')

    # API object only for configuration, finalize() not needed
    api = create_api(mode=None)
    api.bootstrap(context='server', confdir=paths.ETC_IPA, in_server=True)
    try:
        bind(api.env.ldap_uri, api.env.basedn, form_data['username'].value,
             form_data['password'].value)
    except IOError as err:
        if err.errno == errno.EPERM:
            return wsgi_redirect(start_response, 'invalid.html')
        if err.errno == errno.EIO:
            return wsgi_redirect(start_response, 'error.html')

    ui_url = get_ui_url(environ)
    return wsgi_redirect(start_response, ui_url)
Example #19
0
    def __init__(self, dm_password=_sentinel, sub_dict=None,
                 online=_sentinel, ldapi=_sentinel, api=api):
        '''
        :parameters:
            dm_password
                deprecated and no longer used
            sub_dict
                substitution dictionary
            online
                deprecated and no longer used
            ldapi
                deprecated and no longer used
            api
                bootstrapped API object (for configuration)

        Data Structure Example:
        -----------------------

        dn_by_rdn_count = {
            3: 'cn=config,dc=example,dc=com':
            4: 'cn=bob,ou=people,dc=example,dc=com',
        }

        all_updates = [
            {
                'dn': 'cn=config,dc=example,dc=com',
                'default': [
                    dict(attr='attr1', value='default1'),
                ],
                'updates': [
                    dict(action='action', attr='attr1', value='value1'),
                    dict(action='replace', attr='attr2', value=['old', 'new']),
                ]
            },
            {
                'dn': 'cn=bob,ou=people,dc=example,dc=com',
                'default': [
                    dict(attr='attr3', value='default3'),
                ],
                'updates': [
                    dict(action='action', attr='attr3', value='value3'),
                    dict(action='action', attr='attr4', value='value4'),
                }
            }
        ]

        Please notice the replace action requires two values in list

        The default and update lists are "dispositions"

        Plugins:

        Plugins has to be specified in update file to be executed, using
        'plugin' directive

        Example:
        plugin: update_uniqueness_plugins_to_new_syntax

        Each plugin returns two values:

        1. restart: dirsrv will be restarted AFTER this update is
                     applied.
        2. updates: A list of updates to be applied.

        The value of an update is a dictionary with the following possible
        values:
          - dn: DN, equal to the dn attribute
          - updates: list of updates against the dn
          - default: list of the default entry to be added if it doesn't
                     exist
          - deleteentry: list of dn's to be deleted (typically single dn)

        For example, this update file:

          dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
          replace:krbPwdLockoutDuration:10::600
          replace: krbPwdMaxFailure:3::6

        Generates this list which contain the update dictionary:

        [
          {
            'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
            'updates': [
              dict(action='replace', attr='krbPwdLockoutDuration',
                   value=['10','600']),
              dict(action='replace', attr='krbPwdMaxFailure',
                   value=['3','6']),
            ]
          }
        ]

        Here is another example showing how a default entry is configured:

          dn: cn=Managed Entries,cn=etc,$SUFFIX
          default: objectClass: nsContainer
          default: objectClass: top
          default: cn: Managed Entries

        This generates:

        [
          {
            'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
            'default': [
              dict(attr='objectClass', value='nsContainer'),
              dict(attr='objectClass', value='top'),
              dict(attr='cn', value='Managed Entries'),
            ]
          }
        ]

        Note that the variable substitution in both examples has been completed.

        Either may make changes directly in LDAP or can return updates in
        update format.

        '''
        if any(arg is not _sentinel for arg in (dm_password, online, ldapi)):
            warnings.warn(
                "dm_password, online, and ldapi arguments are deprecated",
                DeprecationWarning,
                stacklevel=2
            )
        self.sub_dict = sub_dict if sub_dict is not None else {}
        self.conn = None
        self.modified = False
        self.ldapuri = ipaldap.realm_to_ldapi_uri(api.env.realm)

        self.api = create_api(mode=None)
        self.api.bootstrap(
            in_server=True,
            context='updates',
            confdir=paths.ETC_IPA,
            ldap_uri=self.ldapuri
        )
        self.api.finalize()
        self.create_connection()

        # get ipa-local domain idrange settings
        domain_range = f"{self.api.env.realm}_id_range"
        try:
            result = self.api.Command.idrange_show(domain_range)["result"]
        except errors.NotFound:
            idstart = None
            idmax = None
        else:
            idstart = int(result['ipabaseid'][0])
            idrange_size = int(result['ipaidrangesize'][0])
            idmax = idstart + idrange_size - 1

        default_sub = get_sub_dict(
            realm=api.env.realm,
            domain=api.env.domain,
            suffix=api.env.basedn,
            fqdn=api.env.host,
            idstart=idstart,
            idmax=idmax,
        )
        replication_plugin = (
            installutils.get_replication_plugin_name(self.conn.get_entry)
        )
        default_sub["REPLICATION_PLUGIN"] = replication_plugin

        for k, v in default_sub.items():
            self.sub_dict.setdefault(k, v)
Example #20
0
    def __init__(self, dm_password=None, sub_dict={},
                 online=True, ldapi=False):
        '''
        :parameters:
            dm_password
                Directory Manager password
            sub_dict
                substitution dictionary
            online
                Do an online LDAP update or use an experimental LDIF updater
            ldapi
                Bind using ldapi. This assumes autobind is enabled.

        Data Structure Example:
        -----------------------

        dn_by_rdn_count = {
            3: 'cn=config,dc=example,dc=com':
            4: 'cn=bob,ou=people,dc=example,dc=com',
        }

        all_updates = [
            {
                'dn': 'cn=config,dc=example,dc=com',
                'default': [
                    dict(attr='attr1', value='default1'),
                ],
                'updates': [
                    dict(action='action', attr='attr1', value='value1'),
                    dict(action='replace', attr='attr2', value=['old', 'new']),
                ]
            },
            {
                'dn': 'cn=bob,ou=people,dc=example,dc=com',
                'default': [
                    dict(attr='attr3', value='default3'),
                ],
                'updates': [
                    dict(action='action', attr='attr3', value='value3'),
                    dict(action='action', attr='attr4', value='value4'),
                }
            }
        ]

        Please notice the replace action requires two values in list

        The default and update lists are "dispositions"

        Plugins:

        Plugins has to be specified in update file to be executed, using
        'plugin' directive

        Example:
        plugin: update_uniqueness_plugins_to_new_syntax

        Each plugin returns two values:

        1. restart: dirsrv will be restarted AFTER this update is
                     applied.
        2. updates: A list of updates to be applied.

        The value of an update is a dictionary with the following possible
        values:
          - dn: DN, equal to the dn attribute
          - updates: list of updates against the dn
          - default: list of the default entry to be added if it doesn't
                     exist
          - deleteentry: list of dn's to be deleted (typically single dn)

        For example, this update file:

          dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
          replace:krbPwdLockoutDuration:10::600
          replace: krbPwdMaxFailure:3::6

        Generates this list which contain the update dictionary:

        [
          {
            'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
            'updates': [
              dict(action='replace', attr='krbPwdLockoutDuration',
                   value=['10','600']),
              dict(action='replace', attr='krbPwdMaxFailure',
                   value=['3','6']),
            ]
          }
        ]

        Here is another example showing how a default entry is configured:

          dn: cn=Managed Entries,cn=etc,$SUFFIX
          default: objectClass: nsContainer
          default: objectClass: top
          default: cn: Managed Entries

        This generates:

        [
          {
            'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
            'default': [
              dict(attr='objectClass', value='nsContainer'),
              dict(attr='objectClass', value='top'),
              dict(attr='cn', value='Managed Entries'),
            ]
          }
        ]

        Note that the variable substitution in both examples has been completed.

        Either may make changes directly in LDAP or can return updates in
        update format.

        '''
        log_mgr.get_logger(self, True)
        self.sub_dict = sub_dict
        self.dm_password = dm_password
        self.conn = None
        self.modified = False
        self.online = online
        self.ldapi = ldapi
        self.pw_name = pwd.getpwuid(os.geteuid()).pw_name
        self.realm = None
        self.socket_name = (
            paths.SLAPD_INSTANCE_SOCKET_TEMPLATE %
            api.env.realm.replace('.', '-')
        )
        self.ldapuri = 'ldapi://%s' % ipautil.format_netloc(
            self.socket_name
        )
        suffix = None

        if sub_dict.get("REALM"):
            self.realm = sub_dict["REALM"]
        else:
            self.realm = api.env.realm
            suffix = ipautil.realm_to_suffix(self.realm) if self.realm else None

        if suffix is not None:
            assert isinstance(suffix, DN)
        domain = ipautil.get_domain_name()
        libarch = self._identify_arch()

        fqdn = installutils.get_fqdn()
        if fqdn is None:
            raise RuntimeError("Unable to determine hostname")

        if not self.sub_dict.get("REALM") and self.realm is not None:
            self.sub_dict["REALM"] = self.realm
        if not self.sub_dict.get("FQDN"):
            self.sub_dict["FQDN"] = fqdn
        if not self.sub_dict.get("DOMAIN"):
            self.sub_dict["DOMAIN"] = domain
        if not self.sub_dict.get("SUFFIX") and suffix is not None:
            self.sub_dict["SUFFIX"] = suffix
        if not self.sub_dict.get("ESCAPED_SUFFIX"):
            self.sub_dict["ESCAPED_SUFFIX"] = str(suffix)
        if not self.sub_dict.get("LIBARCH"):
            self.sub_dict["LIBARCH"] = libarch
        if not self.sub_dict.get("TIME"):
            self.sub_dict["TIME"] = int(time.time())
        if not self.sub_dict.get("DOMAIN") and domain is not None:
            self.sub_dict["DOMAIN"] = domain
        if not self.sub_dict.get("MIN_DOMAIN_LEVEL"):
            self.sub_dict["MIN_DOMAIN_LEVEL"] = str(constants.MIN_DOMAIN_LEVEL)
        if not self.sub_dict.get("MAX_DOMAIN_LEVEL"):
            self.sub_dict["MAX_DOMAIN_LEVEL"] = str(constants.MAX_DOMAIN_LEVEL)
        if not self.sub_dict.get("STRIP_ATTRS"):
            self.sub_dict["STRIP_ATTRS"] = "%s" % (
                " ".join(constants.REPL_AGMT_STRIP_ATTRS),)
        if not self.sub_dict.get("EXCLUDES"):
            self.sub_dict["EXCLUDES"] = "(objectclass=*) $ EXCLUDE %s" % (
                " ".join(constants.REPL_AGMT_EXCLUDES),)
        if not self.sub_dict.get("TOTAL_EXCLUDES"):
            self.sub_dict["TOTAL_EXCLUDES"] = "(objectclass=*) $ EXCLUDE " + \
                " ".join(constants.REPL_AGMT_TOTAL_EXCLUDES)
        self.api = create_api(mode=None)
        self.api.bootstrap(in_server=True, context='updates')
        self.api.finalize()
        if online:
            # Try out the connection/password
            # (This will raise if the server is not available)
            self.create_connection()
            self.close_connection()
        else:
            raise RuntimeError("Offline updates are not supported.")
Example #21
0
    def __init__(self,
                 dm_password=None,
                 sub_dict={},
                 online=True,
                 ldapi=False):
        '''
        :parameters:
            dm_password
                Directory Manager password
            sub_dict
                substitution dictionary
            online
                Do an online LDAP update or use an experimental LDIF updater
            ldapi
                Bind using ldapi. This assumes autobind is enabled.

        Data Structure Example:
        -----------------------

        dn_by_rdn_count = {
            3: 'cn=config,dc=example,dc=com':
            4: 'cn=bob,ou=people,dc=example,dc=com',
        }

        all_updates = [
            {
                'dn': 'cn=config,dc=example,dc=com',
                'default': ['attr1':default1'],
                'updates': ['action:attr1:value1',
                            'action:attr2:value2]
            },
            {
                'dn': 'cn=bob,ou=people,dc=example,dc=com',
                'default': ['attr3':default3'],
                'updates': ['action:attr3:value3',
                            'action:attr4:value4],
            }
        ]

        The default and update lists are "dispositions"

        Plugins:

        Plugins has to be specified in update file to be executed, using
        'plugin' directive

        Example:
        plugin: update_uniqueness_plugins_to_new_syntax

        Each plugin returns two values:

        1. restart: dirsrv will be restarted AFTER this update is
                     applied.
        2. updates: A list of updates to be applied.

        The value of an update is a dictionary with the following possible
        values:
          - dn: DN, equal to the dn attribute
          - updates: list of updates against the dn
          - default: list of the default entry to be added if it doesn't
                     exist
          - deleteentry: list of dn's to be deleted (typically single dn)

        For example, this update file:

          dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
          replace:krbPwdLockoutDuration:10::600
          replace: krbPwdMaxFailure:3::6

        Generates this list which contain the update dictionary:

        [
          dict(
            'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
            'updates': ['replace:krbPwdLockoutDuration:10::600',
                        'replace:krbPwdMaxFailure:3::6']
          )
        ]

        Here is another example showing how a default entry is configured:

          dn: cn=Managed Entries,cn=etc,$SUFFIX
          default: objectClass: nsContainer
          default: objectClass: top
          default: cn: Managed Entries

        This generates:

        [
          dict(
            'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
            'default': ['objectClass:nsContainer',
                        'objectClass:top',
                        'cn:Managed Entries'
                       ]
           )
        ]

        Note that the variable substitution in both examples has been completed.

        Either may make changes directly in LDAP or can return updates in
        update format.

        '''
        log_mgr.get_logger(self, True)
        self.sub_dict = sub_dict
        self.dm_password = dm_password
        self.conn = None
        self.modified = False
        self.online = online
        self.ldapi = ldapi
        self.pw_name = pwd.getpwuid(os.geteuid()).pw_name
        self.realm = None
        self.socket_name = (paths.SLAPD_INSTANCE_SOCKET_TEMPLATE %
                            api.env.realm.replace('.', '-'))
        self.ldapuri = 'ldapi://%s' % ipautil.format_netloc(self.socket_name)
        suffix = None

        if sub_dict.get("REALM"):
            self.realm = sub_dict["REALM"]
        else:
            krbctx = krbV.default_context()
            try:
                self.realm = krbctx.default_realm
                suffix = ipautil.realm_to_suffix(self.realm)
            except krbV.Krb5Error:
                self.realm = None
                suffix = None

        if suffix is not None:
            assert isinstance(suffix, DN)
        domain = ipautil.get_domain_name()
        libarch = self._identify_arch()

        fqdn = installutils.get_fqdn()
        if fqdn is None:
            raise RuntimeError("Unable to determine hostname")

        if not self.sub_dict.get("REALM") and self.realm is not None:
            self.sub_dict["REALM"] = self.realm
        if not self.sub_dict.get("FQDN"):
            self.sub_dict["FQDN"] = fqdn
        if not self.sub_dict.get("DOMAIN"):
            self.sub_dict["DOMAIN"] = domain
        if not self.sub_dict.get("SUFFIX") and suffix is not None:
            self.sub_dict["SUFFIX"] = suffix
        if not self.sub_dict.get("ESCAPED_SUFFIX"):
            self.sub_dict["ESCAPED_SUFFIX"] = str(suffix)
        if not self.sub_dict.get("LIBARCH"):
            self.sub_dict["LIBARCH"] = libarch
        if not self.sub_dict.get("TIME"):
            self.sub_dict["TIME"] = int(time.time())
        if not self.sub_dict.get("DOMAIN") and domain is not None:
            self.sub_dict["DOMAIN"] = domain
        self.api = create_api(mode=None)
        self.api.bootstrap(in_server=True, context='updates')
        self.api.finalize()
        if online:
            # Try out the connection/password
            # (This will raise if the server is not available)
            self.create_connection()
            self.close_connection()
        else:
            raise RuntimeError("Offline updates are not supported.")
Example #22
0
    def __init__(self, dm_password=_sentinel, sub_dict=None,
                 online=_sentinel, ldapi=_sentinel, api=api):
        '''
        :parameters:
            dm_password
                deprecated and no longer used
            sub_dict
                substitution dictionary
            online
                deprecated and no longer used
            ldapi
                deprecated and no longer used
            api
                bootstrapped API object (for configuration)

        Data Structure Example:
        -----------------------

        dn_by_rdn_count = {
            3: 'cn=config,dc=example,dc=com':
            4: 'cn=bob,ou=people,dc=example,dc=com',
        }

        all_updates = [
            {
                'dn': 'cn=config,dc=example,dc=com',
                'default': [
                    dict(attr='attr1', value='default1'),
                ],
                'updates': [
                    dict(action='action', attr='attr1', value='value1'),
                    dict(action='replace', attr='attr2', value=['old', 'new']),
                ]
            },
            {
                'dn': 'cn=bob,ou=people,dc=example,dc=com',
                'default': [
                    dict(attr='attr3', value='default3'),
                ],
                'updates': [
                    dict(action='action', attr='attr3', value='value3'),
                    dict(action='action', attr='attr4', value='value4'),
                }
            }
        ]

        Please notice the replace action requires two values in list

        The default and update lists are "dispositions"

        Plugins:

        Plugins has to be specified in update file to be executed, using
        'plugin' directive

        Example:
        plugin: update_uniqueness_plugins_to_new_syntax

        Each plugin returns two values:

        1. restart: dirsrv will be restarted AFTER this update is
                     applied.
        2. updates: A list of updates to be applied.

        The value of an update is a dictionary with the following possible
        values:
          - dn: DN, equal to the dn attribute
          - updates: list of updates against the dn
          - default: list of the default entry to be added if it doesn't
                     exist
          - deleteentry: list of dn's to be deleted (typically single dn)

        For example, this update file:

          dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
          replace:krbPwdLockoutDuration:10::600
          replace: krbPwdMaxFailure:3::6

        Generates this list which contain the update dictionary:

        [
          {
            'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
            'updates': [
              dict(action='replace', attr='krbPwdLockoutDuration',
                   value=['10','600']),
              dict(action='replace', attr='krbPwdMaxFailure',
                   value=['3','6']),
            ]
          }
        ]

        Here is another example showing how a default entry is configured:

          dn: cn=Managed Entries,cn=etc,$SUFFIX
          default: objectClass: nsContainer
          default: objectClass: top
          default: cn: Managed Entries

        This generates:

        [
          {
            'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
            'default': [
              dict(attr='objectClass', value='nsContainer'),
              dict(attr='objectClass', value='top'),
              dict(attr='cn', value='Managed Entries'),
            ]
          }
        ]

        Note that the variable substitution in both examples has been completed.

        Either may make changes directly in LDAP or can return updates in
        update format.

        '''
        if any(arg is not _sentinel for arg in (dm_password, online, ldapi)):
            warnings.warn(
                "dm_password, online, and ldapi arguments are deprecated",
                DeprecationWarning,
                stacklevel=2
            )
        self.sub_dict = sub_dict if sub_dict is not None else {}
        self.conn = None
        self.modified = False
        self.ldapuri = ipaldap.realm_to_ldapi_uri(api.env.realm)

        default_sub = dict(
            REALM=api.env.realm,
            DOMAIN=api.env.domain,
            SUFFIX=api.env.basedn,
            ESCAPED_SUFFIX=str(api.env.basedn),
            FQDN=api.env.host,
            LIBARCH=paths.LIBARCH,
            TIME=int(time.time()),
            MIN_DOMAIN_LEVEL=str(constants.MIN_DOMAIN_LEVEL),
            MAX_DOMAIN_LEVEL=str(constants.MAX_DOMAIN_LEVEL),
            STRIP_ATTRS=" ".join(constants.REPL_AGMT_STRIP_ATTRS),
            EXCLUDES="(objectclass=*) $ EXCLUDE %s" % (
                " ".join(constants.REPL_AGMT_EXCLUDES)
            ),
            TOTAL_EXCLUDES="(objectclass=*) $ EXCLUDE %s" % (
                " ".join(constants.REPL_AGMT_TOTAL_EXCLUDES)
            ),
            SELINUX_USERMAP_DEFAULT=platformconstants.SELINUX_USERMAP_DEFAULT,
            SELINUX_USERMAP_ORDER=platformconstants.SELINUX_USERMAP_ORDER,
            FIPS="#" if tasks.is_fips_enabled() else "",
        )
        for k, v in default_sub.items():
            self.sub_dict.setdefault(k, v)

        self.api = create_api(mode=None)
        self.api.bootstrap(
            in_server=True,
            context='updates',
            confdir=paths.ETC_IPA,
            ldap_uri=self.ldapuri
        )
        self.api.finalize()
Example #23
0
def install(filename, options):
    global config

    dogtag_constants = dogtag.install_constants

    # 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_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()

        # Configure ntpd
        if options.conf_ntp:
            ipaclient.ntpconf.force_ntpd(sstore)
            ntp = ntpinstance.NTPInstance()
            ntp.create_instance()

        # Configure dirsrv
        ds = install_replica_ds(config)

        # Always try to install DNS records
        install_dns_records(config, options, remote_api)
    finally:
        if conn.isconnected():
            conn.disconnect()

    if config.setup_ca:
        options.realm_name = config.realm_name
        options.domain_name = config.domain_name
        options.dm_password = config.dirman_password
        options.host_name = config.host_name

        ca.install(False, config, options)

    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))

    # 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 config.setup_ca:
        dogtag_service = services.knownservices[dogtag_constants.SERVICE_NAME]
        dogtag_service.restart(dogtag_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")
Example #24
0
def add_new_adtrust_agents(api, options):
    """
    Find out IPA masters which are not part of the cn=adtrust agents
    and propose them to be added to the list
    :param api: API instance
    :param options: parsed CLI options
    """
    potential_agents_cns = retrieve_potential_adtrust_agents(api)

    if potential_agents_cns:
        print("")
        print("WARNING: %d IPA masters are not yet able to serve "
              "information about users from trusted forests."
              % len(potential_agents_cns))
        print("Installer can add them to the list of IPA masters "
              "allowed to access information about trusts.")
        print("If you choose to do so, you also need to restart "
              "LDAP service on those masters.")
        print("Refer to ipa-adtrust-install(1) man page for details.")
        print("")
        if options.unattended:
            print("Unattended mode was selected, installer will NOT "
                  "add other IPA masters to the list of allowed to")
            print("access information about trusted forests!")
            return

    new_agents = []

    for name in sorted(potential_agents_cns):
        if ipautil.user_input(
                "IPA master [%s]?" % (name),
                default=False,
                allow_empty=False):
            new_agents.append(name)

    if new_agents:
        add_hosts_to_adtrust_agents(api, new_agents)

        # The method trust_enable_agent was added on API version 2.236
        # Specifically request this version in the remote call
        kwargs = {u'version': u'2.236',
                  u'enable_compat': options.enable_compat}
        failed_agents = []
        for agent in new_agents:
            # Try to run the ipa-trust-enable-agent script on the agent
            # If the agent is too old and does not support this,
            # print a msg
            logger.info("Execute trust_enable_agent on remote server %s",
                        agent)
            client = None
            try:
                xmlrpc_uri = 'https://{}/ipa/xml'.format(
                    ipautil.format_netloc(agent))
                remote_api = create_api(mode=None)
                remote_api.bootstrap(context='installer',
                                     confdir=paths.ETC_IPA,
                                     xmlrpc_uri=xmlrpc_uri,
                                     fallback=False)
                client = rpc.jsonclient(remote_api)
                client.finalize()
                client.connect()
                result = client.forward(
                    u'trust_enable_agent',
                    ipautil.fsdecode(agent),
                    **kwargs)
            except errors.CommandError as e:
                logger.debug(
                    "Remote server %s does not support agent enablement "
                    "over RPC: %s", agent, e)
                failed_agents.append(agent)
            except (errors.PublicError, ConnectionRefusedError) as e:
                logger.debug(
                    "Remote call to trust_enable_agent failed on server %s: "
                    "%s", agent, e)
                failed_agents.append(agent)
            else:
                for message in result.get('messages'):
                    logger.debug('%s', message['message'])
                if not int(result['result']):
                    logger.debug(
                        "ipa-trust-enable-agent returned non-zero exit code "
                        " on server %s", agent)
                    failed_agents.append(agent)
            finally:
                if client and client.isconnected():
                    client.disconnect()

        # if enablement failed on some agents, print a WARNING:
        if failed_agents:
            if options.enable_compat:
                print("""
WARNING: you MUST manually enable the Schema compatibility Plugin and """)
            print("""
WARNING: you MUST restart (both "ipactl restart" and "systemctl restart sssd")
the following IPA masters in order to activate them to serve information about
users from trusted forests:
""")

            for x in failed_agents:
                print(x)
Example #25
0
        fqdns = result['result']['fqdns']
        if detail:
            # print per host details
            for fqdn in fqdns:
                textui.print_plain("Hostname %s" % (fqdn))
                details = result['result']['details'][fqdn].items()
                details.sort(key=lambda x: x[0])
                textui.print_keyval(details)
        else:
            # print list of hostnames
             textui.print_plain(" ".join(fqdns))


if __name__ == '__main__':
    from ipalib import create_api
    api = create_api()
else:
    from ipalib import api


api.register(aii)
api.register(aii_find)


if __name__ == '__main__':
    api.finalize()

    from ipalib import cli
    textui = cli.textui()

    args = [unicode('somehost'), unicode('somesubdomain.somedomain')]
Example #26
0
def promote_check(installer):
    options = installer
    installer._enrollment_performed = False
    installer._top_dir = tempfile.mkdtemp("ipa")

    # check selinux status, http and DS ports, NTP conflicting services
    common_check(options.no_ntp)

    client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
    if not client_fstore.has_files():
        ensure_enrolled(installer)
    else:
        if (options.domain_name or options.server or options.realm_name
                or options.host_name or options.password or options.keytab):
            print("IPA client is already configured on this system, ignoring "
                  "the --domain, --server, --realm, --hostname, --password "
                  "and --keytab options.")

        # The NTP configuration can not be touched on pre-installed client:
        if options.no_ntp or options.ntp_servers or options.ntp_pool:
            raise ScriptError(
                "NTP configuration cannot be updated during promotion")

    sstore = sysrestore.StateFile(paths.SYSRESTORE)

    fstore = sysrestore.FileStore(paths.SYSRESTORE)

    env = Env()
    env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None)
    env._finalize_core(**dict(constants.DEFAULT_CONFIG))

    # 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()

    config = ReplicaConfig()
    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 = installer._top_dir
    config.basedn = api.env.basedn

    http_pkcs12_file = None
    http_pkcs12_info = None
    http_ca_cert = None
    dirsrv_pkcs12_file = None
    dirsrv_pkcs12_info = None
    dirsrv_ca_cert = None
    pkinit_pkcs12_file = None
    pkinit_pkcs12_info = None
    pkinit_ca_cert = None

    if options.http_cert_files:
        if options.http_pin is None:
            options.http_pin = installutils.read_password(
                "Enter Apache Server private key unlock",
                confirm=False,
                validate=False,
                retry=False)
            if options.http_pin is None:
                raise ScriptError(
                    "Apache Server private key unlock password required")
        http_pkcs12_file, http_pin, http_ca_cert = load_pkcs12(
            cert_files=options.http_cert_files,
            key_password=options.http_pin,
            key_nickname=options.http_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        http_pkcs12_info = (http_pkcs12_file.name, http_pin)

    if options.dirsrv_cert_files:
        if options.dirsrv_pin is None:
            options.dirsrv_pin = installutils.read_password(
                "Enter Directory Server private key unlock",
                confirm=False,
                validate=False,
                retry=False)
            if options.dirsrv_pin is None:
                raise ScriptError(
                    "Directory Server private key unlock password required")
        dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = load_pkcs12(
            cert_files=options.dirsrv_cert_files,
            key_password=options.dirsrv_pin,
            key_nickname=options.dirsrv_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        dirsrv_pkcs12_info = (dirsrv_pkcs12_file.name, dirsrv_pin)

    if options.pkinit_cert_files:
        if options.pkinit_pin is None:
            options.pkinit_pin = installutils.read_password(
                "Enter Kerberos KDC private key unlock",
                confirm=False,
                validate=False,
                retry=False)
            if options.pkinit_pin is None:
                raise ScriptError(
                    "Kerberos KDC private key unlock password required")
        pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
            cert_files=options.pkinit_cert_files,
            key_password=options.pkinit_pin,
            key_nickname=options.pkinit_cert_name,
            ca_cert_files=options.ca_cert_files,
            realm_name=config.realm_name)
        pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)

    if (options.http_cert_files and options.dirsrv_cert_files
            and http_ca_cert != dirsrv_ca_cert):
        raise RuntimeError("Apache Server SSL certificate and Directory "
                           "Server SSL certificate are not signed by the same"
                           " CA certificate")

    if (options.http_cert_files and options.pkinit_cert_files
            and http_ca_cert != pkinit_ca_cert):
        raise RuntimeError("Apache Server SSL certificate and PKINIT KDC "
                           "certificate are not signed by the same CA "
                           "certificate")

    installutils.verify_fqdn(config.host_name, options.no_host_dns)
    installutils.verify_fqdn(config.master_host_name, options.no_host_dns)

    ccache = os.environ['KRB5CCNAME']
    kinit_keytab('host/{env.host}@{env.realm}'.format(env=api.env),
                 paths.KRB5_KEYTAB, ccache)

    cafile = paths.IPA_CA_CRT
    if not os.path.isfile(cafile):
        raise RuntimeError("CA cert file is not available! Please reinstall"
                           "the client and try again.")

    ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
    xmlrpc_uri = 'https://{}/ipa/xml'.format(
        ipautil.format_netloc(config.master_host_name))
    remote_api = create_api(mode=None)
    remote_api.bootstrap(in_server=True,
                         context='installer',
                         confdir=paths.ETC_IPA,
                         ldap_uri=ldapuri,
                         xmlrpc_uri=xmlrpc_uri)
    remote_api.finalize()
    installer._remote_api = remote_api

    with rpc_client(remote_api) as client:
        check_remote_version(client, parse_version(api.env.version))
        check_remote_fips_mode(client, api.env.fips_mode)

    conn = remote_api.Backend.ldap2
    replman = None
    try:
        # Try out authentication
        conn.connect(ccache=ccache)
        replman = ReplicationManager(config.realm_name,
                                     config.master_host_name, None)

        promotion_check_ipa_domain(conn, remote_api.env.basedn)

        # Make sure that domain fulfills minimal domain level
        # requirement
        domain_level = current_domain_level(remote_api)
        check_domain_level_is_supported(domain_level)
        if domain_level < constants.MIN_DOMAIN_LEVEL:
            raise RuntimeError(
                "Cannot promote this client to a replica. The domain level "
                "must be raised to {mindomainlevel} before the replica can be "
                "installed".format(mindomainlevel=constants.MIN_DOMAIN_LEVEL))

        # Check authorization
        result = remote_api.Command['hostgroup_find'](
            cn=u'ipaservers', host=[unicode(api.env.host)])['result']
        add_to_ipaservers = not result

        if add_to_ipaservers:
            if options.password and not options.admin_password:
                raise errors.ACIError(info="Not authorized")

            if installer._ccache is None:
                del os.environ['KRB5CCNAME']
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

            try:
                installutils.check_creds(options, config.realm_name)
                installer._ccache = os.environ.get('KRB5CCNAME')
            finally:
                os.environ['KRB5CCNAME'] = ccache

            conn.disconnect()
            conn.connect(ccache=installer._ccache)

            try:
                result = remote_api.Command['hostgroup_show'](
                    u'ipaservers', all=True, rights=True)['result']

                if 'w' not in result['attributelevelrights']['member']:
                    raise errors.ACIError(info="Not authorized")
            finally:
                conn.disconnect()
                conn.connect(ccache=ccache)

        # Check that we don't already have a replication agreement
        if replman.get_replication_agreement(config.host_name):
            msg = ("A replication agreement for this host already exists. "
                   "It needs to be removed.\n"
                   "Run this command:\n"
                   "    %% ipa-replica-manage del {host} --force".format(
                       host=config.host_name))
            raise ScriptError(msg, rval=3)

        # Detect if the other master can handle replication managers
        # cn=replication managers,cn=sysaccounts,cn=etc,$SUFFIX
        dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
                ('cn', 'etc'), ipautil.realm_to_suffix(config.realm_name))
        try:
            conn.get_entry(dn)
        except errors.NotFound:
            msg = ("The Replication Managers group is not available in "
                   "the domain. Replica promotion requires the use of "
                   "Replication Managers to be able to replicate data. "
                   "Upgrade the peer master or use the ipa-replica-prepare "
                   "command on the master and use a prep file to install "
                   "this replica.")
            logger.error("%s", msg)
            raise ScriptError(rval=3)

        dns_masters = remote_api.Object['dnsrecord'].get_dns_masters()
        if dns_masters:
            if not options.no_host_dns:
                logger.debug('Check forward/reverse DNS resolution')
                resolution_ok = (
                    check_dns_resolution(config.master_host_name, dns_masters)
                    and check_dns_resolution(config.host_name, dns_masters))
                if not resolution_ok and installer.interactive:
                    if not ipautil.user_input("Continue?", False):
                        raise ScriptError(rval=0)
        else:
            logger.debug('No IPA DNS servers, '
                         'skipping forward/reverse resolution check')

        entry_attrs = conn.get_ipa_config()
        subject_base = entry_attrs.get('ipacertificatesubjectbase', [None])[0]
        if subject_base is not None:
            config.subject_base = DN(subject_base)

        # Find if any server has a CA
        ca_host = service.find_providing_server('CA', conn,
                                                config.ca_host_name)
        if ca_host is not None:
            config.ca_host_name = ca_host
            ca_enabled = True
            if options.dirsrv_cert_files:
                logger.error("Certificates could not be provided when "
                             "CA is present on some master.")
                raise ScriptError(rval=3)
        else:
            if options.setup_ca:
                logger.error("The remote master does not have a CA "
                             "installed, can't set up CA")
                raise ScriptError(rval=3)
            ca_enabled = False
            if not options.dirsrv_cert_files:
                logger.error("Cannot issue certificates: a CA is not "
                             "installed. Use the --http-cert-file, "
                             "--dirsrv-cert-file options to provide "
                             "custom certificates.")
                raise ScriptError(rval=3)

        kra_host = service.find_providing_server('KRA', conn,
                                                 config.kra_host_name)
        if kra_host is not None:
            config.kra_host_name = kra_host
            kra_enabled = True
        else:
            if options.setup_kra:
                logger.error("There is no KRA server in the domain, "
                             "can't setup a KRA clone")
                raise ScriptError(rval=3)
            kra_enabled = False

        if ca_enabled:
            options.realm_name = config.realm_name
            options.host_name = config.host_name
            ca.install_check(False, config, options)

        if kra_enabled:
            try:
                kra.install_check(remote_api, config, options)
            except RuntimeError as e:
                raise ScriptError(e)

        if options.setup_dns:
            dns.install_check(False, remote_api, True, options,
                              config.host_name)
            config.ips = dns.ip_addresses
        else:
            config.ips = installutils.get_server_ip_address(
                config.host_name, not installer.interactive, False,
                options.ip_addresses)

            # check addresses here, dns module is doing own check
            no_matching_interface_for_ip_address_warning(config.ips)

        if options.setup_adtrust:
            adtrust.install_check(False, options, remote_api)

    except errors.ACIError:
        logger.debug("%s", traceback.format_exc())
        raise ScriptError("\nInsufficient privileges to promote the server."
                          "\nPossible issues:"
                          "\n- A user has insufficient privileges"
                          "\n- This client has insufficient privileges "
                          "to become an IPA replica")
    except errors.LDAPError:
        logger.debug("%s", traceback.format_exc())
        raise ScriptError("\nUnable to connect to LDAP server %s" %
                          config.master_host_name)
    finally:
        if replman and replman.conn:
            replman.conn.unbind()
        if conn.isconnected():
            conn.disconnect()

    # check connection
    if not options.skip_conncheck:
        if add_to_ipaservers:
            # use user's credentials when the server host is not ipaservers
            if installer._ccache is None:
                del os.environ['KRB5CCNAME']
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

        try:
            replica_conn_check(config.master_host_name,
                               config.host_name,
                               config.realm_name,
                               options.setup_ca,
                               389,
                               options.admin_password,
                               principal=options.principal,
                               ca_cert_file=cafile)
        finally:
            if add_to_ipaservers:
                os.environ['KRB5CCNAME'] = ccache

    installer._ca_enabled = ca_enabled
    installer._kra_enabled = kra_enabled
    installer._ca_file = cafile
    installer._fstore = fstore
    installer._sstore = sstore
    installer._config = config
    installer._add_to_ipaservers = add_to_ipaservers
    installer._dirsrv_pkcs12_file = dirsrv_pkcs12_file
    installer._dirsrv_pkcs12_info = dirsrv_pkcs12_info
    installer._http_pkcs12_file = http_pkcs12_file
    installer._http_pkcs12_info = http_pkcs12_info
    installer._pkinit_pkcs12_file = pkinit_pkcs12_file
    installer._pkinit_pkcs12_info = pkinit_pkcs12_info
Example #27
0
def install_check(installer):
    options = installer
    filename = installer.replica_file

    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'.")

    sstore = sysrestore.StateFile(paths.SYSRESTORE)

    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 not options.no_ntp:
        try:
            ipaclient.ntpconf.check_timedate_services()
        except ipaclient.ntpconf.NTPConflictingService as 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)
    installer._top_dir = config.top_dir
    config.setup_ca = options.setup_ca
    config.setup_kra = options.setup_kra

    # Create the management framework config file
    # Note: We must do this before bootstraping and finalizing ipalib.api
    old_umask = os.umask(0o22)   # 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("mode=production\n")
        fd.close()
    finally:
        os.umask(old_umask)

    api.bootstrap(in_server=True, context='installer')
    api.finalize()

    installutils.verify_fqdn(config.master_host_name, options.no_host_dns)

    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)
    remote_api.finalize()
    conn = remote_api.Backend.ldap2
    replman = None
    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
        if replman.get_replication_agreement(config.host_name):
            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)

        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 installer.interactive:
                    if not ipautil.user_input("Continue?", False):
                        sys.exit(0)
        else:
            root_logger.debug('No IPA DNS servers, '
                              'skipping forward/reverse resolution check')

        if options.setup_ca:
            options.realm_name = config.realm_name
            options.host_name = config.host_name
            options.subject = config.subject_base
            ca.install_check(False, config, options)

        if config.setup_kra:
            try:
                kra.install_check(remote_api, config, options)
            except RuntimeError as e:
                print(str(e))
                sys.exit(1)
    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 conn.isconnected():
            conn.disconnect()

    if options.setup_dns:
        dns.install_check(False, True, options, config.host_name)
        config.ips = dns.ip_addresses
    else:
        config.ips = installutils.get_server_ip_address(
            config.host_name, not installer.interactive, False,
            options.ip_addresses)

    # installer needs to update hosts file when DNS subsystem will be
    # installed or custom addresses are used
    if options.setup_dns or options.ip_addresses:
        installer._update_hosts_file = True

    # 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)

    installer._remote_api = remote_api
    installer._fstore = fstore
    installer._sstore = sstore
    installer._config = config
Example #28
0
        fd.close()
    finally:
        os.umask(old_umask)

    api.bootstrap(in_server=True, context='installer')
    api.finalize()

    installutils.verify_fqdn(config.master_host_name, options.no_host_dns)

    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)
    remote_api.finalize()
    conn = remote_api.Backend.ldap2
    replman = None
    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
        if replman.get_replication_agreement(config.host_name):
Example #29
0
        fqdns = result['result']['fqdns']
        if detail:
            # print per host details
            for fqdn in fqdns:
                textui.print_plain("Hostname %s" % (fqdn))
                details = result['result']['details'][fqdn].items()
                details.sort(key=lambda x: x[0])
                textui.print_keyval(details)
        else:
            # print list of hostnames
            textui.print_plain(" ".join(fqdns))


if __name__ == '__main__':
    from ipalib import create_api
    api = create_api()
else:
    from ipalib import api

api.register(aii)
api.register(aii_find)

if __name__ == '__main__':
    api.finalize()

    from ipalib import cli
    textui = cli.textui()

    args = [unicode('somehost'), unicode('somesubdomain.somedomain')]
    options = {
        'install': True,
Example #30
0
    def __init__(self,
                 dm_password=None,
                 sub_dict={},
                 online=True,
                 ldapi=False):
        '''
        :parameters:
            dm_password
                Directory Manager password
            sub_dict
                substitution dictionary
            online
                Do an online LDAP update or use an experimental LDIF updater
            ldapi
                Bind using ldapi. This assumes autobind is enabled.

        Data Structure Example:
        -----------------------

        dn_by_rdn_count = {
            3: 'cn=config,dc=example,dc=com':
            4: 'cn=bob,ou=people,dc=example,dc=com',
        }

        all_updates = [
            {
                'dn': 'cn=config,dc=example,dc=com',
                'default': [
                    dict(attr='attr1', value='default1'),
                ],
                'updates': [
                    dict(action='action', attr='attr1', value='value1'),
                    dict(action='replace', attr='attr2', value=['old', 'new']),
                ]
            },
            {
                'dn': 'cn=bob,ou=people,dc=example,dc=com',
                'default': [
                    dict(attr='attr3', value='default3'),
                ],
                'updates': [
                    dict(action='action', attr='attr3', value='value3'),
                    dict(action='action', attr='attr4', value='value4'),
                }
            }
        ]

        Please notice the replace action requires two values in list

        The default and update lists are "dispositions"

        Plugins:

        Plugins has to be specified in update file to be executed, using
        'plugin' directive

        Example:
        plugin: update_uniqueness_plugins_to_new_syntax

        Each plugin returns two values:

        1. restart: dirsrv will be restarted AFTER this update is
                     applied.
        2. updates: A list of updates to be applied.

        The value of an update is a dictionary with the following possible
        values:
          - dn: DN, equal to the dn attribute
          - updates: list of updates against the dn
          - default: list of the default entry to be added if it doesn't
                     exist
          - deleteentry: list of dn's to be deleted (typically single dn)

        For example, this update file:

          dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
          replace:krbPwdLockoutDuration:10::600
          replace: krbPwdMaxFailure:3::6

        Generates this list which contain the update dictionary:

        [
          {
            'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
            'updates': [
              dict(action='replace', attr='krbPwdLockoutDuration',
                   value=['10','600']),
              dict(action='replace', attr='krbPwdMaxFailure',
                   value=['3','6']),
            ]
          }
        ]

        Here is another example showing how a default entry is configured:

          dn: cn=Managed Entries,cn=etc,$SUFFIX
          default: objectClass: nsContainer
          default: objectClass: top
          default: cn: Managed Entries

        This generates:

        [
          {
            'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
            'default': [
              dict(attr='objectClass', value='nsContainer'),
              dict(attr='objectClass', value='top'),
              dict(attr='cn', value='Managed Entries'),
            ]
          }
        ]

        Note that the variable substitution in both examples has been completed.

        Either may make changes directly in LDAP or can return updates in
        update format.

        '''
        self.sub_dict = sub_dict
        self.dm_password = dm_password
        self.conn = None
        self.modified = False
        self.online = online
        self.ldapi = ldapi
        self.pw_name = pwd.getpwuid(os.geteuid()).pw_name
        self.realm = None
        self.socket_name = (paths.SLAPD_INSTANCE_SOCKET_TEMPLATE %
                            api.env.realm.replace('.', '-'))
        suffix = None

        if sub_dict.get("REALM"):
            self.realm = sub_dict["REALM"]
        else:
            self.realm = api.env.realm
            suffix = ipautil.realm_to_suffix(
                self.realm) if self.realm else None

        self.ldapuri = installutils.realm_to_ldapi_uri(self.realm)
        if suffix is not None:
            assert isinstance(suffix, DN)

        fqdn = installutils.get_fqdn()
        if fqdn is None:
            raise RuntimeError("Unable to determine hostname")

        if not self.sub_dict.get("REALM") and self.realm is not None:
            self.sub_dict["REALM"] = self.realm
        if not self.sub_dict.get("FQDN"):
            self.sub_dict["FQDN"] = fqdn
        if not self.sub_dict.get("DOMAIN"):
            self.sub_dict["DOMAIN"] = api.env.domain
        if not self.sub_dict.get("SUFFIX") and suffix is not None:
            self.sub_dict["SUFFIX"] = suffix
        if not self.sub_dict.get("ESCAPED_SUFFIX"):
            self.sub_dict["ESCAPED_SUFFIX"] = str(suffix)
        if not self.sub_dict.get("LIBARCH"):
            self.sub_dict["LIBARCH"] = paths.LIBARCH
        if not self.sub_dict.get("TIME"):
            self.sub_dict["TIME"] = int(time.time())
        if not self.sub_dict.get("MIN_DOMAIN_LEVEL"):
            self.sub_dict["MIN_DOMAIN_LEVEL"] = str(constants.MIN_DOMAIN_LEVEL)
        if not self.sub_dict.get("MAX_DOMAIN_LEVEL"):
            self.sub_dict["MAX_DOMAIN_LEVEL"] = str(constants.MAX_DOMAIN_LEVEL)
        if not self.sub_dict.get("STRIP_ATTRS"):
            self.sub_dict["STRIP_ATTRS"] = "%s" % (" ".join(
                constants.REPL_AGMT_STRIP_ATTRS), )
        if not self.sub_dict.get("EXCLUDES"):
            self.sub_dict["EXCLUDES"] = "(objectclass=*) $ EXCLUDE %s" % (
                " ".join(constants.REPL_AGMT_EXCLUDES), )
        if not self.sub_dict.get("TOTAL_EXCLUDES"):
            self.sub_dict["TOTAL_EXCLUDES"] = "(objectclass=*) $ EXCLUDE " + \
                " ".join(constants.REPL_AGMT_TOTAL_EXCLUDES)
        self.api = create_api(mode=None)
        self.api.bootstrap(in_server=True,
                           context='updates',
                           confdir=paths.ETC_IPA,
                           ldap_uri=self.ldapuri)
        self.api.finalize()
        if online:
            # Try out the connection/password
            # (This will raise if the server is not available)
            self.create_connection()
            self.close_connection()
        else:
            raise RuntimeError("Offline updates are not supported.")
Example #31
0
def promote_check(installer):
    options = installer
    installer._enrollment_performed = False
    installer._top_dir = tempfile.mkdtemp("ipa")

    # check selinux status, http and DS ports, NTP conflicting services
    common_check(options.no_ntp)

    client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
    if not client_fstore.has_files():
        # One-step replica installation
        if options.password and options.admin_password:
            raise ScriptError("--password and --admin-password options are "
                              "mutually exclusive")
        ensure_enrolled(installer)
    else:
        if (options.domain_name or options.server or options.realm_name or
                options.host_name or options.password or options.keytab):
            print("IPA client is already configured on this system, ignoring "
                  "the --domain, --server, --realm, --hostname, --password "
                  "and --keytab options.")

        # The NTP configuration can not be touched on pre-installed client:
        if options.no_ntp or options.ntp_servers or options.ntp_pool:
                raise ScriptError(
                    "NTP configuration cannot be updated during promotion")

    sstore = sysrestore.StateFile(paths.SYSRESTORE)

    fstore = sysrestore.FileStore(paths.SYSRESTORE)

    env = Env()
    env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None)
    env._finalize_core(**dict(constants.DEFAULT_CONFIG))

    # 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=ipaldap.realm_to_ldapi_uri(env.realm),
                  xmlrpc_uri=xmlrpc_uri)
    # pylint: enable=no-member
    api.finalize()

    config = ReplicaConfig()
    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
    if not api.env.ca_host or api.env.ca_host == api.env.host:
        # ca_host has not been configured explicitly, prefer source master
        config.ca_host_name = api.env.server
    else:
        # default to ca_host from IPA config
        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 = installer._top_dir
    config.basedn = api.env.basedn
    config.hidden_replica = options.hidden_replica

    http_pkcs12_file = None
    http_pkcs12_info = None
    http_ca_cert = None
    dirsrv_pkcs12_file = None
    dirsrv_pkcs12_info = None
    dirsrv_ca_cert = None
    pkinit_pkcs12_file = None
    pkinit_pkcs12_info = None
    pkinit_ca_cert = None

    if options.http_cert_files:
        if options.http_pin is None:
            options.http_pin = installutils.read_password(
                "Enter Apache Server private key unlock",
                confirm=False, validate=False, retry=False)
            if options.http_pin is None:
                raise ScriptError(
                    "Apache Server private key unlock password required")
        http_pkcs12_file, http_pin, http_ca_cert = load_pkcs12(
            cert_files=options.http_cert_files,
            key_password=options.http_pin,
            key_nickname=options.http_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        http_pkcs12_info = (http_pkcs12_file.name, http_pin)

    if options.dirsrv_cert_files:
        if options.dirsrv_pin is None:
            options.dirsrv_pin = installutils.read_password(
                "Enter Directory Server private key unlock",
                confirm=False, validate=False, retry=False)
            if options.dirsrv_pin is None:
                raise ScriptError(
                    "Directory Server private key unlock password required")
        dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = load_pkcs12(
            cert_files=options.dirsrv_cert_files,
            key_password=options.dirsrv_pin,
            key_nickname=options.dirsrv_cert_name,
            ca_cert_files=options.ca_cert_files,
            host_name=config.host_name)
        dirsrv_pkcs12_info = (dirsrv_pkcs12_file.name, dirsrv_pin)

    if options.pkinit_cert_files:
        if options.pkinit_pin is None:
            options.pkinit_pin = installutils.read_password(
                "Enter Kerberos KDC private key unlock",
                confirm=False, validate=False, retry=False)
            if options.pkinit_pin is None:
                raise ScriptError(
                    "Kerberos KDC private key unlock password required")
        pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = load_pkcs12(
            cert_files=options.pkinit_cert_files,
            key_password=options.pkinit_pin,
            key_nickname=options.pkinit_cert_name,
            ca_cert_files=options.ca_cert_files,
            realm_name=config.realm_name)
        pkinit_pkcs12_info = (pkinit_pkcs12_file.name, pkinit_pin)

    if (options.http_cert_files and options.dirsrv_cert_files and
            http_ca_cert != dirsrv_ca_cert):
        raise RuntimeError("Apache Server SSL certificate and Directory "
                           "Server SSL certificate are not signed by the same"
                           " CA certificate")

    if (options.http_cert_files and
            options.pkinit_cert_files and
            http_ca_cert != pkinit_ca_cert):
        raise RuntimeError("Apache Server SSL certificate and PKINIT KDC "
                           "certificate are not signed by the same CA "
                           "certificate")

    installutils.verify_fqdn(config.host_name, options.no_host_dns)
    installutils.verify_fqdn(config.master_host_name, options.no_host_dns)

    ccache = os.environ['KRB5CCNAME']
    kinit_keytab('host/{env.host}@{env.realm}'.format(env=api.env),
                 paths.KRB5_KEYTAB,
                 ccache)

    cafile = paths.IPA_CA_CRT
    if not os.path.isfile(cafile):
        raise RuntimeError("CA cert file is not available! Please reinstall"
                           "the client and try again.")

    ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
    xmlrpc_uri = 'https://{}/ipa/xml'.format(
        ipautil.format_netloc(config.master_host_name))
    remote_api = create_api(mode=None)
    remote_api.bootstrap(in_server=True,
                         context='installer',
                         confdir=paths.ETC_IPA,
                         ldap_uri=ldapuri,
                         xmlrpc_uri=xmlrpc_uri)
    remote_api.finalize()
    installer._remote_api = remote_api

    with rpc_client(remote_api) as client:
        check_remote_version(client, parse_version(api.env.version))
        check_remote_fips_mode(client, api.env.fips_mode)

    conn = remote_api.Backend.ldap2
    replman = None
    try:
        # Try out authentication
        conn.connect(ccache=ccache)
        replman = ReplicationManager(config.realm_name,
                                     config.master_host_name, None)

        promotion_check_ipa_domain(conn, remote_api.env.basedn)

        # Make sure that domain fulfills minimal domain level
        # requirement
        domain_level = current_domain_level(remote_api)
        check_domain_level_is_supported(domain_level)
        if domain_level < constants.MIN_DOMAIN_LEVEL:
            raise RuntimeError(
                "Cannot promote this client to a replica. The domain level "
                "must be raised to {mindomainlevel} before the replica can be "
                "installed".format(
                    mindomainlevel=constants.MIN_DOMAIN_LEVEL
                ))

        # Check authorization
        result = remote_api.Command['hostgroup_find'](
            cn=u'ipaservers',
            host=[unicode(api.env.host)]
        )['result']
        add_to_ipaservers = not result

        if add_to_ipaservers:
            if options.password and not options.admin_password:
                raise errors.ACIError(info="Not authorized")

            if installer._ccache is None:
                del os.environ['KRB5CCNAME']
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

            try:
                installutils.check_creds(options, config.realm_name)
                installer._ccache = os.environ.get('KRB5CCNAME')
            finally:
                os.environ['KRB5CCNAME'] = ccache

            conn.disconnect()
            conn.connect(ccache=installer._ccache)

            try:
                result = remote_api.Command['hostgroup_show'](
                    u'ipaservers',
                    all=True,
                    rights=True
                )['result']

                if 'w' not in result['attributelevelrights']['member']:
                    raise errors.ACIError(info="Not authorized")
            finally:
                conn.disconnect()
                conn.connect(ccache=ccache)


        # Check that we don't already have a replication agreement
        if replman.get_replication_agreement(config.host_name):
            msg = ("A replication agreement for this host already exists. "
                   "It needs to be removed.\n"
                   "Run this command:\n"
                   "    %% ipa-replica-manage del {host} --force"
                   .format(host=config.host_name))
            raise ScriptError(msg, rval=3)

        # Detect if the other master can handle replication managers
        # cn=replication managers,cn=sysaccounts,cn=etc,$SUFFIX
        dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
                ('cn', 'etc'), ipautil.realm_to_suffix(config.realm_name))
        try:
            conn.get_entry(dn)
        except errors.NotFound:
            msg = ("The Replication Managers group is not available in "
                   "the domain. Replica promotion requires the use of "
                   "Replication Managers to be able to replicate data. "
                   "Upgrade the peer master or use the ipa-replica-prepare "
                   "command on the master and use a prep file to install "
                   "this replica.")
            logger.error("%s", msg)
            raise ScriptError(rval=3)

        dns_masters = remote_api.Object['dnsrecord'].get_dns_masters()
        if dns_masters:
            if not options.no_host_dns:
                logger.debug('Check forward/reverse DNS resolution')
                resolution_ok = (
                    check_dns_resolution(config.master_host_name,
                                         dns_masters) and
                    check_dns_resolution(config.host_name, dns_masters))
                if not resolution_ok and installer.interactive:
                    if not ipautil.user_input("Continue?", False):
                        raise ScriptError(rval=0)
        else:
            logger.debug('No IPA DNS servers, '
                         'skipping forward/reverse resolution check')

        entry_attrs = conn.get_ipa_config()
        subject_base = entry_attrs.get('ipacertificatesubjectbase', [None])[0]
        if subject_base is not None:
            config.subject_base = DN(subject_base)

        # Find any server with a CA
        ca_host = find_providing_server(
            'CA', conn, [config.ca_host_name]
        )
        if ca_host is not None:
            config.ca_host_name = ca_host
            ca_enabled = True
            if options.dirsrv_cert_files:
                logger.error("Certificates could not be provided when "
                             "CA is present on some master.")
                raise ScriptError(rval=3)
        else:
            if options.setup_ca:
                logger.error("The remote master does not have a CA "
                             "installed, can't set up CA")
                raise ScriptError(rval=3)
            ca_enabled = False
            if not options.dirsrv_cert_files:
                logger.error("Cannot issue certificates: a CA is not "
                             "installed. Use the --http-cert-file, "
                             "--dirsrv-cert-file options to provide "
                             "custom certificates.")
                raise ScriptError(rval=3)

        # Find any server with a KRA
        kra_host = find_providing_server(
            'KRA', conn, [config.kra_host_name]
        )
        if kra_host is not None:
            config.kra_host_name = kra_host
            kra_enabled = True
        else:
            if options.setup_kra:
                logger.error("There is no active KRA server in the domain, "
                             "can't setup a KRA clone")
                raise ScriptError(rval=3)
            kra_enabled = False

        if ca_enabled:
            options.realm_name = config.realm_name
            options.host_name = config.host_name
            ca.install_check(False, config, options)

        if kra_enabled:
            try:
                kra.install_check(remote_api, config, options)
            except RuntimeError as e:
                raise ScriptError(e)

        if options.setup_dns:
            dns.install_check(False, remote_api, True, options,
                              config.host_name)
            config.ips = dns.ip_addresses
        else:
            config.ips = installutils.get_server_ip_address(
                config.host_name, not installer.interactive,
                False, options.ip_addresses)

            # check addresses here, dns module is doing own check
            no_matching_interface_for_ip_address_warning(config.ips)

        if options.setup_adtrust:
            adtrust.install_check(False, options, remote_api)

    except errors.ACIError:
        logger.debug("%s", traceback.format_exc())
        raise ScriptError("\nInsufficient privileges to promote the server."
                          "\nPossible issues:"
                          "\n- A user has insufficient privileges"
                          "\n- This client has insufficient privileges "
                          "to become an IPA replica")
    except errors.LDAPError:
        logger.debug("%s", traceback.format_exc())
        raise ScriptError("\nUnable to connect to LDAP server %s" %
                          config.master_host_name)
    finally:
        if replman and replman.conn:
            replman.conn.unbind()
        if conn.isconnected():
            conn.disconnect()

    # check connection
    if not options.skip_conncheck:
        if add_to_ipaservers:
            # use user's credentials when the server host is not ipaservers
            if installer._ccache is None:
                del os.environ['KRB5CCNAME']
            else:
                os.environ['KRB5CCNAME'] = installer._ccache

        try:
            replica_conn_check(
                config.master_host_name, config.host_name, config.realm_name,
                options.setup_ca, 389,
                options.admin_password, principal=options.principal,
                ca_cert_file=cafile)
        finally:
            if add_to_ipaservers:
                os.environ['KRB5CCNAME'] = ccache

    installer._ca_enabled = ca_enabled
    installer._kra_enabled = kra_enabled
    installer._ca_file = cafile
    installer._fstore = fstore
    installer._sstore = sstore
    installer._config = config
    installer._add_to_ipaservers = add_to_ipaservers
    installer._dirsrv_pkcs12_file = dirsrv_pkcs12_file
    installer._dirsrv_pkcs12_info = dirsrv_pkcs12_info
    installer._http_pkcs12_file = http_pkcs12_file
    installer._http_pkcs12_info = http_pkcs12_info
    installer._pkinit_pkcs12_file = pkinit_pkcs12_file
    installer._pkinit_pkcs12_info = pkinit_pkcs12_info