URL: https://github.com/freeipa/freeipa/pull/479
Author: martbab
 Title: #479: Merge AD trust installer into composite ones
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/479/head:pr479
git checkout pr479
From 279684608d667d31e919021f4b7ffb397c4a6fcf Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 16 Feb 2017 14:06:08 +0100
Subject: [PATCH 01/13] Refactor the code checking for missing SIDs

Decompose the individual sub-tasks into separate functions. Also perform
the lookup only when LDAP is connected.

https://fedorahosted.org/freeipa/ticket/6630
---
 ipaserver/install/adtrust.py | 107 ++++++++++++++++++++++++++-----------------
 1 file changed, 64 insertions(+), 43 deletions(-)

diff --git a/ipaserver/install/adtrust.py b/ipaserver/install/adtrust.py
index 92fe031..69e9834 100644
--- a/ipaserver/install/adtrust.py
+++ b/ipaserver/install/adtrust.py
@@ -168,6 +168,69 @@ def check_for_installed_deps():
         raise ScriptError("Aborting installation.")
 
 
+def retrieve_entries_without_sid(api):
+    """
+    Retrieve a list of entries without assigned SIDs.
+    :returns: a list of entries or an empty list if an error occurs
+    """
+    # The filter corresponds to ipa_sidgen_task.c LDAP search filter
+    filter = '(&(objectclass=ipaobject)(!(objectclass=mepmanagedentry))' \
+             '(|(objectclass=posixaccount)(objectclass=posixgroup)' \
+             '(objectclass=ipaidobject))(!(ipantsecurityidentifier=*)))'
+    base_dn = api.env.basedn
+    try:
+        root_logger.debug(
+            "Searching for objects with missing SID with "
+            "filter=%s, base_dn=%s", filter, base_dn)
+        entries, _truncated = api.Backend.ldap2.find_entries(
+            filter=filter, base_dn=base_dn, attrs_list=[''])
+        return entries
+    except errors.NotFound:
+        # All objects have SIDs assigned
+        pass
+    except (errors.DatabaseError, errors.NetworkError) as e:
+        print("Could not retrieve a list of objects that need a SID "
+              "identifier assigned:")
+        print(unicode(e))
+
+    return []
+
+
+def retrieve_and_ask_about_sids(api, options):
+    entries = []
+    if api.Backend.ldap2.isconnected():
+        entries = retrieve_entries_without_sid(api)
+    else:
+        root_logger.debug(
+            "LDAP backend not connected, can not retrieve entries "
+            "with missing SID")
+
+    object_count = len(entries)
+    if object_count > 0:
+        print("")
+        print("WARNING: %d existing users or groups do not have "
+              "a SID identifier assigned." % len(entries))
+        print("Installer can run a task to have ipa-sidgen "
+              "Directory Server plugin generate")
+        print("the SID identifier for all these users. Please note, "
+              "the in case of a high")
+        print("number of users and groups, the operation might "
+              "lead to high replication")
+        print("traffic and performance degradation. Refer to "
+              "ipa-adtrust-install(1) man page")
+        print("for details.")
+        print("")
+        if options.unattended:
+            print("Unattended mode was selected, installer will "
+                  "NOT run ipa-sidgen task!")
+        else:
+            if ipautil.user_input(
+                    "Do you want to run the ipa-sidgen task?",
+                    default=False,
+                    allow_empty=False):
+                options.add_sids = True
+
+
 def install_check(standalone, options, api):
     global netbios_name
     global reset_netbios_name
@@ -225,49 +288,7 @@ def install_check(standalone, options, api):
         options.netbios_name, options.unattended, api)
 
     if not options.add_sids:
-        # The filter corresponds to ipa_sidgen_task.c LDAP search filter
-        filter = '(&(objectclass=ipaobject)(!(objectclass=mepmanagedentry))' \
-                 '(|(objectclass=posixaccount)(objectclass=posixgroup)' \
-                 '(objectclass=ipaidobject))(!(ipantsecurityidentifier=*)))'
-        base_dn = api.env.basedn
-        try:
-            root_logger.debug(
-                "Searching for objects with missing SID with "
-                "filter=%s, base_dn=%s", filter, base_dn)
-            entries, _truncated = api.Backend.ldap2.find_entries(
-                filter=filter, base_dn=base_dn, attrs_list=[''])
-        except errors.NotFound:
-            # All objects have SIDs assigned
-            pass
-        except (errors.DatabaseError, errors.NetworkError) as e:
-            print("Could not retrieve a list of objects that need a SID "
-                  "identifier assigned:")
-            print(unicode(e))
-        else:
-            object_count = len(entries)
-            if object_count > 0:
-                print("")
-                print("WARNING: %d existing users or groups do not have "
-                      "a SID identifier assigned." % len(entries))
-                print("Installer can run a task to have ipa-sidgen "
-                      "Directory Server plugin generate")
-                print("the SID identifier for all these users. Please note, "
-                      "the in case of a high")
-                print("number of users and groups, the operation might "
-                      "lead to high replication")
-                print("traffic and performance degradation. Refer to "
-                      "ipa-adtrust-install(1) man page")
-                print("for details.")
-                print("")
-                if options.unattended:
-                    print("Unattended mode was selected, installer will "
-                          "NOT run ipa-sidgen task!")
-                else:
-                    if ipautil.user_input(
-                            "Do you want to run the ipa-sidgen task?",
-                            default=False,
-                            allow_empty=False):
-                        options.add_sids = True
+        retrieve_and_ask_about_sids(api, options)
 
 
 def install(options, fstore, api):

From ce45a3965c7fe44f2a9dc589e31a498b3bab5e10 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 16 Feb 2017 13:41:32 +0100
Subject: [PATCH 02/13] only check for netbios name when LDAP backend is
 connected

This is to prevent errors due to non-existent LDAP connection such as
when installing first IPA master.

https://fedorahosted.org/freeipa/ticket/6630
---
 ipaserver/install/adtrust.py | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/ipaserver/install/adtrust.py b/ipaserver/install/adtrust.py
index 69e9834..fd26e69 100644
--- a/ipaserver/install/adtrust.py
+++ b/ipaserver/install/adtrust.py
@@ -59,6 +59,21 @@ def read_netbios_name(netbios_default):
     return netbios_name
 
 
+def retrieve_netbios_name(api):
+    flat_name_attr = 'ipantflatname'
+    try:
+        entry = api.Backend.ldap2.get_entry(
+            DN(('cn', api.env.domain), api.env.container_cifsdomains,
+               ipautil.realm_to_suffix(api.env.realm)),
+            [flat_name_attr])
+    except errors.NotFound:
+        # trust not configured
+        root_logger.debug("No previous trust configuration found")
+        return None
+    else:
+        return entry.get(flat_name_attr)[0]
+
+
 def set_and_check_netbios_name(netbios_name, unattended, api):
     """
     Depending if trust in already configured or not a given NetBIOS domain
@@ -71,22 +86,16 @@ def set_and_check_netbios_name(netbios_name, unattended, api):
     the stored NetBIOS name it it differs from the current one.
     """
 
-    flat_name_attr = 'ipantflatname'
     cur_netbios_name = None
     gen_netbios_name = None
     reset_netbios_name = False
     entry = None
 
-    try:
-        entry = api.Backend.ldap2.get_entry(
-            DN(('cn', api.env.domain), api.env.container_cifsdomains,
-               ipautil.realm_to_suffix(api.env.realm)),
-            [flat_name_attr])
-    except errors.NotFound:
-        # trust not configured
-        pass
+    if api.Backend.ldap2.isconnected():
+        cur_netbios_name = retrieve_netbios_name(api)
     else:
-        cur_netbios_name = entry.get(flat_name_attr)[0]
+        root_logger.debug(
+            "LDAP is not connected, can not retrieve NetBIOS name")
 
     if cur_netbios_name and not netbios_name:
         # keep the current NetBIOS name

From 0ff276204f8996c1dfb3a414f93a3034b61d09d0 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 16 Feb 2017 14:14:01 +0100
Subject: [PATCH 03/13] Refactor the code searching and presenting missing
 trust agents

Use newly implemented APIs for searching and presenting potential
trust agents.

https://fedorahosted.org/freeipa/ticket/6639
---
 ipaserver/install/adtrust.py | 196 +++++++++++++++++++++++--------------------
 1 file changed, 106 insertions(+), 90 deletions(-)

diff --git a/ipaserver/install/adtrust.py b/ipaserver/install/adtrust.py
index fd26e69..deb4301 100644
--- a/ipaserver/install/adtrust.py
+++ b/ipaserver/install/adtrust.py
@@ -9,10 +9,10 @@
 from __future__ import print_function
 
 import os
-import ldap
 
 import six
 
+from ipalib.constants import DOMAIN_LEVEL_0
 from ipalib import errors
 from ipaplatform.paths import paths
 from ipapython.admintool import ScriptError
@@ -240,6 +240,110 @@ def retrieve_and_ask_about_sids(api, options):
                 options.add_sids = True
 
 
+def retrieve_potential_adtrust_agents(api):
+    """
+    Retrieve a sorted list of potential AD trust agents
+
+    :param api: initialized API instance
+    :returns: sorted list of FQDNs of masters which are not AD trust agents
+    """
+    try:
+        # Search only masters which have support for domain levels
+        # because only these masters will have SSSD recent enough
+        # to support AD trust agents
+        dl_enabled_masters = api.Command.server_find(
+            ipamindomainlevel=DOMAIN_LEVEL_0, all=True)['result']
+    except (errors.DatabaseError, errors.NetworkError) as e:
+        print("Could not retrieve a list of existing IPA masters:")
+        print(unicode(e))
+        return
+
+    try:
+        # search for existing AD trust agents
+        adtrust_agents = api.Command.server_find(
+            servrole=u'AD trust agent', all=True)['result']
+    except (errors.DatabaseError, errors.NetworkError) as e:
+        print("Could not retrieve a list of adtrust agents:")
+        print(unicode(e))
+        return
+
+    dl_enabled_master_cns = {m['cn'][0] for m in dl_enabled_masters}
+    adtrust_agents_cns = {m['cn'][0] for m in adtrust_agents}
+
+    potential_agents_cns = dl_enabled_master_cns - adtrust_agents_cns
+
+    # remove the local host from the potential agents since it will be set up
+    # by adtrustinstance configuration code
+    potential_agents_cns -= {api.env.host}
+    return sorted(potential_agents_cns)
+
+
+def add_hosts_to_adtrust_agents(api, host_list):
+    """
+    Add the CIFS and host principals to the 'adtrust agents'
+    group as 389-ds only operates with GroupOfNames, we have to
+    use the principal's proper dn as defined in self.cifs_agent
+
+    :param api: API instance
+    :param host_list: list of potential AD trust agent FQDNs
+    """
+    agents_dn = DN(
+        ('cn', 'adtrust agents'), ('cn', 'sysaccounts'),
+        ('cn', 'etc'), api.env.basedn)
+
+    service.add_principals_to_group(
+        api.Backend.ldap2,
+        agents_dn,
+        "member",
+        [api.Object.host.get_dn(x) for x in host_list])
+
+
+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)
+
+        print("""
+WARNING: you MUST restart (e.g. ipactl restart) the following IPA masters in
+order to activate them to serve information about users from trusted forests:
+""")
+        for x in new_agents:
+            print(x)
+
+
 def install_check(standalone, options, api):
     global netbios_name
     global reset_netbios_name
@@ -321,92 +425,4 @@ def install(options, fstore, api):
     if options.add_agents:
         # Find out IPA masters which are not part of the cn=adtrust agents
         # and propose them to be added to the list
-        base_dn = api.env.basedn
-        masters_dn = DN(
-            ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), base_dn)
-        agents_dn = DN(
-            ('cn', 'adtrust agents'), ('cn', 'sysaccounts'),
-            ('cn', 'etc'), base_dn)
-        new_agents = []
-        entries_m = []
-        entries_a = []
-        try:
-            # Search only masters which have support for domain levels
-            # because only these masters will have SSSD recent enough
-            # to support AD trust agents
-            entries_m, _truncated = api.Backend.ldap2.find_entries(
-                filter=("(&(objectclass=ipaSupportedDomainLevelConfig)"
-                        "(ipaMaxDomainLevel=*)(ipaMinDomainLevel=*))"),
-                base_dn=masters_dn, attrs_list=['cn'],
-                scope=ldap.SCOPE_ONELEVEL)
-        except errors.NotFound:
-            pass
-        except (errors.DatabaseError, errors.NetworkError) as e:
-            print("Could not retrieve a list of existing IPA masters:")
-            print(unicode(e))
-
-        try:
-            entries_a, _truncated = api.Backend.ldap2.find_entries(
-               filter="", base_dn=agents_dn, attrs_list=['member'],
-               scope=ldap.SCOPE_BASE)
-        except errors.NotFound:
-            pass
-        except (errors.DatabaseError, errors.NetworkError) as e:
-            print("Could not retrieve a list of adtrust agents:")
-            print(unicode(e))
-
-        if len(entries_m) > 0:
-            existing_masters = [x['cn'][0] for x in entries_m]
-            adtrust_agents = entries_a[0]['member']
-            potential_agents = []
-            for m in existing_masters:
-                mdn = DN(('fqdn', m), api.env.container_host, api.env.basedn)
-                found = False
-                for a in adtrust_agents:
-                    if mdn == a:
-                        found = True
-                        break
-                if not found:
-                    potential_agents += [[m, mdn]]
-
-            object_count = len(potential_agents)
-            if object_count > 0:
-                print("")
-                print("WARNING: %d IPA masters are not yet able to serve "
-                      "information about users from trusted forests."
-                      % (object_count))
-                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!")
-                else:
-                    print(
-                        "Do you want to allow following IPA masters to "
-                        "serve information about users from trusted forests?")
-                    for (name, dn) in potential_agents:
-                        if name == api.env.host:
-                            # Don't add this host here
-                            # it shouldn't be here as it was added by the
-                            # adtrustinstance setup code
-                            continue
-                        if ipautil.user_input(
-                                "IPA master [%s]?" % (name),
-                                default=False,
-                                allow_empty=False):
-                            new_agents += [[name, dn]]
-
-            if len(new_agents) > 0:
-                # Add the CIFS and host principals to the 'adtrust agents'
-                # group as 389-ds only operates with GroupOfNames, we have to
-                # use the principal's proper dn as defined in self.cifs_agent
-                service.add_principals_to_group(
-                    api.Backend.ldap2,
-                    agents_dn,
-                    "member",
-                    [x[1] for x in new_agents])
+        add_new_adtrust_agents(api, options)

From 3e5a36f6fb4c7dacc455f8e26d2b4078235b2629 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 17 Feb 2017 14:00:24 +0100
Subject: [PATCH 04/13] adtrust.py: Use logging to emit error messages

Plain print messages are a) not logged into files and b) get lost in the
output from composite installer.

https://fedorahosted.org/freeipa/ticket/6630
---
 ipaserver/install/adtrust.py | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/ipaserver/install/adtrust.py b/ipaserver/install/adtrust.py
index deb4301..4694a25 100644
--- a/ipaserver/install/adtrust.py
+++ b/ipaserver/install/adtrust.py
@@ -31,9 +31,10 @@
 
 
 def netbios_name_error(name):
-    print("\nIllegal NetBIOS name [%s].\n" % name)
-    print("Up to 15 characters and only uppercase ASCII letters, digits "
-          "and dashes are allowed. Empty string is not allowed.")
+    root_logger.error("\nIllegal NetBIOS name [%s].\n" % name)
+    root_logger.error(
+        "Up to 15 characters and only uppercase ASCII letters, digits "
+        "and dashes are allowed. Empty string is not allowed.")
 
 
 def read_netbios_name(netbios_default):
@@ -198,9 +199,9 @@ def retrieve_entries_without_sid(api):
         # All objects have SIDs assigned
         pass
     except (errors.DatabaseError, errors.NetworkError) as e:
-        print("Could not retrieve a list of objects that need a SID "
-              "identifier assigned:")
-        print(unicode(e))
+        root_logger.error(
+            "Could not retrieve a list of objects that need a SID "
+            "identifier assigned: %s", e)
 
     return []
 
@@ -254,8 +255,8 @@ def retrieve_potential_adtrust_agents(api):
         dl_enabled_masters = api.Command.server_find(
             ipamindomainlevel=DOMAIN_LEVEL_0, all=True)['result']
     except (errors.DatabaseError, errors.NetworkError) as e:
-        print("Could not retrieve a list of existing IPA masters:")
-        print(unicode(e))
+        root_logger.error(
+            "Could not retrieve a list of existing IPA masters: %s", e)
         return
 
     try:
@@ -263,8 +264,7 @@ def retrieve_potential_adtrust_agents(api):
         adtrust_agents = api.Command.server_find(
             servrole=u'AD trust agent', all=True)['result']
     except (errors.DatabaseError, errors.NetworkError) as e:
-        print("Could not retrieve a list of adtrust agents:")
-        print(unicode(e))
+        root_logger.error("Could not retrieve a list of adtrust agents: %s", e)
         return
 
     dl_enabled_master_cns = {m['cn'][0] for m in dl_enabled_masters}

From 76d1e2a7c529ee004785cce8e31297c05a09a6d7 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 17 Feb 2017 13:48:46 +0100
Subject: [PATCH 05/13] print the installation info only in standalone mode

There is no point in emitting this message during server/replica
install.

https://fedorahosted.org/freeipa/ticket/6630
---
 install/tools/ipa-adtrust-install | 2 +-
 ipaserver/install/adtrust.py      | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 443c3c4..1484598 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -208,7 +208,7 @@ def main():
             "Unrecognized error during check of admin rights: %s" % e)
 
     adtrust.install_check(True, options, api)
-    adtrust.install(options, fstore, api)
+    adtrust.install(True, options, fstore, api)
 
     print("""
 =============================================================================
diff --git a/ipaserver/install/adtrust.py b/ipaserver/install/adtrust.py
index 4694a25..2eddf45 100644
--- a/ipaserver/install/adtrust.py
+++ b/ipaserver/install/adtrust.py
@@ -404,8 +404,8 @@ def install_check(standalone, options, api):
         retrieve_and_ask_about_sids(api, options)
 
 
-def install(options, fstore, api):
-    if not options.unattended:
+def install(standalone, options, fstore, api):
+    if not options.unattended and standalone:
         print("")
         print("The following operations may take some minutes to complete.")
         print("Please wait until the prompt is returned.")

From b657abfa4fdc0fc11c794e8bfbefd39195ce3cdb Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Tue, 28 Feb 2017 13:58:36 +0100
Subject: [PATCH 06/13] check for installed dependencies when *not* in
 standalone mode

The condition that controls when to check for samba dependencies was
misformulated. The check should be run when the installer is *not* run
as standalone. In standalone mode the check is already made in different
place so the original code triggered it twice.

https://fedorahosted.org/freeipa/ticket/6630
---
 ipaserver/install/adtrust.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/install/adtrust.py b/ipaserver/install/adtrust.py
index 2eddf45..a8e4dc8 100644
--- a/ipaserver/install/adtrust.py
+++ b/ipaserver/install/adtrust.py
@@ -348,7 +348,7 @@ def install_check(standalone, options, api):
     global netbios_name
     global reset_netbios_name
 
-    if standalone:
+    if not standalone:
         check_for_installed_deps()
 
     realm_not_matching_domain = (api.env.domain.upper() != api.env.realm)

From 559cd12190321a750ce7792a6e22ba4416bec902 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Wed, 15 Feb 2017 10:16:27 +0100
Subject: [PATCH 07/13] Add AD trust installer interface for composite
 installer

This interface is to be used to provide AD trust-related options in
server and replica installer.

https://fedorahosted.org/freeipa/ticket/6630
---
 ipaserver/install/adtrust.py | 50 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/ipaserver/install/adtrust.py b/ipaserver/install/adtrust.py
index a8e4dc8..b81c27c 100644
--- a/ipaserver/install/adtrust.py
+++ b/ipaserver/install/adtrust.py
@@ -14,10 +14,12 @@
 
 from ipalib.constants import DOMAIN_LEVEL_0
 from ipalib import errors
+from ipalib.install.service import ServiceAdminInstallInterface
 from ipaplatform.paths import paths
 from ipapython.admintool import ScriptError
 from ipapython import ipaldap, ipautil
 from ipapython.dn import DN
+from ipapython.install.core import knob
 from ipapython.ipa_log_manager import root_logger
 from ipaserver.install import adtrustinstance
 from ipaserver.install import service
@@ -426,3 +428,51 @@ def install(standalone, options, fstore, api):
         # Find out IPA masters which are not part of the cn=adtrust agents
         # and propose them to be added to the list
         add_new_adtrust_agents(api, options)
+
+
+class ADTrustInstallInterface(ServiceAdminInstallInterface):
+    """
+    Interface for the AD trust installer
+
+    Knobs defined here will be available in:
+    * ipa-server-install
+    * ipa-replica-install
+    * ipa-adtrust-install
+    """
+
+    # the following knobs are provided on top of those specified for
+    # admin credentials
+    add_sids = knob(
+        None,
+        description="Add SIDs for existing users and groups as the final step"
+    )
+    add_agents = knob(
+        None,
+        description="Add IPA masters to a list of hosts allowed to "
+                    "serve information about users from trusted forests"
+    )
+    enable_compat = knob(
+        None,
+        description="Enable support for trusted domains for old clients"
+    )
+    netbios_name = knob(
+        str,
+        None,
+        description="NetBIOS name of the IPA domain"
+    )
+    no_msdcs = knob(
+        None,
+        description="Deprecated: has no effect",
+        deprecated=True
+    )
+    rid_base = knob(
+        int,
+        1000,
+        description="Start value for mapping UIDs and GIDs to RIDs"
+    )
+    secondary_rid_base = knob(
+        int,
+        100000000,
+        description="Start value of the secondary range for mapping "
+                    "UIDs and GIDs to RIDs"
+    )

From 7e8f6b33ca5ddb8d07ede82353c4635f7afebefc Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 17 Feb 2017 13:50:09 +0100
Subject: [PATCH 08/13] expose AD trust related knobs in composite installers

---
 ipaserver/install/server/__init__.py | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py
index 86cb4a9..743da8d 100644
--- a/ipaserver/install/server/__init__.py
+++ b/ipaserver/install/server/__init__.py
@@ -38,13 +38,14 @@
 from .replicainstall import promote_check as replica_promote_check
 from .upgrade import upgrade_check, upgrade
 
-from .. import ca, conncheck, dns, kra
+from .. import adtrust, ca, conncheck, dns, kra
 
 
 class ServerInstallInterface(client.ClientInstallInterface,
                              ca.CAInstallInterface,
                              kra.KRAInstallInterface,
                              dns.DNSInstallInterface,
+                             adtrust.ADTrustInstallInterface,
                              conncheck.ConnCheckInterface):
     """
     Interface of server installers
@@ -144,6 +145,10 @@ def domain_level(self, value):
                 "Domain Level cannot be higher than {0}".format(
                     constants.MAX_DOMAIN_LEVEL))
 
+    setup_adtrust = knob(
+        None,
+        description="configure AD trust capability"
+    )
     setup_ca = knob(
         None,
         description="configure a dogtag CA",
@@ -331,6 +336,11 @@ def dirsrv_config_file(self, value):
     )
     pkinit_cert_name = prepare_only(pkinit_cert_name)
 
+    add_agents = knob(
+        bases=adtrust.ADTrustInstallInterface.add_agents
+    )
+    add_agents = replica_install_only(add_agents)
+
     def __init__(self, **kwargs):
         super(ServerInstallInterface, self).__init__(**kwargs)
 
@@ -548,6 +558,10 @@ def dm_password(self, value):
     def admin_password(self, value):
         validate_admin_password(value)
 
+    # always run sidgen task and do not allow adding agents on first master
+    add_sids = True
+    add_agents = False
+
     def __init__(self, **kwargs):
         super(ServerMasterInstall, self).__init__(**kwargs)
         master_init(self)

From 2b8d375b139136a123f61ea8e65bf28428e4764a Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 17 Feb 2017 13:50:36 +0100
Subject: [PATCH 09/13] Merge AD trust configurator into server installer

ipa-server-install is now able to configure Samba and winbind services
and manage trusts to Active Directory right off the bat with following
alterations from standalone installer:

   * sidgen task is always triggered since there are only a few entries
     to tag in the beginning

   * the `--add-agents` option is hardcoded to False, as there are no
     potential agents to resolve and addd when setting up the first
     master in topology

https://fedorahosted.org/freeipa/ticket/6630
---
 ipaserver/install/server/install.py | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index dd04624..aa95cb5 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -32,7 +32,7 @@
 )
 import ipaclient.install.ntpconf
 from ipaserver.install import (
-    bindinstance, ca, certs, dns, dsinstance,
+    adtrust, bindinstance, ca, certs, dns, dsinstance,
     httpinstance, installutils, kra, krbinstance,
     ntpinstance, otpdinstance, custodiainstance, replication, service,
     sysupgrade)
@@ -386,6 +386,8 @@ def install_check(installer):
     print("  * Configure Apache (httpd)")
     if options.setup_dns:
         print("  * Configure DNS (bind)")
+    if options.setup_adtrust:
+        print("  * Configure Samba (smb) and winbind for managing AD trusts")
     if not options.no_pkinit:
         print("  * Configure the KDC to enable PKINIT")
     if options.no_ntp:
@@ -610,6 +612,9 @@ def install_check(installer):
         network_ip_address_warning(ip_addresses)
         broadcast_ip_address_warning(ip_addresses)
 
+    if options.setup_adtrust:
+        adtrust.install_check(False, options, api)
+
     # installer needs to update hosts file when DNS subsystem will be
     # installed or custom addresses are used
     if options.ip_addresses or options.setup_dns:
@@ -636,16 +641,17 @@ def install_check(installer):
         ))
         print()
 
-    # If domain name and realm does not match, IPA server will not be able
-    # to estabilish trust with Active Directory. Print big fat warning.
+    if not options.setup_adtrust:
+        # If domain name and realm does not match, IPA server will not be able
+        # to estabilish trust with Active Directory. Print big fat warning.
 
-    realm_not_matching_domain = (domain_name.upper() != realm_name)
+        realm_not_matching_domain = (domain_name.upper() != realm_name)
 
-    if realm_not_matching_domain:
-        print("WARNING: Realm name does not match the domain name.\n"
-              "You will not be able to estabilish trusts with Active "
-              "Directory unless\nthe realm name of the IPA server matches "
-              "its domain name.\n\n")
+        if realm_not_matching_domain:
+            print("WARNING: Realm name does not match the domain name.\n"
+                  "You will not be able to estabilish trusts with Active "
+                  "Directory unless\nthe realm name of the IPA server matches "
+                  "its domain name.\n\n")
 
     if installer.interactive and not user_input(
             "Continue to configure the system with these values?", False):
@@ -855,6 +861,9 @@ def install(installer):
                    no_dnssec_validation=options.no_dnssec_validation)
         bind.create_file_with_system_records()
 
+    if options.setup_adtrust:
+        adtrust.install(False, options, fstore, api)
+
     # Set the admin user kerberos password
     ds.change_admin_password(admin_password)
 

From b03c1f3639fb6550f4cf8f6db108047b62ad931f Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 17 Feb 2017 13:51:00 +0100
Subject: [PATCH 10/13] Merge AD trust configurator into replica installer

`ipa-replica-install` is now able to configure Samba and winbind
services in order to manage Active Directory trusts. `--add-agents`
option is exposed in replica installer, while `--add-sids` now defaults
to `False` since adding a first AD trust controller to an existing
sizeable deployment can result in stuck installation as sidgen tasks can
take a long time to complete. That's why adding SIDs should be a
conscious decision in this case.

https://fedorahosted.org/freeipa/ticket/6630
---
 ipaserver/install/server/replicainstall.py | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index c181258..3757700 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -38,7 +38,7 @@
 )
 from ipaclient.install.client import configure_krb5_conf, purge_host_keytab
 from ipaserver.install import (
-    bindinstance, ca, certs, dns, dsinstance, httpinstance,
+    adtrust, bindinstance, ca, certs, dns, dsinstance, httpinstance,
     installutils, kra, krbinstance,
     ntpinstance, otpdinstance, custodiainstance, service)
 from ipaserver.install.installutils import (
@@ -862,6 +862,9 @@ def install_check(installer):
             network_ip_address_warning(config.ips)
             broadcast_ip_address_warning(config.ips)
 
+        if options.setup_adtrust:
+            adtrust.install_check(False, options, remote_api)
+
         enroll_dl0_replica(installer, fstore, remote_api)
         ccache = os.environ['KRB5CCNAME']
         kinit_keytab('host/{env.host}@{env.realm}'.format(env=api.env),
@@ -1283,6 +1286,9 @@ def promote_check(installer):
             network_ip_address_warning(config.ips)
             broadcast_ip_address_warning(config.ips)
 
+        if options.setup_adtrust:
+            adtrust.install_check(False, options, remote_api)
+
     except errors.ACIError:
         root_logger.debug(traceback.format_exc())
         raise ScriptError("\nInsufficient privileges to promote the server."
@@ -1473,6 +1479,10 @@ def install(installer):
         dns.install(False, True, options, api)
     else:
         api.Command.dns_update_system_records()
+
+    if options.setup_adtrust:
+        adtrust.install(False, options, fstore, api)
+
     api.Backend.ldap2.disconnect()
 
     if not promote:

From 3a56356ea262faef71849eb986dc3ea2deb6b652 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 17 Feb 2017 17:04:53 +0100
Subject: [PATCH 11/13] Fix erroneous short name options in ipa-adtrust-install
 man page

`--rid-base` and `--secondary-rid-base` had `-U` option assigned
by error in the man page. Remove it as these options have not short
alias.

https://fedorahosted.org/freeipa/ticket/6630
---
 install/tools/man/ipa-adtrust-install.1 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/install/tools/man/ipa-adtrust-install.1 b/install/tools/man/ipa-adtrust-install.1
index 6e8438b..058422b 100644
--- a/install/tools/man/ipa-adtrust-install.1
+++ b/install/tools/man/ipa-adtrust-install.1
@@ -101,12 +101,12 @@ version 1.13 on IPA master is required to be able to perform as a trust agent.
 \fB\-U\fR, \fB\-\-unattended\fR
 An unattended installation that will never prompt for user input
 .TP
-\fB\-U\fR, \fB\-\-rid-base\fR=\fIRID_BASE\fR
+\fB\-\-rid-base\fR=\fIRID_BASE\fR
 First RID value of the local domain. The first Posix ID of the local domain will
 be assigned to this RID, the second to RID+1 etc. See the online help of the
 idrange CLI for details.
 .TP
-\fB\-U\fR, \fB\-\-secondary-rid-base\fR=\fISECONDARY_RID_BASE\fR
+\fB\-\-secondary-rid-base\fR=\fISECONDARY_RID_BASE\fR
 Start value of the secondary RID range, which is only used in the case a user
 and a group share numerically the same Posix ID. See the online help of the
 idrange CLI for details.

From 19edfbe24abfe3c07bec984f6b5f990260df2cee Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 17 Feb 2017 17:06:42 +0100
Subject: [PATCH 12/13] Update server/replica installer man pages

Since AD trust installer is now a part of composite installers, their
man pages were updated with separate section documenting relevant AD
trust-related option descriptions.

https://fedorahosted.org/freeipa/ticket/6630
---
 install/tools/man/ipa-replica-install.1 | 64 +++++++++++++++++++++++++++++++++
 install/tools/man/ipa-server-install.1  | 44 +++++++++++++++++++++++
 2 files changed, 108 insertions(+)

diff --git a/install/tools/man/ipa-replica-install.1 b/install/tools/man/ipa-replica-install.1
index 2c09666..f9ebd87 100644
--- a/install/tools/man/ipa-replica-install.1
+++ b/install/tools/man/ipa-replica-install.1
@@ -199,6 +199,70 @@ Do not automatically create DNS SSHFP records.
 \fB\-\-no\-dnssec\-validation\fR
 Disable DNSSEC validation on this server.
 
+.SS "AD TRUST OPTIONS"
+.TP
+\fB\-\-netbios\-name\fR=\fINETBIOS_NAME\fR
+The NetBIOS name for the IPA domain. If not provided then this is determined
+based on the leading component of the DNS domain name. Running
+ipa\-adtrust\-install for a second time with a different NetBIOS name will
+change the name. Please note that changing the NetBIOS name might break
+existing trust relationships to other domains.
+.TP
+\fB\-\-add\-sids\fR
+Add SIDs to existing users and groups as on of final steps of the
+ipa\-adtrust\-install run. If there a many existing users and groups and a
+couple of replicas in the environment this operation might lead to a high
+replication traffic and a performance degradation of all IPA servers in the
+environment. To avoid this the SID generation can be run after
+ipa\-adtrust\-install is run and scheduled independently. To start this task
+you have to load an edited version of ipa-sidgen-task-run.ldif with the
+ldapmodify command info the directory server.
+.TP
+\fB\-\-add\-agents\fR
+Add IPA masters to the list that allows to serve information about
+users from trusted forests. Starting with FreeIPA 4.2, a regular IPA master
+can provide this information to SSSD clients. IPA masters aren't added
+to the list automatically as restart of the LDAP service on each of them
+is required. The host where ipa\-adtrust\-install is being run is added
+automatically.
+.IP
+Note that IPA masters where ipa\-adtrust\-install wasn't run, can serve
+information about users from trusted forests only if they are enabled
+via \ipa-adtrust\-install run on any other IPA master. At least SSSD
+version 1.13 on IPA master is required to be able to perform as a trust agent.
+.TP
+\fB\-\-rid-base\fR=\fIRID_BASE\fR
+First RID value of the local domain. The first Posix ID of the local domain will
+be assigned to this RID, the second to RID+1 etc. See the online help of the
+idrange CLI for details.
+.TP
+\fB\-\-secondary-rid-base\fR=\fISECONDARY_RID_BASE\fR
+Start value of the secondary RID range, which is only used in the case a user
+and a group share numerically the same Posix ID. See the online help of the
+idrange CLI for details.
+.TP
+\fB\-\-enable\-compat\fR
+Enables support for trusted domains users for old clients through Schema Compatibility plugin.
+SSSD supports trusted domains natively starting with version 1.9. For platforms that
+lack SSSD or run older SSSD version one needs to use this option. When enabled, slapi\-nis package
+needs to be installed and schema\-compat\-plugin will be configured to provide lookup of
+users and groups from trusted domains via SSSD on IPA server. These users and groups will be
+available under \fBcn=users,cn=compat,$SUFFIX\fR and \fBcn=groups,cn=compat,$SUFFIX\fR trees.
+SSSD will normalize names of users and groups to lower case.
+.IP
+In addition to providing these users and groups through the compat tree, this option enables
+authentication over LDAP for trusted domain users with DN under compat tree, i.e. using bind DN
+\fBuid=administrator@ad.domain,cn=users,cn=compat,$SUFFIX\fR.
+.IP
+LDAP authentication performed by the compat tree is done via PAM '\fBsystem\-auth\fR' service.
+This service exists by default on Linux systems and is provided by pam package as /etc/pam.d/system\-auth.
+If your IPA install does not have default HBAC rule 'allow_all' enabled, then make sure
+to define in IPA special service called '\fBsystem\-auth\fR' and create an HBAC
+rule to allow access to anyone to this rule on IPA masters.
+.IP
+As '\fBsystem\-auth\fR' PAM service is not used directly by any other
+application, it is safe to use it for trusted domain users via compatibility
+path.
 .SH "EXIT STATUS"
 0 if the command was successful
 
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index a7c7f81..cd68f72 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -195,6 +195,49 @@ Disable DNSSEC validation on this server.
 \fB\-\-allow\-zone\-overlap\fR
 Allow creatin of (reverse) zone even if the zone is already resolvable. Using this option is discouraged as it result in later problems with domain name resolution.
 
+.SS "AD TRUST OPRIONS"
+
+.TP
+\fB\-\-netbios\-name\fR=\fINETBIOS_NAME\fR
+The NetBIOS name for the IPA domain. If not provided then this is determined
+based on the leading component of the DNS domain name. Running
+ipa\-adtrust\-install for a second time with a different NetBIOS name will
+change the name. Please note that changing the NetBIOS name might break
+existing trust relationships to other domains.
+.TP
+\fB\-\-rid-base\fR=\fIRID_BASE\fR
+First RID value of the local domain. The first Posix ID of the local domain will
+be assigned to this RID, the second to RID+1 etc. See the online help of the
+idrange CLI for details.
+.TP
+\fB\-\-secondary-rid-base\fR=\fISECONDARY_RID_BASE\fR
+Start value of the secondary RID range, which is only used in the case a user
+and a group share numerically the same Posix ID. See the online help of the
+idrange CLI for details.
+.TP
+\fB\-\-enable\-compat\fR
+Enables support for trusted domains users for old clients through Schema Compatibility plugin.
+SSSD supports trusted domains natively starting with version 1.9. For platforms that
+lack SSSD or run older SSSD version one needs to use this option. When enabled, slapi\-nis package
+needs to be installed and schema\-compat\-plugin will be configured to provide lookup of
+users and groups from trusted domains via SSSD on IPA server. These users and groups will be
+available under \fBcn=users,cn=compat,$SUFFIX\fR and \fBcn=groups,cn=compat,$SUFFIX\fR trees.
+SSSD will normalize names of users and groups to lower case.
+.IP
+In addition to providing these users and groups through the compat tree, this option enables
+authentication over LDAP for trusted domain users with DN under compat tree, i.e. using bind DN
+\fBuid=administrator@ad.domain,cn=users,cn=compat,$SUFFIX\fR.
+.IP
+LDAP authentication performed by the compat tree is done via PAM '\fBsystem\-auth\fR' service.
+This service exists by default on Linux systems and is provided by pam package as /etc/pam.d/system\-auth.
+If your IPA install does not have default HBAC rule 'allow_all' enabled, then make sure
+to define in IPA special service called '\fBsystem\-auth\fR' and create an HBAC
+rule to allow access to anyone to this rule on IPA masters.
+.IP
+As '\fBsystem\-auth\fR' PAM service is not used directly by any other
+application, it is safe to use it for trusted domain users via compatibility
+path.
+
 .SS "UNINSTALL OPTIONS"
 .TP
 \fB\-\-uninstall\fR
@@ -215,3 +258,4 @@ The kerberos master password (normally autogenerated).
 
 .SH "SEE ALSO"
 .BR ipa-dns-install (1)
+.BR ipa-adtrust-install (1)

From 936d286dbe604d22327217c22d174635ad2bb522 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Mon, 20 Feb 2017 15:17:11 +0100
Subject: [PATCH 13/13] Provide basic integration tests for built-in AD trust
 installer

A couple of tests were added to server/replica install integration
suite to test AD trust install w/ various combinations of other optional
components.

https://fedorahosted.org/freeipa/ticket/6630
---
 ipatests/test_integration/tasks.py             | 15 +++++---
 ipatests/test_integration/test_installation.py | 49 ++++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 5 deletions(-)

diff --git a/ipatests/test_integration/tasks.py b/ipatests/test_integration/tasks.py
index 9523450..6620d12 100644
--- a/ipatests/test_integration/tasks.py
+++ b/ipatests/test_integration/tasks.py
@@ -249,9 +249,9 @@ def enable_replication_debugging(host):
                      stdin_text=logging_ldif)
 
 
-def install_master(host, setup_dns=True, setup_kra=False, extra_args=(),
-                   domain_level=None, unattended=True, stdin_text=None,
-                   raiseonerr=True):
+def install_master(host, setup_dns=True, setup_kra=False, setup_adtrust=False,
+                   extra_args=(), domain_level=None, unattended=True,
+                   stdin_text=None, raiseonerr=True):
     if domain_level is None:
         domain_level = host.config.domain_level
     setup_server_logs_collecting(host)
@@ -275,6 +275,8 @@ def install_master(host, setup_dns=True, setup_kra=False, extra_args=(),
             '--forwarder', host.config.dns_forwarder,
             '--auto-reverse'
         ])
+    if setup_adtrust:
+        args.append('--setup-adtrust')
 
     args.extend(extra_args)
     result = host.run_command(args, raiseonerr=raiseonerr,
@@ -343,8 +345,9 @@ def replica_prepare(master, replica, extra_args=(),
 
 
 def install_replica(master, replica, setup_ca=True, setup_dns=False,
-                    setup_kra=False, extra_args=(), domain_level=None,
-                    unattended=True, stdin_text=None, raiseonerr=True):
+                    setup_kra=False, setup_adtrust=False, extra_args=(),
+                    domain_level=None, unattended=True, stdin_text=None,
+                    raiseonerr=True):
     if domain_level is None:
         domain_level = domainlevel(master)
     apply_common_fixes(replica)
@@ -367,6 +370,8 @@ def install_replica(master, replica, setup_ca=True, setup_dns=False,
             '--setup-dns',
             '--forwarder', replica.config.dns_forwarder
         ])
+    if setup_adtrust:
+        args.append('--setup-adtrust')
     if master_authoritative_for_client_domain(master, replica):
         args.extend(['--ip-address', replica.ip])
 
diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
index 0929f13..f3e9eba 100644
--- a/ipatests/test_integration/test_installation.py
+++ b/ipatests/test_integration/test_installation.py
@@ -84,6 +84,29 @@ def test_replica2_ipa_kra_install(self):
         tasks.install_kra(self.replicas[2])
 
 
+class ADTrustInstallTestBase(IntegrationTest):
+    """
+    Base test for builtin AD trust installation im combination with other
+    components
+    """
+    num_replicas = 2
+    topology = 'star'
+
+    @classmethod
+    def install(cls, mh):
+        tasks.install_master(cls.master, setup_dns=False)
+
+    def install_replica(self, replica, **kwargs):
+        tasks.install_replica(self.master, replica, setup_adtrust=True,
+                              **kwargs)
+
+    def test_replica0_only_adtrust(self):
+        self.install_replica(self.replicas[0], setup_ca=False)
+
+    def test_replica1_all_components_adtrust(self):
+        self.install_replica(self.replicas[1], setup_ca=True)
+
+
 ##
 # Master X Replicas installation tests
 ##
@@ -213,6 +236,32 @@ def install(cls, mh):
         tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
 
 
+class TestADTrustInstall(ADTrustInstallTestBase):
+    """
+    Tests built-in AD trust installation in various combinations (see the base
+    class for more details) against plain IPA master (no DNS, no KRA, no AD
+    trust)
+    """
+    pass
+
+
+class TestADTrustInstallWithDNS_KRA_ADTrust(ADTrustInstallTestBase):
+    """
+    Tests built-in AD trust installation in various combinations (see the base
+    class for more details) against fully equipped (DNS, CA, KRA, ADtrust)
+    master. Additional two test cases were added to test interplay including
+    KRA installer
+    """
+
+    @classmethod
+    def install(cls, mh):
+        tasks.install_master(cls.master, setup_dns=True, setup_kra=True,
+                             setup_adtrust=True)
+
+    def test_replica1_all_components_adtrust(self):
+        self.install_replica(self.replicas[1], setup_ca=True, setup_kra=True)
+
+
 ##
 # Rest of master installation tests
 ##
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to