diff --git a/.gitignore b/.gitignore
index 26693ca..2d2eb03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
-SOURCES/freeipa-4.6.4.tar.gz
+SOURCES/freeipa-4.6.5.tar.gz
 SOURCES/header-logo.png
 SOURCES/login-screen-background.jpg
-SOURCES/login-screen-logo.png
 SOURCES/product-name.png
diff --git a/.ipa.metadata b/.ipa.metadata
index 2d52a52..298fd0e 100644
--- a/.ipa.metadata
+++ b/.ipa.metadata
@@ -1,5 +1,4 @@
-a3be356b119c9c5e223e4c077fad21e1dbc0ce92 SOURCES/freeipa-4.6.4.tar.gz
+45d419f50412d016d194fb0e02a54251b26a7b35 SOURCES/freeipa-4.6.5.tar.gz
 77c318cf1f4fc25cf847de0692a77859a767c0e3 SOURCES/header-logo.png
 8727245558422bf966d60677568925f081b8e299 SOURCES/login-screen-background.jpg
-24a29d79efbd0906777be4639957abda111fca4b SOURCES/login-screen-logo.png
 af82b7b7d327bd683c7d062a6f15713ea91ebedf SOURCES/product-name.png
diff --git a/SOURCES/0001-Coverity-fix-issue-in-ipa_extdom_extop.c.patch b/SOURCES/0001-Coverity-fix-issue-in-ipa_extdom_extop.c.patch
new file mode 100644
index 0000000..836750c
--- /dev/null
+++ b/SOURCES/0001-Coverity-fix-issue-in-ipa_extdom_extop.c.patch
@@ -0,0 +1,33 @@
+From 5fb390bc2aed0f460e96c0e98dd57ecf01aec01c Mon Sep 17 00:00:00 2001
+From: Florence Blanc-Renaud <flo@redhat.com>
+Date: Thu, 21 Mar 2019 13:59:32 +0100
+Subject: [PATCH] Coverity: fix issue in ipa_extdom_extop.c
+
+Coverity found the following issue:
+Error: BAD_COMPARE (CWE-697): [#def1]
+freeipa-4.6.5/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c:121: null_misuse: Comparing pointer "threadnumber" against "NULL" using anything besides "==" or "!=" is likely to be incorrect.
+
+The comparison is using the pointer while it should use the pointed value.
+
+Fixes: https://pagure.io/freeipa/issue/7884
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
+index 3abaa411d2873dedb27ac83bc53d82677577e5b5..10d3f86ebad920fb9c051aa428cbd675b682f14a 100644
+--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
++++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
+@@ -118,7 +118,7 @@ static int ipa_get_threadnumber(Slapi_ComponentId *plugin_id, size_t *threadnumb
+     *threadnumber = slapi_entry_attr_get_uint(search_entries[0],
+                                               NSSLAPD_THREADNUMBER);
+ 
+-    if (threadnumber <= 0) {
++    if (*threadnumber <= 0) {
+         LOG_FATAL("No thread number found.\n");
+         ret = LDAP_OPERATIONS_ERROR;
+         goto done;
+-- 
+2.20.1
+
diff --git a/SOURCES/0001-Use-replace-instead-of-add-to-set-new-default-ipaSEL.patch b/SOURCES/0001-Use-replace-instead-of-add-to-set-new-default-ipaSEL.patch
deleted file mode 100644
index d3d5c74..0000000
--- a/SOURCES/0001-Use-replace-instead-of-add-to-set-new-default-ipaSEL.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 463e5e73a27bb31e3549c9204efe20555b7cb8dd Mon Sep 17 00:00:00 2001
-From: Rob Crittenden <rcritten@redhat.com>
-Date: Fri, 1 Jun 2018 15:19:35 -0400
-Subject: [PATCH] Use replace instead of add to set new default
- ipaSELinuxUserMapOrder
-
-The add was in effect replacing whatever data was already there
-causing any custom order to be lost on each run of
-ipa-server-upgrade.
-
-https://pagure.io/freeipa/issue/6610
-
-Signed-off-by: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
-Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
----
- install/updates/50-ipaconfig.update        |  2 +-
- ipatests/test_integration/test_commands.py | 48 ++++++++++++++++++++++++++++++
- 2 files changed, 49 insertions(+), 1 deletion(-)
- create mode 100644 ipatests/test_integration/test_commands.py
-
-diff --git a/install/updates/50-ipaconfig.update b/install/updates/50-ipaconfig.update
-index 23d2919dbd976c34d9217fc31cca88a0df6c7f5b..18501cb7b8a87377a76bc53b7fe3c469c23e2d41 100644
---- a/install/updates/50-ipaconfig.update
-+++ b/install/updates/50-ipaconfig.update
-@@ -1,5 +1,5 @@
- dn: cn=ipaConfig,cn=etc,$SUFFIX
--add:ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023
-+replace: ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0-s0:c0.c1023$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023::ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023
- add:ipaSELinuxUserMapDefault: unconfined_u:s0-s0:c0.c1023
- add:ipaUserObjectClasses: ipasshuser
- remove:ipaConfigString:AllowLMhash
-diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
-new file mode 100644
-index 0000000000000000000000000000000000000000..b2c0d5c710c9810cfd74216983f793808f4cf3c4
---- /dev/null
-+++ b/ipatests/test_integration/test_commands.py
-@@ -0,0 +1,48 @@
-+#
-+# Copyright (C) 2018  FreeIPA Contributors see COPYING for license
-+#
-+"""Misc test for 'ipa' CLI regressions
-+"""
-+from __future__ import absolute_import
-+
-+from ipatests.test_integration.base import IntegrationTest
-+
-+
-+class TestIPACommand(IntegrationTest):
-+    """
-+    A lot of commands can be executed against a single IPA installation
-+    so provide a generic class to execute one-off commands that need to be
-+    tested without having to fire up a full server to run one command.
-+    """
-+    topology = 'line'
-+
-+    def test_change_selinuxusermaporder(self):
-+        """
-+        An update file meant to ensure a more sane default was
-+        overriding any customization done to the order.
-+        """
-+        maporder = "unconfined_u:s0-s0:c0.c1023"
-+
-+        # set a new default
-+        result = self.master.run_command(
-+            ["ipa", "config-mod",
-+             "--ipaselinuxusermaporder={}".format(maporder)],
-+            raiseonerr=False
-+        )
-+        assert result.returncode == 0
-+
-+        # apply the update
-+        result = self.master.run_command(
-+            ["ipa-server-upgrade"],
-+            raiseonerr=False
-+        )
-+        assert result.returncode == 0
-+
-+        # ensure result is the same
-+        result = self.master.run_command(
-+            ["ipa", "config-show"],
-+            raiseonerr=False
-+        )
-+        assert result.returncode == 0
-+        assert "SELinux user map order: {}".format(
-+            maporder) in result.stdout_text
--- 
-2.14.4
-
diff --git a/SOURCES/0002-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch b/SOURCES/0002-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch
deleted file mode 100644
index 23b67a9..0000000
--- a/SOURCES/0002-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch
+++ /dev/null
@@ -1,438 +0,0 @@
-From a56b9ef37dde90a593da6adbae2525048c5c8627 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 15 Jun 2018 17:03:29 +0200
-Subject: [PATCH] Sort and shuffle SRV record by priority and weight
-
-On multiple occasions, SRV query answers were not properly sorted by
-priority. Records with same priority weren't randomized and shuffled.
-This caused FreeIPA to contact the same remote peer instead of
-distributing the load across all available servers.
-
-Two new helper functions now take care of SRV queries. sort_prio_weight()
-sorts SRV and URI records. query_srv() combines SRV lookup with
-sort_prio_weight().
-
-Fixes: https://pagure.io/freeipa/issue/7475
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaclient/install/ipadiscovery.py       |   5 +-
- ipalib/rpc.py                           |  21 +++----
- ipalib/util.py                          |  11 ++--
- ipapython/config.py                     |   8 +--
- ipapython/dnsutil.py                    |  92 ++++++++++++++++++++++++++-
- ipaserver/dcerpc.py                     |   4 +-
- ipatests/test_ipapython/test_dnsutil.py | 106 ++++++++++++++++++++++++++++++++
- 7 files changed, 217 insertions(+), 30 deletions(-)
- create mode 100644 ipatests/test_ipapython/test_dnsutil.py
-
-diff --git a/ipaclient/install/ipadiscovery.py b/ipaclient/install/ipadiscovery.py
-index 363970c86ba65b7ec49e403adc745fe61b2242aa..a8283d8e6ad070a8b6626a187ff55485bb74eba4 100644
---- a/ipaclient/install/ipadiscovery.py
-+++ b/ipaclient/install/ipadiscovery.py
-@@ -20,7 +20,6 @@
- from __future__ import absolute_import
- 
- import logging
--import operator
- import socket
- 
- import six
-@@ -28,6 +27,7 @@ import six
- from dns import resolver, rdatatype
- from dns.exception import DNSException
- from ipalib import errors
-+from ipapython.dnsutil import query_srv
- from ipapython import ipaldap
- from ipaplatform.paths import paths
- from ipapython.ipautil import valid_ip, realm_to_suffix
-@@ -498,8 +498,7 @@ class IPADiscovery(object):
-         logger.debug("Search DNS for SRV record of %s", qname)
- 
-         try:
--            answers = resolver.query(qname, rdatatype.SRV)
--            answers = sorted(answers, key=operator.attrgetter('priority'))
-+            answers = query_srv(qname)
-         except DNSException as e:
-             logger.debug("DNS record not found: %s", e.__class__.__name__)
-             answers = []
-diff --git a/ipalib/rpc.py b/ipalib/rpc.py
-index c6a8989f5dc157f4c4e59637e6e7d114c5fa952c..17368f160148d6b821ec4934d31b4b4034a7e67b 100644
---- a/ipalib/rpc.py
-+++ b/ipalib/rpc.py
-@@ -45,7 +45,6 @@ import gzip
- from cryptography import x509 as crypto_x509
- 
- import gssapi
--from dns import resolver, rdatatype
- from dns.exception import DNSException
- from ssl import SSLError
- import six
-@@ -61,7 +60,7 @@ from ipalib.x509 import Encoding as x509_Encoding
- from ipapython import ipautil
- from ipapython import session_storage
- from ipapython.cookie import Cookie
--from ipapython.dnsutil import DNSName
-+from ipapython.dnsutil import DNSName, query_srv
- from ipalib.text import _
- from ipalib.util import create_https_connection
- from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \
-@@ -878,7 +877,7 @@ class RPCClient(Connectible):
-         name = '_ldap._tcp.%s.' % self.env.domain
- 
-         try:
--            answers = resolver.query(name, rdatatype.SRV)
-+            answers = query_srv(name)
-         except DNSException:
-             answers = []
- 
-@@ -886,17 +885,11 @@ class RPCClient(Connectible):
-             server = str(answer.target).rstrip(".")
-             servers.append('https://%s%s' % (ipautil.format_netloc(server), path))
- 
--        servers = list(set(servers))
--        # the list/set conversion won't preserve order so stick in the
--        # local config file version here.
--        cfg_server = rpc_uri
--        if cfg_server in servers:
--            # make sure the configured master server is there just once and
--            # it is the first one
--            servers.remove(cfg_server)
--            servers.insert(0, cfg_server)
--        else:
--            servers.insert(0, cfg_server)
-+        # make sure the configured master server is there just once and
-+        # it is the first one.
-+        if rpc_uri in servers:
-+            servers.remove(rpc_uri)
-+        servers.insert(0, rpc_uri)
- 
-         return servers
- 
-diff --git a/ipalib/util.py b/ipalib/util.py
-index ebf6eb3faf91cefc02514afa84ad9b63d0f82b0b..592821f9ff4f9c16fb2589d697e3e202443d0eba 100644
---- a/ipalib/util.py
-+++ b/ipalib/util.py
-@@ -973,14 +973,13 @@ def detect_dns_zone_realm_type(api, domain):
- 
-     try:
-         # The presence of this record is enough, return foreign in such case
--        result = resolver.query(ad_specific_record_name, rdatatype.SRV)
--        return 'foreign'
--
-+        resolver.query(ad_specific_record_name, rdatatype.SRV)
-     except DNSException:
--        pass
-+        # If we could not detect type with certainty, return unknown
-+        return 'unknown'
-+    else:
-+        return 'foreign'
- 
--    # If we could not detect type with certainity, return unknown
--    return 'unknown'
- 
- def has_managed_topology(api):
-     domainlevel = api.Command['domainlevel_get']().get('result', DOMAIN_LEVEL_0)
-diff --git a/ipapython/config.py b/ipapython/config.py
-index c3360779f1320e7d41f4d98b317d930a1d0b8242..f701122bd4828693a41848668e49052912042837 100644
---- a/ipapython/config.py
-+++ b/ipapython/config.py
-@@ -26,7 +26,6 @@ from copy import copy
- import socket
- import functools
- 
--from dns import resolver, rdatatype
- from dns.exception import DNSException
- import dns.name
- # pylint: disable=import-error
-@@ -36,6 +35,7 @@ from six.moves.urllib.parse import urlsplit
- 
- from ipaplatform.paths import paths
- from ipapython.dn import DN
-+from ipapython.dnsutil import query_srv
- from ipapython.ipautil import CheckedIPAddress, CheckedIPAddressLoopback
- 
- 
-@@ -210,7 +210,7 @@ def __discover_config(discover_server = True):
-             name = "_ldap._tcp." + domain
- 
-             try:
--                servers = resolver.query(name, rdatatype.SRV)
-+                servers = query_srv(name)
-             except DNSException:
-                 # try cycling on domain components of FQDN
-                 try:
-@@ -225,7 +225,7 @@ def __discover_config(discover_server = True):
-                         return False
-                     name = "_ldap._tcp.%s" % domain
-                     try:
--                        servers = resolver.query(name, rdatatype.SRV)
-+                        servers = query_srv(name)
-                         break
-                     except DNSException:
-                         pass
-@@ -236,7 +236,7 @@ def __discover_config(discover_server = True):
-             if not servers:
-                 name = "_ldap._tcp.%s." % config.default_domain
-                 try:
--                    servers = resolver.query(name, rdatatype.SRV)
-+                    servers = query_srv(name)
-                 except DNSException:
-                     pass
- 
-diff --git a/ipapython/dnsutil.py b/ipapython/dnsutil.py
-index b40302d0efbb32108626d2cc1216ad4858343407..6157183a0fa5802a5fb772f078ee4c1688857fc8 100644
---- a/ipapython/dnsutil.py
-+++ b/ipapython/dnsutil.py
-@@ -17,12 +17,17 @@
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- #
- 
-+import copy
- import logging
-+import operator
-+import random
- 
- import dns.name
- import dns.exception
- import dns.resolver
--import copy
-+import dns.rdataclass
-+import dns.rdatatype
-+
- 
- import six
- 
-@@ -373,3 +378,88 @@ def check_zone_overlap(zone, raise_on_error=True):
-         if ns:
-             msg += u" and is handled by server(s): {0}".format(', '.join(ns))
-         raise ValueError(msg)
-+
-+
-+def _mix_weight(records):
-+    """Weighted population sorting for records with same priority
-+    """
-+    # trivial case
-+    if len(records) <= 1:
-+        return records
-+
-+    # Optimization for common case: If all weights are the same (e.g. 0),
-+    # just shuffle the records, which is about four times faster.
-+    if all(rr.weight == records[0].weight for rr in records):
-+        random.shuffle(records)
-+        return records
-+
-+    noweight = 0.01  # give records with 0 weight a small chance
-+    result = []
-+    records = set(records)
-+    while len(records) > 1:
-+        # Compute the sum of the weights of those RRs. Then choose a
-+        # uniform random number between 0 and the sum computed (inclusive).
-+        urn = random.uniform(0, sum(rr.weight or noweight for rr in records))
-+        # Select the RR whose running sum value is the first in the selected
-+        # order which is greater than or equal to the random number selected.
-+        acc = 0.
-+        for rr in records.copy():
-+            acc += rr.weight or noweight
-+            if acc >= urn:
-+                records.remove(rr)
-+                result.append(rr)
-+    if records:
-+        result.append(records.pop())
-+    return result
-+
-+
-+def sort_prio_weight(records):
-+    """RFC 2782 sorting algorithm for SRV and URI records
-+
-+    RFC 2782 defines a sorting algorithms for SRV records, that is also used
-+    for sorting URI records. Records are sorted by priority and than randomly
-+    shuffled according to weight.
-+
-+    This implementation also removes duplicate entries.
-+    """
-+    # order records by priority
-+    records = sorted(records, key=operator.attrgetter("priority"))
-+
-+    # remove duplicate entries
-+    uniquerecords = []
-+    seen = set()
-+    for rr in records:
-+        # A SRV record has target and port, URI just has target.
-+        target = (rr.target, getattr(rr, "port", None))
-+        if target not in seen:
-+            uniquerecords.append(rr)
-+            seen.add(target)
-+
-+    # weighted randomization of entries with same priority
-+    result = []
-+    sameprio = []
-+    for rr in uniquerecords:
-+        # add all items with same priority in a bucket
-+        if not sameprio or sameprio[0].priority == rr.priority:
-+            sameprio.append(rr)
-+        else:
-+            # got different priority, shuffle bucket
-+            result.extend(_mix_weight(sameprio))
-+            # start a new priority list
-+            sameprio = [rr]
-+    # add last batch of records with same priority
-+    if sameprio:
-+        result.extend(_mix_weight(sameprio))
-+    return result
-+
-+
-+def query_srv(qname, resolver=None, **kwargs):
-+    """Query SRV records and sort reply according to RFC 2782
-+
-+    :param qname: query name, _service._proto.domain.
-+    :return: list of dns.rdtypes.IN.SRV.SRV instances
-+    """
-+    if resolver is None:
-+        resolver = dns.resolver
-+    answer = resolver.query(qname, rdtype=dns.rdatatype.SRV, **kwargs)
-+    return sort_prio_weight(answer)
-diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
-index e3aa9f6a6c239f8606f09bde06f21cb9cf64eb14..1e2d6fbbb5372bea7c6d68b73acededfe714a56a 100644
---- a/ipaserver/dcerpc.py
-+++ b/ipaserver/dcerpc.py
-@@ -32,6 +32,7 @@ from ipalib import api, _
- from ipalib import errors
- from ipapython import ipautil
- from ipapython.dn import DN
-+from ipapython.dnsutil import query_srv
- from ipapython.ipaldap import ldap_initialize
- from ipaserver.install import installutils
- from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL,
-@@ -55,7 +56,6 @@ import samba
- import ldap as _ldap
- from ipapython import ipaldap
- from ipapython.dnsutil import DNSName
--from dns import resolver, rdatatype
- from dns.exception import DNSException
- import pysss_nss_idmap
- import pysss
-@@ -802,7 +802,7 @@ class DomainValidator(object):
-             gc_name = '_gc._tcp.%s.' % info['dns_domain']
- 
-             try:
--                answers = resolver.query(gc_name, rdatatype.SRV)
-+                answers = query_srv(gc_name)
-             except DNSException as e:
-                 answers = []
- 
-diff --git a/ipatests/test_ipapython/test_dnsutil.py b/ipatests/test_ipapython/test_dnsutil.py
-new file mode 100644
-index 0000000000000000000000000000000000000000..36adb077cf38f6d036aa1048b201dee7d08eb310
---- /dev/null
-+++ b/ipatests/test_ipapython/test_dnsutil.py
-@@ -0,0 +1,106 @@
-+#
-+# Copyright (C) 2018  FreeIPA Contributors.  See COPYING for license
-+#
-+import dns.name
-+import dns.rdataclass
-+import dns.rdatatype
-+from dns.rdtypes.IN.SRV import SRV
-+from dns.rdtypes.ANY.URI import URI
-+
-+from ipapython import dnsutil
-+
-+import pytest
-+
-+
-+def mksrv(priority, weight, port, target):
-+    return SRV(
-+        rdclass=dns.rdataclass.IN,
-+        rdtype=dns.rdatatype.SRV,
-+        priority=priority,
-+        weight=weight,
-+        port=port,
-+        target=dns.name.from_text(target)
-+    )
-+
-+
-+def mkuri(priority, weight, target):
-+    return URI(
-+        rdclass=dns.rdataclass.IN,
-+        rdtype=dns.rdatatype.URI,
-+        priority=priority,
-+        weight=weight,
-+        target=target
-+    )
-+
-+
-+class TestSortSRV(object):
-+    def test_empty(self):
-+        assert dnsutil.sort_prio_weight([]) == []
-+
-+    def test_one(self):
-+        h1 = mksrv(1, 0, 443, u"host1")
-+        assert dnsutil.sort_prio_weight([h1]) == [h1]
-+
-+        h2 = mksrv(10, 5, 443, u"host2")
-+        assert dnsutil.sort_prio_weight([h2]) == [h2]
-+
-+    def test_prio(self):
-+        h1 = mksrv(1, 0, 443, u"host1")
-+        h2 = mksrv(2, 0, 443, u"host2")
-+        h3 = mksrv(3, 0, 443, u"host3")
-+        assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3]
-+        assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3]
-+        assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2]
-+
-+        h380 = mksrv(4, 0, 80, u"host3")
-+        assert dnsutil.sort_prio_weight([h1, h3, h380]) == [h1, h3, h380]
-+
-+        hs = mksrv(-1, 0, 443, u"special")
-+        assert dnsutil.sort_prio_weight([h1, h2, hs]) == [hs, h1, h2]
-+
-+    def assert_permutations(self, answers, permutations):
-+        seen = set()
-+        for _unused in range(1000):
-+            result = tuple(dnsutil.sort_prio_weight(answers))
-+            assert result in permutations
-+            seen.add(result)
-+            if seen == permutations:
-+                break
-+        else:
-+            pytest.fail("sorting didn't exhaust all permutations.")
-+
-+    def test_sameprio(self):
-+        h1 = mksrv(1, 0, 443, u"host1")
-+        h2 = mksrv(1, 0, 443, u"host2")
-+        permutations = {
-+            (h1, h2),
-+            (h2, h1),
-+        }
-+        self.assert_permutations([h1, h2], permutations)
-+
-+    def test_weight(self):
-+        h1 = mksrv(1, 0, 443, u"host1")
-+        h2_w15 = mksrv(2, 15, 443, u"host2")
-+        h3_w10 = mksrv(2, 10, 443, u"host3")
-+
-+        permutations = {
-+            (h1, h2_w15, h3_w10),
-+            (h1, h3_w10, h2_w15),
-+        }
-+        self.assert_permutations([h1, h2_w15, h3_w10], permutations)
-+
-+    def test_large(self):
-+        records = tuple(
-+            mksrv(1, i, 443, "host{}".format(i)) for i in range(1000)
-+        )
-+        assert len(dnsutil.sort_prio_weight(records)) == len(records)
-+
-+
-+class TestSortURI(object):
-+    def test_prio(self):
-+        h1 = mkuri(1, 0, u"https://host1/api")
-+        h2 = mkuri(2, 0, u"https://host2/api")
-+        h3 = mkuri(3, 0, u"https://host3/api")
-+        assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3]
-+        assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3]
-+        assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2]
--- 
-2.14.4
-
diff --git a/SOURCES/0002-Web-UI-topology-graph-Show-FQDN-for-nodes-if-they-ha.patch b/SOURCES/0002-Web-UI-topology-graph-Show-FQDN-for-nodes-if-they-ha.patch
new file mode 100644
index 0000000..ba1a806
--- /dev/null
+++ b/SOURCES/0002-Web-UI-topology-graph-Show-FQDN-for-nodes-if-they-ha.patch
@@ -0,0 +1,361 @@
+From 4b853e8c2f57034c9a72b96fc028e66aa3175a07 Mon Sep 17 00:00:00 2001
+From: Serhii Tsymbaliuk <stsymbal@redhat.com>
+Date: Tue, 12 Feb 2019 10:44:33 +0100
+Subject: [PATCH] Web UI (topology graph): Show FQDN for nodes if they have no
+ common DNS zone
+
+It allows to avoid confusion with identical short hostnames.
+
+There are two cases implemented:
+- no common DNS zone: graph shows FQDN for all nodes
+- all nodes have one common DNS zone: graph shows DN relatively to the common zone
+
+https://pagure.io/freeipa/issue/7206
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ install/ui/src/freeipa/topology.js       |  64 +++++++++
+ install/ui/src/freeipa/topology_graph.js |   3 +-
+ install/ui/test/all_tests.html           |   4 +-
+ install/ui/test/index.html               |   1 +
+ install/ui/test/topology_tests.html      |  25 ++++
+ install/ui/test/topology_tests.js        | 158 +++++++++++++++++++++++
+ 6 files changed, 252 insertions(+), 3 deletions(-)
+ create mode 100644 install/ui/test/topology_tests.html
+ create mode 100644 install/ui/test/topology_tests.js
+
+diff --git a/install/ui/src/freeipa/topology.js b/install/ui/src/freeipa/topology.js
+index e98cb1e0ace1874056d3affa98c9518509f90558..fd7a2833abc04ffe7c3f8405bde4df0e6b942349 100644
+--- a/install/ui/src/freeipa/topology.js
++++ b/install/ui/src/freeipa/topology.js
+@@ -1374,6 +1374,33 @@ topology.TopologyGraphWidget = declare([Stateful, Evented], {
+         return deferred.promise;
+     },
+ 
++    _find_common_domain: function(nodes) {
++        if (nodes.length < 2) {
++            return '';
++        }
++
++        var common_labels = null;
++
++        for (var i=0, l=nodes.length; i<l; i++) {
++            var node = nodes[i];
++            var labels = node.id.split('.').reverse();
++
++            if (common_labels === null) {
++                common_labels = labels;
++                continue;
++            }
++
++            for (var j=0; j<common_labels.length; j++) {
++                if (labels[j] !== common_labels[j]) {
++                    common_labels = common_labels.slice(0, j);
++                    break;
++                }
++            }
++        }
++
++        return common_labels.reverse().join('.');
++    },
++
+     /**
+      * @param {Object} size - dict contains height and width value. (optional)
+      */
+@@ -1383,6 +1410,43 @@ topology.TopologyGraphWidget = declare([Stateful, Evented], {
+         if (IPA.domain_level < topology.required_domain_level) return;
+ 
+         when(this._get_data()).then(function(data) {
++            // remove common domain labels from node FQDN
++            // Example #1:
++            //   nodes:
++            //    - master.ipa.example.com
++            //    - replica.ipa.example.com
++            //   common domain: ipa.example.com
++            //   captions: master, replica
++            //
++            // Example #2:
++            //   nodes:
++            //    - master.net1.example.com
++            //    - replica.net1.example.com
++            //    - replica.net2.example.com
++            //   common domain: example.com
++            //   captions: master.net1, replica.net1, replica.net2
++            //
++            var common_domain = this._find_common_domain(data.nodes);
++
++            if (this.parent) {
++                var title = this.parent.title;
++                if (common_domain) {
++                    title += ' (' + common_domain + ')';
++                }
++                this.parent.header.title_widget.update({text: title});
++            }
++
++            for (var i=0,l=data.nodes.length; i<l; i++) {
++                var node = data.nodes[i];
++                if (l > 1 && common_domain.length > 0) {
++                    node.caption = node.id.substring(
++                        0, node.id.length - common_domain.length - 1
++                    );
++                } else {
++                    node.caption = node.id;
++                }
++            }
++
+             if (!this.graph) {
+                 this.graph = new topology_graph.TopoGraph({
+                     nodes: data.nodes,
+diff --git a/install/ui/src/freeipa/topology_graph.js b/install/ui/src/freeipa/topology_graph.js
+index 9f549133b516dbfe471080714845b457fd62ab1a..b736a22f63aa9a5685ac5840f60f9e2c89fb4525 100644
+--- a/install/ui/src/freeipa/topology_graph.js
++++ b/install/ui/src/freeipa/topology_graph.js
+@@ -180,7 +180,6 @@ topology_graph.TopoGraph = declare([Evented], {
+         this._target_node = null;
+         this.restart();
+     },
+-
+     _create_svg: function(container) {
+         var self = this;
+ 
+@@ -804,7 +803,7 @@ topology_graph.TopoGraph = declare([Evented], {
+             .attr('class', 'id')
+             .attr('fill', '#002235')
+             .text(function(d) {
+-                return d.id.split('.')[0];
++                return d.caption;
+             });
+ 
+         // remove old nodes
+diff --git a/install/ui/test/all_tests.html b/install/ui/test/all_tests.html
+index cdb04b395a878db4338da7458b8f52f13514ead9..f85ae3390a923ad3083779e1ddfcdca6afa2377e 100644
+--- a/install/ui/test/all_tests.html
++++ b/install/ui/test/all_tests.html
+@@ -26,7 +26,8 @@
+             'test/utils_tests',
+             'test/build_tests',
+             'test/binding_tests',
+-        ], function(om, ipa, details, entity, as, nav, cert, aci, wid, ip, ut, bt, bi){
++            'test/topology_tests',
++        ], function(om, ipa, details, entity, as, nav, cert, aci, wid, ip, ut, bt, bi, topo){
+             om();
+             ipa();
+             details();
+@@ -40,6 +41,7 @@
+             ut();
+             bt();
+             bi();
++            topo();
+         });
+     </script>
+ </head>
+diff --git a/install/ui/test/index.html b/install/ui/test/index.html
+index 89af3211cd9791ee3055ca85ddae6afdf5b9edcf..0fd5b83d696e7e368f5ca7d726c5e4301b05acfe 100644
+--- a/install/ui/test/index.html
++++ b/install/ui/test/index.html
+@@ -37,6 +37,7 @@
+         <li><a href="utils_tests.html">Utils Test Suite</a>
+         <li><a href="build_tests.html">Build Test Suite</a>
+         <li><a href="binding_tests.html">Binding Test Suite</a>
++        <li><a href="topology_tests.html">Topology Test Suite</a>
+         </ul>
+     </div>
+ 
+diff --git a/install/ui/test/topology_tests.html b/install/ui/test/topology_tests.html
+new file mode 100644
+index 0000000000000000000000000000000000000000..29ca44ddcc004efbdad9860276906b7111bb40b0
+--- /dev/null
++++ b/install/ui/test/topology_tests.html
+@@ -0,0 +1,25 @@
++<!DOCTYPE html>
++<html>
++<head>
++    <title>Topology Test Suite</title>
++    <link rel="stylesheet" href="qunit.css" type="text/css" media="screen">
++    <script type="text/javascript" src="qunit.js"></script>
++    <script type="text/javascript" src="../js/libs/loader.js"></script>
++    <script type="text/javascript" src="../js/libs/jquery.js"></script>
++    <script type="text/javascript" src="../js/libs/jquery.ordered-map.js"></script>
++    <script type="text/javascript" src="config.js"></script>
++    <script type="text/javascript" src="../js/dojo/dojo.js"></script>
++
++    <script type="text/javascript">
++        require(['test/topology_tests'], function(tests){ tests() });
++    </script>
++</head>
++<body>
++    <h1 id="qunit-header">Topology Test Suite</h1>
++    <h2 id="qunit-banner"></h2>
++    <div id="qunit-testrunner-toolbar"></div>
++    <h2 id="qunit-userAgent"></h2>
++    <ol id="qunit-tests"></ol>
++    <div id="qunit-fixture"></div>
++</body>
++</html>
+diff --git a/install/ui/test/topology_tests.js b/install/ui/test/topology_tests.js
+new file mode 100644
+index 0000000000000000000000000000000000000000..6458a83f2794969e2916899975707b6c554ac314
+--- /dev/null
++++ b/install/ui/test/topology_tests.js
+@@ -0,0 +1,158 @@
++/**
++ * Copyright (C) 2019  FreeIPA Contributors see COPYING for license
++ */
++
++define([
++        'freeipa/ipa',
++        'freeipa/topology',
++        'freeipa/jquery'],
++    function(IPA, topology, $) {
++        return function() {
++
++var widget;
++
++function inject_data(widget, data) {
++    widget._get_data = function() {
++        return data;
++    };
++}
++
++QUnit.module('topology', {
++    beforeEach: function(assert) {
++        widget = new topology.TopologyGraphWidget(
++            topology.topology_graph_facet_spec
++        );
++        widget.render();
++    }
++});
++
++QUnit.test('Testing TopoGraph nodes', function(assert) {
++    var nodes = [
++        { id: 'master.ipa.test' },
++        { id: 'replica.ipa.test' }
++    ];
++
++    var suffixes = [
++        { cn: ['ca'] },
++        { cn: ['domain'] }
++    ];
++
++    inject_data(widget, { nodes: nodes, links: [], suffixes: suffixes });
++
++    widget.update();
++
++    assert.ok($('circle.node', widget.el).length === nodes.length,
++        'Checking rendered nodes count');
++
++    assert.ok($('text.id:eq(0)', widget.el).text() === 'master',
++        'Checking "master" node label');
++    assert.ok($('text.id:eq(1)', widget.el).text() === 'replica',
++        'Checking "replica" node label');
++
++    assert.ok($('text.suffix:eq(0)', widget.el).text() === 'ca',
++        'Checking "ca" suffix');
++    assert.ok($('text.suffix:eq(1)', widget.el).text() === 'domain',
++        'Checking "domain" suffix');
++});
++
++QUnit.test('Testing TopoGraph links', function(assert) {
++    var nodes = [
++        { id: 'master.ipa.test', targets: { 'replica.ipa.test': [] } },
++        { id: 'replica.ipa.test' }
++    ];
++
++    var suffixes = [
++        { cn: ['ca'] },
++        { cn: ['domain'] }
++    ];
++
++    var links = [{
++        source: 0,
++        target: 1,
++        left: false,
++        right: true,
++        suffix: suffixes[0]
++    }];
++
++    inject_data(widget, { nodes: nodes, links: links, suffixes: suffixes });
++    widget.update();
++
++    assert.ok($('circle.node', widget.el).length === nodes.length,
++        'Checking rendered nodes count');
++
++    var rendered_links = $('path.link', widget.el).not('.dragline');
++    assert.ok(rendered_links.length === 1,
++        'Checking right direction link is rendered');
++
++    var marker = rendered_links.first().css('marker-end');
++    assert.ok(marker && marker !== 'none',
++        'Checking right direction link has proper marker');
++
++    links.push({
++        source: 0,
++        target: 1,
++        left: true,
++        right: false,
++        suffix: suffixes[1]
++    })
++
++    inject_data(widget, {
++        nodes: nodes,
++        links: links,
++        suffixes: suffixes
++    });
++    widget.update();
++
++    rendered_links = $('path.link', widget.el).not('.dragline')
++    assert.ok(rendered_links.length === 2,
++        'Checking left direction link is rendered');
++
++    marker = rendered_links.last().css('marker-start');
++    assert.ok(marker && marker !== 'none',
++        'Checking left direction link has proper marker');
++});
++
++QUnit.test('Testing TopoGraph for multiple DNS zones', function(assert) {
++    var nodes = [
++        { id: 'master.ipa.zone1' },
++        { id: 'replica.ipa.zone1' },
++        { id: 'master.ipa.zone2' },
++        { id: 'master.ipa.zone1.common' },
++        { id: 'replica.ipa.zone2.common' },
++    ];
++
++    var suffixes = [
++        { cn: ['ca'] },
++        { cn: ['domain'] }
++    ];
++
++    inject_data(widget, { nodes: nodes, links: [], suffixes: suffixes });
++    widget.update();
++
++    $('text.id', widget.el).each(function(i) {
++        assert.ok($(this).text() === nodes[i].id,
++            'Checking node label "' + $(this).text() + '" is FQDN');
++    });
++
++    nodes = nodes.filter(function(node) { return /\.common$/.test(node.id) });
++
++    inject_data(widget, { nodes: nodes, links: [], suffixes: suffixes });
++    widget.update();
++
++    $('text.id', widget.el).each(function(i) {
++        assert.ok($(this).text().indexOf('common') < 0,
++            'Checking node label "' + $(this).text() + '" is relative');
++    });
++});
++
++QUnit.test('Testing TopoGraph with one node', function(assert) {
++    var node = { id: 'master.ipa.test' };
++
++    inject_data(widget, { nodes: [node], links: [], suffixes: [] });
++    widget.update();
++
++    assert.ok($('text.id:eq(0)', widget.el).text() === node.id,
++        'Checking node label is FQDN');
++});
++
++};});
+-- 
+2.20.1
+
diff --git a/SOURCES/0003-Increase-WSGI-process-count-to-5-on-64bit.patch b/SOURCES/0003-Increase-WSGI-process-count-to-5-on-64bit.patch
deleted file mode 100644
index 2c1575f..0000000
--- a/SOURCES/0003-Increase-WSGI-process-count-to-5-on-64bit.patch
+++ /dev/null
@@ -1,91 +0,0 @@
-From f9d883b97b24efaf82e510601a4711bc9c0ea9c7 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Thu, 14 Jun 2018 17:04:13 +0200
-Subject: [PATCH] Increase WSGI process count to 5 on 64bit
-
-Increase the WSGI daemon worker process count from 2 processes to 5
-processes. This allows IPA RPC to handle more parallel requests. The
-additional processes increase memory consumption by approximante 250 MB
-in total.
-
-Since memory is scarce on 32bit platforms, only 64bit platforms are
-bumped to 5 workers.
-
-Fixes: https://pagure.io/freeipa/issue/7587
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- install/conf/ipa.conf               | 2 +-
- ipaplatform/base/constants.py       | 5 +++++
- ipaserver/install/httpinstance.py   | 1 +
- ipaserver/install/server/upgrade.py | 3 ++-
- 4 files changed, 9 insertions(+), 2 deletions(-)
-
-diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf
-index 696e5aab1aca1875eb711b91351f41987a8379d1..912a63c2240e0681dfbeeac223a902b15b304716 100644
---- a/install/conf/ipa.conf
-+++ b/install/conf/ipa.conf
-@@ -47,7 +47,7 @@ WSGISocketPrefix /run/httpd/wsgi
- 
- 
- # Configure mod_wsgi handler for /ipa
--WSGIDaemonProcess ipa processes=2 threads=1 maximum-requests=500 \
-+WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \
-   user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 \
-   lang=C.UTF-8 locale=C.UTF-8
- WSGIImportScript /usr/share/ipa/wsgi.py process-group=ipa application-group=ipa
-diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py
-index ca4a12ec017e81b32dc796062608a96ce65a0235..075a3ba774e1286e50258b464bd7687d484f6029 100644
---- a/ipaplatform/base/constants.py
-+++ b/ipaplatform/base/constants.py
-@@ -5,9 +5,11 @@
- '''
- This base platform module exports platform dependant constants.
- '''
-+import sys
- 
- 
- class BaseConstantsNamespace(object):
-+    IS_64BITS = sys.maxsize > 2 ** 32
-     DS_USER = 'dirsrv'
-     DS_GROUP = 'dirsrv'
-     HTTPD_USER = "apache"
-@@ -42,6 +44,9 @@ class BaseConstantsNamespace(object):
-     # WSGI module override, only used on Fedora
-     MOD_WSGI_PYTHON2 = None
-     MOD_WSGI_PYTHON3 = None
-+    # WSGIDaemonProcess process count. On 64bit platforms, each process
-+    # consumes about 110 MB RSS, from which are about 35 MB shared.
-+    WSGI_PROCESSES = 5 if IS_64BITS else 2
- 
- 
- constants = BaseConstantsNamespace()
-diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
-index 231bffa2c6ec1fd124738649912e4bd958e802b7..3764870ee77f2ba0da18ec004664e6f66c13bba1 100644
---- a/ipaserver/install/httpinstance.py
-+++ b/ipaserver/install/httpinstance.py
-@@ -148,6 +148,7 @@ class HTTPInstance(service.Service):
-             DOMAIN=self.domain,
-             AUTOREDIR='' if auto_redirect else '#',
-             CRL_PUBLISH_PATH=paths.PKI_CA_PUBLISH_DIR,
-+            WSGI_PROCESSES=constants.WSGI_PROCESSES,
-         )
-         self.ca_file = ca_file
-         if ca_is_configured is not None:
-diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
-index ab09b98406df251f769c4747c9250075ab610362..ee3cedd78a2f45f33665bf562e9426cf68325544 100644
---- a/ipaserver/install/server/upgrade.py
-+++ b/ipaserver/install/server/upgrade.py
-@@ -1681,7 +1681,8 @@ def upgrade_configuration():
-         AUTOREDIR='' if auto_redirect else '#',
-         CRL_PUBLISH_PATH=paths.PKI_CA_PUBLISH_DIR,
-         DOGTAG_PORT=8009,
--        CLONE='#'
-+        CLONE='#',
-+        WSGI_PROCESSES=constants.WSGI_PROCESSES,
-     )
- 
-     subject_base = find_subject_base()
--- 
-2.14.4
-
diff --git a/SOURCES/0003-ipa-replica-manage-fix-force-sync.patch b/SOURCES/0003-ipa-replica-manage-fix-force-sync.patch
new file mode 100644
index 0000000..60b4fbe
--- /dev/null
+++ b/SOURCES/0003-ipa-replica-manage-fix-force-sync.patch
@@ -0,0 +1,45 @@
+From 6c72f4241c70200d3d6637c254c07504aa19cdc9 Mon Sep 17 00:00:00 2001
+From: Florence Blanc-Renaud <flo@redhat.com>
+Date: Mon, 25 Mar 2019 14:22:59 +0100
+Subject: [PATCH] ipa-replica-manage: fix force-sync
+
+ipa-replica-manage force-sync --from <server> is performing a wrong check
+that may result in the tool looping on "No status yet".
+
+force-sync is adding a nsds5replicaupdateschedule attribute to the
+replication agreement in order to force replication to wake up. Note that
+this is not a re-initialization (re init drops the current db and reloads
+the entire db).
+
+In a second step, force-sync is checking the replication agreement by reading
+nsds5BeginReplicaRefresh, nsds5ReplicaLastInitStatus,
+nsds5ReplicaLastInitStart and nsds5ReplicaLastInitEnd. This is a wrong
+test as force-sync is not an init operation and does not touch these
+attributes.
+
+The tool should call wait_for_repl_update rather than wait_for_repl_init.
+This way, the check is done on the replication agreement attributes
+nsds5replicaUpdateInProgress, nsds5ReplicaLastUpdateStatus,
+nsds5ReplicaLastUpdateStart and nsds5ReplicaLastUpdateEnd.
+
+Fixes: https://pagure.io/freeipa/issue/7886
+---
+ install/tools/ipa-replica-manage | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage
+index cffed58b79f3e2cc41f8cf45b066f3843da01915..2a875b8fa58390c704d36b7d355ac79181021c35 100755
+--- a/install/tools/ipa-replica-manage
++++ b/install/tools/ipa-replica-manage
+@@ -1246,7 +1246,7 @@ def force_sync(realm, thishost, fromhost, dirman_passwd, nolookup=False):
+         repl = replication.ReplicationManager(realm, fromhost, dirman_passwd)
+         repl.force_sync(repl.conn, thishost)
+         agreement = repl.get_replication_agreement(thishost)
+-        repl.wait_for_repl_init(repl.conn, agreement.dn)
++        repl.wait_for_repl_update(repl.conn, agreement.dn)
+         ds.replica_manage_time_skew(prevent=True)
+ 
+ def show_DNA_ranges(hostname, master, realm, dirman_passwd, nextrange=False,
+-- 
+2.20.1
+
diff --git a/SOURCES/0004-Always-set-ca_host-when-installing-replica.patch b/SOURCES/0004-Always-set-ca_host-when-installing-replica.patch
deleted file mode 100644
index 2023e60..0000000
--- a/SOURCES/0004-Always-set-ca_host-when-installing-replica.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 5a5b232b721a68e37de2e25f134b8e585ad71393 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Tue, 19 Jun 2018 19:10:27 +0200
-Subject: [PATCH] Always set ca_host when installing replica
-
-ipa-replica-install only set ca_host in its temporary
-/etc/ipa/default.conf, when it wasn't installing a replica with CA. As a
-consequence, the replica installer was picking a random CA server from
-LDAP.
-
-Always set the replication peer as ca_host. This will ensure that the
-installer uses the same replication peer for CA. In case the replication
-peer is not a CA master, the installer will automatically pick another
-host later.
-
-See: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
----
- ipaserver/install/server/replicainstall.py | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index e2a37bc4c8305c525f224f2fb80cb2629e8ece24..33f3ae9e616b34a3ab0ff8e4257552855e817e7c 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -241,11 +241,9 @@ def create_ipa_conf(fstore, config, ca_enabled, master=None):
-         gopts.extend([
-             ipaconf.setOption('enable_ra', 'True'),
-             ipaconf.setOption('ra_plugin', 'dogtag'),
--            ipaconf.setOption('dogtag_version', '10')
-+            ipaconf.setOption('dogtag_version', '10'),
-+            ipaconf.setOption('ca_host', config.ca_host_name)
-         ])
--
--        if not config.setup_ca:
--            gopts.append(ipaconf.setOption('ca_host', config.ca_host_name))
-     else:
-         gopts.extend([
-             ipaconf.setOption('enable_ra', 'False'),
--- 
-2.14.4
-
diff --git a/SOURCES/0004-Unify-and-simplify-LDAP-service-discovery.patch b/SOURCES/0004-Unify-and-simplify-LDAP-service-discovery.patch
new file mode 100644
index 0000000..a90fffc
--- /dev/null
+++ b/SOURCES/0004-Unify-and-simplify-LDAP-service-discovery.patch
@@ -0,0 +1,660 @@
+From 04f68b7354cf1268019b08885eeb3a6915f314ef Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Thu, 12 Jul 2018 14:37:18 +0200
+Subject: [PATCH] Unify and simplify LDAP service discovery
+
+Move LDAP service discovery and service definitions from
+ipaserver.install to ipaserver. Simplify and unify different
+implementations in favor of a single implementation.
+
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+---
+ install/tools/ipa-ca-install                |   9 +-
+ install/tools/ipactl                        |   4 +-
+ ipaserver/install/cainstance.py             |   3 +-
+ ipaserver/install/ipa_kra_install.py        |  11 +-
+ ipaserver/install/opendnssecinstance.py     |   3 +-
+ ipaserver/install/server/replicainstall.py  |  18 +--
+ ipaserver/install/service.py                |  65 +----------
+ ipaserver/masters.py                        | 123 ++++++++++++++++++++
+ ipaserver/plugins/cert.py                   |  10 +-
+ ipaserver/plugins/dogtag.py                 |  99 ++++------------
+ ipaserver/servroles.py                      |   9 +-
+ ipatests/test_ipaserver/test_serverroles.py |   3 +-
+ 12 files changed, 192 insertions(+), 165 deletions(-)
+ create mode 100644 ipaserver/masters.py
+
+diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
+index f78f43d94981d29939a247f3c492c5e7340298ea..dcdbe884f15b13b92ec68a11d9f00e3e28771b42 100755
+--- a/install/tools/ipa-ca-install
++++ b/install/tools/ipa-ca-install
+@@ -34,6 +34,7 @@ from ipaserver.install.installutils import check_creds, ReplicaConfig
+ from ipaserver.install import dsinstance, ca
+ from ipaserver.install import cainstance, service
+ from ipaserver.install import custodiainstance
++from ipaserver.masters import find_providing_server
+ from ipapython import version
+ from ipalib import api
+ from ipalib.constants import DOMAIN_LEVEL_0
+@@ -211,8 +212,9 @@ def install_replica(safe_options, options, filename):
+         config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
+ 
+     if config.ca_host_name is None:
+-        config.ca_host_name = \
+-            service.find_providing_server('CA', api.Backend.ldap2, api.env.ca_host)
++        config.ca_host_name = find_providing_server(
++            'CA', api.Backend.ldap2, [api.env.ca_host]
++        )
+ 
+     options.realm_name = config.realm_name
+     options.domain_name = config.domain_name
+@@ -299,7 +301,8 @@ def promote(safe_options, options, filename):
+             paths.KRB5_KEYTAB,
+             ccache)
+ 
+-        ca_host = service.find_providing_server('CA', api.Backend.ldap2)
++        ca_host = find_providing_server('CA', api.Backend.ldap2)
++
+         if ca_host is None:
+             install_master(safe_options, options)
+         else:
+diff --git a/install/tools/ipactl b/install/tools/ipactl
+index ade91f7f75dab4fdf3b7fa73d2624a7395bc2a53..2767a26d1b70337d37dbcd87c707919579fe7e29 100755
+--- a/install/tools/ipactl
++++ b/install/tools/ipactl
+@@ -224,9 +224,9 @@ def get_config(dirsrv):
+         svc_list.append([order, name])
+ 
+     ordered_list = []
+-    for (order, svc) in sorted(svc_list):
++    for order, svc in sorted(svc_list):
+         if svc in service.SERVICE_LIST:
+-            ordered_list.append(service.SERVICE_LIST[svc][0])
++            ordered_list.append(service.SERVICE_LIST[svc].systemd_name)
+     return ordered_list
+ 
+ def get_config_from_file():
+diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
+index e101087ac2d738eb95cc643bfb12faaf5e65f0be..f424e7cd76d24a5a633a4f4babf3e112537be92c 100644
+--- a/ipaserver/install/cainstance.py
++++ b/ipaserver/install/cainstance.py
+@@ -71,6 +71,7 @@ from ipaserver.install import replication
+ from ipaserver.install import sysupgrade
+ from ipaserver.install.dogtaginstance import DogtagInstance
+ from ipaserver.plugins import ldap2
++from ipaserver.masters import ENABLED_SERVICE
+ 
+ logger = logging.getLogger(__name__)
+ 
+@@ -1304,7 +1305,7 @@ class CAInstance(DogtagInstance):
+             config = ['caRenewalMaster']
+         else:
+             config = []
+-        self._ldap_enable(u'enabledService', "CA", self.fqdn, basedn, config)
++        self._ldap_enable(ENABLED_SERVICE, "CA", self.fqdn, basedn, config)
+ 
+     def setup_lightweight_ca_key_retrieval(self):
+         if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'):
+diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
+index b536685f5f1f3fccab07fd37aa001958e2d38420..19260ac7f23a7c6f3a6328d4f146510a186b706e 100644
+--- a/ipaserver/install/ipa_kra_install.py
++++ b/ipaserver/install/ipa_kra_install.py
+@@ -41,6 +41,7 @@ from ipaserver.install.installutils import create_replica_config
+ from ipaserver.install import dogtaginstance
+ from ipaserver.install import kra
+ from ipaserver.install.installutils import ReplicaConfig
++from ipaserver.masters import find_providing_server
+ 
+ logger = logging.getLogger(__name__)
+ 
+@@ -206,8 +207,14 @@ class KRAInstaller(KRAInstall):
+                 config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
+ 
+             if config.kra_host_name is None:
+-                config.kra_host_name = service.find_providing_server(
+-                    'KRA', api.Backend.ldap2, api.env.ca_host)
++                config.kra_host_name = find_providing_server(
++                    'KRA', api.Backend.ldap2, [api.env.ca_host]
++                )
++                if config.kra_host_name is None:
++                    # all CA/KRA servers are down or unreachable.
++                    raise admintool.ScriptError(
++                        "Failed to find an active KRA server!"
++                    )
+             custodia = custodiainstance.get_custodia_instance(
+                 config, custodiainstance.CustodiaModes.KRA_PEER)
+         else:
+diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
+index 0337bb22fea44f95ee9077423136353a991325db..d6725dff11599a8755a29b6707dc7b451258629f 100644
+--- a/ipaserver/install/opendnssecinstance.py
++++ b/ipaserver/install/opendnssecinstance.py
+@@ -15,6 +15,7 @@ from subprocess import CalledProcessError
+ from ipalib.install import sysrestore
+ from ipaserver.install import service
+ from ipaserver.install import installutils
++from ipaserver.masters import ENABLED_SERVICE
+ from ipapython.dn import DN
+ from ipapython import ipautil
+ from ipaplatform import services
+@@ -45,7 +46,7 @@ def get_dnssec_key_masters(conn):
+     filter_attrs = {
+         u'cn': u'DNSSEC',
+         u'objectclass': u'ipaConfigObject',
+-        u'ipaConfigString': [KEYMASTER, u'enabledService'],
++        u'ipaConfigString': [KEYMASTER, ENABLED_SERVICE],
+     }
+     only_masters_f = conn.make_filter(filter_attrs, rules=conn.MATCH_ALL)
+ 
+diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
+index b221e1291f973e7255263a39cfd680af1321598d..37ecbe4146fa908c30fb708037fcaa47af1a258b 100644
+--- a/ipaserver/install/server/replicainstall.py
++++ b/ipaserver/install/server/replicainstall.py
+@@ -46,6 +46,7 @@ from ipaserver.install.installutils import (
+     validate_mask)
+ from ipaserver.install.replication import (
+     ReplicationManager, replica_conn_check)
++from ipaserver.masters import find_providing_servers, find_providing_server
+ import SSSDConfig
+ from subprocess import CalledProcessError
+ 
+@@ -1257,9 +1258,10 @@ def promote_check(installer):
+         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)
++        # 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
+@@ -1280,14 +1282,16 @@ def promote_check(installer):
+                              "custom certificates.")
+                 raise ScriptError(rval=3)
+ 
+-        kra_host = service.find_providing_server(
+-                'KRA', conn, config.kra_host_name)
++        # 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 KRA server in the domain, "
++                logger.error("There is no active KRA server in the domain, "
+                              "can't setup a KRA clone")
+                 raise ScriptError(rval=3)
+             kra_enabled = False
+@@ -1577,7 +1581,7 @@ def install(installer):
+     # Enable configured services and update DNS SRV records
+     service.enable_services(config.host_name)
+     api.Command.dns_update_system_records()
+-    ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api)
++    ca_servers = find_providing_servers('CA', api.Backend.ldap2, api=api)
+     api.Backend.ldap2.disconnect()
+ 
+     # Everything installed properly, activate ipa service.
+diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
+index baefc0076990e67e56dfa68da48b56f4cd55849f..a030801175491f65dc83aa9d42afdb1dfdb65b0f 100644
+--- a/ipaserver/install/service.py
++++ b/ipaserver/install/service.py
+@@ -38,34 +38,15 @@ from ipapython import kerberos
+ from ipalib import api, errors
+ from ipaplatform import services
+ from ipaplatform.paths import paths
++from ipaserver.masters import (
++    CONFIGURED_SERVICE, ENABLED_SERVICE, SERVICE_LIST
++)
+ 
+ logger = logging.getLogger(__name__)
+ 
+ if six.PY3:
+     unicode = str
+ 
+-# The service name as stored in cn=masters,cn=ipa,cn=etc. In the tuple
+-# the first value is the *nix service name, the second the start order.
+-SERVICE_LIST = {
+-    'KDC': ('krb5kdc', 10),
+-    'KPASSWD': ('kadmin', 20),
+-    'DNS': ('named', 30),
+-    'HTTP': ('httpd', 40),
+-    'KEYS': ('ipa-custodia', 41),
+-    'NTP': ('ntpd', 45),
+-    'CA': ('pki-tomcatd', 50),
+-    'KRA': ('pki-tomcatd', 51),
+-    'ADTRUST': ('smb', 60),
+-    'EXTID': ('winbind', 70),
+-    'OTPD': ('ipa-otpd', 80),
+-    'DNSKeyExporter': ('ipa-ods-exporter', 90),
+-    'DNSSEC': ('ods-enforcerd', 100),
+-    'DNSKeySync': ('ipa-dnskeysyncd', 110),
+-}
+-
+-CONFIGURED_SERVICE = u'configuredService'
+-ENABLED_SERVICE = u'enabledService'
+-
+ 
+ def print_msg(message, output_fd=sys.stdout):
+     logger.debug("%s", message)
+@@ -117,44 +98,6 @@ def add_principals_to_group(admin_conn, group, member_attr, principals):
+         pass
+ 
+ 
+-def find_providing_servers(svcname, conn, api):
+-    """
+-    Find servers that provide the given service.
+-
+-    :param svcname: The service to find
+-    :param conn: a connection to the LDAP server
+-    :return: list of host names (possibly empty)
+-
+-    """
+-    dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+-    query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
+-                                     'ipaConfigString': ENABLED_SERVICE,
+-                                     'cn': svcname}, rules='&')
+-    try:
+-        entries, _trunc = conn.find_entries(filter=query_filter, base_dn=dn)
+-    except errors.NotFound:
+-        return []
+-    else:
+-        return [entry.dn[1].value for entry in entries]
+-
+-
+-def find_providing_server(svcname, conn, host_name=None, api=api):
+-    """
+-    Find a server that provides the given service.
+-
+-    :param svcname: The service to find
+-    :param conn: a connection to the LDAP server
+-    :param host_name: the preferred server
+-    :return: the selected host name
+-
+-    """
+-    servers = find_providing_servers(svcname, conn, api)
+-    if len(servers) == 0:
+-        return None
+-    if host_name in servers:
+-        return host_name
+-    return servers[0]
+-
+ 
+ def case_insensitive_attr_has_value(attr, value):
+     """
+@@ -618,7 +561,7 @@ class Service(object):
+ 
+     def _ldap_enable(self, value, name, fqdn, ldap_suffix, config):
+         extra_config_opts = [
+-            ' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])])
++            u'startOrder {}'.format(SERVICE_LIST[name].startorder),
+         ]
+         extra_config_opts.extend(config)
+ 
+diff --git a/ipaserver/masters.py b/ipaserver/masters.py
+new file mode 100644
+index 0000000000000000000000000000000000000000..171c3abe0d6eea5aa6bcc642815eceae3ae885e7
+--- /dev/null
++++ b/ipaserver/masters.py
+@@ -0,0 +1,123 @@
++#
++# Copyright (C) 2018  FreeIPA Contributors see COPYING for license
++#
++"""Helpers services in for cn=masters,cn=ipa,cn=etc
++"""
++
++from __future__ import absolute_import
++
++import collections
++import logging
++import random
++
++from ipapython.dn import DN
++from ipalib import api
++from ipalib import errors
++
++logger = logging.getLogger(__name__)
++
++# constants for ipaConfigString
++CONFIGURED_SERVICE = u'configuredService'
++ENABLED_SERVICE = u'enabledService'
++
++# The service name as stored in cn=masters,cn=ipa,cn=etc. The values are:
++# 0: systemd service name
++# 1: start order for system service
++# 2: LDAP server entry CN, also used as SERVICE_LIST key
++service_definition = collections.namedtuple(
++    "service_definition",
++    "systemd_name startorder service_entry"
++)
++
++SERVICES = [
++    service_definition('krb5kdc', 10, 'KDC'),
++    service_definition('kadmin', 20, 'KPASSWD'),
++    service_definition('named', 30, 'DNS'),
++    service_definition('httpd', 40, 'HTTP'),
++    service_definition('ipa-custodia', 41, 'KEYS'),
++    service_definition('ntpd', 45, 'NTP'),
++    service_definition('pki-tomcatd', 50, 'CA'),
++    service_definition('pki-tomcatd', 51, 'KRA'),
++    service_definition('smb', 60, 'ADTRUST'),
++    service_definition('winbind', 70, 'EXTID'),
++    service_definition('ipa-otpd', 80, 'OTPD'),
++    service_definition('ipa-ods-exporter', 90, 'DNSKeyExporter'),
++    service_definition('ods-enforcerd', 100, 'DNSSEC'),
++    service_definition('ipa-dnskeysyncd', 110, 'DNSKeySync'),
++]
++
++SERVICE_LIST = {s.service_entry: s for s in SERVICES}
++
++
++def find_providing_servers(svcname, conn=None, preferred_hosts=(), api=api):
++    """Find servers that provide the given service.
++
++    :param svcname: The service to find
++    :param preferred_hosts: preferred servers
++    :param conn: a connection to the LDAP server
++    :param api: ipalib.API instance
++    :return: list of host names in randomized order (possibly empty)
++
++    Preferred servers are moved to the front of the list if and only if they
++    are found as providing servers.
++    """
++    assert isinstance(preferred_hosts, (tuple, list))
++    if svcname not in SERVICE_LIST:
++        raise ValueError("Unknown service '{}'.".format(svcname))
++    if conn is None:
++        conn = api.Backend.ldap2
++
++    dn = DN(api.env.container_masters, api.env.basedn)
++    query_filter = conn.make_filter(
++        {
++            'objectClass': 'ipaConfigObject',
++            'ipaConfigString': ENABLED_SERVICE,
++            'cn': svcname
++        },
++        rules='&'
++    )
++    try:
++        entries, _trunc = conn.find_entries(
++            filter=query_filter,
++            attrs_list=[],
++            base_dn=dn
++        )
++    except errors.NotFound:
++        return []
++
++    # unique list of host names, DNS is case insensitive
++    servers = list(set(entry.dn[1].value.lower() for entry in entries))
++    # shuffle the list like DNS SRV would randomize it
++    random.shuffle(servers)
++    # Move preferred hosts to front
++    for host_name in reversed(preferred_hosts):
++        host_name = host_name.lower()
++        try:
++            servers.remove(host_name)
++        except ValueError:
++            # preferred server not found, log and ignore
++            logger.warning(
++                "Lookup failed: Preferred host %s does not provide %s.",
++                host_name, svcname
++            )
++        else:
++            servers.insert(0, host_name)
++    return servers
++
++
++def find_providing_server(svcname, conn=None, preferred_hosts=(), api=api):
++    """Find a server that provides the given service.
++
++    :param svcname: The service to find
++    :param conn: a connection to the LDAP server
++    :param host_name: the preferred server
++    :param api: ipalib.API instance
++    :return: the selected host name or None
++    """
++    servers = find_providing_servers(
++        svcname, conn=conn, preferred_hosts=preferred_hosts, api=api
++    )
++    if not servers:
++        return None
++    else:
++        return servers[0]
+diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
+index da77f507cb2307eeec63acd0ab9d58c985ea8fe2..ff750e9d38ff98e0e1fa1c2eee5a3d0719da94bf 100644
+--- a/ipaserver/plugins/cert.py
++++ b/ipaserver/plugins/cert.py
+@@ -55,6 +55,7 @@ from ipalib import output
+ from ipapython import dnsutil, kerberos
+ from ipapython.dn import DN
+ from ipaserver.plugins.service import normalize_principal, validate_realm
++from ipaserver.masters import ENABLED_SERVICE, CONFIGURED_SERVICE
+ 
+ try:
+     import pyhbac
+@@ -297,19 +298,14 @@ def caacl_check(principal, ca, profile_id):
+ def ca_kdc_check(api_instance, hostname):
+     master_dn = api_instance.Object.server.get_dn(unicode(hostname))
+     kdc_dn = DN(('cn', 'KDC'), master_dn)
+-
++    wanted = {ENABLED_SERVICE, CONFIGURED_SERVICE}
+     try:
+         kdc_entry = api_instance.Backend.ldap2.get_entry(
+             kdc_dn, ['ipaConfigString'])
+-
+-        ipaconfigstring = {val.lower() for val in kdc_entry['ipaConfigString']}
+-
+-        if 'enabledservice' not in ipaconfigstring \
+-                and 'configuredservice' not in ipaconfigstring:
++        if not wanted.intersection(kdc_entry['ipaConfigString']):
+             raise errors.NotFound(
+                 reason=_("enabledService/configuredService not in "
+                          "ipaConfigString kdc entry"))
+-
+     except errors.NotFound:
+         raise errors.ACIError(
+             info=_("Host '%(hostname)s' is not an active KDC")
+diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
+index 5a44f00c583cd4bae8211a511f907c4e179bb3f6..17e2225688c0c3878ef22e5979c65e30f971b5b3 100644
+--- a/ipaserver/plugins/dogtag.py
++++ b/ipaserver/plugins/dogtag.py
+@@ -255,6 +255,7 @@ from ipalib import Backend, api
+ from ipapython.dn import DN
+ import ipapython.cookie
+ from ipapython import dogtag, ipautil, certdb
++from ipaserver.masters import find_providing_server
+ 
+ if api.env.in_server:
+     import pki
+@@ -1208,56 +1209,6 @@ def parse_updateCRL_xml(doc):
+     return response
+ 
+ 
+-def host_has_service(host, ldap2, service='CA'):
+-    """
+-    :param host: A host which might be a master for a service.
+-    :param ldap2: connection to the local database
+-    :param service: The service for which the host might be a master.
+-    :return:   (true, false)
+-
+-    Check if a specified host is a master for a specified service.
+-    """
+-    base_dn = DN(('cn', host), ('cn', 'masters'), ('cn', 'ipa'),
+-                 ('cn', 'etc'), api.env.basedn)
+-    filter_attrs = {
+-        'objectClass': 'ipaConfigObject',
+-        'cn': service,
+-        'ipaConfigString': 'enabledService',
+-        }
+-    query_filter = ldap2.make_filter(filter_attrs, rules='&')
+-    try:
+-        ent, _trunc = ldap2.find_entries(filter=query_filter, base_dn=base_dn)
+-        if len(ent):
+-            return True
+-    except Exception:
+-        pass
+-    return False
+-
+-
+-def select_any_master(ldap2, service='CA'):
+-    """
+-    :param ldap2: connection to the local database
+-    :param service: The service for which we're looking for a master.
+-    :return:   host as str
+-
+-    Select any host which is a master for a specified service.
+-    """
+-    base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+-                  api.env.basedn)
+-    filter_attrs = {
+-         'objectClass': 'ipaConfigObject',
+-         'cn': service,
+-         'ipaConfigString': 'enabledService',}
+-    query_filter = ldap2.make_filter(filter_attrs, rules='&')
+-    try:
+-        ent, _trunc = ldap2.find_entries(filter=query_filter, base_dn=base_dn)
+-        if len(ent):
+-            entry = random.choice(ent)
+-            return entry.dn[1].value
+-    except Exception:
+-        pass
+-    return None
+-
+ #-------------------------------------------------------------------------------
+ 
+ from ipalib import Registry, errors, SkipPluginModule
+@@ -1265,7 +1216,6 @@ if api.env.ra_plugin != 'dogtag':
+     # In this case, abort loading this plugin module...
+     raise SkipPluginModule(reason='dogtag not selected as RA plugin')
+ import os
+-import random
+ from ipaserver.plugins import rabase
+ from ipalib.constants import TYPE_ERROR
+ from ipalib import _
+@@ -1330,17 +1280,19 @@ class RestClient(Backend):
+         if self._ca_host is not None:
+             return self._ca_host
+ 
+-        ldap2 = self.api.Backend.ldap2
+-        if host_has_service(api.env.ca_host, ldap2, "CA"):
+-            object.__setattr__(self, '_ca_host', api.env.ca_host)
+-        elif api.env.host != api.env.ca_host:
+-            if host_has_service(api.env.host, ldap2, "CA"):
+-                object.__setattr__(self, '_ca_host', api.env.host)
+-        else:
+-            object.__setattr__(self, '_ca_host', select_any_master(ldap2))
+-        if self._ca_host is None:
+-            object.__setattr__(self, '_ca_host', api.env.ca_host)
+-        return self._ca_host
++        preferred = [api.env.ca_host]
++        if api.env.host != api.env.ca_host:
++            preferred.append(api.env.host)
++        ca_host = find_providing_server(
++            'CA', conn=self.api.Backend.ldap2, preferred_hosts=preferred,
++            api=self.api
++        )
++        if ca_host is None:
++            # TODO: need during installation, CA is not yet set as enabled
++            ca_host = api.env.ca_host
++        # object is locked, need to use __setattr__()
++        object.__setattr__(self, '_ca_host', ca_host)
++        return ca_host
+ 
+     def __enter__(self):
+         """Log into the REST API"""
+@@ -2082,9 +2034,7 @@ class kra(Backend):
+     """
+ 
+     def __init__(self, api, kra_port=443):
+-
+         self.kra_port = kra_port
+-
+         super(kra, self).__init__(api)
+ 
+     @property
+@@ -2095,17 +2045,18 @@ class kra(Backend):
+ 
+         Select our KRA host.
+         """
+-        ldap2 = self.api.Backend.ldap2
+-        if host_has_service(api.env.ca_host, ldap2, "KRA"):
+-            return api.env.ca_host
++        preferred = [api.env.ca_host]
+         if api.env.host != api.env.ca_host:
+-            if host_has_service(api.env.host, ldap2, "KRA"):
+-                return api.env.host
+-        host = select_any_master(ldap2, "KRA")
+-        if host:
+-            return host
+-        else:
+-            return api.env.ca_host
++            preferred.append(api.env.host)
++
++        kra_host = find_providing_server(
++            'KRA', self.api.Backend.ldap2, preferred_hosts=preferred,
++            api=self.api
++        )
++        if kra_host is None:
++            # TODO: need during installation, KRA is not yet set as enabled
++            kra_host = api.env.ca_host
++        return kra_host
+ 
+     @contextlib.contextmanager
+     def get_client(self):
+diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
+index 994a59e35bb5d3a007e3d63bb273ba9ea552407d..af4e63710136a15e1673210c3e2207658698fbb5 100644
+--- a/ipaserver/servroles.py
++++ b/ipaserver/servroles.py
+@@ -79,7 +79,7 @@ import six
+ 
+ from ipalib import _, errors
+ from ipapython.dn import DN
+-
++from ipaserver.masters import ENABLED_SERVICE
+ 
+ if six.PY3:
+     unicode = str
+@@ -483,11 +483,8 @@ class ServiceBasedRole(BaseServerRole):
+         :param entry: LDAPEntry of the service
+         :returns: True if the service entry is enabled, False otherwise
+         """
+-        enabled_value = 'enabledservice'
+-        ipaconfigstring_values = set(
+-            e.lower() for e in entry.get('ipaConfigString', []))
+-
+-        return enabled_value in ipaconfigstring_values
++        ipaconfigstring_values = set(entry.get('ipaConfigString', []))
++        return ENABLED_SERVICE in ipaconfigstring_values
+ 
+     def _get_services_by_masters(self, entries):
+         """
+diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py
+index 76f1378ed5a94069cdef918d577e3658b78ecc43..e1bf0254dddfe03ade4fe72419b609de8e95b79a 100644
+--- a/ipatests/test_ipaserver/test_serverroles.py
++++ b/ipatests/test_ipaserver/test_serverroles.py
+@@ -16,6 +16,7 @@ import pytest
+ from ipaplatform.paths import paths
+ from ipalib import api, create_api, errors
+ from ipapython.dn import DN
++from ipaserver.masters import ENABLED_SERVICE
+ 
+ pytestmark = pytest.mark.needs_ipaapi
+ 
+@@ -25,7 +26,7 @@ def _make_service_entry(ldap_backend, dn, enabled=True, other_config=None):
+         'objectClass': ['top', 'nsContainer', 'ipaConfigObject'],
+     }
+     if enabled:
+-        mods.update({'ipaConfigString': ['enabledService']})
++        mods.update({'ipaConfigString': [ENABLED_SERVICE]})
+ 
+     if other_config is not None:
+         mods.setdefault('ipaConfigString', [])
+-- 
+2.20.1
+
diff --git a/SOURCES/0005-Use-api.env.container_masters.patch b/SOURCES/0005-Use-api.env.container_masters.patch
new file mode 100644
index 0000000..97a8b44
--- /dev/null
+++ b/SOURCES/0005-Use-api.env.container_masters.patch
@@ -0,0 +1,252 @@
+From 3283ba88cdd7821a430132dec23a788ea4241f76 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Wed, 27 Mar 2019 11:03:00 +0100
+Subject: [PATCH] Use api.env.container_masters
+
+Replace occurences of ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc')
+with api.env.container_masters.
+
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+---
+ ipaserver/install/bindinstance.py              |  3 +--
+ ipaserver/install/cainstance.py                |  7 +++----
+ ipaserver/install/dns.py                       |  4 ++--
+ ipaserver/install/ipa_backup.py                |  3 ++-
+ ipaserver/install/ipa_restore.py               |  3 ++-
+ ipaserver/install/krbinstance.py               |  6 +-----
+ ipaserver/install/plugins/ca_renewal_master.py |  3 +--
+ ipaserver/install/replication.py               |  3 +--
+ ipaserver/install/server/upgrade.py            |  4 ++--
+ ipaserver/install/service.py                   | 11 +++++------
+ ipaserver/plugins/baseldap.py                  |  2 +-
+ ipaserver/plugins/domainlevel.py               | 13 +++----------
+ 12 files changed, 24 insertions(+), 38 deletions(-)
+
+diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
+index c175ca4f23b4f4440e1acaac2495276388daf3ae..6156ecdfbd1a62d5b1e0a26db47ef2b9a9448bc1 100644
+--- a/ipaserver/install/bindinstance.py
++++ b/ipaserver/install/bindinstance.py
+@@ -862,8 +862,7 @@ class BindInstance(service.Service):
+ 
+     def __add_others(self):
+         entries = api.Backend.ldap2.get_entries(
+-            DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+-               self.suffix),
++            DN(api.env.container_masters, self.suffix),
+             api.Backend.ldap2.SCOPE_ONELEVEL, None, ['dn'])
+ 
+         for entry in entries:
+diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
+index f424e7cd76d24a5a633a4f4babf3e112537be92c..2946b5cc2b4b8b708a060aa79d1b7ab0e7b4e651 100644
+--- a/ipaserver/install/cainstance.py
++++ b/ipaserver/install/cainstance.py
+@@ -1173,8 +1173,8 @@ class CAInstance(DogtagInstance):
+         if fqdn is None:
+             fqdn = api.env.host
+ 
+-        dn = DN(('cn', 'CA'), ('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'),
+-                ('cn', 'etc'), api.env.basedn)
++        dn = DN(('cn', 'CA'), ('cn', fqdn), api.env.container_masters,
++                api.env.basedn)
+         renewal_filter = '(ipaConfigString=caRenewalMaster)'
+         try:
+             api.Backend.ldap2.get_entries(base_dn=dn, filter=renewal_filter,
+@@ -1188,8 +1188,7 @@ class CAInstance(DogtagInstance):
+         if fqdn is None:
+             fqdn = api.env.host
+ 
+-        base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+-                     api.env.basedn)
++        base_dn = DN(api.env.container_masters, api.env.basedn)
+         filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'
+         try:
+             entries = api.Backend.ldap2.get_entries(
+diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
+index b17848a80c4300ed74aedc1e29a0dedbee79e6d9..930e038e4d7629563d2cea39fe581987dd0edfef 100644
+--- a/ipaserver/install/dns.py
++++ b/ipaserver/install/dns.py
+@@ -98,8 +98,8 @@ def _disable_dnssec():
+                                                api.env.basedn)
+ 
+     conn = api.Backend.ldap2
+-    dn = DN(('cn', 'DNSSEC'), ('cn', api.env.host), ('cn', 'masters'),
+-            ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
++    dn = DN(('cn', 'DNSSEC'), ('cn', api.env.host),
++            api.env.container_masters, api.env.basedn)
+     try:
+         entry = conn.get_entry(dn)
+     except errors.NotFound:
+diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
+index 789955a67dfc255285a2c82d9a8060495c3469e2..cef01d30454ea1adb8bf9c68f428b9555f1b9557 100644
+--- a/ipaserver/install/ipa_backup.py
++++ b/ipaserver/install/ipa_backup.py
+@@ -576,7 +576,8 @@ class Backup(admintool.AdminTool):
+         config.set('ipa', 'ipa_version', str(version.VERSION))
+         config.set('ipa', 'version', '1')
+ 
+-        dn = DN(('cn', api.env.host), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
++        dn = DN(('cn', api.env.host), api.env.container_masters,
++                api.env.basedn)
+         services_cns = []
+         try:
+             conn = self.get_connection()
+diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
+index 8b2f5bef7c9b1b8e2e2bae4e88850cf18b67b889..bd065a038db4d523048f0566f65458402d801e18 100644
+--- a/ipaserver/install/ipa_restore.py
++++ b/ipaserver/install/ipa_restore.py
+@@ -507,7 +507,8 @@ class Restore(admintool.AdminTool):
+                                 master, e)
+                 continue
+ 
+-            master_dn = DN(('cn', master), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
++            master_dn = DN(('cn', master), api.env.container_masters,
++                           api.env.basedn)
+             try:
+                 services = repl.conn.get_entries(master_dn,
+                                                  repl.conn.SCOPE_ONELEVEL)
+diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
+index aa9243dc69674a00f2e1bcdc3e71d44ae8862fbe..319eeb82bcbe61acd70b2943982b6fec6fa33f92 100644
+--- a/ipaserver/install/krbinstance.py
++++ b/ipaserver/install/krbinstance.py
+@@ -470,11 +470,7 @@ class KrbInstance(service.Service):
+         unadvertise enabled PKINIT feature in master's KDC entry in LDAP
+         """
+         ldap = api.Backend.ldap2
+-        dn = DN(('cn', 'KDC'),
+-                ('cn', self.fqdn),
+-                ('cn', 'masters'),
+-                ('cn', 'ipa'),
+-                ('cn', 'etc'),
++        dn = DN(('cn', 'KDC'), ('cn', self.fqdn), api.env.container_masters,
+                 self.suffix)
+ 
+         entry = ldap.get_entry(dn, ['ipaConfigString'])
+diff --git a/ipaserver/install/plugins/ca_renewal_master.py b/ipaserver/install/plugins/ca_renewal_master.py
+index 618f51244019c2a77a9d0a93437f95c037f1a728..259bd5a991d39adb9f30fe5b22e59c7eef09cfc6 100644
+--- a/ipaserver/install/plugins/ca_renewal_master.py
++++ b/ipaserver/install/plugins/ca_renewal_master.py
+@@ -46,8 +46,7 @@ class update_ca_renewal_master(Updater):
+             return False, []
+ 
+         ldap = self.api.Backend.ldap2
+-        base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+-                     self.api.env.basedn)
++        base_dn = DN(self.api.env.container_masters, self.api.env.basedn)
+         dn = DN(('cn', 'CA'), ('cn', self.api.env.host), base_dn)
+         filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'
+         try:
+diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
+index 70629b4528f033908c584bfaf0793cfa4ce259d4..8644b9ff618d28614a319d6da6a2041fea3c1c1f 100644
+--- a/ipaserver/install/replication.py
++++ b/ipaserver/install/replication.py
+@@ -1419,8 +1419,7 @@ class ReplicationManager(object):
+ 
+         # delete master entry with all active services
+         try:
+-            dn = DN(('cn', replica), ('cn', 'masters'), ('cn', 'ipa'),
+-                    ('cn', 'etc'), self.suffix)
++            dn = DN(('cn', replica), api.env.container_masters, self.suffix)
+             entries = self.conn.get_entries(dn, ldap.SCOPE_SUBTREE)
+             if entries:
+                 entries.sort(key=lambda x: len(x.dn), reverse=True)
+diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
+index 57c70ea9250bf6fcf027665304e02cc6def8e442..f4389d37909fc0b5aed960638de67243906b634d 100644
+--- a/ipaserver/install/server/upgrade.py
++++ b/ipaserver/install/server/upgrade.py
+@@ -1244,8 +1244,8 @@ def uninstall_dogtag_9(ds, http):
+         logger.debug('Dogtag is version 10 or above')
+         return
+ 
+-    dn = DN(('cn', 'CA'), ('cn', api.env.host), ('cn', 'masters'),
+-            ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
++    dn = DN(('cn', 'CA'), ('cn', api.env.host), api.env.container_masters,
++            api.env.basedn)
+     try:
+         api.Backend.ldap2.delete_entry(dn)
+     except ipalib.errors.PublicError as e:
+diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
+index a030801175491f65dc83aa9d42afdb1dfdb65b0f..261eedc85be24478b99e5ae8886aec7bc23a80ed 100644
+--- a/ipaserver/install/service.py
++++ b/ipaserver/install/service.py
+@@ -134,8 +134,7 @@ def set_service_entry_config(name, fqdn, config_values,
+     assert isinstance(ldap_suffix, DN)
+ 
+     entry_name = DN(
+-        ('cn', name), ('cn', fqdn), ('cn', 'masters'),
+-        ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix)
++        ('cn', name), ('cn', fqdn), api.env.container_masters, ldap_suffix)
+ 
+     # enable disabled service
+     try:
+@@ -577,8 +576,8 @@ class Service(object):
+     def ldap_disable(self, name, fqdn, ldap_suffix):
+         assert isinstance(ldap_suffix, DN)
+ 
+-        entry_dn = DN(('cn', name), ('cn', fqdn), ('cn', 'masters'),
+-                        ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix)
++        entry_dn = DN(('cn', name), ('cn', fqdn), api.env.container_masters,
++                      ldap_suffix)
+         search_kw = {'ipaConfigString': ENABLED_SERVICE}
+         filter = api.Backend.ldap2.make_filter(search_kw)
+         try:
+@@ -611,8 +610,8 @@ class Service(object):
+         logger.debug("service %s startup entry disabled", name)
+ 
+     def ldap_remove_service_container(self, name, fqdn, ldap_suffix):
+-        entry_dn = DN(('cn', name), ('cn', fqdn), ('cn', 'masters'),
+-                        ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix)
++        entry_dn = DN(('cn', name), ('cn', fqdn),
++                      self.api.env.container_masters, ldap_suffix)
+         try:
+             api.Backend.ldap2.delete_entry(entry_dn)
+         except errors.NotFound:
+diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py
+index 08ddc6d10d6431f51296bca9ae28aca8fa8586b2..25449b5aec72cbdbfb57527aa834cc69291398d6 100644
+--- a/ipaserver/plugins/baseldap.py
++++ b/ipaserver/plugins/baseldap.py
+@@ -497,7 +497,7 @@ def host_is_master(ldap, fqdn):
+ 
+     Raises an exception if a master, otherwise returns nothing.
+     """
+-    master_dn = DN(('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
++    master_dn = DN(('cn', fqdn), api.env.container_masters, api.env.basedn)
+     try:
+         ldap.get_entry(master_dn, ['objectclass'])
+         raise errors.ValidationError(name='hostname', error=_('An IPA master host cannot be deleted or disabled'))
+diff --git a/ipaserver/plugins/domainlevel.py b/ipaserver/plugins/domainlevel.py
+index 306ca0a6d147b2c0dc7a91ee1aefc0e7a5c98048..0d36dc08c07612dc565417a66ab9c467eb7f0555 100644
+--- a/ipaserver/plugins/domainlevel.py
++++ b/ipaserver/plugins/domainlevel.py
+@@ -72,25 +72,18 @@ def check_conflict_entries(ldap, api, desired_value):
+     except errors.NotFound:
+         pass
+ 
++
+ def get_master_entries(ldap, api):
+     """
+     Returns list of LDAPEntries representing IPA masters.
+     """
+-
+-    container_masters = DN(
+-        ('cn', 'masters'),
+-        ('cn', 'ipa'),
+-        ('cn', 'etc'),
+-        api.env.basedn
+-    )
+-
++    dn = DN(api.env.container_masters, api.env.basedn)
+     masters, _dummy = ldap.find_entries(
+         filter="(cn=*)",
+-        base_dn=container_masters,
++        base_dn=dn,
+         scope=ldap.SCOPE_ONELEVEL,
+         paged_search=True,  # we need to make sure to get all of them
+     )
+-
+     return masters
+ 
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0005-ipaserver-config-plugin-Increase-search-records-mini.patch b/SOURCES/0005-ipaserver-config-plugin-Increase-search-records-mini.patch
deleted file mode 100644
index 0944ea8..0000000
--- a/SOURCES/0005-ipaserver-config-plugin-Increase-search-records-mini.patch
+++ /dev/null
@@ -1,139 +0,0 @@
-From 2461d69242108fe6f4bc067cc8255e41f66c58aa Mon Sep 17 00:00:00 2001
-From: Armando Neto <neto.armando@gmail.com>
-Date: Mon, 18 Jun 2018 18:26:01 -0300
-Subject: [PATCH] ipaserver config plugin: Increase search records minimum
- limit
-
-Check if the given search records value is greater than an arbitrary number that is not so close to zero.
-
-https://pagure.io/freeipa/issue/6617
-
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaserver/plugins/config.py                | 14 +++++-
- ipatests/test_xmlrpc/test_config_plugin.py | 76 ++++++++++++++++++++++++++++++
- 2 files changed, 89 insertions(+), 1 deletion(-)
-
-diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py
-index 33ed38ba016567b9df57503f2f8418cf7c7fc794..d367c3c5aa421bb22d1630c88bbac846e7d84386 100644
---- a/ipaserver/plugins/config.py
-+++ b/ipaserver/plugins/config.py
-@@ -85,6 +85,18 @@ EXAMPLES:
- 
- register = Registry()
- 
-+
-+def validate_search_records_limit(ugettext, value):
-+    """Check if value is greater than a realistic minimum.
-+
-+    Values 0 and -1 are valid, as they represent unlimited.
-+    """
-+    if value in {-1, 0}:
-+        return
-+    if value < 10:
-+        return _('must be at least 10')
-+
-+
- @register()
- class config(LDAPObject):
-     """
-@@ -161,10 +173,10 @@ class config(LDAPObject):
-             minvalue=-1,
-         ),
-         Int('ipasearchrecordslimit',
-+            validate_search_records_limit,
-             cli_name='searchrecordslimit',
-             label=_('Search size limit'),
-             doc=_('Maximum number of records to search (-1 or 0 is unlimited)'),
--            minvalue=-1,
-         ),
-         IA5Str('ipausersearchfields',
-             cli_name='usersearch',
-diff --git a/ipatests/test_xmlrpc/test_config_plugin.py b/ipatests/test_xmlrpc/test_config_plugin.py
-index c037224162e2c29f6dd76eabefe7fededc6f882d..666b7c2c87b4f0a1f7bde18c78780a1ea6072b71 100644
---- a/ipatests/test_xmlrpc/test_config_plugin.py
-+++ b/ipatests/test_xmlrpc/test_config_plugin.py
-@@ -211,4 +211,80 @@ class test_config(Declarative):
-                 summary=None,
-                 ),
-         ),
-+        dict(
-+            desc='Set the number of search records to -1 (unlimited)',
-+            command=(
-+                'config_mod', [], {
-+                    'ipasearchrecordslimit': u'-1',
-+                },
-+            ),
-+            expected={
-+                'result': lambda d: d['ipasearchrecordslimit'] == (u'-1',),
-+                'summary': None,
-+                'value': None,
-+            },
-+        ),
-+        dict(
-+            desc='Set the number of search records to greater than 10',
-+            command=(
-+                'config_mod', [], {
-+                    'ipasearchrecordslimit': u'100',
-+                },
-+            ),
-+            expected={
-+                'result': lambda d: d['ipasearchrecordslimit'] == (u'100',),
-+                'summary': None,
-+                'value': None,
-+            },
-+        ),
-+        dict(
-+            desc='Set the number of search records to lower than -1',
-+            command=(
-+                'config_mod', [], {
-+                    'ipasearchrecordslimit': u'-10',
-+                },
-+            ),
-+            expected=errors.ValidationError(
-+                name=u'searchrecordslimit',
-+                error=u'must be at least 10',
-+            ),
-+        ),
-+        dict(
-+            desc='Set the number of search records to lower than 10',
-+            command=(
-+                'config_mod', [], {
-+                    'ipasearchrecordslimit': u'1',
-+                },
-+            ),
-+            expected=errors.ValidationError(
-+                name=u'searchrecordslimit',
-+                error=u'must be at least 10',
-+            ),
-+        ),
-+        dict(
-+            desc='Set the number of search records to zero (unlimited)',
-+            command=(
-+                'config_mod', [], {
-+                    'ipasearchrecordslimit': u'0',
-+                },
-+            ),
-+            expected={
-+                'result': lambda d: d['ipasearchrecordslimit'] == (u'-1',),
-+                'summary': None,
-+                'value': None,
-+            },
-+        ),
-+        dict(
-+            desc='Set the number of search records back to 100',
-+            command=(
-+                'config_mod', [], {
-+                    'ipasearchrecordslimit': u'100',
-+                },
-+            ),
-+            expected={
-+                'result': lambda d: d['ipasearchrecordslimit'] == (u'100',),
-+                'summary': None,
-+                'value': None,
-+            },
-+        ),
-     ]
--- 
-2.14.4
-
diff --git a/SOURCES/0006-Consolidate-container_masters-queries.patch b/SOURCES/0006-Consolidate-container_masters-queries.patch
new file mode 100644
index 0000000..ad86b7f
--- /dev/null
+++ b/SOURCES/0006-Consolidate-container_masters-queries.patch
@@ -0,0 +1,316 @@
+From 406a70cb9e481fb194bbbc67461f3cfd3c97a108 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Wed, 27 Mar 2019 11:30:40 +0100
+Subject: [PATCH] Consolidate container_masters queries
+
+Replace manual queries of container_masters with new APIs get_masters()
+and is_service_enabled().
+
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+---
+ ipaserver/install/bindinstance.py |  8 ++---
+ ipaserver/install/ipa_restore.py  | 12 ++------
+ ipaserver/masters.py              | 51 +++++++++++++++++++++++++++++++
+ ipaserver/plugins/cert.py         | 15 +++------
+ ipaserver/plugins/dns.py          | 49 ++++-------------------------
+ ipaserver/plugins/user.py         | 15 ++-------
+ ipaserver/plugins/vault.py        | 12 ++------
+ 7 files changed, 70 insertions(+), 92 deletions(-)
+
+diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
+index 6156ecdfbd1a62d5b1e0a26db47ef2b9a9448bc1..940667db9c40559d5369188395bb18f2d1eac025 100644
+--- a/ipaserver/install/bindinstance.py
++++ b/ipaserver/install/bindinstance.py
+@@ -40,6 +40,7 @@ from ipaserver.dns_data_management import (
+ from ipaserver.install import installutils
+ from ipaserver.install import service
+ from ipaserver.install import sysupgrade
++from ipaserver.masters import get_masters
+ from ipapython import ipautil
+ from ipapython import dnsutil
+ from ipapython.dnsutil import DNSName
+@@ -1037,13 +1038,8 @@ class BindInstance(service.Service):
+             cname_fqdn[cname] = fqdn
+ 
+         # get FQDNs of all IPA masters
+-        ldap = self.api.Backend.ldap2
+         try:
+-            entries = ldap.get_entries(
+-                DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+-                   self.api.env.basedn),
+-                ldap.SCOPE_ONELEVEL, None, ['cn'])
+-            masters = set(e['cn'][0] for e in entries)
++            masters = set(get_masters(self.api.Backend.ldap2))
+         except errors.NotFound:
+             masters = set()
+ 
+diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
+index bd065a038db4d523048f0566f65458402d801e18..c7e996bbe284a7eb2d03fbedb4798d3b15f3dcc0 100644
+--- a/ipaserver/install/ipa_restore.py
++++ b/ipaserver/install/ipa_restore.py
+@@ -41,6 +41,7 @@ from ipaserver.install.replication import (wait_for_task, ReplicationManager,
+                                            get_cs_replication_manager)
+ from ipaserver.install import installutils
+ from ipaserver.install import dsinstance, httpinstance, cainstance, krbinstance
++from ipaserver.masters import get_masters
+ from ipapython import ipaldap
+ import ipapython.errors
+ from ipaplatform.constants import constants
+@@ -485,16 +486,7 @@ class Restore(admintool.AdminTool):
+             logger.error('Unable to get connection, skipping disabling '
+                          'agreements: %s', e)
+             return
+-        masters = []
+-        dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+-        try:
+-            entries = conn.get_entries(dn, conn.SCOPE_ONELEVEL)
+-        except Exception as e:
+-            raise admintool.ScriptError(
+-                "Failed to read master data: %s" % e)
+-        else:
+-            masters = [ent.single_value['cn'] for ent in entries]
+-
++        masters = get_masters(conn)
+         for master in masters:
+             if master == api.env.host:
+                 continue
+diff --git a/ipaserver/masters.py b/ipaserver/masters.py
+index 171c3abe0d6eea5aa6bcc642815eceae3ae885e7..6fa8f02332ceaa10ec30aa5142912f351fb58936 100644
+--- a/ipaserver/masters.py
++++ b/ipaserver/masters.py
+@@ -121,3 +121,54 @@ def find_providing_server(svcname, conn=None, preferred_hosts=(), api=api):
+         return None
+     else:
+         return servers[0]
++
++
++def get_masters(conn=None, api=api):
++    """Get all master hostnames
++
++    :param conn: a connection to the LDAP server
++    :param api: ipalib.API instance
++    :return: list of hostnames
++    """
++    if conn is None:
++        conn = api.Backend.ldap2
++
++    dn = DN(api.env.container_masters, api.env.basedn)
++    entries = conn.get_entries(dn, conn.SCOPE_ONELEVEL, None, ['cn'])
++    return list(e['cn'][0] for e in entries)
++
++
++def is_service_enabled(svcname, conn=None, api=api):
++    """Check if service is enabled on any master
++
++    The check function only looks for presence of service entries. It
++    ignores enabled/hidden flags.
++
++    :param svcname: The service to find
++    :param conn: a connection to the LDAP server
++    :param api: ipalib.API instance
++    :return: True/False
++    """
++    if svcname not in SERVICE_LIST:
++        raise ValueError("Unknown service '{}'.".format(svcname))
++    if conn is None:
++        conn = api.Backend.ldap2
++
++    dn = DN(api.env.container_masters, api.env.basedn)
++    query_filter = conn.make_filter(
++        {
++            'objectClass': 'ipaConfigObject',
++            'cn': svcname
++        },
++        rules='&'
++    )
++    try:
++        conn.find_entries(
++            filter=query_filter,
++            attrs_list=[],
++            base_dn=dn
++        )
++    except errors.NotFound:
++        return False
++    else:
++        return True
+diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
+index ff750e9d38ff98e0e1fa1c2eee5a3d0719da94bf..36a7a859292b2ddb9dd721e2bf786c6be130f323 100644
+--- a/ipaserver/plugins/cert.py
++++ b/ipaserver/plugins/cert.py
+@@ -55,7 +55,9 @@ from ipalib import output
+ from ipapython import dnsutil, kerberos
+ from ipapython.dn import DN
+ from ipaserver.plugins.service import normalize_principal, validate_realm
+-from ipaserver.masters import ENABLED_SERVICE, CONFIGURED_SERVICE
++from ipaserver.masters import (
++    ENABLED_SERVICE, CONFIGURED_SERVICE, is_service_enabled
++)
+ 
+ try:
+     import pyhbac
+@@ -1908,14 +1910,5 @@ class ca_is_enabled(Command):
+     has_output = output.standard_value
+ 
+     def execute(self, *args, **options):
+-        base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+-                     self.api.env.basedn)
+-        filter = '(&(objectClass=ipaConfigObject)(cn=CA))'
+-        try:
+-            self.api.Backend.ldap2.find_entries(
+-                base_dn=base_dn, filter=filter, attrs_list=[])
+-        except errors.NotFound:
+-            result = False
+-        else:
+-            result = True
++        result = is_service_enabled('CA', conn=self.api.Backend.ldap2)
+         return dict(result=result, value=pkey_to_value(None, options))
+diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py
+index 3c006c5701a426cc03971c9e3361dfd091770241..d6baa2fc1769701ac62b8c6d686fbed74ea3f002 100644
+--- a/ipaserver/plugins/dns.py
++++ b/ipaserver/plugins/dns.py
+@@ -86,6 +86,7 @@ from ipaserver.dns_data_management import (
+     IPASystemRecords,
+     IPADomainIsNotManagedByIPAError,
+ )
++from ipaserver.masters import find_providing_servers, is_service_enabled
+ 
+ if six.PY3:
+     unicode = str
+@@ -1593,19 +1594,7 @@ def dnssec_installed(ldap):
+     :param ldap: ldap connection
+     :return: True if DNSSEC was installed, otherwise False
+     """
+-    dn = DN(api.env.container_masters, api.env.basedn)
+-
+-    filter_attrs = {
+-        u'cn': u'DNSSEC',
+-        u'objectclass': u'ipaConfigObject',
+-    }
+-    only_masters_f = ldap.make_filter(filter_attrs, rules=ldap.MATCH_ALL)
+-
+-    try:
+-        ldap.find_entries(filter=only_masters_f, base_dn=dn)
+-    except errors.NotFound:
+-        return False
+-    return True
++    return is_service_enabled('DNSSEC', conn=ldap)
+ 
+ 
+ def default_zone_update_policy(zone):
+@@ -3191,24 +3180,9 @@ class dnsrecord(LDAPObject):
+         return cliname
+ 
+     def get_dns_masters(self):
+-        ldap = self.api.Backend.ldap2
+-        base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), self.api.env.basedn)
+-        ldap_filter = '(&(objectClass=ipaConfigObject)(cn=DNS))'
+-        dns_masters = []
+-
+-        try:
+-            entries = ldap.find_entries(filter=ldap_filter, base_dn=base_dn)[0]
+-
+-            for entry in entries:
+-                try:
+-                    master = entry.dn[1]['cn']
+-                    dns_masters.append(master)
+-                except (IndexError, KeyError):
+-                    pass
+-        except errors.NotFound:
+-            return []
+-
+-        return dns_masters
++        return find_providing_servers(
++            'DNS', self.api.Backend.ldap2, preferred_hosts=[api.env.host]
++        )
+ 
+     def get_record_entry_attrs(self, entry_attrs):
+         entry_attrs = entry_attrs.copy()
+@@ -4077,19 +4051,8 @@ class dns_is_enabled(Command):
+     NO_CLI = True
+     has_output = output.standard_value
+ 
+-    base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+-    filter = '(&(objectClass=ipaConfigObject)(cn=DNS))'
+-
+     def execute(self, *args, **options):
+-        ldap = self.api.Backend.ldap2
+-        dns_enabled = False
+-
+-        try:
+-            ldap.find_entries(filter=self.filter, base_dn=self.base_dn)
+-            dns_enabled = True
+-        except errors.EmptyResult:
+-            dns_enabled = False
+-
++        dns_enabled = is_service_enabled('DNS', conn=self.api.Backend.ldap2)
+         return dict(result=dns_enabled, value=pkey_to_value(None, options))
+ 
+ 
+diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py
+index 1aa19ab3c2763983b2216e332013b45df1a4a496..980385dc83e93ec4a65726077b34917e21115efa 100644
+--- a/ipaserver/plugins/user.py
++++ b/ipaserver/plugins/user.py
+@@ -69,6 +69,7 @@ from ipapython.dn import DN
+ from ipapython.ipaldap import LDAPClient
+ from ipapython.ipautil import ipa_generate_password, TMP_PWD_ENTROPY_BITS
+ from ipalib.capabilities import client_has_capability
++from ipaserver.masters import get_masters
+ 
+ if six.PY3:
+     unicode = str
+@@ -1105,21 +1106,11 @@ class user_status(LDAPQuery):
+         attr_list = ['krbloginfailedcount', 'krblastsuccessfulauth', 'krblastfailedauth', 'nsaccountlock']
+ 
+         disabled = False
+-        masters = []
+-        # Get list of masters
+-        try:
+-            masters, _truncated = ldap.find_entries(
+-                None, ['*'], DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn),
+-                ldap.SCOPE_ONELEVEL
+-            )
+-        except errors.NotFound:
+-            # If this happens we have some pretty serious problems
+-            logger.error('No IPA masters found!')
++        masters = get_masters(ldap)
+ 
+         entries = []
+         count = 0
+-        for master in masters:
+-            host = master['cn'][0]
++        for host in masters:
+             if host == api.env.host:
+                 other_ldap = self.obj.backend
+             else:
+diff --git a/ipaserver/plugins/vault.py b/ipaserver/plugins/vault.py
+index 682f6bea74df024bd28e15157c9b809888d08c38..a2603217cc3b79c6ec891b4deadeea91364eeeb9 100644
+--- a/ipaserver/plugins/vault.py
++++ b/ipaserver/plugins/vault.py
+@@ -34,6 +34,7 @@ from .service import normalize_principal, validate_realm
+ from ipalib import _, ngettext
+ from ipapython import kerberos
+ from ipapython.dn import DN
++from ipaserver.masters import is_service_enabled
+ 
+ if api.env.in_server:
+     import pki.account
+@@ -1220,14 +1221,5 @@ class kra_is_enabled(Command):
+     has_output = output.standard_value
+ 
+     def execute(self, *args, **options):
+-        base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+-                     self.api.env.basedn)
+-        filter = '(&(objectClass=ipaConfigObject)(cn=KRA))'
+-        try:
+-            self.api.Backend.ldap2.find_entries(
+-                base_dn=base_dn, filter=filter, attrs_list=[])
+-        except errors.NotFound:
+-            result = False
+-        else:
+-            result = True
++        result = is_service_enabled('KRA', conn=self.api.Backend.ldap2)
+         return dict(result=result, value=pkey_to_value(None, options))
+-- 
+2.20.1
+
diff --git a/SOURCES/0006-Improve-and-fix-timeout-bug-in-wait_for_entry.patch b/SOURCES/0006-Improve-and-fix-timeout-bug-in-wait_for_entry.patch
deleted file mode 100644
index dcff4e2..0000000
--- a/SOURCES/0006-Improve-and-fix-timeout-bug-in-wait_for_entry.patch
+++ /dev/null
@@ -1,107 +0,0 @@
-From b17a85bded822c4078a9b817720652807a939bc8 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 22 Jun 2018 09:39:26 +0200
-Subject: [PATCH] Improve and fix timeout bug in wait_for_entry()
-
-replication.wait_for_entry() now can wait for an attribute value to
-appear on a replica.
-
-Fixed timeout handling caused by bad rounding and comparison. For small
-timeouts, the actual time was rounded down. For example for 60 seconds
-timeout and fast replica, the query accumulated to about 0.45 seconds
-plus 60 seconds sleep. 60.45 is large enough to terminate the loop
-"while int(time.time()) < timeout", but not large enough to trigger the
-exception in "if int(time.time()) > timeout", because int(60.65) == 60.
-
-See: https://pagure.io/freeipa/issue/7593
-Fixes: https://pagure.io/freeipa/issue/7595
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
----
- ipaserver/install/replication.py | 54 +++++++++++++++++++++-------------------
- 1 file changed, 29 insertions(+), 25 deletions(-)
-
-diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
-index 2e7ce53fef89093a7f661d846aa138ee330961e6..6d9878e16faab1b88a5ab79649571de6802c8536 100644
---- a/ipaserver/install/replication.py
-+++ b/ipaserver/install/replication.py
-@@ -20,6 +20,7 @@
- from __future__ import print_function, absolute_import
- 
- import logging
-+import itertools
- 
- import six
- import time
-@@ -160,40 +161,43 @@ def wait_for_task(conn, dn):
-     return exit_code
- 
- 
--def wait_for_entry(connection, dn, timeout=7200, attr='', quiet=True):
--    """Wait for entry and/or attr to show up"""
--
--    filter = "(objectclass=*)"
-+def wait_for_entry(connection, dn, timeout=7200, attr=None, attrvalue='*',
-+                   quiet=True):
-+    """Wait for entry and/or attr to show up
-+    """
-+    log = logger.debug if quiet else logger.info
-     attrlist = []
--    if attr:
--        filter = "(%s=*)" % attr
-+    if attr is not None:
-+        filterstr = ipaldap.LDAPClient.make_filter_from_attr(attr, attrvalue)
-         attrlist.append(attr)
--    timeout += int(time.time())
--
--    if not quiet:
--        sys.stdout.write("Waiting for %s %s:%s " % (connection, dn, attr))
--        sys.stdout.flush()
--    entry = None
--    while not entry and int(time.time()) < timeout:
-+    else:
-+        filterstr = "(objectclass=*)"
-+    log("Waiting for replication (%s) %s %s", connection, dn, filterstr)
-+    entry = []
-+    deadline = time.time() + timeout
-+    for i in itertools.count(start=1):
-         try:
--            [entry] = connection.get_entries(
--                dn, ldap.SCOPE_BASE, filter, attrlist)
-+            entry = connection.get_entries(
-+                dn, ldap.SCOPE_BASE, filterstr, attrlist)
-         except errors.NotFound:
-             pass  # no entry yet
-         except Exception as e:  # badness
-             logger.error("Error reading entry %s: %s", dn, e)
-             raise
--        if not entry:
--            if not quiet:
--                sys.stdout.write(".")
--                sys.stdout.flush()
--            time.sleep(1)
- 
--    if not entry and int(time.time()) > timeout:
--        raise errors.NotFound(
--            reason="wait_for_entry timeout for %s for %s" % (connection, dn))
--    elif entry and not quiet:
--        logger.error("The waited for entry is: %s", entry)
-+        if entry:
-+            log("Entry found %r", entry)
-+            return
-+        elif time.time() > deadline:
-+            raise errors.NotFound(
-+                reason="wait_for_entry timeout on {} for {}".format(
-+                    connection, dn
-+                )
-+            )
-+        else:
-+            if i % 10 == 0:
-+                logger.debug("Still waiting for replication of %s", dn)
-+            time.sleep(1)
- 
- 
- class ReplicationManager(object):
--- 
-2.14.4
-
diff --git a/SOURCES/0007-Replace-hard-coded-paths-with-path-constants.patch b/SOURCES/0007-Replace-hard-coded-paths-with-path-constants.patch
new file mode 100644
index 0000000..c84eb02
--- /dev/null
+++ b/SOURCES/0007-Replace-hard-coded-paths-with-path-constants.patch
@@ -0,0 +1,570 @@
+From f4995135a97531819503632e72f9910101f5ce61 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Wed, 7 Feb 2018 17:18:07 +0100
+Subject: [PATCH] Replace hard-coded paths with path constants
+
+Several run() calls used hard-coded paths rather than pre-defined paths
+from ipaplatform.paths. The patch fixes all places that I was able to
+find with a simple search.
+
+The fix simplifies Darix's port of freeIPA on openSuSE.
+
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Rob Crittenden <rcritten@redhat.com>
+(cherry picked from commit 2391c75e3d7efcdc5c2f49defa5138fc7e6def06)
+
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ client/ipa-client-automount                   |  2 +-
+ install/tools/ipa-adtrust-install             |  2 +-
+ install/tools/ipa-ca-install                  |  2 +-
+ install/tools/ipa-dns-install                 |  2 +-
+ ipaclient/install/client.py                   | 10 ++++----
+ ipaplatform/base/paths.py                     |  8 +++++++
+ ipapython/kernel_keyring.py                   | 24 +++++++++++++------
+ ipaserver/install/adtrustinstance.py          |  6 +++--
+ ipaserver/install/installutils.py             | 17 +++++++++----
+ ipaserver/install/ipa_backup.py               |  6 ++---
+ ipaserver/install/ipa_restore.py              |  4 ++--
+ ipaserver/install/krbinstance.py              |  2 +-
+ ipatests/pytest_ipa/integration/__init__.py   |  3 ++-
+ ipatests/test_integration/test_caless.py      |  2 +-
+ ipatests/test_ipapython/test_ipautil.py       | 15 ++++++------
+ .../test_caacl_profile_enforcement.py         |  3 ++-
+ ipatests/test_xmlrpc/test_cert_plugin.py      |  2 +-
+ 17 files changed, 70 insertions(+), 40 deletions(-)
+
+diff --git a/client/ipa-client-automount b/client/ipa-client-automount
+index ee55d655c9531c8fb7baebd0e7a99f3db484f7db..6c2816c410642967e95a7b1eb60583600a7f5fb0 100755
+--- a/client/ipa-client-automount
++++ b/client/ipa-client-automount
+@@ -92,7 +92,7 @@ def wait_for_sssd():
+     time.sleep(1)
+     while n < 10 and not found:
+         try:
+-            ipautil.run(["getent", "passwd", "admin@%s" % api.env.realm])
++            ipautil.run([paths.GETENT, "passwd", "admin@%s" % api.env.realm])
+             found = True
+         except Exception:
+             time.sleep(1)
+diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
+index a870d136e242affe6627cd4c44a173a80a9ab1c6..9dbfadb6fae193e2f4a54b3a0e226e0a6b1fd26f 100755
+--- a/install/tools/ipa-adtrust-install
++++ b/install/tools/ipa-adtrust-install
+@@ -110,7 +110,7 @@ def read_admin_password(admin_name):
+ 
+ def ensure_admin_kinit(admin_name, admin_password):
+     try:
+-        ipautil.run(['kinit', admin_name], stdin=admin_password+'\n')
++        ipautil.run([paths.KINIT, admin_name], stdin=admin_password+'\n')
+     except ipautil.CalledProcessError:
+         print("There was error to automatically re-kinit your admin user "
+               "ticket.")
+diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
+index dcdbe884f15b13b92ec68a11d9f00e3e28771b42..55182dc30e4736618f749e78db161fc7eefe37ac 100755
+--- a/install/tools/ipa-ca-install
++++ b/install/tools/ipa-ca-install
+@@ -352,7 +352,7 @@ def main():
+     api.Backend.ldap2.disconnect()
+ 
+     # execute ipactl to refresh services status
+-    ipautil.run(['ipactl', 'start', '--ignore-service-failures'],
++    ipautil.run([paths.IPACTL, 'start', '--ignore-service-failures'],
+                 raiseonerr=False)
+ 
+ 
+diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
+index 32a17d223ae2bdd9a1ded62defcc272a40d2627b..0e76a5ab93bd37c2f0c9c5ea4894023588697782 100755
+--- a/install/tools/ipa-dns-install
++++ b/install/tools/ipa-dns-install
+@@ -151,7 +151,7 @@ def main():
+     # Services are enabled in dns_installer.install()
+ 
+     # execute ipactl to refresh services status
+-    ipautil.run(['ipactl', 'start', '--ignore-service-failures'],
++    ipautil.run([paths.IPACTL, 'start', '--ignore-service-failures'],
+                 raiseonerr=False)
+ 
+     api.Backend.ldap2.disconnect()
+diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
+index 80b572ce9b5a250c0c32a1d7fcd06ec53af32984..babebfc667c5a096fb2e0238de444ffa3ce62b77 100644
+--- a/ipaclient/install/client.py
++++ b/ipaclient/install/client.py
+@@ -1125,7 +1125,7 @@ def configure_sshd_config(fstore, options):
+         )
+ 
+         for candidate in candidates:
+-            args = ['sshd', '-t', '-f', os.devnull]
++            args = [paths.SSHD, '-t', '-f', os.devnull]
+             for item in candidate.items():
+                 args.append('-o')
+                 args.append('%s=%s' % item)
+@@ -1157,7 +1157,7 @@ def configure_automount(options):
+     logger.info('\nConfiguring automount:')
+ 
+     args = [
+-        'ipa-client-automount', '--debug', '-U', '--location',
++        paths.IPA_CLIENT_AUTOMOUNT, '--debug', '-U', '--location',
+         options.location
+     ]
+ 
+@@ -2615,7 +2615,7 @@ def _install(options):
+                 subject_base = DN(subject_base)
+ 
+             if options.principal is not None:
+-                run(["kdestroy"], raiseonerr=False, env=env)
++                run([paths.KDESTROY], raiseonerr=False, env=env)
+ 
+             # Obtain the TGT. We do it with the temporary krb5.conf, so that
+             # only the KDC we're installing under is contacted.
+@@ -2954,7 +2954,7 @@ def _install(options):
+             # Particulary, SSSD might take longer than 6-8 seconds.
+             while n < 10 and not found:
+                 try:
+-                    ipautil.run(["getent", "passwd", user])
++                    ipautil.run([paths.GETENT, "passwd", user])
+                     found = True
+                 except Exception as e:
+                     time.sleep(1)
+@@ -3036,7 +3036,7 @@ def uninstall(options):
+     statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
+ 
+     try:
+-        run(["ipa-client-automount", "--uninstall", "--debug"])
++        run([paths.IPA_CLIENT_AUTOMOUNT, "--uninstall", "--debug"])
+     except Exception as e:
+         logger.error(
+             "Unconfigured automount client failed: %s", str(e))
+diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
+index f1327daa11840bb9416cb0a12f2b5a1300b0374b..435d1b7de9083ee74e80da6fef5c3e3cdad654bb 100644
+--- a/ipaplatform/base/paths.py
++++ b/ipaplatform/base/paths.py
+@@ -25,6 +25,8 @@ This base platform module exports default filesystem paths.
+ class BasePathNamespace(object):
+     BASH = "/bin/bash"
+     BIN_HOSTNAMECTL = "/bin/hostnamectl"
++    ECHO = "/bin/echo"
++    GZIP = "/usr/bin/gzip"
+     LS = "/bin/ls"
+     SH = "/bin/sh"
+     SYSTEMCTL = "/bin/systemctl"
+@@ -160,8 +162,10 @@ class BasePathNamespace(object):
+     GPG = "/usr/bin/gpg"
+     GPG_AGENT = "/usr/bin/gpg-agent"
+     IPA_GETCERT = "/usr/bin/ipa-getcert"
++    KADMIN_LOCAL = '/usr/sbin/kadmin.local'
+     KDESTROY = "/usr/bin/kdestroy"
+     KINIT = "/usr/bin/kinit"
++    KLIST = "/usr/bin/klist"
+     BIN_KVNO = "/usr/bin/kvno"
+     LDAPMODIFY = "/usr/bin/ldapmodify"
+     LDAPPASSWD = "/usr/bin/ldappasswd"
+@@ -207,6 +211,7 @@ class BasePathNamespace(object):
+     GROUPADD = "/usr/sbin/groupadd"
+     USERMOD = "/usr/sbin/usermod"
+     HTTPD = "/usr/sbin/httpd"
++    IPA_CLIENT_AUTOMOUNT = "/usr/sbin/ipa-client-automount"
+     IPA_CLIENT_INSTALL = "/usr/sbin/ipa-client-install"
+     IPA_DNS_INSTALL = "/usr/sbin/ipa-dns-install"
+     SBIN_IPA_JOIN = "/usr/sbin/ipa-join"
+@@ -362,6 +367,9 @@ class BasePathNamespace(object):
+     IF_INET6 = '/proc/net/if_inet6'
+     AUTHCONFIG = None
+     IPA_SERVER_UPGRADE = '/usr/sbin/ipa-server-upgrade'
++    KEYCTL = '/usr/bin/keyctl'
++    GETENT = '/usr/bin/getent'
++    SSHD = '/usr/sbin/sshd'
+ 
+ 
+ paths = BasePathNamespace()
+diff --git a/ipapython/kernel_keyring.py b/ipapython/kernel_keyring.py
+index 4b7010e32e90a52fefb0ebbd4fec930ae82b7ea6..6ae1e74493810fa25093fe134447dd4ba0f5da74 100644
+--- a/ipapython/kernel_keyring.py
++++ b/ipapython/kernel_keyring.py
+@@ -23,6 +23,7 @@ import os
+ import six
+ 
+ from ipapython.ipautil import run
++from ipaplatform.paths import paths
+ 
+ # NOTE: Absolute path not required for keyctl since we reset the environment
+ #       in ipautil.run.
+@@ -35,34 +36,38 @@ from ipapython.ipautil import run
+ KEYRING = '@s'
+ KEYTYPE = 'user'
+ 
++
+ def dump_keys():
+     """
+     Dump all keys
+     """
+-    result = run(['keyctl', 'list', KEYRING], raiseonerr=False,
++    result = run([paths.KEYCTL, 'list', KEYRING], raiseonerr=False,
+                  capture_output=True)
+     return result.output
+ 
++
+ def get_real_key(key):
+     """
+     One cannot request a key based on the description it was created with
+     so find the one we're looking for.
+     """
+     assert isinstance(key, six.string_types)
+-    result = run(['keyctl', 'search', KEYRING, KEYTYPE, key],
++    result = run([paths.KEYCTL, 'search', KEYRING, KEYTYPE, key],
+                  raiseonerr=False, capture_output=True)
+     if result.returncode:
+         raise ValueError('key %s not found' % key)
+     return result.raw_output.rstrip()
+ 
++
+ def get_persistent_key(key):
+     assert isinstance(key, six.string_types)
+-    result = run(['keyctl', 'get_persistent', KEYRING, key],
++    result = run([paths.KEYCTL, 'get_persistent', KEYRING, key],
+                  raiseonerr=False, capture_output=True)
+     if result.returncode:
+         raise ValueError('persistent key %s not found' % key)
+     return result.raw_output.rstrip()
+ 
++
+ def is_persistent_keyring_supported():
+     uid = os.geteuid()
+     try:
+@@ -72,6 +77,7 @@ def is_persistent_keyring_supported():
+ 
+     return True
+ 
++
+ def has_key(key):
+     """
+     Returns True/False whether the key exists in the keyring.
+@@ -83,6 +89,7 @@ def has_key(key):
+     except ValueError:
+         return False
+ 
++
+ def read_key(key):
+     """
+     Read the keyring and return the value for key.
+@@ -91,13 +98,14 @@ def read_key(key):
+     """
+     assert isinstance(key, six.string_types)
+     real_key = get_real_key(key)
+-    result = run(['keyctl', 'pipe', real_key], raiseonerr=False,
++    result = run([paths.KEYCTL, 'pipe', real_key], raiseonerr=False,
+                  capture_output=True)
+     if result.returncode:
+         raise ValueError('keyctl pipe failed: %s' % result.error_log)
+ 
+     return result.raw_output
+ 
++
+ def update_key(key, value):
+     """
+     Update the keyring data. If they key doesn't exist it is created.
+@@ -106,13 +114,14 @@ def update_key(key, value):
+     assert isinstance(value, bytes)
+     if has_key(key):
+         real_key = get_real_key(key)
+-        result = run(['keyctl', 'pupdate', real_key], stdin=value,
++        result = run([paths.KEYCTL, 'pupdate', real_key], stdin=value,
+                      raiseonerr=False)
+         if result.returncode:
+             raise ValueError('keyctl pupdate failed: %s' % result.error_log)
+     else:
+         add_key(key, value)
+ 
++
+ def add_key(key, value):
+     """
+     Add a key to the kernel keyring.
+@@ -121,18 +130,19 @@ def add_key(key, value):
+     assert isinstance(value, bytes)
+     if has_key(key):
+         raise ValueError('key %s already exists' % key)
+-    result = run(['keyctl', 'padd', KEYTYPE, key, KEYRING],
++    result = run([paths.KEYCTL, 'padd', KEYTYPE, key, KEYRING],
+                  stdin=value, raiseonerr=False)
+     if result.returncode:
+         raise ValueError('keyctl padd failed: %s' % result.error_log)
+ 
++
+ def del_key(key):
+     """
+     Remove a key from the keyring
+     """
+     assert isinstance(key, six.string_types)
+     real_key = get_real_key(key)
+-    result = run(['keyctl', 'unlink', real_key, KEYRING],
++    result = run([paths.KEYCTL, 'unlink', real_key, KEYRING],
+                  raiseonerr=False)
+     if result.returncode:
+         raise ValueError('keyctl unlink failed: %s' % result.error_log)
+diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
+index 1f875c26fe909428ebf2e9c2acc9a8ad70de9a72..e787fccb9482809b180012ed8e7be2e5a6494f93 100644
+--- a/ipaserver/install/adtrustinstance.py
++++ b/ipaserver/install/adtrustinstance.py
+@@ -555,8 +555,10 @@ class ADTRUSTInstance(service.Service):
+     def clean_samba_keytab(self):
+         if os.path.exists(self.keytab):
+             try:
+-                ipautil.run(["ipa-rmkeytab", "--principal", self.principal,
+-                             "-k", self.keytab])
++                ipautil.run([
++                    paths.IPA_RMKEYTAB, "--principal", self.principal,
++                    "-k", self.keytab
++                ])
+             except ipautil.CalledProcessError as e:
+                 if e.returncode != 5:
+                     logger.critical("Failed to remove old key for %s",
+diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
+index 005fbeef37309ee3891e82ac0727adb031213da6..e110a5c3fc3e214736cb650f6da8a330eaa665a2 100644
+--- a/ipaserver/install/installutils.py
++++ b/ipaserver/install/installutils.py
+@@ -599,19 +599,26 @@ def get_directive(filename, directive, separator=' '):
+     fd.close()
+     return None
+ 
++
+ def kadmin(command):
+-    return ipautil.run(["kadmin.local", "-q", command,
+-                        "-x", "ipa-setup-override-restrictions"],
+-                       capture_output=True,
+-                       capture_error=True)
++    return ipautil.run(
++        [
++            paths.KADMIN_LOCAL, "-q", command,
++            "-x", "ipa-setup-override-restrictions"
++        ],
++        capture_output=True,
++        capture_error=True
++    )
+ 
+ 
+ def kadmin_addprinc(principal):
+     return kadmin("addprinc -randkey " + principal)
+ 
++
+ def kadmin_modprinc(principal, options):
+     return kadmin("modprinc " + options + " " + principal)
+ 
++
+ def create_keytab(path, principal):
+     try:
+         if os.path.isfile(path):
+@@ -832,7 +839,7 @@ def expand_replica_info(filename, password):
+     tarfile = top_dir+"/files.tar"
+     dir_path = top_dir + "/realm_info"
+     decrypt_file(filename, tarfile, password, top_dir)
+-    ipautil.run(["tar", "xf", tarfile, "-C", top_dir])
++    ipautil.run([paths.TAR, "xf", tarfile, "-C", top_dir])
+     os.remove(tarfile)
+ 
+     return top_dir, dir_path
+diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
+index cef01d30454ea1adb8bf9c68f428b9555f1b9557..db4b28c6deebd833855c32c5ff832dad3e5c741e 100644
+--- a/ipaserver/install/ipa_backup.py
++++ b/ipaserver/install/ipa_backup.py
+@@ -314,7 +314,7 @@ class Backup(admintool.AdminTool):
+                     dirsrv.stop(capture_output=False)
+             else:
+                 logger.info('Stopping IPA services')
+-                run(['ipactl', 'stop'])
++                run([paths.IPACTL, 'stop'])
+ 
+             instance = installutils.realm_to_serverid(api.env.realm)
+             if os.path.exists(paths.VAR_LIB_SLAPD_INSTANCE_DIR_TEMPLATE %
+@@ -336,7 +336,7 @@ class Backup(admintool.AdminTool):
+                     dirsrv.start(capture_output=False)
+             else:
+                 logger.info('Starting IPA service')
+-                run(['ipactl', 'start'])
++                run([paths.IPACTL, 'start'])
+ 
+             # Compress after services are restarted to minimize
+             # the unavailability window
+@@ -549,7 +549,7 @@ class Backup(admintool.AdminTool):
+         # Compress the archive. This is done separately, since 'tar' cannot
+         # append to a compressed archive.
+         if self.tarfile:
+-            result = run(['gzip', self.tarfile], raiseonerr=False)
++            result = run([paths.GZIP, self.tarfile], raiseonerr=False)
+             if result.returncode != 0:
+                 raise admintool.ScriptError(
+                     'gzip returned non-zero code %d '
+diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
+index c7e996bbe284a7eb2d03fbedb4798d3b15f3dcc0..4941831585f473c4937b23b3f59d8ff99a654b0e 100644
+--- a/ipaserver/install/ipa_restore.py
++++ b/ipaserver/install/ipa_restore.py
+@@ -386,7 +386,7 @@ class Restore(admintool.AdminTool):
+                     dirsrv.start(capture_output=False)
+             else:
+                 logger.info('Stopping IPA services')
+-                result = run(['ipactl', 'stop'], raiseonerr=False)
++                result = run([paths.IPACTL, 'stop'], raiseonerr=False)
+                 if result.returncode not in [0, 6]:
+                     logger.warning('Stopping IPA failed: %s', result.error_log)
+ 
+@@ -426,7 +426,7 @@ class Restore(admintool.AdminTool):
+                 gssproxy = services.service('gssproxy', api)
+                 gssproxy.reload_or_restart()
+                 logger.info('Starting IPA services')
+-                run(['ipactl', 'start'])
++                run([paths.IPACTL, 'start'])
+                 logger.info('Restarting SSSD')
+                 sssd = services.service('sssd', api)
+                 sssd.restart()
+diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
+index 319eeb82bcbe61acd70b2943982b6fec6fa33f92..139803ffbe26a6197535e66b63ba566c2a917e01 100644
+--- a/ipaserver/install/krbinstance.py
++++ b/ipaserver/install/krbinstance.py
+@@ -346,7 +346,7 @@ class KrbInstance(service.Service):
+         MIN_KRB5KDC_WITH_WORKERS = "1.9"
+         cpus = os.sysconf('SC_NPROCESSORS_ONLN')
+         workers = False
+-        result = ipautil.run(['klist', '-V'],
++        result = ipautil.run([paths.KLIST, '-V'],
+                              raiseonerr=False, capture_output=True)
+         if result.returncode == 0:
+             verstr = result.output.split()[-1]
+diff --git a/ipatests/pytest_ipa/integration/__init__.py b/ipatests/pytest_ipa/integration/__init__.py
+index fb9990a15b8e28dbe27f2b9275e4877e00f25755..7c1eb2232e2362b8f691329d9a022391a0e79d91 100644
+--- a/ipatests/pytest_ipa/integration/__init__.py
++++ b/ipatests/pytest_ipa/integration/__init__.py
+@@ -31,6 +31,7 @@ import pytest
+ from pytest_multihost import make_multihost_fixture
+ 
+ from ipapython import ipautil
++from ipaplatform.paths import paths
+ from ipatests.test_util import yield_fixture
+ from .config import Config
+ from .env_config import get_global_config
+@@ -150,7 +151,7 @@ def collect_logs(name, logs_dict, logfile_dir=None, beakerlib_plugin=None):
+             # delete from remote
+             host.run_command(['rm', '-f', tmpname])
+             # Unpack on the local side
+-            ipautil.run(['tar', 'xJvf', 'logs.tar.xz'], cwd=dirname,
++            ipautil.run([paths.TAR, 'xJvf', 'logs.tar.xz'], cwd=dirname,
+                         raiseonerr=False)
+             os.unlink(tarname)
+ 
+diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py
+index f93bdc976e03e23536c4cb2dc7401d44ddddcea1..ff8d95caa6fed00d3876f1d08e2170a9587a6d86 100644
+--- a/ipatests/test_integration/test_caless.py
++++ b/ipatests/test_integration/test_caless.py
+@@ -336,7 +336,7 @@ class CALessBase(IntegrationTest):
+                 with open(cert_fname) as cert:
+                     chain.write(cert.read())
+ 
+-        ipautil.run(["openssl", "pkcs12", "-export", "-out", filename,
++        ipautil.run([paths.OPENSSL, "pkcs12", "-export", "-out", filename,
+                      "-inkey", key_fname, "-in", certchain_fname, "-passin",
+                      "pass:" + cls.cert_password, "-passout", "pass:" +
+                      password, "-name", nickname], cwd=cls.cert_dir)
+diff --git a/ipatests/test_ipapython/test_ipautil.py b/ipatests/test_ipapython/test_ipautil.py
+index e15b4f948e8c6f927ee5594780609dace0345d28..88b591e5cb173799c2d5dffddadcfe65958b7c6b 100644
+--- a/ipatests/test_ipapython/test_ipautil.py
++++ b/ipatests/test_ipapython/test_ipautil.py
+@@ -30,6 +30,7 @@ import pytest
+ import six
+ import tempfile
+ 
++from ipaplatform.paths import paths
+ from ipalib.constants import IPAAPI_USER
+ from ipapython import ipautil
+ 
+@@ -419,7 +420,7 @@ class TestTimeParser(object):
+ 
+ 
+ def test_run():
+-    result = ipautil.run(['echo', 'foo\x02bar'],
++    result = ipautil.run([paths.ECHO, 'foo\x02bar'],
+                          capture_output=True,
+                          capture_error=True)
+     assert result.returncode == 0
+@@ -430,7 +431,7 @@ def test_run():
+ 
+ 
+ def test_run_no_capture_output():
+-    result = ipautil.run(['echo', 'foo\x02bar'])
++    result = ipautil.run([paths.ECHO, 'foo\x02bar'])
+     assert result.returncode == 0
+     assert result.output is None
+     assert result.raw_output == b'foo\x02bar\n'
+@@ -439,13 +440,13 @@ def test_run_no_capture_output():
+ 
+ 
+ def test_run_bytes():
+-    result = ipautil.run(['echo', b'\x01\x02'], capture_output=True)
++    result = ipautil.run([paths.ECHO, b'\x01\x02'], capture_output=True)
+     assert result.returncode == 0
+     assert result.raw_output == b'\x01\x02\n'
+ 
+ 
+ def test_run_decode():
+-    result = ipautil.run(['echo', u'á'.encode('utf-8')],
++    result = ipautil.run([paths.ECHO, u'á'.encode('utf-8')],
+                          encoding='utf-8', capture_output=True)
+     assert result.returncode == 0
+     if six.PY3:
+@@ -457,11 +458,11 @@ def test_run_decode():
+ def test_run_decode_bad():
+     if six.PY3:
+         with pytest.raises(UnicodeDecodeError):
+-            ipautil.run(['echo', b'\xa0\xa1'],
++            ipautil.run([paths.ECHO, b'\xa0\xa1'],
+                         capture_output=True,
+                         encoding='utf-8')
+     else:
+-        result = ipautil.run(['echo', '\xa0\xa1'],
++        result = ipautil.run([paths.ECHO, '\xa0\xa1'],
+                              capture_output=True,
+                              encoding='utf-8')
+         assert result.returncode == 0
+@@ -469,7 +470,7 @@ def test_run_decode_bad():
+ 
+ 
+ def test_backcompat():
+-    result = out, err, rc = ipautil.run(['echo', 'foo\x02bar'],
++    result = out, err, rc = ipautil.run([paths.ECHO, 'foo\x02bar'],
+                                         capture_output=True,
+                                         capture_error=True)
+     assert rc is result.returncode
+diff --git a/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
+index 931f7aff344859ee62e8e195a5fa76a1b4807eb1..6ed6cbf34c62bb83b6ebaf84cc3b105d6f76aea6 100644
+--- a/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
++++ b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py
+@@ -18,6 +18,7 @@ from cryptography.hazmat.primitives import hashes, serialization
+ from cryptography.hazmat.primitives.asymmetric import rsa
+ 
+ from ipalib import api, errors
++from ipaplatform.paths import paths
+ from ipatests.util import (
+     prepare_config, unlock_principal_password, change_principal,
+     host_keytab)
+@@ -50,7 +51,7 @@ def generate_user_csr(username, domain=None):
+         username=username)
+ 
+     with tempfile.NamedTemporaryFile(mode='w') as csr_file:
+-        run(['openssl', 'req', '-new', '-key', CERT_RSA_PRIVATE_KEY_PATH,
++        run([paths.OPENSSL, 'req', '-new', '-key', CERT_RSA_PRIVATE_KEY_PATH,
+              '-out', csr_file.name,
+              '-config', prepare_config(
+                  CERT_OPENSSL_CONFIG_TEMPLATE, csr_values)])
+diff --git a/ipatests/test_xmlrpc/test_cert_plugin.py b/ipatests/test_xmlrpc/test_cert_plugin.py
+index 9001e7f0989764a904275d11c5d96afc53322054..16f2058b1a9a38ec76479a184a23214275f5f551 100644
+--- a/ipatests/test_xmlrpc/test_cert_plugin.py
++++ b/ipatests/test_xmlrpc/test_cert_plugin.py
+@@ -176,7 +176,7 @@ class test_cert(BaseCert):
+         result = api.Command.cert_show(sn, out=unicode(self.certfile))
+         with open(self.certfile, "rb") as f:
+             pem_cert = f.read().decode('ascii')
+-        result = run(['openssl', 'x509', '-text'],
++        result = run([paths.OPENSSL, 'x509', '-text'],
+                      stdin=pem_cert, capture_output=True)
+         assert _EXP_CRL_URI in result.output
+         assert _EXP_OCSP_URI in result.output
+-- 
+2.20.1
+
diff --git a/SOURCES/0007-Use-common-replication-wait-timeout-of-5min.patch b/SOURCES/0007-Use-common-replication-wait-timeout-of-5min.patch
deleted file mode 100644
index d09ecc8..0000000
--- a/SOURCES/0007-Use-common-replication-wait-timeout-of-5min.patch
+++ /dev/null
@@ -1,123 +0,0 @@
-From 08d27e373976f82e054d17a76b8653221c56225c Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 22 Jun 2018 10:00:24 +0200
-Subject: [PATCH] Use common replication wait timeout of 5min
-
-Instead of multiple timeout values all over the code base, all
-replication waits now use a common timeout value from api.env of 5
-minutes. Waiting for HTTP/replica principal takes 90 to 120 seconds, so
-5 minutes seem like a sufficient value for slow setups.
-
-Fixes: https://pagure.io/freeipa/issue/7595
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
----
- ipalib/constants.py                   |  2 ++
- ipaserver/install/custodiainstance.py |  4 +++-
- ipaserver/install/httpinstance.py     |  6 +++++-
- ipaserver/install/krbinstance.py      | 13 ++++++++-----
- ipaserver/install/replication.py      |  6 ++++--
- 5 files changed, 22 insertions(+), 9 deletions(-)
-
-diff --git a/ipalib/constants.py b/ipalib/constants.py
-index 9ae6e0aaaee0577372fe458feb7660e05c7fed4d..38ed57b3cc35afe536f90b1f4245fd73ad4d3740 100644
---- a/ipalib/constants.py
-+++ b/ipalib/constants.py
-@@ -149,6 +149,8 @@ DEFAULT_CONFIG = (
-     ('startup_timeout', 300),
-     # How long http connection should wait for reply [seconds].
-     ('http_timeout', 30),
-+    # How long to wait for an entry to appear on a replica
-+    ('replication_wait_timeout', 300),
- 
-     # Web Application mount points
-     ('mount_ipa', '/ipa/'),
-diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py
-index c87306d2f48367031e888613b07c1091fc1a70f4..fcfe0908426bf71243974c384b0147eb763ad32f 100644
---- a/ipaserver/install/custodiainstance.py
-+++ b/ipaserver/install/custodiainstance.py
-@@ -5,6 +5,7 @@ from __future__ import print_function, absolute_import
- import enum
- import logging
- 
-+from ipalib import api
- from ipaserver.secrets.kem import IPAKEMKeys, KEMLdap
- from ipaserver.secrets.client import CustodiaClient
- from ipaplatform.paths import paths
-@@ -214,7 +215,8 @@ class CustodiaInstance(SimpleServiceInstance):
-         cli = self._get_custodia_client()
-         cli.fetch_key('dm/DMHash')
- 
--    def _wait_keys(self, timeout=300):
-+    def _wait_keys(self):
-+        timeout = api.env.replication_wait_timeout
-         deadline = int(time.time()) + timeout
-         logger.info("Waiting up to %s seconds to see our keys "
-                     "appear on host %s", timeout, self.ldap_uri)
-diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
-index 3764870ee77f2ba0da18ec004664e6f66c13bba1..bdd79b1dafda7de664eed664a18bf36c541212bc 100644
---- a/ipaserver/install/httpinstance.py
-+++ b/ipaserver/install/httpinstance.py
-@@ -644,4 +644,8 @@ class HTTPInstance(service.Service):
-                 else:
-                     remote_ldap.simple_bind(ipaldap.DIRMAN_DN,
-                                             self.dm_password)
--                replication.wait_for_entry(remote_ldap, service_dn, timeout=60)
-+                replication.wait_for_entry(
-+                    remote_ldap,
-+                    service_dn,
-+                    timeout=api.env.replication_wait_timeout
-+                )
-diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
-index 4a5a52dcc831c4edfe0cd52c44fb18bcb6adaef7..a356d5e0c1b96dc6511c335fc22a326a2133bdd8 100644
---- a/ipaserver/install/krbinstance.py
-+++ b/ipaserver/install/krbinstance.py
-@@ -399,13 +399,16 @@ class KrbInstance(service.Service):
-     def _wait_for_replica_kdc_entry(self):
-         master_dn = self.api.Object.server.get_dn(self.fqdn)
-         kdc_dn = DN(('cn', 'KDC'), master_dn)
--
--        ldap_uri = 'ldap://{}'.format(self.master_fqdn)
--
-+        ldap_uri = ipaldap.get_ldap_uri(self.master_fqdn)
-         with ipaldap.LDAPClient(
--                ldap_uri, cacert=paths.IPA_CA_CRT) as remote_ldap:
-+                ldap_uri, cacert=paths.IPA_CA_CRT, start_tls=True
-+        ) as remote_ldap:
-             remote_ldap.gssapi_bind()
--            replication.wait_for_entry(remote_ldap, kdc_dn, timeout=60)
-+            replication.wait_for_entry(
-+                remote_ldap,
-+                kdc_dn,
-+                timeout=api.env.replication_wait_timeout
-+            )
- 
-     def _call_certmonger(self, certmonger_ca='IPA'):
-         subject = str(DN(('cn', self.fqdn), self.subject_base))
-diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
-index 6d9878e16faab1b88a5ab79649571de6802c8536..5ce8fa689c1a6fd3b9d4cbddbd5454d36334b729 100644
---- a/ipaserver/install/replication.py
-+++ b/ipaserver/install/replication.py
-@@ -161,7 +161,7 @@ def wait_for_task(conn, dn):
-     return exit_code
- 
- 
--def wait_for_entry(connection, dn, timeout=7200, attr=None, attrvalue='*',
-+def wait_for_entry(connection, dn, timeout, attr=None, attrvalue='*',
-                    quiet=True):
-     """Wait for entry and/or attr to show up
-     """
-@@ -751,7 +751,9 @@ class ReplicationManager(object):
-             # that we will have to set the memberof fixup task
-             self.need_memberof_fixup = True
- 
--        wait_for_entry(a_conn, entry.dn)
-+        wait_for_entry(
-+            a_conn, entry.dn, timeout=api.env.replication_wait_timeout
-+        )
- 
-     def needs_memberof_fixup(self):
-         return self.need_memberof_fixup
--- 
-2.14.4
-
diff --git a/SOURCES/0008-Fix-replication-races-in-Dogtag-admin-code.patch b/SOURCES/0008-Fix-replication-races-in-Dogtag-admin-code.patch
deleted file mode 100644
index fccb1fd..0000000
--- a/SOURCES/0008-Fix-replication-races-in-Dogtag-admin-code.patch
+++ /dev/null
@@ -1,204 +0,0 @@
-From ecd1fa6b42c6ae76ce620c5ac949e64ba11d375a Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 22 Jun 2018 10:04:38 +0200
-Subject: [PATCH] Fix replication races in Dogtag admin code
-
-DogtagInstance.setup_admin and related methods have multiple LDAP
-replication race conditions. The bugs can cause parallel
-ipa-replica-install to fail.
-
-The code from __add_admin_to_group() has been changed to use MOD_ADD
-ather than search + MOD_REPLACE. The MOD_REPLACE approach can lead to
-data loss, when more than one writer changes a group.
-
-setup_admin() now waits until both admin user and group membership have
-been replicated to the master peer. The method also adds a new ACI to
-allow querying group member in the replication check.
-
-Fixes: https://pagure.io/freeipa/issue/7593
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
----
- ipaserver/install/cainstance.py     |  1 +
- ipaserver/install/dogtaginstance.py | 96 +++++++++++++++++++++++++++++--------
- ipaserver/install/krainstance.py    |  1 +
- 3 files changed, 77 insertions(+), 21 deletions(-)
-
-diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
-index 5bdac82a57c2228b1b196337a2ceadb062fdc13c..b58fbb4c881d247d6b5fb661f4085ec82c3cc811 100644
---- a/ipaserver/install/cainstance.py
-+++ b/ipaserver/install/cainstance.py
-@@ -392,6 +392,7 @@ class CAInstance(DogtagInstance):
-                 # Setup Database
-                 self.step("creating certificate server db", self.__create_ds_db)
-                 self.step("setting up initial replication", self.__setup_replication)
-+                self.step("creating ACIs for admin", self.add_ipaca_aci)
-                 self.step("creating installation admin user", self.setup_admin)
-             self.step("configuring certificate server instance",
-                       self.__spawn_instance)
-diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
-index 07e63024c6e138269634f0206b14c6a1710df936..5b2c30f8a1b7e932ce1cca3ca38f5962a3d54266 100644
---- a/ipaserver/install/dogtaginstance.py
-+++ b/ipaserver/install/dogtaginstance.py
-@@ -21,6 +21,7 @@ from __future__ import absolute_import
- 
- import base64
- import logging
-+import time
- 
- import ldap
- import os
-@@ -90,6 +91,16 @@ class DogtagInstance(service.Service):
-     tracking_reqs = None
-     server_cert_name = None
- 
-+    ipaca_groups = DN(('ou', 'groups'), ('o', 'ipaca'))
-+    ipaca_people = DN(('ou', 'people'), ('o', 'ipaca'))
-+    groups_aci = (
-+        b'(targetfilter="(objectClass=groupOfUniqueNames)")'
-+        b'(targetattr="cn || description || objectclass || uniquemember")'
-+        b'(version 3.0; acl "Allow users from o=ipaca to read groups"; '
-+        b'allow (read, search, compare) '
-+        b'userdn="ldap:///uid=*,ou=people,o=ipaca";)'
-+    )
-+
-     def __init__(self, realm, subsystem, service_desc, host_name=None,
-                  nss_db=paths.PKI_TOMCAT_ALIAS_DIR, service_prefix=None,
-                  config=None):
-@@ -108,10 +119,11 @@ class DogtagInstance(service.Service):
-         self.pkcs12_info = None
-         self.clone = False
- 
--        self.basedn = DN(('o', 'ipa%s' % subsystem.lower()))
-+        self.basedn = DN(('o', 'ipaca'))
-         self.admin_user = "admin"
--        self.admin_dn = DN(('uid', self.admin_user),
--                           ('ou', 'people'), ('o', 'ipaca'))
-+        self.admin_dn = DN(
-+            ('uid', self.admin_user), self.ipaca_people
-+        )
-         self.admin_groups = None
-         self.tmp_agent_db = None
-         self.subsystem = subsystem
-@@ -393,27 +405,31 @@ class DogtagInstance(service.Service):
- 
-         raise RuntimeError("%s configuration failed." % self.subsystem)
- 
--    def __add_admin_to_group(self, group):
--        dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
--        entry = api.Backend.ldap2.get_entry(dn)
--        members = entry.get('uniqueMember', [])
--        members.append(self.admin_dn)
--        mod = [(ldap.MOD_REPLACE, 'uniqueMember', members)]
-+    def add_ipaca_aci(self):
-+        """Add ACI to allow ipaca users to read their own group information
-+
-+        Dogtag users aren't allowed to enumerate their own groups. The
-+        setup_admin() method needs the permission to wait, until all group
-+        information has been replicated.
-+        """
-+        dn = self.ipaca_groups
-+        mod = [(ldap.MOD_ADD, 'aci', [self.groups_aci])]
-         try:
-             api.Backend.ldap2.modify_s(dn, mod)
-         except ldap.TYPE_OR_VALUE_EXISTS:
--            # already there
--            pass
-+            logger.debug("%s already has ACI to read group information", dn)
-+        else:
-+            logger.debug("Added ACI to read groups to %s", dn)
- 
-     def setup_admin(self):
-         self.admin_user = "admin-%s" % self.fqdn
-         self.admin_password = ipautil.ipa_generate_password()
--        self.admin_dn = DN(('uid', self.admin_user),
--                           ('ou', 'people'), ('o', 'ipaca'))
--
-+        self.admin_dn = DN(
-+            ('uid', self.admin_user), self.ipaca_people
-+        )
-         # remove user if left-over exists
-         try:
--            entry = api.Backend.ldap2.delete_entry(self.admin_dn)
-+            api.Backend.ldap2.delete_entry(self.admin_dn)
-         except errors.NotFound:
-             pass
- 
-@@ -432,18 +448,56 @@ class DogtagInstance(service.Service):
-         )
-         api.Backend.ldap2.add_entry(entry)
- 
-+        wait_groups = []
-         for group in self.admin_groups:
--            self.__add_admin_to_group(group)
-+            group_dn = DN(('cn', group), self.ipaca_groups)
-+            mod = [(ldap.MOD_ADD, 'uniqueMember', [self.admin_dn])]
-+            try:
-+                api.Backend.ldap2.modify_s(group_dn, mod)
-+            except ldap.TYPE_OR_VALUE_EXISTS:
-+                # already there
-+                return None
-+            else:
-+                wait_groups.append(group_dn)
- 
-         # Now wait until the other server gets replicated this data
-         ldap_uri = ipaldap.get_ldap_uri(self.master_host)
--        master_conn = ipaldap.LDAPClient(ldap_uri)
--        master_conn.gssapi_bind()
--        replication.wait_for_entry(master_conn, entry.dn)
--        del master_conn
-+        master_conn = ipaldap.LDAPClient(ldap_uri, start_tls=True)
-+        logger.debug(
-+            "Waiting for %s to appear on %s", self.admin_dn, master_conn
-+        )
-+        deadline = time.time() + api.env.replication_wait_timeout
-+        while time.time() < deadline:
-+            time.sleep(1)
-+            try:
-+                master_conn.simple_bind(self.admin_dn, self.admin_password)
-+            except ldap.INVALID_CREDENTIALS:
-+                pass
-+            else:
-+                logger.debug("Successfully logged in as %s", self.admin_dn)
-+                break
-+        else:
-+            logger.error(
-+                "Unable to log in as %s on %s", self.admin_dn, master_conn
-+            )
-+            raise errors.NotFound(
-+                reason="{} did not replicate to {}".format(
-+                    self.admin_dn, master_conn
-+                )
-+            )
-+
-+        # wait for group membership
-+        for group_dn in wait_groups:
-+            replication.wait_for_entry(
-+                master_conn,
-+                group_dn,
-+                timeout=api.env.replication_wait_timeout,
-+                attr='uniqueMember',
-+                attrvalue=self.admin_dn
-+            )
- 
-     def __remove_admin_from_group(self, group):
--        dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
-+        dn = DN(('cn', group), self.ipaca_groups)
-         mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)]
-         try:
-             api.Backend.ldap2.modify_s(dn, mod)
-diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
-index b959398706d051a2584e722e176a9f2b0a9a0dc7..9483f0ec4edbabea0f7eff0dd5dd223377653536 100644
---- a/ipaserver/install/krainstance.py
-+++ b/ipaserver/install/krainstance.py
-@@ -115,6 +115,7 @@ class KRAInstance(DogtagInstance):
-                 "A Dogtag CA must be installed first")
- 
-         if promote:
-+            self.step("creating ACIs for admin", self.add_ipaca_aci)
-             self.step("creating installation admin user", self.setup_admin)
-         self.step("configuring KRA instance", self.__spawn_instance)
-         if not self.clone:
--- 
-2.14.4
-
diff --git a/SOURCES/0008-Support-Samba-4.9.patch b/SOURCES/0008-Support-Samba-4.9.patch
new file mode 100644
index 0000000..cccdcce
--- /dev/null
+++ b/SOURCES/0008-Support-Samba-4.9.patch
@@ -0,0 +1,120 @@
+From 9650b233bdbda82bcbb447a3fc94523655cabc39 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Fri, 21 Sep 2018 10:57:23 +0300
+Subject: [PATCH] Support Samba 4.9
+
+Samba 4.9 became a bit more strict about creating a local NT token and a
+failure to resolve or create BUILTIN\Guests group will cause a rejection
+of the connection for a successfully authenticated one.
+
+Add a default mapping of the nobody group to BUILTIN\Guests.
+
+BUILTIN\Guests is a special group SID that is added to the NT token for
+authenticated users.
+
+For real guests there is 'guest account' option in smb.conf which
+defaults to 'nobody' user.
+
+This was implicit behavior before as 'guest account = nobody' by
+default would pick up 'nobody' group as well.
+
+Fixes: https://pagure.io/freeipa/issue/7705
+Reviewed-By: Rob Crittenden <rcritten@redhat.com>
+(cherry picked from commit 703497532abe4189835d0a02b32f9919c889bc1c)
+
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ .../updates/90-post_upgrade_plugins.update    |  1 +
+ ipaserver/install/adtrustinstance.py          | 14 +++++++++++++
+ ipaserver/install/plugins/adtrust.py          | 20 ++++++++++++++++++-
+ 3 files changed, 34 insertions(+), 1 deletion(-)
+
+diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
+index bbc3e29422fc0f139c2ca68a7033863e4c25f8cf..4e9378d9b567842e1cc9a8eeae819a931810895d 100644
+--- a/install/updates/90-post_upgrade_plugins.update
++++ b/install/updates/90-post_upgrade_plugins.update
+@@ -19,6 +19,7 @@ plugin: update_fix_duplicate_cacrt_in_ldap
+ plugin: update_upload_cacrt
+ # update_ra_cert_store has to be executed after update_ca_renewal_master
+ plugin: update_ra_cert_store
++plugin: update_mapping_Guests_to_nobody
+ 
+ # last
+ # DNS version 1
+diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
+index e787fccb9482809b180012ed8e7be2e5a6494f93..d6b8f5cfa66c0cfbc6d47906703fc09c3e961a53 100644
+--- a/ipaserver/install/adtrustinstance.py
++++ b/ipaserver/install/adtrustinstance.py
+@@ -120,6 +120,15 @@ def make_netbios_name(s):
+     return ''.join([c for c in s.split('.')[0].upper() \
+                     if c in ALLOWED_NETBIOS_CHARS])[:15]
+ 
++
++def map_Guests_to_nobody():
++    env = {'LC_ALL': 'C'}
++    args = [paths.NET, 'groupmap', 'add', 'sid=S-1-5-32-546',
++            'unixgroup=nobody', 'type=builtin']
++
++    logger.debug("Map BUILTIN\\Guests to a group 'nobody'")
++    ipautil.run(args, env=env, raiseonerr=False, capture_error=True)
++
+ class ADTRUSTInstance(service.Service):
+ 
+     ATTR_SID = "ipaNTSecurityIdentifier"
+@@ -532,6 +541,9 @@ class ADTRUSTInstance(service.Service):
+             tmp_conf.flush()
+             ipautil.run([paths.NET, "conf", "import", tmp_conf.name])
+ 
++    def __map_Guests_to_nobody(self):
++        map_Guests_to_nobody()
++
+     def __setup_group_membership(self):
+         # Add the CIFS and host principals to the 'adtrust agents' group
+         # as 389-ds only operates with GroupOfNames, we have to use
+@@ -833,6 +845,8 @@ class ADTRUSTInstance(service.Service):
+                   self.__create_samba_domain_object)
+         self.step("creating samba config registry", self.__write_smb_registry)
+         self.step("writing samba config file", self.__write_smb_conf)
++        self.step("map BUILTIN\\Guests to nobody group",
++                  self.__map_Guests_to_nobody)
+         self.step("adding cifs Kerberos principal",
+                   self.request_service_keytab)
+         self.step("adding cifs and host Kerberos principals to the adtrust agents group", \
+diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
+index bec5a09c1c129b1129f31e3df59a2fa87aac0691..1f50bef891770c53a9086c7aa36d0ee1f088fbe6 100644
+--- a/ipaserver/install/plugins/adtrust.py
++++ b/ipaserver/install/plugins/adtrust.py
+@@ -23,7 +23,8 @@ from ipalib import Registry, errors
+ from ipalib import Updater
+ from ipapython.dn import DN
+ from ipaserver.install import sysupgrade
+-from ipaserver.install.adtrustinstance import ADTRUSTInstance
++from ipaserver.install.adtrustinstance import (
++    ADTRUSTInstance, map_Guests_to_nobody)
+ 
+ logger = logging.getLogger(__name__)
+ 
+@@ -382,3 +383,20 @@ class update_tdo_gidnumber(Updater):
+             return False, ()
+ 
+         return False, ()
++
++
++@register()
++class update_mapping_Guests_to_nobody(Updater):
++    """
++    Map BUILTIN\\Guests group to nobody
++
++    Samba 4.9 became more strict on availability of builtin Guests group
++    """
++    def execute(self, **options):
++        # First, see if trusts are enabled on the server
++        if not self.api.Command.adtrust_is_enabled()['result']:
++            logger.debug('AD Trusts are not enabled on this server')
++            return False, []
++
++        map_Guests_to_nobody()
++        return False, []
+-- 
+2.20.1
+
diff --git a/SOURCES/0009-Add-design-page-for-one-way-trust-to-AD-with-shared-.patch b/SOURCES/0009-Add-design-page-for-one-way-trust-to-AD-with-shared-.patch
new file mode 100644
index 0000000..78963c4
--- /dev/null
+++ b/SOURCES/0009-Add-design-page-for-one-way-trust-to-AD-with-shared-.patch
@@ -0,0 +1,196 @@
+From fd9e724513e4c574cfa3f2498fbdae0682ccb9a9 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Tue, 19 Mar 2019 10:23:20 +0200
+Subject: [PATCH] Add design page for one-way trust to AD with shared secret
+
+(cherry picked from commit b3911ade55eec139e07f8b4ea83612f1b2794857)
+
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ .../oneway-trust-with-shared-secret.md        | 174 ++++++++++++++++++
+ 1 file changed, 174 insertions(+)
+ create mode 100644 doc/designs/adtrust/oneway-trust-with-shared-secret.md
+
+diff --git a/doc/designs/adtrust/oneway-trust-with-shared-secret.md b/doc/designs/adtrust/oneway-trust-with-shared-secret.md
+new file mode 100644
+index 0000000000000000000000000000000000000000..dc58a08941acea447f9234107ebcba775351089e
+--- /dev/null
++++ b/doc/designs/adtrust/oneway-trust-with-shared-secret.md
+@@ -0,0 +1,174 @@
++# One-way trust with shared secret
++
++## Overview
++
++FreeIPA does support trust to an Active Directory forest. The trust can be
++established using administrative credentials from the forest root domain or
++using a so-called shared secret. In the latter case no administrative access is
++given to the remote side of the trust and each administrator performs their
++configuration separately: FreeIPA administrator configures IPA side, Active
++Directory administrator adds IPA forest as a trusted one on the Active
++Directory side.
++
++For trust to be active, one needs to validate it. Validation process includes a
++sequences of DCE RPC calls that force a domain controller on the trusted side
++to establish a so-called "secure channel" to a remote domain controller in the
++trusting domain. This is an administrative operation and requires
++administrative privileges to activate. If trust was established using a shared
++secret, IPA side will lack ability to initiate a validation process.
++
++At the same time, FreeIPA 4.6 or earlier versions do not include functionality
++to allow a remote validation from Active Directory to happen before trust
++objects are created and SSSD can retrieve information from the Active Directory
++side. Unfortunately, the latter is not possible until trust is validated.
++
++The purpose of this design is to extend FreeIPA setup to allow trust validation
++to be initiated from Windows UI in case a shared secret is used to create a
++trust agreement.
++
++## Use Cases
++
++As a FreeIPA administrator, I'd like to establish a trust between an Active
++Directory forest and a FreeIPA deployment using a shared secret. As FreeIPA
++administrator, I have no administrative access to Active Directory and would
++like to delegate the operation to create trust on Active Directory side to my
++counterpart in Active Directory forest.
++
++
++## How to Use
++
++
++1. Establish a one-way trust with a shared secret on IPA side:
++   `ipa trust-add <ad-domain> --shared-secret`
++
++2. On Windows side, open Active Directory Domain and Trusts tool
++   * Open properties for the Windows forest
++   * Choose 'Trusts' tab and press 'New trust' button there
++   * Navigate through the trust wizard by entering:
++      *  IPA forest name, then 'next'
++      *  Choose 'Forest trust' on the Trust Type page
++      *  Choose 'One-way: incoming' on the Direction of Trust page
++      *  Choose 'This domain only' on the Sides of Trust page
++      *  Enter the same shared secret one was using in step (1) with 'ipa trust-add'
++   *  Complete trust wizard
++
++3. Going back to the trust properties, one can now validate the trust from Windows side.
++
++One limitation is that it is not possible to retrieve forest trust information
++about IPA realm by Active Directory domain controllers due to the fact that
++Samba configuration used by IPA does not support a remote query for this
++information. It is only available when Samba is used in Samba AD configuration.
++
++TODO: check whether it is possible to force to set forest trust information
++from IPA side after both sides of trust are were configured.
++
++## Design
++
++There are two important parts of the solution. On IPA side, there is a module
++to allow Samba to look up trusted domains and user/group information in IPA
++LDAP. On the other side, there is support for POSIX identities of trusted
++domain objects in SSSD.
++
++### FreeIPA module for Samba passdb interface
++
++FreeIPA provides a special module for Samba, `ipasam`, that looks up
++information about trusted domains and user/group in FreeIPA LDAP. The module
++also maintains trust-related information when trust is created via  DCE RPC
++interfaces.
++
++When trust is created, `ipasam` module needs to create a set of Kerberos
++principals to allow Kerberos KDC to issue cross-realm ticket granting tickets.
++These principals will have the same keys as trusted domain objects on Active
++Directory level.
++
++When a secure channel is established between two domain controllers from
++separate trusted domains, both DCs rely on the trusted domain object account
++credentials to be the same on both sides. However, since Samba has to perform
++SMB to POSIX translation when running in POSIX environment, it also needs to
++have a POSIX identity associated with the trusted domain object account.
++
++As result, `ipasam` module needs to maintain POSIX attributes for the trusted
++domain object account, along with Kerberos principals associated with the
++trust.
++
++### SSSD
++
++When Windows successfully authenticates to Samba, Samba needs a POSIX identity
++to run `smbd` processes as the authenticated 'user'. `smbd` and `winbindd`
++processes use standard system calls to resolve authenticated user to a system
++one (`getpwnam_r()`) and if the call fails, whole Windows request is rejected.
++
++Given that trusted domain object accounts are associated with the cross-realm
++Kerberos principals, they are located in a special subtree in FreeIPA LDAP:
++`cn=trusts,$SUFFIX`. However, SSSD does not look by default in this subtree for
++users. By default, SSSD configuration for user accounts looks in
++`cn=users,cn=accounts,$SUFFIX` for `id_provider = ipa` and will not be able to
++see trusted domain object accounts.
++
++Thus, to allow Windows to successfully validate a one-way shared incoming trust
++to FreeIPA, SSSD needs to resolve trusted domain object accounts as POSIX users
++on IPA master side.
++
++
++## Implementation
++
++### FreeIPA `ipasam` module
++
++`ipasam` module needs to create and maintain POSIX identities of the trusted
++domain object accounts.
++
++Following objects and their aliases are created and maintained by `ipasam`
++module. In the description below `REMOTE` means Kerberos realm of the Active
++Directory forest's root domain (e.g. `AD.EXAMPLE.COM`), `REMOTE-FLAT` is
++NetBIOS name of the Active Directory forest's root domain (e.g. `AD`).
++Correspondingly, `LOCAL` means FreeIPA Kerberos realm (e.g. `IPA.EXAMPLE.COM`)
++and `LOCAL-FLAT` is the NetBIOS name of the FreeIPA primary domain (e.g.
++`IPA`).
++
++  Principal | Description
++  --------- | -----------
++  krbtgt/REMOTE@LOCAL | Cross-realm principal representing Active Directory forest root domain
++  REMOTE-FLAT$@LOCAL | Trusted domain object account for the Active Directory forest root domain
++  krbtgt/REMOTE-FLAT@LOCAL | Alias to REMOTE-FLAT$ TDO
++  krbtgt/LOCAL@REMOTE | Cross-realm principal representing IPA domain in Active Directory forest to allow crross-realm TGT issuance from IPA KDC side
++  LOCAL-FLAT$@REMOTE | Trusted domain object account for IPA domain in Active Directory forest
++  krbtgt/LOCAL-FLAT@REMOTE | Alias to LOCAL-FLAT$
++
++For inbound trust `ipasam` module creates following principals:
++  * `krbtgt/LOCAL@REMOTE`, enabled by default
++  * `LOCAL-FLAT$@REMOTE`, used by SSSD to talk to Active Directory domain
++    controllers, with canonical name set to `LOCAL-FLAT$` because Kerberos KDC
++    must use this salt when issuing tickets for this principal. The use of this
++    principal is disabled on IPA side (IPA KDC does not issue tickets in this name)
++    --- we only retrieve a keytab for the principal in SSSD.
++
++For outbound trust `ipasam` module creates following principals:
++  * `krbtgt/REMOTE@LOCAL`, enabled by default.
++  * `REMOTE-FLAT$@LOCAL`, enabled by default. Used by a remote AD DC to
++    authenticate against Samba on IPA master. This principal will have POSIX
++    identity associated.
++
++
++### SSSD
++
++In IPA master mode, SSSD needs to start look into `cn=trusts,$SUFFIX` subtree
++in addition to `cn=users,cn=accounts,$SUFFIX` subtree to find trusted domain
++object accounts. This can be achieved either by explicitly adding a second
++search base to `ldap_search_user_base` option in the `[domain/...]` section or
++by automatically expanding a list of search bases when running in the IPA
++master mode. The latter is already implemented in SSSD 1.16.3 and 2.1.0 with
++https://pagure.io/SSSD/sssd/c/14faec9cd9437ef116ae054412d25ec2e820e409
++
++
++Feature Management
++------------------
++
++We already allow to create a shared secret-based trust to Active Directory. No
++functional change is needed in either Web UI or CLI.
++
++Upgrade
++-------
++
++During upgrade POSIX identities need to be created for existing trust
++agreements.
++
+-- 
+2.20.1
+
diff --git a/SOURCES/0009-Allow-anonymous-access-to-ParentID-attribute.patch b/SOURCES/0009-Allow-anonymous-access-to-ParentID-attribute.patch
deleted file mode 100644
index 94179ab..0000000
--- a/SOURCES/0009-Allow-anonymous-access-to-ParentID-attribute.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 34d06b2be71823bc8898732f1ced0185f83afb01 Mon Sep 17 00:00:00 2001
-From: Alexander Bokovoy <abokovoy@redhat.com>
-Date: Wed, 28 Mar 2018 12:39:12 +0300
-Subject: [PATCH] Allow anonymous access to parentID attribute
-
-Due to optimizations in 389-ds performed as result of
-https://pagure.io/389-ds-base/issue/49372, LDAP search filter
-is rewritten to include parentID information. It implies that parentID
-has to be readable for a bound identity performing the search. This is
-what 389-ds expects right now but FreeIPA DS instance does not allow it.
-
-As result, searches with a one-level scope fail to return results that
-otherwise are matched in a sub scope search.
-
-While 389-ds developers are working on the fix for issue
-https://pagure.io/389-ds-base/issue/49617, we can fix it by adding an
-explicit ACI to allow reading parentID attribute at the suffix level.
-
-Fixes: https://pagure.io/freeipa/issue/7466
-Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- install/updates/20-aci.update | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
-index dec2e16ee..b8a172eb5 100644
---- a/install/updates/20-aci.update
-+++ b/install/updates/20-aci.update
-@@ -21,6 +21,10 @@ add:aci:(targetattr="ipasshpubkey")(version 3.0; acl "Hosts can manage other hos
- dn: $SUFFIX
- add:aci:(targetfilter="(objectclass=domain)")(targetattr="objectclass || dc || info || nisDomain || associatedDomain")(version 3.0; acl "Anonymous read access to DIT root"; allow(read, search, compare) userdn = "ldap:///anyone";)
- 
-+# Read access to parentID information to allow filter optimizations in 389-ds
-+dn: $SUFFIX
-+add:aci:(targetattr="parentid")(version 3.0; acl "Anonymous read access to parentID information"; allow(read, search, compare) userdn = "ldap:///anyone";)
-+
- # Read access to containers
- dn: $SUFFIX
- add:aci:(targetfilter="(&(objectclass=nsContainer)(!(objectclass=krbPwdPolicy)))")(target!="ldap:///cn=masters,cn=ipa,cn=etc,$SUFFIX")(targetattr="objectclass || cn")(version 3.0; acl "Anonymous read access to containers"; allow(read, search, compare) userdn = "ldap:///anyone";)
--- 
-2.17.1
-
diff --git a/SOURCES/0010-Use-4-WSGI-workers-on-64bit-systems.patch b/SOURCES/0010-Use-4-WSGI-workers-on-64bit-systems.patch
deleted file mode 100644
index 212eb15..0000000
--- a/SOURCES/0010-Use-4-WSGI-workers-on-64bit-systems.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From bf539f80ed34c30bc852a6061ce6444c5c308a26 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Mon, 25 Jun 2018 10:59:18 +0200
-Subject: [PATCH] Use 4 WSGI workers on 64bit systems
-
-Commit f1d5ab3a03191dbb02e5f95308cf8c4f1971cdcf increases WSGI worker
-count to five. This turned out to be a bit much for our test systems.
-Four workers are good enough and still double the old amount.
-
-See: https://pagure.io/freeipa/issue/7587
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
----
- ipaplatform/base/constants.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py
-index 075a3ba774e1286e50258b464bd7687d484f6029..e9ce4378892a5cd498398e37cc52f295608152cd 100644
---- a/ipaplatform/base/constants.py
-+++ b/ipaplatform/base/constants.py
-@@ -46,7 +46,7 @@ class BaseConstantsNamespace(object):
-     MOD_WSGI_PYTHON3 = None
-     # WSGIDaemonProcess process count. On 64bit platforms, each process
-     # consumes about 110 MB RSS, from which are about 35 MB shared.
--    WSGI_PROCESSES = 5 if IS_64BITS else 2
-+    WSGI_PROCESSES = 4 if IS_64BITS else 2
- 
- 
- constants = BaseConstantsNamespace()
--- 
-2.17.1
-
diff --git a/SOURCES/0010-trust-allow-trust-agents-to-read-POSIX-identities-of.patch b/SOURCES/0010-trust-allow-trust-agents-to-read-POSIX-identities-of.patch
new file mode 100644
index 0000000..f07f1a1
--- /dev/null
+++ b/SOURCES/0010-trust-allow-trust-agents-to-read-POSIX-identities-of.patch
@@ -0,0 +1,36 @@
+From 58478231b10a670bc8c8bf5a1a41a5e3cb075b3f Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Fri, 22 Mar 2019 18:50:30 +0200
+Subject: [PATCH] trust: allow trust agents to read POSIX identities of trust
+
+SSSD and Samba on IPA masters need to be able to look up POSIX
+attributes of trusted domain objects in order to allow Active Directory
+domain controllers from trusted forests to connect to LSA and NETLOGON
+pipes.
+
+We only have access to read POSIX attributes in cn=accounts,$SUFFIX
+subtree rather than whole $SUFFIX. Thus, add an ACI to trusts subtree.
+
+Fixes: https://pagure.io/freeipa/issue/6077
+(cherry picked from commit 8908b5085179d07cff45ebb11d498b872d28eee7)
+
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ install/updates/60-trusts.update | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/install/updates/60-trusts.update b/install/updates/60-trusts.update
+index 04c85ba77389b12415849ed81b8131c07b1c7a32..90201e1b4bfc01509da722303cb975a970131fb5 100644
+--- a/install/updates/60-trusts.update
++++ b/install/updates/60-trusts.update
+@@ -33,6 +33,7 @@ add:aci: (target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType ||
+ replace:aci:(target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ipaNTTrustDirection || ipaNTTrustPartner || ipaNTFlatName || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming || ipaNTSecurityIdentifier || ipaNTTrustForestTrustInfo || ipaNTTrustPosixOffset || ipaNTSupportedEncryptionTypes || krbPrincipalName || krbLastPwdChange || krbTicketFlags || krbLoginFailedCount || krbExtraData || krbPrincipalKey")(version 3.0;acl "Allow trust system user to create and delete trust accounts and cross realm principals"; allow (read,write,add,delete) groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)::(target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ipaNTTrustDirection || ipaNTTrustPartner || ipaNTFlatName || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming || ipaNTSecurityIdentifier || ipaNTTrustForestTrustInfo || ipaNTTrustPosixOffset || ipaNTSupportedEncryptionTypes || ipaNTSIDBlacklistIncoming || ipaNTSIDBlacklistOutgoing || krbPrincipalName || krbLastPwdChange || krbTicketFlags || krbLoginFailedCount || krbExtraData || krbPrincipalKey")(version 3.0;acl "Allow trust system user to create and delete trust accounts and cross realm principals"; allow (read,write,add,delete) groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)
+ replace:aci:(target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ipaNTTrustDirection || ipaNTTrustPartner || ipaNTFlatName || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming || ipaNTSecurityIdentifier || ipaNTTrustForestTrustInfo || ipaNTTrustPosixOffset || ipaNTSupportedEncryptionTypes")(version 3.0;acl "Allow trust admins manage trust accounts"; allow (read,write,add,delete) groupdn="ldap:///cn=trust admins,cn=groups,cn=accounts,$SUFFIX";)::(target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ipaNTTrustDirection || ipaNTTrustPartner || ipaNTFlatName || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming || ipaNTSecurityIdentifier || ipaNTTrustForestTrustInfo || ipaNTTrustPosixOffset || ipaNTSupportedEncryptionTypes || ipaNTSIDBlacklistIncoming || ipaNTSIDBlacklistOutgoing")(version 3.0;acl "Allow trust admins manage trust accounts"; allow (read,write,add,delete) groupdn="ldap:///cn=trust admins,cn=groups,cn=accounts,$SUFFIX";)
+ add:aci: (target = "ldap:///cn=trusts,$SUFFIX")(targetattr = "ipaNTTrustType || ipaNTTrustAttributes || ipaNTTrustDirection || ipaNTTrustPartner || ipaNTFlatName || ipaNTTrustAuthOutgoing || ipaNTTrustAuthIncoming || ipaNTSecurityIdentifier || ipaNTTrustForestTrustInfo || ipaNTTrustPosixOffset || ipaNTSupportedEncryptionTypes || ipaNTSIDBlacklistIncoming || ipaNTSIDBlacklistOutgoing")(version 3.0;acl "Allow trust admins manage trust accounts"; allow (read,write,add,delete) groupdn="ldap:///cn=trust admins,cn=groups,cn=accounts,$SUFFIX";)
++add:aci: (targetattr = "cn || createtimestamp || description || displayname || entryusn || gecos || gidnumber || givenname || homedirectory || ipantsecurityidentifier || loginshell || modifytimestamp || objectclass || uid || uidnumber")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "Allow reading POSIX information about trusted domain objects";allow (compare,read,search) groupdn = "ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)
+ 
+ # Samba user should be able to read NT passwords to authenticate
+ # Add ipaNTHash to global ACIs, leave DNS tree out of global allow access rule
+-- 
+2.20.1
+
diff --git a/SOURCES/0011-Query-for-server-role-IPA-master.patch b/SOURCES/0011-Query-for-server-role-IPA-master.patch
deleted file mode 100644
index f592f40..0000000
--- a/SOURCES/0011-Query-for-server-role-IPA-master.patch
+++ /dev/null
@@ -1,108 +0,0 @@
-From ef7fe9e24844f73a27650d4d2a4f118cc364caac Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Thu, 5 Jul 2018 23:50:37 +0200
-Subject: [PATCH] Query for server role IPA master
-
-server_find and server_role plugin were hiding IPA master role
-information. It's now possible to fetch IPA master role information and
-to filter by IPA master role, e.g. to ignore servers that have some
-services configured but not (yet) enabled.
-
-See: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- API.txt                         |  3 ++-
- ipaserver/plugins/server.py     |  9 +++++++--
- ipaserver/plugins/serverrole.py | 18 +++++++++++++++---
- 3 files changed, 24 insertions(+), 6 deletions(-)
-
-diff --git a/API.txt b/API.txt
-index 96c1548331750577e899f397f6f5a9a9d863248a..0e09e58a6ecaa4f724fb0c92b4faaf64df9fab5a 100644
---- a/API.txt
-+++ b/API.txt
-@@ -4421,9 +4421,10 @@ output: Entry('result')
- output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
- output: PrimaryKey('value')
- command: server_role_find/1
--args: 1,8,4
-+args: 1,9,4
- arg: Str('criteria?')
- option: Flag('all', autofill=True, cli_name='all', default=False)
-+option: Flag('include_master', autofill=True, default=False)
- option: Flag('raw', autofill=True, cli_name='raw', default=False)
- option: Str('role_servrole?', autofill=False, cli_name='role')
- option: Str('server_server?', autofill=False, cli_name='server')
-diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py
-index 4ea6f5b4b458db701c6c041d1c05cf4a7c6bc8a4..e265883e3637938e3df5ecf132f4add62413a997 100644
---- a/ipaserver/plugins/server.py
-+++ b/ipaserver/plugins/server.py
-@@ -205,7 +205,10 @@ class server(LDAPObject):
-             return
- 
-         enabled_roles = self.api.Command.server_role_find(
--            server_server=entry_attrs['cn'][0], status=ENABLED)['result']
-+            server_server=entry_attrs['cn'][0],
-+            status=ENABLED,
-+            include_master=True,
-+        )['result']
- 
-         enabled_role_names = [r[u'role_servrole'] for r in enabled_roles]
- 
-@@ -339,7 +342,9 @@ class server_find(LDAPSearch):
-             role_status = self.api.Command.server_role_find(
-                 server_server=None,
-                 role_servrole=role,
--                status=ENABLED)['result']
-+                status=ENABLED,
-+                include_master=True,
-+            )['result']
- 
-             return set(
-                 r[u'server_server'] for r in role_status)
-diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py
-index 1b19c7e867a0223d1c2e72372d9f3dc65fc5f771..5b7ccfb342d0a54bfd6f2cdc53c7d31201ed5989 100644
---- a/ipaserver/plugins/serverrole.py
-+++ b/ipaserver/plugins/serverrole.py
-@@ -5,7 +5,7 @@
- from ipalib.crud import Retrieve, Search
- from ipalib.errors import NotFound
- from ipalib.frontend import Object
--from ipalib.parameters import Int, Str, StrEnum
-+from ipalib.parameters import Flag, Int, Str, StrEnum
- from ipalib.plugable import Registry
- from ipalib import _, ngettext
- 
-@@ -129,6 +129,10 @@ class server_role_find(Search):
-             minvalue=0,
-             autofill=False,
-         ),
-+        Flag(
-+            'include_master',
-+            doc=_('Include IPA master entries'),
-+        )
-     )
- 
-     def execute(self, *keys, **options):
-@@ -151,8 +155,16 @@ class server_role_find(Search):
-             role_servrole=role_name,
-             status=status)
- 
--        result = [
--            r for r in role_status if r[u'role_servrole'] != "IPA master"]
-+        # Don't display "IPA master" information unless the role is
-+        # requested explicitly. All servers are considered IPA masters,
-+        # except for replicas during installation.
-+        if options.get('include_master') or role_name == "IPA master":
-+            result = role_status
-+        else:
-+            result = [
-+                r for r in role_status
-+                if r[u'role_servrole'] != "IPA master"
-+            ]
-         return dict(
-             result=result,
-             count=len(result),
--- 
-2.17.1
-
diff --git a/SOURCES/0011-trusts-add-support-for-one-way-shared-secret-trust.patch b/SOURCES/0011-trusts-add-support-for-one-way-shared-secret-trust.patch
new file mode 100644
index 0000000..9e03c88
--- /dev/null
+++ b/SOURCES/0011-trusts-add-support-for-one-way-shared-secret-trust.patch
@@ -0,0 +1,416 @@
+From 4be3de451c8b2a6314c29df43e5ade17f39d8777 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Fri, 22 Mar 2019 18:56:52 +0200
+Subject: [PATCH] trusts: add support for one-way shared secret trust
+
+Refactor ipa-sam code to generate principals with additional POSIX
+information so that FreeIPA is capable to establish trust when using a
+shared secret from Active Directory domain controller side.
+
+Trust verification process from Samba AD DC or Microsoft Windows AD DC
+side requires us to have a working local TDO object with POSIX
+attributes so that smbd would be able to map incoming authenticated
+Kerberos principal for the TDO to a local POSIX account.
+
+Note that FreeIPA stores TDO objects in a subtree of cn=trusts,$SUFFIX
+and thus SSSD is not able to see these POSIX accounts unless
+specifically instructed to do so via multiple search bases. The support
+for automatically enabling cn=trusts,$SUFFIX search base in IPA server
+mode was added to SSSD 1.16.3 and 2.1.0 with the commit
+https://pagure.io/SSSD/sssd/c/14faec9cd9437ef116ae054412d25ec2e820e409
+
+Fixes: https://pagure.io/freeipa/issue/6077
+(cherry picked from commit f30f7e380ef9d327ced3e1b0e5c800a8b1069097)
+
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ daemons/ipa-sam/ipa_sam.c | 232 +++++++++++++++++++++++++++++---------
+ 1 file changed, 179 insertions(+), 53 deletions(-)
+
+diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c
+index 675a511f0febf13cc5e00b547c18a050ac534f2e..3cf878c3f99774f7715f776c31d70e2950f9451c 100644
+--- a/daemons/ipa-sam/ipa_sam.c
++++ b/daemons/ipa-sam/ipa_sam.c
+@@ -140,6 +140,7 @@ bool E_md4hash(const char *passwd, uint8_t p16[16]); /* available in libcliauth-
+ #define LDAP_ATTRIBUTE_OBJECTCLASS "objectClass"
+ #define LDAP_ATTRIBUTE_HOME_DRIVE "ipaNTHomeDirectoryDrive"
+ #define LDAP_ATTRIBUTE_HOME_PATH "ipaNTHomeDirectory"
++#define LDAP_ATTRIBUTE_HOMEDIRECTORY "homeDirectory"
+ #define LDAP_ATTRIBUTE_LOGON_SCRIPT "ipaNTLogonScript"
+ #define LDAP_ATTRIBUTE_PROFILE_PATH "ipaNTProfilePath"
+ #define LDAP_ATTRIBUTE_SID_BLACKLIST_INCOMING "ipaNTSIDBlacklistIncoming"
+@@ -1797,9 +1798,10 @@ done:
+ #define KRB_PRINC_CREATE_DISABLED           0x00000001
+ #define KRB_PRINC_CREATE_AGENT_PERMISSION   0x00000002
+ 
++
+ static bool set_krb_princ(struct ipasam_private *ipasam_state,
+ 			  TALLOC_CTX *mem_ctx,
+-			  const char *princ, const char *saltprinc,
++			  const char *princ, const char *alias,
+ 			  const char *pwd,
+ 			  const char *base_dn,
+ 			  uint32_t   create_flags)
+@@ -1857,14 +1859,15 @@ static bool set_krb_princ(struct ipasam_private *ipasam_state,
+ 			 LDAP_ATTRIBUTE_KRB_CANONICAL, princ);
+ 	smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ 			 LDAP_ATTRIBUTE_KRB_PRINCIPAL, princ);
+-        if (saltprinc) {
+-	    smbldap_set_mod(&mods, LDAP_MOD_ADD,
+-			    LDAP_ATTRIBUTE_KRB_PRINCIPAL, saltprinc);
+-        }
++	if (alias) {
++		smbldap_set_mod(&mods, LDAP_MOD_ADD,
++				LDAP_ATTRIBUTE_KRB_PRINCIPAL, alias);
++	}
+ 
+ 	if ((create_flags & KRB_PRINC_CREATE_DISABLED)) {
+-		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
+-				LDAP_ATTRIBUTE_KRB_TICKET_FLAGS, __TALLOC_STRING_LINE2__(IPASAM_DISALLOW_ALL_TIX));
++		smbldap_set_mod(&mods, LDAP_MOD_ADD,
++				LDAP_ATTRIBUTE_KRB_TICKET_FLAGS,
++				__TALLOC_STRING_LINE2__(IPASAM_DISALLOW_ALL_TIX));
+ 	}
+ 
+ 	if ((create_flags & KRB_PRINC_CREATE_AGENT_PERMISSION)) {
+@@ -1877,18 +1880,19 @@ static bool set_krb_princ(struct ipasam_private *ipasam_state,
+ 		smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ 				LDAP_ATTRIBUTE_OBJECTCLASS,
+ 				LDAP_OBJ_IPAOPALLOW);
+-		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
+-				LDAP_ATTRIBUTE_IPAOPALLOW, agent_dn);
++		smbldap_set_mod(&mods, LDAP_MOD_ADD,
++				LDAP_ATTRIBUTE_IPAOPALLOW,
++				agent_dn);
+ 		agent_dn = talloc_asprintf(mem_ctx, LDAP_CN_ADTRUST_ADMINS",%s", ipasam_state->base_dn);
+ 		if (agent_dn == NULL) {
+ 			DEBUG(1, ("error configuring cross realm principal data for trust admins!\n"));
+ 			return false;
+ 		}
+-		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
+-				LDAP_ATTRIBUTE_IPAOPALLOW, agent_dn);
++		smbldap_set_mod(&mods, LDAP_MOD_ADD,
++				LDAP_ATTRIBUTE_IPAOPALLOW,
++				agent_dn);
+ 	}
+ 
+-
+ 	if (entry == NULL) {
+ 		ret = smbldap_add(ipasam_state->ldap_state, dn, mods);
+ 	} else {
+@@ -1899,7 +1903,7 @@ static bool set_krb_princ(struct ipasam_private *ipasam_state,
+ 		return false;
+ 	}
+ 
+-	ret = set_cross_realm_pw(ipasam_state, saltprinc ? saltprinc : princ, pwd);
++	ret = set_cross_realm_pw(ipasam_state, princ, pwd);
+ 	if (ret != 0) {
+ 		DEBUG(1, ("set_cross_realm_pw failed.\n"));
+ 		return false;
+@@ -1941,18 +1945,21 @@ enum princ_mod {
+ };
+ 
+ static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
+-				      const char *domain, const char *pwd,
++				      const char *domain, const char *flat_name,
++				      const char *pwd_incoming,
++				      const char *pwd_outgoing,
+ 				      uint32_t trust_direction,
+ 				      enum princ_mod mod)
+ {
+ 	char *trusted_dn;
+ 	char *princ_l;
+ 	char *princ_r;
+-	char *princ_tdo;
+-	char *saltprinc_tdo;
++	char *princ_r_tdo, *princ_l_tdo;
+ 	char *remote_realm;
+ 	bool ok;
++        int failed = 0;
+ 	TALLOC_CTX *tmp_ctx;
++	const char *r_tdo_alias, *l_tdo_alias;
+ 
+ 	tmp_ctx = talloc_new(NULL);
+ 	if (tmp_ctx == NULL) {
+@@ -1967,46 +1974,111 @@ static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
+ 
+ 	trusted_dn = trusted_domain_dn(tmp_ctx, ipasam_state, domain);
+ 
+-	princ_l = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", remote_realm,
+-			ipasam_state->realm);
+-	princ_r = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
+-			ipasam_state->realm, remote_realm);
++	princ_l = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
++				  remote_realm, ipasam_state->realm);
++	princ_l_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
++				      flat_name, ipasam_state->realm);
++	l_tdo_alias = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
++				      flat_name, ipasam_state->realm);
+ 
+-	princ_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
+-			ipasam_state->flat_name, remote_realm);
++	princ_r = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
++				  ipasam_state->realm, remote_realm);
++	princ_r_tdo = talloc_asprintf(tmp_ctx, "%s$@%s",
++				      ipasam_state->flat_name, remote_realm);
+ 
+-	saltprinc_tdo = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
++	r_tdo_alias = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s",
+ 			ipasam_state->flat_name, remote_realm);
+ 
+-	if (trusted_dn == NULL || princ_l == NULL ||
+-	    princ_r == NULL || princ_tdo == NULL || saltprinc_tdo == NULL) {
++	if (trusted_dn == NULL || princ_l == NULL || princ_l_tdo == NULL ||
++		l_tdo_alias == NULL || princ_r == NULL || princ_r_tdo == NULL ||
++		r_tdo_alias == NULL) {
+ 		ok = false;
+ 		goto done;
+ 	}
+ 
+ 	switch (mod) {
+ 		case SET_PRINC:
+-			/* Create Kerberos principal for inbound trust, enabled by default */
+-			ok   = set_krb_princ(ipasam_state, tmp_ctx, princ_r, NULL, pwd, trusted_dn, KRB_PRINC_CREATE_DEFAULT);
+-			/* Create Kerberos principal corresponding to TDO in AD for SSSD usage, disabled by default */
+-			ok |= set_krb_princ(ipasam_state, tmp_ctx, princ_tdo, saltprinc_tdo, pwd, trusted_dn,
+-					    KRB_PRINC_CREATE_DISABLED | KRB_PRINC_CREATE_AGENT_PERMISSION);
+-			if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
+-				/* Create Kerberos principal for outbound trust, enabled by default */
+-				ok |= set_krb_princ(ipasam_state, tmp_ctx, princ_l, NULL, pwd, trusted_dn, KRB_PRINC_CREATE_DEFAULT);
++			/* We must use two sets by two principals here because
++			 * they are used for different needs and must have
++			 * different salts */
++
++			failed = 0;
++			/* INBOUND TRUST */
++			if ((trust_direction & LSA_TRUST_DIRECTION_INBOUND) != 0) {
++				/* First: krbtgt/<OUR REALM>@<REMOTE REALM>, enabled by default
++				 * in case of the inboud trust */
++				failed += !set_krb_princ(ipasam_state, tmp_ctx, princ_r, NULL,
++							 pwd_outgoing, trusted_dn,
++							 KRB_PRINC_CREATE_DEFAULT);
++
++				/* Second: <OUR FLATNAME$>@<REMOTE REALM> is only used
++				 * for SSSD to be able to talk to AD DCs but it has to
++				 * have canonical name set to <OUR FLATNAME>$ because
++				 * this is the salt used by AD DCs when using this
++				 * principal, otherwise authentication will fail.
++				 *
++				 * *disable* use of this principal on our side as it is
++				 * only used to retrieve trusted domain credentials by
++				 * AD Trust Agents across the IPA topology */
++				failed += !set_krb_princ(ipasam_state, tmp_ctx,
++							 r_tdo_alias, princ_r_tdo,
++							 pwd_incoming, trusted_dn,
++							 (KRB_PRINC_CREATE_DISABLED |
++							  KRB_PRINC_CREATE_AGENT_PERMISSION));
++
++	                        ok = (failed == 0);
++				if (!ok) {
++					goto done;
++				}
+ 			}
+-			if (!ok) {
+-				goto done;
++
++			failed = 0;
++			/* OUTBOUND TRUST */
++			if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
++				/* First: krbtgt/<REMOTE REALM>@<OUR REALM>, enabled by default */
++				failed += !set_krb_princ(ipasam_state, tmp_ctx,
++							 princ_l, NULL,
++							 pwd_outgoing, trusted_dn,
++							 KRB_PRINC_CREATE_DEFAULT);
++
++				/* Second: <REMOTE FLAT NAME>$@<OUR REALM>, enabled by default
++				 * as it is used for a remote DC to authenticate against IPA Samba
++				 *
++				 * A local account for the outbound trust must have
++				 * POSIX and SMB identities associated with our domain but we associate
++				 * them with the trust domain object itself */
++				failed += !set_krb_princ(ipasam_state, tmp_ctx,
++							 princ_l_tdo, l_tdo_alias,
++							 pwd_incoming, trusted_dn,
++							 KRB_PRINC_CREATE_DEFAULT);
++
++	                        ok = (failed == 0);
++				if (!ok) {
++					goto done;
++				}
+ 			}
+ 			break;
+ 		case DEL_PRINC:
+-			ok  = del_krb_princ(ipasam_state, tmp_ctx, princ_r, trusted_dn);
+-			ok |= del_krb_princ(ipasam_state, tmp_ctx, princ_tdo, trusted_dn);
+-			if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
+-				ok |= del_krb_princ(ipasam_state, tmp_ctx, princ_l, trusted_dn);
++			failed = 0;
++			if ((trust_direction & LSA_TRUST_DIRECTION_INBOUND) != 0) {
++				failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_r, trusted_dn);
++				failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_r_tdo, trusted_dn);
++
++	                        ok = (failed == 0);
++				if (!ok) {
++					goto done;
++				}
+ 			}
+-			if (!ok) {
+-				goto done;
++
++			failed = 0;
++			if ((trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) != 0) {
++				failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_l, trusted_dn);
++				failed += !del_krb_princ(ipasam_state, tmp_ctx, princ_l_tdo, trusted_dn);
++
++	                        ok = (failed == 0);
++				if (!ok) {
++					goto done;
++				}
+ 			}
+ 			break;
+ 		default:
+@@ -2022,16 +2094,22 @@ done:
+ }
+ 
+ static bool set_cross_realm_princs(struct ipasam_private *ipasam_state,
+-				   const char *domain, const char *pwd, uint32_t trust_direction)
++				   const char *domain, const char* flat_name,
++				   const char *pwd_incoming, const char *pwd_outgoing,
++				   uint32_t trust_direction)
+ {
+-	return handle_cross_realm_princs(ipasam_state, domain, pwd, trust_direction, SET_PRINC);
++	return handle_cross_realm_princs(ipasam_state, domain, flat_name,
++					 pwd_incoming,
++					 pwd_outgoing,
++					 trust_direction, SET_PRINC);
+ }
+ 
+ static bool del_cross_realm_princs(struct ipasam_private *ipasam_state,
+-				   const char *domain)
++				   const char *domain, const char *flat_name)
+ {
+ 	uint32_t trust_direction = LSA_TRUST_DIRECTION_INBOUND | LSA_TRUST_DIRECTION_OUTBOUND;
+-	return handle_cross_realm_princs(ipasam_state, domain, NULL, trust_direction, DEL_PRINC);
++	return handle_cross_realm_princs(ipasam_state, domain, flat_name,
++					 NULL, NULL, trust_direction, DEL_PRINC);
+ }
+ 
+ static bool get_trusted_domain_int(struct ipasam_private *ipasam_state,
+@@ -2439,8 +2517,8 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
+ 	int ret, i, count;
+ 	NTSTATUS status;
+ 	TALLOC_CTX *tmp_ctx;
+-	char *trustpw;
+-	char *sid;
++	char *trustpw_incoming, *trustpw_outgoing;
++	char *sid, *tda_name;
+ 	char **in_blacklist = NULL;
+ 	char **out_blacklist = NULL;
+ 	uint32_t enctypes, trust_offset;
+@@ -2465,6 +2543,8 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
+ 				 LDAP_OBJ_TRUSTED_DOMAIN);
+ 		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods, "objectClass",
+ 				 LDAP_OBJ_ID_OBJECT);
++		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods, "objectClass",
++				 LDAP_OBJ_POSIXACCOUNT);
+ 	}
+ 
+ 	if (entry != NULL) {
+@@ -2477,12 +2557,23 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
+ 		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
+ 		                 LDAP_ATTRIBUTE_GIDNUMBER,
+ 				 ipasam_state->fallback_primary_group_gid_str);
++		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
++		                 LDAP_ATTRIBUTE_HOMEDIRECTORY,
++				 "/dev/null");
+ 	}
+ 
+ 	if (td->netbios_name != NULL) {
++		tda_name = talloc_asprintf(tmp_ctx, "%s$", td->netbios_name);
++		if (!tda_name) {
++			status = NT_STATUS_UNSUCCESSFUL;
++			goto done;
++		}
+ 		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
+ 				 LDAP_ATTRIBUTE_FLAT_NAME,
+ 				 td->netbios_name);
++		smbldap_make_mod(priv2ld(ipasam_state), entry, &mods,
++				 LDAP_ATTRIBUTE_UID,
++				 tda_name);
+ 	}
+ 
+ 	if (td->domain_name != NULL) {
+@@ -2618,13 +2709,38 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
+ 
+ 	if (entry == NULL) { /* FIXME: allow password updates here */
+ 		status = get_trust_pwd(tmp_ctx, &td->trust_auth_incoming,
+-				       &trustpw, NULL);
++				       &trustpw_incoming, NULL);
+ 		if (!NT_STATUS_IS_OK(status)) {
+ 			goto done;
+ 		}
+-		res = set_cross_realm_princs(ipasam_state, td->domain_name,
+-					     trustpw, td->trust_direction);
+-		memset(trustpw, 0, strlen(trustpw));
++		status = get_trust_pwd(tmp_ctx, &td->trust_auth_outgoing,
++				       &trustpw_outgoing, NULL);
++		if (!NT_STATUS_IS_OK(status)) {
++			goto done;
++		}
++		res = set_cross_realm_princs(ipasam_state, td->domain_name, td->netbios_name,
++					     trustpw_incoming, trustpw_outgoing,
++					     td->trust_direction);
++		{
++			/* Replace memset() use by an explicit loop to avoid
++			 * both compile time and link time optimisations.
++			 * We could have used memset_s() from C++11 but it is
++			 * currently not implemented by GCC or glibc.
++			 */
++			volatile char *p = (void *) trustpw_incoming;
++			volatile char *q = (void *) trustpw_outgoing;
++			size_t plen = strlen(trustpw_incoming);
++			size_t qlen = strlen(trustpw_outgoing);
++
++			while (plen--) {
++				*p++ = '\0';
++			}
++
++			while (qlen--) {
++				*q++ = '\0';
++			}
++		}
++
+ 		if (!res) {
+ 			DEBUG(1, ("error writing cross realm principals!\n"));
+ 			status = NT_STATUS_UNSUCCESSFUL;
+@@ -2693,7 +2809,7 @@ static NTSTATUS ipasam_del_trusted_domain(struct pdb_methods *methods,
+ 		talloc_get_type_abort(methods->private_data, struct ipasam_private);
+ 	LDAPMessage *entry = NULL;
+ 	char *dn;
+-	const char *domain_name;
++	const char *domain_name, *flat_name;
+ 	TALLOC_CTX *tmp_ctx;
+ 	NTSTATUS status;
+ 
+@@ -2731,7 +2847,17 @@ static NTSTATUS ipasam_del_trusted_domain(struct pdb_methods *methods,
+ 		goto done;
+ 	}
+ 
+-	if (!del_cross_realm_princs(ipasam_state, domain_name)) {
++	flat_name = get_single_attribute(tmp_ctx, priv2ld(ipasam_state), entry,
++					 LDAP_ATTRIBUTE_FLAT_NAME);
++	if (flat_name == NULL) {
++		DEBUG(1, ("Attribute %s not present.\n",
++			  LDAP_ATTRIBUTE_FLAT_NAME));
++		status = NT_STATUS_INVALID_PARAMETER;
++		goto done;
++	}
++
++
++	if (!del_cross_realm_princs(ipasam_state, domain_name, flat_name)) {
+ 		DEBUG(1, ("error deleting cross realm principals!\n"));
+ 		status = NT_STATUS_UNSUCCESSFUL;
+ 		goto done;
+-- 
+2.20.1
+
diff --git a/SOURCES/0012-Only-create-DNS-SRV-records-for-ready-server.patch b/SOURCES/0012-Only-create-DNS-SRV-records-for-ready-server.patch
deleted file mode 100644
index 25c3bb8..0000000
--- a/SOURCES/0012-Only-create-DNS-SRV-records-for-ready-server.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From 7297060d59534cff6a703ad95c68bf20e53c14ae Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Thu, 5 Jul 2018 23:59:06 +0200
-Subject: [PATCH] Only create DNS SRV records for ready server
-
-When installing multiple replicas in parallel, one replica may create
-SRV entries for other replicas, although the replicas aren't fully
-installed yet. This may cause some services to connect to a server, that
-isn't ready to serve requests.
-
-The DNS IPASystemRecords framework now skips all servers that aren't
-ready IPA masters.
-
-See: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- ipaserver/dns_data_management.py | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py
-index bdf83de0cd76d8b571b5c2190ef8c4d63bcbf2d7..675dd481b461aa14d8adf8393a2168ac84ecac86 100644
---- a/ipaserver/dns_data_management.py
-+++ b/ipaserver/dns_data_management.py
-@@ -96,7 +96,9 @@ class IPASystemRecords(object):
-         self.servers_data = {}
- 
-         servers_result = self.api_instance.Command.server_find(
--            no_members=False)['result']
-+            no_members=False,
-+            servrole=u"IPA master",  # only active, fully installed masters
-+        )['result']
-         for s in servers_result:
-             weight, location, roles = self.__get_server_attrs(s)
-             self.servers_data[s['cn'][0]] = {
-@@ -348,7 +350,9 @@ class IPASystemRecords(object):
-         zone_obj = zone.Zone(self.domain_abs, relativize=False)
-         if servers is None:
-             servers_result = self.api_instance.Command.server_find(
--                pkey_only=True)['result']
-+                pkey_only=True,
-+                servrole=u"IPA master",  # only fully installed masters
-+            )['result']
-             servers = [s['cn'][0] for s in servers_result]
- 
-         locations_result = self.api_instance.Command.location_find()['result']
--- 
-2.17.1
-
diff --git a/SOURCES/0012-upgrade-upgrade-existing-trust-agreements-to-new-lay.patch b/SOURCES/0012-upgrade-upgrade-existing-trust-agreements-to-new-lay.patch
new file mode 100644
index 0000000..d6a0c1e
--- /dev/null
+++ b/SOURCES/0012-upgrade-upgrade-existing-trust-agreements-to-new-lay.patch
@@ -0,0 +1,443 @@
+From baf93c9bdfcb761ab469efdb2a1ba4ae8485f165 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Fri, 22 Mar 2019 19:02:16 +0200
+Subject: [PATCH] upgrade: upgrade existing trust agreements to new layout
+
+Existing trust agreements will lack required Kerberos principals and
+POSIX attributes expected to allow Active Directory domain controllers
+to query IPA master over LSA and NETLOGON RPC pipes.
+
+Upgrade code is split into two parts:
+ - upgrade trusted domain object to have proper POSIX attributes
+ - generate required Kerberos principals for AD DC communication
+
+Fixes: https://pagure.io/freeipa/issue/6077
+(cherry picked from commit 090e09df130f27fb30a986529ef0944383e668e1)
+
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipaserver/install/plugins/adtrust.py | 371 ++++++++++++++++++++++++---
+ 1 file changed, 329 insertions(+), 42 deletions(-)
+
+diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
+index 1f50bef891770c53a9086c7aa36d0ee1f088fbe6..1461e000dc855a21665eb5ea0cfe4a47df419344 100644
+--- a/ipaserver/install/plugins/adtrust.py
++++ b/ipaserver/install/plugins/adtrust.py
+@@ -1,30 +1,27 @@
+-# Authors:
+-#   Martin Kosek <mkosek@redhat.com>
+-#
+-# Copyright (C) 2012  Red Hat
+-# see file 'COPYING' for use and warranty information
+-#
+-# This program is free software; you can redistribute it and/or modify
+-# it under the terms of the GNU General Public License as published by
+-# the Free Software Foundation, either version 3 of the License, or
+-# (at your option) any later version.
+-#
+-# This program is distributed in the hope that it will be useful,
+-# but WITHOUT ANY WARRANTY; without even the implied warranty of
+-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-# GNU General Public License for more details.
+-#
+-# You should have received a copy of the GNU General Public License
+-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
++# Copyright (C) 2012-2019  FreeIPA Contributors see COPYING for license
+ 
++from __future__ import absolute_import
+ import logging
+ 
+ from ipalib import Registry, errors
+ from ipalib import Updater
+ from ipapython.dn import DN
++from ipapython import ipautil
++from ipaplatform.paths import paths
+ from ipaserver.install import sysupgrade
+ from ipaserver.install.adtrustinstance import (
+     ADTRUSTInstance, map_Guests_to_nobody)
++from ipaserver.dcerpc_common import TRUST_BIDIRECTIONAL
++
++try:
++    from samba.ndr import ndr_unpack
++    from samba.dcerpc import lsa, drsblobs
++except ImportError:
++    # If samba.ndr is not available, this machine is not provisioned
++    # for serving a trust to Active Directory. As result, it does
++    # not matter what ndr_unpack does but we save on pylint checks
++    def ndr_unpack(x):
++        raise NotImplementedError
+ 
+ logger = logging.getLogger(__name__)
+ 
+@@ -325,6 +322,28 @@ class update_sids(Updater):
+         return False, ()
+ 
+ 
++def get_gidNumber(ldap, env):
++    # Read the gidnumber of the fallback group and returns a list with it
++    dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME),
++            env.container_group,
++            env.basedn)
++
++    try:
++        entry = ldap.get_entry(dn, ['gidnumber'])
++        gidNumber = entry.get('gidnumber')
++    except errors.NotFound:
++        logger.error("%s not found",
++                     ADTRUSTInstance.FALLBACK_GROUP_NAME)
++        return None
++
++    if gidNumber is None:
++        logger.error("%s does not have a gidnumber",
++                     ADTRUSTInstance.FALLBACK_GROUP_NAME)
++        return None
++
++    return gidNumber
++
++
+ @register()
+ class update_tdo_gidnumber(Updater):
+     """
+@@ -340,43 +359,55 @@ class update_tdo_gidnumber(Updater):
+             logger.debug('AD Trusts are not enabled on this server')
+             return False, []
+ 
+-        # Read the gidnumber of the fallback group
+-        dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME),
+-                self.api.env.container_group,
+-                self.api.env.basedn)
+-
+-        try:
+-            entry = ldap.get_entry(dn, ['gidnumber'])
+-            gidNumber = entry.get('gidnumber')
+-        except errors.NotFound:
+-            logger.error("%s not found",
+-                         ADTRUSTInstance.FALLBACK_GROUP_NAME)
+-            return False, ()
+-
++        gidNumber = get_gidNumber(ldap, self.api.env)
+         if not gidNumber:
+             logger.error("%s does not have a gidnumber",
+                          ADTRUSTInstance.FALLBACK_GROUP_NAME)
+             return False, ()
+ 
+-        # For each trusted domain object, add gidNumber
++        # For each trusted domain object, add posix attributes
++        # to allow use of a trusted domain account by AD DCs
++        # to authenticate against our Samba instance
+         try:
+             tdos = ldap.get_entries(
+                 DN(self.api.env.container_adtrusts, self.api.env.basedn),
+                 scope=ldap.SCOPE_ONELEVEL,
+-                filter="(objectclass=ipaNTTrustedDomain)",
+-                attrs_list=['gidnumber'])
++                filter="(&(objectclass=ipaNTTrustedDomain)"
++                       "(objectclass=ipaIDObject))",
++                attrs_list=['gidnumber', 'uidnumber', 'objectclass',
++                            'ipantsecurityidentifier',
++                            'ipaNTTrustDirection'
++                            'uid', 'cn', 'ipantflatname'])
+             for tdo in tdos:
+                 # if the trusted domain object does not contain gidnumber,
+                 # add the default fallback group gidnumber
+                 if not tdo.get('gidnumber'):
+-                    try:
+-                        tdo['gidnumber'] = gidNumber
+-                        ldap.update_entry(tdo)
+-                        logger.debug("Added gidnumber %s to %s",
+-                                     gidNumber, tdo.dn)
+-                    except Exception:
+-                        logger.warning(
+-                            "Failed to add gidnumber to %s", tdo.dn)
++                    tdo['gidnumber'] = gidNumber
++
++                # Generate uidNumber and ipaNTSecurityIdentifier if
++                # uidNumber is missing. We rely on sidgen plugin here
++                # to generate ipaNTSecurityIdentifier.
++                if not tdo.get('uidnumber'):
++                    tdo['uidnumber'] = ['-1']
++
++                if 'posixAccount' not in tdo.get('objectclass'):
++                    tdo['objectclass'].extend(['posixAccount'])
++                # Based on the flat name of a TDO,
++                # add user name FLATNAME$ (note dollar sign)
++                # to allow SSSD to map this TDO to a POSIX account
++                if not tdo.get('uid'):
++                    tdo['uid'] = ["{flatname}$".format(
++                                  flatname=tdo.single_value['ipantflatname'])]
++                if not tdo.get('homedirectory'):
++                    tdo['homedirectory'] = ['/dev/null']
++
++                # Store resulted entry
++                try:
++                    ldap.update_entry(tdo)
++                except errors.ExecutionError as e:
++                    logger.warning(
++                        "Failed to update trusted domain object %s", tdo.dn)
++                    logger.debug("Exception during TDO update: %s", str(e))
+ 
+         except errors.NotFound:
+             logger.debug("No trusted domain object to update")
+@@ -400,3 +431,259 @@ class update_mapping_Guests_to_nobody(Updater):
+ 
+         map_Guests_to_nobody()
+         return False, []
++
++
++@register()
++class update_tdo_to_new_layout(Updater):
++    """
++    Transform trusted domain objects into a new layout
++
++    There are now two Kerberos principals per direction of trust:
++
++    INBOUND:
++     - krbtgt/<OUR REALM>@<REMOTE REALM>, enabled by default
++
++     - <OUR FLATNAME$>@<REMOTE REALM>, disabled by default on our side
++       as it is only used by SSSD to retrieve TDO creds when operating
++       as an AD Trust agent across IPA topology
++
++    OUTBOUND:
++     - krbtgt/<REMOTE REALM>@<OUR REALM>, enabled by default
++
++     - <REMOTE FLATNAME$>@<OUR REALM>, enabled by default and
++       used by remote trusted DCs to authenticate against us
++
++       This principal also has krbtgt/<REMOTE FLATNAME>@<OUR REALM> defined
++       as a Kerberos principal alias. This is due to how Kerberos
++       key salt is derived for cross-realm principals on AD side
++
++    Finally, Samba requires <REMOTE FLATNAME$> account to also possess POSIX
++    and SMB identities. We ensure this by making the trusted domain object to
++    be this account with 'uid' and 'cn' attributes being '<REMOTE FLATNAME$>'
++    and uidNumber/gidNumber generated automatically. Also, we ensure the
++    trusted domain object is given a SID.
++
++    The update to <REMOTE FLATNAME$> POSIX/SMB identities is done through
++    the update plugin update_tdo_gidnumber.
++    """
++    tgt_principal_template = "krbtgt/{remote}@{local}"
++    nbt_principal_template = "{nbt}$@{realm}"
++    trust_filter = \
++        "(&(objectClass=ipaNTTrustedDomain)(objectClass=ipaIDObject))"
++    trust_attrs = ("ipaNTFlatName", "ipaNTTrustPartner", "ipaNTTrustDirection",
++                   "cn", "ipaNTTrustAttributes", "ipaNTAdditionalSuffixes",
++                   "ipaNTTrustedDomainSID", "ipaNTTrustType",
++                   "ipaNTTrustAuthIncoming", "ipaNTTrustAuthOutgoing")
++    change_password_template = \
++        "change_password -pw {password} " \
++        "-e aes256-cts-hmac-sha1-96,aes128-cts-hmac-sha1-96 " \
++        "{principal}"
++
++    KRB_PRINC_CREATE_DEFAULT = 0x00000000
++    KRB_PRINC_CREATE_DISABLED = 0x00000001
++    KRB_PRINC_CREATE_AGENT_PERMISSION = 0x00000002
++    KRB_PRINC_CREATE_IDENTITY = 0x00000004
++    KRB_PRINC_MUST_EXIST = 0x00000008
++
++    # This is a flag for krbTicketFlags attribute
++    # to disallow creating any tickets using this principal
++    KRB_DISALLOW_ALL_TIX = 0x00000040
++
++    def retrieve_trust_password(self, packed):
++        # The structure of the trust secret is described at
++        # https://github.com/samba-team/samba/blob/master/
++        # librpc/idl/drsblobs.idl#L516-L569
++        # In our case in LDAP TDO object stores
++        # `struct trustAuthInOutBlob` that has `count` and
++        # the `current` of `AuthenticationInformationArray` struct
++        # which has own `count` and `array` of `AuthenticationInformation`
++        # structs that have `AuthType` field which should be equal to
++        # `LSA_TRUST_AUTH_TYPE_CLEAR`.
++        # Then AuthInfo field would contain a password as an array of bytes
++        assert(packed.count != 0)
++        assert(packed.current.count != 0)
++        assert(packed.current.array[0].AuthType == lsa.TRUST_AUTH_TYPE_CLEAR)
++        clear_value = packed.current.array[0].AuthInfo.password
++
++        return ''.join(map(chr, clear_value))
++
++    def set_krb_principal(self, principals, password, trustdn, flags=None):
++
++        ldap = self.api.Backend.ldap2
++
++        if isinstance(principals, (list, tuple)):
++            trust_principal = principals[0]
++            aliases = principals[1:]
++        else:
++            trust_principal = principals
++            aliases = []
++
++        try:
++            entry = ldap.get_entry(
++                DN(('krbprincipalname', trust_principal), trustdn))
++            dn = entry.dn
++            action = ldap.update_entry
++            logger.debug("Updating Kerberos principal entry for %s",
++                         trust_principal)
++        except errors.NotFound:
++            # For a principal that must exist, we re-raise the exception
++            # to let the caller to handle this situation
++            if flags & self.KRB_PRINC_MUST_EXIST:
++                raise
++
++            dn = DN(('krbprincipalname', trust_principal), trustdn)
++            entry = ldap.make_entry(dn)
++            logger.debug("Adding Kerberos principal entry for %s",
++                         trust_principal)
++            action = ldap.add_entry
++
++        entry_data = {
++            'objectclass':
++                ['krbPrincipal', 'krbPrincipalAux',
++                 'krbTicketPolicyAux', 'top'],
++            'krbcanonicalname': [trust_principal],
++            'krbprincipalname': [trust_principal],
++        }
++
++        entry_data['krbprincipalname'].extend(aliases)
++
++        if flags & self.KRB_PRINC_CREATE_DISABLED:
++            flg = int(entry.single_value.get('krbticketflags', 0))
++            entry_data['krbticketflags'] = flg | self.KRB_DISALLOW_ALL_TIX
++
++        if flags & self.KRB_PRINC_CREATE_AGENT_PERMISSION:
++            entry_data['objectclass'].extend(['ipaAllowedOperations'])
++
++        entry.update(entry_data)
++        try:
++            action(entry)
++        except errors.EmptyModlist:
++            logger.debug("No update was required for Kerberos principal %s",
++                         trust_principal)
++
++        # If entry existed, no need to set Kerberos keys on it
++        if action == ldap.update_entry:
++            logger.debug("No need to update Kerberos keys for "
++                         "existing Kerberos principal %s",
++                         trust_principal)
++            return
++
++        # Now that entry is updated, set its Kerberos keys.
++        #
++        # It would be a complication to use ipa-getkeytab LDAP extended control
++        # here because we would need to encode the request in ASN.1 sequence
++        # and we don't have the code to do so exposed in Python bindings.
++        # Instead, as we run on IPA master, we can use kadmin.local for that
++        # directly.
++        # We pass the command as a stdin to both avoid shell interpolation
++        # of the passwords and also to avoid its exposure to other processes
++        # Since we don't want to record the output, make also a redacted log
++        change_password = self.change_password_template.format(
++            password=password,
++            principal=trust_principal)
++
++        redacted = self.change_password_template.format(
++            password='<REDACTED OUT>',
++            principal=trust_principal)
++        logger.debug("Updating Kerberos keys for %s with the following "
++                     "kadmin command:\n\t%s", trust_principal, redacted)
++
++        ipautil.run([paths.KADMIN_LOCAL, "-x",
++                    "ipa-setup-override-restrictions"],
++                    stdin=change_password, skip_output=True)
++
++    def execute(self, **options):
++        # First, see if trusts are enabled on the server
++        if not self.api.Command.adtrust_is_enabled()['result']:
++            logger.debug('AD Trusts are not enabled on this server')
++            return False, []
++
++        ldap = self.api.Backend.ldap2
++        gidNumber = get_gidNumber(ldap, self.api.env)
++        if gidNumber is None:
++            return False, []
++
++        result = self.api.Command.trustconfig_show()['result']
++        our_nbt_name = result.get('ipantflatname', [None])[0]
++        if not our_nbt_name:
++            return False, []
++
++        trusts_dn = self.api.env.container_adtrusts + self.api.env.basedn
++
++        trusts = ldap.get_entries(
++            base_dn=trusts_dn,
++            scope=ldap.SCOPE_ONELEVEL,
++            filter=self.trust_filter,
++            attrs_list=self.trust_attrs)
++
++        # For every trust, retrieve its principals and convert
++        for t_entry in trusts:
++            t_dn = t_entry.dn
++            logger.debug('Processing trust domain object %s', str(t_dn))
++            t_realm = t_entry.single_value.get('ipaNTTrustPartner').upper()
++            direction = int(t_entry.single_value.get('ipaNTTrustDirection'))
++            passwd_incoming = self.retrieve_trust_password(
++                ndr_unpack(drsblobs.trustAuthInOutBlob,
++                           t_entry.single_value.get('ipaNTTrustAuthIncoming')))
++            passwd_outgoing = self.retrieve_trust_password(
++                ndr_unpack(drsblobs.trustAuthInOutBlob,
++                           t_entry.single_value.get('ipaNTTrustAuthOutgoing')))
++            # For outbound and inbound trusts, process four principals total
++            if (direction & TRUST_BIDIRECTIONAL) == TRUST_BIDIRECTIONAL:
++                # 1. OUTBOUND: krbtgt/<REMOTE REALM>@<OUR REALM> must exist
++                trust_principal = self.tgt_principal_template.format(
++                    remote=t_realm, local=self.api.env.realm)
++                try:
++                    self.set_krb_principal(trust_principal,
++                                           passwd_outgoing,
++                                           t_dn,
++                                           flags=self.KRB_PRINC_CREATE_DEFAULT)
++                except errors.NotFound:
++                    # It makes no sense to convert this one, skip the trust
++                    # completely, better to re-establish one
++                    logger.error(
++                        "Broken trust to AD: %s not found, "
++                        "please re-establish the trust to %s",
++                        trust_principal, t_realm)
++                    continue
++
++                # 2. Create <REMOTE FLATNAME$>@<OUR REALM>
++                nbt_name = t_entry.single_value.get('ipaNTFlatName')
++                nbt_principal = self.nbt_principal_template.format(
++                    nbt=nbt_name, realm=self.api.env.realm)
++                tgt_principal = self.tgt_principal_template.format(
++                    remote=nbt_name, local=self.api.env.realm)
++                self.set_krb_principal([nbt_principal, tgt_principal],
++                                       passwd_incoming,
++                                       t_dn,
++                                       flags=self.KRB_PRINC_CREATE_DEFAULT)
++
++            # 3. INBOUND: krbtgt/<OUR REALM>@<REMOTE REALM> must exist
++            trust_principal = self.tgt_principal_template.format(
++                remote=self.api.env.realm, local=t_realm)
++            try:
++                self.set_krb_principal(trust_principal, passwd_outgoing,
++                                       t_dn,
++                                       flags=self.KRB_PRINC_CREATE_DEFAULT)
++            except errors.NotFound:
++                # It makes no sense to convert this one, skip the trust
++                # completely, better to re-establish one
++                logger.error(
++                    "Broken trust to AD: %s not found, "
++                    "please re-establish the trust to %s",
++                    trust_principal, t_realm)
++                continue
++
++            # 4. Create <OUR FLATNAME$>@<REMOTE REALM>, disabled
++            nbt_principal = self.nbt_principal_template.format(
++                nbt=our_nbt_name, realm=t_realm)
++            tgt_principal = self.tgt_principal_template.format(
++                remote=our_nbt_name, local=t_realm)
++            self.set_krb_principal([nbt_principal, tgt_principal],
++                                   passwd_incoming,
++                                   t_dn,
++                                   flags=self.KRB_PRINC_CREATE_DEFAULT |
++                                   self.KRB_PRINC_CREATE_AGENT_PERMISSION |
++                                   self.KRB_PRINC_CREATE_DISABLED)
++
++        return False, []
+-- 
+2.20.1
+
diff --git a/SOURCES/0013-Delay-enabling-services-until-end-of-installer.patch b/SOURCES/0013-Delay-enabling-services-until-end-of-installer.patch
deleted file mode 100644
index b44176e..0000000
--- a/SOURCES/0013-Delay-enabling-services-until-end-of-installer.patch
+++ /dev/null
@@ -1,601 +0,0 @@
-From 4caf0c14ad38a1dae494489b13f4109cdb3ba340 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 6 Jul 2018 00:04:39 +0200
-Subject: [PATCH] Delay enabling services until end of installer
-
-Service entries in cn=FQDN,cn=masters,cn=ipa,cn=etc are no longer
-created as enabled. Instead they are flagged as configuredService. At
-the very end of the installer, the service entries are switched from
-configured to enabled service.
-
-- SRV records are created at the very end of the installer.
-- Dogtag installer only picks fully installed servers
-- Certmonger ignores all configured but not yet enabled servers.
-
-Fixes: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- install/tools/ipa-adtrust-install          |  6 +-
- install/tools/ipa-ca-install               | 12 ++-
- install/tools/ipa-dns-install              |  4 +
- ipaserver/dns_data_management.py           | 18 +++--
- ipaserver/install/adtrustinstance.py       |  6 +-
- ipaserver/install/bindinstance.py          |  8 +-
- ipaserver/install/cainstance.py            |  2 +-
- ipaserver/install/dnskeysyncinstance.py    |  4 +-
- ipaserver/install/httpinstance.py          |  4 +-
- ipaserver/install/ipa_kra_install.py       |  7 ++
- ipaserver/install/krainstance.py           |  2 +-
- ipaserver/install/krbinstance.py           |  4 +-
- ipaserver/install/odsexporterinstance.py   |  4 +-
- ipaserver/install/opendnssecinstance.py    |  6 +-
- ipaserver/install/server/install.py        | 18 +++--
- ipaserver/install/server/replicainstall.py |  8 +-
- ipaserver/install/service.py               | 88 ++++++++++++++++++++--
- ipaserver/plugins/serverrole.py            |  7 +-
- 18 files changed, 161 insertions(+), 47 deletions(-)
-
-diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
-index d4e5d4c09cf6b7c1521bcecb79bb6fd7235fc799..a870d136e242affe6627cd4c44a173a80a9ab1c6 100755
---- a/install/tools/ipa-adtrust-install
-+++ b/install/tools/ipa-adtrust-install
-@@ -32,7 +32,7 @@ import six
- from optparse import SUPPRESS_HELP  # pylint: disable=deprecated-module
- 
- from ipalib.install import sysrestore
--from ipaserver.install import adtrust
-+from ipaserver.install import adtrust, service
- from ipaserver.install.installutils import (
-     read_password,
-     check_server_configuration,
-@@ -212,6 +212,10 @@ def main():
-     adtrust.install_check(True, options, api)
-     adtrust.install(True, options, fstore, api)
- 
-+    # Enable configured services and update DNS SRV records
-+    service.enable_services(api.env.host)
-+    api.Command.dns_update_system_records()
-+
-     print("""
- =============================================================================
- Setup complete
-diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
-index e4e24fb8c5261e77dd9d3e89cbe42dba519b932e..f78f43d94981d29939a247f3c492c5e7340298ea 100755
---- a/install/tools/ipa-ca-install
-+++ b/install/tools/ipa-ca-install
-@@ -332,18 +332,26 @@ def main():
-     )
-     api.finalize()
-     api.Backend.ldap2.connect()
--
-     domain_level = dsinstance.get_domain_level(api)
-+
-     if domain_level > DOMAIN_LEVEL_0:
-         promote(safe_options, options, filename)
-     else:
-         install(safe_options, options, filename)
- 
-+    # pki-spawn restarts 389-DS, reconnect
-+    api.Backend.ldap2.close()
-+    api.Backend.ldap2.connect()
-+
-+    # Enable configured services and update DNS SRV records
-+    service.enable_services(api.env.host)
-+    api.Command.dns_update_system_records()
-+    api.Backend.ldap2.disconnect()
-+
-     # execute ipactl to refresh services status
-     ipautil.run(['ipactl', 'start', '--ignore-service-failures'],
-                 raiseonerr=False)
- 
--    api.Backend.ldap2.disconnect()
- 
- fail_message = '''
- Your system may be partly configured.
-diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
-index a7f136b16ab4871518f5fd776d49e55c2364c54e..57dde5a5da4fad162c93e9e0416b54961de4c1e3 100755
---- a/install/tools/ipa-dns-install
-+++ b/install/tools/ipa-dns-install
-@@ -37,6 +37,7 @@ from ipapython.config import IPAOptionParser
- from ipapython.ipa_log_manager import standard_logging_setup
- 
- from ipaserver.install import dns as dns_installer
-+from ipaserver.install import service
- 
- logger = logging.getLogger(os.path.basename(__file__))
- 
-@@ -148,6 +149,9 @@ def main():
- 
-     dns_installer.install_check(True, api, False, options, hostname=api.env.host)
-     dns_installer.install(True, False, options)
-+    # Enable configured services and update DNS SRV records
-+    service.enable_services(api.env.host)
-+    api.Command.dns_update_system_records()
- 
-     # execute ipactl to refresh services status
-     ipautil.run(['ipactl', 'start', '--ignore-service-failures'],
-diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py
-index 675dd481b461aa14d8adf8393a2168ac84ecac86..673397ef2b2252f431eec1f3e1f71dc45ff87511 100644
---- a/ipaserver/dns_data_management.py
-+++ b/ipaserver/dns_data_management.py
-@@ -68,11 +68,11 @@ class IPASystemRecords(object):
-     PRIORITY_HIGH = 0
-     PRIORITY_LOW = 50
- 
--    def __init__(self, api_instance):
-+    def __init__(self, api_instance, all_servers=False):
-         self.api_instance = api_instance
-         self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute()
-         self.servers_data = {}
--        self.__init_data()
-+        self.__init_data(all_servers=all_servers)
- 
-     def reload_data(self):
-         """
-@@ -92,14 +92,16 @@ class IPASystemRecords(object):
-     def __get_location_suffix(self, location):
-         return location + DNSName('_locations') + self.domain_abs
- 
--    def __init_data(self):
-+    def __init_data(self, all_servers=False):
-         self.servers_data = {}
- 
--        servers_result = self.api_instance.Command.server_find(
--            no_members=False,
--            servrole=u"IPA master",  # only active, fully installed masters
--        )['result']
--        for s in servers_result:
-+        kwargs = dict(no_members=False)
-+        if not all_servers:
-+            # only active, fully installed masters]
-+            kwargs["servrole"] = u"IPA master"
-+        servers = self.api_instance.Command.server_find(**kwargs)
-+
-+        for s in servers['result']:
-             weight, location, roles = self.__get_server_attrs(s)
-             self.servers_data[s['cn'][0]] = {
-                 'weight': weight,
-diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
-index a075801ebec20ea8277445e0bac788c06e0b0a91..2da46d67495014fb38e5ee8c6a98ede93ef8762d 100644
---- a/ipaserver/install/adtrustinstance.py
-+++ b/ipaserver/install/adtrustinstance.py
-@@ -581,7 +581,7 @@ class ADTRUSTInstance(service.Service):
-             self.print_msg(err_msg)
-             self.print_msg("Add the following service records to your DNS " \
-                            "server for DNS zone %s: " % zone)
--            system_records = IPASystemRecords(api)
-+            system_records = IPASystemRecords(api, all_servers=True)
-             adtrust_records = system_records.get_base_records(
-                 [self.fqdn], ["AD trust controller"],
-                 include_master_role=False, include_kerberos_realm=False)
-@@ -736,12 +736,12 @@ class ADTRUSTInstance(service.Service):
-         # Note that self.dm_password is None for ADTrustInstance because
-         # we ensure to be called as root and using ldapi to use autobind
-         try:
--            self.ldap_enable('ADTRUST', self.fqdn, None, self.suffix)
-+            self.ldap_configure('ADTRUST', self.fqdn, None, self.suffix)
-         except (ldap.ALREADY_EXISTS, errors.DuplicateEntry):
-             logger.info("ADTRUST Service startup entry already exists.")
- 
-         try:
--            self.ldap_enable('EXTID', self.fqdn, None, self.suffix)
-+            self.ldap_configure('EXTID', self.fqdn, None, self.suffix)
-         except (ldap.ALREADY_EXISTS, errors.DuplicateEntry):
-             logger.info("EXTID Service startup entry already exists.")
- 
-diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
-index 203a6405f815d47c0dc33977e77012a2c85916ff..7c858aab4417ccf3a4999fcaaa1c7e0f93464e4d 100644
---- a/ipaserver/install/bindinstance.py
-+++ b/ipaserver/install/bindinstance.py
-@@ -669,7 +669,7 @@ class BindInstance(service.Service):
-         return normalize_zone(self.host_domain) == normalize_zone(self.domain)
- 
-     def create_file_with_system_records(self):
--        system_records = IPASystemRecords(self.api)
-+        system_records = IPASystemRecords(self.api, all_servers=True)
-         text = u'\n'.join(
-             IPASystemRecords.records_list_from_zone(
-                 system_records.get_base_records()
-@@ -746,7 +746,7 @@ class BindInstance(service.Service):
-         # Instead we reply on the IPA init script to start only enabled
-         # components as found in our LDAP configuration tree
-         try:
--            self.ldap_enable('DNS', self.fqdn, None, self.suffix)
-+            self.ldap_configure('DNS', self.fqdn, None, self.suffix)
-         except errors.DuplicateEntry:
-             # service already exists (forced DNS reinstall)
-             # don't crash, just report error
-@@ -1180,7 +1180,9 @@ class BindInstance(service.Service):
-             except ValueError as error:
-                 logger.debug('%s', error)
- 
--        # disabled by default, by ldap_enable()
-+        installutils.rmtree(paths.BIND_LDAP_DNS_IPA_WORKDIR)
-+
-+        # disabled by default, by ldap_configure()
-         if enabled:
-             self.enable()
-         else:
-diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
-index b58fbb4c881d247d6b5fb661f4085ec82c3cc811..51fdbe9c61e06ab9d72d78aee8786f9bceca137b 100644
---- a/ipaserver/install/cainstance.py
-+++ b/ipaserver/install/cainstance.py
-@@ -1258,7 +1258,7 @@ class CAInstance(DogtagInstance):
-             config = ['caRenewalMaster']
-         else:
-             config = []
--        self.ldap_enable('CA', self.fqdn, None, basedn, config)
-+        self.ldap_configure('CA', self.fqdn, None, basedn, config)
- 
-     def setup_lightweight_ca_key_retrieval(self):
-         if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'):
-diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py
-index b865ee8aa79c17502a3784878f8f6f45d05213a6..2e773f3adae8130f578e6f3fbfe8c3a414d523cb 100644
---- a/ipaserver/install/dnskeysyncinstance.py
-+++ b/ipaserver/install/dnskeysyncinstance.py
-@@ -382,8 +382,8 @@ class DNSKeySyncInstance(service.Service):
- 
-     def __enable(self):
-         try:
--            self.ldap_enable('DNSKeySync', self.fqdn, None,
--                             self.suffix, self.extra_config)
-+            self.ldap_configure('DNSKeySync', self.fqdn, None,
-+                                self.suffix, self.extra_config)
-         except errors.DuplicateEntry:
-             logger.error("DNSKeySync service already exists")
- 
-diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
-index bdd79b1dafda7de664eed664a18bf36c541212bc..0b7023c2f1b0feb996e0dd0adbefbd49c51da757 100644
---- a/ipaserver/install/httpinstance.py
-+++ b/ipaserver/install/httpinstance.py
-@@ -196,7 +196,7 @@ class HTTPInstance(service.Service):
-         # We do not let the system start IPA components on its own,
-         # Instead we reply on the IPA init script to start only enabled
-         # components as found in our LDAP configuration tree
--        self.ldap_enable('HTTP', self.fqdn, None, self.suffix)
-+        self.ldap_configure('HTTP', self.fqdn, None, self.suffix)
- 
-     def configure_selinux_for_httpd(self):
-         try:
-@@ -609,7 +609,7 @@ class HTTPInstance(service.Service):
-         if running:
-             self.restart()
- 
--        # disabled by default, by ldap_enable()
-+        # disabled by default, by ldap_configure()
-         if enabled:
-             self.enable()
- 
-diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
-index 07e11ea69ded8832015dd69ea43ff338c5f9df95..b536685f5f1f3fccab07fd37aa001958e2d38420 100644
---- a/ipaserver/install/ipa_kra_install.py
-+++ b/ipaserver/install/ipa_kra_install.py
-@@ -227,4 +227,11 @@ class KRAInstaller(KRAInstall):
-             logger.error('%s', dedent(self.FAIL_MESSAGE))
-             raise
- 
-+        # pki-spawn restarts 389-DS, reconnect
-+        api.Backend.ldap2.close()
-+        api.Backend.ldap2.connect()
-+
-+        # Enable configured services and update DNS SRV records
-+        service.enable_services(api.env.host)
-+        api.Command.dns_update_system_records()
-         api.Backend.ldap2.disconnect()
-diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
-index 9483f0ec4edbabea0f7eff0dd5dd223377653536..c1daa2869b7cba79e29d2db61c090c145304397f 100644
---- a/ipaserver/install/krainstance.py
-+++ b/ipaserver/install/krainstance.py
-@@ -392,4 +392,4 @@ class KRAInstance(DogtagInstance):
-                 directives[nickname], cert)
- 
-     def __enable_instance(self):
--        self.ldap_enable('KRA', self.fqdn, None, self.suffix)
-+        self.ldap_configure('KRA', self.fqdn, None, self.suffix)
-diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
-index a356d5e0c1b96dc6511c335fc22a326a2133bdd8..33d66fb94b0a1f7571b22120e5159a0e0ad2e675 100644
---- a/ipaserver/install/krbinstance.py
-+++ b/ipaserver/install/krbinstance.py
-@@ -242,7 +242,7 @@ class KrbInstance(service.Service):
-         # We do not let the system start IPA components on its own,
-         # Instead we reply on the IPA init script to start only enabled
-         # components as found in our LDAP configuration tree
--        self.ldap_enable('KDC', self.fqdn, None, self.suffix)
-+        self.ldap_configure('KDC', self.fqdn, None, self.suffix)
- 
-     def __start_instance(self):
-         try:
-@@ -607,7 +607,7 @@ class KrbInstance(service.Service):
-             except ValueError as error:
-                 logger.debug("%s", error)
- 
--        # disabled by default, by ldap_enable()
-+        # disabled by default, by ldap_configure()
-         if enabled:
-             self.enable()
- 
-diff --git a/ipaserver/install/odsexporterinstance.py b/ipaserver/install/odsexporterinstance.py
-index b301a167f80d171c0dd0e6282a6021fcbdca8f9e..4856f5642d8e2c4c9e9089cd44a85e759c4c6ca0 100644
---- a/ipaserver/install/odsexporterinstance.py
-+++ b/ipaserver/install/odsexporterinstance.py
-@@ -73,8 +73,8 @@ class ODSExporterInstance(service.Service):
-     def __enable(self):
- 
-         try:
--            self.ldap_enable('DNSKeyExporter', self.fqdn, None,
--                             self.suffix)
-+            self.ldap_configure('DNSKeyExporter', self.fqdn, None,
-+                                self.suffix)
-         except errors.DuplicateEntry:
-             logger.error("DNSKeyExporter service already exists")
- 
-diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
-index d608294cbbab9179d95b2333323f5d378940a936..0337bb22fea44f95ee9077423136353a991325db 100644
---- a/ipaserver/install/opendnssecinstance.py
-+++ b/ipaserver/install/opendnssecinstance.py
-@@ -140,8 +140,8 @@ class OpenDNSSECInstance(service.Service):
- 
-     def __enable(self):
-         try:
--            self.ldap_enable('DNSSEC', self.fqdn, None,
--                             self.suffix, self.extra_config)
-+            self.ldap_configure('DNSSEC', self.fqdn, None,
-+                                self.suffix, self.extra_config)
-         except errors.DuplicateEntry:
-             logger.error("DNSSEC service already exists")
- 
-@@ -372,7 +372,7 @@ class OpenDNSSECInstance(service.Service):
- 
-         self.restore_state("kasp_db_configured")  # just eat state
- 
--        # disabled by default, by ldap_enable()
-+        # disabled by default, by ldap_configure()
-         if enabled:
-             self.enable()
- 
-diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
-index e96ae97c74ee1598683d1ef3f2570e8de93c9943..a341408f78f24055d807ae49c8a0cda81bfb3ec4 100644
---- a/ipaserver/install/server/install.py
-+++ b/ipaserver/install/server/install.py
-@@ -870,14 +870,6 @@ def install(installer):
- 
-     if options.setup_dns:
-         dns.install(False, False, options)
--    else:
--        # Create a BIND instance
--        bind = bindinstance.BindInstance(fstore)
--        bind.setup(host_name, ip_addresses, realm_name,
--                   domain_name, (), 'first', (),
--                   zonemgr=options.zonemgr,
--                   no_dnssec_validation=options.no_dnssec_validation)
--        bind.create_file_with_system_records()
- 
-     if options.setup_adtrust:
-         adtrust.install(False, options, fstore, api)
-@@ -906,6 +898,16 @@ def install(installer):
-     except Exception:
-         raise ScriptError("Configuration of client side components failed!")
- 
-+    # Enable configured services and update DNS SRV records
-+    service.enable_services(host_name)
-+    api.Command.dns_update_system_records()
-+
-+    if not options.setup_dns:
-+        # After DNS and AD trust are configured and services are
-+        # enabled, create a dummy instance to dump DNS configuration.
-+        bind = bindinstance.BindInstance(fstore)
-+        bind.create_file_with_system_records()
-+
-     # Everything installed properly, activate ipa service.
-     services.knownservices.ipa.enable()
- 
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 33f3ae9e616b34a3ab0ff8e4257552855e817e7c..0bf3568a300a133fa505dc8fc339c6677f9c5f73 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -1520,14 +1520,11 @@ def install(installer):
- 
-     if options.setup_dns:
-         dns.install(False, True, options, api)
--    else:
--        api.Command.dns_update_system_records()
- 
-     if options.setup_adtrust:
-         adtrust.install(False, options, fstore, api)
- 
-     ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api)
--    api.Backend.ldap2.disconnect()
- 
-     if not promote:
-         # Call client install script
-@@ -1556,6 +1553,11 @@ def install(installer):
-         # remove the extracted replica file
-         remove_replica_info_dir(installer)
- 
-+    # Enable configured services and update DNS SRV records
-+    service.enable_services(config.host_name)
-+    api.Command.dns_update_system_records()
-+    api.Backend.ldap2.disconnect()
-+
-     # Everything installed properly, activate ipa service.
-     services.knownservices.ipa.enable()
- 
-diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
-index 4c320de9d64676373cd41ff839889f2448a19a46..0106379ea38e4a3fef8436256d6f315f524b8dee 100644
---- a/ipaserver/install/service.py
-+++ b/ipaserver/install/service.py
-@@ -27,6 +27,7 @@ import socket
- import datetime
- import traceback
- import tempfile
-+import warnings
- 
- import six
- 
-@@ -62,6 +63,10 @@ SERVICE_LIST = {
-     'DNSKeySync': ('ipa-dnskeysyncd', 110),
- }
- 
-+CONFIGURED_SERVICE = u'configuredService'
-+ENABLED_SERVICE = 'enabledService'
-+
-+
- def print_msg(message, output_fd=sys.stdout):
-     logger.debug("%s", message)
-     output_fd.write(message)
-@@ -123,7 +128,7 @@ def find_providing_servers(svcname, conn, api):
-     """
-     dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
-     query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
--                                     'ipaConfigString': 'enabledService',
-+                                     'ipaConfigString': ENABLED_SERVICE,
-                                      'cn': svcname}, rules='&')
-     try:
-         entries, _trunc = conn.find_entries(filter=query_filter, base_dn=dn)
-@@ -232,6 +237,51 @@ def set_service_entry_config(name, fqdn, config_values,
-         raise e
- 
- 
-+def enable_services(fqdn):
-+    """Change all configured services to enabled
-+
-+    Server.ldap_configure() only marks a service as configured. Services
-+    are enabled at the very end of installation.
-+
-+    Note: DNS records must be updated with dns_update_system_records, too.
-+
-+    :param fqdn: hostname of server
-+    """
-+    ldap2 = api.Backend.ldap2
-+    search_base = DN(('cn', fqdn), api.env.container_masters, api.env.basedn)
-+    search_filter = ldap2.make_filter(
-+        {
-+            'objectClass': 'ipaConfigObject',
-+            'ipaConfigString': CONFIGURED_SERVICE
-+        },
-+        rules='&'
-+    )
-+    entries = ldap2.get_entries(
-+        search_base,
-+        filter=search_filter,
-+        scope=api.Backend.ldap2.SCOPE_ONELEVEL,
-+        attrs_list=['cn', 'ipaConfigString']
-+    )
-+    for entry in entries:
-+        name = entry['cn']
-+        cfgstrings = entry.setdefault('ipaConfigString', [])
-+        for value in list(cfgstrings):
-+            if value.lower() == CONFIGURED_SERVICE.lower():
-+                cfgstrings.remove(value)
-+        if not case_insensitive_attr_has_value(cfgstrings, ENABLED_SERVICE):
-+            cfgstrings.append(ENABLED_SERVICE)
-+
-+        try:
-+            ldap2.update_entry(entry)
-+        except errors.EmptyModlist:
-+            logger.debug("Nothing to do for service %s", name)
-+        except Exception:
-+            logger.exception("failed to set service %s config values", name)
-+            raise
-+        else:
-+            logger.debug("Enabled service %s for %s", name, fqdn)
-+
-+
- class Service(object):
-     def __init__(self, service_name, service_desc=None, sstore=None,
-                  fstore=None, api=api, realm_name=None,
-@@ -538,7 +588,35 @@ class Service(object):
-         self.steps = []
- 
-     def ldap_enable(self, name, fqdn, dm_password=None, ldap_suffix='',
--                    config=[]):
-+                    config=()):
-+        """Legacy function, all services should use ldap_configure()
-+        """
-+        warnings.warn(
-+            "ldap_enable is deprecated, use ldap_configure instead.",
-+            DeprecationWarning,
-+            stacklevel=2
-+        )
-+        self._ldap_enable(ENABLED_SERVICE, name, fqdn, ldap_suffix, config)
-+
-+    def ldap_configure(self, name, fqdn, dm_password=None, ldap_suffix='',
-+                       config=()):
-+        """Create or modify service entry in cn=masters,cn=ipa,cn=etc
-+
-+        Contrary to ldap_enable(), the method only sets
-+        ipaConfigString=configuredService. ipaConfigString=enabledService
-+        is set at the very end of the installation process, to ensure that
-+        other machines see this master/replica after it is fully installed.
-+
-+        To switch all configured services to enabled, use::
-+
-+            ipaserver.install.service.enable_services(api.env.host)
-+            api.Command.dns_update_system_records()
-+        """
-+        self._ldap_enable(
-+            CONFIGURED_SERVICE, name, fqdn, ldap_suffix, config
-+        )
-+
-+    def _ldap_enable(self, value, name, fqdn, ldap_suffix, config):
-         extra_config_opts = [
-             ' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])])
-         ]
-@@ -549,7 +627,7 @@ class Service(object):
-         set_service_entry_config(
-             name,
-             fqdn,
--            [u'enabledService'],
-+            [value],
-             ldap_suffix=ldap_suffix,
-             post_add_config=extra_config_opts)
- 
-@@ -575,7 +653,7 @@ class Service(object):
- 
-         # case insensitive
-         for value in entry.get('ipaConfigString', []):
--            if value.lower() == u'enabledservice':
-+            if value.lower() == ENABLED_SERVICE:
-                 entry['ipaConfigString'].remove(value)
-                 break
- 
-@@ -688,7 +766,7 @@ class SimpleServiceInstance(Service):
-         if self.gensvc_name == None:
-             self.enable()
-         else:
--            self.ldap_enable(self.gensvc_name, self.fqdn, None, self.suffix)
-+            self.ldap_configure(self.gensvc_name, self.fqdn, None, self.suffix)
- 
-     def is_installed(self):
-         return self.service.is_installed()
-diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py
-index 5b7ccfb342d0a54bfd6f2cdc53c7d31201ed5989..199978000ce8cf783bda50c46b7c9fa109f70ad6 100644
---- a/ipaserver/plugins/serverrole.py
-+++ b/ipaserver/plugins/serverrole.py
-@@ -15,16 +15,21 @@ IPA server roles
- """) + _("""
- Get status of roles (DNS server, CA, etc.) provided by IPA masters.
- """) + _("""
-+The status of a role is either enabled, configured, or absent.
-+""") + _("""
- EXAMPLES:
- """) + _("""
-   Show status of 'DNS server' role on a server:
-     ipa server-role-show ipa.example.com "DNS server"
- """) + _("""
-   Show status of all roles containing 'AD' on a server:
--    ipa server-role-find --server ipa.example.com --role='AD'
-+    ipa server-role-find --server ipa.example.com --role="AD trust controller"
- """) + _("""
-   Show status of all configured roles on a server:
-     ipa server-role-find ipa.example.com
-+""") + _("""
-+  Show implicit IPA master role:
-+    ipa server-role-find --include-master
- """)
- 
- 
--- 
-2.17.1
-
diff --git a/SOURCES/0013-upgrade-add-trust-upgrade-to-actual-upgrade-code.patch b/SOURCES/0013-upgrade-add-trust-upgrade-to-actual-upgrade-code.patch
new file mode 100644
index 0000000..86aebcf
--- /dev/null
+++ b/SOURCES/0013-upgrade-add-trust-upgrade-to-actual-upgrade-code.patch
@@ -0,0 +1,28 @@
+From 25166da75410026b9b634535caacb7747cd88cd1 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Fri, 22 Mar 2019 19:08:33 +0200
+Subject: [PATCH] upgrade: add trust upgrade to actual upgrade code
+
+Fixes: https://pagure.io/freeipa/issue/6077
+(cherry picked from commit f51f90e4b4bac032e076ab0f6cb3dfecb5ae12ce)
+
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ install/updates/90-post_upgrade_plugins.update | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
+index 4e9378d9b567842e1cc9a8eeae819a931810895d..a9f5f6a924d330b924d9adb8b7eee728258f27c6 100644
+--- a/install/updates/90-post_upgrade_plugins.update
++++ b/install/updates/90-post_upgrade_plugins.update
+@@ -11,6 +11,7 @@ plugin: update_sids
+ plugin: update_default_range
+ plugin: update_default_trust_view
+ plugin: update_tdo_gidnumber
++plugin: update_tdo_to_new_layout
+ plugin: update_ca_renewal_master
+ plugin: update_idrange_type
+ plugin: update_pacs
+-- 
+2.20.1
+
diff --git a/SOURCES/0014-RFE-ipa-client-should-setup-openldap-for-GSSAPI.patch b/SOURCES/0014-RFE-ipa-client-should-setup-openldap-for-GSSAPI.patch
new file mode 100644
index 0000000..b2e2e77
--- /dev/null
+++ b/SOURCES/0014-RFE-ipa-client-should-setup-openldap-for-GSSAPI.patch
@@ -0,0 +1,57 @@
+From 8cdf6a3dd64d36f40a9107dad2ab2d9a470f58b5 Mon Sep 17 00:00:00 2001
+From: amitkuma <amitkuma@redhat.com>
+Date: Tue, 16 Jan 2018 17:34:08 +0530
+Subject: [PATCH] RFE: ipa client should setup openldap for GSSAPI
+
+The IPA client installer currently edits /etc/openldap/ldap.conf, setting up
+the client to consume LDAP data from IPA.  It currently sets:
+URI
+BASE
+TLS_CACERT
+
+This PR makes ipa-client to add this AV pair:
+SASL_MECH GSSAPI
+
+Resolves: https://pagure.io/freeipa/issue/7366
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipaclient/install/client.py | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
+index babebfc667c5a096fb2e0238de444ffa3ce62b77..ca404ab80fd1586e7098950545a343fa6812ca39 100644
+--- a/ipaclient/install/client.py
++++ b/ipaclient/install/client.py
+@@ -523,8 +523,12 @@ def configure_openldap_conf(fstore, cli_basedn, cli_server):
+         {
+             'name': 'comment',
+             'type': 'comment',
+-            'value': '   URI, BASE and TLS_CACERT have been added if they '
+-                     'were not set.'
++            'value': '   URI, BASE, TLS_CACERT and SASL_MECH'
++        },
++        {
++            'name': 'comment',
++            'type': 'comment',
++            'value': '   have been added if they were not set.'
+         },
+         {
+             'name': 'comment',
+@@ -575,6 +579,12 @@ def configure_openldap_conf(fstore, cli_basedn, cli_server):
+             'type': 'option',
+             'value': paths.IPA_CA_CRT
+         },
++        {
++            'action': 'addifnotset',
++            'name': 'SASL_MECH',
++            'type': 'option',
++            'value': 'GSSAPI'
++        },
+     ]
+ 
+     target_fname = paths.OPENLDAP_LDAP_CONF
+-- 
+2.20.1
+
diff --git a/SOURCES/0014-ipa-client-uninstall-clean-the-state-store-when-rest.patch b/SOURCES/0014-ipa-client-uninstall-clean-the-state-store-when-rest.patch
deleted file mode 100644
index 4cd9653..0000000
--- a/SOURCES/0014-ipa-client-uninstall-clean-the-state-store-when-rest.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 7f6f620ec43b5606413524d25d3cae1003930f55 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Fri, 6 Jul 2018 12:47:34 +0200
-Subject: [PATCH] ipa client uninstall: clean the state store when restoring
- hostname
-
-When ipa client was installed with the --hostname= option, it stores
-[network]
-hostname = (current hostname)
-in /var/lib/ipa-client/sysrestore/sysrestore.state and changes the hostname
-from (current hostname) to the value provided in --hostname.
-
-During uninstall, the previous hostname is restored but the entry does
-not get removed from sysrestore.state. As the uninstaller checks if all
-entries from sysrestore.state have been restored, it warns that some
-state has not been restored.
-
-The fix calls statestore.restore_state() instead of statestore.get_state()
-as this method also clears the entry.
-
-https://pagure.io/freeipa/issue/7620
-
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaplatform/redhat/tasks.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
-index dcafd735c0161e5cd979ccb113863e7885a14ab3..6a4270defc9f444f76677bdf08d2a680649664bb 100644
---- a/ipaplatform/redhat/tasks.py
-+++ b/ipaplatform/redhat/tasks.py
-@@ -380,7 +380,7 @@ class RedHatTaskNamespace(BaseTaskNamespace):
-         statestore.backup_state('network', 'hostname', old_hostname)
- 
-     def restore_hostname(self, fstore, statestore):
--        old_hostname = statestore.get_state('network', 'hostname')
-+        old_hostname = statestore.restore_state('network', 'hostname')
- 
-         if old_hostname is not None:
-             try:
--- 
-2.17.1
-
diff --git a/SOURCES/0015-Add-uniqueness-constraint-on-CA-ACL-name.patch b/SOURCES/0015-Add-uniqueness-constraint-on-CA-ACL-name.patch
new file mode 100644
index 0000000..f689e9b
--- /dev/null
+++ b/SOURCES/0015-Add-uniqueness-constraint-on-CA-ACL-name.patch
@@ -0,0 +1,47 @@
+From 4145fbdb5428b11274344cfc97eb2fe5ba9537a5 Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Thu, 7 Dec 2017 12:52:54 +1100
+Subject: [PATCH] Add uniqueness constraint on CA ACL name
+
+It is possible to add caacl entries with same "name" (cn).  The
+command is supposed to prevent this but direct LDAP operations allow
+it and doing that will cause subsequent errors.
+
+Enable the DS uniqueness constraint plugin for the cn attribute in
+CA ACL entries.
+
+Fixes: https://pagure.io/freeipa/issue/7304
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ install/updates/10-uniqueness.update | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/install/updates/10-uniqueness.update b/install/updates/10-uniqueness.update
+index 050bfd55ec2e6a09c44700ae40757ee1d72c136f..77facba195cb5a1564818010f97afdd15d65a274 100644
+--- a/install/updates/10-uniqueness.update
++++ b/install/updates/10-uniqueness.update
+@@ -92,3 +92,20 @@ add:uniqueness-across-all-subtrees: on
+ dn: cn=ipaUniqueID uniqueness,cn=plugins,cn=config
+ add:uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX
+ add:uniqueness-across-all-subtrees: on
++
++dn: cn=caacl name uniqueness,cn=plugins,cn=config
++default:objectClass: top
++default:objectClass: nsSlapdPlugin
++default:objectClass: extensibleObject
++default:cn: caacl name uniqueness
++default:nsslapd-pluginDescription: Enforce unique attribute values
++default:nsslapd-pluginPath: libattr-unique-plugin
++default:nsslapd-pluginInitfunc: NSUniqueAttr_Init
++default:nsslapd-pluginType: preoperation
++default:nsslapd-pluginEnabled: on
++default:uniqueness-attribute-name: cn
++default:uniqueness-subtrees: cn=caacls,cn=ca,$SUFFIX
++default:nsslapd-plugin-depends-on-type: database
++default:nsslapd-pluginId: NSUniqueAttr
++default:nsslapd-pluginVersion: 1.1.0
++default:nsslapd-pluginVendor: Fedora Project
+-- 
+2.20.1
+
diff --git a/SOURCES/0015-Fix-CA-topology-warning.patch b/SOURCES/0015-Fix-CA-topology-warning.patch
deleted file mode 100644
index c053de1..0000000
--- a/SOURCES/0015-Fix-CA-topology-warning.patch
+++ /dev/null
@@ -1,65 +0,0 @@
-From f637bcbe1066eec41e90b99d7a4ab12288f08a74 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 6 Jul 2018 15:07:27 +0200
-Subject: [PATCH] Fix CA topology warning
-
-Commit 7284097eedef70dd556270732e6ab8e23501ce09 kept
-find_providing_servers('CA') call before enable_services(). Therefore the
-list of known CA servers did not contain the current replica.
-ipa-replica-install on the first replica with --setup-ca still printed
-the CA topology warning.
-
-See: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- ipaserver/install/server/replicainstall.py | 3 +--
- ipaserver/install/service.py               | 4 ++--
- 2 files changed, 3 insertions(+), 4 deletions(-)
-
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 0bf3568a300a133fa505dc8fc339c6677f9c5f73..387d4ca85d9ad41db3ce2d9bc2ae67ba11836ada 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -1524,8 +1524,6 @@ def install(installer):
-     if options.setup_adtrust:
-         adtrust.install(False, options, fstore, api)
- 
--    ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api)
--
-     if not promote:
-         # Call client install script
-         service.print_msg("Configuring client side components")
-@@ -1556,6 +1554,7 @@ def install(installer):
-     # Enable configured services and update DNS SRV records
-     service.enable_services(config.host_name)
-     api.Command.dns_update_system_records()
-+    ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api)
-     api.Backend.ldap2.disconnect()
- 
-     # Everything installed properly, activate ipa service.
-diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
-index 0106379ea38e4a3fef8436256d6f315f524b8dee..39dc49e872c82961defc09c7240604bc1cba2228 100644
---- a/ipaserver/install/service.py
-+++ b/ipaserver/install/service.py
-@@ -64,7 +64,7 @@ SERVICE_LIST = {
- }
- 
- CONFIGURED_SERVICE = u'configuredService'
--ENABLED_SERVICE = 'enabledService'
-+ENABLED_SERVICE = u'enabledService'
- 
- 
- def print_msg(message, output_fd=sys.stdout):
-@@ -636,7 +636,7 @@ class Service(object):
- 
-         entry_dn = DN(('cn', name), ('cn', fqdn), ('cn', 'masters'),
-                         ('cn', 'ipa'), ('cn', 'etc'), ldap_suffix)
--        search_kw = {'ipaConfigString': u'enabledService'}
-+        search_kw = {'ipaConfigString': ENABLED_SERVICE}
-         filter = api.Backend.ldap2.make_filter(search_kw)
-         try:
-             entries, _truncated = api.Backend.ldap2.find_entries(
--- 
-2.17.1
-
diff --git a/SOURCES/0016-Add-sysadm_r-to-default-SELinux-user-map-order.patch b/SOURCES/0016-Add-sysadm_r-to-default-SELinux-user-map-order.patch
new file mode 100644
index 0000000..99f519e
--- /dev/null
+++ b/SOURCES/0016-Add-sysadm_r-to-default-SELinux-user-map-order.patch
@@ -0,0 +1,69 @@
+From dfb0b27748aebb307fa3ab72aa1c43a71da1d78e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= <fcami@redhat.com>
+Date: Fri, 9 Nov 2018 17:30:32 +0100
+Subject: [PATCH] Add sysadm_r to default SELinux user map order
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It is a standard SELinux user role included in RHEL (like
+user_r, staff_r, guest_r) and used quite often.
+
+Fixes: https://pagure.io/freeipa/issue/7658
+Signed-off-by: François Cami <fcami@redhat.com>
+Reviewed-By: Rob Crittenden <rcritten@redhat.com>
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ install/share/bootstrap-template.ldif      | 2 +-
+ install/ui/test/data/ipa_init.json         | 2 +-
+ ipatests/test_xmlrpc/test_config_plugin.py | 8 ++++++--
+ 3 files changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif
+index 1b80794e66a5986d049195885ae1c9d6380eec81..6cd17e37ef147169b65709a56b0fa7312889c262 100644
+--- a/install/share/bootstrap-template.ldif
++++ b/install/share/bootstrap-template.ldif
+@@ -425,7 +425,7 @@ ipaDefaultEmailDomain: $DOMAIN
+ ipaMigrationEnabled: FALSE
+ ipaConfigString: AllowNThash
+ ipaConfigString: KDC:Disable Last Success
+-ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023
++ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$sysadm_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023
+ ipaSELinuxUserMapDefault: unconfined_u:s0-s0:c0.c1023
+ 
+ dn: cn=cosTemplates,cn=accounts,$SUFFIX
+diff --git a/install/ui/test/data/ipa_init.json b/install/ui/test/data/ipa_init.json
+index dd4b84cc920408ec8fa0bb92e9d6debc67cede7e..52f6ab191e05b314fcbeaa4786230e5ae2ebea4b 100644
+--- a/install/ui/test/data/ipa_init.json
++++ b/install/ui/test/data/ipa_init.json
+@@ -863,7 +863,7 @@
+                   "ipausers"
+                ],
+                "ipaselinuxusermaporder" : [
+-                  "guest_u:s0$xguest_u:s0$user_u:s0$staff_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023"
++                  "guest_u:s0$xguest_u:s0$user_u:s0$staff_u:s0-s0:c0.c1023$sysadm_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023"
+                ],
+                "ca_renewal_master_server" : [
+                   "vm.example.com"
+diff --git a/ipatests/test_xmlrpc/test_config_plugin.py b/ipatests/test_xmlrpc/test_config_plugin.py
+index a277eab7eeb44590f982510564e92935c444c744..bd5a62acbed6a6c29bbf5fcbd041ef59bb0363bd 100644
+--- a/ipatests/test_xmlrpc/test_config_plugin.py
++++ b/ipatests/test_xmlrpc/test_config_plugin.py
+@@ -148,8 +148,12 @@ class test_config(Declarative):
+ 
+         dict(
+             desc='Try to set new selinux order and invalid default user',
+-            command=('config_mod', [],
+-                dict(ipaselinuxusermaporder=u'xguest_u:s0$guest_u:s0$user_u:s0-s0:c0.c1023$staff_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023',
++            command=(
++                'config_mod', [],
++                dict(
++                    ipaselinuxusermaporder=u'xguest_u:s0$guest_u:s0'
++                    u'$user_u:s0-s0:c0.c1023$staff_u:s0-s0:c0.c1023'
++                    u'$sysadm_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023',
+                     ipaselinuxusermapdefault=u'unknown_u:s0')),
+             expected=errors.ValidationError(name='ipaselinuxusermapdefault',
+                 error='SELinux user map default user not in order list'),
+-- 
+2.20.1
+
diff --git a/SOURCES/0016-replicainstall-DS-SSL-replica-install-pick-right-cer.patch b/SOURCES/0016-replicainstall-DS-SSL-replica-install-pick-right-cer.patch
deleted file mode 100644
index 32a3d45..0000000
--- a/SOURCES/0016-replicainstall-DS-SSL-replica-install-pick-right-cer.patch
+++ /dev/null
@@ -1,55 +0,0 @@
-From 44ec2e641c809afbed96bcde2a31388a37a7ec32 Mon Sep 17 00:00:00 2001
-From: Rob Crittenden <rcritten@redhat.com>
-Date: Fri, 6 Jul 2018 09:26:19 -0400
-Subject: [PATCH] replicainstall: DS SSL replica install pick right certmonger
- host
-
-Extend fix 0f31564b35aac250456233f98730811560eda664 to also move
-the DS SSL setup so that the xmlrpc_uri is configured to point
-to the remote master we are configuring against.
-
-https://pagure.io/freeipa/issue/7566
-
-Signed-off-by: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/server/replicainstall.py | 14 +++++++-------
- 1 file changed, 7 insertions(+), 7 deletions(-)
-
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 387d4ca85d9ad41db3ce2d9bc2ae67ba11836ada..542e1d4d145f266d6fd9ad8e0eaffcb12e8f6bc6 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -1448,15 +1448,12 @@ def install(installer):
-         pkcs12_info=pkinit_pkcs12_info,
-         promote=promote)
- 
--    # we now need to enable ssl on the ds
--    ds.enable_ssl()
--
-     if promote:
-         # We need to point to the master when certmonger asks for
--        # HTTP certificate.
--        # During http installation, the HTTP/hostname principal is created
--        # locally then the installer waits for the entry to appear on the
--        # master selected for the installation.
-+        # a DS or HTTP certificate.
-+        # During http installation, the <service>/hostname principal is
-+        # created locally then the installer waits for the entry to appear
-+        # on the master selected for the installation.
-         # In a later step, the installer requests a SSL certificate through
-         # Certmonger (and the op adds the principal if it does not exist yet).
-         # If xmlrpc_uri points to the soon-to-be replica,
-@@ -1470,6 +1467,9 @@ def install(installer):
-         create_ipa_conf(fstore, config, ca_enabled,
-                         master=config.master_host_name)
- 
-+    # we now need to enable ssl on the ds
-+    ds.enable_ssl()
-+
-     install_http(
-         config,
-         auto_redirect=not options.no_ui_redirect,
--- 
-2.17.1
-
diff --git a/SOURCES/0017-Extend-CALessBase-installer_server-to-accept-extra_a.patch b/SOURCES/0017-Extend-CALessBase-installer_server-to-accept-extra_a.patch
new file mode 100644
index 0000000..ae49738
--- /dev/null
+++ b/SOURCES/0017-Extend-CALessBase-installer_server-to-accept-extra_a.patch
@@ -0,0 +1,55 @@
+From 853b56f4096734c47a26cf11767a7d6d582b11eb Mon Sep 17 00:00:00 2001
+From: Rob Crittenden <rcritten@redhat.com>
+Date: Mon, 25 Jun 2018 17:04:16 -0400
+Subject: [PATCH] Extend CALessBase::installer_server to accept extra_args
+
+Allow callers to pass abitrary extra arguments to the installer.
+
+This is useful when using a CALess installation in order to
+speed up tests that require a full install but do not require
+a full PKI.
+
+Reviewed-By: Rob Crittenden <rcritten@redhat.com>
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipatests/test_integration/test_caless.py | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py
+index ff8d95caa6fed00d3876f1d08e2170a9587a6d86..1ebe8fa368b88e949c0bd30bfc3999bf15c4df66 100644
+--- a/ipatests/test_integration/test_caless.py
++++ b/ipatests/test_integration/test_caless.py
+@@ -171,7 +171,7 @@ class CALessBase(IntegrationTest):
+                        http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, pkinit_pin=None,
+                        root_ca_file='root.pem', pkinit_pkcs12_exists=False,
+                        pkinit_pkcs12='server-kdc.p12', unattended=True,
+-                       stdin_text=None):
++                       stdin_text=None, extra_args=None):
+         """Install a CA-less server
+ 
+         Return value is the remote ipa-server-install command
+@@ -179,10 +179,16 @@ class CALessBase(IntegrationTest):
+         if host is None:
+             host = cls.master
+ 
+-        extra_args = ['--http-cert-file', http_pkcs12,
+-                      '--dirsrv-cert-file', dirsrv_pkcs12,
+-                      '--ca-cert-file', root_ca_file,
+-                      '--ip-address', host.ip]
++        std_args = [
++            '--http-cert-file', http_pkcs12,
++            '--dirsrv-cert-file', dirsrv_pkcs12,
++            '--ca-cert-file', root_ca_file,
++            '--ip-address', host.ip
++        ]
++        if extra_args:
++            extra_args.extend(std_args)
++        else:
++            extra_args = std_args
+ 
+         if http_pin is _DEFAULT:
+             http_pin = cls.cert_password
+-- 
+2.20.1
+
diff --git a/SOURCES/0017-Fix-race-condition-in-get_locations_records.patch b/SOURCES/0017-Fix-race-condition-in-get_locations_records.patch
deleted file mode 100644
index 804894f..0000000
--- a/SOURCES/0017-Fix-race-condition-in-get_locations_records.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From 72517c7b316f7cba93a3b2cdc6e2c69dad63a8b1 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 6 Jul 2018 21:47:32 +0200
-Subject: [PATCH] Fix race condition in get_locations_records()
-
-The method IPASystemRecords.get_locations_records() has a race condition.
-The IPASystemRecords object creates a mapping of server names to server
-data. get_locations_records() uses server_find() again to get a list of
-servers, but then operates on the cached dict of server names.
-
-In parallel replication case, the second server_find() call in
-get_locations_records() can return additional servers. Since the rest of
-the code operates on the cached data, the method then fails with a KeyError.
-
-server_data is now an OrderedDict to keep same sorting as with
-server_find().
-
-Fixes: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
----
- ipaserver/dns_data_management.py | 14 +++++---------
- 1 file changed, 5 insertions(+), 9 deletions(-)
-
-diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py
-index 673397ef2b2252f431eec1f3e1f71dc45ff87511..90c4d8536478599e5a35462c7f00ea4c958ff63a 100644
---- a/ipaserver/dns_data_management.py
-+++ b/ipaserver/dns_data_management.py
-@@ -8,7 +8,7 @@ import logging
- 
- import six
- 
--from collections import defaultdict
-+from collections import defaultdict, OrderedDict
- from dns import (
-     rdata,
-     rdataclass,
-@@ -71,7 +71,7 @@ class IPASystemRecords(object):
-     def __init__(self, api_instance, all_servers=False):
-         self.api_instance = api_instance
-         self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute()
--        self.servers_data = {}
-+        self.servers_data = OrderedDict()
-         self.__init_data(all_servers=all_servers)
- 
-     def reload_data(self):
-@@ -93,7 +93,7 @@ class IPASystemRecords(object):
-         return location + DNSName('_locations') + self.domain_abs
- 
-     def __init_data(self, all_servers=False):
--        self.servers_data = {}
-+        self.servers_data.clear()
- 
-         kwargs = dict(no_members=False)
-         if not all_servers:
-@@ -328,7 +328,7 @@ class IPASystemRecords(object):
- 
-         zone_obj = zone.Zone(self.domain_abs, relativize=False)
-         if servers is None:
--            servers = self.servers_data.keys()
-+            servers = list(self.servers_data)
- 
-         for server in servers:
-             self._add_base_dns_records_for_server(zone_obj, server,
-@@ -351,11 +351,7 @@ class IPASystemRecords(object):
-         """
-         zone_obj = zone.Zone(self.domain_abs, relativize=False)
-         if servers is None:
--            servers_result = self.api_instance.Command.server_find(
--                pkey_only=True,
--                servrole=u"IPA master",  # only fully installed masters
--            )['result']
--            servers = [s['cn'][0] for s in servers_result]
-+            servers = list(self.servers_data)
- 
-         locations_result = self.api_instance.Command.location_find()['result']
-         locations = [l['idnsname'][0] for l in locations_result]
--- 
-2.17.1
-
diff --git a/SOURCES/0018-Skip-zone-overlap-check-with-auto-reverse.patch b/SOURCES/0018-Skip-zone-overlap-check-with-auto-reverse.patch
new file mode 100644
index 0000000..1251db9
--- /dev/null
+++ b/SOURCES/0018-Skip-zone-overlap-check-with-auto-reverse.patch
@@ -0,0 +1,188 @@
+From 85bc681683a8a5d71b9cfaf9c70e0f388732f285 Mon Sep 17 00:00:00 2001
+From: Justin Stephenson <jstephen@redhat.com>
+Date: Wed, 27 Dec 2017 16:32:47 -0500
+Subject: [PATCH] Skip zone overlap check with auto-reverse
+
+Skip the existing reverse zone overlap check during DNS installation
+when both --auto-reverse and --allow-zone-overlap arguments are
+provided.
+
+https://pagure.io/freeipa/issue/7239
+
+Reviewed-By: Rob Crittenden <rcritten@redhat.com>
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipaserver/install/bindinstance.py             | 18 ++--
+ .../test_integration/test_installation.py     | 95 +++++++++++++++++++
+ 2 files changed, 105 insertions(+), 8 deletions(-)
+
+diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
+index 940667db9c40559d5369188395bb18f2d1eac025..4de2c6442175a64f4048a93b2006a6d5c6b2a045 100644
+--- a/ipaserver/install/bindinstance.py
++++ b/ipaserver/install/bindinstance.py
+@@ -304,7 +304,7 @@ def read_reverse_zone(default, ip_address, allow_zone_overlap=False):
+     return normalize_zone(zone)
+ 
+ 
+-def get_auto_reverse_zones(ip_addresses):
++def get_auto_reverse_zones(ip_addresses, allow_zone_overlap=False):
+     auto_zones = []
+     for ip in ip_addresses:
+         if ipautil.reverse_record_exists(ip):
+@@ -312,12 +312,13 @@ def get_auto_reverse_zones(ip_addresses):
+             logger.info("Reverse record for IP address %s already exists", ip)
+             continue
+         default_reverse = get_reverse_zone_default(ip)
+-        try:
+-            dnsutil.check_zone_overlap(default_reverse)
+-        except ValueError:
+-            logger.info("Reverse zone %s for IP address %s already exists",
+-                        default_reverse, ip)
+-            continue
++        if not allow_zone_overlap:
++            try:
++                dnsutil.check_zone_overlap(default_reverse)
++            except ValueError:
++                logger.info("Reverse zone %s for IP address %s already exists",
++                            default_reverse, ip)
++                continue
+         auto_zones.append((ip, default_reverse))
+     return auto_zones
+ 
+@@ -488,7 +489,8 @@ def check_reverse_zones(ip_addresses, reverse_zones, options, unattended,
+             ips_missing_reverse.append(ip)
+ 
+     # create reverse zone for IP addresses that does not have one
+-    for (ip, rz) in get_auto_reverse_zones(ips_missing_reverse):
++    for (ip, rz) in get_auto_reverse_zones(ips_missing_reverse,
++                                           options.allow_zone_overlap):
+         if options.auto_reverse:
+             logger.info("Reverse zone %s will be created", rz)
+             checked_reverse_zones.append(rz)
+diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
+index 4cd8a3e33927b7522e9b2a8908a8c179daaa1280..7e6c728102e29753ec7b56cedea734dd18b8bb8c 100644
+--- a/ipatests/test_integration/test_installation.py
++++ b/ipatests/test_integration/test_installation.py
+@@ -9,6 +9,7 @@ installed.
+ 
+ from __future__ import absolute_import
+ 
++import os
+ import pytest
+ from ipalib.constants import DOMAIN_LEVEL_0
+ from ipaplatform.constants import constants
+@@ -16,9 +17,46 @@ from ipaplatform.paths import paths
+ from ipatests.pytest_ipa.integration.env_config import get_global_config
+ from ipatests.test_integration.base import IntegrationTest
+ from ipatests.pytest_ipa.integration import tasks
++from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup
+ 
+ config = get_global_config()
+ 
++
++def create_broken_resolv_conf(master):
++    # Force a broken resolv.conf to simulate a bad response to
++    # reverse zone lookups
++    master.run_command([
++        '/usr/bin/mv',
++        paths.RESOLV_CONF,
++        '%s.sav' % paths.RESOLV_CONF
++    ])
++
++    contents = "# Set as broken by ipatests\nnameserver 127.0.0.2\n"
++    master.put_file_contents(paths.RESOLV_CONF, contents)
++
++
++def restore_resolv_conf(master):
++    if os.path.exists('%s.sav' % paths.RESOLV_CONF):
++        master.run_command([
++            '/usr/bin/mv',
++            '%s.sav' % paths.RESOLV_CONF,
++            paths.RESOLV_CONF
++        ])
++
++
++def server_install_setup(func):
++    def wrapped(*args):
++        master = args[0].master
++        create_broken_resolv_conf(master)
++        try:
++            func(*args)
++        finally:
++            tasks.uninstall_master(master, clean=False)
++            restore_resolv_conf(master)
++            ipa_certs_cleanup(master)
++    return wrapped
++
++
+ class InstallTestBase1(IntegrationTest):
+ 
+     num_replicas = 3
+@@ -226,6 +264,63 @@ class TestInstallWithCA_DNS2(InstallTestBase2):
+         super(TestInstallWithCA_DNS2, self).test_replica2_ipa_kra_install()
+ 
+ 
++class TestInstallWithCA_DNS3(CALessBase):
++    """
++    Test an install with a bad DNS resolver configured to force a
++    timeout trying to verify the existing zones. In the case of a reverse
++    zone it is skipped unless --allow-zone-overlap is set regardless of
++    the value of --auto-reverse. Confirm that --allow-zone-overlap
++    lets the reverse zone be created.
++
++    ticket 7239
++    """
++
++    @server_install_setup
++    def test_number_of_zones(self):
++        """There should be two zones: one forward, one reverse"""
++
++        self.create_pkcs12('ca1/server')
++        self.prepare_cacert('ca1')
++
++        self.install_server(extra_args=['--allow-zone-overlap'])
++
++        result = self.master.run_command([
++            'ipa', 'dnszone-find'])
++
++        assert "in-addr.arpa." in result.stdout_text
++
++        assert "returned 2" in result.stdout_text
++
++
++class TestInstallWithCA_DNS4(CALessBase):
++    """
++    Test an install with a bad DNS resolver configured to force a
++    timeout trying to verify the existing zones. In the case of a reverse
++    zone it is skipped unless --allow-zone-overlap is set regardless of
++    the value of --auto-reverse. Confirm that without --allow-reverse-zone
++    only the forward zone is created.
++
++    ticket 7239
++    """
++
++    @server_install_setup
++    def test_number_of_zones(self):
++        """There should be one zone, a forward because rev timed-out"""
++
++        self.create_pkcs12('ca1/server')
++        self.prepare_cacert('ca1')
++
++        # no zone overlap by default
++        self.install_server()
++
++        result = self.master.run_command([
++            'ipa', 'dnszone-find'])
++
++        assert "in-addr.arpa." not in result.stdout_text
++
++        assert "returned 1" in result.stdout_text
++
++
+ @pytest.mark.cs_acceptance
+ class TestInstallWithCA_KRA_DNS1(InstallTestBase1):
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0018-Tune-DS-replication-settings.patch b/SOURCES/0018-Tune-DS-replication-settings.patch
deleted file mode 100644
index bb37404..0000000
--- a/SOURCES/0018-Tune-DS-replication-settings.patch
+++ /dev/null
@@ -1,336 +0,0 @@
-From 4d99ab7ce65064aacf2a7429d8ebc956c5b43b34 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Tue, 3 Jul 2018 19:40:05 +0200
-Subject: [PATCH] Tune DS replication settings
-
-Tune 389-DS replication settings to improve performance and avoid
-timeouts. During installation of a replica, the value of
-nsDS5ReplicaBindDnGroupCheckInterval is reduced to 2 seconds. At the end
-of the installation, the value is increased sensible production
-settings. This avoids long delays during replication.
-
-See: https://pagure.io/freeipa/issue/7617
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
----
- ipaserver/install/cainstance.py               |  8 +-
- ipaserver/install/dsinstance.py               | 48 ++++++----
- ipaserver/install/replication.py              | 92 +++++++++++++++----
- ipaserver/install/server/replicainstall.py    |  2 +
- ipaserver/install/server/upgrade.py           | 12 ++-
- ipatests/test_integration/test_external_ca.py | 21 +++++
- 6 files changed, 140 insertions(+), 43 deletions(-)
-
-diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
-index 51fdbe9c61e06ab9d72d78aee8786f9bceca137b..8193f3da854b3a20d175de523fbc453f5c5104d8 100644
---- a/ipaserver/install/cainstance.py
-+++ b/ipaserver/install/cainstance.py
-@@ -410,6 +410,9 @@ class CAInstance(DogtagInstance):
-                           self.teardown_admin)
-             self.step("starting certificate server instance",
-                       self.start_instance)
-+            if promote:
-+                self.step("Finalize replication settings",
-+                          self.finalize_replica_config)
-         # Step 1 of external is getting a CSR so we don't need to do these
-         # steps until we get a cert back from the external CA.
-         if self.external != 1:
-@@ -1245,13 +1248,16 @@ class CAInstance(DogtagInstance):
-         api.Backend.ldap2.add_entry(entry)
- 
-     def __setup_replication(self):
--
-         repl = replication.CAReplicationManager(self.realm, self.fqdn)
-         repl.setup_cs_replication(self.master_host)
- 
-         # Activate Topology for o=ipaca segments
-         self.__update_topology()
- 
-+    def finalize_replica_config(self):
-+        repl = replication.CAReplicationManager(self.realm, self.fqdn)
-+        repl.finalize_replica_config(self.master_host)
-+
-     def __enable_instance(self):
-         basedn = ipautil.realm_to_suffix(self.realm)
-         if not self.clone:
-diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
-index 06c1273dc21a88f99a0f543cfd12bb6563c7e214..eefbde3356e1077d490d09c4ea47d961ce3ce8e6 100644
---- a/ipaserver/install/dsinstance.py
-+++ b/ipaserver/install/dsinstance.py
-@@ -418,6 +418,20 @@ class DsInstance(service.Service):
- 
-         self.start_creation(runtime=30)
- 
-+    def _get_replication_manager(self):
-+        # Always connect to self over ldapi
-+        ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm)
-+        conn = ipaldap.LDAPClient(ldap_uri)
-+        conn.external_bind()
-+        repl = replication.ReplicationManager(
-+            self.realm, self.fqdn, self.dm_password, conn=conn
-+        )
-+        if self.dm_password is not None and not self.promote:
-+            bind_dn = DN(('cn', 'Directory Manager'))
-+            bind_pw = self.dm_password
-+        else:
-+            bind_dn = bind_pw = None
-+        return repl, bind_dn, bind_pw
- 
-     def __setup_replica(self):
-         """
-@@ -434,26 +448,24 @@ class DsInstance(service.Service):
-             self.realm,
-             self.dm_password)
- 
--        # Always connect to self over ldapi
--        ldap_uri = ipaldap.get_ldap_uri(protocol='ldapi', realm=self.realm)
--        conn = ipaldap.LDAPClient(ldap_uri)
--        conn.external_bind()
--        repl = replication.ReplicationManager(self.realm,
--                                              self.fqdn,
--                                              self.dm_password, conn=conn)
--
--        if self.dm_password is not None and not self.promote:
--            bind_dn = DN(('cn', 'Directory Manager'))
--            bind_pw = self.dm_password
--        else:
--            bind_dn = bind_pw = None
--
--        repl.setup_promote_replication(self.master_fqdn,
--                                       r_binddn=bind_dn,
--                                       r_bindpw=bind_pw,
--                                       cacert=self.ca_file)
-+        repl, bind_dn, bind_pw = self._get_replication_manager()
-+        repl.setup_promote_replication(
-+            self.master_fqdn,
-+            r_binddn=bind_dn,
-+            r_bindpw=bind_pw,
-+            cacert=self.ca_file
-+        )
-         self.run_init_memberof = repl.needs_memberof_fixup()
- 
-+    def finalize_replica_config(self):
-+        repl, bind_dn, bind_pw = self._get_replication_manager()
-+        repl.finalize_replica_config(
-+            self.master_fqdn,
-+            r_binddn=bind_dn,
-+            r_bindpw=bind_pw,
-+            cacert=self.ca_file
-+        )
-+
-     def __configure_sasl_mappings(self):
-         # we need to remove any existing SASL mappings in the directory as otherwise they
-         # they may conflict.
-diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
-index 5ce8fa689c1a6fd3b9d4cbddbd5454d36334b729..d0f92c76c108d237104a81c72567250583ac4ff1 100644
---- a/ipaserver/install/replication.py
-+++ b/ipaserver/install/replication.py
-@@ -75,6 +75,20 @@ STRIP_ATTRS = ('modifiersName',
-                'internalModifiersName',
-                'internalModifyTimestamp')
- 
-+# settings for cn=replica,cn=$DB,cn=mapping tree,cn=config
-+# during replica installation
-+REPLICA_CREATION_SETTINGS = {
-+    "nsds5ReplicaReleaseTimeout": ["20"],
-+    "nsds5ReplicaBackoffMax": ["3"],
-+    "nsDS5ReplicaBindDnGroupCheckInterval": ["2"]
-+}
-+# after replica installation
-+REPLICA_FINAL_SETTINGS = {
-+    "nsds5ReplicaReleaseTimeout": ["60"],
-+    "nsds5ReplicaBackoffMax": ["300"],  # default
-+    "nsDS5ReplicaBindDnGroupCheckInterval": ["60"]
-+}
-+
- 
- def replica_conn_check(master_host, host_name, realm, check_ca,
-                        dogtag_master_ds_port, admin_password=None,
-@@ -201,9 +215,13 @@ def wait_for_entry(connection, dn, timeout, attr=None, attrvalue='*',
- 
- 
- class ReplicationManager(object):
--    """Manage replication agreements between DS servers, and sync
--    agreements with Windows servers"""
--    def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False, conn=None):
-+    """Manage replication agreements
-+
-+    between DS servers, and sync  agreements with Windows servers
-+    """
-+
-+    def __init__(self, realm, hostname, dirman_passwd=None, port=PORT,
-+                 starttls=False, conn=None):
-         self.hostname = hostname
-         self.port = port
-         self.dirman_passwd = dirman_passwd
-@@ -481,22 +499,16 @@ class ReplicationManager(object):
-         except errors.NotFound:
-             pass
-         else:
--            managers = {DN(m) for m in entry.get('nsDS5ReplicaBindDN', [])}
--
--            mods = []
--            if replica_binddn not in managers:
-+            binddns = entry.setdefault('nsDS5ReplicaBindDN', [])
-+            if replica_binddn not in {DN(m) for m in binddns}:
-                 # Add the new replication manager
--                mods.append(
--                    (ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn)
--                )
--            if 'nsds5replicareleasetimeout' not in entry:
--                # See https://pagure.io/freeipa/issue/7488
--                mods.append(
--                    (ldap.MOD_ADD, 'nsds5replicareleasetimeout', ['60'])
--                )
--
--            if mods:
--                conn.modify_s(dn, mods)
-+                binddns.append(replica_binddn)
-+            for key, value in REPLICA_CREATION_SETTINGS.items():
-+                entry[key] = value
-+            try:
-+                conn.update_entry(entry)
-+            except errors.EmptyModlist:
-+                pass
- 
-             self.set_replica_binddngroup(conn, entry)
- 
-@@ -515,9 +527,8 @@ class ReplicationManager(object):
-             nsds5flags=["1"],
-             nsds5replicabinddn=[replica_binddn],
-             nsds5replicabinddngroup=[self.repl_man_group_dn],
--            nsds5replicabinddngroupcheckinterval=["60"],
--            nsds5replicareleasetimeout=["60"],
-             nsds5replicalegacyconsumer=["off"],
-+            **REPLICA_CREATION_SETTINGS
-         )
-         conn.add_entry(entry)
- 
-@@ -543,6 +554,47 @@ class ReplicationManager(object):
-         except errors.DuplicateEntry:
-             return
- 
-+    def _finalize_replica_settings(self, conn):
-+        """Change replica settings to final values
-+
-+        During replica installation, some settings are configured for faster
-+        replication.
-+        """
-+        dn = self.replica_dn()
-+        entry = conn.get_entry(dn)
-+        for key, value in REPLICA_FINAL_SETTINGS.items():
-+            entry[key] = value
-+        try:
-+            conn.update_entry(entry)
-+        except errors.EmptyModlist:
-+            pass
-+
-+    def finalize_replica_config(self, r_hostname, r_binddn=None,
-+                                r_bindpw=None, cacert=paths.IPA_CA_CRT):
-+        """Apply final cn=replica settings
-+
-+        replica_config() sets several attribute to fast cache invalidation
-+        and fast reconnects to optimize replicat installation. For
-+        production, longer timeouts and less aggressive cache invalidation
-+        is sufficient. finalize_replica_config() sets the values on new
-+        replica and the master.
-+
-+        When installing multiple replicas in parallel, one replica may
-+        finalize the values while another is still installing.
-+
-+        See https://pagure.io/freeipa/issue/7617
-+        """
-+        self._finalize_replica_settings(self.conn)
-+
-+        ldap_uri = ipaldap.get_ldap_uri(r_hostname)
-+        r_conn = ipaldap.LDAPClient(ldap_uri, cacert=cacert)
-+        if r_bindpw:
-+            r_conn.simple_bind(r_binddn, r_bindpw)
-+        else:
-+            r_conn.gssapi_bind()
-+        self._finalize_replica_settings(r_conn)
-+        r_conn.close()
-+
-     def setup_chaining_backend(self, conn):
-         chaindn = DN(('cn', 'chaining database'), ('cn', 'plugins'), ('cn', 'config'))
-         benamebase = "chaindb"
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 542e1d4d145f266d6fd9ad8e0eaffcb12e8f6bc6..8826da232a90380084b0e4f3dca783125a5500da 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -1506,6 +1506,8 @@ def install(installer):
-     # Apply any LDAP updates. Needs to be done after the replica is synced-up
-     service.print_msg("Applying LDAP updates")
-     ds.apply_updates()
-+    service.print_msg("Finalize replication settings")
-+    ds.finalize_replica_config()
- 
-     if kra_enabled:
-         kra.install(api, config, options, custodia=custodia)
-diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
-index ee3cedd78a2f45f33665bf562e9426cf68325544..4e5096e598cd10e3bd98f91946b4d26377d0de6e 100644
---- a/ipaserver/install/server/upgrade.py
-+++ b/ipaserver/install/server/upgrade.py
-@@ -45,6 +45,7 @@ from ipaserver.install import dnskeysyncinstance
- from ipaserver.install import dogtaginstance
- from ipaserver.install import krbinstance
- from ipaserver.install import adtrustinstance
-+from ipaserver.install import replication
- from ipaserver.install.upgradeinstance import IPAUpgrade
- from ipaserver.install.ldapupdate import BadSyntax
- 
-@@ -1645,11 +1646,14 @@ def update_replica_config(db_suffix):
-     except ipalib.errors.NotFound:
-         return  # entry does not exist until a replica is installed
- 
--    if 'nsds5replicareleasetimeout' not in entry:
--        # See https://pagure.io/freeipa/issue/7488
--        logger.info("Adding nsds5replicaReleaseTimeout=60 to %s", dn)
--        entry['nsds5replicareleasetimeout'] = '60'
-+    for key, value in replication.REPLICA_FINAL_SETTINGS.items():
-+        entry[key] = value
-+    try:
-         api.Backend.ldap2.update_entry(entry)
-+    except ipalib.errors.EmptyModlist:
-+        pass
-+    else:
-+        logger.info("Updated entry %s", dn)
- 
- 
- def upgrade_configuration():
-diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py
-index d21e6d543f51dcee1e548e322cbe01fbe0c13d48..2fc2478ae8bb49d95f7ec60270fb5bb6e582d6f5 100644
---- a/ipatests/test_integration/test_external_ca.py
-+++ b/ipatests/test_integration/test_external_ca.py
-@@ -130,6 +130,27 @@ class TestExternalCA(IntegrationTest):
-         result = self.master.run_command(['ipa', 'user-show', 'admin'])
-         assert 'User login: admin' in result.stdout_text
- 
-+        # check that we can also install replica
-+        tasks.install_replica(self.master, self.replicas[0])
-+
-+        # check that nsds5ReplicaReleaseTimeout option was set
-+        result = self.master.run_command([
-+            'ldapsearch',
-+            '-x',
-+            '-D',
-+            'cn=directory manager',
-+            '-w', self.master.config.dirman_password,
-+            '-b', 'cn=mapping tree,cn=config',
-+            '(cn=replica)',
-+            '-LLL',
-+            '-o',
-+            'ldif-wrap=no'])
-+        # case insensitive match
-+        text = result.stdout_text.lower()
-+        # see ipaserver.install.replication.REPLICA_FINAL_SETTINGS
-+        assert 'nsds5ReplicaReleaseTimeout: 60'.lower() in text
-+        assert 'nsDS5ReplicaBindDnGroupCheckInterval: 60'.lower() in text
-+
-     def test_client_installation_with_otp(self):
-         # Test for issue 7526: client installation fails with one-time
-         # password when the master is installed with an externally signed
--- 
-2.17.1
-
diff --git a/SOURCES/0019-Add-hidden-replica-feature.patch b/SOURCES/0019-Add-hidden-replica-feature.patch
new file mode 100644
index 0000000..9048ce4
--- /dev/null
+++ b/SOURCES/0019-Add-hidden-replica-feature.patch
@@ -0,0 +1,390 @@
+From fc64de3f833ab63f2b2ee8984db95866b3f718a7 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Fri, 22 Mar 2019 15:14:06 +0100
+Subject: [PATCH] Add hidden replica feature
+
+A hidden replica is a replica that does not advertise its services via
+DNS SRV records, ipa-ca DNS entry, or LDAP. Clients do not auto-select a
+hidden replica, but are still free to explicitly connect to it.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Co-authored-by: Francois Cami <fcami@redhat.com>:
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ API.txt                                    |  2 +-
+ install/tools/ipactl                       | 12 ++++-
+ ipaserver/install/server/__init__.py       |  7 +++
+ ipaserver/install/server/replicainstall.py | 11 ++++-
+ ipaserver/install/service.py               | 57 +++++++++++++++++-----
+ ipaserver/masters.py                       | 46 ++++++++++++-----
+ ipaserver/plugins/serverrole.py            |  2 +-
+ ipaserver/servroles.py                     | 27 +++++++---
+ 8 files changed, 131 insertions(+), 33 deletions(-)
+
+diff --git a/API.txt b/API.txt
+index b9dc35fb5752ce04f58aa8c4c3e89c7299f34cd7..2135300183e3dc2126309e8f892e79fe6b5178fb 100644
+--- a/API.txt
++++ b/API.txt
+@@ -4443,7 +4443,7 @@ option: Flag('raw', autofill=True, cli_name='raw', default=False)
+ option: Str('role_servrole?', autofill=False, cli_name='role')
+ option: Str('server_server?', autofill=False, cli_name='server')
+ option: Int('sizelimit?', autofill=False)
+-option: StrEnum('status?', autofill=False, cli_name='status', default=u'enabled', values=[u'enabled', u'configured', u'absent'])
++option: StrEnum('status?', autofill=False, cli_name='status', default=u'enabled', values=[u'enabled', u'configured', u'hidden', u'absent'])
+ option: Int('timelimit?', autofill=False)
+ option: Str('version?')
+ output: Output('count', type=[<type 'int'>])
+diff --git a/install/tools/ipactl b/install/tools/ipactl
+index 2767a26d1b70337d37dbcd87c707919579fe7e29..f40ea5a6df74f04ec7e6e8959d731553651a81d3 100755
+--- a/install/tools/ipactl
++++ b/install/tools/ipactl
+@@ -29,6 +29,7 @@ import ldapurl
+ from ipaserver.install import service, installutils
+ from ipaserver.install.dsinstance import config_dirname
+ from ipaserver.install.installutils import is_ipa_configured, ScriptError
++from ipaserver.masters import ENABLED_SERVICE, HIDDEN_SERVICE
+ from ipalib import api, errors
+ from ipapython.ipaldap import LDAPClient
+ from ipapython.ipautil import wait_for_open_ports, wait_for_open_socket
+@@ -162,7 +163,16 @@ def version_check():
+ 
+ def get_config(dirsrv):
+     base = DN(('cn', api.env.host), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+-    srcfilter = '(ipaConfigString=enabledService)'
++    srcfilter = LDAPClient.combine_filters(
++        [
++            LDAPClient.make_filter({'objectClass': 'ipaConfigObject'}),
++            LDAPClient.make_filter(
++                {'ipaConfigString': [ENABLED_SERVICE, HIDDEN_SERVICE]},
++                rules=LDAPClient.MATCH_ANY
++            ),
++        ],
++        rules=LDAPClient.MATCH_ALL
++    )
+     attrs = ['cn', 'ipaConfigString']
+     if not dirsrv.is_running():
+         raise IpactlError("Failed to get list of services to probe status:\n" +
+diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py
+index b6c01d0971b827dc1547adcfff48fbcb545f4b18..f20b3dac4c7f79454a2b8871409319578ee2eb9e 100644
+--- a/ipaserver/install/server/__init__.py
++++ b/ipaserver/install/server/__init__.py
+@@ -240,6 +240,13 @@ class ServerInstallInterface(ServerCertificateInstallInterface,
+     )
+     master_password = master_install_only(master_password)
+ 
++    hidden_replica = knob(
++        None,
++        cli_names='--hidden-replica',
++        description="Install a hidden replica",
++    )
++    hidden_replica = replica_install_only(hidden_replica)
++
+     domain_level = knob(
+         int, constants.MAX_DOMAIN_LEVEL,
+         description="IPA domain level",
+diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
+index 37ecbe4146fa908c30fb708037fcaa47af1a258b..7178238bfb996f987b5e3beaebe05fa104ada089 100644
+--- a/ipaserver/install/server/replicainstall.py
++++ b/ipaserver/install/server/replicainstall.py
+@@ -1055,6 +1055,7 @@ def promote_check(installer):
+     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
+@@ -1579,8 +1580,16 @@ def install(installer):
+         remove_replica_info_dir(installer)
+ 
+     # Enable configured services and update DNS SRV records
+-    service.enable_services(config.host_name)
++    if options.hidden_replica:
++        # Set services to hidden
++        service.hide_services(config.host_name)
++    else:
++        # Enable configured services
++        service.enable_services(config.host_name)
++    # update DNS SRV records. Although it's only really necessary in
++    # enabled-service case, also perform update in hidden replica case.
+     api.Command.dns_update_system_records()
++
+     ca_servers = find_providing_servers('CA', api.Backend.ldap2, api=api)
+     api.Backend.ldap2.disconnect()
+ 
+diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
+index 261eedc85be24478b99e5ae8886aec7bc23a80ed..6d7997c559f8d748f00dd9df28371c53bc12ee21 100644
+--- a/ipaserver/install/service.py
++++ b/ipaserver/install/service.py
+@@ -39,7 +39,7 @@ from ipalib import api, errors
+ from ipaplatform import services
+ from ipaplatform.paths import paths
+ from ipaserver.masters import (
+-    CONFIGURED_SERVICE, ENABLED_SERVICE, SERVICE_LIST
++    CONFIGURED_SERVICE, ENABLED_SERVICE, HIDDEN_SERVICE, SERVICE_LIST
+ )
+ 
+ logger = logging.getLogger(__name__)
+@@ -180,7 +180,7 @@ def set_service_entry_config(name, fqdn, config_values,
+ 
+ 
+ def enable_services(fqdn):
+-    """Change all configured services to enabled
++    """Change all services to enabled state
+ 
+     Server.ldap_configure() only marks a service as configured. Services
+     are enabled at the very end of installation.
+@@ -189,15 +189,46 @@ def enable_services(fqdn):
+ 
+     :param fqdn: hostname of server
+     """
++    _set_services_state(fqdn, ENABLED_SERVICE)
++
++
++def hide_services(fqdn):
++    """Change all services to hidden state
++
++    Note: DNS records must be updated with dns_update_system_records, too.
++
++    :param fqdn: hostname of server
++    """
++    _set_services_state(fqdn, HIDDEN_SERVICE)
++
++
++def _set_services_state(fqdn, dest_state):
++    """Change all services of a host
++
++    :param fqdn: hostname of server
++    :param dest_state: destination state
++    """
+     ldap2 = api.Backend.ldap2
+     search_base = DN(('cn', fqdn), api.env.container_masters, api.env.basedn)
+-    search_filter = ldap2.make_filter(
+-        {
+-            'objectClass': 'ipaConfigObject',
+-            'ipaConfigString': CONFIGURED_SERVICE
+-        },
+-        rules='&'
++
++    source_states = {
++        CONFIGURED_SERVICE.lower(),
++        ENABLED_SERVICE.lower(),
++        HIDDEN_SERVICE.lower()
++    }
++    source_states.remove(dest_state.lower())
++
++    search_filter = ldap2.combine_filters(
++        [
++            ldap2.make_filter({'objectClass': 'ipaConfigObject'}),
++            ldap2.make_filter(
++                {'ipaConfigString': list(source_states)},
++                rules=ldap2.MATCH_ANY
++            ),
++        ],
++        rules=ldap2.MATCH_ALL
+     )
++
+     entries = ldap2.get_entries(
+         search_base,
+         filter=search_filter,
+@@ -208,10 +239,10 @@ def enable_services(fqdn):
+         name = entry['cn']
+         cfgstrings = entry.setdefault('ipaConfigString', [])
+         for value in list(cfgstrings):
+-            if value.lower() == CONFIGURED_SERVICE.lower():
++            if value.lower() in source_states:
+                 cfgstrings.remove(value)
+-        if not case_insensitive_attr_has_value(cfgstrings, ENABLED_SERVICE):
+-            cfgstrings.append(ENABLED_SERVICE)
++        if not case_insensitive_attr_has_value(cfgstrings, dest_state):
++            cfgstrings.append(dest_state)
+ 
+         try:
+             ldap2.update_entry(entry)
+@@ -221,7 +252,9 @@ def enable_services(fqdn):
+             logger.exception("failed to set service %s config values", name)
+             raise
+         else:
+-            logger.debug("Enabled service %s for %s", name, fqdn)
++            logger.debug(
++                "Set service %s for %s to %s", name, fqdn, dest_state
++            )
+ 
+ 
+ class Service(object):
+diff --git a/ipaserver/masters.py b/ipaserver/masters.py
+index 6fa8f02332ceaa10ec30aa5142912f351fb58936..76c1a9594d8b5f88c503a08b84a17e14ac320df3 100644
+--- a/ipaserver/masters.py
++++ b/ipaserver/masters.py
+@@ -19,6 +19,7 @@ logger = logging.getLogger(__name__)
+ # constants for ipaConfigString
+ CONFIGURED_SERVICE = u'configuredService'
+ ENABLED_SERVICE = u'enabledService'
++HIDDEN_SERVICE = u'hiddenService'
+ 
+ # The service name as stored in cn=masters,cn=ipa,cn=etc. The values are:
+ # 0: systemd service name
+@@ -68,30 +69,53 @@ def find_providing_servers(svcname, conn=None, preferred_hosts=(), api=api):
+         conn = api.Backend.ldap2
+ 
+     dn = DN(api.env.container_masters, api.env.basedn)
+-    query_filter = conn.make_filter(
+-        {
+-            'objectClass': 'ipaConfigObject',
+-            'ipaConfigString': ENABLED_SERVICE,
+-            'cn': svcname
+-        },
+-        rules='&'
++
++    query_filter = conn.combine_filters(
++        [
++            conn.make_filter(
++                {
++                    'objectClass': 'ipaConfigObject',
++                    'cn': svcname
++                },
++                rules=conn.MATCH_ALL,
++            ),
++            conn.make_filter(
++                {
++                    'ipaConfigString': [ENABLED_SERVICE, HIDDEN_SERVICE]
++                },
++                rules=conn.MATCH_ANY
++            ),
++        ],
++        rules=conn.MATCH_ALL
+     )
++
+     try:
+         entries, _trunc = conn.find_entries(
+             filter=query_filter,
+-            attrs_list=[],
++            attrs_list=['ipaConfigString'],
+             base_dn=dn
+         )
+     except errors.NotFound:
+         return []
+ 
+-    # unique list of host names, DNS is case insensitive
+-    servers = list(set(entry.dn[1].value.lower() for entry in entries))
++    # DNS is case insensitive
++    preferred_hosts = list(host_name.lower() for host_name in preferred_hosts)
++    servers = []
++    for entry in entries:
++        servername = entry.dn[1].value.lower()
++        cfgstrings = entry.get('ipaConfigString', [])
++        # always consider enabled services
++        if ENABLED_SERVICE in cfgstrings:
++            servers.append(servername)
++        # use hidden services on preferred hosts
++        elif HIDDEN_SERVICE in cfgstrings and servername in preferred_hosts:
++            servers.append(servername)
++    # unique list of host names
++    servers = list(set(servers))
+     # shuffle the list like DNS SRV would randomize it
+     random.shuffle(servers)
+     # Move preferred hosts to front
+     for host_name in reversed(preferred_hosts):
+-        host_name = host_name.lower()
+         try:
+             servers.remove(host_name)
+         except ValueError:
+diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py
+index 199978000ce8cf783bda50c46b7c9fa109f70ad6..1f6d2dca518d374d7bd07e96019610e3ef6430be 100644
+--- a/ipaserver/plugins/serverrole.py
++++ b/ipaserver/plugins/serverrole.py
+@@ -70,7 +70,7 @@ class server_role(Object):
+             cli_name='status',
+             label=_('Role status'),
+             doc=_('Status of the role'),
+-            values=(u'enabled', u'configured', u'absent'),
++            values=(u'enabled', u'configured', u'hidden', u'absent'),
+             default=u'enabled',
+             flags={'virtual_attribute', 'no_create', 'no_update'}
+         )
+diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
+index af4e63710136a15e1673210c3e2207658698fbb5..02a22e77dbb615f735660c53d1b2eb7da022591d 100644
+--- a/ipaserver/servroles.py
++++ b/ipaserver/servroles.py
+@@ -79,7 +79,7 @@ import six
+ 
+ from ipalib import _, errors
+ from ipapython.dn import DN
+-from ipaserver.masters import ENABLED_SERVICE
++from ipaserver.masters import ENABLED_SERVICE, HIDDEN_SERVICE
+ 
+ if six.PY3:
+     unicode = str
+@@ -87,6 +87,7 @@ if six.PY3:
+ 
+ ENABLED = u'enabled'
+ CONFIGURED = u'configured'
++HIDDEN = u'hidden'
+ ABSENT = u'absent'
+ 
+ 
+@@ -190,6 +191,7 @@ class BaseServerRole(LDAPBasedProperty):
+         :returns: * 'enabled' if the role is enabled on the master
+                   * 'configured' if it is not enabled but has
+                     been configured by installer
++                  * 'hidden' if the role is not advertised
+                   * 'absent' otherwise
+         """
+         ldap2 = api_instance.Backend.ldap2
+@@ -442,7 +444,7 @@ class SingleValuedServerAttribute(ServerAttribute):
+         return masters
+ 
+ 
+-_Service = namedtuple('Service', ['name', 'enabled'])
++_Service = namedtuple('Service', ['name', 'enabled', 'hidden'])
+ 
+ 
+ class ServiceBasedRole(BaseServerRole):
+@@ -470,8 +472,9 @@ class ServiceBasedRole(BaseServerRole):
+         entry_cn = entry['cn'][0]
+ 
+         enabled = self._is_service_enabled(entry)
++        hidden = self._is_service_hidden(entry)
+ 
+-        return _Service(name=entry_cn, enabled=enabled)
++        return _Service(name=entry_cn, enabled=enabled, hidden=hidden)
+ 
+     def _is_service_enabled(self, entry):
+         """
+@@ -486,6 +489,15 @@ class ServiceBasedRole(BaseServerRole):
+         ipaconfigstring_values = set(entry.get('ipaConfigString', []))
+         return ENABLED_SERVICE in ipaconfigstring_values
+ 
++    def _is_service_hidden(self, entry):
++        """Determine if service is hidden
++
++        :param entry: LDAPEntry of the service
++        :returns: True if the service entry is enabled, False otherwise
++        """
++        ipaconfigstring_values = set(entry.get('ipaConfigString', []))
++        return HIDDEN_SERVICE in ipaconfigstring_values
++
+     def _get_services_by_masters(self, entries):
+         """
+         given list of entries, return a dictionary keyed by master FQDNs which
+@@ -509,9 +521,12 @@ class ServiceBasedRole(BaseServerRole):
+             except ValueError:
+                 continue
+ 
+-            status = (
+-                ENABLED if all(s.enabled for s in services) else
+-                CONFIGURED)
++            if all(s.enabled for s in services):
++                status = ENABLED
++            elif all(s.hidden for s in services):
++                status = HIDDEN
++            else:
++                status = CONFIGURED
+ 
+             result.append(self.create_role_status_dict(master, status))
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0019-Fix-DNSSEC-install-regression.patch b/SOURCES/0019-Fix-DNSSEC-install-regression.patch
deleted file mode 100644
index de4e93b..0000000
--- a/SOURCES/0019-Fix-DNSSEC-install-regression.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 8110958d141392014bf3e3eae01654e7864d7647 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Tue, 10 Jul 2018 12:51:36 +0200
-Subject: [PATCH] Fix DNSSEC install regression
-
-7284097eedef70dd556270732e6ab8e23501ce09 introduced a regression in
-DNSSEC master installation. For standalone and replica installation,
-services have to be enabled before checking bind config.
-
-Fixes: https://pagure.io/freeipa/issue/7635
-See: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
----
- install/tools/ipa-dns-install | 5 +----
- ipaserver/install/dns.py      | 5 +++++
- 2 files changed, 6 insertions(+), 4 deletions(-)
-
-diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
-index 57dde5a5da4fad162c93e9e0416b54961de4c1e3..32a17d223ae2bdd9a1ded62defcc272a40d2627b 100755
---- a/install/tools/ipa-dns-install
-+++ b/install/tools/ipa-dns-install
-@@ -37,7 +37,6 @@ from ipapython.config import IPAOptionParser
- from ipapython.ipa_log_manager import standard_logging_setup
- 
- from ipaserver.install import dns as dns_installer
--from ipaserver.install import service
- 
- logger = logging.getLogger(os.path.basename(__file__))
- 
-@@ -149,9 +148,7 @@ def main():
- 
-     dns_installer.install_check(True, api, False, options, hostname=api.env.host)
-     dns_installer.install(True, False, options)
--    # Enable configured services and update DNS SRV records
--    service.enable_services(api.env.host)
--    api.Command.dns_update_system_records()
-+    # Services are enabled in dns_installer.install()
- 
-     # execute ipactl to refresh services status
-     ipautil.run(['ipactl', 'start', '--ignore-service-failures'],
-diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
-index e14b353e9cb655a6e7ef228d47dfc7a1badd7286..cac7a9213796d6618854b12da6c2a7fe60afdbf9 100644
---- a/ipaserver/install/dns.py
-+++ b/ipaserver/install/dns.py
-@@ -44,6 +44,7 @@ from ipaserver.install import bindinstance
- from ipaserver.install import dnskeysyncinstance
- from ipaserver.install import odsexporterinstance
- from ipaserver.install import opendnssecinstance
-+from ipaserver.install import service
- 
- if six.PY3:
-     unicode = str
-@@ -357,6 +358,10 @@ def install(standalone, replica, options, api=api):
-     dnskeysyncd.start_dnskeysyncd()
-     bind.start_named()
- 
-+    # Enable configured services for standalone check_global_configuration()
-+    if standalone:
-+        service.enable_services(api.env.host)
-+
-     # this must be done when bind is started and operational
-     bind.update_system_records()
- 
--- 
-2.17.1
-
diff --git a/SOURCES/0020-Handle-races-in-replica-config.patch b/SOURCES/0020-Handle-races-in-replica-config.patch
deleted file mode 100644
index 466a33d..0000000
--- a/SOURCES/0020-Handle-races-in-replica-config.patch
+++ /dev/null
@@ -1,267 +0,0 @@
-From 0598ed6e1c48c6600774c53065716aad759da5e7 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Tue, 10 Jul 2018 14:03:28 +0200
-Subject: [PATCH] Handle races in replica config
-
-When multiple replicas are installed in parallel, two replicas may try
-to create the cn=replica entry at the same time. This leads to a
-conflict on one of the replicas. replica_config() and
-ensure_replication_managers() now handle conflicts.
-
-ipaldap now maps TYPE_OR_VALUE_EXISTS to DuplicateEntry(). The type or
-value exists exception is raised, when an attribute value or type is
-already set.
-
-Fixes: https://pagure.io/freeipa/issue/7566
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
----
- ipapython/ipaldap.py             |   5 ++
- ipaserver/install/replication.py | 123 ++++++++++++++++++-------------
- ipaserver/plugins/ldap2.py       |   3 +-
- 3 files changed, 79 insertions(+), 52 deletions(-)
-
-diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py
-index dcac32c31c670753b83f99eac7f6483fbad2b14d..53fdf4967868961effea7f3f64dfb3c0edfc75f3 100644
---- a/ipapython/ipaldap.py
-+++ b/ipapython/ipaldap.py
-@@ -1016,7 +1016,12 @@ class LDAPClient(object):
-         except ldap.NO_SUCH_OBJECT:
-             raise errors.NotFound(reason=arg_desc or 'no such entry')
-         except ldap.ALREADY_EXISTS:
-+            # entry already exists
-             raise errors.DuplicateEntry()
-+        except ldap.TYPE_OR_VALUE_EXISTS:
-+            # attribute type or attribute value already exists, usually only
-+            # occurs, when two machines try to write at the same time.
-+            raise errors.DuplicateEntry(message=unicode(desc))
-         except ldap.CONSTRAINT_VIOLATION:
-             # This error gets thrown by the uniqueness plugin
-             _msg = 'Another entry with the same attribute value already exists'
-diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
-index d0f92c76c108d237104a81c72567250583ac4ff1..78c4a43cc9b8d9a6740d26209f347f64b879743e 100644
---- a/ipaserver/install/replication.py
-+++ b/ipaserver/install/replication.py
-@@ -34,7 +34,7 @@ import ldap
- from ipalib import api, errors
- from ipalib.cli import textui
- from ipalib.text import _
--from ipapython import ipautil, ipaldap, kerberos
-+from ipapython import ipautil, ipaldap
- from ipapython.admintool import ScriptError
- from ipapython.dn import DN
- from ipapython.ipaldap import ldap_initialize
-@@ -461,7 +461,7 @@ class ReplicationManager(object):
-         return DN(('cn', 'replica'), ('cn', self.db_suffix),
-                   ('cn', 'mapping tree'), ('cn', 'config'))
- 
--    def set_replica_binddngroup(self, r_conn, entry):
-+    def _set_replica_binddngroup(self, r_conn, entry):
-         """
-         Set nsds5replicabinddngroup attribute on remote master's replica entry.
-         Older masters (ipa < 3.3) may not support setting this attribute. In
-@@ -476,11 +476,6 @@ class ReplicationManager(object):
-             mod.append((ldap.MOD_ADD, 'nsds5replicabinddngroup',
-                         self.repl_man_group_dn))
- 
--        if 'nsds5replicabinddngroupcheckinterval' not in entry:
--            mod.append(
--                (ldap.MOD_ADD,
--                 'nsds5replicabinddngroupcheckinterval',
--                 '60'))
-         if mod:
-             try:
-                 r_conn.modify_s(entry.dn, mod)
-@@ -488,49 +483,62 @@ class ReplicationManager(object):
-                 logger.debug(
-                     "nsds5replicabinddngroup attribute not supported on "
-                     "remote master.")
-+            except (ldap.ALREADY_EXISTS, ldap.CONSTRAINT_VIOLATION):
-+                logger.debug("No update to %s necessary", entry.dn)
- 
-     def replica_config(self, conn, replica_id, replica_binddn):
-         assert isinstance(replica_binddn, DN)
-         dn = self.replica_dn()
-         assert isinstance(dn, DN)
- 
-+        logger.debug("Add or update replica config %s", dn)
-         try:
-             entry = conn.get_entry(dn)
-         except errors.NotFound:
--            pass
--        else:
--            binddns = entry.setdefault('nsDS5ReplicaBindDN', [])
--            if replica_binddn not in {DN(m) for m in binddns}:
--                # Add the new replication manager
--                binddns.append(replica_binddn)
--            for key, value in REPLICA_CREATION_SETTINGS.items():
--                entry[key] = value
-+            # no entry, create new one
-+            entry = conn.make_entry(
-+                dn,
-+                objectclass=["top", "nsds5replica", "extensibleobject"],
-+                cn=["replica"],
-+                nsds5replicaroot=[str(self.db_suffix)],
-+                nsds5replicaid=[str(replica_id)],
-+                nsds5replicatype=[self.get_replica_type()],
-+                nsds5flags=["1"],
-+                nsds5replicabinddn=[replica_binddn],
-+                nsds5replicabinddngroup=[self.repl_man_group_dn],
-+                nsds5replicalegacyconsumer=["off"],
-+                **REPLICA_CREATION_SETTINGS
-+            )
-             try:
--                conn.update_entry(entry)
--            except errors.EmptyModlist:
--                pass
-+                conn.add_entry(entry)
-+            except errors.DuplicateEntry:
-+                logger.debug("Lost race against another replica, updating")
-+                # fetch entry that have been added by another replica
-+                entry = conn.get_entry(dn)
-+            else:
-+                logger.debug("Added replica config %s", dn)
-+                # added entry successfully
-+                return entry
- 
--            self.set_replica_binddngroup(conn, entry)
-+        # either existing entry or lost race
-+        binddns = entry.setdefault('nsDS5ReplicaBindDN', [])
-+        if replica_binddn not in {DN(m) for m in binddns}:
-+            # Add the new replication manager
-+            binddns.append(replica_binddn)
- 
--            # replication is already configured
--            return
-+        for key, value in REPLICA_CREATION_SETTINGS.items():
-+            entry[key] = value
- 
--        replica_type = self.get_replica_type()
-+        try:
-+            conn.update_entry(entry)
-+        except errors.EmptyModlist:
-+            logger.debug("No update to %s necessary", entry.dn)
-+        else:
-+            logger.debug("Update replica config %s", entry.dn)
- 
--        entry = conn.make_entry(
--            dn,
--            objectclass=["top", "nsds5replica", "extensibleobject"],
--            cn=["replica"],
--            nsds5replicaroot=[str(self.db_suffix)],
--            nsds5replicaid=[str(replica_id)],
--            nsds5replicatype=[replica_type],
--            nsds5flags=["1"],
--            nsds5replicabinddn=[replica_binddn],
--            nsds5replicabinddngroup=[self.repl_man_group_dn],
--            nsds5replicalegacyconsumer=["off"],
--            **REPLICA_CREATION_SETTINGS
--        )
--        conn.add_entry(entry)
-+        self._set_replica_binddngroup(conn, entry)
-+
-+        return entry
- 
-     def setup_changelog(self, conn):
-         ent = conn.get_entry(
-@@ -690,7 +698,10 @@ class ReplicationManager(object):
-                 uid=["passsync"],
-                 userPassword=[password],
-             )
--            conn.add_entry(entry)
-+            try:
-+                conn.add_entry(entry)
-+            except errors.DuplicateEntry:
-+                pass
- 
-         # Add the user to the list of users allowed to bypass password policy
-         extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config'))
-@@ -1658,7 +1669,10 @@ class ReplicationManager(object):
-             objectclass=['top', 'groupofnames'],
-             cn=['replication managers']
-         )
--        conn.add_entry(entry)
-+        try:
-+            conn.add_entry(entry)
-+        except errors.DuplicateEntry:
-+            pass
- 
-     def ensure_replication_managers(self, conn, r_hostname):
-         """
-@@ -1668,23 +1682,24 @@ class ReplicationManager(object):
-         On FreeIPA 3.x masters lacking support for nsds5ReplicaBinddnGroup
-         attribute, add replica bind DN directly into the replica entry.
-         """
--        my_princ = kerberos.Principal((u'ldap', unicode(self.hostname)),
--                                      realm=self.realm)
--        remote_princ = kerberos.Principal((u'ldap', unicode(r_hostname)),
--                                          realm=self.realm)
--        services_dn = DN(api.env.container_service, api.env.basedn)
--
--        mydn, remote_dn = tuple(
--            DN(('krbprincipalname', unicode(p)), services_dn) for p in (
--                my_princ, remote_princ))
-+        my_dn = DN(
-+            ('krbprincipalname', u'ldap/%s@%s' % (self.hostname, self.realm)),
-+            api.env.container_service,
-+            api.env.basedn
-+        )
-+        remote_dn = DN(
-+            ('krbprincipalname', u'ldap/%s@%s' % (r_hostname, self.realm)),
-+            api.env.container_service,
-+            api.env.basedn
-+        )
- 
-         try:
-             conn.get_entry(self.repl_man_group_dn)
-         except errors.NotFound:
--            self._add_replica_bind_dn(conn, mydn)
-+            self._add_replica_bind_dn(conn, my_dn)
-             self._add_replication_managers(conn)
- 
--        self._add_dn_to_replication_managers(conn, mydn)
-+        self._add_dn_to_replication_managers(conn, my_dn)
-         self._add_dn_to_replication_managers(conn, remote_dn)
- 
-     def add_temp_sasl_mapping(self, conn, r_hostname):
-@@ -1704,7 +1719,10 @@ class ReplicationManager(object):
- 
-         entry = conn.get_entry(self.replica_dn())
-         entry['nsDS5ReplicaBindDN'].append(replica_binddn)
--        conn.update_entry(entry)
-+        try:
-+            conn.update_entry(entry)
-+        except errors.EmptyModlist:
-+            pass
- 
-         entry = conn.make_entry(
-             DN(('cn', 'Peer Master'), ('cn', 'mapping'), ('cn', 'sasl'),
-@@ -1716,7 +1734,10 @@ class ReplicationManager(object):
-             nsSaslMapFilterTemplate=['(cn=&@%s)' % self.realm],
-             nsSaslMapPriority=['1'],
-         )
--        conn.add_entry(entry)
-+        try:
-+            conn.add_entry(entry)
-+        except errors.DuplicateEntry:
-+            pass
- 
-     def remove_temp_replication_user(self, conn, r_hostname):
-         """
-diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py
-index 8745866ad11cf6248d2b9dcff560c0a5ee6d109b..5d39cdf628d7e20ce5b259d97b479ef7c8ed88d5 100644
---- a/ipaserver/plugins/ldap2.py
-+++ b/ipaserver/plugins/ldap2.py
-@@ -419,7 +419,8 @@ class ldap2(CrudBackend, LDAPClient):
-                 modlist = [(a, b, self.encode(c))
-                            for a, b, c in modlist]
-                 self.conn.modify_s(str(group_dn), modlist)
--        except errors.DatabaseError:
-+        except errors.DuplicateEntry:
-+            # TYPE_OR_VALUE_EXISTS
-             raise errors.AlreadyGroupMember()
- 
-     def remove_entry_from_group(self, dn, group_dn, member_attr='member'):
--- 
-2.17.1
-
diff --git a/SOURCES/0020-ipatests-Exercise-hidden-replica-feature.patch b/SOURCES/0020-ipatests-Exercise-hidden-replica-feature.patch
new file mode 100644
index 0000000..fbb0dc9
--- /dev/null
+++ b/SOURCES/0020-ipatests-Exercise-hidden-replica-feature.patch
@@ -0,0 +1,150 @@
+From f643289f42a0d537da2e8ab6be4727d0bc679690 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= <fcami@redhat.com>
+Date: Fri, 22 Mar 2019 19:29:01 +0100
+Subject: [PATCH] ipatests: Exercise hidden replica feature
+
+A hidden replica is a replica that does not advertise its services via
+DNS SRV records, ipa-ca DNS entry, or LDAP. Clients do not auto-select a
+hidden replica, but are still free to explicitly connect to it.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Co-authored-by: Francois Cami <fcami@redhat.com>
+Signed-off-by: Francois Cami <fcami@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ .../test_replica_promotion.py                 | 114 ++++++++++++++++++
+ 1 file changed, 114 insertions(+)
+
+diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
+index 6608b23f7fb37948d54c21c88d572527356e7335..80890bf05cb242fe09af77aa27b411ac6194e2d6 100644
+--- a/ipatests/test_integration/test_replica_promotion.py
++++ b/ipatests/test_integration/test_replica_promotion.py
+@@ -17,6 +17,10 @@ from ipatests.pytest_ipa.integration.env_config import get_global_config
+ from ipalib.constants import (
+     DOMAIN_LEVEL_0, DOMAIN_LEVEL_1, DOMAIN_SUFFIX_NAME, IPA_CA_NICKNAME)
+ from ipaplatform.paths import paths
++from ipatests.test_integration.test_backup_and_restore import backup
++from ipatests.test_integration.test_dns_locations import (
++    resolve_records_from_server
++)
+ 
+ config = get_global_config()
+ 
+@@ -795,3 +799,113 @@ class TestReplicaInForwardZone(IntegrationTest):
+             # Restore /etc/hosts on master and replica
+             restore_etc_hosts(master)
+             restore_etc_hosts(replica)
++
++
++class TestHiddenReplicaPromotion(IntegrationTest):
++    """
++    Test hidden replica features
++    """
++
++    topology = 'star'
++    num_replicas = 1
++
++    @classmethod
++    def install(cls, mh):
++        tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
++
++    @replicas_cleanup
++    def test_hidden_replica_install(self):
++        self.replicas[0].run_command([
++            'ipa-client-install',
++            '-p', 'admin',
++            '-w', self.master.config.admin_password,
++            '--domain', self.master.domain.name,
++            '--realm', self.master.domain.realm,
++            '--server', self.master.hostname,
++            '-U'
++        ])
++        self.replicas[0].run_command([
++            'ipa-replica-install', '-w',
++            self.master.config.admin_password,
++            '-n', self.master.domain.name,
++            '-r', self.master.domain.realm,
++            '--server', self.master.hostname,
++            '--setup-ca',
++            '--setup-dns', '--no-forwarders',
++            '--hidden-replica',
++            '--setup-kra',
++            '-U'
++        ])
++        expected_txt = 'hidden'
++        result = self.replicas[0].run_command([
++            'ipa', 'ipa server-role-find',
++            '--server', self.replicas[0].hostname
++        ])
++        assert expected_txt in result.stdout
++        dnsrecords = {
++            '.'.join(('_kerberos._udp', self.master.domain.name)): 'SRV',
++            '.'.join(('_kerberos._tcp', self.master.domain.name)): 'SRV',
++            '.'.join(('_ldap._tcp', self.master.domain.name)): 'SRV',
++            self.master.domain.name: 'NS'
++        }
++        nameserver = self.master.ip
++        results = []
++        for record in dnsrecords:
++            srvr = resolve_records_from_server(
++                record, dnsrecords[record], nameserver
++            )
++            results.extend(re.findall(
++                '|'.join((self.master.hostname, self.replicas[0].hostname)),
++                srvr)
++            )
++        assert self.master.hostname in results
++        assert self.replicas[0].hostname not in results
++
++    def test_hidden_replica_promote(self):
++        self.replicas[0].run_command([
++            'ipa', 'server-mod', '--state=enabled'
++        ])
++        unexpected_txt = 'hidden'
++        result = self.replicas[0].run_command([
++            'ipa', 'ipa server-role-find',
++            '--server', self.replicas[0].hostname
++        ])
++        assert unexpected_txt not in result.stdout
++
++    def test_hidden_replica_demote(self):
++        self.replicas[0].run_command([
++            'ipa', 'server-mod', '--state=hidden'
++        ])
++        expected_txt = 'hidden'
++        result = self.replicas[0].run_command([
++            'ipa', 'ipa server-role-find',
++            '--server', self.replicas[0].hostname
++        ])
++        assert expected_txt in result.stdout
++
++    def test_hidden_replica_backup_and_restore(self):
++        """
++        Exercises backup+restore and hidden replica uninstall
++        """
++        # set expectations
++        expected_txt = 'hidden'
++        result = self.replicas[0].run_command([
++            'ipa', 'ipa server-role-find',
++            '--server', self.replicas[0].hostname
++        ])
++        assert expected_txt in result.stdout
++        # backup
++        backup_path = backup(self.replicas[0])
++        # uninstall
++        result = self.replicas[0].run_command([
++            'ipa-server-uninstall', '-U', 'hidden-replica'
++        ])
++        # restore
++        dirman_password = self.master.config.dirman_password
++        self.replicas[0].run_command(
++            ['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes'
++        )
++        # check that the resulting server can be promoted to enabled
++        self.replicas[0].run_command([
++            'ipa', 'server-mod', '--state=enabled'
++        ])
+-- 
+2.20.1
+
diff --git a/SOURCES/0021-Fix-regression-Handle-unicode-where-str-is-expected.patch b/SOURCES/0021-Fix-regression-Handle-unicode-where-str-is-expected.patch
deleted file mode 100644
index 5e8c1ab..0000000
--- a/SOURCES/0021-Fix-regression-Handle-unicode-where-str-is-expected.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From d12aa9762dcd841661cc4fb384a4700d53b5bef2 Mon Sep 17 00:00:00 2001
-From: Armando Neto <abiagion@redhat.com>
-Date: Tue, 17 Jul 2018 16:08:49 -0300
-Subject: [PATCH] Fix regression: Handle unicode where str is expected
-
-Regression caused by 947ac4bc1f6f4016cf5baf2ecb4577e893bc3948 when
-trying to fix a similar issue for clients running Python 3. However,
-that fix broke Python 2 clients.
-
-Issue: https://pagure.io/freeipa/issue/7626
-
-Signed-off-by: Armando Neto <abiagion@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaclient/remote_plugins/schema.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py
-index 863d8f19923898c0a531c68189dd01b401e531b4..9d2c8ca9d1d3e820154425c4f9e4e39cd64d6fd1 100644
---- a/ipaclient/remote_plugins/schema.py
-+++ b/ipaclient/remote_plugins/schema.py
-@@ -602,7 +602,7 @@ def get_package(server_info, client):
-             s = topic['topic_topic']
-             if isinstance(s, bytes):
-                 s = s.decode('utf-8')
--            module.topic = s.partition('/')[0]
-+            module.topic = str(s).partition('/')[0]
-         else:
-             module.topic = None
- 
--- 
-2.17.1
-
diff --git a/SOURCES/0021-Simplify-and-improve-tests.patch b/SOURCES/0021-Simplify-and-improve-tests.patch
new file mode 100644
index 0000000..699780f
--- /dev/null
+++ b/SOURCES/0021-Simplify-and-improve-tests.patch
@@ -0,0 +1,212 @@
+From a88af3d2f21b6a949885981aa82ff87a1336f40c Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Mon, 25 Mar 2019 08:17:28 +0100
+Subject: [PATCH] Simplify and improve tests
+
+Move tests for DNS and roles into helper methods to make them reusable.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ .../test_replica_promotion.py                 | 147 +++++++++---------
+ 1 file changed, 70 insertions(+), 77 deletions(-)
+
+diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
+index 80890bf05cb242fe09af77aa27b411ac6194e2d6..a4f3e402ce5d6f74af4bd6fed9376f0f039f297a 100644
+--- a/ipatests/test_integration/test_replica_promotion.py
++++ b/ipatests/test_integration/test_replica_promotion.py
+@@ -19,8 +19,11 @@ from ipalib.constants import (
+ from ipaplatform.paths import paths
+ from ipatests.test_integration.test_backup_and_restore import backup
+ from ipatests.test_integration.test_dns_locations import (
+-    resolve_records_from_server
++    resolve_records_from_server, IPA_DEFAULT_MASTER_SRV_REC
+ )
++from ipapython.dnsutil import DNSName
++from ipalib.constants import IPA_CA_RECORD
++
+ 
+ config = get_global_config()
+ 
+@@ -802,110 +805,100 @@ class TestReplicaInForwardZone(IntegrationTest):
+ 
+ 
+ class TestHiddenReplicaPromotion(IntegrationTest):
++    """Test hidden replica features
+     """
+-    Test hidden replica features
+-    """
+-
+     topology = 'star'
+     num_replicas = 1
+ 
+     @classmethod
+     def install(cls, mh):
+         tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
++        tasks.install_replica(
++            cls.master, cls.replicas[0],
++            setup_dns=True, setup_kra=True,
++            extra_args=('--hidden-replica',)
++        )
+ 
+-    @replicas_cleanup
+-    def test_hidden_replica_install(self):
+-        self.replicas[0].run_command([
+-            'ipa-client-install',
+-            '-p', 'admin',
+-            '-w', self.master.config.admin_password,
+-            '--domain', self.master.domain.name,
+-            '--realm', self.master.domain.realm,
+-            '--server', self.master.hostname,
+-            '-U'
+-        ])
+-        self.replicas[0].run_command([
+-            'ipa-replica-install', '-w',
+-            self.master.config.admin_password,
+-            '-n', self.master.domain.name,
+-            '-r', self.master.domain.realm,
+-            '--server', self.master.hostname,
+-            '--setup-ca',
+-            '--setup-dns', '--no-forwarders',
+-            '--hidden-replica',
+-            '--setup-kra',
+-            '-U'
+-        ])
+-        expected_txt = 'hidden'
+-        result = self.replicas[0].run_command([
+-            'ipa', 'ipa server-role-find',
+-            '--server', self.replicas[0].hostname
+-        ])
+-        assert expected_txt in result.stdout
+-        dnsrecords = {
+-            '.'.join(('_kerberos._udp', self.master.domain.name)): 'SRV',
+-            '.'.join(('_kerberos._tcp', self.master.domain.name)): 'SRV',
+-            '.'.join(('_ldap._tcp', self.master.domain.name)): 'SRV',
+-            self.master.domain.name: 'NS'
+-        }
+-        nameserver = self.master.ip
+-        results = []
+-        for record in dnsrecords:
+-            srvr = resolve_records_from_server(
+-                record, dnsrecords[record], nameserver
+-            )
+-            results.extend(re.findall(
+-                '|'.join((self.master.hostname, self.replicas[0].hostname)),
+-                srvr)
++    def _check_dnsrecords(self, hosts_expected, hosts_unexpected=()):
++        domain = DNSName(self.master.domain.name).make_absolute()
++        rset = [
++            (rname, 'SRV')
++            for rname, _port in IPA_DEFAULT_MASTER_SRV_REC
++        ]
++        rset.append((DNSName(IPA_CA_RECORD), 'A'))
++
++        for rname, rtype in rset:
++            name_abs = rname.derelativize(domain)
++            query = resolve_records_from_server(
++                name_abs, rtype, self.master.ip
+             )
+-        assert self.master.hostname in results
+-        assert self.replicas[0].hostname not in results
++            txt = query.to_text()
++            for host in hosts_expected:
++                value = host.hostname if rtype == 'SRV' else host.ip
++                assert value in txt
++            for host in hosts_unexpected:
++                value = host.hostname if rtype == 'SRV' else host.ip
++                assert value not in txt
++
++    def _check_server_role(self, host, status):
++        roles = [u'IPA master', u'CA server', u'KRA server', u'DNS server']
++        for role in roles:
++            result = self.master.run_command([
++                'ipa', 'server-role-find',
++                '--server', host.hostname,
++                '--role', role
++            ])
++            expected = 'Role status: {}'.format(status)
++            assert expected in result.stdout_text
++
++    def test_hidden_replica_install(self):
++        # TODO: check that all services are running on hidden replica
++        self._check_server_role(self.master, 'enabled')
++        self._check_server_role(self.replicas[0], 'hidden')
++        self._check_dnsrecords([self.master], [self.replicas[0]])
+ 
+     def test_hidden_replica_promote(self):
+         self.replicas[0].run_command([
+-            'ipa', 'server-mod', '--state=enabled'
++            'ipa', 'server-state',
++            self.replicas[0].hostname, '--state=enabled'
+         ])
+-        unexpected_txt = 'hidden'
++        self._check_server_role(self.replicas[0], 'enabled')
++        self._check_dnsrecords([self.master, self.replicas[0]])
+         result = self.replicas[0].run_command([
+-            'ipa', 'ipa server-role-find',
+-            '--server', self.replicas[0].hostname
+-        ])
+-        assert unexpected_txt not in result.stdout
++            'ipa', 'server-state',
++            self.replicas[0].hostname, '--state=enabled'
++        ], raiseonerr=False)
++        assert result.returncode == 1
++        assert 'no modifications to be performed' in result.stderr_text
+ 
+     def test_hidden_replica_demote(self):
+         self.replicas[0].run_command([
+-            'ipa', 'server-mod', '--state=hidden'
++            'ipa', 'server-state',
++            self.replicas[0].hostname, '--state=hidden'
+         ])
+-        expected_txt = 'hidden'
+-        result = self.replicas[0].run_command([
+-            'ipa', 'ipa server-role-find',
+-            '--server', self.replicas[0].hostname
+-        ])
+-        assert expected_txt in result.stdout
++        self._check_server_role(self.replicas[0], 'hidden')
++        self._check_dnsrecords([self.master], [self.replicas[0]])
+ 
+     def test_hidden_replica_backup_and_restore(self):
++        """Exercises backup+restore and hidden replica uninstall
+         """
+-        Exercises backup+restore and hidden replica uninstall
+-        """
+-        # set expectations
+-        expected_txt = 'hidden'
+-        result = self.replicas[0].run_command([
+-            'ipa', 'ipa server-role-find',
+-            '--server', self.replicas[0].hostname
+-        ])
+-        assert expected_txt in result.stdout
++        self._check_server_role(self.replicas[0], 'hidden')
+         # backup
+         backup_path = backup(self.replicas[0])
+         # uninstall
+-        result = self.replicas[0].run_command([
+-            'ipa-server-uninstall', '-U', 'hidden-replica'
+-        ])
++        tasks.uninstall_replica(self.master, self.replicas[0])
+         # restore
+         dirman_password = self.master.config.dirman_password
+         self.replicas[0].run_command(
+-            ['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes'
++            ['ipa-restore', backup_path],
++            stdin_text=dirman_password + '\nyes'
+         )
++        # check that role is still hidden
++        self._check_server_role(self.replicas[0], 'hidden')
++        self._check_dnsrecords([self.master], [self.replicas[0]])
+         # check that the resulting server can be promoted to enabled
+         self.replicas[0].run_command([
+-            'ipa', 'server-mod', '--state=enabled'
++            'ipa', 'server-mod', self.replicas[0].hostname, '--state=enabled'
+         ])
++        self._check_server_role(self.replicas[0], 'enabled')
++        self._check_dnsrecords([self.master, self.replicas[0]])
+-- 
+2.20.1
+
diff --git a/SOURCES/0022-Implement-server-state-state-enabled-hidden.patch b/SOURCES/0022-Implement-server-state-state-enabled-hidden.patch
new file mode 100644
index 0000000..6b4b812
--- /dev/null
+++ b/SOURCES/0022-Implement-server-state-state-enabled-hidden.patch
@@ -0,0 +1,129 @@
+From 483fea9c199d76f1e759241ad32c990f5e0eaabc Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Mon, 25 Mar 2019 08:36:53 +0100
+Subject: [PATCH] Implement server-state --state=enabled/hidden
+
+server-state modified the hidden / enabled flags of all configured
+services of a server. Since the command does not directly modify the
+server LDAP entry, the command has to be implemented as a dedicated plugin.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ API.txt                     |  9 ++++++
+ ipaserver/plugins/server.py | 58 +++++++++++++++++++++++++++++++++++--
+ 2 files changed, 65 insertions(+), 2 deletions(-)
+
+diff --git a/API.txt b/API.txt
+index 2135300183e3dc2126309e8f892e79fe6b5178fb..222e30915ccc1fb4a6f3ce228669453f346fdde4 100644
+--- a/API.txt
++++ b/API.txt
+@@ -4471,6 +4471,14 @@ option: Str('version?')
+ output: Entry('result')
+ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
+ output: PrimaryKey('value')
++command: server_state/1
++args: 1,2,3
++arg: Str('cn', cli_name='name')
++option: StrEnum('state', values=[u'enabled', u'hidden'])
++option: Str('version?')
++output: Output('result', type=[<type 'bool'>])
++output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
++output: PrimaryKey('value')
+ command: service_add/1
+ args: 1,13,3
+ arg: Principal('krbcanonicalname', cli_name='canonical_principal')
+@@ -6900,6 +6908,7 @@ default: server_role/1
+ default: server_role_find/1
+ default: server_role_show/1
+ default: server_show/1
++default: server_state/1
+ default: service/1
+ default: service_add/1
+ default: service_add_cert/1
+diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py
+index e265883e3637938e3df5ecf132f4add62413a997..0d144d13bca66b65de64328139fd7126eea24c89 100644
+--- a/ipaserver/plugins/server.py
++++ b/ipaserver/plugins/server.py
+@@ -12,7 +12,7 @@ import ldap
+ import time
+ 
+ from ipalib import api, crud, errors, messages
+-from ipalib import Int, Flag, Str, DNSNameParam
++from ipalib import Int, Flag, Str, StrEnum, DNSNameParam
+ from ipalib.plugable import Registry
+ from .baseldap import (
+     LDAPSearch,
+@@ -28,8 +28,9 @@ from ipaplatform import services
+ from ipapython.dn import DN
+ from ipapython.dnsutil import DNSName
+ from ipaserver import topology
+-from ipaserver.servroles import ENABLED
++from ipaserver.servroles import ENABLED, HIDDEN
+ from ipaserver.install import bindinstance, dnskeysyncinstance
++from ipaserver.install.service import hide_services, enable_services
+ 
+ __doc__ = _("""
+ IPA servers
+@@ -949,3 +950,56 @@ class server_conncheck(crud.PKQuery):
+                                  messages.ExternalCommandOutput(line=line))
+ 
+         return result
++
++
++@register()
++class server_state(crud.PKQuery):
++    __doc__ = _("Set enabled/hidden state of a server.")
++
++    takes_options = (
++        StrEnum(
++            'state',
++            values=(u'enabled', u'hidden'),
++            label=_('State'),
++            doc=_('Server state'),
++            flags={'virtual_attribute', 'no_create', 'no_search'},
++        ),
++    )
++
++    msg_summary = _('Changed server state of "%(value)s".')
++
++    has_output = output.standard_boolean
++
++    def execute(self, *keys, **options):
++        fqdn = keys[0]
++        if options['state'] == u'enabled':
++            to_status = ENABLED
++            from_status = HIDDEN
++        else:
++            to_status = HIDDEN
++            from_status = ENABLED
++
++        roles = self.api.Command.server_role_find(
++            server_server=fqdn,
++            status=from_status,
++            include_master=True,
++        )['result']
++        from_roles = [r[u'role_servrole'] for r in roles]
++        if not from_roles:
++            # no server role is in source status
++            raise errors.EmptyModlist
++
++        if to_status == ENABLED:
++            enable_services(fqdn)
++        else:
++            hide_services(fqdn)
++
++        # update system roles
++        result = self.api.Command.dns_update_system_records()
++        if not result.get('value'):
++            self.add_message(messages.AutomaticDNSRecordsUpdateFailed())
++
++        return {
++            'value': fqdn,
++            'result': True,
++        }
+-- 
+2.20.1
+
diff --git a/SOURCES/0022-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch b/SOURCES/0022-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch
deleted file mode 100644
index c4131cb..0000000
--- a/SOURCES/0022-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 8d72395890fc47d0ec6f1d810b0ea063e28575f5 Mon Sep 17 00:00:00 2001
-From: Thierry Bordaz <tbordaz@redhat.com>
-Date: Tue, 24 Jul 2018 12:16:35 +0200
-Subject: [PATCH] In IPA 4.4 when updating userpassword with ldapmodify does
- not update krbPasswordExpiration nor krbLastPwdChange
-
-When making ipa-pwd-extop TXN aware, some callbacks are call twice.
-Particularily
-	ipapwd_pre_add is called during PRE_ADD and TXN_PRE_ADD
-	ipapwd_pre_mod is called during PRE_MOD and TXN_PRE_MOD
-	ipapwd_post_modadd is called during POST_ADD and TXN_POST_ADD
-	ipapwd_post_modadd is called during POST_MOD and TXN_POST_MOD
-It is not the expected behavior and it results on some skipped updates krbPasswordExpiration
-and krbLastPwdChange
-
-https://pagure.io/freeipa/issue/7601
-
-Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
----
- daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 4 ----
- 1 file changed, 4 deletions(-)
-
-diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
-index c62eae33418f8368c831a91f611915addf7d423b..209d596255d05bfd03de432b7930e6406cc025dc 100644
---- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
-+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
-@@ -1488,8 +1488,6 @@ int ipapwd_pre_init(Slapi_PBlock *pb)
-     ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
-     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc);
-     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *)ipapwd_pre_bind);
--    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *)ipapwd_pre_add);
--    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *)ipapwd_pre_mod);
- 
-     return ret;
- }
-@@ -1513,9 +1511,7 @@ int ipapwd_post_init(Slapi_PBlock *pb)
- 
-     ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
-     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc);
--    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)ipapwd_post_modadd);
-     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)ipapwd_post_updatecfg);
--    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)ipapwd_post_modadd);
-     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *)ipapwd_post_updatecfg);
- 
-     return ret;
--- 
-2.17.1
-
diff --git a/SOURCES/0023-Consider-hidden-servers-as-role-provider.patch b/SOURCES/0023-Consider-hidden-servers-as-role-provider.patch
new file mode 100644
index 0000000..d847594
--- /dev/null
+++ b/SOURCES/0023-Consider-hidden-servers-as-role-provider.patch
@@ -0,0 +1,45 @@
+From d5538488447a42110c2b6f77ffdc80d4c6e0e61e Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Mon, 25 Mar 2019 15:58:07 +0100
+Subject: [PATCH] Consider hidden servers as role provider
+
+Hidden services are now considered as associated role providers, too. This
+fixes the issue of:
+
+    invalid 'PKINIT enabled server': all masters must have IPA
+    master role enabled
+
+and similar issues with CA and DNS.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ ipaserver/servroles.py | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
+index 02a22e77dbb615f735660c53d1b2eb7da022591d..9c963be53527bb955ebf2b8cec7960f0d90717a4 100644
+--- a/ipaserver/servroles.py
++++ b/ipaserver/servroles.py
+@@ -338,12 +338,13 @@ class ServerAttribute(LDAPBasedProperty):
+         ldap.update_entry(service_entry)
+ 
+     def _get_assoc_role_providers(self, api_instance):
+-        """
+-        get list of all servers on which the associated role is enabled
++        """get list of all servers on which the associated role is enabled
++
++        Consider a hidden server as a valid provider for a role.
+         """
+         return [
+             r[u'server_server'] for r in self.associated_role.status(
+-                api_instance) if r[u'status'] == ENABLED]
++                api_instance) if r[u'status'] in {ENABLED, HIDDEN}]
+ 
+     def _remove(self, api_instance, masters):
+         """
+-- 
+2.20.1
+
diff --git a/SOURCES/0023-Move-fips_enabled-to-a-common-library-to-share-acros.patch b/SOURCES/0023-Move-fips_enabled-to-a-common-library-to-share-acros.patch
deleted file mode 100644
index 24c52bd..0000000
--- a/SOURCES/0023-Move-fips_enabled-to-a-common-library-to-share-acros.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From 08ada3f8d7f80067a1b43e6172394d1326e3d178 Mon Sep 17 00:00:00 2001
-From: Alexander Bokovoy <abokovoy@redhat.com>
-Date: Wed, 8 Aug 2018 12:28:53 +0300
-Subject: [PATCH] Move fips_enabled to a common library to share across
- different plugins
-
-Related: https://pagure.io/freeipa/issue/7659
-Reviewed-By: Robbie Harwood <rharwood@redhat.com>
----
- .../ipa-slapi-plugins/ipa-pwd-extop/common.c  | 24 +-----------------
- util/ipa_pwd.c                                | 25 +++++++++++++++++++
- util/ipa_pwd.h                                |  2 ++
- 3 files changed, 28 insertions(+), 23 deletions(-)
-
-diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
-index 5efadac5b1fd57e5f91a886224fa2f1ab88305ac..db7183bf2b115dcb0c21f7a6bdb8e55db26224c4 100644
---- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
-+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
-@@ -46,7 +46,6 @@
- /* Type of connection for this operation;*/
- #define LDAP_EXTOP_PASSMOD_CONN_SECURE
- 
--#define PROC_SYS_FIPS "/proc/sys/crypto/fips_enabled"
- 
- /* Uncomment the following #undef FOR TESTING:
-  * allows non-SSL connections to use the password change extended op */
-@@ -64,27 +63,6 @@ static const char *ipapwd_def_encsalts[] = {
-     NULL
- };
- 
--static bool fips_enabled(void)
--{
--    int fd;
--    ssize_t len;
--    char buf[8];
--
--    fd = open(PROC_SYS_FIPS, O_RDONLY);
--    if (fd != -1) {
--        len = read(fd, buf, sizeof(buf));
--        close(fd);
--        /* Assume FIPS in enabled if PROC_SYS_FIPS contains a non-0 value
--         * similar to the is_fips_enabled() check in
--         * ipaplatform/redhat/tasks.py */
--        if (!(len == 2 && buf[0] == '0' && buf[1] == '\n')) {
--            return true;
--        }
--    }
--
--    return false;
--}
--
- static struct ipapwd_krbcfg *ipapwd_getConfig(void)
- {
-     krb5_error_code krberr;
-@@ -255,7 +233,7 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void)
- 
-     /* get the ipa etc/ipaConfig entry */
-     config->allow_nt_hash = false;
--    if (fips_enabled()) {
-+    if (ipapwd_fips_enabled()) {
-         LOG("FIPS mode is enabled, NT hashes are not allowed.\n");
-     } else {
-         ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL);
-diff --git a/util/ipa_pwd.c b/util/ipa_pwd.c
-index f6564c8021c656031d3f459dd50d830934c7143b..9890c980cacad08302fb5305c3aa9598a81ec681 100644
---- a/util/ipa_pwd.c
-+++ b/util/ipa_pwd.c
-@@ -27,6 +27,8 @@
- #include <stdio.h>
- #include <time.h>
- #include <ctype.h>
-+#include <fcntl.h>
-+#include <unistd.h>
- #include <nss.h>
- #include <nssb64.h>
- #include <hasht.h>
-@@ -656,3 +658,26 @@ done:
-     free(hash);
-     return ret;
- }
-+
-+#define PROC_SYS_FIPS "/proc/sys/crypto/fips_enabled"
-+
-+bool ipapwd_fips_enabled(void)
-+{
-+    int fd;
-+    ssize_t len;
-+    char buf[8];
-+
-+    fd = open(PROC_SYS_FIPS, O_RDONLY);
-+    if (fd != -1) {
-+        len = read(fd, buf, sizeof(buf));
-+        close(fd);
-+        /* Assume FIPS in enabled if PROC_SYS_FIPS contains a non-0 value
-+         * similar to the is_fips_enabled() check in
-+         * ipaplatform/redhat/tasks.py */
-+        if (!(len == 2 && buf[0] == '0' && buf[1] == '\n')) {
-+            return true;
-+        }
-+    }
-+
-+    return false;
-+}
-diff --git a/util/ipa_pwd.h b/util/ipa_pwd.h
-index b3ee75063adc4baa93bbd4991161bebe1d233bb8..664c8b1827591e716095d9ef1727e422c7d82680 100644
---- a/util/ipa_pwd.h
-+++ b/util/ipa_pwd.h
-@@ -77,3 +77,5 @@ int ipapwd_generate_new_history(char *password,
-                                 int *new_pwd_hlen);
- 
- int encode_nt_key(char *newPasswd, uint8_t *nt_key);
-+
-+bool ipapwd_fips_enabled(void);
--- 
-2.17.1
-
diff --git a/SOURCES/0024-Improve-config-show-to-show-hidden-servers.patch b/SOURCES/0024-Improve-config-show-to-show-hidden-servers.patch
new file mode 100644
index 0000000..e2e34f5
--- /dev/null
+++ b/SOURCES/0024-Improve-config-show-to-show-hidden-servers.patch
@@ -0,0 +1,246 @@
+From ae9ab0545ff06b60df4b66c0ebccb7baa79b8898 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Mon, 25 Mar 2019 15:59:51 +0100
+Subject: [PATCH] Improve config-show to show hidden servers
+
+config-show only used to show enabled servers. Now also show hidden
+servers on separate lines. Additionally include information about
+KRA and DNS servers.
+
+The augmented config-show output makes it easier to diagnose a cluster
+and simplifies sanity checks.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ ipaserver/plugins/config.py                   | 56 +++++++++++++++++--
+ ipaserver/plugins/serverroles.py              | 25 ++++++---
+ ipaserver/servroles.py                        |  6 ++
+ .../test_replica_promotion.py                 | 23 ++++++++
+ 4 files changed, 97 insertions(+), 13 deletions(-)
+
+diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py
+index fff58ac656f4fad3491dfa9f95c22b7d54a6da56..58b48935c2c7471ff2ce0bb3f5ce92a9fb47a503 100644
+--- a/ipaserver/plugins/config.py
++++ b/ipaserver/plugins/config.py
+@@ -249,6 +249,18 @@ class config(LDAPObject):
+             doc=_('List of all IPA masters'),
+             flags={'virtual_attribute', 'no_create', 'no_update'}
+         ),
++        Str(
++            'ipa_master_hidden_server*',
++            label=_('Hidden IPA masters'),
++            doc=_('List of all hidden IPA masters'),
++            flags={'virtual_attribute', 'no_create', 'no_update'}
++        ),
++        Str(
++            'pkinit_server_server*',
++            label=_('IPA master capable of PKINIT'),
++            doc=_('IPA master which can process PKINIT requests'),
++            flags={'virtual_attribute', 'no_create', 'no_update'}
++        ),
+         Str(
+             'ca_server_server*',
+             label=_('IPA CA servers'),
+@@ -261,6 +273,12 @@ class config(LDAPObject):
+             doc=_('IPA servers with enabled NTP'),
+             flags={'virtual_attribute', 'no_create', 'no_update'}
+         ),
++        Str(
++            'ca_server_hidden_server*',
++            label=_('Hidden IPA CA servers'),
++            doc=_('Hidden IPA servers configured as certificate authority'),
++            flags={'virtual_attribute', 'no_create', 'no_update'}
++        ),
+         Str(
+             'ca_renewal_master_server?',
+             label=_('IPA CA renewal master'),
+@@ -268,9 +286,15 @@ class config(LDAPObject):
+             flags={'virtual_attribute', 'no_create'}
+         ),
+         Str(
+-            'pkinit_server_server*',
+-            label=_('IPA master capable of PKINIT'),
+-            doc=_('IPA master which can process PKINIT requests'),
++            'kra_server_server*',
++            label=_('IPA KRA servers'),
++            doc=_('IPA servers configured as key recovery agent'),
++            flags={'virtual_attribute', 'no_create', 'no_update'}
++        ),
++        Str(
++            'kra_server_hidden_server*',
++            label=_('Hidden IPA KRA servers'),
++            doc=_('Hidden IPA servers configured as key recovery agent'),
+             flags={'virtual_attribute', 'no_create', 'no_update'}
+         ),
+         Str(
+@@ -279,7 +303,25 @@ class config(LDAPObject):
+             label=_('Domain resolution order'),
+             doc=_('colon-separated list of domains used for short name'
+                   ' qualification')
+-        )
++        ),
++        Str(
++            'dns_server_server*',
++            label=_('IPA DNS servers'),
++            doc=_('IPA servers configured as domain name server'),
++            flags={'virtual_attribute', 'no_create', 'no_update'}
++        ),
++        Str(
++            'dns_server_hidden_server*',
++            label=_('Hidden IPA DNS servers'),
++            doc=_('Hidden IPA servers configured as domain name server'),
++            flags={'virtual_attribute', 'no_create', 'no_update'}
++        ),
++        Str(
++            'dnssec_key_master_server?',
++            label=_('IPA DNSSec key master'),
++            doc=_('DNSec key master'),
++            flags={'virtual_attribute', 'no_create', 'no_update'}
++        ),
+     )
+ 
+     def get_dn(self, *keys, **kwargs):
+@@ -560,7 +602,8 @@ class config_mod(LDAPUpdate):
+ 
+     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+         self.obj.show_servroles_attributes(
+-            entry_attrs, "CA server", "IPA master", "NTP server", **options)
++            entry_attrs, "CA server", "KRA server", "IPA master",
++            "NTP server", "DNS server", **options)
+         return dn
+ 
+ 
+@@ -570,5 +613,6 @@ class config_show(LDAPRetrieve):
+ 
+     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+         self.obj.show_servroles_attributes(
+-            entry_attrs, "CA server", "IPA master", "NTP server", **options)
++            entry_attrs, "CA server", "KRA server", "IPA master",
++            "NTP server", "DNS server", **options)
+         return dn
+diff --git a/ipaserver/plugins/serverroles.py b/ipaserver/plugins/serverroles.py
+index 0abf48ae5295be5f622bf8c90d32e7adff5e6cf7..5bba84972a5828b6c7230d0d0d858e6683e5986b 100644
+--- a/ipaserver/plugins/serverroles.py
++++ b/ipaserver/plugins/serverroles.py
+@@ -45,7 +45,9 @@ import six
+ from ipalib import errors, _
+ from ipalib.backend import Backend
+ from ipalib.plugable import Registry
+-from ipaserver.servroles import (attribute_instances, ENABLED, role_instances)
++from ipaserver.servroles import (
++    attribute_instances, ENABLED, HIDDEN, role_instances
++)
+ from ipaserver.servroles import SingleValuedServerAttribute
+ 
+ 
+@@ -81,17 +83,26 @@ class serverroles(Backend):
+             raise errors.NotFound(
+                 reason=_("{role}: role not found".format(role=role_name)))
+ 
+-    def _get_enabled_masters(self, role_name):
++    def _get_masters(self, role_name, include_hidden):
+         result = {}
+         role = self._get_role(role_name)
++        role_states = role.status(self.api, server=None)
+ 
+         enabled_masters = [
+-            r[u'server_server'] for r in role.status(self.api, server=None) if
+-            r[u'status'] == ENABLED]
+-
++            r[u'server_server'] for r in role_states if
++            r[u'status'] == ENABLED
++        ]
+         if enabled_masters:
+             result.update({role.attr_name: enabled_masters})
+ 
++        if include_hidden and role.attr_name_hidden is not None:
++            hidden_masters = [
++                r[u'server_server'] for r in role_states if
++                r[u'status'] == HIDDEN
++            ]
++            if hidden_masters:
++                result.update({role.attr_name_hidden: hidden_masters})
++
+         return result
+ 
+     def _get_assoc_attributes(self, role_name):
+@@ -131,8 +142,8 @@ class serverroles(Backend):
+         return self._get_role(role_servrole).status(
+             self.api, server=server_server)
+ 
+-    def config_retrieve(self, servrole):
+-        result = self._get_enabled_masters(servrole)
++    def config_retrieve(self, servrole, include_hidden=True):
++        result = self._get_masters(servrole, include_hidden=include_hidden)
+ 
+         try:
+             assoc_attributes = self._get_assoc_attributes(servrole)
+diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
+index 9c963be53527bb955ebf2b8cec7960f0d90717a4..0988cbdd7eac599ef26c89a015523b2d92e9502f 100644
+--- a/ipaserver/servroles.py
++++ b/ipaserver/servroles.py
+@@ -104,6 +104,12 @@ class LDAPBasedProperty(object):
+     def __init__(self, attr_name, name):
+         self.attr_name = attr_name
+         self.name = name
++        # for hidden services, insert hidden before '_server' suffix
++        if attr_name.endswith(u'_server'):
++            parts = attr_name.rsplit(u'_', 1)
++            self.attr_name_hidden = u'{}_hidden_server'.format(parts[0])
++        else:
++            self.attr_name_hidden = None
+ 
+ 
+ @six.add_metaclass(abc.ABCMeta)
+diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
+index a4f3e402ce5d6f74af4bd6fed9376f0f039f297a..3be2ea95fa2325edfc74cbb902ce0a5966aa82d7 100644
+--- a/ipatests/test_integration/test_replica_promotion.py
++++ b/ipatests/test_integration/test_replica_promotion.py
+@@ -851,11 +851,32 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+             expected = 'Role status: {}'.format(status)
+             assert expected in result.stdout_text
+ 
++    def _check_config(self, enabled=(), hidden=()):
++        enabled = {host.hostname for host in enabled}
++        hidden = {host.hostname for host in hidden}
++        services = [
++            'IPA masters', 'IPA CA servers', 'IPA KRA servers',
++            'IPA DNS servers'
++        ]
++
++        result = self.master.run_command(['ipa', 'config-show'])
++        values = {}
++        for line in result.stdout_text.split('\n'):
++            if ':' not in line:
++                continue
++            k, v = line.split(':', 1)
++            values[k.strip()] = {item.strip() for item in v.split(',')}
++
++        for service in services:
++            assert values[service] == enabled
++            assert values['Hidden {}'.format(service)] == hidden
++
+     def test_hidden_replica_install(self):
+         # TODO: check that all services are running on hidden replica
+         self._check_server_role(self.master, 'enabled')
+         self._check_server_role(self.replicas[0], 'hidden')
+         self._check_dnsrecords([self.master], [self.replicas[0]])
++        self._check_config([self.master], [self.replicas[0]])
+ 
+     def test_hidden_replica_promote(self):
+         self.replicas[0].run_command([
+@@ -864,6 +885,8 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+         ])
+         self._check_server_role(self.replicas[0], 'enabled')
+         self._check_dnsrecords([self.master, self.replicas[0]])
++        self._check_config([self.master, self.replicas[0]])
++
+         result = self.replicas[0].run_command([
+             'ipa', 'server-state',
+             self.replicas[0].hostname, '--state=enabled'
+-- 
+2.20.1
+
diff --git a/SOURCES/0024-ipasam-do-not-use-RC4-in-FIPS-mode.patch b/SOURCES/0024-ipasam-do-not-use-RC4-in-FIPS-mode.patch
deleted file mode 100644
index 041ca91..0000000
--- a/SOURCES/0024-ipasam-do-not-use-RC4-in-FIPS-mode.patch
+++ /dev/null
@@ -1,96 +0,0 @@
-From 1a7538f0d73b4b35769c4df5ba32ed836e26a648 Mon Sep 17 00:00:00 2001
-From: Alexander Bokovoy <abokovoy@redhat.com>
-Date: Wed, 8 Aug 2018 13:04:04 +0300
-Subject: [PATCH] ipasam: do not use RC4 in FIPS mode
-
-When creating Kerberos keys for trusted domain object account, ipasam
-module requests to generate keys using a series of well-known encryption
-types. In FIPS mode it is not possible to generate RC4-HMAC key:
-MIT Kerberos is using openssl crypto backend and openssl does not allow
-use of RC4 in FIPS mode.
-
-Thus, we have to filter out RC4-HMAC encryption type when running in
-FIPS mode. A side-effect is that a trust to Active Directory running
-with Windows Server 2003 will not be possible anymore in FIPS mode.
-
-Resolves: https://pagure.io/freeipa/issue/7659
-Reviewed-By: Robbie Harwood <rharwood@redhat.com>
----
- daemons/ipa-sam/ipa_sam.c | 23 +++++++++++++++++++----
- 1 file changed, 19 insertions(+), 4 deletions(-)
-
-diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c
-index 0cd48d845b2edf9f328de0a949f80f98f9ef9025..675a511f0febf13cc5e00b547c18a050ac534f2e 100644
---- a/daemons/ipa-sam/ipa_sam.c
-+++ b/daemons/ipa-sam/ipa_sam.c
-@@ -213,6 +213,7 @@ struct ipasam_private {
- 	char *client_princ;
- 	struct sss_idmap_ctx *idmap_ctx;
- 	uint32_t supported_enctypes;
-+	bool fips_enabled;
- };
- 
- 
-@@ -1737,6 +1738,10 @@ static bool search_krb_princ(struct ipasam_private *ipasam_state,
- 	return true;
- }
- 
-+/* Please keep ENCTYPE_ARCFOUR_HMAC the last in the list
-+ * of the default encryption types so that we can exclude
-+ * it when running in a FIPS mode where it is not allowed
-+ */
- #define DEF_ENCTYPE_NUM 3
- long default_enctypes[DEF_ENCTYPE_NUM] = {
-     ENCTYPE_AES256_CTS_HMAC_SHA1_96,
-@@ -1754,9 +1759,14 @@ static int set_cross_realm_pw(struct ipasam_private *ipasam_state,
- 	struct berval reqdata = { 0 };
- 	struct berval *retdata = NULL;
-         char *retoid;
-+	int enctypes_num = DEF_ENCTYPE_NUM;
- 
-+        if (ipasam_state->fips_enabled) {
-+		DEBUG(1, ("FIPS mode enabled: TDO account credentials will not have RC4-HMAC!\n"));
-+                enctypes_num = DEF_ENCTYPE_NUM - 1;
-+        }
-         ret = ipaasn1_enc_getkt(true, princ, pwd,
--                                default_enctypes, DEF_ENCTYPE_NUM,
-+                                default_enctypes, enctypes_num,
-                                 &buffer, &buflen);
-         if (!ret) goto done;
- 
-@@ -3935,7 +3945,9 @@ static NTSTATUS ipasam_get_enctypes(struct ipasam_private *ipasam_state,
- 				*enctypes |= KERB_ENCTYPE_DES_CBC_MD5;
- 				break;
- 			case ENCTYPE_ARCFOUR_HMAC:
--				*enctypes |= KERB_ENCTYPE_RC4_HMAC_MD5;
-+				if (!ipasam_state->fips_enabled) {
-+					*enctypes |= KERB_ENCTYPE_RC4_HMAC_MD5;
-+				}
- 				break;
- 			case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- 				*enctypes |= KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96;
-@@ -4563,6 +4575,7 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method,
- 		return NT_STATUS_INVALID_PARAMETER;
- 	}
- 
-+	ipasam_state->fips_enabled = ipapwd_fips_enabled();
- 	ipasam_state->trust_dn = talloc_asprintf(ipasam_state,
- 						 "cn=ad,cn=trusts,%s",
- 						 ipasam_state->base_dn);
-@@ -4684,9 +4697,11 @@ static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method,
- 				     &enctypes);
- 
- 	if (!NT_STATUS_IS_OK(status)) {
--		enctypes = KERB_ENCTYPE_RC4_HMAC_MD5 |
--			   KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 |
-+		enctypes = KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 |
- 			   KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96;
-+		if (!ipasam_state->fips_enabled) {
-+			enctypes |= KERB_ENCTYPE_RC4_HMAC_MD5;
-+		}
- 	}
- 
- 	ipasam_state->supported_enctypes = enctypes;
--- 
-2.17.1
-
diff --git a/SOURCES/0025-More-test-fixes.patch b/SOURCES/0025-More-test-fixes.patch
new file mode 100644
index 0000000..f1559ca
--- /dev/null
+++ b/SOURCES/0025-More-test-fixes.patch
@@ -0,0 +1,78 @@
+From 9c62af973d71cabaf8d93216717468385ac2abbe Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Mon, 25 Mar 2019 18:36:19 +0100
+Subject: [PATCH] More test fixes
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ .../test_integration/test_replica_promotion.py | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
+index 3be2ea95fa2325edfc74cbb902ce0a5966aa82d7..9ce0074ea0df276b4800b306530d04c8274ece69 100644
+--- a/ipatests/test_integration/test_replica_promotion.py
++++ b/ipatests/test_integration/test_replica_promotion.py
+@@ -843,7 +843,7 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+     def _check_server_role(self, host, status):
+         roles = [u'IPA master', u'CA server', u'KRA server', u'DNS server']
+         for role in roles:
+-            result = self.master.run_command([
++            result = self.replicas[0].run_command([
+                 'ipa', 'server-role-find',
+                 '--server', host.hostname,
+                 '--role', role
+@@ -859,7 +859,7 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+             'IPA DNS servers'
+         ]
+ 
+-        result = self.master.run_command(['ipa', 'config-show'])
++        result = self.replicas[0].run_command(['ipa', 'config-show'])
+         values = {}
+         for line in result.stdout_text.split('\n'):
+             if ':' not in line:
+@@ -868,8 +868,9 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+             values[k.strip()] = {item.strip() for item in v.split(',')}
+ 
+         for service in services:
+-            assert values[service] == enabled
+-            assert values['Hidden {}'.format(service)] == hidden
++            hservice = 'Hidden {}'.format(service)
++            assert values.get(service, set()) == enabled
++            assert values.get(hservice, set()) == hidden
+ 
+     def test_hidden_replica_install(self):
+         # TODO: check that all services are running on hidden replica
+@@ -909,19 +910,26 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+         # backup
+         backup_path = backup(self.replicas[0])
+         # uninstall
+-        tasks.uninstall_replica(self.master, self.replicas[0])
++        tasks.uninstall_master(self.replicas[0])
+         # restore
+         dirman_password = self.master.config.dirman_password
+         self.replicas[0].run_command(
+             ['ipa-restore', backup_path],
+             stdin_text=dirman_password + '\nyes'
+         )
++        # give replication some time
++        time.sleep(5)
++
+         # check that role is still hidden
++        tasks.kinit_admin(self.replicas[0])
++        self._check_config([self.master], [self.replicas[0]])
+         self._check_server_role(self.replicas[0], 'hidden')
+         self._check_dnsrecords([self.master], [self.replicas[0]])
++
+         # check that the resulting server can be promoted to enabled
+         self.replicas[0].run_command([
+             'ipa', 'server-mod', self.replicas[0].hostname, '--state=enabled'
+         ])
++        self._check_config([self.master, self.replicas[0]])
+         self._check_server_role(self.replicas[0], 'enabled')
+         self._check_dnsrecords([self.master, self.replicas[0]])
+-- 
+2.20.1
+
diff --git a/SOURCES/0025-Re-open-the-ldif-file-to-prevent-error-message.patch b/SOURCES/0025-Re-open-the-ldif-file-to-prevent-error-message.patch
deleted file mode 100644
index 964792c..0000000
--- a/SOURCES/0025-Re-open-the-ldif-file-to-prevent-error-message.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From fd54380d684494e13d8e797b17412a6713937b95 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= <tdudlak@redhat.com>
-Date: Thu, 2 Aug 2018 18:05:51 +0200
-Subject: [PATCH] Re-open the ldif file to prevent error message
-
-There was an issue with ipa-server-upgrade and it was
-showing an error while upgrading:
-DN... does not exists or haven't been updated, caused
-by not moving pointer to file begining when re-reading.
-
-Resolves: https://pagure.io/freeipa/issue/7644
-Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaserver/install/upgradeinstance.py | 21 +++++++++++++--------
- 1 file changed, 13 insertions(+), 8 deletions(-)
-
-diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py
-index 19a06a0a84536cb548822e47038142e97d8bee39..cb83d7bb1817d1fd9a36f2e97a5cc80bf806cabd 100644
---- a/ipaserver/install/upgradeinstance.py
-+++ b/ipaserver/install/upgradeinstance.py
-@@ -237,17 +237,22 @@ class IPAUpgrade(service.Service):
- 
-     def __disable_schema_compat(self):
-         ldif_outfile = "%s.modified.out" % self.filename
-+
-+        with open(self.filename, "r") as in_file:
-+            parser = GetEntryFromLDIF(in_file, entries_dn=[COMPAT_DN])
-+            parser.parse()
-+
-+        try:
-+            compat_entry = parser.get_results()[COMPAT_DN]
-+        except KeyError:
-+            return
-+
-+        if not compat_entry.get('nsslapd-pluginEnabled'):
-+            return
-+
-         with open(ldif_outfile, "w") as out_file:
-             with open(self.filename, "r") as in_file:
--                parser = GetEntryFromLDIF(in_file, entries_dn=[COMPAT_DN])
--                parser.parse()
--                try:
--                    compat_entry = parser.get_results()[COMPAT_DN]
--                except KeyError:
--                    return
-                 parser = installutils.ModifyLDIF(in_file, out_file)
--                if not compat_entry.get('nsslapd-pluginEnabled'):
--                    return
-                 parser.remove_value(COMPAT_DN, "nsslapd-pluginEnabled")
-                 parser.remove_value(COMPAT_DN, "nsslapd-pluginenabled")
-                 parser.add_value(COMPAT_DN, "nsslapd-pluginEnabled",
--- 
-2.17.1
-
diff --git a/SOURCES/0026-Catch-ACIError-instead-of-invalid-credentials.patch b/SOURCES/0026-Catch-ACIError-instead-of-invalid-credentials.patch
deleted file mode 100644
index 9521355..0000000
--- a/SOURCES/0026-Catch-ACIError-instead-of-invalid-credentials.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From a24178a743e7a90ca80702207345a398bf8074ad Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 29 Jun 2018 11:08:45 +0200
-Subject: [PATCH] Catch ACIError instead of invalid credentials
-
-ipaldap's LDAPClient client turns INVALID_CREDENTIAL error into
-ACIError. Catch the ACIError and wait until the user has been
-replicated.
-
-Apparently no manual or automated test ran into the timeout during
-testing.
-
-Fixes: Fixes: https://pagure.io/freeipa/issue/7593
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- ipaserver/install/dogtaginstance.py | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
-index 5b2c30f8a1b7e932ce1cca3ca38f5962a3d54266..437029315cb6774ce9057baafda27cdb68454b49 100644
---- a/ipaserver/install/dogtaginstance.py
-+++ b/ipaserver/install/dogtaginstance.py
-@@ -471,7 +471,8 @@ class DogtagInstance(service.Service):
-             time.sleep(1)
-             try:
-                 master_conn.simple_bind(self.admin_dn, self.admin_password)
--            except ldap.INVALID_CREDENTIALS:
-+            except errors.ACIError:
-+                # user not replicated yet
-                 pass
-             else:
-                 logger.debug("Successfully logged in as %s", self.admin_dn)
--- 
-2.17.1
-
diff --git a/SOURCES/0026-Don-t-allow-to-hide-last-server-for-a-role.patch b/SOURCES/0026-Don-t-allow-to-hide-last-server-for-a-role.patch
new file mode 100644
index 0000000..53e180e
--- /dev/null
+++ b/SOURCES/0026-Don-t-allow-to-hide-last-server-for-a-role.patch
@@ -0,0 +1,118 @@
+From ec58278fa7fc6d0b5ae8340c4054005a7864086f Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Tue, 26 Mar 2019 13:10:23 +0100
+Subject: [PATCH] Don't allow to hide last server for a role
+
+DNSSec key master and CA renewal master can't be hidden. There must be
+at least one enabled server available for each role, too.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ ipaserver/plugins/server.py                   | 30 ++++++++++++++++++
+ .../test_replica_promotion.py                 | 31 +++++++++++++++++++
+ 2 files changed, 61 insertions(+)
+
+diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py
+index 0d144d13bca66b65de64328139fd7126eea24c89..bfd1406ff826aea97195aa08ca35018e35cac18c 100644
+--- a/ipaserver/plugins/server.py
++++ b/ipaserver/plugins/server.py
+@@ -970,6 +970,35 @@ class server_state(crud.PKQuery):
+ 
+     has_output = output.standard_boolean
+ 
++    def _check_hide_server(self, fqdn):
++        result = self.api.Command.config_show()['result']
++        err = []
++        # single value entries
++        if result.get("ca_renewal_master_server") == fqdn:
++            err.append(_("Cannot hide CA renewal master."))
++        if result.get("dnssec_key_master_server") == fqdn:
++            err.append(_("Cannot hide DNSSec key master."))
++        # multi value entries, only fail if we are the last one
++        checks = [
++            ("ca_server_server", "CA"),
++            ("dns_server_server", "DNS"),
++            ("ipa_master_server", "IPA"),
++            ("kra_server_server", "KRA"),
++        ]
++        for key, name in checks:
++            values = result.get(key, [])
++            if values == [fqdn]:  # fqdn is the only entry
++                err.append(
++                    _("Cannot hide last enabled %(name)s server.") % {
++                        'name': name
++                    }
++                )
++        if err:
++            raise errors.ValidationError(
++                name=fqdn,
++                error=' '.join(str(e) for e in err)
++            )
++
+     def execute(self, *keys, **options):
+         fqdn = keys[0]
+         if options['state'] == u'enabled':
+@@ -992,6 +1021,7 @@ class server_state(crud.PKQuery):
+         if to_status == ENABLED:
+             enable_services(fqdn)
+         else:
++            self._check_hide_server(fqdn)
+             hide_services(fqdn)
+ 
+         # update system roles
+diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
+index 9ce0074ea0df276b4800b306530d04c8274ece69..bf028bf7dc58abb6455ba1659f2d19bede69daa2 100644
+--- a/ipatests/test_integration/test_replica_promotion.py
++++ b/ipatests/test_integration/test_replica_promotion.py
+@@ -812,7 +812,15 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+ 
+     @classmethod
+     def install(cls, mh):
++        # master with DNSSEC master
+         tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
++        cls.master.run_command([
++            "ipa-dns-install",
++            "--dnssec-master",
++            "--forwarder", cls.master.config.dns_forwarder,
++            "-U",
++        ])
++        # hidden replica with CA and DNS
+         tasks.install_replica(
+             cls.master, cls.replicas[0],
+             setup_dns=True, setup_kra=True,
+@@ -879,6 +887,29 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+         self._check_dnsrecords([self.master], [self.replicas[0]])
+         self._check_config([self.master], [self.replicas[0]])
+ 
++    def test_hide_master_fails(self):
++        # verify state
++        self._check_config([self.master], [self.replicas[0]])
++        # nothing to do
++        result = self.master.run_command([
++            'ipa', 'server-state',
++            self.master.hostname, '--state=enabled'
++        ], raiseonerr=False)
++        assert result.returncode == 1
++        assert "no modifications to be performed" in result.stderr_text
++        # hiding the last master fails
++        result = self.master.run_command([
++            'ipa', 'server-state',
++            self.master.hostname, '--state=hidden'
++        ], raiseonerr=False)
++        assert result.returncode == 1
++        keys = [
++            "CA renewal master", "DNSSec key master", "CA server",
++            "KRA server", "DNS server", "IPA server"
++        ]
++        for key in keys:
++            assert key in result.stderr_text
++
+     def test_hidden_replica_promote(self):
+         self.replicas[0].run_command([
+             'ipa', 'server-state',
+-- 
+2.20.1
+
diff --git a/SOURCES/0027-Auto-retry-failed-certmonger-requests.patch b/SOURCES/0027-Auto-retry-failed-certmonger-requests.patch
deleted file mode 100644
index 54e1655..0000000
--- a/SOURCES/0027-Auto-retry-failed-certmonger-requests.patch
+++ /dev/null
@@ -1,195 +0,0 @@
-From 3e8427f3aa392f961923f5c9d509bab64ce3c9ab Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Sun, 8 Jul 2018 11:53:58 +0200
-Subject: [PATCH] Auto-retry failed certmonger requests
-
-During parallel replica installation, a request sometimes fails with
-CA_REJECTED or CA_UNREACHABLE. The error occur when the master is
-either busy or some information haven't been replicated yet. Even
-a stuck request can be recovered, e.g. when permission and group
-information have been replicated.
-
-A new function request_and_retry_cert() automatically resubmits failing
-requests until it times out.
-
-Fixes: https://pagure.io/freeipa/issue/7623
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
----
- ipalib/install/certmonger.py      | 64 ++++++++++++++++++++++++-------
- ipaserver/install/cainstance.py   |  4 +-
- ipaserver/install/certs.py        | 19 ++++++---
- ipaserver/install/dsinstance.py   |  4 +-
- ipaserver/install/httpinstance.py |  5 ++-
- ipaserver/install/krbinstance.py  |  4 +-
- 6 files changed, 76 insertions(+), 24 deletions(-)
-
-diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
-index c07242412affa29eca3312fd27985f65869d3f7a..3e1862192eb0d9245797ebbe8abf2ff69d7e7767 100644
---- a/ipalib/install/certmonger.py
-+++ b/ipalib/install/certmonger.py
-@@ -305,20 +305,56 @@ def add_subject(request_id, subject):
- def request_and_wait_for_cert(
-         certpath, subject, principal, nickname=None, passwd_fname=None,
-         dns=None, ca='IPA', profile=None,
--        pre_command=None, post_command=None, storage='NSSDB', perms=None):
--    """
--    Execute certmonger to request a server certificate.
--
--    The method also waits for the certificate to be available.
--    """
--    reqId = request_cert(certpath, subject, principal, nickname,
--                         passwd_fname, dns, ca, profile,
--                         pre_command, post_command, storage, perms)
--    state = wait_for_request(reqId, api.env.startup_timeout)
--    ca_error = get_request_value(reqId, 'ca-error')
--    if state != 'MONITORING' or ca_error:
--        raise RuntimeError("Certificate issuance failed ({})".format(state))
--    return reqId
-+        pre_command=None, post_command=None, storage='NSSDB', perms=None,
-+        resubmit_timeout=0):
-+    """Request certificate, wait and possibly resubmit failing requests
-+
-+    Submit a cert request to certmonger and wait until the request has
-+    finished.
-+
-+    With timeout, a failed request is resubmitted. During parallel replica
-+    installation, a request sometimes fails with CA_REJECTED or
-+    CA_UNREACHABLE. The error occurs when the master is either busy or some
-+    information haven't been replicated yet. Even a stuck request can be
-+    recovered, e.g. when permission and group information have been
-+    replicated.
-+    """
-+    req_id = request_cert(
-+        certpath, subject, principal, nickname, passwd_fname, dns, ca,
-+        profile, pre_command, post_command, storage, perms
-+    )
-+
-+    deadline = time.time() + resubmit_timeout
-+    while True:  # until success, timeout, or error
-+        state = wait_for_request(req_id, api.env.replication_wait_timeout)
-+        ca_error = get_request_value(req_id, 'ca-error')
-+        if state == 'MONITORING' and ca_error is None:
-+            # we got a winner, exiting
-+            logger.debug("Cert request %s was successful", req_id)
-+            return req_id
-+
-+        logger.debug(
-+            "Cert request %s failed: %s (%s)", req_id, state, ca_error
-+        )
-+        if state not in {'CA_REJECTED', 'CA_UNREACHABLE'}:
-+            # probably unrecoverable error
-+            logger.debug("Giving up on cert request %s", req_id)
-+            break
-+        elif not resubmit_timeout:
-+            # no resubmit
-+            break
-+        elif time.time() > deadline:
-+            logger.debug("Request %s reached resubmit dead line", req_id)
-+            break
-+        else:
-+            # sleep and resubmit
-+            logger.debug("Sleep and resubmit cert request %s", req_id)
-+            time.sleep(10)
-+            resubmit_request(req_id)
-+
-+    raise RuntimeError(
-+        "Certificate issuance failed ({}: {})".format(state, ca_error)
-+    )
- 
- 
- def request_cert(
-diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
-index 8193f3da854b3a20d175de523fbc453f5c5104d8..6dbf69b3e5833f220a4d7d640b66a8fcf824f445 100644
---- a/ipaserver/install/cainstance.py
-+++ b/ipaserver/install/cainstance.py
-@@ -917,7 +917,9 @@ class CAInstance(DogtagInstance):
-                 profile='caServerCert',
-                 pre_command='renew_ra_cert_pre',
-                 post_command='renew_ra_cert',
--                storage="FILE")
-+                storage="FILE",
-+                resubmit_timeout=api.env.replication_wait_timeout
-+            )
-             self.__set_ra_cert_perms()
- 
-             self.requestId = str(reqId)
-diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
-index 3f843399f4f964223f52242d610e842a5dc473e8..30b2aa0d3e7b2cafbcc17ad3d04764a342ae8002 100644
---- a/ipaserver/install/certs.py
-+++ b/ipaserver/install/certs.py
-@@ -600,12 +600,19 @@ class CertDB(object):
-     def export_pem_cert(self, nickname, location):
-         return self.nssdb.export_pem_cert(nickname, location)
- 
--    def request_service_cert(self, nickname, principal, host):
--        certmonger.request_and_wait_for_cert(certpath=self.secdir,
--                                             nickname=nickname,
--                                             principal=principal,
--                                             subject=host,
--                                             passwd_fname=self.passwd_fname)
-+    def request_service_cert(self, nickname, principal, host,
-+                             resubmit_timeout=None):
-+        if resubmit_timeout is None:
-+            resubmit_timeout = api.env.replication_wait_timeout
-+        return certmonger.request_and_wait_for_cert(
-+            certpath=self.secdir,
-+            storage='NSSDB',
-+            nickname=nickname,
-+            principal=principal,
-+            subject=host,
-+            passwd_fname=self.passwd_fname,
-+            resubmit_timeout=resubmit_timeout
-+        )
- 
-     def is_ipa_issued_cert(self, api, nickname):
-         """
-diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
-index eefbde3356e1077d490d09c4ea47d961ce3ce8e6..ac95f8c746477da375de518526dea0d02d51d984 100644
---- a/ipaserver/install/dsinstance.py
-+++ b/ipaserver/install/dsinstance.py
-@@ -851,7 +851,9 @@ class DsInstance(service.Service):
-                     ca='IPA',
-                     profile=dogtag.DEFAULT_PROFILE,
-                     dns=[self.fqdn],
--                    post_command=cmd)
-+                    post_command=cmd,
-+                    resubmit_timeout=api.env.replication_wait_timeout
-+                )
-             finally:
-                 if prev_helper is not None:
-                     certmonger.modify_ca_helper('IPA', prev_helper)
-diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
-index 0b7023c2f1b0feb996e0dd0adbefbd49c51da757..3f83248dd89118aeecfbf458c5079dde8b2cb93d 100644
---- a/ipaserver/install/httpinstance.py
-+++ b/ipaserver/install/httpinstance.py
-@@ -447,7 +447,10 @@ class HTTPInstance(service.Service):
-                     ca='IPA',
-                     profile=dogtag.DEFAULT_PROFILE,
-                     dns=[self.fqdn],
--                    post_command='restart_httpd')
-+                    post_command='restart_httpd',
-+                    storage='NSSDB',
-+                    resubmit_timeout=api.env.replication_wait_timeout
-+                )
-             finally:
-                 if prev_helper is not None:
-                     certmonger.modify_ca_helper('IPA', prev_helper)
-diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
-index 33d66fb94b0a1f7571b22120e5159a0e0ad2e675..09cafb7b84623594fe88083f5b914cee0f050409 100644
---- a/ipaserver/install/krbinstance.py
-+++ b/ipaserver/install/krbinstance.py
-@@ -445,7 +445,9 @@ class KrbInstance(service.Service):
-                 storage='FILE',
-                 profile=KDC_PROFILE,
-                 post_command='renew_kdc_cert',
--                perms=(0o644, 0o600))
-+                perms=(0o644, 0o600),
-+                resubmit_timeout=api.env.replication_wait_timeout
-+            )
-         except dbus.DBusException as e:
-             # if the certificate is already tracked, ignore the error
-             name = e.get_dbus_name()
--- 
-2.17.1
-
diff --git a/SOURCES/0027-Synchronize-hidden-state-from-IPA-master-role.patch b/SOURCES/0027-Synchronize-hidden-state-from-IPA-master-role.patch
new file mode 100644
index 0000000..ba3a6c8
--- /dev/null
+++ b/SOURCES/0027-Synchronize-hidden-state-from-IPA-master-role.patch
@@ -0,0 +1,118 @@
+From d0327f33d3bc426db5c8dd86666e680da6a44b61 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Tue, 26 Mar 2019 13:27:35 +0100
+Subject: [PATCH] Synchronize hidden state from IPA master role
+
+ipa-{adtrust|ca|dns|kra}-install on a hidden replica also installs the
+new service as hidden service.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ install/tools/ipa-adtrust-install             |  2 +-
+ install/tools/ipa-ca-install                  |  2 +-
+ ipaserver/install/ipa_kra_install.py          |  2 +-
+ ipaserver/install/service.py                  | 22 +++++++++++++++++++
+ .../test_replica_promotion.py                 |  4 +++-
+ 5 files changed, 28 insertions(+), 4 deletions(-)
+
+diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
+index 9dbfadb6fae193e2f4a54b3a0e226e0a6b1fd26f..19bd21866119b4a23f5a6a02cc8ea37c8f5d36ea 100755
+--- a/install/tools/ipa-adtrust-install
++++ b/install/tools/ipa-adtrust-install
+@@ -213,7 +213,7 @@ def main():
+     adtrust.install(True, options, fstore, api)
+ 
+     # Enable configured services and update DNS SRV records
+-    service.enable_services(api.env.host)
++    service.sync_services_state(api.env.host)
+     api.Command.dns_update_system_records()
+ 
+     print("""
+diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
+index 55182dc30e4736618f749e78db161fc7eefe37ac..dda7a0527b07695c51140c437a2699c8634f2724 100755
+--- a/install/tools/ipa-ca-install
++++ b/install/tools/ipa-ca-install
+@@ -347,7 +347,7 @@ def main():
+     api.Backend.ldap2.connect()
+ 
+     # Enable configured services and update DNS SRV records
+-    service.enable_services(api.env.host)
++    service.sync_services_state(api.env.host)
+     api.Command.dns_update_system_records()
+     api.Backend.ldap2.disconnect()
+ 
+diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
+index 19260ac7f23a7c6f3a6328d4f146510a186b706e..006bc92bec581e1983f11bfd75498b5484f2567a 100644
+--- a/ipaserver/install/ipa_kra_install.py
++++ b/ipaserver/install/ipa_kra_install.py
+@@ -239,6 +239,6 @@ class KRAInstaller(KRAInstall):
+         api.Backend.ldap2.connect()
+ 
+         # Enable configured services and update DNS SRV records
+-        service.enable_services(api.env.host)
++        service.sync_services_state(api.env.host)
+         api.Command.dns_update_system_records()
+         api.Backend.ldap2.disconnect()
+diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
+index 6d7997c559f8d748f00dd9df28371c53bc12ee21..8948f64c2ec2db4cd013699e07dd94d5dba6c043 100644
+--- a/ipaserver/install/service.py
++++ b/ipaserver/install/service.py
+@@ -41,6 +41,7 @@ from ipaplatform.paths import paths
+ from ipaserver.masters import (
+     CONFIGURED_SERVICE, ENABLED_SERVICE, HIDDEN_SERVICE, SERVICE_LIST
+ )
++from ipaserver.servroles import HIDDEN
+ 
+ logger = logging.getLogger(__name__)
+ 
+@@ -202,6 +203,27 @@ def hide_services(fqdn):
+     _set_services_state(fqdn, HIDDEN_SERVICE)
+ 
+ 
++def sync_services_state(fqdn):
++    """Synchronize services state from IPA master role state
++
++    Hide all services if the IPA master role state is in hidden state.
++    Otherwise enable all services.
++
++    :param fqdn: hostname of server
++    """
++    result = api.Command.server_role_find(
++        server_server=fqdn,
++        role_servrole='IPA master',
++        status=HIDDEN
++    )
++    if result['count']:
++        # one hidden server role
++        hide_services(fqdn)
++    else:
++        # IPA master is either enabled or configured, enable all
++        enable_services(fqdn)
++
++
+ def _set_services_state(fqdn, dest_state):
+     """Change all services of a host
+ 
+diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
+index bf028bf7dc58abb6455ba1659f2d19bede69daa2..df71972a2ba3ad503011a558295bd38f587faf44 100644
+--- a/ipatests/test_integration/test_replica_promotion.py
++++ b/ipatests/test_integration/test_replica_promotion.py
+@@ -823,9 +823,11 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+         # hidden replica with CA and DNS
+         tasks.install_replica(
+             cls.master, cls.replicas[0],
+-            setup_dns=True, setup_kra=True,
++            setup_dns=True, setup_kra=False,
+             extra_args=('--hidden-replica',)
+         )
++        # manually install KRA to verify that hidden state is synced
++        tasks.install_kra(cls.replicas[0])
+ 
+     def _check_dnsrecords(self, hosts_expected, hosts_unexpected=()):
+         domain = DNSName(self.master.domain.name).make_absolute()
+-- 
+2.20.1
+
diff --git a/SOURCES/0028-Test-replica-installation-from-hidden-replica.patch b/SOURCES/0028-Test-replica-installation-from-hidden-replica.patch
new file mode 100644
index 0000000..35e9db8
--- /dev/null
+++ b/SOURCES/0028-Test-replica-installation-from-hidden-replica.patch
@@ -0,0 +1,183 @@
+From 4850c91e063ddc0968a451ba9654c587f29a73d8 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Tue, 26 Mar 2019 16:43:55 +0100
+Subject: [PATCH] Test replica installation from hidden replica
+
+Exercise ipa-replica-install with a hidden replica as source server and
+creation of replication agreements between a hidden and an enabled
+replica.
+
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ ipatests/pytest_ipa/integration/tasks.py      | 22 ++++--
+ .../test_replica_promotion.py                 | 73 +++++++++++++++----
+ 2 files changed, 74 insertions(+), 21 deletions(-)
+
+diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
+index 1f0509189d2e3b3ad7402dd042d61e4ad4f97ed5..46506e4eee09dd1981777c8facef7c1938e07605 100644
+--- a/ipatests/pytest_ipa/integration/tasks.py
++++ b/ipatests/pytest_ipa/integration/tasks.py
+@@ -691,28 +691,38 @@ def sync_time(host, server):
+     host.run_command(['ntpdate', server.hostname])
+ 
+ 
+-def connect_replica(master, replica, domain_level=None):
++def connect_replica(master, replica, domain_level=None,
++                    database=DOMAIN_SUFFIX_NAME):
+     if domain_level is None:
+         domain_level = master.config.domain_level
+     if domain_level == DOMAIN_LEVEL_0:
+-        replica.run_command(['ipa-replica-manage', 'connect', master.hostname])
++        if database == DOMAIN_SUFFIX_NAME:
++            cmd = 'ipa-replica-manage'
++        else:
++            cmd = 'ipa-csreplica-manage'
++        replica.run_command([cmd, 'connect', master.hostname])
+     else:
+         kinit_admin(master)
+-        master.run_command(["ipa", "topologysegment-add", DOMAIN_SUFFIX_NAME,
++        master.run_command(["ipa", "topologysegment-add", database,
+                             "%s-to-%s" % (master.hostname, replica.hostname),
+                             "--leftnode=%s" % master.hostname,
+                             "--rightnode=%s" % replica.hostname
+                             ])
+ 
+ 
+-def disconnect_replica(master, replica, domain_level=None):
++def disconnect_replica(master, replica, domain_level=None,
++                       database=DOMAIN_SUFFIX_NAME):
+     if domain_level is None:
+         domain_level = master.config.domain_level
+     if domain_level == DOMAIN_LEVEL_0:
+-        replica.run_command(['ipa-replica-manage', 'disconnect', master.hostname])
++        if database == DOMAIN_SUFFIX_NAME:
++            cmd = 'ipa-replica-manage'
++        else:
++            cmd = 'ipa-csreplica-manage'
++        replica.run_command([cmd, 'disconnect', master.hostname])
+     else:
+         kinit_admin(master)
+-        master.run_command(["ipa", "topologysegment-del", DOMAIN_SUFFIX_NAME,
++        master.run_command(["ipa", "topologysegment-del", database,
+                             "%s-to-%s" % (master.hostname, replica.hostname),
+                             "--continue"
+                             ])
+diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
+index df71972a2ba3ad503011a558295bd38f587faf44..f9cc3d833072666fed348795c414e3840615ac70 100644
+--- a/ipatests/test_integration/test_replica_promotion.py
++++ b/ipatests/test_integration/test_replica_promotion.py
+@@ -15,7 +15,8 @@ from ipatests.pytest_ipa.integration.tasks import (
+     assert_error, replicas_cleanup)
+ from ipatests.pytest_ipa.integration.env_config import get_global_config
+ from ipalib.constants import (
+-    DOMAIN_LEVEL_0, DOMAIN_LEVEL_1, DOMAIN_SUFFIX_NAME, IPA_CA_NICKNAME)
++    DOMAIN_LEVEL_0, DOMAIN_LEVEL_1, DOMAIN_SUFFIX_NAME, IPA_CA_NICKNAME,
++    CA_SUFFIX_NAME)
+ from ipaplatform.paths import paths
+ from ipatests.test_integration.test_backup_and_restore import backup
+ from ipatests.test_integration.test_dns_locations import (
+@@ -807,8 +808,8 @@ class TestReplicaInForwardZone(IntegrationTest):
+ class TestHiddenReplicaPromotion(IntegrationTest):
+     """Test hidden replica features
+     """
+-    topology = 'star'
+-    num_replicas = 1
++    topology = None
++    num_replicas = 2
+ 
+     @classmethod
+     def install(cls, mh):
+@@ -850,8 +851,12 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+                 value = host.hostname if rtype == 'SRV' else host.ip
+                 assert value not in txt
+ 
+-    def _check_server_role(self, host, status):
+-        roles = [u'IPA master', u'CA server', u'KRA server', u'DNS server']
++    def _check_server_role(self, host, status, kra=True, dns=True):
++        roles = [u'IPA master', u'CA server']
++        if kra:
++            roles.append(u'KRA server')
++        if dns:
++            roles.append(u'DNS server')
+         for role in roles:
+             result = self.replicas[0].run_command([
+                 'ipa', 'server-role-find',
+@@ -936,6 +941,52 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+         self._check_server_role(self.replicas[0], 'hidden')
+         self._check_dnsrecords([self.master], [self.replicas[0]])
+ 
++    def test_replica_from_hidden(self):
++        # install a replica from a hidden replica
++        self._check_server_role(self.replicas[0], 'hidden')
++        tasks.install_replica(
++            master=self.replicas[0],
++            replica=self.replicas[1],
++            setup_dns=True
++        )
++        self._check_server_role(self.replicas[0], 'hidden')
++        self._check_server_role(
++            self.replicas[1], 'enabled', kra=False, dns=False
++        )
++        self._check_dnsrecords(
++            [self.master, self.replicas[1]], [self.replicas[0]]
++        )
++        # hide the new replica
++        self.replicas[0].run_command([
++            'ipa', 'server-state',
++            self.replicas[1].hostname, '--state=hidden'
++        ])
++        # and establish replication agreements from master
++        tasks.connect_replica(
++            master=self.master,
++            replica=self.replicas[1],
++        )
++        tasks.connect_replica(
++            master=self.master,
++            replica=self.replicas[1],
++            database=CA_SUFFIX_NAME,
++        )
++        # remove replication agreements again
++        tasks.disconnect_replica(
++            master=self.master,
++            replica=self.replicas[1],
++        )
++        tasks.disconnect_replica(
++            master=self.master,
++            replica=self.replicas[1],
++            database=CA_SUFFIX_NAME,
++        )
++        # and uninstall
++        tasks.uninstall_replica(
++            master=self.replicas[0],
++            replica=self.replicas[1],
++        )
++
+     def test_hidden_replica_backup_and_restore(self):
+         """Exercises backup+restore and hidden replica uninstall
+         """
+@@ -950,19 +1001,11 @@ class TestHiddenReplicaPromotion(IntegrationTest):
+             ['ipa-restore', backup_path],
+             stdin_text=dirman_password + '\nyes'
+         )
++
+         # give replication some time
+         time.sleep(5)
+-
+-        # check that role is still hidden
+         tasks.kinit_admin(self.replicas[0])
+-        self._check_config([self.master], [self.replicas[0]])
+-        self._check_server_role(self.replicas[0], 'hidden')
+-        self._check_dnsrecords([self.master], [self.replicas[0]])
+ 
+-        # check that the resulting server can be promoted to enabled
+-        self.replicas[0].run_command([
+-            'ipa', 'server-mod', self.replicas[0].hostname, '--state=enabled'
+-        ])
++        # FIXME: restore turns hidden replica into enabled replica
+         self._check_config([self.master, self.replicas[0]])
+         self._check_server_role(self.replicas[0], 'enabled')
+-        self._check_dnsrecords([self.master, self.replicas[0]])
+-- 
+2.20.1
+
diff --git a/SOURCES/0028-Wait-for-client-certificates.patch b/SOURCES/0028-Wait-for-client-certificates.patch
deleted file mode 100644
index e2f204a..0000000
--- a/SOURCES/0028-Wait-for-client-certificates.patch
+++ /dev/null
@@ -1,66 +0,0 @@
-From fb346fab2495a9343ed68131c0ebf071e3e9654f Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Mon, 9 Jul 2018 13:53:44 +0200
-Subject: [PATCH] Wait for client certificates
-
-ipa-client-install --request-cert now waits until certmonger has
-provided a host certificate. In case of an error, ipa-client-install no
-longer pretents to success but fails with an error code.
-
-The --request-cert option also ensures that certmonger is enabled and
-running.
-
-See: Fixes: https://pagure.io/freeipa/issue/7623
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
----
- ipaclient/install/client.py | 25 ++++++++++++++++++-------
- 1 file changed, 18 insertions(+), 7 deletions(-)
-
-diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
-index 5173d90bfeb61acab6032f2972dcc4a0d1344094..0fbe31b762561b3e2ee2f35a666a93de8857bced 100644
---- a/ipaclient/install/client.py
-+++ b/ipaclient/install/client.py
-@@ -775,6 +775,7 @@ def configure_certmonger(
-     cmonger = services.knownservices.certmonger
-     try:
-         cmonger.enable()
-+        cmonger.start()
-     except Exception as e:
-         logger.error(
-             "Failed to configure automatic startup of the %s daemon: %s",
-@@ -786,14 +787,24 @@ def configure_certmonger(
-     subject = str(DN(('CN', hostname), subject_base))
-     passwd_fname = os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt')
-     try:
--        certmonger.request_cert(
-+        certmonger.request_and_wait_for_cert(
-             certpath=paths.IPA_NSSDB_DIR,
--            nickname='Local IPA host', subject=subject, dns=[hostname],
--            principal=principal, passwd_fname=passwd_fname)
--    except Exception as ex:
--        logger.error(
--            "%s request for host certificate failed: %s",
--            cmonger.service_name, ex)
-+            storage='NSSDB',
-+            nickname='Local IPA host',
-+            subject=subject,
-+            dns=[hostname],
-+            principal=principal,
-+            passwd_fname=passwd_fname,
-+            resubmit_timeout=120,
-+        )
-+    except Exception as e:
-+        logger.exception("certmonger request failed")
-+        raise ScriptError(
-+            "{} request for host certificate failed: {}".format(
-+                cmonger.service_name, e
-+            ),
-+            rval=CLIENT_INSTALL_ERROR
-+        )
- 
- 
- def configure_sssd_conf(
--- 
-2.17.1
-
diff --git a/SOURCES/0029-Add-design-draft.patch b/SOURCES/0029-Add-design-draft.patch
new file mode 100644
index 0000000..6dff1da
--- /dev/null
+++ b/SOURCES/0029-Add-design-draft.patch
@@ -0,0 +1,179 @@
+From 451eb489193538c9ead259919669b3ca3658cbd8 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Tue, 26 Mar 2019 16:45:02 +0100
+Subject: [PATCH] Add design draft
+
+The design draft explains implementation details, limitations, and API
+changes for the new feature.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ doc/designs/hidden-replicas.md | 153 +++++++++++++++++++++++++++++++++
+ 1 file changed, 153 insertions(+)
+ create mode 100644 doc/designs/hidden-replicas.md
+
+diff --git a/doc/designs/hidden-replicas.md b/doc/designs/hidden-replicas.md
+new file mode 100644
+index 0000000000000000000000000000000000000000..1abc73612cbf2e5636d7076b5c6f50eb0ecbce4c
+--- /dev/null
++++ b/doc/designs/hidden-replicas.md
+@@ -0,0 +1,153 @@
++# Hidden replicas
++
++**TECH PREVIEW**
++
++## Overview
++
++A hidden replica is an IPA master server that is not advertised to
++clients or other masters. Hidden replicas have all services running
++and available, but none of the services has any DNS SRV records or
++enabled LDAP server roles. This makes hidden replicas invisible for
++service discovery.
++
++* IPA clients and SSSD ignore hidden replicas and don't consider them
++  during installation or daily operations.
++* Kerberos clients with ``dns_lookup_kdc = True`` do not auto-discover
++  hidden replicas.
++* Certmonger does not use a hidden replica to renew certificates.
++* Masters without a CA or KRA instance never use CA or KRA services
++  of a hidden replica.
++
++By default, only services on a hidden replica use other services on
++the same machine, e.g. local LDAP and Kerberos services.
++
++## Limitations
++
++It's critical to understand that hidden replicas have limitations. Most
++importantly, hidden replicas are just concealed, but not isolated and
++secluded. Other machines merely don't see hidden replicas, when they
++use standard mechanisms to discover IPA servers. Other machines are
++able to find hidden replicas if they know what to look for. Any machine
++is able to use services on a hidden replica, when they are explicitly
++configured to do so.
++
++* Hidden replicas are neither firewalled nor do they have any ACLs in
++  place to prevent connections from other machines. All IPA TCP and
++  UDP ports must be open for at least all other IPA servers.
++* There must be at least one regular, non-hidden server available and
++  online for each service (IPA master, DNS, CA, KRA). If DNS locations
++  are used, there should be at least one regular replica in each
++  location.
++* As of now, a hidden replica cannot be a *CA renewal master* or
++  a *DNSSEC key master*. The restriction may be lifted in the future.
++* Hard-coded server names and explicit configurations like
++  ``ipa-client-install --server=$HOST``, SSSD config, or ``ca_host``
++  setting in ``/etc/ipa/default.conf`` override auto-discovery.
++* The process of demoting a regular replica to hidden replica or
++  promotion from hidden to regular replica is not instantaneous. It
++  takes a while until the changes have been replicated and cached
++  settings are refreshed.
++
++## Use Cases
++
++Hidden replicas are primarily designed for dedicated services that may
++otherwise disrupt clients. For example a full backup requires a
++complete shutdown of all IPA services. Since a hidden replica is not
++used by any clients by default, a temporary shutdown does not affect
++clients.
++
++Other use cases include operations that put a high load on the IPA
++API or LDAP server, like mass imports or extensive queries.
++
++## How to Use
++
++### installation of a hidden replica
++
++A new hidden replica can be installed with
++``ipa-replica-install --hidden-replica``.
++
++### demotion / promotion of hidden replicas
++
++A new command ``ipa server-state`` can be used to modify the state of a
++replica. An existing replica can be demoted to a hidden replica by
++executing ``ipa server-state $HOST --state=hidden``. The command
++``ipa server-state $HOST --state=enable`` turns a hidden replica
++into an enabled, visible replica.
++
++A *CA renewal master* or *DNSSEC key master* can't be demoted to hidden
++replica. First the services must be moved to another replica with
++``ipa-dns-install --dnssec-master`` and
++``ipa config-mod --ca-renewal-master-server=$HOST``.
++
++### query status
++
++The ``ipa config-show`` command now shows additional information about
++DNS and KRA as well as hidden servers:
++
++```
++$ ipa config-show
++  ...
++  IPA masters: server1.ipa.example
++  Hidden IPA masters: hidden1.ipa.example
++  IPA master capable of PKINIT: hidden1.ipa.example, server1.ipa.example
++  IPA CA servers: server1.ipa.example
++  Hidden IPA CA servers: hidden1.ipa.example
++  IPA CA renewal master: server1.ipa.example
++  IPA KRA servers: server1.ipa.example
++  Hidden IPA KRA servers: hidden1.ipa.example
++  IPA DNS servers: server1.ipa.example
++  Hidden IPA DNS servers: hidden1.ipa.example
++  IPA DNSSec key master: server1.ipa.example
++$ ipa server-role-find --server=hidden1.ipa.example --include-master
++----------------------
++6 server roles matched
++----------------------
++  Server name: hidden1.ipa.example
++  Role name: AD trust agent
++  Role status: absent
++
++  Server name: hidden1.ipa.example
++  Role name: AD trust controller
++  Role status: absent
++
++  Server name: hidden1.ipa.example
++  Role name: CA server
++  Role status: hidden
++
++  Server name: hidden1.ipa.example
++  Role name: DNS server
++  Role status: hidden
++
++  Server name: hidden1.ipa.example
++  Role name: IPA master
++  Role status: hidden
++
++  Server name: hidden1.ipa.example
++  Role name: KRA server
++  Role status: hidden
++----------------------------
++Number of entries returned 6
++----------------------------
++```
++
++## Implementation
++
++The status of a service is stored in LDAP inside the
++``cn=masters,cn=ipa,cn=etc,$SUFFIX`` subtree. The subtree contains
++entries for each IPA master. Each entry holds a bunch of sub entries
++for services. For example
++``cn=CA,cn=hidden1.ipa.example,cn=masters,cn=ipa,cn=etc,$SUFFIX`` is
++the container for the *CA* service on the IPA master
++*hidden1.ipa.example*. During the installation process the service
++entries are created with multi-valued attribute ``ipaConfigString``
++set to ``configuredService``. At the end of the installation,
++``configuredService`` is either replaced with ``enabledService`` for a
++standard, enabled, and visible replica. Or it is set to
++``hiddenService`` for hidden, unadvertised replicas.
++
++Auto-discovery ignores any and all hidden services. The
++``dns-update-system-records`` does not create SRV records for hidden
++services. The ``find_providing_servers`` API ignores hidden services
++except for preferred hosts. CA and KRA service discovery use the
++current host or explicit ``ca_host`` option from
++``/etc/ipa/default.conf`` as preferred host.
+-- 
+2.20.1
+
diff --git a/SOURCES/0029-Fix-KRA-replica-installation-from-CA-master.patch b/SOURCES/0029-Fix-KRA-replica-installation-from-CA-master.patch
deleted file mode 100644
index 689560f..0000000
--- a/SOURCES/0029-Fix-KRA-replica-installation-from-CA-master.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 609ccb601843b97b25f2fde3c4981839822af503 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Tue, 17 Jul 2018 08:53:39 +0200
-Subject: [PATCH] Fix KRA replica installation from CA master
-
-ipa-replica-install --kra-install can fail when the topology already has
-a KRA, but replica is installed from a master with just CA. In that
-case, Custodia may pick a machine that doesn't have the KRA auditing and
-signing certs in its NSSDB.
-
-Example:
- * master with CA
- * replica1 with CA and KRA
- * new replica gets installed from master
-
-The replica installer now always picks a KRA peer.
-
-The change fixes test scenario TestInstallWithCA1::()::test_replica2_ipa_dns_install
-
-Fixes: https://pagure.io/freeipa/issue/7518
-See: https://pagure.io/freeipa/issue/7008
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaserver/install/server/replicainstall.py | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 8826da232a90380084b0e4f3dca783125a5500da..e78a2b992fbd44b8ee3ccd8183ebd6e13dfd1749 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -1486,7 +1486,10 @@ def install(installer):
-     otpd.create_instance('OTPD', config.host_name,
-                          ipautil.realm_to_suffix(config.realm_name))
- 
--    if ca_enabled:
-+    if kra_enabled:
-+        # A KRA peer always provides a CA, too.
-+        mode = custodiainstance.CustodiaModes.KRA_PEER
-+    elif ca_enabled:
-         mode = custodiainstance.CustodiaModes.CA_PEER
-     else:
-         mode = custodiainstance.CustodiaModes.MASTER_PEER
--- 
-2.17.1
-
diff --git a/SOURCES/0030-Don-t-fail-if-config-show-does-not-return-servers.patch b/SOURCES/0030-Don-t-fail-if-config-show-does-not-return-servers.patch
new file mode 100644
index 0000000..3358ee3
--- /dev/null
+++ b/SOURCES/0030-Don-t-fail-if-config-show-does-not-return-servers.patch
@@ -0,0 +1,60 @@
+From a9b0d9b109d0f35adbf45ef8d158f276014c7c8b Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Tue, 26 Mar 2019 20:09:27 +0100
+Subject: [PATCH] Don't fail if config-show does not return servers
+
+When uninstalling a cluster and only hidden servers are left,
+config-show can return a result set without ipa_master_server entry.
+
+Fixes: https://pagure.io/freeipa/issue/7892
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Thomas Woerner <twoerner@redhat.com>
+Reviewed-By: Francois Cami <fcami@redhat.com>
+---
+ ipaserver/install/ca.py     | 2 +-
+ ipaserver/plugins/pkinit.py | 2 +-
+ ipaserver/plugins/server.py | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
+index 716f3b3dc1fc81438b74d4fa393f7f17d6d2a802..09b3c09e758775de77cdf4ac5cae755183aa8f8c 100644
+--- a/ipaserver/install/ca.py
++++ b/ipaserver/install/ca.py
+@@ -106,7 +106,7 @@ def uninstall_check(options):
+ 
+     # skip the checks if the host is the last master
+     ipa_config = api.Command.config_show()['result']
+-    ipa_masters = ipa_config['ipa_master_server']
++    ipa_masters = ipa_config.get('ipa_master_server', [])
+     if len(ipa_masters) <= 1:
+         return
+ 
+diff --git a/ipaserver/plugins/pkinit.py b/ipaserver/plugins/pkinit.py
+index 8853938460073f69f6e6242c5ae5c362b3faf4f7..6d42a9af11f2079054e59416f3930fbae5b65555 100644
+--- a/ipaserver/plugins/pkinit.py
++++ b/ipaserver/plugins/pkinit.py
+@@ -91,7 +91,7 @@ class pkinit_status(Search):
+         if server is not None:
+             servers = [server]
+         else:
+-            servers = ipa_master_config['ipa_master_server']
++            servers = ipa_master_config.get('ipa_master_server', [])
+ 
+         pkinit_servers = ipa_master_config.get('pkinit_server_server')
+         if pkinit_servers is None:
+diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py
+index bfd1406ff826aea97195aa08ca35018e35cac18c..ee9c2ba7440dce276213bb97eaca621e1ef33efe 100644
+--- a/ipaserver/plugins/server.py
++++ b/ipaserver/plugins/server.py
+@@ -482,7 +482,7 @@ class server_del(LDAPDelete):
+ 
+         ipa_config = self.api.Command.config_show()['result']
+ 
+-        ipa_masters = ipa_config['ipa_master_server']
++        ipa_masters = ipa_config.get('ipa_master_server', [])
+ 
+         # skip these checks if the last master is being removed
+         if len(ipa_masters) <= 1:
+-- 
+2.20.1
+
diff --git a/SOURCES/0030-ipaserver-plugins-cert.py-Added-reason-to-raise-of-e.patch b/SOURCES/0030-ipaserver-plugins-cert.py-Added-reason-to-raise-of-e.patch
deleted file mode 100644
index e744031..0000000
--- a/SOURCES/0030-ipaserver-plugins-cert.py-Added-reason-to-raise-of-e.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From f49068ecd3484b4898517854b9886bd3b0691691 Mon Sep 17 00:00:00 2001
-From: Thomas Woerner <twoerner@redhat.com>
-Date: Fri, 27 Jul 2018 12:15:17 +0200
-Subject: [PATCH] ipaserver/plugins/cert.py: Added reason to raise of
- errors.NotFound
-
-In the case that enabledService is not found ipaConfigString kdc entry, a
-NotFound error was raised without setting the reason. This resulted in a
-traceback.
-
-Fixes: https://pagure.io/freeipa/issue/7652
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaserver/plugins/cert.py | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
-index db624357af189753980927655215b17c06291a15..0663272c9b4fd73225f63fe52d8d31157d5cc690 100644
---- a/ipaserver/plugins/cert.py
-+++ b/ipaserver/plugins/cert.py
-@@ -301,7 +301,8 @@ def ca_kdc_check(api_instance, hostname):
-         ipaconfigstring = {val.lower() for val in kdc_entry['ipaConfigString']}
- 
-         if 'enabledservice' not in ipaconfigstring:
--            raise errors.NotFound()
-+            raise errors.NotFound(
-+                reason=_("enabledService not in ipaConfigString kdc entry"))
- 
-     except errors.NotFound:
-         raise errors.ACIError(
--- 
-2.17.1
-
diff --git a/SOURCES/0031-Extract-ca_renewal-cert-update-subroutine.patch b/SOURCES/0031-Extract-ca_renewal-cert-update-subroutine.patch
new file mode 100644
index 0000000..8044f47
--- /dev/null
+++ b/SOURCES/0031-Extract-ca_renewal-cert-update-subroutine.patch
@@ -0,0 +1,89 @@
+From 474c13d1543608c8c4da06957295215bbcd5b67c Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Fri, 22 Mar 2019 13:37:45 +1100
+Subject: [PATCH] Extract ca_renewal cert update subroutine
+
+When the CA renewal master renews certificates that are shared
+across CA replicas, it puts them in LDAP for the other CA replicas
+to see.  The code to create/update these entries lives in the
+dogtag-ipa-ca-renew-agent renewal helper, but it will be useful for
+the ipa-cert-fix program too.  Extract it to a subroutine in the
+cainstance module.
+
+Part of: https://pagure.io/freeipa/issue/7885
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ .../dogtag-ipa-ca-renew-agent-submit          | 16 +-----------
+ ipaserver/install/cainstance.py               | 26 +++++++++++++++++++
+ 2 files changed, 27 insertions(+), 15 deletions(-)
+
+diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+index c33404c18c0022af6b801d25cac1eb0bec019cdf..c2ba9cb842ba835948925a8e415d1e25fe8ee139 100755
+--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+@@ -270,23 +270,9 @@ def store_cert(**kwargs):
+         return (REJECTED, "New certificate requests not supported")
+     cert = x509.load_pem_x509_certificate(cert.encode('ascii'))
+ 
+-    dn = DN(('cn', nickname), ('cn', 'ca_renewal'),
+-            ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+     try:
+         with ldap_connect() as conn:
+-            try:
+-                entry = conn.get_entry(dn, ['usercertificate'])
+-                entry['usercertificate'] = [cert]
+-                conn.update_entry(entry)
+-            except errors.NotFound:
+-                entry = conn.make_entry(
+-                    dn,
+-                    objectclass=['top', 'pkiuser', 'nscontainer'],
+-                    cn=[nickname],
+-                    usercertificate=[cert])
+-                conn.add_entry(entry)
+-            except errors.EmptyModlist:
+-                pass
++            cainstance.update_ca_renewal_entry(conn, nickname, cert)
+     except Exception as e:
+         attempts += 1
+         if attempts < 10:
+diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
+index 2946b5cc2b4b8b708a060aa79d1b7ab0e7b4e651..527ad0a1f492050d452336105cc5cf3c645af693 100644
+--- a/ipaserver/install/cainstance.py
++++ b/ipaserver/install/cainstance.py
+@@ -1711,6 +1711,32 @@ def update_authority_entry(cert):
+     return __update_entry_from_cert(make_filter, make_entry, cert)
+ 
+ 
++def update_ca_renewal_entry(conn, nickname, cert):
++    """
++    Update the ca_renewal entry for the given nickname.
++
++    :param conn: A *connected* LDAP handle
++    :param nickname: NSSDB nickname
++    :param cert: python-cryptography X509Certificate
++
++    """
++    dn = DN(('cn', nickname), ('cn', 'ca_renewal'),
++            ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
++    try:
++        entry = conn.get_entry(dn, ['usercertificate'])
++        entry['usercertificate'] = [cert]
++        conn.update_entry(entry)
++    except errors.NotFound:
++        entry = conn.make_entry(
++            dn,
++            objectclass=['top', 'pkiuser', 'nscontainer'],
++            cn=[nickname],
++            usercertificate=[cert])
++        conn.add_entry(entry)
++    except errors.EmptyModlist:
++        pass
++
++
+ def ensure_ldap_profiles_container():
+     ensure_entry(
+         DN(('ou', 'certificateProfiles'), ('ou', 'ca'), ('o', 'ipaca')),
+-- 
+2.20.1
+
diff --git a/SOURCES/0031-ipa-commands-print-IPA-is-not-configured-when-ipa-is.patch b/SOURCES/0031-ipa-commands-print-IPA-is-not-configured-when-ipa-is.patch
deleted file mode 100644
index b2c1b19..0000000
--- a/SOURCES/0031-ipa-commands-print-IPA-is-not-configured-when-ipa-is.patch
+++ /dev/null
@@ -1,135 +0,0 @@
-From 040df002df8c58c84a94d2fb3dec717beb6e5f0a Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Wed, 22 Aug 2018 18:29:07 +0200
-Subject: [PATCH] ipa commands: print 'IPA is not configured' when ipa is not
- setup
-
-Some commands print tracebacks or unclear error message when
-they are called on a machine where ipa packages are installed but
-IPA is not configured.
-Consistently report 'IPA is not configured on this system' in this
-case.
-
-Related to https://pagure.io/freeipa/issue/6261
-
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- install/tools/ipa-compat-manage          | 2 ++
- install/tools/ipa-csreplica-manage       | 4 +++-
- ipaserver/advise/base.py                 | 2 ++
- ipaserver/install/ipa_pkinit_manage.py   | 2 ++
- ipaserver/install/ipa_server_upgrade.py  | 2 ++
- ipaserver/install/ipa_winsync_migrate.py | 2 +-
- 6 files changed, 12 insertions(+), 2 deletions(-)
-
-diff --git a/install/tools/ipa-compat-manage b/install/tools/ipa-compat-manage
-index 6dd259d2aad870e575093d6732157de743502ac2..ce61b702689448dcfbddbf0dad6c2adde861e6e9 100755
---- a/install/tools/ipa-compat-manage
-+++ b/install/tools/ipa-compat-manage
-@@ -82,6 +82,8 @@ def main():
-     retval = 0
-     files = [paths.SCHEMA_COMPAT_ULDIF]
- 
-+    installutils.check_server_configuration()
-+
-     options, args = parse_options()
- 
-     if len(args) != 1:
-diff --git a/install/tools/ipa-csreplica-manage b/install/tools/ipa-csreplica-manage
-index 87f034d1205938c4551f939b0e8c0da292e90bc1..c7b62b293d68c95912ab641294180eeb74a98573 100755
---- a/install/tools/ipa-csreplica-manage
-+++ b/install/tools/ipa-csreplica-manage
-@@ -32,6 +32,7 @@ from ipaserver.install import (replication, installutils, bindinstance,
- from ipalib import api, errors
- from ipalib.util import has_managed_topology
- from ipapython import ipautil, ipaldap, version
-+from ipapython.admintool import ScriptError
- from ipapython.dn import DN
- 
- logger = logging.getLogger(os.path.basename(__file__))
-@@ -408,6 +409,7 @@ def exit_on_managed_topology(what, hint="topologysegment"):
- 
- 
- def main():
-+    installutils.check_server_configuration()
-     options, args = parse_options()
- 
-     # Just initialize the environment. This is so the installer can have
-@@ -496,7 +498,7 @@ try:
-     main()
- except KeyboardInterrupt:
-     sys.exit(1)
--except SystemExit as e:
-+except (SystemExit, ScriptError) as e:
-     sys.exit(e)
- except Exception as e:
-     sys.exit("unexpected error: %s" % e)
-diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py
-index 40f2e65dc8f74a833ef6fe107eabf96ac6a3c12b..07b1431e84c31cb7ba2ecc58a9d152e596dd8b00 100644
---- a/ipaserver/advise/base.py
-+++ b/ipaserver/advise/base.py
-@@ -30,6 +30,7 @@ from ipalib.errors import ValidationError
- from ipaplatform.paths import paths
- from ipapython import admintool
- from ipapython.ipa_log_manager import Filter
-+from ipaserver.install import installutils
- 
- 
- """
-@@ -418,6 +419,7 @@ class IpaAdvise(admintool.AdminTool):
- 
-     def validate_options(self):
-         super(IpaAdvise, self).validate_options(needs_root=False)
-+        installutils.check_server_configuration()
- 
-         if len(self.args) > 1:
-             raise self.option_parser.error("You can only provide one "
-diff --git a/ipaserver/install/ipa_pkinit_manage.py b/ipaserver/install/ipa_pkinit_manage.py
-index c54bb14f58b9bfd19ae860270a1c1955b4de1732..4a79bba5d1b636827a7a031965b49cf7b34c6330 100644
---- a/ipaserver/install/ipa_pkinit_manage.py
-+++ b/ipaserver/install/ipa_pkinit_manage.py
-@@ -9,6 +9,7 @@ import logging
- from ipalib import api
- from ipaplatform.paths import paths
- from ipapython.admintool import AdminTool
-+from ipaserver.install import installutils
- from ipaserver.install.krbinstance import KrbInstance, is_pkinit_enabled
- 
- logger = logging.getLogger(__name__)
-@@ -21,6 +22,7 @@ class PKINITManage(AdminTool):
- 
-     def validate_options(self):
-         super(PKINITManage, self).validate_options(needs_root=True)
-+        installutils.check_server_configuration()
- 
-         option_parser = self.option_parser
- 
-diff --git a/ipaserver/install/ipa_server_upgrade.py b/ipaserver/install/ipa_server_upgrade.py
-index 1e52bca9765671c0b63bf08b0ad47d40477037ee..7643b97f22fbf6c6861317a171c6e2da377365cd 100644
---- a/ipaserver/install/ipa_server_upgrade.py
-+++ b/ipaserver/install/ipa_server_upgrade.py
-@@ -35,6 +35,8 @@ class ServerUpgrade(admintool.AdminTool):
-     def validate_options(self):
-         super(ServerUpgrade, self).validate_options(needs_root=True)
- 
-+        installutils.check_server_configuration()
-+
-         if self.options.force:
-             self.options.skip_version_check = True
- 
-diff --git a/ipaserver/install/ipa_winsync_migrate.py b/ipaserver/install/ipa_winsync_migrate.py
-index 0399b9b7b683cd92692094b53208dcc0f40fd392..43330ad502e736816a1af8e8c2a444e1eae05baa 100644
---- a/ipaserver/install/ipa_winsync_migrate.py
-+++ b/ipaserver/install/ipa_winsync_migrate.py
-@@ -350,7 +350,7 @@ class WinsyncMigrate(admintool.AdminTool):
-         # Check if the IPA server is configured before attempting to migrate
-         try:
-             installutils.check_server_configuration()
--        except RuntimeError as e:
-+        except admintool.ScriptError as e:
-             sys.exit(e)
- 
-         # Finalize API
--- 
-2.17.1
-
diff --git a/SOURCES/0032-DS-replication-settings-fix-regression-with-3.3-mast.patch b/SOURCES/0032-DS-replication-settings-fix-regression-with-3.3-mast.patch
deleted file mode 100644
index 3b21d3c..0000000
--- a/SOURCES/0032-DS-replication-settings-fix-regression-with-3.3-mast.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From e561f8bb163e68766b61bca72619a54cc5e8bc2d Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Tue, 21 Aug 2018 11:37:17 +0200
-Subject: [PATCH] DS replication settings: fix regression with <3.3 master
-
-Commit 811b0fdb4620938963f1a29d3fdd22257327562c introduced a regression
-when configuring replication with a master < 3.3
-Even if 389-ds schema is extended with nsds5ReplicaReleaseTimeout,
-nsds5ReplicaBackoffMax and nsDS5ReplicaBindDnGroupCheckInterval
-attributes, it will return UNWILLING_TO_PERFORM when a mod
-operation is performed on the cn=replica entry.
-
-This patch ignores the error and logs a debug msg.
-
-See: https://pagure.io/freeipa/issue/7617
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/replication.py | 16 +++++++++++++++-
- 1 file changed, 15 insertions(+), 1 deletion(-)
-
-diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
-index 78c4a43cc9b8d9a6740d26209f347f64b879743e..92a99cd9482f86d6820230479bf94c871669572e 100644
---- a/ipaserver/install/replication.py
-+++ b/ipaserver/install/replication.py
-@@ -22,6 +22,7 @@ from __future__ import print_function, absolute_import
- import logging
- import itertools
- 
-+import re
- import six
- import time
- import datetime
-@@ -600,7 +601,20 @@ class ReplicationManager(object):
-             r_conn.simple_bind(r_binddn, r_bindpw)
-         else:
-             r_conn.gssapi_bind()
--        self._finalize_replica_settings(r_conn)
-+        # If the remote server has 389-ds < 1.3, it does not
-+        # support the attributes we are trying to set.
-+        # Find which 389-ds is installed
-+        rootdse = r_conn.get_entry(DN(''), ['vendorVersion'])
-+        version = rootdse.single_value.get('vendorVersion')
-+        mo = re.search(r'(\d+)\.(\d+)\.(\d+)[\.\d]*', version)
-+        vendor_version = tuple(int(v) for v in mo.groups())
-+        if vendor_version >= (1, 3, 0):
-+            # 389-ds understands the replication attributes,
-+            # we can safely modify them
-+            self._finalize_replica_settings(r_conn)
-+        else:
-+            logger.debug("replication attributes not supported "
-+                         "on remote master, skipping update.")
-         r_conn.close()
- 
-     def setup_chaining_backend(self, conn):
--- 
-2.17.1
-
diff --git a/SOURCES/0032-cainstance-add-function-to-determine-ca_renewal-nick.patch b/SOURCES/0032-cainstance-add-function-to-determine-ca_renewal-nick.patch
new file mode 100644
index 0000000..df1d189
--- /dev/null
+++ b/SOURCES/0032-cainstance-add-function-to-determine-ca_renewal-nick.patch
@@ -0,0 +1,85 @@
+From d868e27dfe7ab9955faebf0a08be3b41971bf816 Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Fri, 22 Mar 2019 15:22:21 +1100
+Subject: [PATCH] cainstance: add function to determine ca_renewal nickname
+
+The ipa-cert-fix program needs to know where to put shared
+certificates.  Extract the logic that computes the nickname from
+dogtag-ipa-ca-renew-agent to new subroutine
+cainstance.get_ca_renewal_nickname().
+
+Part of: https://pagure.io/freeipa/issue/7885
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ .../dogtag-ipa-ca-renew-agent-submit          | 16 ++----------
+ ipaserver/install/cainstance.py               | 26 +++++++++++++++++++
+ 2 files changed, 28 insertions(+), 14 deletions(-)
+
+diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+index c2ba9cb842ba835948925a8e415d1e25fe8ee139..31b4a1b7fc23567e91f8aa9938ce4e0941a84a8c 100755
+--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
++++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+@@ -85,20 +85,8 @@ def get_nickname():
+ 
+     ca_subject_dn = ca.lookup_ca_subject(api, subject_base)
+ 
+-    nickname_by_subject_dn = {
+-        DN(ca_subject_dn): 'caSigningCert cert-pki-ca',
+-        DN('CN=CA Audit', subject_base): 'auditSigningCert cert-pki-ca',
+-        DN('CN=OCSP Subsystem', subject_base): 'ocspSigningCert cert-pki-ca',
+-        DN('CN=CA Subsystem', subject_base): 'subsystemCert cert-pki-ca',
+-        DN('CN=KRA Audit', subject_base): 'auditSigningCert cert-pki-kra',
+-        DN('CN=KRA Transport Certificate', subject_base):
+-            'transportCert cert-pki-kra',
+-        DN('CN=KRA Storage Certificate', subject_base):
+-            'storageCert cert-pki-kra',
+-        DN('CN=IPA RA', subject_base): 'ipaCert',
+-    }
+-
+-    return nickname_by_subject_dn.get(DN(subject))
++    return cainstance.get_ca_renewal_nickname(
++        subject_base, ca_subject_dn, DN(subject))
+ 
+ 
+ def is_replicated():
+diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
+index 527ad0a1f492050d452336105cc5cf3c645af693..b4f6262b2c41e2da7992c403154a476aa3b82dd1 100644
+--- a/ipaserver/install/cainstance.py
++++ b/ipaserver/install/cainstance.py
+@@ -1711,6 +1711,32 @@ def update_authority_entry(cert):
+     return __update_entry_from_cert(make_filter, make_entry, cert)
+ 
+ 
++def get_ca_renewal_nickname(subject_base, ca_subject_dn, sdn):
++    """
++    Get the nickname for storage in the cn_renewal container.
++
++    :param subject_base: Certificate subject base
++    :param ca_subject_dn: IPA CA subject DN
++    :param sdn: Subject DN
++    :return: string, or None if nickname cannot be determined.
++
++    """
++    assert isinstance(sdn, DN)
++    nickname_by_subject_dn = {
++        DN(ca_subject_dn): 'caSigningCert cert-pki-ca',
++        DN('CN=CA Audit', subject_base): 'auditSigningCert cert-pki-ca',
++        DN('CN=OCSP Subsystem', subject_base): 'ocspSigningCert cert-pki-ca',
++        DN('CN=CA Subsystem', subject_base): 'subsystemCert cert-pki-ca',
++        DN('CN=KRA Audit', subject_base): 'auditSigningCert cert-pki-kra',
++        DN('CN=KRA Transport Certificate', subject_base):
++            'transportCert cert-pki-kra',
++        DN('CN=KRA Storage Certificate', subject_base):
++            'storageCert cert-pki-kra',
++        DN('CN=IPA RA', subject_base): 'ipaCert',
++    }
++    return nickname_by_subject_dn.get(sdn)
++
++
+ def update_ca_renewal_entry(conn, nickname, cert):
+     """
+     Update the ca_renewal entry for the given nickname.
+-- 
+2.20.1
+
diff --git a/SOURCES/0033-Disable-message-about-log-in-ipa-backup-if-IPA-is-no.patch b/SOURCES/0033-Disable-message-about-log-in-ipa-backup-if-IPA-is-no.patch
deleted file mode 100644
index 6d0a2af..0000000
--- a/SOURCES/0033-Disable-message-about-log-in-ipa-backup-if-IPA-is-no.patch
+++ /dev/null
@@ -1,92 +0,0 @@
-From 840e6144cd1ad75a7cf6ba5a8c936c6aa9bfc58d Mon Sep 17 00:00:00 2001
-From: Rob Crittenden <rcritten@redhat.com>
-Date: Tue, 1 May 2018 11:15:18 -0400
-Subject: [PATCH] Disable message about log in ipa-backup if IPA is not
- configured
-
-Introduce server installation constants similar to the client
-but only tie in SERVER_NOT_CONFIGURED right now.
-
-For the case of not configured don't spit out the "See <some log>
-for more information" because no logging was actually done.
-
-In the case of ipa-backup this could also be confusing if the
---log-file option was also passed in because it would not be
-used.
-
-https://pagure.io/freeipa/issue/6843
-
-Signed-off-by: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- install/tools/man/ipa-backup.1    | 2 ++
- ipapython/admintool.py            | 8 +++++++-
- ipaserver/install/installutils.py | 5 +++--
- 3 files changed, 12 insertions(+), 3 deletions(-)
-
-diff --git a/install/tools/man/ipa-backup.1 b/install/tools/man/ipa-backup.1
-index ff9759ec77d54f32532c4ececfa5081daab9ec15..9e2900f770880d3a554df5cd5d0430716e3bf70e 100644
---- a/install/tools/man/ipa-backup.1
-+++ b/install/tools/man/ipa-backup.1
-@@ -69,6 +69,8 @@ Log to the given file
- 0 if the command was successful
- 
- 1 if an error occurred
-+
-+2 if IPA is not configured
- .SH "FILES"
- .PP
- \fI/var/lib/ipa/backup\fR
-diff --git a/ipapython/admintool.py b/ipapython/admintool.py
-index 329e20f3744e304626bebcadfbb187782451dd4f..5a5d3eb421b204944123e19a2d7303d7237492cb 100644
---- a/ipapython/admintool.py
-+++ b/ipapython/admintool.py
-@@ -32,6 +32,10 @@ from ipapython import version
- from ipapython import config
- from ipapython.ipa_log_manager import standard_logging_setup
- 
-+SUCCESS = 0
-+SERVER_INSTALL_ERROR = 1
-+SERVER_NOT_CONFIGURED = 2
-+
- logger = logging.getLogger(__name__)
- 
- 
-@@ -301,7 +305,9 @@ class AdminTool(object):
-         if error_message:
-             logger.error('%s', error_message)
-         message = "The %s command failed." % self.command_name
--        if self.log_file_name:
-+        if self.log_file_name and return_value != 2:
-+            # magic value because this is common between server and client
-+            # but imports are not straigthforward
-             message += " See %s for more information" % self.log_file_name
-         logger.error('%s', message)
- 
-diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
-index 22889ed1b305b8df91eda0831c56c4b187cf6f86..6614da69a0a046cdfa1b309f77972ead9de1279f 100644
---- a/ipaserver/install/installutils.py
-+++ b/ipaserver/install/installutils.py
-@@ -56,7 +56,7 @@ from ipalib.install import sysrestore
- from ipalib.install.kinit import kinit_password
- import ipaplatform
- from ipapython import ipautil, admintool, version
--from ipapython.admintool import ScriptError
-+from ipapython.admintool import ScriptError, SERVER_NOT_CONFIGURED  # noqa: E402
- from ipapython.certdb import EXTERNAL_CA_TRUST_FLAGS
- from ipapython.ipaldap import DIRMAN_DN, LDAPClient
- from ipalib.util import validate_hostname
-@@ -934,7 +934,8 @@ def check_server_configuration():
-     """
-     server_fstore = sysrestore.FileStore(paths.SYSRESTORE)
-     if not server_fstore.has_files():
--        raise RuntimeError("IPA is not configured on this system.")
-+        raise ScriptError("IPA is not configured on this system.",
-+                          rval=SERVER_NOT_CONFIGURED)
- 
- 
- def remove_file(filename):
--- 
-2.17.1
-
diff --git a/SOURCES/0033-constants-add-ca_renewal-container.patch b/SOURCES/0033-constants-add-ca_renewal-container.patch
new file mode 100644
index 0000000..f0d798f
--- /dev/null
+++ b/SOURCES/0033-constants-add-ca_renewal-container.patch
@@ -0,0 +1,42 @@
+From dc96bc41bbff6b0f596649e992df53734278e24f Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Tue, 26 Mar 2019 19:43:25 +1100
+Subject: [PATCH] constants: add ca_renewal container
+
+Part of: https://pagure.io/freeipa/issue/7885
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ ipalib/constants.py             | 1 +
+ ipaserver/install/cainstance.py | 3 +--
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/ipalib/constants.py b/ipalib/constants.py
+index 7ff9c6aed32debd609db2650420994aa32e2466c..678a708aa19b682046b570fdce1804ea58865c88 100644
+--- a/ipalib/constants.py
++++ b/ipalib/constants.py
+@@ -129,6 +129,7 @@ DEFAULT_CONFIG = (
+     ('container_sysaccounts', DN(('cn', 'sysaccounts'), ('cn', 'etc'))),
+     ('container_certmap', DN(('cn', 'certmap'))),
+     ('container_certmaprules', DN(('cn', 'certmaprules'), ('cn', 'certmap'))),
++    ('container_ca_renewal', DN(('cn', 'ca_renewal'), ('cn', 'ipa'), ('cn', 'etc'))),
+ 
+     # Ports, hosts, and URIs:
+     # Following values do not have any reasonable default.
+diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
+index b4f6262b2c41e2da7992c403154a476aa3b82dd1..1f22d120478a6d4019663281d3191a27a5ee09ea 100644
+--- a/ipaserver/install/cainstance.py
++++ b/ipaserver/install/cainstance.py
+@@ -1746,8 +1746,7 @@ def update_ca_renewal_entry(conn, nickname, cert):
+     :param cert: python-cryptography X509Certificate
+ 
+     """
+-    dn = DN(('cn', nickname), ('cn', 'ca_renewal'),
+-            ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
++    dn = DN(('cn', nickname), api.env.container_ca_renewal, api.env.basedn)
+     try:
+         entry = conn.get_entry(dn, ['usercertificate'])
+         entry['usercertificate'] = [cert]
+-- 
+2.20.1
+
diff --git a/SOURCES/0034-Add-ipa-cert-fix-tool.patch b/SOURCES/0034-Add-ipa-cert-fix-tool.patch
new file mode 100644
index 0000000..bfd021b
--- /dev/null
+++ b/SOURCES/0034-Add-ipa-cert-fix-tool.patch
@@ -0,0 +1,363 @@
+From 0a65f076441cfbbf659e109ea6de026504261dba Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Fri, 22 Mar 2019 16:53:53 +1100
+Subject: [PATCH] Add ipa-cert-fix tool
+
+The ipa-cert-fix tool wraps `pki-server cert-fix`, performing
+additional certificate requests for non-Dogtag IPA certificates and
+performing additional actions.  In particular:
+
+- Run cert-fix with arguments particular to the IPA deployment.
+
+- Update IPA RA certificate in the ipara user entry (if renewed).
+
+- Add shared certificates (if renewed) to the ca_renewal LDAP
+  container for replication.
+
+- Become the CA renewal master if shared certificates were renewed.
+  This ensures other CA replicas, including the previous CA renewal
+  master if not the current host, pick up those new certificates
+  when Certmonger attempts to renew them.
+
+Fixes: https://pagure.io/freeipa/issue/7885
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ freeipa.spec.in                   |   2 +
+ install/tools/Makefile.am         |   1 +
+ install/tools/ipa-cert-fix        |   8 +
+ ipaserver/install/ipa_cert_fix.py | 276 ++++++++++++++++++++++++++++++
+ 4 files changed, 287 insertions(+)
+ create mode 100755 install/tools/ipa-cert-fix
+ create mode 100644 ipaserver/install/ipa_cert_fix.py
+
+diff --git a/freeipa.spec.in b/freeipa.spec.in
+index 34394b5caa811bffc677da9644120c90a09b1e81..775394619ab0eb682935c0d28fe434bcf8248a01 100644
+--- a/freeipa.spec.in
++++ b/freeipa.spec.in
+@@ -995,6 +995,7 @@ install/tools/ipa-adtrust-install
+ install/tools/ipa-backup
+ install/tools/ipa-ca-install
+ install/tools/ipa-cacert-manage
++install/tools/ipa-cert-fix
+ install/tools/ipa-compat-manage
+ install/tools/ipa-crlgen-manage
+ install/tools/ipa-csreplica-manage
+@@ -1383,6 +1384,7 @@ fi
+ %{_sbindir}/ipa-winsync-migrate
+ %{_sbindir}/ipa-pkinit-manage
+ %{_sbindir}/ipa-crlgen-manage
++%{_sbindir}/ipa-cert-fix
+ %{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit
+ %{_libexecdir}/certmonger/ipa-server-guard
+ %dir %{_libexecdir}/ipa
+diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
+index 9dcd76762f678684b421ce4a7b7b27e673a3ff94..6cdfd109552bff2effbcfabe77ff8bf44c1c60da 100644
+--- a/install/tools/Makefile.am
++++ b/install/tools/Makefile.am
+@@ -30,6 +30,7 @@ dist_sbin_SCRIPTS =		\
+ 	ipa-winsync-migrate	\
+ 	ipa-pkinit-manage	\
+ 	ipa-crlgen-manage	\
++	ipa-cert-fix		\
+ 	$(NULL)
+ 
+ appdir = $(libexecdir)/ipa/
+diff --git a/install/tools/ipa-cert-fix b/install/tools/ipa-cert-fix
+new file mode 100755
+index 0000000000000000000000000000000000000000..e1fc6f056a2a02ee350a2b09433be6cef46e514a
+--- /dev/null
++++ b/install/tools/ipa-cert-fix
+@@ -0,0 +1,8 @@
++#! /usr/bin/python2 -E
++#
++# Copyright (C) 2019  FreeIPA Contributors see COPYING for license
++#
++
++from ipaserver.install.ipa_cert_fix import IPACertFix
++
++IPACertFix.run_cli()
+diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
+new file mode 100644
+index 0000000000000000000000000000000000000000..3d9070eac1e7dc03840215dffeb4d73f4d3d0a47
+--- /dev/null
++++ b/ipaserver/install/ipa_cert_fix.py
+@@ -0,0 +1,276 @@
++#
++# Copyright (C) 2019  FreeIPA Contributors see COPYING for license
++#
++
++# ipa-cert-fix performs the following steps:
++#
++# 1. Confirm running as root (AdminTool.validate_options does this)
++#
++# 2. Confirm that DS is up.
++#
++# 3. Determine which of following certs (if any) need renewing
++#     - IPA RA
++#     - Apache HTTPS
++#     - 389 LDAPS
++#     - Kerberos KDC (PKINIT)
++#
++# 4. Execute `pki-server cert-fix` with relevant options,
++#    including `--extra-cert SERIAL` for each cert from #3.
++#
++# 5. Print details of renewed certificates.
++#
++# 6. Install renewed certs from #3 in relevant places
++#
++# 7. ipactl restart
++
++from __future__ import print_function, absolute_import
++
++import datetime
++from enum import Enum
++import logging
++import shutil
++
++from ipalib import api
++from ipalib import x509
++from ipaplatform.paths import paths
++from ipapython.admintool import AdminTool
++from ipapython.certdb import NSSDatabase, EMPTY_TRUST_FLAGS
++from ipapython.dn import DN
++from ipaserver.install import ca, cainstance, dsinstance, installutils
++from ipaserver.install.installutils import is_ipa_configured
++from ipapython import ipautil
++
++msg = """
++                          WARNING
++
++ipa-cert-fix is intended for recovery when expired certificates
++prevent the normal operation of FreeIPA.  It should ONLY be used
++in such scenarios, and backup of the system, especially certificates
++and keys, is STRONGLY RECOMMENDED.
++
++"""
++
++logger = logging.getLogger(__name__)
++
++
++class IPACertType(Enum):
++    IPARA = "IPA RA"
++    HTTPS = "Apache HTTPS"
++    LDAPS = "LDAP"
++    KDC = "KDC"
++
++
++class IPACertFix(AdminTool):
++    command_name = "ipa-cert-fix"
++    usage = "%prog"
++    description = "Renew expired certificates."
++
++    def validate_options(self):
++        super(IPACertFix, self).validate_options(needs_root=True)
++
++    def run(self):
++        if not is_ipa_configured():
++            print("IPA is not configured.")
++            return 0  # not really an error
++
++        if not cainstance.is_ca_installed_locally():
++            print("CA is not installed on this server.")
++            return 0  # not really an error
++
++        try:
++            ipautil.run(['pki-server', 'cert-fix', '--help'], raiseonerr=True)
++        except ipautil.CalledProcessError:
++            print(
++                "The 'pki-server cert-fix' command is not available; "
++                "cannot proceed."
++            )
++            return 1
++
++        api.bootstrap(in_server=True, confdir=paths.ETC_IPA)
++        api.finalize()
++        api.Backend.ldap2.connect()  # ensure DS is up
++
++        subject_base = dsinstance.DsInstance().find_subject_base()
++        if not subject_base:
++            raise RuntimeError("Cannot determine certificate subject base.")
++
++        ca_subject_dn = ca.lookup_ca_subject(api, subject_base)
++
++        now = datetime.datetime.now() + datetime.timedelta(weeks=2)
++        certs, extra_certs = expired_certs(now)
++
++        if not certs and not extra_certs:
++            print("Nothing to do.")
++            return 0
++
++        print(msg)
++
++        print_intentions(certs, extra_certs)
++
++        response = ipautil.user_input('Enter "yes" to proceed')
++        if response.lower() != 'yes':
++            print("Not proceeding.")
++            return 0
++        print("Proceeding.")
++
++        run_cert_fix(certs, extra_certs)
++
++        replicate_dogtag_certs(subject_base, ca_subject_dn, certs)
++        install_ipa_certs(subject_base, ca_subject_dn, extra_certs)
++
++        if any(x != 'sslserver' for x in certs) \
++                or any(x[0] is IPACertType.IPARA for x in extra_certs):
++            # we renewed a "shared" certificate, therefore we must
++            # become the renewal master
++            print("Becoming renewal master.")
++            cainstance.CAInstance().set_renewal_master()
++
++        ipautil.run(['ipactl', 'restart'], raiseonerr=True)
++
++
++def expired_certs(now):
++    return expired_dogtag_certs(now), expired_ipa_certs(now)
++
++
++def expired_dogtag_certs(now):
++    """
++    Determine which Dogtag certs are expired, or close to expiry.
++
++    Return a list of (cert_id, cert) pairs.
++
++    """
++    certs = []
++    db = NSSDatabase(nssdir=paths.PKI_TOMCAT_ALIAS_DIR)
++
++    for certid, nickname in [
++        ('sslserver', 'Server-Cert cert-pki-ca'),
++        ('subsystem', 'subsystemCert cert-pki-ca'),
++        ('ca_ocsp_signing', 'ocspSigningCert cert-pki-ca'),
++        ('ca_audit_signing', 'auditSigningCert cert-pki-ca'),
++        ('kra_transport', 'transportCert cert-pki-kra'),
++        ('kra_storage', 'storageCert cert-pki-kra'),
++        ('kra_audit_signing', 'auditSigningCert cert-pki-kra'),
++    ]:
++        try:
++            cert = db.get_cert(nickname)
++        except RuntimeError:
++            pass  # unfortunately certdb doesn't give us a better exception
++        else:
++            if cert.not_valid_after <= now:
++                certs.append((certid, cert))
++
++    return certs
++
++
++def expired_ipa_certs(now):
++    """
++    Determine which IPA certs are expired, or close to expiry.
++
++    Return a list of (IPACertType, cert) pairs.
++
++    """
++    certs = []
++
++    # IPA RA
++    cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
++    if cert.not_valid_after <= now:
++        certs.append((IPACertType.IPARA, cert))
++
++    # Apache HTTPD
++    db = NSSDatabase(nssdir=paths.HTTPD_ALIAS_DIR)
++    cert = db.get_cert('Server-Cert')
++    if cert.not_valid_after <= now:
++        certs.append((IPACertType.HTTPS, cert))
++
++    # LDAPS
++    ds_dbdir = dsinstance.config_dirname(
++        installutils.realm_to_serverid(api.env.realm))
++    db = NSSDatabase(nssdir=ds_dbdir)
++    cert = db.get_cert('Server-Cert')
++    if cert.not_valid_after <= now:
++        certs.append((IPACertType.LDAPS, cert))
++
++    # KDC
++    cert = x509.load_certificate_from_file(paths.KDC_CERT)
++    if cert.not_valid_after <= now:
++        certs.append((IPACertType.KDC, cert))
++
++    return certs
++
++
++def print_intentions(dogtag_certs, ipa_certs):
++    print("The following certificates will be renewed: ")
++    print()
++
++    for certid, cert in dogtag_certs:
++        print_cert_info("Dogtag", certid, cert)
++
++    for certtype, cert in ipa_certs:
++        print_cert_info("IPA", certtype.value, cert)
++
++
++def print_cert_info(context, desc, cert):
++    print("{} {} certificate:".format(context, desc))
++    print("  Subject: {}".format(DN(cert.subject)))
++    print("  Serial:  {}".format(cert.serial_number))
++    print("  Expires: {}".format(cert.not_valid_after))
++    print()
++
++
++def run_cert_fix(certs, extra_certs):
++    ldapi_path = (
++        paths.SLAPD_INSTANCE_SOCKET_TEMPLATE
++        % '-'.join(api.env.realm.split('.'))
++    )
++    cmd = [
++        'pki-server',
++        'cert-fix',
++        '--ldapi-socket', ldapi_path,
++        '--agent-uid', 'ipara',
++    ]
++    for certid, _cert in certs:
++        cmd.extend(['--cert', certid])
++    for _certtype, cert in extra_certs:
++        cmd.extend(['--extra-cert', str(cert.serial_number)])
++    ipautil.run(cmd, raiseonerr=True)
++
++
++def replicate_dogtag_certs(subject_base, ca_subject_dn, certs):
++    for certid, _oldcert in certs:
++        cert_path = "/etc/pki/pki-tomcat/certs/{}.crt".format(certid)
++        cert = x509.load_certificate_from_file(cert_path)
++        print_cert_info("Renewed Dogtag", certid, cert)
++        replicate_cert(subject_base, ca_subject_dn, cert)
++
++
++def install_ipa_certs(subject_base, ca_subject_dn, certs):
++    """Print details and install renewed IPA certificates."""
++    for certtype, oldcert in certs:
++        cert_path = "/etc/pki/pki-tomcat/certs/{}-renewed.crt" \
++                .format(oldcert.serial_number)
++        cert = x509.load_certificate_from_file(cert_path)
++        print_cert_info("Renewed IPA", certtype.value, cert)
++
++        if certtype is IPACertType.IPARA:
++            shutil.copyfile(cert_path, paths.RA_AGENT_PEM)
++            cainstance.update_people_entry(cert)
++            replicate_cert(subject_base, ca_subject_dn, cert)
++        elif certtype is IPACertType.HTTPS:
++            db = NSSDatabase(nssdir=paths.HTTPD_ALIAS_DIR)
++            db.delete_cert('Server-Cert')
++            db.import_pem_cert('Server-Cert', EMPTY_TRUST_FLAGS, cert_path)
++        elif certtype is IPACertType.LDAPS:
++            ds_dbdir = dsinstance.config_dirname(
++                installutils.realm_to_serverid(api.env.realm))
++            db = NSSDatabase(nssdir=ds_dbdir)
++            db.delete_cert('Server-Cert')
++            db.import_pem_cert('Server-Cert', EMPTY_TRUST_FLAGS, cert_path)
++        elif certtype is IPACertType.KDC:
++            shutil.copyfile(cert_path, paths.KDC_CERT)
++
++
++def replicate_cert(subject_base, ca_subject_dn, cert):
++    nickname = cainstance.get_ca_renewal_nickname(
++        subject_base, ca_subject_dn, DN(cert.subject))
++    if nickname:
++        cainstance.update_ca_renewal_entry(api.Backend.ldap2, nickname, cert)
+-- 
+2.20.1
+
diff --git a/SOURCES/0034-uninstall-v-remove-Tracebacks.patch b/SOURCES/0034-uninstall-v-remove-Tracebacks.patch
deleted file mode 100644
index d498c81..0000000
--- a/SOURCES/0034-uninstall-v-remove-Tracebacks.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-From da98cbda7b61792d956b21f0a9999d91894ae194 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Tue, 21 Aug 2018 17:55:45 +0200
-Subject: [PATCH] uninstall -v: remove Tracebacks
-
-ipa-server-install --uninstall -v -U prints Traceback in its log file.
-This issue happens because it calls subprocess.Popen with close_fds=True
-(which closes all file descriptors in the child process)
-but it is trying to use the file logger in the child process
-(preexec_fn is called in the child just before the child is executed).
-The fix is using the logger only in the parent process.
-
-Fixes: https://pagure.io/freeipa/issue/7681
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipapython/ipautil.py | 23 ++++++++++++-----------
- 1 file changed, 12 insertions(+), 11 deletions(-)
-
-diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
-index 0de5fe86374429bfb9666a0d257e079883a1c56b..42329ad7c1677ad16c8d3275d79666005571cbc7 100644
---- a/ipapython/ipautil.py
-+++ b/ipapython/ipautil.py
-@@ -481,20 +481,21 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None,
-     logger.debug('Starting external process')
-     logger.debug('args=%s', arg_string)
- 
--    def preexec_fn():
--        if runas is not None:
--            pent = pwd.getpwnam(runas)
-+    if runas is not None:
-+        pent = pwd.getpwnam(runas)
- 
--            suplementary_gids = [
--                grp.getgrnam(sgroup).gr_gid for sgroup in suplementary_groups
--            ]
-+        suplementary_gids = [
-+            grp.getgrnam(sgroup).gr_gid for sgroup in suplementary_groups
-+        ]
- 
--            logger.debug('runas=%s (UID %d, GID %s)', runas,
--                         pent.pw_uid, pent.pw_gid)
--            if suplementary_groups:
--                for group, gid in zip(suplementary_groups, suplementary_gids):
--                    logger.debug('suplementary_group=%s (GID %d)', group, gid)
-+        logger.debug('runas=%s (UID %d, GID %s)', runas,
-+                     pent.pw_uid, pent.pw_gid)
-+        if suplementary_groups:
-+            for group, gid in zip(suplementary_groups, suplementary_gids):
-+                logger.debug('suplementary_group=%s (GID %d)', group, gid)
- 
-+    def preexec_fn():
-+        if runas is not None:
-             os.setgroups(suplementary_gids)
-             os.setregid(pent.pw_gid, pent.pw_gid)
-             os.setreuid(pent.pw_uid, pent.pw_uid)
--- 
-2.17.1
-
diff --git a/SOURCES/0035-Do-not-set-ca_host-when-setup-ca-is-used.patch b/SOURCES/0035-Do-not-set-ca_host-when-setup-ca-is-used.patch
deleted file mode 100644
index 27b11e1..0000000
--- a/SOURCES/0035-Do-not-set-ca_host-when-setup-ca-is-used.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-From 06654aba40bd79eff8bd44ac629bb5bb9b8f9c26 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= <tdudlak@redhat.com>
-Date: Thu, 26 Jul 2018 11:46:55 +0200
-Subject: [PATCH] Do not set ca_host when --setup-ca is used
-
-Setting ca_host caused replication failures on DL0
-because it was trying to connect to wrong CA host.
-Trying to avoid corner-case in ipaserver/plugins/dogtag.py
-when api.env.host nor api.env.ca_host had not CA configured
-and there was ca_host set to api.env.ca_host variable.
-
-See: https://pagure.io/freeipa/issue/7566
-Resolves: https://pagure.io/freeipa/issue/7629
-Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaserver/install/cainstance.py            | 24 ++++++++++++++++++++++
- ipaserver/install/server/replicainstall.py |  7 +++++--
- 2 files changed, 29 insertions(+), 2 deletions(-)
-
-diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
-index 6dbf69b3e5833f220a4d7d640b66a8fcf824f445..ffcebd719a16ebc5a991b35507b96411ad31eb10 100644
---- a/ipaserver/install/cainstance.py
-+++ b/ipaserver/install/cainstance.py
-@@ -451,6 +451,11 @@ class CAInstance(DogtagInstance):
-                 self.step("updating IPA configuration", update_ipa_conf)
-                 self.step("enabling CA instance", self.__enable_instance)
-                 if not promote:
-+                    if self.clone:
-+                        # DL0 workaround; see docstring of __expose_ca_in_ldap
-+                        self.step("exposing CA instance on LDAP",
-+                                  self.__expose_ca_in_ldap)
-+
-                     self.step("migrating certificate profiles to LDAP",
-                               migrate_profiles_to_ldap)
-                     self.step("importing IPA certificate profiles",
-@@ -1268,6 +1273,25 @@ class CAInstance(DogtagInstance):
-             config = []
-         self.ldap_configure('CA', self.fqdn, None, basedn, config)
- 
-+    def __expose_ca_in_ldap(self):
-+        """
-+        In a case when replica is created on DL0 we need to make
-+        sure that query for CA service record of this replica in
-+        ldap will succeed in time of installation.
-+        This method is needed for sucessfull replica installation
-+        on DL0 and should be removed alongside with code for DL0.
-+
-+        To suppress deprecation warning message this method is
-+        not invoking ldap_enable() but _ldap_enable() method.
-+        """
-+
-+        basedn = ipautil.realm_to_suffix(self.realm)
-+        if not self.clone:
-+            config = ['caRenewalMaster']
-+        else:
-+            config = []
-+        self._ldap_enable(u'enabledService', "CA", self.fqdn, basedn, config)
-+
-     def setup_lightweight_ca_key_retrieval(self):
-         if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'):
-             return
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index e78a2b992fbd44b8ee3ccd8183ebd6e13dfd1749..42c723b57699340d7dfa67f581ab7d4d4fdcf551 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -241,9 +241,12 @@ def create_ipa_conf(fstore, config, ca_enabled, master=None):
-         gopts.extend([
-             ipaconf.setOption('enable_ra', 'True'),
-             ipaconf.setOption('ra_plugin', 'dogtag'),
--            ipaconf.setOption('dogtag_version', '10'),
--            ipaconf.setOption('ca_host', config.ca_host_name)
-+            ipaconf.setOption('dogtag_version', '10')
-         ])
-+
-+        if not config.setup_ca:
-+            gopts.append(ipaconf.setOption('ca_host', config.ca_host_name))
-+
-     else:
-         gopts.extend([
-             ipaconf.setOption('enable_ra', 'False'),
--- 
-2.17.1
-
diff --git a/SOURCES/0035-ipa-cert-fix-add-man-page.patch b/SOURCES/0035-ipa-cert-fix-add-man-page.patch
new file mode 100644
index 0000000..c9cdcaf
--- /dev/null
+++ b/SOURCES/0035-ipa-cert-fix-add-man-page.patch
@@ -0,0 +1,114 @@
+From 03e3540e74e7b6da68987574d65668c07d484396 Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Mon, 25 Mar 2019 16:13:38 +1100
+Subject: [PATCH] ipa-cert-fix: add man page
+
+Part of: https://pagure.io/freeipa/issue/7885
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ freeipa.spec.in                  |  1 +
+ install/tools/man/Makefile.am    |  1 +
+ install/tools/man/ipa-cert-fix.1 | 66 ++++++++++++++++++++++++++++++++
+ 3 files changed, 68 insertions(+)
+ create mode 100644 install/tools/man/ipa-cert-fix.1
+
+diff --git a/freeipa.spec.in b/freeipa.spec.in
+index 775394619ab0eb682935c0d28fe434bcf8248a01..a18a5b4aab335ad104f1263fa3ae8b26659c3095 100644
+--- a/freeipa.spec.in
++++ b/freeipa.spec.in
+@@ -1450,6 +1450,7 @@ fi
+ %{_mandir}/man1/ipa-winsync-migrate.1*
+ %{_mandir}/man1/ipa-pkinit-manage.1*
+ %{_mandir}/man1/ipa-crlgen-manage.1*
++%{_mandir}/man1/ipa-cert-fix.1*
+ 
+ 
+ %files -n python2-ipaserver
+diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
+index 947e5c65f7d97734a320ee0a1979d7e890de6ed2..28fb57e87648d2a1a8904cc9d96921aa7e0f206e 100644
+--- a/install/tools/man/Makefile.am
++++ b/install/tools/man/Makefile.am
+@@ -29,6 +29,7 @@ dist_man1_MANS = 			\
+ 	ipa-winsync-migrate.1		\
+ 	ipa-pkinit-manage.1		\
+ 	ipa-crlgen-manage.1		\
++	ipa-cert-fix.1			\
+         $(NULL)
+ 
+ dist_man8_MANS =			\
+diff --git a/install/tools/man/ipa-cert-fix.1 b/install/tools/man/ipa-cert-fix.1
+new file mode 100644
+index 0000000000000000000000000000000000000000..3edef3118947d203d8972994d0d880850302a348
+--- /dev/null
++++ b/install/tools/man/ipa-cert-fix.1
+@@ -0,0 +1,66 @@
++.\"
++.\" Copyright (C) 2019  FreeIPA Contributors see COPYING for license
++.\"
++.TH "ipa-cert-fix" "1" "Mar 25 2019" "FreeIPA" "FreeIPA Manual Pages"
++.SH "NAME"
++ipa\-cert\-fix \- Renew expired certificates
++.SH "SYNOPSIS"
++ipa\-cert\-fix [options]
++.SH "DESCRIPTION"
++
++\fIipa-cert-fix\fR is a tool for recovery when expired certificates
++prevent the normal operation of FreeIPA.  It should ONLY be used in
++such scenarios, and backup of the system, especially certificates
++and keys, is \fBSTRONGLY RECOMMENDED\fR.
++
++Do not use this program unless expired certificates are inhibiting
++normal operation and renewal procedures.
++
++To renew the IPA CA certificate, use \fIipa-cacert-manage(1)\fR.
++
++This tool cannot renew certificates signed by external CAs.  To
++install new, externally-signed HTTP, LDAP or KDC certificates, use
++\fIipa-server-certinstall(1)\fR.
++
++\fIipa-cert-fix\fR will examine FreeIPA and Certificate System
++certificates and renew certificates that are expired, or close to
++expiry (less than two weeks).  If any "shared" certificates are
++renewed, \fIipa-cert-fix\fR will set the current server to be the CA
++renewal master, and add the new shared certificate(s) to LDAP for
++replication to other CA servers.  Shared certificates include all
++Dogtag system certificates except the HTTPS certificate, and the IPA
++RA certificate.
++
++To repair certificates across multiple CA servers, first ensure that
++LDAP replication is working across the topology.  Then run
++\fIipa-cert-fix\fR on one CA server.  Before running
++\fIipa-cert-fix\fR on another CA server, trigger Certmonger renewals
++for shared certificates via \fIgetcert-resubmit(1)\fR (on the other
++CA server).  This is to avoid unnecessary renewal of shared
++certificates.
++
++.SH "OPTIONS"
++.TP
++\fB\-\-version\fR
++Show the program's version and exit.
++.TP
++\fB\-h\fR, \fB\-\-help\fR
++Show the help for this program.
++.TP
++\fB\-v\fR, \fB\-\-verbose\fR
++Print debugging information.
++.TP
++\fB\-q\fR, \fB\-\-quiet\fR
++Output only errors (output from child processes may still be shown).
++.TP
++\fB\-\-log\-file\fR=\fIFILE\fR
++Log to the given file.
++.SH "EXIT STATUS"
++0 if the command was successful
++
++1 if an error occurred
++
++.SH "SEE ALSO"
++.BR ipa-cacert-manage(1)
++.BR ipa-server-certinstall(1)
++.BR getcert-resubmit(1)
+-- 
+2.20.1
+
diff --git a/SOURCES/0036-Fix-ipa-replica-install-when-key-not-protected-by-PI.patch b/SOURCES/0036-Fix-ipa-replica-install-when-key-not-protected-by-PI.patch
deleted file mode 100644
index 8e96414..0000000
--- a/SOURCES/0036-Fix-ipa-replica-install-when-key-not-protected-by-PI.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From 42bb4ee747a2f22db756bd037b2b0044853bb41d Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Tue, 21 Nov 2017 14:49:46 +0100
-Subject: [PATCH] Fix ipa-replica-install when key not protected by PIN
-
-When ipa-replica-install is called in a CA-less environment, the certs,
-keys and pins need to be provided with --{http|dirsrv|pkinit}-cert-file and
---{http|dirsrv|pkinit}-pin. If the pin is not provided in the CLI options,
-and in interactive mode, the installer prompts for the PIN.
-The issue happens when the keys are not protected by any PIN, the installer
-does not accept an empty string and keeps on asking for a PIN.
-
-The fix makes sure that the installer accepts an empty PIN. A similar fix
-was done for ipa-server-install in
-https://pagure.io/freeipa/c/4ee426a68ec60370eee6f5aec917ecce444840c7
-
-Fixes:
-https://pagure.io/freeipa/issue/7274
-
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/server/replicainstall.py | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 42c723b57699340d7dfa67f581ab7d4d4fdcf551..396d6089449225cc83aa28552a2009b9057e65ab 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -1057,7 +1057,7 @@ def promote_check(installer):
-         if options.http_pin is None:
-             options.http_pin = installutils.read_password(
-                 "Enter Apache Server private key unlock",
--                confirm=False, validate=False)
-+                confirm=False, validate=False, retry=False)
-             if options.http_pin is None:
-                 raise ScriptError(
-                     "Apache Server private key unlock password required")
-@@ -1073,7 +1073,7 @@ def promote_check(installer):
-         if options.dirsrv_pin is None:
-             options.dirsrv_pin = installutils.read_password(
-                 "Enter Directory Server private key unlock",
--                confirm=False, validate=False)
-+                confirm=False, validate=False, retry=False)
-             if options.dirsrv_pin is None:
-                 raise ScriptError(
-                     "Directory Server private key unlock password required")
-@@ -1089,7 +1089,7 @@ def promote_check(installer):
-         if options.pkinit_pin is None:
-             options.pkinit_pin = installutils.read_password(
-                 "Enter Kerberos KDC private key unlock",
--                confirm=False, validate=False)
-+                confirm=False, validate=False, retry=False)
-             if options.pkinit_pin is None:
-                 raise ScriptError(
-                     "Kerberos KDC private key unlock password required")
--- 
-2.17.1
-
diff --git a/SOURCES/0036-ipa-cert-fix-use-customary-exit-statuses.patch b/SOURCES/0036-ipa-cert-fix-use-customary-exit-statuses.patch
new file mode 100644
index 0000000..c09450c
--- /dev/null
+++ b/SOURCES/0036-ipa-cert-fix-use-customary-exit-statuses.patch
@@ -0,0 +1,37 @@
+From 8bbec6c72b759767bc19681aab9c3d3d9f091b6a Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Fri, 29 Mar 2019 16:04:20 +1100
+Subject: [PATCH] ipa-cert-fix: use customary exit statuses
+
+It is customary to return 2 when IPA is not configured, and 1 when
+other required bits are not installed or configured.  Update
+ipa-cert-fix exit statuses accordingly.
+
+Part of: https://pagure.io/freeipa/issue/7885
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ ipaserver/install/ipa_cert_fix.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
+index 3d9070eac1e7dc03840215dffeb4d73f4d3d0a47..c8ee51faea9092350c8a182ba55387ddd7b196d8 100644
+--- a/ipaserver/install/ipa_cert_fix.py
++++ b/ipaserver/install/ipa_cert_fix.py
+@@ -71,11 +71,11 @@ class IPACertFix(AdminTool):
+     def run(self):
+         if not is_ipa_configured():
+             print("IPA is not configured.")
+-            return 0  # not really an error
++            return 2
+ 
+         if not cainstance.is_ca_installed_locally():
+             print("CA is not installed on this server.")
+-            return 0  # not really an error
++            return 1
+ 
+         try:
+             ipautil.run(['pki-server', 'cert-fix', '--help'], raiseonerr=True)
+-- 
+2.20.1
+
diff --git a/SOURCES/0037-Clear-next-field-when-returnining-list-elements-in-q.patch b/SOURCES/0037-Clear-next-field-when-returnining-list-elements-in-q.patch
deleted file mode 100644
index 73bbaf0..0000000
--- a/SOURCES/0037-Clear-next-field-when-returnining-list-elements-in-q.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From a099794ab890979dbd9fb567c44fcb105da229ff Mon Sep 17 00:00:00 2001
-From: Robbie Harwood <rharwood@redhat.com>
-Date: Wed, 22 Aug 2018 15:32:16 -0400
-Subject: [PATCH] Clear next field when returnining list elements in queue.c
-
-The ipa-otpd code occasionally removes elements from one queue,
-inspects and modifies them, and then inserts them into
-another (possibly identical, possibly different) queue.  When the next
-pointer isn't cleared, this can result in element membership in both
-queues, leading to double frees, or even self-referential elements,
-causing infinite loops at traversal time.
-
-Rather than eliminating the pattern, make it safe by clearing the next
-field any time an element enters or exits a queue.
-
-Related https://pagure.io/freeipa/issue/7262
-
-Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
----
- daemons/ipa-otpd/queue.c | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/daemons/ipa-otpd/queue.c b/daemons/ipa-otpd/queue.c
-index 9e29fb238d5c7a7395bcf3860ce7445c27ca98ac..2944b7ea0db6f49d0a3230b5f33c7a89281fd8c6 100644
---- a/daemons/ipa-otpd/queue.c
-+++ b/daemons/ipa-otpd/queue.c
-@@ -111,6 +111,8 @@ void otpd_queue_push(struct otpd_queue *q, struct otpd_queue_item *item)
-         q->head = q->tail = item;
-     else
-         q->tail = q->tail->next = item;
-+
-+    item->next = NULL;
- }
- 
- void otpd_queue_push_head(struct otpd_queue *q, struct otpd_queue_item *item)
-@@ -118,6 +120,8 @@ void otpd_queue_push_head(struct otpd_queue *q, struct otpd_queue_item *item)
-     if (item == NULL)
-         return;
- 
-+    item->next = NULL;
-+
-     if (q->head == NULL)
-         q->tail = q->head = item;
-     else {
-@@ -145,6 +149,8 @@ struct otpd_queue_item *otpd_queue_pop(struct otpd_queue *q)
-     if (q->head == NULL)
-         q->tail = NULL;
- 
-+    if (item != NULL)
-+        item->next = NULL;
-     return item;
- }
- 
-@@ -160,6 +166,7 @@ struct otpd_queue_item *otpd_queue_pop_msgid(struct otpd_queue *q, int msgid)
-             *prev = item->next;
-             if (q->head == NULL)
-                 q->tail = NULL;
-+            item->next = NULL;
-             return item;
-         }
-     }
--- 
-2.17.1
-
diff --git a/SOURCES/0037-Show-a-notification-that-sssd-needs-restarting-after.patch b/SOURCES/0037-Show-a-notification-that-sssd-needs-restarting-after.patch
new file mode 100644
index 0000000..aaa3115
--- /dev/null
+++ b/SOURCES/0037-Show-a-notification-that-sssd-needs-restarting-after.patch
@@ -0,0 +1,117 @@
+From 3c2237e481fdb2241494bd285931803335ed44dd Mon Sep 17 00:00:00 2001
+From: Oleg Kozlov <xxblx@posteo.org>
+Date: Fri, 29 Mar 2019 14:35:02 +0100
+Subject: [PATCH] Show a notification that sssd needs restarting after
+ idrange-mod
+
+If the `ipa idrange-mod` command has been used show a notification that sssd.service needs restarting. It's needed for applying changes. E.g. after setup AD trust with a domain with more than 200000 objects (the highest RID > idm's default value, 200000) users with RIDs > 200000 are not able to login, the size needs to be increased via idrange-mod, but it makes an effect only after sssd restarting.
+
+Implementation:
+Notification was implemented via passing `ipalib.messages.ServiceRestartRequired` to `add_message` method in `ipaserver.plugins.idrange.idrange_mod.post_callback`.
+
+Tests:
+Added `messages` with sssd restart required (`ipalib.messages.ServiceRestartRequired`) to cases with idrange_mod where output is expected in `ipatests.test_xmlrpc.test_range_plugin.test_range'.
+
+Fixes: https://pagure.io/freeipa/issue/7708
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipaplatform/base/services.py              |  3 ++-
+ ipaserver/plugins/idrange.py              | 11 ++++++++++-
+ ipatests/test_xmlrpc/test_range_plugin.py | 17 ++++++++++++++++-
+ 3 files changed, 28 insertions(+), 3 deletions(-)
+
+diff --git a/ipaplatform/base/services.py b/ipaplatform/base/services.py
+index 9fd2a8532837c631945fa837be03c80fa42128bf..b037718f22fc36c4a01b3b0962b3f9e7a69f4b7e 100644
+--- a/ipaplatform/base/services.py
++++ b/ipaplatform/base/services.py
+@@ -53,7 +53,8 @@ wellknownservices = ['certmonger', 'dirsrv', 'httpd', 'ipa', 'krb5kdc',
+                      'messagebus', 'nslcd', 'nscd', 'ntpd', 'portmap',
+                      'rpcbind', 'kadmin', 'sshd', 'autofs', 'rpcgssd',
+                      'rpcidmapd', 'pki_tomcatd', 'chronyd', 'domainname',
+-                     'named', 'ods_enforcerd', 'ods_signerd', 'gssproxy']
++                     'named', 'ods_enforcerd', 'ods_signerd', 'gssproxy',
++                     'sssd']
+ 
+ # The common ports for these services. This is used to wait for the
+ # service to become available.
+diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py
+index ea3d1ff5662800957cde99934e9d2577e6b13ea6..364f128de4a04f484bde0def3c782e1a48be1daa 100644
+--- a/ipaserver/plugins/idrange.py
++++ b/ipaserver/plugins/idrange.py
+@@ -17,13 +17,16 @@
+ # You should have received a copy of the GNU General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ 
++from __future__ import absolute_import
++
+ import six
+ 
+ from ipalib.plugable import Registry
+ from .baseldap import (LDAPObject, LDAPCreate, LDAPDelete,
+                                      LDAPRetrieve, LDAPSearch, LDAPUpdate)
+-from ipalib import api, Int, Str, StrEnum, _, ngettext
++from ipalib import api, Int, Str, StrEnum, _, ngettext, messages
+ from ipalib import errors
++from ipaplatform import services
+ from ipapython.dn import DN
+ 
+ if six.PY3:
+@@ -768,4 +771,10 @@ class idrange_mod(LDAPUpdate):
+         assert isinstance(dn, DN)
+         self.obj.handle_ipabaserid(entry_attrs, options)
+         self.obj.handle_iparangetype(entry_attrs, options)
++        self.add_message(
++            messages.ServiceRestartRequired(
++                service=services.knownservices['sssd'].systemd_name,
++                server=keys[0]
++            )
++        )
+         return dn
+diff --git a/ipatests/test_xmlrpc/test_range_plugin.py b/ipatests/test_xmlrpc/test_range_plugin.py
+index 0a8f66b62dac9ea2e4692f0f64b1c8fbc9272fd2..2adc57ed5f5d23fe5f457ab3a5e23462f24a62fe 100644
+--- a/ipatests/test_xmlrpc/test_range_plugin.py
++++ b/ipatests/test_xmlrpc/test_range_plugin.py
+@@ -21,9 +21,12 @@
+ Test the `ipaserver/plugins/idrange.py` module, and XML-RPC in general.
+ """
+ 
++from __future__ import absolute_import
++
+ import six
+ 
+-from ipalib import api, errors
++from ipalib import api, errors, messages
++from ipaplatform import services
+ from ipatests.test_xmlrpc.xmlrpc_test import Declarative, fuzzy_uuid
+ from ipatests.test_xmlrpc import objectclasses
+ from ipatests.util import MockLDAP
+@@ -786,6 +789,12 @@ class test_range(Declarative):
+             command=('idrange_mod', [domain3range2],
+                      dict(ipabaseid=domain3range1_base_id)),
+             expected=dict(
++                messages=(
++                    messages.ServiceRestartRequired(
++                        service=services.knownservices['sssd'].systemd_name,
++                        server=domain3range2
++                    ).to_dict(),
++                ),
+                 result=dict(
+                     cn=[domain3range2],
+                     ipabaseid=[unicode(domain3range1_base_id)],
+@@ -851,6 +860,12 @@ class test_range(Declarative):
+             command=('idrange_mod', [domain2range1],
+                      dict(ipabaserid=domain5range1_base_rid)),
+             expected=dict(
++                messages=(
++                    messages.ServiceRestartRequired(
++                        service=services.knownservices['sssd'].systemd_name,
++                        server=domain2range1
++                    ).to_dict(),
++                ),
+                 result=dict(
+                     cn=[domain2range1],
+                     ipabaseid=[unicode(domain2range1_base_id)],
+-- 
+2.20.1
+
diff --git a/SOURCES/0038-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch b/SOURCES/0038-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch
deleted file mode 100644
index a1380c7..0000000
--- a/SOURCES/0038-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch
+++ /dev/null
@@ -1,253 +0,0 @@
-From efd0a988a7431e3f31f5dde78f66eaec8c7f7f37 Mon Sep 17 00:00:00 2001
-From: Robbie Harwood <rharwood@redhat.com>
-Date: Thu, 30 Aug 2018 15:34:31 -0400
-Subject: [PATCH] Add cmocka unit tests for ipa otpd queue code
-
-Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
----
- daemons/ipa-otpd/Makefile.am                  |  12 +
- .../ipa-otpd/ipa_otpd_queue_cmocka_tests.c    | 212 ++++++++++++++++++
- 2 files changed, 224 insertions(+)
- create mode 100644 daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c
-
-diff --git a/daemons/ipa-otpd/Makefile.am b/daemons/ipa-otpd/Makefile.am
-index 923e16ebe127a36cea372d656dd913c4ffa09313..c1d6c2053354a59b1fd897f9a67cc7507eed5294 100644
---- a/daemons/ipa-otpd/Makefile.am
-+++ b/daemons/ipa-otpd/Makefile.am
-@@ -21,3 +21,15 @@ ipa_otpd_SOURCES = bind.c forward.c main.c parse.c query.c queue.c stdio.c
- 	     $< > $@
- 
- CLEANFILES = $(systemdsystemunit_DATA)
-+
-+TESTS =
-+check_PROGRAMS =
-+
-+if HAVE_CMOCKA
-+TESTS += queue_tests
-+check_PROGRAMS += queue_tests
-+endif
-+
-+queue_tests_SOURCES = ipa_otpd_queue_cmocka_tests.c queue.c
-+queue_tests_CFLAGS = $(CMOCKA_CFLAGS)
-+queue_tests_LDADD = $(CMOCKA_LIBS)
-diff --git a/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c b/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..068431e6475bb74b01acbcab22115915dec1a278
---- /dev/null
-+++ b/daemons/ipa-otpd/ipa_otpd_queue_cmocka_tests.c
-@@ -0,0 +1,212 @@
-+/*
-+ * FreeIPA 2FA companion daemon - internal queue tests
-+ *
-+ * Author: Robbie Harwood <rharwood@redhat.com>
-+ *
-+ * Copyright (C) 2018  Robbie Harwood, Red Hat
-+ * see file 'COPYING' for use and warranty information
-+ *
-+ * This program is free software you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the Free
-+ * Software Foundation, either version 3 of the License, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful, but WITHOUT
-+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-+ * more details.
-+ *
-+ * You should have received a copy of the GNU General Public License along
-+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+#include <setjmp.h>
-+#include <stdarg.h>
-+#include <stddef.h>
-+
-+#include <cmocka.h>
-+
-+#include "internal.h"
-+
-+/* Bypass otpd queue allocation/freeing to avoid calling into LDAP and
-+ * krad.  No effort is made to make the types match. */
-+static struct otpd_queue_item *new_elt(int id)
-+{
-+    krb5_error_code ret;
-+    struct otpd_queue_item *e = NULL;
-+
-+    ret = otpd_queue_item_new(NULL, &e);
-+    assert_int_equal(ret, 0);
-+    assert_ptr_not_equal(e, NULL);
-+
-+    e->msgid = id;
-+    return e;
-+}
-+static void free_elt(struct otpd_queue_item **e)
-+{
-+    assert_ptr_not_equal(e, NULL);
-+    free(*e);
-+    *e = NULL;
-+}
-+static void free_elts(struct otpd_queue *q)
-+{
-+    assert_ptr_not_equal(q, NULL);
-+    for (struct otpd_queue_item *e = otpd_queue_pop(q); e != NULL;
-+         e = otpd_queue_pop(q))
-+        free_elt(&e);
-+}
-+#define otpd_queue_item_new new_elt
-+#define otpd_queue_item_free free_elt
-+#define otpd_queue_free_items free_elts
-+
-+static void assert_elt_equal(struct otpd_queue_item *e1,
-+                             struct otpd_queue_item *e2)
-+{
-+    if (e1 == NULL && e2 == NULL)
-+        return;
-+    assert_ptr_not_equal(e1, NULL);
-+    assert_ptr_not_equal(e2, NULL);
-+    assert_int_equal(e1->msgid, e2->msgid);
-+}
-+
-+static void test_single_insert()
-+{
-+    struct otpd_queue q = { NULL };
-+    struct otpd_queue_item *ein, *eout;
-+
-+    ein = new_elt(0);
-+    otpd_queue_push(&q, ein);
-+
-+    eout = otpd_queue_peek(&q);
-+    assert_elt_equal(ein, eout);
-+
-+    eout = otpd_queue_pop(&q);
-+    assert_elt_equal(ein, eout);
-+    free_elt(&eout);
-+
-+    eout = otpd_queue_pop(&q);
-+    assert_ptr_equal(eout, NULL);
-+
-+    free_elts(&q);
-+}
-+
-+static void test_jump_insert()
-+{
-+    struct otpd_queue q = { NULL };
-+    struct otpd_queue_item *echeck;
-+
-+    for (int i = 0; i < 3; i++) {
-+        struct otpd_queue_item *e = new_elt(i);
-+        otpd_queue_push_head(&q, e);
-+
-+        echeck = otpd_queue_peek(&q);
-+        assert_elt_equal(e, echeck);
-+    }
-+
-+    free_elts(&q);
-+}
-+
-+static void test_garbage_insert()
-+{
-+    struct otpd_queue q = { NULL };
-+    struct otpd_queue_item *e, *g;
-+
-+    g = new_elt(0);
-+    g->next = g;
-+    otpd_queue_push(&q, g);
-+
-+    e = otpd_queue_peek(&q);
-+    assert_ptr_equal(e->next, NULL);
-+
-+    free_elts(&q);
-+}
-+
-+static void test_removal()
-+{
-+    struct otpd_queue q = { NULL };
-+
-+    for (int i = 0; i < 3; i++) {
-+        struct otpd_queue_item *e = new_elt(i);
-+        otpd_queue_push(&q, e);
-+    }
-+    for (int i = 0; i < 3; i++) {
-+        struct otpd_queue_item *e = otpd_queue_pop(&q);
-+        assert_ptr_not_equal(e, NULL);
-+        assert_ptr_equal(e->next, NULL);
-+        assert_int_equal(e->msgid, i);
-+        free_elt(&e);
-+    }
-+}
-+
-+static void pick_id(struct otpd_queue *q, int msgid)
-+{
-+    struct otpd_queue_item *e;
-+
-+    e = otpd_queue_pop_msgid(q, msgid);
-+    assert_int_equal(e->msgid, msgid);
-+    assert_ptr_equal(e->next, NULL);
-+    free_elt(&e);
-+    e = otpd_queue_pop_msgid(q, msgid);
-+    assert_ptr_equal(e, NULL);
-+}
-+static void test_pick_removal()
-+{
-+    struct otpd_queue q = { NULL };
-+
-+    for (int i = 0; i < 4; i++) {
-+        struct otpd_queue_item *e = new_elt(i);
-+        otpd_queue_push(&q, e);
-+    }
-+
-+    pick_id(&q, 0); /* first */
-+    pick_id(&q, 2); /* middle */
-+    pick_id(&q, 3); /* last */
-+    pick_id(&q, 1); /* singleton */
-+
-+    free_elts(&q);
-+}
-+
-+static void test_iter()
-+{
-+    krb5_error_code ret;
-+    struct otpd_queue q = { NULL };
-+    const struct otpd_queue *queues[3];
-+    struct otpd_queue_iter *iter = NULL;
-+    const krad_packet *p = NULL;
-+
-+    for (ptrdiff_t i = 1; i <= 3; i++) {
-+        struct otpd_queue_item *e = new_elt(i);
-+        e->req = (void *)i;
-+        otpd_queue_push(&q, e);
-+    }
-+
-+    queues[0] = &q;
-+    queues[1] = &q;
-+    queues[2] = NULL;
-+    ret = otpd_queue_iter_new(queues, &iter);
-+    assert_int_equal(ret, 0);
-+    assert_ptr_not_equal(iter, NULL);
-+
-+    for (ptrdiff_t i = 0; i < 6; i++) {
-+        p = otpd_queue_iter_func(iter, FALSE);
-+        assert_ptr_equal(p, (void *) (i % 3 + 1));
-+    }
-+    p = otpd_queue_iter_func(iter, FALSE);
-+    assert_ptr_equal(p, NULL);
-+
-+    free_elts(&q);
-+}
-+
-+int main(int argc, char *argv[])
-+{
-+    const struct CMUnitTest tests[] = {
-+        cmocka_unit_test(test_single_insert),
-+        cmocka_unit_test(test_jump_insert),
-+        cmocka_unit_test(test_garbage_insert),
-+        cmocka_unit_test(test_removal),
-+        cmocka_unit_test(test_pick_removal),
-+        cmocka_unit_test(test_iter),
-+    };
-+
-+    return cmocka_run_group_tests(tests, NULL, NULL);
-+}
--- 
-2.17.1
-
diff --git a/SOURCES/0038-ipa-server-upgrade-fix-add_systemd_user_hbac.patch b/SOURCES/0038-ipa-server-upgrade-fix-add_systemd_user_hbac.patch
new file mode 100644
index 0000000..cd57fe6
--- /dev/null
+++ b/SOURCES/0038-ipa-server-upgrade-fix-add_systemd_user_hbac.patch
@@ -0,0 +1,53 @@
+From e3c8a9b78f01e268a907bc97ad2914bf0d1236fc Mon Sep 17 00:00:00 2001
+From: Florence Blanc-Renaud <flo@redhat.com>
+Date: Mon, 1 Apr 2019 11:10:26 +0200
+Subject: [PATCH] ipa-server-upgrade: fix add_systemd_user_hbac
+
+During upgrade, the method add_systemd_user_hbac is creating
+a hbacsvc and a hbacrule, but fails in python2 because of
+unicode conversion errors.
+The arguments should be defined as u'value'.
+
+Fixes: https://pagure.io/freeipa/issue/7896
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipaserver/install/server/upgrade.py | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
+index f4389d37909fc0b5aed960638de67243906b634d..ba0dfa423a3fc9560ef3c81c1d98cad39c320575 100644
+--- a/ipaserver/install/server/upgrade.py
++++ b/ipaserver/install/server/upgrade.py
+@@ -1689,12 +1689,12 @@ def update_replica_config(db_suffix):
+ 
+ def add_systemd_user_hbac():
+     logger.info('[Create systemd-user hbac service and rule]')
+-    rule = 'allow_systemd-user'
+-    service = 'systemd-user'
++    rule = u'allow_systemd-user'
++    service = u'systemd-user'
+     try:
+         api.Command.hbacsvc_add(
+             service,
+-            description='pam_systemd and systemd user@.service'
++            description=u'pam_systemd and systemd user@.service'
+         )
+     except ipalib.errors.DuplicateEntry:
+         logger.info('hbac service %s already exists', service)
+@@ -1707,10 +1707,10 @@ def add_systemd_user_hbac():
+     try:
+         api.Command.hbacrule_add(
+             rule,
+-            description=('Allow pam_systemd to run user@.service to create '
++            description=(u'Allow pam_systemd to run user@.service to create '
+                          'a system user session'),
+-            usercategory='all',
+-            hostcategory='all',
++            usercategory=u'all',
++            hostcategory=u'all',
+         )
+     except ipalib.errors.DuplicateEntry:
+         logger.info('hbac rule %s already exists', rule)
+-- 
+2.20.1
+
diff --git a/SOURCES/0039-ipa-server-install-do-not-perform-forwarder-validati.patch b/SOURCES/0039-ipa-server-install-do-not-perform-forwarder-validati.patch
deleted file mode 100644
index 37eef97..0000000
--- a/SOURCES/0039-ipa-server-install-do-not-perform-forwarder-validati.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From f387cbe6f9d1e21cb46c7b9752735bf6ded176b7 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Fri, 31 Aug 2018 10:09:15 +0200
-Subject: [PATCH] ipa-server-install: do not perform forwarder validation with
- --no-dnssec-validation
-
-ipa-server-install is checking if the forwarder(s) specified with
---forwarder argument support DNSSEC. When the --no-dnssec-validation
-option is added, the installer should not perform the check.
-
-Fixes: https://pagure.io/freeipa/issue/7666
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
----
- ipaserver/install/dns.py | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
-index cac7a9213796d6618854b12da6c2a7fe60afdbf9..e4f73ac025dfe8aa19ef99c8d0ab9379caa32610 100644
---- a/ipaserver/install/dns.py
-+++ b/ipaserver/install/dns.py
-@@ -293,8 +293,8 @@ def install_check(standalone, api, replica, options, hostname):
- 
-     # test DNSSEC forwarders
-     if options.forwarders:
--        if (not bindinstance.check_forwarders(options.forwarders)
--                and not options.no_dnssec_validation):
-+        if not options.no_dnssec_validation \
-+                and not bindinstance.check_forwarders(options.forwarders):
-             options.no_dnssec_validation = True
-             print("WARNING: DNSSEC validation will be disabled")
- 
--- 
-2.17.1
-
diff --git a/SOURCES/0039-ipa-setup-kra-fix-python2-parameter.patch b/SOURCES/0039-ipa-setup-kra-fix-python2-parameter.patch
new file mode 100644
index 0000000..4f31e3a
--- /dev/null
+++ b/SOURCES/0039-ipa-setup-kra-fix-python2-parameter.patch
@@ -0,0 +1,35 @@
+From 031b79d7111d6deaab05a8682014504b328614d7 Mon Sep 17 00:00:00 2001
+From: Florence Blanc-Renaud <flo@redhat.com>
+Date: Mon, 1 Apr 2019 11:35:48 +0200
+Subject: [PATCH] ipa-setup-kra: fix python2 parameter
+
+ipa-setup-kra is failing in python2 with
+invalid 'role_servrole': must be Unicode text
+because of a unicode conversion error.
+
+The method api.Command.server_role_find is called with the parameter
+role_servrole='IPA master' but it should rather be
+role_servrole=u'IPA master'
+
+Fixes: https://pagure.io/freeipa/issue/7897
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipaserver/install/service.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
+index 8948f64c2ec2db4cd013699e07dd94d5dba6c043..a60cb7f63dc0673deae0839de8c2bb8dc3a905c8 100644
+--- a/ipaserver/install/service.py
++++ b/ipaserver/install/service.py
+@@ -213,7 +213,7 @@ def sync_services_state(fqdn):
+     """
+     result = api.Command.server_role_find(
+         server_server=fqdn,
+-        role_servrole='IPA master',
++        role_servrole=u'IPA master',
+         status=HIDDEN
+     )
+     if result['count']:
+-- 
+2.20.1
+
diff --git a/SOURCES/0040-ipa-replica-install-fix-pkinit-setup.patch b/SOURCES/0040-ipa-replica-install-fix-pkinit-setup.patch
deleted file mode 100644
index 8ef6481..0000000
--- a/SOURCES/0040-ipa-replica-install-fix-pkinit-setup.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 7fe3cba3d4cbe62c23e0e74f6bf3c44c50feb985 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Tue, 4 Sep 2018 14:15:50 +0200
-Subject: [PATCH] ipa-replica-install: fix pkinit setup
-
-commit 7284097 (Delay enabling services until end of installer)
-introduced a regression in replica installation.
-When the replica requests a cert for PKINIT, a check is done
-to ensure that the hostname corresponds to a machine with a
-KDC service enabled (ipaconfigstring attribute of
-cn=KDC,cn=<hostname>,cn=masters,cn=ipa,cn=etc,$BASEDN must contain
-'enabledService').
-With the commit mentioned above, the service is set to enabled only
-at the end of the installation.
-
-The fix makes a less strict check, ensuring that 'enabledService'
-or 'configuredService' is in ipaconfigstring.
-
-Fixes: https://pagure.io/freeipa/issue/7566
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/plugins/cert.py | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
-index 0663272c9b4fd73225f63fe52d8d31157d5cc690..ed78388c8b8b4688873a5b047fb1b67e417a8a6d 100644
---- a/ipaserver/plugins/cert.py
-+++ b/ipaserver/plugins/cert.py
-@@ -300,9 +300,11 @@ def ca_kdc_check(api_instance, hostname):
- 
-         ipaconfigstring = {val.lower() for val in kdc_entry['ipaConfigString']}
- 
--        if 'enabledservice' not in ipaconfigstring:
-+        if 'enabledservice' not in ipaconfigstring \
-+                and 'configuredservice' not in ipaconfigstring:
-             raise errors.NotFound(
--                reason=_("enabledService not in ipaConfigString kdc entry"))
-+                reason=_("enabledService/configuredService not in "
-+                         "ipaConfigString kdc entry"))
- 
-     except errors.NotFound:
-         raise errors.ACIError(
--- 
-2.17.1
-
diff --git a/SOURCES/0040-oddjob-allow-to-pass-options-to-trust-fetch-domains.patch b/SOURCES/0040-oddjob-allow-to-pass-options-to-trust-fetch-domains.patch
new file mode 100644
index 0000000..461a3af
--- /dev/null
+++ b/SOURCES/0040-oddjob-allow-to-pass-options-to-trust-fetch-domains.patch
@@ -0,0 +1,286 @@
+From 12a079a0b599cf192c4bee8bcc37f1824f82539e Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Sun, 31 Mar 2019 10:39:05 +0300
+Subject: [PATCH] oddjob: allow to pass options to trust-fetch-domains
+
+Refactor com.redhat.idm.trust-fetch.domains oddjob helper to allow
+passing administrative credentials and a domain controller to talk to.
+
+This approach allows to avoid rediscovering a domain controller in case
+a user actually specified the domain controller when establishing trust.
+
+It also allows to pass through admin credentials if user decides to do
+so. The latter will be used later to allow updating trust topology in a
+similar oddjob helper.
+
+Resolves: https://pagure.io/freeipa/issue/7895
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+(cherry picked from commit de4a9875d410c68ae4f9602b70c753a11034b31b)
+---
+ API.txt                                       |  4 +-
+ VERSION.m4                                    |  4 +-
+ .../oddjob/com.redhat.idm.trust-fetch-domains | 91 +++++++++++--------
+ .../etc/oddjobd.conf.d/oddjobd-ipa-trust.conf |  2 +-
+ ipaserver/plugins/trust.py                    | 31 ++++++-
+ 5 files changed, 83 insertions(+), 49 deletions(-)
+
+diff --git a/API.txt b/API.txt
+index 222e30915ccc1fb4a6f3ce228669453f346fdde4..325df7a6ef48afbc438bcc1f7b8ca64efb229886 100644
+--- a/API.txt
++++ b/API.txt
+@@ -5765,10 +5765,12 @@ output: Output('result', type=[<type 'dict'>])
+ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
+ output: ListOfPrimaryKeys('value')
+ command: trust_fetch_domains/1
+-args: 1,5,4
++args: 1,7,4
+ arg: Str('cn', cli_name='realm')
+ option: Flag('all', autofill=True, cli_name='all', default=False)
+ option: Flag('raw', autofill=True, cli_name='raw', default=False)
++option: Str('realm_admin?', cli_name='admin')
++option: Password('realm_passwd?', cli_name='password', confirm=False)
+ option: Str('realm_server?', cli_name='server')
+ option: Flag('rights', autofill=True, default=False)
+ option: Str('version?')
+diff --git a/VERSION.m4 b/VERSION.m4
+index b1425626ca00ffbcc902c66685cb27cbb2136539..da391d8bd4dad6ae8e7418e6af54a9d481133cc8 100644
+--- a/VERSION.m4
++++ b/VERSION.m4
+@@ -82,8 +82,8 @@ define(IPA_DATA_VERSION, 20100614120000)
+ #                                                      #
+ ########################################################
+ define(IPA_API_VERSION_MAJOR, 2)
+-define(IPA_API_VERSION_MINOR, 230)
+-# Last change: Added `automember-find-orphans' command
++define(IPA_API_VERSION_MINOR, 231)
++# Last change: Added admin creds to trust-fetch-domains
+ 
+ 
+ ########################################################
+diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
+index 30150793d98011d153081477c0856618e1454ba5..1e90759e0e6e7be36745ec12f4478bfec0f0358e 100755
+--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
++++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
+@@ -14,11 +14,31 @@ import pwd
+ import six
+ import gssapi
+ 
+-from ipalib.install.kinit import kinit_keytab
++from ipalib.install.kinit import kinit_keytab, kinit_password
+ 
+ if six.PY3:
+     unicode = str
+ 
++
++def parse_options():
++    usage = "%prog <trusted domain name>\n"
++    parser = config.IPAOptionParser(usage=usage,
++                                    formatter=config.IPAFormatter())
++
++    parser.add_option("-d", "--debug", action="store_true", dest="debug",
++                      help="Display debugging information")
++    parser.add_option("-s", "--server", action="store", dest="server",
++                      help="Domain controller for the Active Directory domain (optional)")
++    parser.add_option("-a", "--admin", action="store", dest="admin",
++                      help="Active Directory administrator (optional)")
++    parser.add_option("-p", "--password", action="store", dest="password",
++                      help="Display debugging information")
++
++    options, args = parser.parse_args()
++    safe_options = parser.get_safe_opts(options)
++
++    return safe_options, options, args
++
+ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
+     getkeytab_args = ["/usr/sbin/ipa-getkeytab",
+                       "-s", api.env.host,
+@@ -40,7 +60,7 @@ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
+         pass
+ 
+ 
+-def get_forest_root_domain(api_instance, trusted_domain):
++def get_forest_root_domain(api_instance, trusted_domain, server=None):
+     """
+     retrieve trusted forest root domain for given domain name
+ 
+@@ -53,25 +73,11 @@ def get_forest_root_domain(api_instance, trusted_domain):
+     flatname = trustconfig_show()['result']['ipantflatname'][0]
+ 
+     remote_domain = dcerpc.retrieve_remote_domain(
+-        api_instance.env.host, flatname, trusted_domain)
++        api_instance.env.host, flatname, trusted_domain,
++        realm_server=server)
+ 
+     return remote_domain.info['dns_forest']
+ 
+-
+-def parse_options():
+-    usage = "%prog <trusted domain name>\n"
+-    parser = config.IPAOptionParser(usage=usage,
+-                                    formatter=config.IPAFormatter())
+-
+-    parser.add_option("-d", "--debug", action="store_true", dest="debug",
+-                      help="Display debugging information")
+-
+-    options, args = parser.parse_args()
+-    safe_options = parser.get_safe_opts(options)
+-
+-    return safe_options, options, args
+-
+-
+ if not is_ipa_configured():
+     # LSB status code 6: program is not configured
+     raise ScriptError("IPA is not configured " +
+@@ -153,32 +159,37 @@ trusted_domain = trusted_domain_entry.single_value.get('cn').lower()
+ # At this point if we didn't find trusted forest name, an exception will be raised
+ # and script will quit. This is actually intended.
+ 
+-oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
+-oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
++if not (options.admin and options.password):
++    oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
++    oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
+ 
+-# If keytab does not exist, retrieve it
+-if not os.path.isfile(oneway_keytab_name):
+-    retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
++    # If keytab does not exist, retrieve it
++    if not os.path.isfile(oneway_keytab_name):
++        retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
+ 
+-try:
+-    have_ccache = False
+     try:
+-        # The keytab may have stale key material (from older trust-add run)
+-        cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
+-        if cred.lifetime > 0:
+-            have_ccache = True
+-    except gssapi.exceptions.ExpiredCredentialsError:
+-        pass
+-    if not have_ccache:
++        have_ccache = False
++        try:
++            # The keytab may have stale key material (from older trust-add run)
++            cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
++            if cred.lifetime > 0:
++                have_ccache = True
++        except gssapi.exceptions.ExpiredCredentialsError:
++            pass
++        if not have_ccache:
++            if os.path.exists(oneway_ccache_name):
++                os.unlink(oneway_ccache_name)
++            kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
++    except gssapi.exceptions.GSSError:
++        # If there was failure on using keytab, assume it is stale and retrieve again
++        retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
+         if os.path.exists(oneway_ccache_name):
+             os.unlink(oneway_ccache_name)
+         kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
+-except gssapi.exceptions.GSSError:
+-    # If there was failure on using keytab, assume it is stale and retrieve again
+-    retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
+-    if os.path.exists(oneway_ccache_name):
+-        os.unlink(oneway_ccache_name)
+-    kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
++else:
++    cred = kinit_password(options.admin, options.password,
++                          oneway_ccache_name,
++                          canonicalize=True, enterprise=True)
+ 
+ # We are done: we have ccache with TDO credentials and can fetch domains
+ ipa_domain = api.env.domain
+@@ -186,9 +197,9 @@ os.environ['KRB5CCNAME'] = oneway_ccache_name
+ 
+ # retrieve the forest root domain name and contact it to retrieve trust
+ # topology info
+-forest_root = get_forest_root_domain(api, trusted_domain)
++forest_root = get_forest_root_domain(api, trusted_domain, server=options.server)
+ 
+-domains = dcerpc.fetch_domains(api, ipa_domain, forest_root, creds=True)
++domains = dcerpc.fetch_domains(api, ipa_domain, forest_root, creds=True, server=options.server)
+ trust_domain_object = api.Command.trust_show(trusted_domain, raw=True)['result']
+ trust.add_new_domains_from_trust(api, None, trust_domain_object, domains)
+ 
+diff --git a/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf b/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf
+index 630a4e6cd4c0d3ed9ce48bf9b882b7d630f6bbf3..9f3f168a51abfdd1cb7cb8b76221bb4ec806859c 100644
+--- a/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf
++++ b/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf
+@@ -11,7 +11,7 @@
+       <interface name="com.redhat.idm.trust">
+         <method name="fetch_domains">
+           <helper exec="/usr/libexec/ipa/oddjob/com.redhat.idm.trust-fetch-domains"
+-		  arguments="1"
++		  arguments="30"
+                   argument_passing_method="cmdline"
+ 		  prepend_user_name="no"/>
+         </method>
+diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
+index 91cd1387ce058f34c2319676767d9e50f9b46ed1..bb9e0fe3303989c49cf339df6e5aeeb4ce1435cf 100644
+--- a/ipaserver/plugins/trust.py
++++ b/ipaserver/plugins/trust.py
+@@ -418,9 +418,19 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options):
+     return range_type, range_size, base_id
+ 
+ 
+-def fetch_trusted_domains_over_dbus(myapi, forest_name):
++def fetch_trusted_domains_over_dbus(myapi, *keys, **options):
+     if not _bindings_installed:
+         return
++
++    forest_name = keys[0]
++    method_options = []
++    if 'realm_server' in options:
++        method_options.extend(['--server', options['realm_server']])
++    if 'realm_admin' in options:
++        method_options.extend(['--admin', options['realm_admin']])
++    if 'realm_passwd' in options:
++        method_options.extend(['--password', options['realm_passwd']])
++
+     # Calling oddjobd-activated service via DBus has some quirks:
+     # - Oddjobd registers multiple canonical names on the same address
+     # - python-dbus only follows name owner changes when mainloop is in use
+@@ -436,7 +446,8 @@ def fetch_trusted_domains_over_dbus(myapi, forest_name):
+         fetch_domains_method = intf.get_dbus_method(
+                 'fetch_domains',
+                 dbus_interface=DBUS_IFACE_TRUST)
+-        (_ret, _stdout, _stderr) = fetch_domains_method(forest_name)
++        (_ret, _stdout, _stderr) = fetch_domains_method(
++            [forest_name] + method_options)
+     except dbus.DBusException as e:
+         logger.error('Failed to call %s.fetch_domains helper.'
+                      'DBus exception is %s.', DBUS_IFACE_TRUST, str(e))
+@@ -1760,10 +1771,20 @@ class trust_fetch_domains(LDAPRetrieve):
+ 
+     has_output = output.standard_list_of_entries
+     takes_options = LDAPRetrieve.takes_options + (
++        Str('realm_admin?',
++            cli_name='admin',
++            label=_("Active Directory domain administrator"),
++            ),
++        Password('realm_passwd?',
++                 cli_name='password',
++                 label=_("Active Directory domain administrator's password"),
++                 confirm=False,
++                 ),
+         Str('realm_server?',
+             cli_name='server',
+-            label=_('Domain controller for the Active Directory domain (optional)'),
+-        ),
++            label=_('Domain controller for the Active Directory domain '
++                    '(optional)'),
++            ),
+     )
+ 
+     def execute(self, *keys, **options):
+@@ -1784,7 +1805,7 @@ class trust_fetch_domains(LDAPRetrieve):
+         # With privilege separation we also cannot authenticate as
+         # HTTP/ principal because we have no access to its key material.
+         # Thus, we'll use DBus call out to oddjobd helper in all cases
+-        fetch_trusted_domains_over_dbus(self.api, keys[0])
++        fetch_trusted_domains_over_dbus(self.api, *keys, **options)
+         result['summary'] = unicode(_('List of trust domains successfully '
+                                       'refreshed. Use trustdomain-find '
+                                       'command to list them.'))
+-- 
+2.20.1
+
diff --git a/SOURCES/0041-adtrust-define-Guests-mapping-after-creating-cifs-pr.patch b/SOURCES/0041-adtrust-define-Guests-mapping-after-creating-cifs-pr.patch
new file mode 100644
index 0000000..f3dc8fe
--- /dev/null
+++ b/SOURCES/0041-adtrust-define-Guests-mapping-after-creating-cifs-pr.patch
@@ -0,0 +1,51 @@
+From bb5026c5a265b78f9c889bd818ccfac9959b7d77 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Tue, 9 Oct 2018 17:21:37 +0300
+Subject: [PATCH] adtrust: define Guests mapping after creating cifs/ principal
+
+All Samba utilities load passdb modules from the configuration file. As
+result, 'net groupmap' call would try to initialize ipasam passdb module
+and that one would try to connect to LDAP using Kerberos authentication.
+
+We should be running it after cifs/ principal is actually created in
+ipa-adtrust-install or otherwise setting up group mapping will fail.
+
+This only affects new installations. For older ones 'net groupmap' would
+work just fine because adtrust is already configured and all principals
+exist already.
+
+A re-run of 'ipa-server-upgrade' is a workaround too but better to fix
+the initial setup.
+
+Related: https://pagure.io/freeipa/issue/7705
+Reviewed-By: Rob Crittenden <rcritten@redhat.com>
+(cherry picked from commit 1ef0fe8bb824282c2f48417efda3a60e7c1bf580)
+---
+ ipaserver/install/adtrustinstance.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
+index d6b8f5cfa66c0cfbc6d47906703fc09c3e961a53..4141d8991cf75b60dea4ec123f0e3931bb3e6976 100644
+--- a/ipaserver/install/adtrustinstance.py
++++ b/ipaserver/install/adtrustinstance.py
+@@ -845,8 +845,6 @@ class ADTRUSTInstance(service.Service):
+                   self.__create_samba_domain_object)
+         self.step("creating samba config registry", self.__write_smb_registry)
+         self.step("writing samba config file", self.__write_smb_conf)
+-        self.step("map BUILTIN\\Guests to nobody group",
+-                  self.__map_Guests_to_nobody)
+         self.step("adding cifs Kerberos principal",
+                   self.request_service_keytab)
+         self.step("adding cifs and host Kerberos principals to the adtrust agents group", \
+@@ -858,6 +856,8 @@ class ADTRUSTInstance(service.Service):
+         self.step("updating Kerberos config", self.__update_krb5_conf)
+         self.step("activating CLDAP plugin", self.__add_cldap_module)
+         self.step("activating sidgen task", self.__add_sidgen_task)
++        self.step("map BUILTIN\\Guests to nobody group",
++                  self.__map_Guests_to_nobody)
+         self.step("configuring smbd to start on boot", self.__enable)
+         self.step("adding special DNS service records", \
+                   self.__add_dns_service_records)
+-- 
+2.20.1
+
diff --git a/SOURCES/0041-ipa-replica-install-properly-use-the-file-store.patch b/SOURCES/0041-ipa-replica-install-properly-use-the-file-store.patch
deleted file mode 100644
index b1e37b0..0000000
--- a/SOURCES/0041-ipa-replica-install-properly-use-the-file-store.patch
+++ /dev/null
@@ -1,141 +0,0 @@
-From d169b6fc759a7586c6b3372db7e81c7862b2f96e Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Wed, 5 Sep 2018 17:36:16 +0200
-Subject: [PATCH] ipa-replica-install: properly use the file store
-
-In ipa-replica-install, many components use their own instance
-of the FileStore to backup configuration files to the pre-install
-state. This causes issues when the calls are mixed, like for
-instance:
-ds.do_task1_that_backups_file (using ds.filestore)
-http.do_task2_that_backups_file (using http.filestore)
-ds.do_task3_that_backups_file (using ds.filestore)
-
-because the list of files managed by ds.filestore does not include
-the files managed by http.filestore, and the 3rd call would remove
-any file added on 2nd call.
-
-The symptom of this bug is that ipa-replica-install does not save
-/etc/httpd/conf.d/ssl.conf and subsequent uninstallation does not
-restore the file, leading to a line referring to ipa-rewrite.conf
-that prevents httpd startup.
-
-The installer should consistently use the same filestore.
-
-Fixes https://pagure.io/freeipa/issue/7684
-
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/server/replicainstall.py | 31 +++++++++++++---------
- 1 file changed, 18 insertions(+), 13 deletions(-)
-
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 396d6089449225cc83aa28552a2009b9057e65ab..525a62c474c7429b7efee4853eb71e487e656bba 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -79,7 +79,7 @@ def make_pkcs12_info(directory, cert_name, password_name):
- 
- 
- def install_replica_ds(config, options, ca_is_configured, remote_api,
--                       ca_file, promote=False, pkcs12_info=None):
-+                       ca_file, promote=False, pkcs12_info=None, fstore=None):
-     dsinstance.check_ports()
- 
-     # if we have a pkcs12 file, create the cert db from
-@@ -95,7 +95,8 @@ def install_replica_ds(config, options, ca_is_configured, remote_api,
-         ca_subject = installutils.default_ca_subject_dn(config.subject_base)
- 
-     ds = dsinstance.DsInstance(
--        config_ldif=options.dirsrv_config_file)
-+        config_ldif=options.dirsrv_config_file,
-+        fstore=fstore)
-     ds.create_replica(
-         realm_name=config.realm_name,
-         master_fqdn=config.master_host_name,
-@@ -115,8 +116,9 @@ def install_replica_ds(config, options, ca_is_configured, remote_api,
-     return ds
- 
- 
--def install_krb(config, setup_pkinit=False, pkcs12_info=None, promote=False):
--    krb = krbinstance.KrbInstance()
-+def install_krb(config, setup_pkinit=False, pkcs12_info=None, promote=False,
-+                fstore=None):
-+    krb = krbinstance.KrbInstance(fstore=fstore)
- 
-     # pkinit files
-     if pkcs12_info is None:
-@@ -153,7 +155,8 @@ def install_ca_cert(ldap, base_dn, realm, cafile, destfile=paths.IPA_CA_CRT):
- 
- def install_http(config, auto_redirect, ca_is_configured, ca_file,
-                  promote=False,
--                 pkcs12_info=None):
-+                 pkcs12_info=None,
-+                 fstore=None):
-     # if we have a pkcs12 file, create the cert db from
-     # that. Otherwise the ds setup will create the CA
-     # cert
-@@ -161,8 +164,7 @@ def install_http(config, auto_redirect, ca_is_configured, ca_file,
-         pkcs12_info = make_pkcs12_info(config.dir, "httpcert.p12",
-                                        "http_pin.txt")
- 
--
--    http = httpinstance.HTTPInstance()
-+    http = httpinstance.HTTPInstance(fstore=fstore)
-     http.create_instance(
-         config.realm_name, config.host_name, config.domain_name,
-         config.dirman_password, pkcs12_info,
-@@ -173,14 +175,14 @@ def install_http(config, auto_redirect, ca_is_configured, ca_file,
-     return http
- 
- 
--def install_dns_records(config, options, remote_api):
-+def install_dns_records(config, options, remote_api, fstore=None):
- 
-     if not bindinstance.dns_container_exists(
-             ipautil.realm_to_suffix(config.realm_name)):
-         return
- 
-     try:
--        bind = bindinstance.BindInstance(api=remote_api)
-+        bind = bindinstance.BindInstance(api=remote_api, fstore=fstore)
-         for ip in config.ips:
-             reverse_zone = bindinstance.find_reverse_zone(ip, remote_api)
- 
-@@ -1425,10 +1427,11 @@ def install(installer):
-                                 remote_api,
-                                 ca_file=cafile,
-                                 promote=promote,
--                                pkcs12_info=dirsrv_pkcs12_info)
-+                                pkcs12_info=dirsrv_pkcs12_info,
-+                                fstore=fstore)
- 
-         # Always try to install DNS records
--        install_dns_records(config, options, remote_api)
-+        install_dns_records(config, options, remote_api, fstore=fstore)
- 
-         ntpinstance.ntp_ldap_enable(config.host_name, ds.suffix,
-                                     remote_api.env.realm)
-@@ -1449,7 +1452,8 @@ def install(installer):
-         config,
-         setup_pkinit=not options.no_pkinit,
-         pkcs12_info=pkinit_pkcs12_info,
--        promote=promote)
-+        promote=promote,
-+        fstore=fstore)
- 
-     if promote:
-         # We need to point to the master when certmonger asks for
-@@ -1479,7 +1483,8 @@ def install(installer):
-         promote=promote,
-         pkcs12_info=http_pkcs12_info,
-         ca_is_configured=ca_enabled,
--        ca_file=cafile)
-+        ca_file=cafile,
-+        fstore=fstore)
- 
-     if promote:
-         # Need to point back to ourself after the cert for HTTP is obtained
--- 
-2.17.1
-
diff --git a/SOURCES/0042-Ensure-that-public-cert-and-CA-bundle-are-readable.patch b/SOURCES/0042-Ensure-that-public-cert-and-CA-bundle-are-readable.patch
deleted file mode 100644
index ce6f140..0000000
--- a/SOURCES/0042-Ensure-that-public-cert-and-CA-bundle-are-readable.patch
+++ /dev/null
@@ -1,175 +0,0 @@
-From e4084fc1608cd13e73ce201471ff518105f4b9f5 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 22 Jun 2018 12:17:23 +0200
-Subject: [PATCH] Ensure that public cert and CA bundle are readable
-
-In CIS hardened mode, the process umask is 027. This results in some
-files not being world readable. Ensure that write_certificate_list()
-calls in client installer, server installer, and upgrader create cert
-bundles with permission bits 0644.
-
-Fixes: https://pagure.io/freeipa/issue/7594
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaclient/install/client.py                | 10 +++++++---
- ipaclient/install/ipa_certupdate.py        |  4 ++--
- ipalib/x509.py                             |  5 ++++-
- ipaserver/install/cainstance.py            |  2 +-
- ipaserver/install/httpinstance.py          |  2 +-
- ipaserver/install/installutils.py          |  6 +++++-
- ipaserver/install/krbinstance.py           |  2 +-
- ipaserver/install/server/replicainstall.py |  2 +-
- 8 files changed, 22 insertions(+), 11 deletions(-)
-
-diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
-index 0fbe31b762561b3e2ee2f35a666a93de8857bced..a1c74b41f790917a302ab32f6be7a4d23a989708 100644
---- a/ipaclient/install/client.py
-+++ b/ipaclient/install/client.py
-@@ -1837,7 +1837,7 @@ def get_ca_certs(fstore, options, server, basedn, realm):
- 
-     if ca_certs is not None:
-         try:
--            x509.write_certificate_list(ca_certs, ca_file)
-+            x509.write_certificate_list(ca_certs, ca_file, mode=0o644)
-         except Exception as e:
-             if os.path.exists(ca_file):
-                 try:
-@@ -2775,10 +2775,14 @@ def _install(options):
- 
-     x509.write_certificate_list(
-         [c for c, n, t, u in ca_certs if t is not False],
--        paths.KDC_CA_BUNDLE_PEM)
-+        paths.KDC_CA_BUNDLE_PEM,
-+        mode=0o644
-+    )
-     x509.write_certificate_list(
-         [c for c, n, t, u in ca_certs if t is not False],
--        paths.CA_BUNDLE_PEM)
-+        paths.CA_BUNDLE_PEM,
-+        mode=0o644
-+    )
- 
-     # Add the CA certificates to the IPA NSS database
-     logger.debug("Adding CA certificates to the IPA NSS database.")
-diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py
-index 875cd7000c09aee044c5141a889617c9195182b1..878af250428fbf122c048d612fefc17e4ea674ff 100644
---- a/ipaclient/install/ipa_certupdate.py
-+++ b/ipaclient/install/ipa_certupdate.py
-@@ -187,10 +187,10 @@ def update_server(certs):
-     update_file(paths.CACERT_PEM, certs)
- 
- 
--def update_file(filename, certs, mode=0o444):
-+def update_file(filename, certs, mode=0o644):
-     certs = (c[0] for c in certs if c[2] is not False)
-     try:
--        x509.write_certificate_list(certs, filename)
-+        x509.write_certificate_list(certs, filename, mode=mode)
-     except Exception as e:
-         logger.error("failed to update %s: %s", filename, e)
- 
-diff --git a/ipalib/x509.py b/ipalib/x509.py
-index 67a9af4c5d3456f8cdd6d966373be75d7036f1b7..cfacfa6fc784a4bc1559d68da0cc505874346e22 100644
---- a/ipalib/x509.py
-+++ b/ipalib/x509.py
-@@ -36,6 +36,7 @@ import datetime
- import ipaddress
- import ssl
- import base64
-+import os
- import re
- 
- from cryptography import x509 as crypto_x509
-@@ -519,7 +520,7 @@ def write_certificate(cert, filename):
-         raise errors.FileError(reason=str(e))
- 
- 
--def write_certificate_list(certs, filename):
-+def write_certificate_list(certs, filename, mode=None):
-     """
-     Write a list of certificates to a file in PEM format.
- 
-@@ -529,6 +530,8 @@ def write_certificate_list(certs, filename):
- 
-     try:
-         with open(filename, 'wb') as f:
-+            if mode is not None:
-+                os.fchmod(f.fileno(), mode)
-             for cert in certs:
-                 f.write(cert.public_bytes(Encoding.PEM))
-     except (IOError, OSError) as e:
-diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
-index ffcebd719a16ebc5a991b35507b96411ad31eb10..d6e467097808594756d947fa721b8cf10fe7d043 100644
---- a/ipaserver/install/cainstance.py
-+++ b/ipaserver/install/cainstance.py
-@@ -848,7 +848,7 @@ class CAInstance(DogtagInstance):
-         for path in [paths.IPA_CA_CRT,
-                      paths.KDC_CA_BUNDLE_PEM,
-                      paths.CA_BUNDLE_PEM]:
--            x509.write_certificate_list(certlist, path)
-+            x509.write_certificate_list(certlist, path, mode=0o644)
- 
-     def __request_ra_certificate(self):
-         """
-diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
-index 3f83248dd89118aeecfbf458c5079dde8b2cb93d..05b88998353597aebc39b6dad5e1a688dca84f49 100644
---- a/ipaserver/install/httpinstance.py
-+++ b/ipaserver/install/httpinstance.py
-@@ -481,7 +481,7 @@ class HTTPInstance(service.Service):
-             raise RuntimeError("HTTPD cert was issued by an unknown CA.")
-         # at this time we can assume any CA cert will be valid since this is
-         # only run during installation
--        x509.write_certificate_list(certlist, paths.CA_CRT)
-+        x509.write_certificate_list(certlist, paths.CA_CRT, mode=0o644)
- 
-     def is_kdcproxy_configured(self):
-         """Check if KDC proxy has already been configured in the past"""
-diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
-index 6614da69a0a046cdfa1b309f77972ead9de1279f..64beeffaebc4bcfe2b11dbf1109e54d7a9479d67 100644
---- a/ipaserver/install/installutils.py
-+++ b/ipaserver/install/installutils.py
-@@ -1314,7 +1314,11 @@ def load_external_cert(files, ca_subject):
-     cert_file.flush()
- 
-     ca_file = tempfile.NamedTemporaryFile()
--    x509.write_certificate_list(ca_cert_chain[1:], ca_file.name)
-+    x509.write_certificate_list(
-+        ca_cert_chain[1:],
-+        ca_file.name,
-+        mode=0o644
-+    )
-     ca_file.flush()
- 
-     return cert_file, ca_file
-diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
-index 09cafb7b84623594fe88083f5b914cee0f050409..a3079bd6304a41116f9aa5e78b6c6c71d72d7aa6 100644
---- a/ipaserver/install/krbinstance.py
-+++ b/ipaserver/install/krbinstance.py
-@@ -495,7 +495,7 @@ class KrbInstance(service.Service):
-                                           self.api.env.realm,
-                                           False)
-         ca_certs = [c for c, _n, t, _u in ca_certs if t is not False]
--        x509.write_certificate_list(ca_certs, paths.CACERT_PEM)
-+        x509.write_certificate_list(ca_certs, paths.CACERT_PEM, mode=0o644)
- 
-     def issue_selfsigned_pkinit_certs(self):
-         self._call_certmonger(certmonger_ca="SelfSign")
-diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index 525a62c474c7429b7efee4853eb71e487e656bba..eb354f81ba6e4cbc3848f9c24338fb85cc7639ae 100644
---- a/ipaserver/install/server/replicainstall.py
-+++ b/ipaserver/install/server/replicainstall.py
-@@ -147,7 +147,7 @@ def install_ca_cert(ldap, base_dn, realm, cafile, destfile=paths.IPA_CA_CRT):
-                 pass
-         else:
-             certs = [c[0] for c in certs if c[2] is not False]
--            x509.write_certificate_list(certs, destfile)
-+            x509.write_certificate_list(certs, destfile, mode=0o644)
-     except Exception as e:
-         raise ScriptError("error copying files: " + str(e))
-     return destfile
--- 
-2.17.1
-
diff --git a/SOURCES/0042-net-groupmap-force-using-empty-config-when-mapping-G.patch b/SOURCES/0042-net-groupmap-force-using-empty-config-when-mapping-G.patch
new file mode 100644
index 0000000..2b2b7ae
--- /dev/null
+++ b/SOURCES/0042-net-groupmap-force-using-empty-config-when-mapping-G.patch
@@ -0,0 +1,56 @@
+From dae784292fb49559de4aaee2a999c444a72ce272 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Tue, 16 Oct 2018 17:54:09 +0300
+Subject: [PATCH] net groupmap: force using empty config when mapping Guests
+
+When we define a group mapping for BUILTIN\Guests to 'nobody' group in
+we run 'net groupmap add ...' with a default /etc/samba/smb.conf which
+is now configured to use ipasam passdb module. We authenticate to LDAP
+with GSSAPI in ipasam passdb module initialization.
+
+If GSSAPI authentication failed (KDC is offline, for example, during
+server upgrade), 'net groupmap add' crashes after ~10 attempts to
+re-authenticate. This is intended behavior in smbd/winbindd as they
+cannot work anymore. However, for the command line tools there are
+plenty of operations where passdb module is not needed.
+
+Additionally, GSSAPI authentication uses the default ccache in the
+environment and a key from /etc/samba/samba.keytab keytab. This means
+that if you'd run 'net *' as root, it will replace whatever Kerberos
+tickets you have with a TGT for cifs/`hostname` and a service ticket to
+ldap/`hostname` of IPA master.
+
+Apply a simple solution to avoid using /etc/samba/smb.conf when we
+set up the group mapping by specifying '-s /dev/null' in 'net groupmap'
+call.
+
+For upgrade code this is enough as in
+a678336b8b36cdbea2512e79c09e475fdc249569 we enforce use of empty
+credentials cache during upgrade to prevent tripping on individual
+ccaches from KEYRING: or KCM: cache collections.
+
+Related: https://pagure.io/freeipa/issue/7705
+Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
+(cherry picked from commit 3b79deae537f73ffd18e85f52e00e611543e5e45)
+---
+ ipaserver/install/adtrustinstance.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
+index 4141d8991cf75b60dea4ec123f0e3931bb3e6976..a21be5fba375a48fb82ea4224d00fb71c3688eb8 100644
+--- a/ipaserver/install/adtrustinstance.py
++++ b/ipaserver/install/adtrustinstance.py
+@@ -123,8 +123,8 @@ def make_netbios_name(s):
+ 
+ def map_Guests_to_nobody():
+     env = {'LC_ALL': 'C'}
+-    args = [paths.NET, 'groupmap', 'add', 'sid=S-1-5-32-546',
+-            'unixgroup=nobody', 'type=builtin']
++    args = [paths.NET, '-s', '/dev/null', 'groupmap', 'add',
++            'sid=S-1-5-32-546', 'unixgroup=nobody', 'type=builtin']
+ 
+     logger.debug("Map BUILTIN\\Guests to a group 'nobody'")
+     ipautil.run(args, env=env, raiseonerr=False, capture_error=True)
+-- 
+2.20.1
+
diff --git a/SOURCES/0043-Always-make-ipa.p11-kit-world-readable.patch b/SOURCES/0043-Always-make-ipa.p11-kit-world-readable.patch
deleted file mode 100644
index 48c83c6..0000000
--- a/SOURCES/0043-Always-make-ipa.p11-kit-world-readable.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From 8ba1a1e89b34e587a9898a85f1c545dbd1c7765a Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 22 Jun 2018 12:22:06 +0200
-Subject: [PATCH] Always make ipa.p11-kit world-readable
-
-Ensure that ipa.p11-kit is always world-readable.
-
-Fixes: https://pagure.io/freeipa/issue/7594
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaplatform/redhat/tasks.py | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
-index 6a4270defc9f444f76677bdf08d2a680649664bb..8fc8b54c146d540c988d97b7fb0927fced7c3e29 100644
---- a/ipaplatform/redhat/tasks.py
-+++ b/ipaplatform/redhat/tasks.py
-@@ -269,6 +269,7 @@ class RedHatTaskNamespace(BaseTaskNamespace):
- 
-         try:
-             f = open(new_cacert_path, 'w')
-+            os.fchmod(f.fileno(), 0o644)
-         except IOError as e:
-             logger.info("Failed to open %s: %s", new_cacert_path, e)
-             return False
--- 
-2.17.1
-
diff --git a/SOURCES/0043-Bypass-D-BUS-interface-definition-deficiences-for-tr.patch b/SOURCES/0043-Bypass-D-BUS-interface-definition-deficiences-for-tr.patch
new file mode 100644
index 0000000..a9fd36f
--- /dev/null
+++ b/SOURCES/0043-Bypass-D-BUS-interface-definition-deficiences-for-tr.patch
@@ -0,0 +1,102 @@
+From 931c7fc1ada309a2d0fe832c95e5a5bf4a89edac Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Mon, 8 Apr 2019 11:43:59 +0300
+Subject: [PATCH] Bypass D-BUS interface definition deficiences for
+ trust-fetch-domains
+
+In oddjobd it is possible to pass arguments as command line or on the
+stdin. We use command line to pass them but the way oddjobd registers
+the D-BUS method signatures is by specifying all arguments as mandatory.
+
+Internally, oddjobd simply ignores if you passed less arguments than
+specified in the D-BUS defition. Unfortunately, it is not possible to
+specify less than maximum due to D-BUS seeing all arguments in the
+list (30 is defined for the trust-fetch-domains).
+
+To pass options, have to pad a list of arguments to maximum with empty
+strings and then filter out unneeded ones in the script. Option parser
+already removes all options from the list of arguments so all we need to
+do is to take our actual arguments. In case of trust-fetch-domains, it
+is the name of the domain so we can only care about args[0].
+
+Fixes: https://pagure.io/freeipa/issue/7903
+Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
+(cherry picked from commit add6180ae5c5771b0b0f1c743df069ece4256512)
+
+Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
+---
+ .../oddjob/com.redhat.idm.trust-fetch-domains | 21 ++++++++++++-------
+ ipaserver/plugins/trust.py                    | 11 ++++++++--
+ 2 files changed, 22 insertions(+), 10 deletions(-)
+
+diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
+index 1e90759e0e6e7be36745ec12f4478bfec0f0358e..029de781b2a1f94b1fd281b79aa69a1e9422dede 100755
+--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
++++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
+@@ -5,6 +5,7 @@ from ipaserver.install.installutils import is_ipa_configured, ScriptError
+ from ipapython import config, ipautil
+ from ipalib import api
+ from ipapython.dn import DN
++from ipapython.dnsutil import DNSName
+ from ipaplatform.constants import constants
+ from ipaplatform.paths import paths
+ import sys
+@@ -37,7 +38,17 @@ def parse_options():
+     options, args = parser.parse_args()
+     safe_options = parser.get_safe_opts(options)
+ 
+-    return safe_options, options, args
++    # We only use first argument of the passed args but as D-BUS interface
++    # in oddjobd cannot expose optional, we fill in empty slots from IPA side
++    # and filter them here.
++    trusted_domain = ipautil.fsdecode(args[0]).lower()
++
++    # Accept domain names that at least have two labels. We do not support
++    # single label Active Directory domains. This also catches empty args.
++    if len(DNSName(trusted_domain).labels) < 2:
++        # LSB status code 2: invalid or excess argument(s)
++        raise ScriptError("You must specify a valid trusted domain name", 2)
++    return safe_options, options, trusted_domain
+ 
+ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
+     getkeytab_args = ["/usr/sbin/ipa-getkeytab",
+@@ -87,13 +98,7 @@ if not os.getegid() == 0:
+     # LSB status code 4: user had insufficient privilege
+     raise ScriptError("You must be root to run ipactl.", 4)
+ 
+-safe_options, options, args = parse_options()
+-
+-if len(args) != 1:
+-    # LSB status code 2: invalid or excess argument(s)
+-    raise ScriptError("You must specify trusted domain name", 2)
+-
+-trusted_domain = ipautil.fsdecode(args[0]).lower()
++safe_options, options, trusted_domain = parse_options()
+ 
+ api.bootstrap(in_server=True, log=None,
+               context='server', confdir=paths.ETC_IPA)
+diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
+index bb9e0fe3303989c49cf339df6e5aeeb4ce1435cf..5de363bda6fdee081d3e4c98e731cecfa9585d21 100644
+--- a/ipaserver/plugins/trust.py
++++ b/ipaserver/plugins/trust.py
+@@ -446,8 +446,15 @@ def fetch_trusted_domains_over_dbus(myapi, *keys, **options):
+         fetch_domains_method = intf.get_dbus_method(
+                 'fetch_domains',
+                 dbus_interface=DBUS_IFACE_TRUST)
+-        (_ret, _stdout, _stderr) = fetch_domains_method(
+-            [forest_name] + method_options)
++        # Oddjobd D-BUS method definition only accepts fixed number
++        # of arguments on the command line. Thus, we need to pass
++        # remaining ones as ''. There are 30 slots to allow for extension
++        # and the number comes from the 'arguments' definition in
++        # install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf
++        method_arguments = [forest_name]
++        method_arguments.extend(method_options)
++        method_arguments.extend([''] * (30 - len(method_arguments)))
++        (_ret, _stdout, _stderr) = fetch_domains_method(*method_arguments)
+     except dbus.DBusException as e:
+         logger.error('Failed to call %s.fetch_domains helper.'
+                      'DBus exception is %s.', DBUS_IFACE_TRUST, str(e))
+-- 
+2.20.1
+
diff --git a/SOURCES/0044-Make-etc-httpd-alias-world-readable-executable.patch b/SOURCES/0044-Make-etc-httpd-alias-world-readable-executable.patch
deleted file mode 100644
index f7b0c12..0000000
--- a/SOURCES/0044-Make-etc-httpd-alias-world-readable-executable.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 9bb9255161eef8da54842c0a6aeb1ddb0b20c0df Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 22 Jun 2018 12:25:33 +0200
-Subject: [PATCH] Make /etc/httpd/alias world readable & executable
-
-The directory /etc/httpd/alias contains public key material. It must be
-world readable and executable, so any client can read public certs.
-
-Note: executable for a directory means, that a process is allowed to
-traverse into the directory.
-
-Fixes: https://pagure.io/freeipa/issue/7594
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaserver/install/httpinstance.py | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
-index 05b88998353597aebc39b6dad5e1a688dca84f49..3f8b18c4e8412c1767b6ad541da18d8b30ad59f7 100644
---- a/ipaserver/install/httpinstance.py
-+++ b/ipaserver/install/httpinstance.py
-@@ -217,6 +217,9 @@ class HTTPInstance(service.Service):
-         self.update_httpd_service_ipa_conf()
-         self.update_httpd_wsgi_conf()
- 
-+        # Must be world-readable / executable
-+        os.chmod(paths.HTTPD_ALIAS_DIR, 0o755)
-+
-         target_fname = paths.HTTPD_IPA_CONF
-         http_txt = ipautil.template_file(
-             os.path.join(paths.USR_SHARE_IPA_DIR, "ipa.conf"), self.sub_dict)
--- 
-2.17.1
-
diff --git a/SOURCES/0044-ipactl-restart-fix-wrong-logic-when-checking-service.patch b/SOURCES/0044-ipactl-restart-fix-wrong-logic-when-checking-service.patch
new file mode 100644
index 0000000..5abeaaa
--- /dev/null
+++ b/SOURCES/0044-ipactl-restart-fix-wrong-logic-when-checking-service.patch
@@ -0,0 +1,97 @@
+From fc9648e33668afa1c61d62ad9e3b033011dd12d4 Mon Sep 17 00:00:00 2001
+From: Florence Blanc-Renaud <flo@redhat.com>
+Date: Fri, 26 Apr 2019 18:40:05 +0200
+Subject: [PATCH] ipactl restart: fix wrong logic when checking service list
+
+ipactl is building a list of currently running services from
+the content of /var/run/ipa/services.list, and a list of expected services
+from the services configured in LDAP.
+
+Because CA and KRA both correspond to the same pki-tomcatd service, the
+lists may contain duplicates. The code handling these duplicates is called
+at the wrong place, and may result in a wrong list of services to
+stop / restart / start.
+The fix removes the duplicates before returning the lists, hence making sure
+that there is no error when building the list of services to stop / restart
+/ start.
+
+Fixes: https://pagure.io/freeipa/issue/7927
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ install/tools/ipactl | 10 ++--------
+ 1 file changed, 2 insertions(+), 8 deletions(-)
+
+diff --git a/install/tools/ipactl b/install/tools/ipactl
+index f40ea5a6df74f04ec7e6e8959d731553651a81d3..5d858b8f9dd2a213a29c50cd2c9778b57fee9e46 100755
+--- a/install/tools/ipactl
++++ b/install/tools/ipactl
+@@ -237,7 +237,7 @@ def get_config(dirsrv):
+     for order, svc in sorted(svc_list):
+         if svc in service.SERVICE_LIST:
+             ordered_list.append(service.SERVICE_LIST[svc].systemd_name)
+-    return ordered_list
++    return deduplicate(ordered_list)
+ 
+ def get_config_from_file():
+ 
+@@ -263,7 +263,7 @@ def get_config_from_file():
+         if svc in svc_list:
+             ordered_list.append(svc)
+ 
+-    return ordered_list
++    return deduplicate(ordered_list)
+ 
+ 
+ def stop_services(svc_list):
+@@ -325,7 +325,6 @@ def ipa_start(options):
+         # no service to start
+         return
+ 
+-    svc_list = deduplicate(svc_list)
+     for svc in svc_list:
+         svchandle = services.service(svc, api=api)
+         try:
+@@ -365,7 +364,6 @@ def ipa_stop(options):
+             finally:
+                 raise IpactlError()
+ 
+-    svc_list = deduplicate(svc_list)
+     for svc in reversed(svc_list):
+         svchandle = services.service(svc, api=api)
+         try:
+@@ -452,7 +450,6 @@ def ipa_restart(options):
+ 
+     if len(old_svc_list) != 0:
+         # we need to definitely stop some services
+-        old_svc_list = deduplicate(old_svc_list)
+         for svc in reversed(old_svc_list):
+             svchandle = services.service(svc, api=api)
+             try:
+@@ -477,7 +474,6 @@ def ipa_restart(options):
+ 
+     if len(svc_list) != 0:
+         # there are services to restart
+-        svc_list = deduplicate(svc_list)
+         for svc in svc_list:
+             svchandle = services.service(svc, api=api)
+             try:
+@@ -500,7 +496,6 @@ def ipa_restart(options):
+ 
+     if len(new_svc_list) != 0:
+         # we still need to start some services
+-        new_svc_list = deduplicate(new_svc_list)
+         for svc in new_svc_list:
+             svchandle = services.service(svc, api=api)
+             try:
+@@ -552,7 +547,6 @@ def ipa_status(options):
+     if len(svc_list) == 0:
+         return
+ 
+-    svc_list = deduplicate(svc_list)
+     for svc in svc_list:
+         svchandle = services.service(svc, api=api)
+         try:
+-- 
+2.20.1
+
diff --git a/SOURCES/0045-Fix-permission-of-public-files-in-upgrader.patch b/SOURCES/0045-Fix-permission-of-public-files-in-upgrader.patch
deleted file mode 100644
index 0b81a5c..0000000
--- a/SOURCES/0045-Fix-permission-of-public-files-in-upgrader.patch
+++ /dev/null
@@ -1,82 +0,0 @@
-From 19bfd7c36d6d087f0cd7def5eb4d8850c395fb4b Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 22 Jun 2018 12:53:19 +0200
-Subject: [PATCH] Fix permission of public files in upgrader
-
-Make CA bundles, certs, and cert directories world-accessible in
-upgrader.
-
-Fixes: https://pagure.io/freeipa/issue/7594
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Rob Crittenden <rcritten@redhat.com>
----
- ipaserver/install/server/upgrade.py | 31 +++++++++++++++++++++++++++++
- 1 file changed, 31 insertions(+)
-
-diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
-index 4e5096e598cd10e3bd98f91946b4d26377d0de6e..7faaacd5d2f0c39bcf744c288b283009ccb3ead5 100644
---- a/ipaserver/install/server/upgrade.py
-+++ b/ipaserver/install/server/upgrade.py
-@@ -4,12 +4,14 @@
- 
- from __future__ import print_function, absolute_import
- 
-+import errno
- import logging
- import re
- import os
- import shutil
- import pwd
- import fileinput
-+import stat
- import sys
- import tempfile
- from contextlib import contextmanager
-@@ -1656,6 +1658,34 @@ def update_replica_config(db_suffix):
-         logger.info("Updated entry %s", dn)
- 
- 
-+def fix_permissions():
-+    """Fix permission of public accessible files and directories
-+
-+    In case IPA was installed with restricted umask, some public files and
-+    directories may not be readable and accessible.
-+
-+    See https://pagure.io/freeipa/issue/7594
-+    """
-+    candidates = [
-+        paths.HTTPD_ALIAS_DIR,
-+        paths.CA_BUNDLE_PEM,
-+        paths.KDC_CA_BUNDLE_PEM,
-+        paths.IPA_CA_CRT,
-+        paths.IPA_P11_KIT,
-+    ]
-+    for filename in candidates:
-+        try:
-+            s = os.stat(filename)
-+        except OSError as e:
-+            if e.errno != errno.ENOENT:
-+                raise
-+            continue
-+        mode = 0o755 if stat.S_ISDIR(s.st_mode) else 0o644
-+        if mode != stat.S_IMODE(s.st_mode):
-+            logger.debug("Fix permission of %s to %o", filename, mode)
-+            os.chmod(filename, mode)
-+
-+
- def upgrade_configuration():
-     """
-     Execute configuration upgrade of the IPA services
-@@ -1677,6 +1707,7 @@ def upgrade_configuration():
-         ds.start(ds_serverid)
- 
-     check_certs()
-+    fix_permissions()
- 
-     auto_redirect = find_autoredirect(fqdn)
-     sub_dict = dict(
--- 
-2.17.1
-
diff --git a/SOURCES/0045-replica-install-acknowledge-ca_host-override.patch b/SOURCES/0045-replica-install-acknowledge-ca_host-override.patch
new file mode 100644
index 0000000..e443c0e
--- /dev/null
+++ b/SOURCES/0045-replica-install-acknowledge-ca_host-override.patch
@@ -0,0 +1,40 @@
+From 14dc08477429ff22acf36052367394a4b59089d0 Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Wed, 27 Mar 2019 11:53:33 +0100
+Subject: [PATCH] replica install: acknowledge ca_host override
+
+Fixup for commit c0fd5e39c726ef4dc12e87a2f9c08ebb32ed27fe. Only set
+ca_host to source master hostname if ca_host points to the local host.
+This permits users to override ca_host in /etc/ipa/default.conf when
+installing a replica.
+
+Related: https://pagure.io/freeipa/issue/7744
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
+Reviewed-By: Tibor Dudlak <tdudlak@redhat.com>
+---
+ ipaserver/install/server/replicainstall.py | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
+index 7178238bfb996f987b5e3beaebe05fa104ada089..e13b7f18c4d4df7efde50ac9cb7d2f71bfa765cc 100644
+--- a/ipaserver/install/server/replicainstall.py
++++ b/ipaserver/install/server/replicainstall.py
+@@ -1048,7 +1048,12 @@ def promote_check(installer):
+     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
++    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
+-- 
+2.20.1
+
diff --git a/SOURCES/0046-Find-orphan-automember-rules.patch b/SOURCES/0046-Find-orphan-automember-rules.patch
deleted file mode 100644
index 66d5136..0000000
--- a/SOURCES/0046-Find-orphan-automember-rules.patch
+++ /dev/null
@@ -1,214 +0,0 @@
-From b78abe934c6c0038f74dd9e52309f61854d86469 Mon Sep 17 00:00:00 2001
-From: Thomas Woerner <twoerner@redhat.com>
-Date: Mon, 1 Oct 2018 11:58:26 +0100
-Subject: [PATCH] Find orphan automember rules
-
-If groups or hostgroups have been removed after automember rules have been
-created using them, then automember-rebuild, automember-add, host-add and
-more commands could fail.
-
-A new command has been added to the ipa tool:
-
-  ipa automember-find-orphans --type={hostgroup,group} [--remove]
-
-This command retuns the list of orphan automember rules in the same way as
-automember-find. With the --remove option the orphan rules are also removed.
-
-The IPA API version has been increased and a test case has been added.
-
-Using ideas from a patch by: Rob Crittenden <rcritten@redhat.com>
-
-See: https://pagure.io/freeipa/issue/6476
-Signed-off-by: Thomas Woerner <twoerner@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
-Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
----
- API.txt                                       | 15 +++++
- VERSION.m4                                    |  4 +-
- ipaserver/plugins/automember.py               | 60 +++++++++++++++++++
- .../test_xmlrpc/test_automember_plugin.py     | 48 +++++++++++++++
- 4 files changed, 125 insertions(+), 2 deletions(-)
-
-diff --git a/API.txt b/API.txt
-index 0e09e58a6ecaa4f724fb0c92b4faaf64df9fab5a..b9dc35fb5752ce04f58aa8c4c3e89c7299f34cd7 100644
---- a/API.txt
-+++ b/API.txt
-@@ -186,6 +186,20 @@ output: Output('count', type=[<type 'int'>])
- output: ListOfEntries('result')
- output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
- output: Output('truncated', type=[<type 'bool'>])
-+command: automember_find_orphans/1
-+args: 1,7,4
-+arg: Str('criteria?')
-+option: Flag('all', autofill=True, cli_name='all', default=False)
-+option: Str('description?', autofill=False, cli_name='desc')
-+option: Flag('pkey_only?', autofill=True, default=False)
-+option: Flag('raw', autofill=True, cli_name='raw', default=False)
-+option: Flag('remove?', autofill=True, default=False)
-+option: StrEnum('type', values=[u'group', u'hostgroup'])
-+option: Str('version?')
-+output: Output('count', type=[<type 'int'>])
-+output: ListOfEntries('result')
-+output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
-+output: Output('truncated', type=[<type 'bool'>])
- command: automember_mod/1
- args: 1,9,3
- arg: Str('cn', cli_name='automember_rule')
-@@ -6498,6 +6512,7 @@ default: automember_default_group_set/1
- default: automember_default_group_show/1
- default: automember_del/1
- default: automember_find/1
-+default: automember_find_orphans/1
- default: automember_mod/1
- default: automember_rebuild/1
- default: automember_remove_condition/1
-diff --git a/VERSION.m4 b/VERSION.m4
-index 81e671ed60f2ada0766b06db879c706cf7c4c77a..7ebf3410c8a688577f1fabc37d65b128e47418a6 100644
---- a/VERSION.m4
-+++ b/VERSION.m4
-@@ -82,8 +82,8 @@ define(IPA_DATA_VERSION, 20100614120000)
- #                                                      #
- ########################################################
- define(IPA_API_VERSION_MAJOR, 2)
--define(IPA_API_VERSION_MINOR, 229)
--# Last change: Added the Certificate parameter
-+define(IPA_API_VERSION_MINOR, 230)
-+# Last change: Added `automember-find-orphans' command
- 
- 
- ########################################################
-diff --git a/ipaserver/plugins/automember.py b/ipaserver/plugins/automember.py
-index 1e29f365784695c2cf1947f62351d99d7da0515d..3f48769f588f8db03caf65e7bc1206047796f63e 100644
---- a/ipaserver/plugins/automember.py
-+++ b/ipaserver/plugins/automember.py
-@@ -116,6 +116,11 @@ EXAMPLES:
- """) + _("""
-  Find all of the automember rules:
-     ipa automember-find
-+""") + _("""
-+ Find all of the orphan automember rules:
-+    ipa automember-find-orphans --type=hostgroup
-+ Find all of the orphan automember rules and remove them:
-+    ipa automember-find-orphans --type=hostgroup --remove
- """) + _("""
-  Display a automember rule:
-     ipa automember-show --type=hostgroup webservers
-@@ -820,3 +825,58 @@ class automember_rebuild(Method):
-             result=result,
-             summary=unicode(summary),
-             value=pkey_to_value(None, options))
-+
-+
-+@register()
-+class automember_find_orphans(LDAPSearch):
-+    __doc__ = _("""
-+    Search for orphan automember rules. The command might need to be run as
-+    a privileged user user to get all orphan rules.
-+    """)
-+    takes_options = group_type + (
-+        Flag(
-+            'remove?',
-+            doc=_("Remove orphan automember rules"),
-+        ),
-+    )
-+
-+    msg_summary = ngettext(
-+        '%(count)d rules matched', '%(count)d rules matched', 0
-+    )
-+
-+    def execute(self, *keys, **options):
-+        results = super(automember_find_orphans, self).execute(*keys,
-+                                                               **options)
-+
-+        remove_option = options.get('remove')
-+        pkey_only = options.get('pkey_only', False)
-+        ldap = self.obj.backend
-+        orphans = []
-+        for entry in results["result"]:
-+            am_dn_entry = entry['automembertargetgroup'][0]
-+            # Make DN for --raw option
-+            if not isinstance(am_dn_entry, DN):
-+                am_dn_entry = DN(am_dn_entry)
-+            try:
-+                ldap.get_entry(am_dn_entry)
-+            except errors.NotFound:
-+                if pkey_only:
-+                    # For pkey_only remove automembertargetgroup
-+                    del(entry['automembertargetgroup'])
-+                orphans.append(entry)
-+                if remove_option:
-+                    ldap.delete_entry(entry['dn'])
-+
-+        results["result"][:] = orphans
-+        results["count"] = len(orphans)
-+        return results
-+
-+    def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args,
-+                     **options):
-+        assert isinstance(base_dn, DN)
-+        scope = ldap.SCOPE_SUBTREE
-+        ndn = DN(('cn', options['type']), base_dn)
-+        if options.get('pkey_only', False):
-+            # For pkey_only add automembertargetgroup
-+            attrs_list.append('automembertargetgroup')
-+        return filters, ndn, scope
-diff --git a/ipatests/test_xmlrpc/test_automember_plugin.py b/ipatests/test_xmlrpc/test_automember_plugin.py
-index ffbc91104ab504a98099babb024f9edab114ac5b..c83e11ac9410ce07a431f818bda79a34fcc3b180 100644
---- a/ipatests/test_xmlrpc/test_automember_plugin.py
-+++ b/ipatests/test_xmlrpc/test_automember_plugin.py
-@@ -715,3 +715,51 @@ class TestMultipleAutomemberConditions(XMLRPC_test):
- 
-         defaultgroup1.ensure_missing()
-         defaulthostgroup1.ensure_missing()
-+
-+
-+@pytest.mark.tier1
-+class TestAutomemberFindOrphans(XMLRPC_test):
-+    def test_create_deps_for_find_orphans(self, hostgroup1, host1,
-+                                          automember_hostgroup):
-+        """ Create host, hostgroup, and automember tracker for this class
-+        of tests. """
-+
-+        # Create hostgroup1 and automember rule with condition
-+        hostgroup1.ensure_exists()
-+        host1.ensure_exists()
-+
-+        # Manually create automember rule and condition, racker will try to
-+        # remove the automember rule in the end, which is failing as the rule
-+        # is already removed
-+        api.Command['automember_add'](hostgroup1.cn, type=u'hostgroup')
-+        api.Command['automember_add_condition'](
-+            hostgroup1.cn,
-+            key=u'fqdn', type=u'hostgroup',
-+            automemberinclusiveregex=[hostgroup_include_regex]
-+        )
-+
-+        hostgroup1.retrieve()
-+
-+    def test_find_orphan_automember_rules(self, hostgroup1):
-+        """ Remove hostgroup1, find and remove obsolete automember rules. """
-+        # Remove hostgroup1
-+
-+        hostgroup1.ensure_missing()
-+
-+        # Find obsolete automember rules
-+        result = api.Command['automember_find_orphans'](type=u'hostgroup')
-+        assert result['count'] == 1
-+
-+        # Find and remove obsolete automember rules
-+        result = api.Command['automember_find_orphans'](type=u'hostgroup',
-+                                                        remove=True)
-+        assert result['count'] == 1
-+
-+        # Find obsolete automember rules
-+        result = api.Command['automember_find_orphans'](type=u'hostgroup')
-+        assert result['count'] == 0
-+
-+        # Final cleanup of automember rule if it still exists
-+        with raises_exact(errors.NotFound(
-+                reason=u'%s: Automember rule not found' % hostgroup1.cn)):
-+            api.Command['automember_del'](hostgroup1.cn, type=u'hostgroup')
--- 
-2.17.2
-
diff --git a/SOURCES/0046-ipa-console-catch-proper-exception-when-history-file.patch b/SOURCES/0046-ipa-console-catch-proper-exception-when-history-file.patch
new file mode 100644
index 0000000..9599ec4
--- /dev/null
+++ b/SOURCES/0046-ipa-console-catch-proper-exception-when-history-file.patch
@@ -0,0 +1,44 @@
+From 9fd415c73d0b70c1afadd5694269bf429fbd1aa2 Mon Sep 17 00:00:00 2001
+From: Sergey Orlov <sorlov@redhat.com>
+Date: Thu, 25 Apr 2019 18:30:24 +0200
+Subject: [PATCH] ipa console: catch proper exception when history file can not
+ be open
+
+When history file could not be open we were catching OSError. This worked
+for python3, as it raises FileNotFoundError, which is a subclass of
+OSError. But in python2 IOError is raised when file does not exist and
+as this exception was not catched, "ipa conosle" command was crashing.
+As far as in pyton3 IOError and OSError have been merged
+(OSError is IOError) we can safely catch only IOError.
+
+Fixes: https://pagure.io/freeipa/issue/7922
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ ipalib/cli.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/ipalib/cli.py b/ipalib/cli.py
+index 7b2af485d94e9f98df44b24489795c45d690956c..2c74a2aaa47a2471a232d81aa6acfafd7f9f930f 100644
+--- a/ipalib/cli.py
++++ b/ipalib/cli.py
+@@ -969,7 +969,7 @@ class console(frontend.Command):
+         history = os.path.join(api.env.dot_ipa, "console.history")
+         try:
+             readline.read_history_file(history)
+-        except OSError:
++        except IOError:
+             pass
+ 
+         def save_history():
+@@ -979,7 +979,7 @@ class console(frontend.Command):
+             readline.set_history_length(50)
+             try:
+                 readline.write_history_file(history)
+-            except OSError:
++            except IOError:
+                 logger.exception("Unable to store history %s", history)
+ 
+         atexit.register(save_history)
+-- 
+2.20.1
+
diff --git a/SOURCES/0047-Add-a-shared-vault-retrieve-test.patch b/SOURCES/0047-Add-a-shared-vault-retrieve-test.patch
deleted file mode 100644
index ba6a17b..0000000
--- a/SOURCES/0047-Add-a-shared-vault-retrieve-test.patch
+++ /dev/null
@@ -1,113 +0,0 @@
-From 107e20a158c867a52eadb0d65982ce2f7f3ce699 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= <fcami@redhat.com>
-Date: Tue, 20 Nov 2018 17:05:30 +0100
-Subject: [PATCH] Add a shared-vault-retrieve test
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add a shared-vault-retrieve test when:
-* master has KRA installed
-* replica has no KRA
-This currently fails because of issue#7691
-
-Related-to: https://pagure.io/freeipa/issue/7691
-Signed-off-by: François Cami <fcami@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipatests/test_integration/test_vault.py | 65 ++++++++++++++++++++++++-
- 1 file changed, 64 insertions(+), 1 deletion(-)
-
-diff --git a/ipatests/test_integration/test_vault.py b/ipatests/test_integration/test_vault.py
-index 496ccb1bbdd06407e9b356ac210f639436312a22..c3465799ff933ae175684ade83b4bf276b921a96 100644
---- a/ipatests/test_integration/test_vault.py
-+++ b/ipatests/test_integration/test_vault.py
-@@ -20,14 +20,17 @@ class TestInstallKRA(IntegrationTest):
- 
-     vault_password = "password"
-     vault_data = "SSBsb3ZlIENJIHRlc3RzCg=="
-+    vault_user = "vault_user"
-+    vault_user_password = "vault_user_password"
-     vault_name_master = "ci_test_vault_master"
-     vault_name_master2 = "ci_test_vault_master2"
-     vault_name_master3 = "ci_test_vault_master3"
-     vault_name_replica_without_KRA = "ci_test_vault_replica_without_kra"
-+    shared_vault_name_replica_without_KRA = ("ci_test_shared"
-+                                             "_vault_replica_without_kra")
-     vault_name_replica_with_KRA = "ci_test_vault_replica_with_kra"
-     vault_name_replica_KRA_uninstalled = "ci_test_vault_replica_KRA_uninstalled"
- 
--
-     @classmethod
-     def install(cls, mh):
-         tasks.install_master(cls.master, setup_kra=True)
-@@ -89,6 +92,66 @@ class TestInstallKRA(IntegrationTest):
- 
-         self._retrieve_secret([self.vault_name_replica_without_KRA])
- 
-+    def test_create_and_retrieve_shared_vault_replica_without_kra(self):
-+        # create vault
-+        self.replicas[0].run_command([
-+            "ipa", "vault-add",
-+            self.shared_vault_name_replica_without_KRA,
-+            "--shared",
-+            "--type", "standard",
-+        ])
-+
-+        # archive secret
-+        self.replicas[0].run_command([
-+            "ipa", "vault-archive",
-+            self.shared_vault_name_replica_without_KRA,
-+            "--shared",
-+            "--data", self.vault_data,
-+        ])
-+        time.sleep(WAIT_AFTER_ARCHIVE)
-+
-+        # add non-admin user
-+        self.replicas[0].run_command([
-+            'ipa', 'user-add', self.vault_user,
-+            '--first', self.vault_user,
-+            '--last', self.vault_user,
-+            '--password'],
-+            stdin_text=self.vault_user_password)
-+
-+        # add it to vault
-+        self.replicas[0].run_command([
-+            "ipa", "vault-add-member",
-+            self.shared_vault_name_replica_without_KRA,
-+            "--shared",
-+            "--users", self.vault_user,
-+        ])
-+
-+        self.replicas[0].run_command([
-+            'kdestroy', '-A'])
-+
-+        user_kinit = "%s\n%s\n%s\n" % (self.vault_user_password,
-+                                       self.vault_user_password,
-+                                       self.vault_user_password)
-+
-+        self.replicas[0].run_command([
-+            'kinit', self.vault_user],
-+            stdin_text=user_kinit)
-+
-+        # TODO: possibly refactor with:
-+        # self._retrieve_secret([self.vault_name_replica_without_KRA])
-+
-+        self.replicas[0].run_command([
-+            "ipa", "vault-retrieve",
-+            "--shared",
-+            self.shared_vault_name_replica_without_KRA,
-+            "--out=test.txt"])
-+
-+        self.replicas[0].run_command([
-+            'kdestroy', '-A'])
-+
-+        tasks.kinit_admin(self.replicas[0])
-+
-+
-     def test_create_and_retrieve_vault_replica_with_kra(self):
- 
-         # install KRA on replica
--- 
-2.17.2
-
diff --git a/SOURCES/0047-upgrade-adtrust-catch-empty-result-when-retrieving-l.patch b/SOURCES/0047-upgrade-adtrust-catch-empty-result-when-retrieving-l.patch
new file mode 100644
index 0000000..26dec09
--- /dev/null
+++ b/SOURCES/0047-upgrade-adtrust-catch-empty-result-when-retrieving-l.patch
@@ -0,0 +1,48 @@
+From dfb08e295420a9ea92776fa99cd1b7fe6a6badaa Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Sat, 11 May 2019 11:54:40 +0300
+Subject: [PATCH] upgrade: adtrust - catch empty result when retrieving list of
+ trusts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Upgrade failure when ipa-server-upgrade is being run on a system with no
+trust established but trust configured
+
+Fixes: https://pagure.io/freeipa/issue/7939
+Reviewed-By: François Cami <fcami@redhat.com>
+---
+ ipaserver/install/plugins/adtrust.py | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
+index 1461e000dc855a21665eb5ea0cfe4a47df419344..55df5cd01fd0f585d5955e700ccf20c7fc9a747f 100644
+--- a/ipaserver/install/plugins/adtrust.py
++++ b/ipaserver/install/plugins/adtrust.py
+@@ -610,11 +610,17 @@ class update_tdo_to_new_layout(Updater):
+ 
+         trusts_dn = self.api.env.container_adtrusts + self.api.env.basedn
+ 
+-        trusts = ldap.get_entries(
+-            base_dn=trusts_dn,
+-            scope=ldap.SCOPE_ONELEVEL,
+-            filter=self.trust_filter,
+-            attrs_list=self.trust_attrs)
++        # We might be in a situation when no trusts exist yet
++        # In such case there is nothing to upgrade but we have to catch
++        # an exception or it will abort the whole upgrade process
++        try:
++            trusts = ldap.get_entries(
++                base_dn=trusts_dn,
++                scope=ldap.SCOPE_ONELEVEL,
++                filter=self.trust_filter,
++                attrs_list=self.trust_attrs)
++        except errors.EmptyResult:
++            trusts = []
+ 
+         # For every trust, retrieve its principals and convert
+         for t_entry in trusts:
+-- 
+2.20.1
+
diff --git a/SOURCES/0048-Add-a-Find-enabled-services-ACI-in-20-aci.update-so-.patch b/SOURCES/0048-Add-a-Find-enabled-services-ACI-in-20-aci.update-so-.patch
deleted file mode 100644
index 26f61fb..0000000
--- a/SOURCES/0048-Add-a-Find-enabled-services-ACI-in-20-aci.update-so-.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 93b58fdbcf1da0a952386e6c8f4e20c344db903c Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= <fcami@redhat.com>
-Date: Wed, 21 Nov 2018 00:01:02 +0100
-Subject: [PATCH] Add a "Find enabled services" ACI in 20-aci.update so that
- all users can find IPA servers and services. ACI suggested by Christian
- Heimes.
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Fixes: https://pagure.io/freeipa/issue/7691
-Signed-off-by: François Cami <fcami@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- install/updates/20-aci.update | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
-index 184749d78106c30fdf542c1fe1c52cb11b53a83e..7650cb48101d866b3a094ec9ab11378de4f68232 100644
---- a/install/updates/20-aci.update
-+++ b/install/updates/20-aci.update
-@@ -36,6 +36,10 @@ remove:aci:(targetfilter="(objectclass=nsContainer)")(version 3.0; acl "Deny rea
- dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
- add:aci:(targetfilter="(objectclass=nsContainer)")(targetattr="objectclass || cn")(version 3.0; acl "Read access to masters"; allow(read, search, compare) userdn = "ldap:///all";)
- 
-+# Allow users to discover enabled services
-+dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
-+add:aci:(targetfilter = "(ipaConfigString=enabledService)")(targetattrs = "ipaConfigString")(version 3.0; acl "Find enabled services"; allow(read, search, compare) userdn = "ldap:///all";)
-+
- # Allow hosts to read masters service configuration
- dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
- add:aci:(targetfilter = "(objectclass=nsContainer)")(targetattr = "ipaConfigString")(version 3.0; acl "Allow hosts to read masters service configuration"; allow(read, search, compare) userdn = "ldap:///fqdn=*,cn=computers,cn=accounts,$SUFFIX";)
--- 
-2.17.2
-
diff --git a/SOURCES/0048-Consider-configured-servers-as-valid.patch b/SOURCES/0048-Consider-configured-servers-as-valid.patch
new file mode 100644
index 0000000..012a807
--- /dev/null
+++ b/SOURCES/0048-Consider-configured-servers-as-valid.patch
@@ -0,0 +1,147 @@
+From 750a60a989d7c1fe0872af292191889e7e23382d Mon Sep 17 00:00:00 2001
+From: Christian Heimes <cheimes@redhat.com>
+Date: Mon, 29 Apr 2019 11:12:30 +0200
+Subject: [PATCH] Consider configured servers as valid
+
+Under some conditions, ipa config-show and several other commands were
+failing with error message:
+
+  ERROR: invalid 'PKINIT enabled server': all masters must have IPA master role enabled
+
+Amongst others the issue can be caused by a broken installation, when
+some services are left in state 'configuredServices'. The problem even
+block uninstallation or removal of replicas. Now configured servers are
+also consider valid providers for associated roles.
+
+A new test verifies that config-show works with hidden and configured HTTP
+service.
+
+Remark: The original intent of the sanity check is no longer clear to me. I
+think it was used to very that all services can be started by ipactl.
+Since ipactl starts hidden, configured, and enabled services, the new
+logic reflect the fact, too.
+
+Fixes: https://pagure.io/freeipa/issue/7929
+Signed-off-by: Christian Heimes <cheimes@redhat.com>
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+---
+ ipaserver/servroles.py                     |  9 ++-
+ ipatests/test_integration/test_commands.py | 72 ++++++++++++----------
+ 2 files changed, 45 insertions(+), 36 deletions(-)
+
+diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
+index 0988cbdd7eac599ef26c89a015523b2d92e9502f..984d4bc269fffb4c667e9b8a432b1caf8d3b4f18 100644
+--- a/ipaserver/servroles.py
++++ b/ipaserver/servroles.py
+@@ -346,11 +346,14 @@ class ServerAttribute(LDAPBasedProperty):
+     def _get_assoc_role_providers(self, api_instance):
+         """get list of all servers on which the associated role is enabled
+ 
+-        Consider a hidden server as a valid provider for a role.
++        Consider a hidden and configured server as a valid provider for a
++        role, as all services are started.
+         """
+         return [
+-            r[u'server_server'] for r in self.associated_role.status(
+-                api_instance) if r[u'status'] in {ENABLED, HIDDEN}]
++            r[u'server_server']
++            for r in self.associated_role.status(api_instance)
++            if r[u'status'] in {ENABLED, HIDDEN, CONFIGURED}
++        ]
+ 
+     def _remove(self, api_instance, masters):
+         """
+diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
+index b172a6dd036b5cba4d117f9160ee1ada8712c949..c20213d4b6f0b9d004a8b44afa161a60c538916f 100644
+--- a/ipatests/test_integration/test_commands.py
++++ b/ipatests/test_integration/test_commands.py
+@@ -24,10 +24,15 @@ from ipalib.constants import IPAAPI_USER
+ 
+ from ipaplatform.paths import paths
+ 
++from ipapython.dn import DN
++
++from ipaserver.masters import (
++    CONFIGURED_SERVICE, ENABLED_SERVICE, HIDDEN_SERVICE
++)
++
+ from ipatests.test_integration.base import IntegrationTest
+ from ipatests.pytest_ipa.integration import tasks
+ from ipatests.pytest_ipa.integration.create_external_ca import ExternalCA
+-from ipatests.test_ipalib.test_x509 import good_pkcs7, badcert
+ 
+ logger = logging.getLogger(__name__)
+ 
+@@ -499,36 +504,37 @@ class TestIPACommand(IntegrationTest):
+         assert result.returncode != 0
+         assert 'HBAC rule not found' in result.stderr_text
+ 
+-    def test_ipa_cacert_manage_install(self):
+-        # Re-install the IPA CA
+-        self.master.run_command([
+-            paths.IPA_CACERT_MANAGE,
+-            'install',
+-            paths.IPA_CA_CRT])
+-
+-        # Test a non-existent file
+-        result = self.master.run_command([
+-            paths.IPA_CACERT_MANAGE,
+-            'install',
+-            '/var/run/cert_not_found'], raiseonerr=False)
+-        assert result.returncode == 1
++    def test_config_show_configured_services(self):
++        # https://pagure.io/freeipa/issue/7929
++        states = {CONFIGURED_SERVICE, ENABLED_SERVICE, HIDDEN_SERVICE}
++        dn = DN(
++            ('cn', 'HTTP'), ('cn', self.master.hostname), ('cn', 'masters'),
++            ('cn', 'ipa'), ('cn', 'etc'),
++            self.master.domain.basedn  # pylint: disable=no-member
++        )
+ 
+-        cmd = self.master.run_command(['mktemp'])
+-        filename = cmd.stdout_text.strip()
+-
+-        for contents in (good_pkcs7,):
+-            self.master.put_file_contents(filename, contents)
+-            result = self.master.run_command([
+-                paths.IPA_CACERT_MANAGE,
+-                'install',
+-                filename])
+-
+-        for contents in (badcert,):
+-            self.master.put_file_contents(filename, contents)
+-            result = self.master.run_command([
+-                paths.IPA_CACERT_MANAGE,
+-                'install',
+-                filename], raiseonerr=False)
+-            assert result.returncode == 1
+-
+-        self.master.run_command(['rm', '-f', filename])
++        conn = self.master.ldap_connect()
++        entry = conn.get_entry(dn)  # pylint: disable=no-member
++
++        # original setting and all settings without state
++        orig_cfg = list(entry['ipaConfigString'])
++        other_cfg = [item for item in orig_cfg if item not in states]
++
++        try:
++            # test with hidden
++            cfg = [HIDDEN_SERVICE]
++            cfg.extend(other_cfg)
++            entry['ipaConfigString'] = cfg
++            conn.update_entry(entry)  # pylint: disable=no-member
++            self.master.run_command(['ipa', 'config-show'])
++
++            # test with configured
++            cfg = [CONFIGURED_SERVICE]
++            cfg.extend(other_cfg)
++            entry['ipaConfigString'] = cfg
++            conn.update_entry(entry)  # pylint: disable=no-member
++            self.master.run_command(['ipa', 'config-show'])
++        finally:
++            # reset
++            entry['ipaConfigString'] = orig_cfg
++            conn.update_entry(entry)  # pylint: disable=no-member
+-- 
+2.20.1
+
diff --git a/SOURCES/0049-ipa-cert-fix-handle-pki-server-cert-fix-failure.patch b/SOURCES/0049-ipa-cert-fix-handle-pki-server-cert-fix-failure.patch
new file mode 100644
index 0000000..8b508d5
--- /dev/null
+++ b/SOURCES/0049-ipa-cert-fix-handle-pki-server-cert-fix-failure.patch
@@ -0,0 +1,47 @@
+From 3c1ac4d5c9c36c2b99ac2b1d9d86e46b563b4361 Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Mon, 27 May 2019 10:00:28 +1000
+Subject: [PATCH] ipa-cert-fix: handle 'pki-server cert-fix' failure
+
+When DS cert is expired, 'pki-server cert-fix' will fail at the
+final step (restart).  When this case arises, ignore the
+CalledProcessError and continue.
+
+We can't know for sure if the error was due to failure of final
+restart, or something going wrong earlier.  But if it was a more
+serious failure, the next step (installing the renewed IPA-specific
+certificates) will fail.
+
+Part of: https://pagure.io/freeipa/issue/7885
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ ipaserver/install/ipa_cert_fix.py | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
+index c8ee51faea9092350c8a182ba55387ddd7b196d8..5d5668b1d89115adcda167222ffc38a0caa690a2 100644
+--- a/ipaserver/install/ipa_cert_fix.py
++++ b/ipaserver/install/ipa_cert_fix.py
+@@ -113,7 +113,17 @@ class IPACertFix(AdminTool):
+             return 0
+         print("Proceeding.")
+ 
+-        run_cert_fix(certs, extra_certs)
++        try:
++            run_cert_fix(certs, extra_certs)
++        except ipautil.CalledProcessError:
++            if any(x[0] is IPACertType.LDAPS for x in extra_certs):
++                # The DS cert was expired.  This will cause
++                # 'pki-server cert-fix' to fail at the final
++                # restart.  Therefore ignore the CalledProcessError
++                # and proceed to installing the IPA-specific certs.
++                pass
++            else:
++                raise  # otherwise re-raise
+ 
+         replicate_dogtag_certs(subject_base, ca_subject_dn, certs)
+         install_ipa_certs(subject_base, ca_subject_dn, extra_certs)
+-- 
+2.20.1
+
diff --git a/SOURCES/0049-ipaldap.py-fix-method-creating-a-ldap-filter-for-IPA.patch b/SOURCES/0049-ipaldap.py-fix-method-creating-a-ldap-filter-for-IPA.patch
deleted file mode 100644
index 0959ec1..0000000
--- a/SOURCES/0049-ipaldap.py-fix-method-creating-a-ldap-filter-for-IPA.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 896c438f1dd7e4aa316503fbf68fef13963d7463 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Thu, 22 Nov 2018 18:31:38 +0100
-Subject: [PATCH] ipaldap.py: fix method creating a ldap filter for
- IPACertificate
-
-ipa user-find --certificate and ipa host-find --certificate
-fail to return matching entries, because the method transforming
-the attribute into a LDAP filter does not properly handle
-IPACertificate objects.
-Directory Server logs show a filter with
-(usercertificate=ipalib.x509.IPACertificate object at 0x7fc0a5575b90>)
-
-When the attribute contains a cryptography.x509.Certificate,
-the method needs to extract the public bytes instead of calling str(value).
-
-Fixes https://pagure.io/freeipa/issue/7770
-
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipapython/ipaldap.py | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py
-index 53fdf4967868961effea7f3f64dfb3c0edfc75f3..a44246e3ee0de5a78de77a593718ecad1aaa0f67 100644
---- a/ipapython/ipaldap.py
-+++ b/ipapython/ipaldap.py
-@@ -36,6 +36,7 @@ from six.moves.urllib.parse import urlparse
- # pylint: enable=import-error
- 
- from cryptography import x509 as crypto_x509
-+from cryptography.hazmat.primitives import serialization
- 
- import ldap
- import ldap.sasl
-@@ -1276,6 +1277,8 @@ class LDAPClient(object):
-             ]
-             return cls.combine_filters(flts, rules)
-         elif value is not None:
-+            if isinstance(value, crypto_x509.Certificate):
-+                value = value.public_bytes(serialization.Encoding.DER)
-             if isinstance(value, bytes):
-                 value = binascii.hexlify(value).decode('ascii')
-                 # value[-2:0] is empty string for the initial '\\'
--- 
-2.17.2
-
diff --git a/SOURCES/0050-ipa-cert-fix-fix-spurious-renewal-master-change.patch b/SOURCES/0050-ipa-cert-fix-fix-spurious-renewal-master-change.patch
new file mode 100644
index 0000000..3d80f7a
--- /dev/null
+++ b/SOURCES/0050-ipa-cert-fix-fix-spurious-renewal-master-change.patch
@@ -0,0 +1,36 @@
+From 1952fb1c90b444f7ecb0057451d5deb599df3d5f Mon Sep 17 00:00:00 2001
+From: Fraser Tweedale <ftweedal@redhat.com>
+Date: Mon, 27 May 2019 10:00:32 +1000
+Subject: [PATCH] ipa-cert-fix: fix spurious renewal master change
+
+We only want to become the renewal master if we actually renewed a
+shared certificate.  But there is a bug in the logic; even if the
+only Dogtag certificate to be renewed is the 'sslserver' (a
+non-shared certificate), the renewal master will be reset.  Fix the
+bug.
+
+A static type system would have excluded this bug.
+
+Part of: https://pagure.io/freeipa/issue/7885
+
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ ipaserver/install/ipa_cert_fix.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
+index 5d5668b1d89115adcda167222ffc38a0caa690a2..fff054c230c414fdf10eef1cf1b00697a63e386a 100644
+--- a/ipaserver/install/ipa_cert_fix.py
++++ b/ipaserver/install/ipa_cert_fix.py
+@@ -128,7 +128,7 @@ class IPACertFix(AdminTool):
+         replicate_dogtag_certs(subject_base, ca_subject_dn, certs)
+         install_ipa_certs(subject_base, ca_subject_dn, extra_certs)
+ 
+-        if any(x != 'sslserver' for x in certs) \
++        if any(x[0] != 'sslserver' for x in certs) \
+                 or any(x[0] is IPACertType.IPARA for x in extra_certs):
+             # we renewed a "shared" certificate, therefore we must
+             # become the renewal master
+-- 
+2.20.1
+
diff --git a/SOURCES/0050-ipatests-add-xmlrpc-test-for-user-host-find-certific.patch b/SOURCES/0050-ipatests-add-xmlrpc-test-for-user-host-find-certific.patch
deleted file mode 100644
index 0e5cfd3..0000000
--- a/SOURCES/0050-ipatests-add-xmlrpc-test-for-user-host-find-certific.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-From 489ac5a5da034394c09043d6c26700e4ae049b78 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Fri, 23 Nov 2018 10:23:40 +0100
-Subject: [PATCH] ipatests: add xmlrpc test for user|host-find --certificate
-
-There were no xmlrpc tests for ipa user-find --certificate
-or ipa host-find --certificate.
-The commit adds tests for these commands.
-
-Related to https://pagure.io/freeipa/issue/7770
-
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipatests/test_xmlrpc/test_host_plugin.py |  5 ++++
- ipatests/test_xmlrpc/test_user_plugin.py | 31 ++++++++++++++++++++++++
- 2 files changed, 36 insertions(+)
-
-diff --git a/ipatests/test_xmlrpc/test_host_plugin.py b/ipatests/test_xmlrpc/test_host_plugin.py
-index 8255296d1794bfa19c1f4642bb4bfb9212567b1e..1bcc90b0c48c811356ec93813834d6aa6805a921 100644
---- a/ipatests/test_xmlrpc/test_host_plugin.py
-+++ b/ipatests/test_xmlrpc/test_host_plugin.py
-@@ -251,6 +251,11 @@ class TestCRUD(XMLRPC_test):
-                         valid_not_after=fuzzy_date,
-                     ))
-         host.retrieve()
-+        # test host-find with --certificate
-+        command = host.make_find_command(
-+            fqdn=host.fqdn, usercertificate=host_cert)
-+        res = command()['result']
-+        assert len(res) == 1
- 
-     def test_try_rename(self, host):
-         host.ensure_exists()
-diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py
-index af825f79daf21720e164dd8cd01576167fb440c4..8e54d04bd79888c447368250c3a2e182029a3b44 100644
---- a/ipatests/test_xmlrpc/test_user_plugin.py
-+++ b/ipatests/test_xmlrpc/test_user_plugin.py
-@@ -25,6 +25,7 @@ Test the `ipaserver/plugins/user.py` module.
- """
- 
- import pytest
-+import base64
- import datetime
- import ldap
- import re
-@@ -220,6 +221,36 @@ class TestUser(XMLRPC_test):
-         user.check_update(result)
-         user.delete()
- 
-+    def test_find_cert(self, user):
-+        """ Add a usercertificate and perform a user-find --certificate """
-+        user_cert = (
-+            u"MIICszCCAZugAwIBAgICM24wDQYJKoZIhvcNAQELBQAwIzEUMBIGA1UEChML\r\n"
-+            "RVhBTVBMRS5PUkcxCzAJBgNVBAMTAkNBMB4XDTE3MDExOTEwMjUyOVoXDTE3M\r\n"
-+            "DQxOTEwMjUyOVowFjEUMBIGA1UEAxMLc3RhZ2V1c2VyLTEwggEiMA0GCSqGSI\r\n"
-+            "b3DQEBAQUAA4IBDwAwggEKAoIBAQCq03FRQQBvq4HwYMKP8USLZuOkKzuIs2V\r\n"
-+            "Pt8k/+nO1dADrzMogKDiUDjCwYoG2UM/sj6P+PJUUCNDLh5eRRI+aR5VE5y2a\r\n"
-+            "K95iCsj1ByDWrugAUXgr8GUUr+UbaGc0XxHCMnQBkYhzbXY3u91KYRRh5l3lx\r\n"
-+            "RSICcVeJFJ/tiMS14Vsor1DWykHGz1wm0Zjwg1XDV3oea+uwrSz5Pa6RNPlgC\r\n"
-+            "+GGW6B7+8qC2XdSSEwvY7y1SAGgqyOxN/FLwvqqMDNU0uX7fww587uZ57IfYz\r\n"
-+            "b8Xn5DAprRFNk40FDc46rMlkPBT+Tij1I0jedD8h2e6WEa7JRU6SGToYDbRm4\r\n"
-+            "RL9xAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHqm1jXzYer9oSjYs9qh1jWpM\r\n"
-+            "vTcN+0/z1uuX++Wezh3lG7IzYtypbZNxlXDECyrkUh+9oxzMJqdlZ562ko2br\r\n"
-+            "uK6X5csbbM9uVsUva8NCsPPfZXDhrYaMKFvQGFY4pO3uhFGhccob037VN5Ifm\r\n"
-+            "aKGM8aJ40cw2PQh38QPDdemizyVCThQ9Pcr+WgWKiG+t2Gd9NldJRLEhky0bW\r\n"
-+            "2fc4zWZVbGq5nFXy1k+d/bgkHbVzf255eFZOKKy0NgZwig+uSlhVWPJjS4Z1w\r\n"
-+            "LbpBKxTZp/xD0yEARs0u1ZcCELO/BkgQM50EDKmahIM4mdCs/7j1B/DdWs2i3\r\n"
-+            "5lnbjxYYiUiyA=")
-+        user.ensure_exists()
-+        user.update(dict(usercertificate=user_cert),
-+                    expected_updates=dict(
-+                        usercertificate=[base64.b64decode(user_cert)])
-+                    )
-+        command = user.make_find_command(uid=user.name,
-+                                         usercertificate=user_cert)
-+        res = command()['result']
-+        assert len(res) == 1
-+        user.delete()
-+
- 
- @pytest.mark.tier1
- class TestFind(XMLRPC_test):
--- 
-2.17.2
-
diff --git a/SOURCES/0051-adtrust-upgrade-fix-wrong-primary-principal-name.patch b/SOURCES/0051-adtrust-upgrade-fix-wrong-primary-principal-name.patch
new file mode 100644
index 0000000..b4a6a8b
--- /dev/null
+++ b/SOURCES/0051-adtrust-upgrade-fix-wrong-primary-principal-name.patch
@@ -0,0 +1,97 @@
+From 5b9d7daf27cb10101432e8a25c364dcbe92b37a4 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Tue, 25 Jun 2019 15:22:57 +0300
+Subject: [PATCH] adtrust upgrade: fix wrong primary principal name
+
+Upgrade code had Kerberos principal names mixed up: instead of creating
+krbtgt/LOCAL-FLAT@REMOTE and marking LOCAL-FLAT$@REMOTE as an alias to
+it, it created LOCAL-FLAT$@REMOTE Kerberos principal and marked
+krbtgt/LOCAL-FLAT@REMOTE as an alias.
+
+This differs from what Active Directory expects and what is created by
+ipasam plugin when trust is established. When upgrading such deployment,
+an upgrade code then unexpectedly failed.
+
+Resolves: https://pagure.io/freeipa/issue/7992
+Reviewed-By: Christian Heimes <cheimes@redhat.com>
+---
+ daemons/ipa-sam/ipa_sam.c                        | 12 +++++++-----
+ .../adtrust/oneway-trust-with-shared-secret.md   | 16 +++++++++-------
+ ipaserver/install/plugins/adtrust.py             |  4 ++--
+ 3 files changed, 18 insertions(+), 14 deletions(-)
+
+diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c
+index 3cf878c3f99774f7715f776c31d70e2950f9451c..2251f3ddcd9d5549d12b3e411245b00395c6b0d9 100644
+--- a/daemons/ipa-sam/ipa_sam.c
++++ b/daemons/ipa-sam/ipa_sam.c
+@@ -2011,11 +2011,13 @@ static bool handle_cross_realm_princs(struct ipasam_private *ipasam_state,
+ 							 pwd_outgoing, trusted_dn,
+ 							 KRB_PRINC_CREATE_DEFAULT);
+ 
+-				/* Second: <OUR FLATNAME$>@<REMOTE REALM> is only used
+-				 * for SSSD to be able to talk to AD DCs but it has to
+-				 * have canonical name set to <OUR FLATNAME>$ because
+-				 * this is the salt used by AD DCs when using this
+-				 * principal, otherwise authentication will fail.
++				/* Second: krbtgt/<OUR FLATNAME>@<REMOTE REALM>
++				 * is only used for SSSD to be able to talk to
++				 * AD DCs but it has to have canonical name set
++				 * to krbtgt/<OUR FLATNAME> and alias it to
++				 * <OUR FLATNAME$> because it is the salt used
++				 * by AD DCs when using this principal,
++				 * otherwise authentication will fail.
+ 				 *
+ 				 * *disable* use of this principal on our side as it is
+ 				 * only used to retrieve trusted domain credentials by
+diff --git a/doc/designs/adtrust/oneway-trust-with-shared-secret.md b/doc/designs/adtrust/oneway-trust-with-shared-secret.md
+index dc58a08941acea447f9234107ebcba775351089e..09a940e34bb43a6e46beb85392c94423d2bfccd3 100644
+--- a/doc/designs/adtrust/oneway-trust-with-shared-secret.md
++++ b/doc/designs/adtrust/oneway-trust-with-shared-secret.md
+@@ -131,16 +131,18 @@ and `LOCAL-FLAT` is the NetBIOS name of the FreeIPA primary domain (e.g.
+   REMOTE-FLAT$@LOCAL | Trusted domain object account for the Active Directory forest root domain
+   krbtgt/REMOTE-FLAT@LOCAL | Alias to REMOTE-FLAT$ TDO
+   krbtgt/LOCAL@REMOTE | Cross-realm principal representing IPA domain in Active Directory forest to allow crross-realm TGT issuance from IPA KDC side
+-  LOCAL-FLAT$@REMOTE | Trusted domain object account for IPA domain in Active Directory forest
+-  krbtgt/LOCAL-FLAT@REMOTE | Alias to LOCAL-FLAT$
++  krbtgt/LOCAL-FLAT@REMOTE | Trusted domain object account for IPA domain in Active Directory forest
++  LOCAL-FLAT$@REMOTE | Alias to krbtgt/LOCAL-FLAT@REMOTE
+ 
+ For inbound trust `ipasam` module creates following principals:
+   * `krbtgt/LOCAL@REMOTE`, enabled by default
+-  * `LOCAL-FLAT$@REMOTE`, used by SSSD to talk to Active Directory domain
+-    controllers, with canonical name set to `LOCAL-FLAT$` because Kerberos KDC
+-    must use this salt when issuing tickets for this principal. The use of this
+-    principal is disabled on IPA side (IPA KDC does not issue tickets in this name)
+-    --- we only retrieve a keytab for the principal in SSSD.
++  * `krbtgt/LOCAL-FLAT@REMOTE`, used by SSSD to talk to Active Directory domain
++    controllers, with canonical name set to `krbtgt/LOCAL-FLAT@REMOTE` because
++    Kerberos KDC must use this salt when issuing tickets for this principal. The
++    use of this principal is disabled on IPA side (IPA KDC does not issue tickets
++    in this name) --- we only retrieve a keytab for the principal in SSSD. SSSD
++    retrieves a keytab for this principal using `LOCAL-FLAT$@REMOTE` Principal
++    name.
+ 
+ For outbound trust `ipasam` module creates following principals:
+   * `krbtgt/REMOTE@LOCAL`, enabled by default.
+diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
+index 55df5cd01fd0f585d5955e700ccf20c7fc9a747f..f810522b236d8c04f4a417aac8fd3717563c358e 100644
+--- a/ipaserver/install/plugins/adtrust.py
++++ b/ipaserver/install/plugins/adtrust.py
+@@ -680,12 +680,12 @@ class update_tdo_to_new_layout(Updater):
+                     trust_principal, t_realm)
+                 continue
+ 
+-            # 4. Create <OUR FLATNAME$>@<REMOTE REALM>, disabled
++            # 4. Create krbtgt/<OUR FLATNAME>@<REMOTE REALM>, disabled
+             nbt_principal = self.nbt_principal_template.format(
+                 nbt=our_nbt_name, realm=t_realm)
+             tgt_principal = self.tgt_principal_template.format(
+                 remote=our_nbt_name, local=t_realm)
+-            self.set_krb_principal([nbt_principal, tgt_principal],
++            self.set_krb_principal([tgt_principal, nbt_principal],
+                                    passwd_incoming,
+                                    t_dn,
+                                    flags=self.KRB_PRINC_CREATE_DEFAULT |
+-- 
+2.20.1
+
diff --git a/SOURCES/0051-ipa-upgrade-handle-double-encoded-certificates.patch b/SOURCES/0051-ipa-upgrade-handle-double-encoded-certificates.patch
deleted file mode 100644
index 193aaa0..0000000
--- a/SOURCES/0051-ipa-upgrade-handle-double-encoded-certificates.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From 086611271c4dfbbf47e76e666142327bf950a9ca Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Mon, 26 Nov 2018 14:15:12 +0100
-Subject: [PATCH] ipa upgrade: handle double-encoded certificates
-
-Issue is linked to the ticket
- #3477 LDAP upload CA cert sometimes double-encodes the value
-In old FreeIPA releases (< 3.2), the upgrade plugin was encoding twice
-the value of the certificate in cn=cacert,cn=ipa,cn=etc,$BASEDN.
-
-The fix for 3477 is only partial as it prevents double-encoding when a
-new cert is uploaded but does not fix wrong values already present in LDAP.
-
-With this commit, the code first tries to read a der cert. If it fails,
-it logs a debug message and re-writes the value caCertificate;binary
-to repair the entry.
-
-Fixes https://pagure.io/freeipa/issue/7775
-Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/plugins/upload_cacrt.py | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
-diff --git a/ipaserver/install/plugins/upload_cacrt.py b/ipaserver/install/plugins/upload_cacrt.py
-index 68d43caa76eb67093745658d20a39700adbd16c6..dc58f0863182ccb92d9fed6aa5f1c2546404b598 100644
---- a/ipaserver/install/plugins/upload_cacrt.py
-+++ b/ipaserver/install/plugins/upload_cacrt.py
-@@ -115,7 +115,18 @@ class update_upload_cacrt(Updater):
-                 entry.single_value['cACertificate;binary'] = ca_cert
-                 ldap.add_entry(entry)
-             else:
--                if b'' in entry['cACertificate;binary']:
-+                force_write = False
-+                try:
-+                    _cert_bin = entry['cACertificate;binary']
-+                except ValueError:
-+                    # BZ 1644874
-+                    # sometimes the cert is badly stored, twice encoded
-+                    # force write to fix the value
-+                    logger.debug('Fixing the value of cACertificate;binary '
-+                                 'in entry %s', entry.dn)
-+                    force_write = True
-+
-+                if force_write or b'' in entry['cACertificate;binary']:
-                     entry.single_value['cACertificate;binary'] = ca_cert
-                     ldap.update_entry(entry)
- 
--- 
-2.17.2
-
diff --git a/SOURCES/0052-adtrust-upgrade-fix-wrong-primary-principal-name-par.patch b/SOURCES/0052-adtrust-upgrade-fix-wrong-primary-principal-name-par.patch
new file mode 100644
index 0000000..62e2acc
--- /dev/null
+++ b/SOURCES/0052-adtrust-upgrade-fix-wrong-primary-principal-name-par.patch
@@ -0,0 +1,95 @@
+From ca4e0582489a432a1f61fc75a27ef831e911f0fe Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Thu, 27 Jun 2019 11:56:08 +0300
+Subject: [PATCH] adtrust upgrade: fix wrong primary principal name, part 2
+
+Second part of the trust principals upgrade
+
+For existing LOCAL-FLAT$@REMOTE object, convert it to
+krbtgt/LOCAL-FLAT@REMOTE and add LOCAL-FLAT$@REMOTE as an alias. To do
+so we need to modify an entry content a bit so it is better to remove
+the old entry and create a new one instead of renaming.
+
+Resolves: https://pagure.io/freeipa/issue/7992
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ ipaserver/install/plugins/adtrust.py | 36 +++++++++++++++++++++++-----
+ 1 file changed, 30 insertions(+), 6 deletions(-)
+
+diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
+index f810522b236d8c04f4a417aac8fd3717563c358e..12596d5bfe71c16a2cb87acb755a88051676e3e5 100644
+--- a/ipaserver/install/plugins/adtrust.py
++++ b/ipaserver/install/plugins/adtrust.py
+@@ -513,16 +513,19 @@ class update_tdo_to_new_layout(Updater):
+ 
+         if isinstance(principals, (list, tuple)):
+             trust_principal = principals[0]
+-            aliases = principals[1:]
++            alias = principals[1]
+         else:
+             trust_principal = principals
+-            aliases = []
++            alias = None
+ 
++        entry = None
++        en = None
+         try:
+             entry = ldap.get_entry(
+                 DN(('krbprincipalname', trust_principal), trustdn))
+             dn = entry.dn
+             action = ldap.update_entry
++            ticket_flags = int(entry.single_value.get('krbticketflags', 0))
+             logger.debug("Updating Kerberos principal entry for %s",
+                          trust_principal)
+         except errors.NotFound:
+@@ -531,6 +534,19 @@ class update_tdo_to_new_layout(Updater):
+             if flags & self.KRB_PRINC_MUST_EXIST:
+                 raise
+ 
++            ticket_flags = 0
++            if alias:
++                try:
++                    en = ldap.get_entry(
++                        DN(('krbprincipalname', alias), trustdn))
++                    ldap.delete_entry(en.dn)
++                    ticket_flags = int(en.single_value.get(
++                        'krbticketflags', 0))
++                except errors.NotFound:
++                    logger.debug("Entry for alias TDO does not exist for "
++                                 "trusted domain object %s, skip it",
++                                 alias)
++
+             dn = DN(('krbprincipalname', trust_principal), trustdn)
+             entry = ldap.make_entry(dn)
+             logger.debug("Adding Kerberos principal entry for %s",
+@@ -545,15 +561,23 @@ class update_tdo_to_new_layout(Updater):
+             'krbprincipalname': [trust_principal],
+         }
+ 
+-        entry_data['krbprincipalname'].extend(aliases)
+-
+         if flags & self.KRB_PRINC_CREATE_DISABLED:
+-            flg = int(entry.single_value.get('krbticketflags', 0))
+-            entry_data['krbticketflags'] = flg | self.KRB_DISALLOW_ALL_TIX
++            entry_data['krbticketflags'] = (ticket_flags |
++                                            self.KRB_DISALLOW_ALL_TIX)
+ 
+         if flags & self.KRB_PRINC_CREATE_AGENT_PERMISSION:
+             entry_data['objectclass'].extend(['ipaAllowedOperations'])
+ 
++        if alias:
++            entry_data['krbprincipalname'].extend([alias])
++            if en:
++                entry_data['krbprincipalkey'] = en.single_value.get(
++                    'krbprincipalkey')
++                entry_data['krbextradata'] = en.single_value.get(
++                    'krbextradata')
++                entry_data['ipaAllowedToPerform;read_keys'] = en.get(
++                    'ipaAllowedToPerform;read_keys', [])
++
+         entry.update(entry_data)
+         try:
+             action(entry)
+-- 
+2.20.1
+
diff --git a/SOURCES/0052-ipatests-add-upgrade-test-for-double-encoded-cacert.patch b/SOURCES/0052-ipatests-add-upgrade-test-for-double-encoded-cacert.patch
deleted file mode 100644
index 874c3f2..0000000
--- a/SOURCES/0052-ipatests-add-upgrade-test-for-double-encoded-cacert.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-From 57a473bd41fbd3520871dbd7ed7dc9524946a48e Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Thu, 29 Nov 2018 15:41:33 +0100
-Subject: [PATCH] ipatests: add upgrade test for double-encoded cacert
-
-Create a test for upgrade with the following scenario:
-- install master
-- write a double-encoded cert in the entry
-cn=cacert,,cn=ipa,cn=etc,$basedn
-to simulate bug 7775
-- call ipa-server-upgrade
-- check that the upgrade fixed the value
-
-The upgrade should finish successfully and repair
-the double-encoded cert.
-
-Related to https://pagure.io/freeipa/issue/7775
-
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipatests/test_integration/test_upgrade.py | 35 +++++++++++++++++++++++
- 1 file changed, 35 insertions(+)
-
-diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py
-index 951747b0b37cd62459a241255190baebdf0f728a..7dbe52d57052d3c640df644705fc3e22fab14334 100644
---- a/ipatests/test_integration/test_upgrade.py
-+++ b/ipatests/test_integration/test_upgrade.py
-@@ -6,6 +6,9 @@
- Module provides tests to verify that the upgrade script works.
- """
- 
-+import base64
-+from cryptography.hazmat.primitives import serialization
-+from ipapython.dn import DN
- from ipatests.test_integration.base import IntegrationTest
- from ipatests.pytest_plugins.integration import tasks
- 
-@@ -19,3 +22,35 @@ class TestUpgrade(IntegrationTest):
-         cmd = self.master.run_command(['ipa-server-upgrade'],
-                                       raiseonerr=False)
-         assert cmd.returncode == 0
-+
-+    def test_double_encoded_cacert(self):
-+        """Test for BZ 1644874
-+
-+        In old IPA version, the entry cn=CAcert,cn=ipa,cn=etc,$basedn
-+        could contain a double-encoded cert, which leads to ipa-server-upgrade
-+        failure.
-+        Force a double-encoded value then call upgrade to check the fix.
-+        """
-+        # Read the current entry from LDAP
-+        ldap = self.master.ldap_connect()
-+        basedn = self.master.domain.basedn  # pylint: disable=no-member
-+        dn = DN(('cn', 'CAcert'), ('cn', 'ipa'), ('cn', 'etc'), basedn)
-+        entry = ldap.get_entry(dn)  # pylint: disable=no-member
-+        # Extract the certificate as DER then double-encode
-+        cacert = entry['cacertificate;binary'][0]
-+        cacert_der = cacert.public_bytes(serialization.Encoding.DER)
-+        cacert_b64 = base64.b64encode(cacert_der)
-+        # overwrite the value with double-encoded cert
-+        entry.single_value['cACertificate;binary'] = cacert_b64
-+        ldap.update_entry(entry)  # pylint: disable=no-member
-+
-+        # try the upgrade
-+        self.master.run_command(['ipa-server-upgrade'])
-+
-+        # read the value after upgrade, should be fixed
-+        entry = ldap.get_entry(dn)  # pylint: disable=no-member
-+        try:
-+            _cacert = entry['cacertificate;binary']
-+        except ValueError:
-+            raise AssertionError('%s contains a double-encoded cert'
-+                                 % entry.dn)
--- 
-2.17.2
-
diff --git a/SOURCES/0053-ipatests-fix-TestUpgrade-test_double_encoded_cacert.patch b/SOURCES/0053-ipatests-fix-TestUpgrade-test_double_encoded_cacert.patch
deleted file mode 100644
index ee850b4..0000000
--- a/SOURCES/0053-ipatests-fix-TestUpgrade-test_double_encoded_cacert.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 840f9cfe17737c9ef1899b9923682a5df53ff4b6 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Tue, 4 Dec 2018 16:44:54 +0100
-Subject: [PATCH] ipatests: fix TestUpgrade::test_double_encoded_cacert
-
-The test is using a stale ldap connection to the master
-(obtained before calling upgrade, and the upgrade stops
-and starts 389-ds, breaking the connection).
-
-The fix re-connects before using the ldap handle.
-
-Related to https://pagure.io/freeipa/issue/7775
----
- ipatests/test_integration/test_upgrade.py | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py
-index 7dbe52d57052d3c640df644705fc3e22fab14334..b03109f7c3bb0f037c8fd6554e3e5420bc557684 100644
---- a/ipatests/test_integration/test_upgrade.py
-+++ b/ipatests/test_integration/test_upgrade.py
-@@ -47,6 +47,8 @@ class TestUpgrade(IntegrationTest):
-         # try the upgrade
-         self.master.run_command(['ipa-server-upgrade'])
- 
-+        # reconnect to the master (upgrade stops 389-ds)
-+        ldap = self.master.ldap_connect()
-         # read the value after upgrade, should be fixed
-         entry = ldap.get_entry(dn)  # pylint: disable=no-member
-         try:
--- 
-2.17.2
-
diff --git a/SOURCES/0053-trust-fetch-domains-make-sure-we-use-right-KDC-when-.patch b/SOURCES/0053-trust-fetch-domains-make-sure-we-use-right-KDC-when-.patch
new file mode 100644
index 0000000..781d9b6
--- /dev/null
+++ b/SOURCES/0053-trust-fetch-domains-make-sure-we-use-right-KDC-when-.patch
@@ -0,0 +1,441 @@
+From a9ff7e93de94de1c7ecaedce582d598d6a4ad30e Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Thu, 27 Jun 2019 15:17:07 +0300
+Subject: [PATCH] trust-fetch-domains: make sure we use right KDC when --server
+ is specified
+
+Since we are authenticating against AD DC before talking to it (by using
+trusted domain object's credentials), we need to override krb5.conf
+configuration in case --server option is specified.
+
+The context is a helper which is launched out of process with the help
+of oddjobd. The helper takes existing trusted domain object, uses its
+credentials to authenticate and then runs LSA RPC calls against that
+trusted domain's domain controller. Previous code directed Samba
+bindings to use the correct domain controller. However, if a DC visible
+to MIT Kerberos is not reachable, we would not be able to obtain TGT and
+the whole process will fail.
+
+trust_add.execute() was calling out to the D-Bus helper without passing
+the options (e.g. --server) so there was no chance to get that option
+visible by the oddjob helper.
+
+Also we need to make errors in the oddjob helper more visible to
+error_log. Thus, move error reporting for a normal communication up from
+the exception catching.
+
+Resolves: https://pagure.io/freeipa/issue/7895
+Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
+Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
+---
+ .../oddjob/com.redhat.idm.trust-fetch-domains | 233 +++++++++++++-----
+ ipaserver/plugins/trust.py                    |  28 ++-
+ 2 files changed, 194 insertions(+), 67 deletions(-)
+
+diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
+index 029de781b2a1f94b1fd281b79aa69a1e9422dede..b406201ec2baf8ee9398fc3634060b6d9a5392b0 100755
+--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
++++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
+@@ -8,9 +8,12 @@ from ipapython.dn import DN
+ from ipapython.dnsutil import DNSName
+ from ipaplatform.constants import constants
+ from ipaplatform.paths import paths
++import io
+ import sys
+ import os
+ import pwd
++import tempfile
++import textwrap
+ 
+ import six
+ import gssapi
+@@ -23,17 +26,38 @@ if six.PY3:
+ 
+ def parse_options():
+     usage = "%prog <trusted domain name>\n"
+-    parser = config.IPAOptionParser(usage=usage,
+-                                    formatter=config.IPAFormatter())
+-
+-    parser.add_option("-d", "--debug", action="store_true", dest="debug",
+-                      help="Display debugging information")
+-    parser.add_option("-s", "--server", action="store", dest="server",
+-                      help="Domain controller for the Active Directory domain (optional)")
+-    parser.add_option("-a", "--admin", action="store", dest="admin",
+-                      help="Active Directory administrator (optional)")
+-    parser.add_option("-p", "--password", action="store", dest="password",
+-                      help="Display debugging information")
++    parser = config.IPAOptionParser(
++        usage=usage, formatter=config.IPAFormatter()
++    )
++
++    parser.add_option(
++        "-d",
++        "--debug",
++        action="store_true",
++        dest="debug",
++        help="Display debugging information",
++    )
++    parser.add_option(
++        "-s",
++        "--server",
++        action="store",
++        dest="server",
++        help="Domain controller for the Active Directory domain (optional)",
++    )
++    parser.add_option(
++        "-a",
++        "--admin",
++        action="store",
++        dest="admin",
++        help="Active Directory administrator (optional)",
++    )
++    parser.add_option(
++        "-p",
++        "--password",
++        action="store",
++        dest="password",
++        help="Display debugging information",
++    )
+ 
+     options, args = parser.parse_args()
+     safe_options = parser.get_safe_opts(options)
+@@ -50,17 +74,26 @@ def parse_options():
+         raise ScriptError("You must specify a valid trusted domain name", 2)
+     return safe_options, options, trusted_domain
+ 
++
+ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
+-    getkeytab_args = ["/usr/sbin/ipa-getkeytab",
+-                      "-s", api.env.host,
+-                      "-p", oneway_principal,
+-                      "-k", oneway_keytab_name,
+-                      "-r"]
++    getkeytab_args = [
++        "/usr/sbin/ipa-getkeytab",
++        "-s",
++        api.env.host,
++        "-p",
++        oneway_principal,
++        "-k",
++        oneway_keytab_name,
++        "-r",
++    ]
+     if os.path.isfile(oneway_keytab_name):
+         os.unlink(oneway_keytab_name)
+ 
+-    ipautil.run(getkeytab_args, env={'KRB5CCNAME': ccache_name, 'LANG': 'C'},
+-                raiseonerr=False)
++    ipautil.run(
++        getkeytab_args,
++        env={"KRB5CCNAME": ccache_name, "LANG": "C"},
++        raiseonerr=False,
++    )
+     # Make sure SSSD is able to read the keytab
+     try:
+         sssd = pwd.getpwnam(constants.SSSD_USER)
+@@ -72,8 +105,7 @@ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
+ 
+ 
+ def get_forest_root_domain(api_instance, trusted_domain, server=None):
+-    """
+-    retrieve trusted forest root domain for given domain name
++    """Retrieve trusted forest root domain for given domain name
+ 
+     :param api_instance: IPA API instance
+     :param trusted_domain: trusted domain name
+@@ -81,18 +113,51 @@ def get_forest_root_domain(api_instance, trusted_domain, server=None):
+     :returns: forest root domain DNS name
+     """
+     trustconfig_show = api_instance.Command.trustconfig_show
+-    flatname = trustconfig_show()['result']['ipantflatname'][0]
++    flatname = trustconfig_show()["result"]["ipantflatname"][0]
+ 
+     remote_domain = dcerpc.retrieve_remote_domain(
+-        api_instance.env.host, flatname, trusted_domain,
+-        realm_server=server)
++        api_instance.env.host, flatname, trusted_domain, realm_server=server
++    )
++
++    return remote_domain.info["dns_forest"]
++
++
++def generate_krb5_config(realm, server):
++    """Generate override krb5 config file for trusted domain DC access
++
++    :param realm: realm of the trusted AD domain
++    :param server: server to override KDC to
++
++    :returns: tuple (temporary config file name, KRB5_CONFIG string)
++    """
++    cfg = paths.KRB5_CONF
++    tcfg = None
++    if server:
++        content = textwrap.dedent(u"""
++            [realms]
++               %s = {
++                   kdc = %s
++               }
++            """) % (
++            realm.upper(),
++            server,
++        )
++
++        (fd, tcfg) = tempfile.mkstemp(dir="/var/run/ipa",
++                prefix="krb5conf", text=True)
++        with io.open(fd, mode='w', encoding='utf-8') as o:
++            o.write(content)
++        cfg = ":".join([tcfg, cfg])
++    return (tcfg, cfg)
+ 
+-    return remote_domain.info['dns_forest']
+ 
+ if not is_ipa_configured():
+     # LSB status code 6: program is not configured
+-    raise ScriptError("IPA is not configured " +
+-                      "(see man pages of ipa-server-install for help)", 6)
++    raise ScriptError(
++        "IPA is not configured "
++        + "(see man pages of ipa-server-install for help)",
++        6,
++    )
+ 
+ if not os.getegid() == 0:
+     # LSB status code 4: user had insufficient privilege
+@@ -100,8 +165,9 @@ if not os.getegid() == 0:
+ 
+ safe_options, options, trusted_domain = parse_options()
+ 
+-api.bootstrap(in_server=True, log=None,
+-              context='server', confdir=paths.ETC_IPA)
++api.bootstrap(
++    in_server=True, log=None, context="server", confdir=paths.ETC_IPA
++)
+ api.finalize()
+ 
+ # Only import trust plugin after api is initialized or internal imports
+@@ -121,12 +187,12 @@ from ipaserver.plugins import trust
+ # and retrieve our own NetBIOS domain name and use cifs/ipa.master@IPA.REALM to
+ # retrieve the keys to oneway_keytab_name.
+ 
+-keytab_name = '/etc/samba/samba.keytab'
++keytab_name = "/etc/samba/samba.keytab"
+ 
+-principal = str('cifs/' + api.env.host)
++principal = str("cifs/" + api.env.host)
+ 
+-oneway_ccache_name = '/var/run/ipa/krb5cc_oddjob_trusts_fetch'
+-ccache_name = '/var/run/ipa/krb5cc_oddjob_trusts'
++oneway_ccache_name = "/var/run/ipa/krb5cc_oddjob_trusts_fetch"
++ccache_name = "/var/run/ipa/krb5cc_oddjob_trusts"
+ 
+ # Standard sequence:
+ # - check if ccache exists
+@@ -140,33 +206,45 @@ try:
+     cred = kinit_keytab(principal, keytab_name, ccache_name)
+     if cred.lifetime > 0:
+         have_ccache = True
+-except gssapi.exceptions.ExpiredCredentialsError:
++except (gssapi.exceptions.ExpiredCredentialsError, gssapi.raw.misc.GSSError):
+     pass
+ if not have_ccache:
+     # delete stale ccache and try again
+-    if os.path.exists(oneway_ccache_name):
++    if os.path.exists(ccache_name):
+         os.unlink(ccache_name)
+     cred = kinit_keytab(principal, keytab_name, ccache_name)
+ 
+-old_ccache = os.environ.get('KRB5CCNAME')
++old_ccache = os.environ.get("KRB5CCNAME")
++old_config = os.environ.get("KRB5_CONFIG")
+ api.Backend.ldap2.connect(ccache_name)
+ 
+ # Retrieve own NetBIOS name and trusted forest's name.
+ # We use script's input to retrieve the trusted forest's name to sanitize input
+ # for file-level access as we might need to wipe out keytab in /var/lib/sss/keytabs
+-own_trust_dn = DN(('cn', api.env.domain),('cn','ad'), ('cn', 'etc'), api.env.basedn)
+-own_trust_entry = api.Backend.ldap2.get_entry(own_trust_dn, ['ipantflatname'])
+-own_trust_flatname = own_trust_entry.single_value.get('ipantflatname').upper()
+-trusted_domain_dn = DN(('cn', trusted_domain.lower()), api.env.container_adtrusts, api.env.basedn)
+-trusted_domain_entry = api.Backend.ldap2.get_entry(trusted_domain_dn, ['cn'])
+-trusted_domain = trusted_domain_entry.single_value.get('cn').lower()
++own_trust_dn = DN(
++    ("cn", api.env.domain), ("cn", "ad"), ("cn", "etc"), api.env.basedn
++)
++own_trust_entry = api.Backend.ldap2.get_entry(own_trust_dn, ["ipantflatname"])
++own_trust_flatname = own_trust_entry.single_value.get("ipantflatname").upper()
++trusted_domain_dn = DN(
++    ("cn", trusted_domain.lower()), api.env.container_adtrusts, api.env.basedn
++)
++trusted_domain_entry = api.Backend.ldap2.get_entry(trusted_domain_dn, ["cn"])
++trusted_domain = trusted_domain_entry.single_value.get("cn").lower()
+ 
+ # At this point if we didn't find trusted forest name, an exception will be raised
+ # and script will quit. This is actually intended.
+ 
++# Generate MIT Kerberos configuration file that potentially overlays
++# the KDC to connect to for a trusted domain to allow --server option
++# to take precedence.
++cfg_file, cfg = generate_krb5_config(trusted_domain, options.server)
++
+ if not (options.admin and options.password):
+-    oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
+-    oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
++    oneway_keytab_name = "/var/lib/sss/keytabs/" + trusted_domain + ".keytab"
++    oneway_principal = str(
++        "%s$@%s" % (own_trust_flatname, trusted_domain.upper())
++    )
+ 
+     # If keytab does not exist, retrieve it
+     if not os.path.isfile(oneway_keytab_name):
+@@ -176,39 +254,78 @@ if not (options.admin and options.password):
+         have_ccache = False
+         try:
+             # The keytab may have stale key material (from older trust-add run)
+-            cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
++            cred = kinit_keytab(
++                oneway_principal,
++                oneway_keytab_name,
++                oneway_ccache_name,
++                config=cfg,
++            )
+             if cred.lifetime > 0:
+                 have_ccache = True
+-        except gssapi.exceptions.ExpiredCredentialsError:
++        except (gssapi.exceptions.ExpiredCredentialsError, gssapi.raw.misc.GSSError):
+             pass
+         if not have_ccache:
+             if os.path.exists(oneway_ccache_name):
+                 os.unlink(oneway_ccache_name)
+-            kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
+-    except gssapi.exceptions.GSSError:
++            kinit_keytab(
++                oneway_principal,
++                oneway_keytab_name,
++                oneway_ccache_name,
++                config=cfg,
++            )
++    except (gssapi.exceptions.GSSError, gssapi.raw.misc.GSSError):
+         # If there was failure on using keytab, assume it is stale and retrieve again
+         retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
+         if os.path.exists(oneway_ccache_name):
+             os.unlink(oneway_ccache_name)
+-        kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
++        cred = kinit_keytab(
++            oneway_principal,
++            oneway_keytab_name,
++            oneway_ccache_name,
++            config=cfg,
++        )
+ else:
+-    cred = kinit_password(options.admin, options.password,
+-                          oneway_ccache_name,
+-                          canonicalize=True, enterprise=True)
++    cred = kinit_password(
++        options.admin,
++        options.password,
++        oneway_ccache_name,
++        canonicalize=True,
++        enterprise=True,
++        config=cfg,
++    )
++
++if cred and cred.lifetime > 0:
++    have_ccache = True
++
++if not have_ccache:
++    sys.exit(1)
+ 
+ # We are done: we have ccache with TDO credentials and can fetch domains
+ ipa_domain = api.env.domain
+-os.environ['KRB5CCNAME'] = oneway_ccache_name
++os.environ["KRB5CCNAME"] = oneway_ccache_name
++os.environ["KRB5_CONFIG"] = cfg
+ 
+ # retrieve the forest root domain name and contact it to retrieve trust
+ # topology info
+-forest_root = get_forest_root_domain(api, trusted_domain, server=options.server)
+-
+-domains = dcerpc.fetch_domains(api, ipa_domain, forest_root, creds=True, server=options.server)
+-trust_domain_object = api.Command.trust_show(trusted_domain, raw=True)['result']
+-trust.add_new_domains_from_trust(api, None, trust_domain_object, domains)
++forest_root = get_forest_root_domain(
++    api, trusted_domain, server=options.server
++)
++domains = dcerpc.fetch_domains(
++    api, ipa_domain, forest_root, creds=True, server=options.server
++)
+ 
+ if old_ccache:
+-   os.environ['KRB5CCNAME'] = old_ccache
++    os.environ["KRB5CCNAME"] = old_ccache
++
++if old_config:
++    os.environ["KRB5_CONFIG"] = old_config
++
++if cfg_file:
++    os.remove(cfg_file)
++
++trust_domain_object = api.Command.trust_show(trusted_domain, raw=True)[
++    "result"
++]
++trust.add_new_domains_from_trust(api, None, trust_domain_object, domains)
+ 
+ sys.exit(0)
+diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
+index 5de363bda6fdee081d3e4c98e731cecfa9585d21..676b5f645a99c40a0c944096d7c6252a4c56f983 100644
+--- a/ipaserver/plugins/trust.py
++++ b/ipaserver/plugins/trust.py
+@@ -424,11 +424,11 @@ def fetch_trusted_domains_over_dbus(myapi, *keys, **options):
+ 
+     forest_name = keys[0]
+     method_options = []
+-    if 'realm_server' in options:
++    if options.get('realm_server', None):
+         method_options.extend(['--server', options['realm_server']])
+-    if 'realm_admin' in options:
++    if options.get('realm_admin', None):
+         method_options.extend(['--admin', options['realm_admin']])
+-    if 'realm_passwd' in options:
++    if options.get('realm_passwd', None):
+         method_options.extend(['--password', options['realm_passwd']])
+ 
+     # Calling oddjobd-activated service via DBus has some quirks:
+@@ -458,11 +458,15 @@ def fetch_trusted_domains_over_dbus(myapi, *keys, **options):
+     except dbus.DBusException as e:
+         logger.error('Failed to call %s.fetch_domains helper.'
+                      'DBus exception is %s.', DBUS_IFACE_TRUST, str(e))
+-        if _ret != 0:
+-            logger.error('Helper was called for forest %s, return code is %d',
+-                         forest_name, _ret)
+-            logger.error('Standard output from the helper:\n%s---\n', _stdout)
+-            logger.error('Error output from the helper:\n%s--\n', _stderr)
++        _ret = 2
++        _stdout = '<not available>'
++        _stderr = '<not available>'
++
++    if _ret != 0:
++        logger.error('Helper fetch_domains was called for forest %s, '
++                     'return code is %d', forest_name, _ret)
++        logger.error('Standard output from the helper:\n%s---\n', _stdout)
++        logger.error('Error output from the helper:\n%s--\n', _stderr)
+         raise errors.ServerCommandError(
+             server=myapi.env.host,
+             error=_('Fetching domains from trusted forest failed. '
+@@ -801,7 +805,13 @@ ipa idrange-del before retrying the command with the desired range type.
+                 # object credentials to authenticate to AD with Kerberos,
+                 # run DCE RPC calls to do discovery and will call
+                 # add_new_domains_from_trust() on its own.
+-                fetch_trusted_domains_over_dbus(self.api, result['value'])
++                # We only pass through the realm_server option because we need
++                # to reach the specified Active Directory domain controller
++                # No need to pass through admin credentials as we have TDO
++                # credentials at this point already
++                fetch_trusted_domains_over_dbus(self.api, result['value'],
++                                                realm_server=options.get(
++                                                    'realm_server', None))
+ 
+         # Format the output into human-readable values unless `--raw` is given
+         self._format_trust_attrs(result, **options)
+-- 
+2.20.1
+
diff --git a/SOURCES/0054-ipatest-add-test-for-ipa-pkinit-manage-enable-disabl.patch b/SOURCES/0054-ipatest-add-test-for-ipa-pkinit-manage-enable-disabl.patch
deleted file mode 100644
index 8dd6196..0000000
--- a/SOURCES/0054-ipatest-add-test-for-ipa-pkinit-manage-enable-disabl.patch
+++ /dev/null
@@ -1,145 +0,0 @@
-From 3e0e8c309c70a0d379b985189c23f1bacd62a96e Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Fri, 30 Nov 2018 15:46:25 +0100
-Subject: [PATCH] ipatest: add test for ipa-pkinit-manage enable|disable
-
-Add a test for ipa-pkinit-manage with the following scenario:
-- install master with option --no-pkinit
-- call ipa-pkinit-manage enable
-- call ipa-pkinit-manage disable
-- call ipa-pkinit-manage enable
-
-At each step, check that the PKINIT cert is consistent with the
-expectations: when pkinit is enabled, the cert is signed by IPA
-CA and tracked by 'IPA' ca helper, but when pkinit is disabled,
-the cert is self-signed and tracked by 'SelfSign' CA helper.
-
-Related to https://pagure.io/freeipa/issue/7200
-
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- .../test_integration/test_pkinit_manage.py    | 111 ++++++++++++++++++
- 1 file changed, 111 insertions(+)
- create mode 100644 ipatests/test_integration/test_pkinit_manage.py
-
-diff --git a/ipatests/test_integration/test_pkinit_manage.py b/ipatests/test_integration/test_pkinit_manage.py
-new file mode 100644
-index 0000000000000000000000000000000000000000..bc1d9e338cdf4e7a503b3c83ac12792894eecce2
---- /dev/null
-+++ b/ipatests/test_integration/test_pkinit_manage.py
-@@ -0,0 +1,111 @@
-+#
-+# Copyright (C) 2018  FreeIPA Contributors see COPYING for license
-+#
-+
-+"""
-+Module provides tests for the ipa-pkinit-manage command.
-+"""
-+
-+from __future__ import absolute_import
-+
-+from ipalib import x509
-+from ipaplatform.paths import paths
-+from ipapython.dn import DN
-+from ipatests.test_integration.base import IntegrationTest
-+from ipatests.pytest_ipa.integration import tasks
-+
-+
-+SELFSIGNED_CA_HELPER = 'SelfSign'
-+IPA_CA_HELPER = 'IPA'
-+PKINIT_STATUS_ENABLED = 'enabled'
-+PKINIT_STATUS_DISABLED = 'disabled'
-+
-+
-+def check_pkinit_status(host, status):
-+    """Ensures that ipa-pkinit-manage status returns the expected state"""
-+    result = host.run_command(['ipa-pkinit-manage', 'status'],
-+                              raiseonerr=False)
-+    assert result.returncode == 0
-+    assert 'PKINIT is {}'.format(status) in result.stdout_text
-+
-+
-+def check_pkinit_tracking(host, ca_helper):
-+    """Ensures that the PKINIT cert is tracked by the expected helper"""
-+    result = host.run_command(['getcert', 'list', '-f', paths.KDC_CERT],
-+                              raiseonerr=False)
-+    assert result.returncode == 0
-+    # Make sure that only one request exists
-+    assert result.stdout_text.count('Request ID') == 1
-+    # Make sure that the right CA helper is used to track the cert
-+    assert 'CA: {}'.format(ca_helper) in result.stdout_text
-+
-+
-+def check_pkinit_cert_issuer(host, issuer):
-+    """Ensures that the PKINIT cert is signed by the expected issuer"""
-+    data = host.get_file_contents(paths.KDC_CERT)
-+    pkinit_cert = x509.load_pem_x509_certificate(data)
-+    # Make sure that the issuer is the expected one
-+    assert DN(pkinit_cert.issuer) == DN(issuer)
-+
-+
-+def check_pkinit(host, enabled=True):
-+    """Checks that PKINIT is configured as expected
-+
-+    If enabled:
-+    ipa-pkinit-manage status must return 'PKINIT is enabled'
-+    the certificate must be tracked by IPA CA helper
-+    the certificate must be signed by IPA CA
-+    If disabled:
-+    ipa-pkinit-manage status must return 'PKINIT is disabled'
-+    the certificate must be tracked by SelfSign CA helper
-+    the certificate must be self-signed
-+    """
-+    if enabled:
-+        # When pkinit is enabled:
-+        # cert is tracked by IPA CA helper
-+        # cert is signed by IPA CA
-+        check_pkinit_status(host, PKINIT_STATUS_ENABLED)
-+        check_pkinit_tracking(host, IPA_CA_HELPER)
-+        check_pkinit_cert_issuer(
-+            host,
-+            'CN=Certificate Authority,O={}'.format(host.domain.realm))
-+    else:
-+        # When pkinit is disabled
-+        # cert is tracked by 'SelfSign' CA helper
-+        # cert is self-signed
-+        check_pkinit_status(host, PKINIT_STATUS_DISABLED)
-+        check_pkinit_tracking(host, SELFSIGNED_CA_HELPER)
-+        check_pkinit_cert_issuer(
-+            host,
-+            'CN={},O={}'.format(host.hostname, host.domain.realm))
-+
-+
-+class TestPkinitManage(IntegrationTest):
-+    """Tests the ipa-pkinit-manage command.
-+
-+    ipa-pkinit-manage can be used to enable, disable or check
-+    the status of PKINIT.
-+    When pkinit is enabled, the kerberos server is using a certificate
-+    signed either externally or by IPA CA. In the latter case, certmonger
-+    is tracking the cert with IPA helper.
-+    When pkinit is disabled, the kerberos server is using a self-signed
-+    certificate that is tracked by certmonger with the SelfSigned helper.
-+    """
-+
-+    @classmethod
-+    def install(cls, mh):
-+        # Install the master with PKINIT disabled
-+        tasks.install_master(cls.master, extra_args=['--no-pkinit'])
-+        check_pkinit(cls.master, enabled=False)
-+
-+    def test_pkinit_enable(self):
-+        self.master.run_command(['ipa-pkinit-manage', 'enable'])
-+        check_pkinit(self.master, enabled=True)
-+
-+    def test_pkinit_disable(self):
-+        self.master.run_command(['ipa-pkinit-manage', 'disable'])
-+        check_pkinit(self.master, enabled=False)
-+
-+    def test_pkinit_reenable(self):
-+        self.master.run_command(['ipa-pkinit-manage', 'enable'])
-+        check_pkinit(self.master, enabled=True)
--- 
-2.17.2
-
diff --git a/SOURCES/0055-PKINIT-fix-ipa-pkinit-manage-enable-disable.patch b/SOURCES/0055-PKINIT-fix-ipa-pkinit-manage-enable-disable.patch
deleted file mode 100644
index 8a02d74..0000000
--- a/SOURCES/0055-PKINIT-fix-ipa-pkinit-manage-enable-disable.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From 977a01a67318a9b0ce01f7803b1126a310bf4140 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Fri, 30 Nov 2018 15:49:20 +0100
-Subject: [PATCH] PKINIT: fix ipa-pkinit-manage enable|disable
-
-The command ipa-pkinit-manage enable|disable is reporting
-success even though the PKINIT cert is not re-issued.
-The command triggers the request of a new certificate
-(signed by IPA CA when state=enable, selfsigned when disabled),
-but as the cert file is still present, certmonger does not create
-a new request and the existing certificate is kept.
-
-The fix consists in deleting the cert and key file before calling
-certmonger to request a new cert.
-
-There was also an issue in the is_pkinit_enabled() function:
-if no tracking request was found for the PKINIT cert,
-is_pkinit_enabled() was returning True while it should not.
-
-Fixes https://pagure.io/freeipa/issue/7200
-
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/ipa_pkinit_manage.py | 2 ++
- ipaserver/install/krbinstance.py       | 9 ++++++---
- 2 files changed, 8 insertions(+), 3 deletions(-)
-
-diff --git a/ipaserver/install/ipa_pkinit_manage.py b/ipaserver/install/ipa_pkinit_manage.py
-index 4a79bba5d1b636827a7a031965b49cf7b34c6330..86bd1baf00178a629864b210ca9f4786668149df 100644
---- a/ipaserver/install/ipa_pkinit_manage.py
-+++ b/ipaserver/install/ipa_pkinit_manage.py
-@@ -72,6 +72,8 @@ class PKINITManage(AdminTool):
-                 if ca_enabled:
-                     logger.warning(
-                         "Failed to stop tracking certificates: %s", e)
-+            # remove the cert and key
-+            krb.delete_pkinit_cert()
- 
-             krb.enable_ssl()
- 
-diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
-index a3079bd6304a41116f9aa5e78b6c6c71d72d7aa6..6221f3f61338308afb406e23d62566b12d8c131d 100644
---- a/ipaserver/install/krbinstance.py
-+++ b/ipaserver/install/krbinstance.py
-@@ -77,7 +77,7 @@ def is_pkinit_enabled():
-     if os.path.exists(paths.KDC_CERT):
-         pkinit_request_ca = get_pkinit_request_ca()
- 
--        if pkinit_request_ca != "SelfSign":
-+        if pkinit_request_ca and pkinit_request_ca != "SelfSign":
-             return True
- 
-     return False
-@@ -591,6 +591,10 @@ class KrbInstance(service.Service):
-     def stop_tracking_certs(self):
-         certmonger.stop_tracking(certfile=paths.KDC_CERT)
- 
-+    def delete_pkinit_cert(self):
-+        installutils.remove_file(paths.KDC_CERT)
-+        installutils.remove_file(paths.KDC_KEY)
-+
-     def uninstall(self):
-         if self.is_configured():
-             self.print_msg("Unconfiguring %s" % self.service_name)
-@@ -616,8 +620,7 @@ class KrbInstance(service.Service):
-         # stop tracking and remove certificates
-         self.stop_tracking_certs()
-         installutils.remove_file(paths.CACERT_PEM)
--        installutils.remove_file(paths.KDC_CERT)
--        installutils.remove_file(paths.KDC_KEY)
-+        self.delete_pkinit_cert()
- 
-         if running:
-             self.restart()
--- 
-2.17.2
-
diff --git a/SOURCES/0056-replication-check-remote-ds-version-before-editing-a.patch b/SOURCES/0056-replication-check-remote-ds-version-before-editing-a.patch
deleted file mode 100644
index 7777371..0000000
--- a/SOURCES/0056-replication-check-remote-ds-version-before-editing-a.patch
+++ /dev/null
@@ -1,87 +0,0 @@
-From e879ca9b693a10f456f03d3c471afa49321516f9 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Thu, 13 Dec 2018 14:54:07 +0100
-Subject: [PATCH] replication: check remote ds version before editing
- attributes
-
-When the remote server has an old DS version, update of the
-replication attributes nsds5ReplicaReleaseTimeout nsds5ReplicaBackoffMax
-and nsDS5ReplicaBindDnGroupCheckInterval fails even if the remote
-schema has been updated.
-
-Check first the remote server version and update the attributes only if
-the version is high enough.
-A previous fix was already performing this check (commit 02f4a7a),
-but not in all the cases. This fix also handles when the remote server
-already has a cn=replica entry (for instance because it has already
-established replication with another host).
-
-Fixes https://pagure.io/freeipa/issue/7796
-
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/replication.py | 33 ++++++++++++++++++++++++++------
- 1 file changed, 27 insertions(+), 6 deletions(-)
-
-diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
-index 92a99cd9482f86d6820230479bf94c871669572e..70629b4528f033908c584bfaf0793cfa4ce259d4 100644
---- a/ipaserver/install/replication.py
-+++ b/ipaserver/install/replication.py
-@@ -215,6 +215,22 @@ def wait_for_entry(connection, dn, timeout, attr=None, attrvalue='*',
-             time.sleep(1)
- 
- 
-+def get_ds_version(conn):
-+    """Returns the DS version
-+
-+    Retrieves the DS version from the vendorVersion attribute stored in LDAP.
-+    :param conn: LDAP connection established and authenticated to the server
-+                 for which we need the version
-+    :return: a tuple containing the DS version
-+    """
-+    # Find which 389-ds is installed
-+    rootdse = conn.get_entry(DN(''), ['vendorVersion'])
-+    version = rootdse.single_value.get('vendorVersion')
-+    mo = re.search(r'(\d+)\.(\d+)\.(\d+)[\.\d]*', version)
-+    vendor_version = tuple(int(v) for v in mo.groups())
-+    return vendor_version
-+
-+
- class ReplicationManager(object):
-     """Manage replication agreements
- 
-@@ -527,8 +543,16 @@ class ReplicationManager(object):
-             # Add the new replication manager
-             binddns.append(replica_binddn)
- 
--        for key, value in REPLICA_CREATION_SETTINGS.items():
--            entry[key] = value
-+        # If the remote server has 389-ds < 1.3, it does not
-+        # support the attributes we are trying to set.
-+        # Find which 389-ds is installed
-+        vendor_version = get_ds_version(conn)
-+        if vendor_version >= (1, 3, 0):
-+            for key, value in REPLICA_CREATION_SETTINGS.items():
-+                entry[key] = value
-+        else:
-+            logger.debug("replication attributes not supported "
-+                         "on remote master, skipping update.")
- 
-         try:
-             conn.update_entry(entry)
-@@ -604,10 +628,7 @@ class ReplicationManager(object):
-         # If the remote server has 389-ds < 1.3, it does not
-         # support the attributes we are trying to set.
-         # Find which 389-ds is installed
--        rootdse = r_conn.get_entry(DN(''), ['vendorVersion'])
--        version = rootdse.single_value.get('vendorVersion')
--        mo = re.search(r'(\d+)\.(\d+)\.(\d+)[\.\d]*', version)
--        vendor_version = tuple(int(v) for v in mo.groups())
-+        vendor_version = get_ds_version(r_conn)
-         if vendor_version >= (1, 3, 0):
-             # 389-ds understands the replication attributes,
-             # we can safely modify them
--- 
-2.17.2
-
diff --git a/SOURCES/0057-ipa-sidgen-make-internal-fetch_attr-helper-really-in.patch b/SOURCES/0057-ipa-sidgen-make-internal-fetch_attr-helper-really-in.patch
deleted file mode 100644
index 2bb8cdd..0000000
--- a/SOURCES/0057-ipa-sidgen-make-internal-fetch_attr-helper-really-in.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From b2cb212a12982cb6c9901ae0e71198c49e915258 Mon Sep 17 00:00:00 2001
-From: Alexander Bokovoy <abokovoy@redhat.com>
-Date: Fri, 14 Dec 2018 14:02:26 +0200
-Subject: [PATCH] ipa-sidgen: make internal fetch_attr helper really internal
-
-With 389-ds landing a change for
-https://pagure.io/389-ds-base/issue/49950, fetch_attr() helper function
-is exposed in slapi-plugin.h. However, in order to be able to build
-FreeIPA plugins against older 389-ds versions, prefer using a local
-variant of it.
-
-Rename fetch_attr() to ipa_sidgen_fetch_attr() so that it doesn't
-conflict at all.
-
-Fixes: https://pagure.io/freeipa/issue/7811
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c
-index 9e474e83dd0e1bfc52b2e2da3fda12420d2ea281..007b1c945d0e37c4061f6a33cfdd667c45118c99 100644
---- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c
-+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c
-@@ -63,7 +63,7 @@ struct worker_ctx {
-     struct range_info **ranges;
- };
- 
--static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
-+static const char *ipa_sidgen_fetch_attr(Slapi_Entry *e, const char *attrname,
-                                               const char *default_val)
- {
-     Slapi_Attr *attr;
-@@ -242,7 +242,7 @@ int sidgen_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
- 
-     worker_ctx->plugin_id = global_sidgen_plugin_id;
- 
--    str = fetch_attr(e, "delay", NULL);
-+    str = ipa_sidgen_fetch_attr(e, "delay", NULL);
-     if (str != NULL) {
-         errno = 0;
-         worker_ctx->delay = strtol(str, &endptr, 10);
-@@ -255,7 +255,7 @@ int sidgen_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
-     }
-     LOG("delay is [%li].\n", worker_ctx->delay);
- 
--    str = fetch_attr(e, "nsslapd-basedn", NULL);
-+    str = ipa_sidgen_fetch_attr(e, "nsslapd-basedn", NULL);
-     if (str == NULL) {
-         LOG_FATAL("Missing nsslapd-basedn!\n");
-         *returncode = LDAP_CONSTRAINT_VIOLATION;
--- 
-2.20.1
-
diff --git a/SOURCES/0058-replica-installation-add-master-record-only-if-in-ma.patch b/SOURCES/0058-replica-installation-add-master-record-only-if-in-ma.patch
deleted file mode 100644
index ff6ff8a..0000000
--- a/SOURCES/0058-replica-installation-add-master-record-only-if-in-ma.patch
+++ /dev/null
@@ -1,61 +0,0 @@
-From 0ed1632ac9f659734f9397c21d0b2de3c2c2d895 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Tue, 15 Jan 2019 17:53:55 +0100
-Subject: [PATCH] replica installation: add master record only if in managed
- zone
-
-Scenario: install a replica with DNS, whose IP address is part of a
-forward zone.
-Currently, the replica installation fails because the installer is
-trying to add a A/AAAA record for the replica in the zone
-when setting up the bind instance, and addition of records in a
-forward zone is forbidden.
-
-The bind installer should check if the IP address is in a master zone
-(i.e. a DNS zone managed by IdM, not a forward zone), and avoid
-creating the record if it's not the case.
-
-During uninstallation, perform the same check before removing the
-DNS record (if in a forward zone, no need to call dnsrecord-del).
-Fixes: https://pagure.io/freeipa/issue/7369
-Reviewed-By: Francois Cami <fcami@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- ipaserver/install/bindinstance.py | 13 ++++++++++---
- 1 file changed, 10 insertions(+), 3 deletions(-)
-
-diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
-index 7c858aab4417ccf3a4999fcaaa1c7e0f93464e4d..3b03e536117677f0f073fc1f06a28ebab0cfe006 100644
---- a/ipaserver/install/bindinstance.py
-+++ b/ipaserver/install/bindinstance.py
-@@ -844,10 +844,13 @@ class BindInstance(service.Service):
- 
-         # Add forward and reverse records to self
-         for addr in addrs:
--            try:
-+            # Check first if the zone is a master zone
-+            # (if it is a forward zone, dns_zone_exists will return False)
-+            if dns_zone_exists(zone, api=self.api):
-                 add_fwd_rr(zone, host, addr, self.api)
--            except errors.NotFound:
--                pass
-+            else:
-+                logger.debug("Skip adding record %s to a zone %s "
-+                             "not managed by IPA", addr, zone)
- 
-             reverse_zone = find_reverse_zone(addr, self.api)
-             if reverse_zone:
-@@ -1063,6 +1066,10 @@ class BindInstance(service.Service):
-         self.fqdn = fqdn
-         self.domain = domain_name
- 
-+        if not dns_zone_exists(zone, api=self.api):
-+            # Zone may be a forward zone, skip update
-+            return
-+
-         areclist = get_fwd_rr(zone, host, api=self.api)
-         for rdata in areclist:
-             del_fwd_rr(zone, host, rdata, api=self.api)
--- 
-2.20.1
-
diff --git a/SOURCES/0059-ipatests-add-test-for-replica-in-forward-zone.patch b/SOURCES/0059-ipatests-add-test-for-replica-in-forward-zone.patch
deleted file mode 100644
index 1a49fcb..0000000
--- a/SOURCES/0059-ipatests-add-test-for-replica-in-forward-zone.patch
+++ /dev/null
@@ -1,130 +0,0 @@
-From 8e5149c36651eaded5d06a32fd94e78fc2e3dcb0 Mon Sep 17 00:00:00 2001
-From: Florence Blanc-Renaud <flo@redhat.com>
-Date: Thu, 17 Jan 2019 11:10:52 +0100
-Subject: [PATCH] ipatests: add test for replica in forward zone
-
-Scenario:
-install a replica with DNS, with the replica part of a forward zone.
-The replica installation should proceed successfully and avoid
-trying to add a DNS record for the replica in the forward zone,
-as the forward zone is not managed by IPA DNS.
-
-Test added to nightly definitions.
-
-Related to https://pagure.io/freeipa/issue/7369
-
-Reviewed-By: Francois Cami <fcami@redhat.com>
-Reviewed-By: Christian Heimes <cheimes@redhat.com>
----
- .../test_replica_promotion.py                 | 98 +++++++++++++++++++
- 1 file changed, 98 insertions(+)
-
-diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
-index 7fdc12dc4a4269772c77ff543239be49c46d199a..c635d932bc92ed8c0a147379718933aabaae0f16 100644
---- a/ipatests/test_integration/test_replica_promotion.py
-+++ b/ipatests/test_integration/test_replica_promotion.py
-@@ -644,3 +644,101 @@ class TestSubCAkeyReplication(IntegrationTest):
-         ssl_cmd = ['openssl', 'x509', '-text', '-in', TEST_CRT_FILE]
-         ssl = replica.run_command(ssl_cmd)
-         assert 'Issuer: CN = {}'.format(self.SUBCA) in ssl.stdout_text
-+
-+
-+def update_etc_hosts(host, ip, old_hostname, new_hostname):
-+    '''Adds or update /etc/hosts
-+
-+    If /etc/hosts contains an entry for old_hostname, replace it with
-+    new_hostname.
-+    If /etc/hosts did not contain the entry, create one for new_hostname with
-+    the provided ip.
-+    The function makes a backup in /etc/hosts.sav
-+
-+    :param host the machine on which /etc/hosts needs to be update_dns_records
-+    :param ip the ip address for the new record
-+    :param old_hostname the hostname to replace
-+    :param new_hostname the new hostname to put in /etc/hosts
-+    '''
-+    # Make a backup
-+    host.run_command(['/usr/bin/cp',
-+                      paths.HOSTS,
-+                      '%s.sav' % paths.HOSTS])
-+    contents = host.get_file_contents(paths.HOSTS, encoding='utf-8')
-+    # If /etc/hosts already contains old_hostname, simply replace
-+    pattern = r'^(.*\s){}(\s)'.format(old_hostname)
-+    new_contents, mods = re.subn(pattern, r'\1{}\2'.format(new_hostname),
-+                                 contents, flags=re.MULTILINE)
-+    # If it didn't contain any entry for old_hostname, just add new_hostname
-+    if mods == 0:
-+        short = new_hostname.split(".", 1)[0]
-+        new_contents = new_contents + "\n{}\t{} {}\n".format(ip,
-+                                                             new_hostname,
-+                                                             short)
-+    host.put_file_contents(paths.HOSTS, new_contents)
-+
-+
-+def restore_etc_hosts(host):
-+    '''Restores /etc/hosts.sav into /etc/hosts
-+    '''
-+    host.run_command(['/usr/bin/mv',
-+                      '%s.sav' % paths.HOSTS,
-+                      paths.HOSTS],
-+                     raiseonerr=False)
-+
-+
-+class TestReplicaInForwardZone(IntegrationTest):
-+    """
-+    Pagure Reference: https://pagure.io/freeipa/issue/7369
-+
-+    Scenario: install a replica whose name is in a forwarded zone
-+    """
-+
-+    forwardzone = 'forward.test'
-+    num_replicas = 1
-+
-+    @classmethod
-+    def install(cls, mh):
-+        tasks.install_master(cls.master, setup_dns=True)
-+
-+    def test_replica_install_in_forward_zone(self):
-+        master = self.master
-+        replica = self.replicas[0]
-+
-+        # Create a forward zone on the master
-+        master.run_command(['ipa', 'dnsforwardzone-add', self.forwardzone,
-+                            '--skip-overlap-check',
-+                            '--forwarder', master.config.dns_forwarder])
-+
-+        # Configure the client with a name in the forwardzone
-+        r_shortname = replica.hostname.split(".", 1)[0]
-+        r_new_hostname = '{}.{}'.format(r_shortname,
-+                                        self.forwardzone)
-+
-+        # Update /etc/hosts on the master with an entry for the replica
-+        # otherwise replica conncheck would fail
-+        update_etc_hosts(master, replica.ip, replica.hostname,
-+                         r_new_hostname)
-+        # Remove the replica previous hostname from /etc/hosts
-+        # and add the replica new hostname
-+        # otherwise replica install will complain because
-+        # hostname does not match
-+        update_etc_hosts(replica, replica.ip, replica.hostname,
-+                         r_new_hostname)
-+
-+        try:
-+            # install client with a hostname in the forward zone
-+            tasks.install_client(self.master, replica,
-+                                 extra_args=['--hostname', r_new_hostname])
-+
-+            replica.run_command(['ipa-replica-install',
-+                                 '--principal', replica.config.admin_name,
-+                                 '--admin-password',
-+                                 replica.config.admin_password,
-+                                 '--setup-dns',
-+                                 '--forwarder', master.config.dns_forwarder,
-+                                 '-U'])
-+        finally:
-+            # Restore /etc/hosts on master and replica
-+            restore_etc_hosts(master)
-+            restore_etc_hosts(replica)
--- 
-2.20.1
-
diff --git a/SOURCES/0060-Add-workaround-for-slow-host-service-del.patch b/SOURCES/0060-Add-workaround-for-slow-host-service-del.patch
deleted file mode 100644
index af4a5cd..0000000
--- a/SOURCES/0060-Add-workaround-for-slow-host-service-del.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From c63b6cbe536987d3e1818542a2f8530e44948812 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 25 Jan 2019 16:12:11 +0100
-Subject: [PATCH] Add workaround for slow host/service del
-
-host-del and service-del are slow because cert revokation is implemented
-inefficiently. The internal cert_find() call retrieves all certificates
-from Dogtag.
-
-The workaround special cases service and host find without additional RA
-search options. A search for service and host certs limits the scope to
-certificate with matching subject common name.
-
-See: https://pagure.io/freeipa/issue/7835
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- ipaserver/plugins/cert.py | 16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
-diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
-index ed78388c8b8b4688873a5b047fb1b67e417a8a6d..b6a132ffdb27b4d7b1f761c4bee835f46c5d9721 100644
---- a/ipaserver/plugins/cert.py
-+++ b/ipaserver/plugins/cert.py
-@@ -1470,6 +1470,22 @@ class cert_find(Search, CertMethod):
-         result = collections.OrderedDict()
-         complete = bool(ra_options)
- 
-+        # workaround for RHBZ#1669012
-+        # Improve performance for service and host case by also searching
-+        # for subject. This limits the amount of certificate retrieved from
-+        # Dogtag. The special case is only used, when no ra_options are set
-+        # and exactly one service or host is supplied.
-+        # The complete flag is left to False.
-+        if not ra_options:
-+            services = options.get('service', ())
-+            hosts = options.get('host', ())
-+            if len(services) == 1 and not hosts:
-+                principal = kerberos.Principal(options['service'][0])
-+                if principal.is_service:
-+                    ra_options['subject'] = principal.hostname
-+            elif len(hosts) == 1 and not services:
-+                ra_options['subject'] = options['host'][0]
-+
-         try:
-             ca_enabled_check(self.api)
-         except errors.NotFound:
--- 
-2.20.1
-
diff --git a/SOURCES/0061-Optimize-cert-remove-case.patch b/SOURCES/0061-Optimize-cert-remove-case.patch
deleted file mode 100644
index b642e25..0000000
--- a/SOURCES/0061-Optimize-cert-remove-case.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-From 964a4d858e7f30e62691e6e0a1abdcd55cc68405 Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Fri, 25 Jan 2019 16:18:59 +0100
-Subject: [PATCH] Optimize cert remove case
-
-The cert_remove and mod subcommands for service and host now pass in the
-name to cert_find() to benefit from special cases.
-
-See: https://pagure.io/freeipa/issue/7835
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- ipaserver/plugins/host.py    | 8 ++++++--
- ipaserver/plugins/service.py | 7 +++++--
- 2 files changed, 11 insertions(+), 4 deletions(-)
-
-diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py
-index 306105d67a58fd4343933349db70a1d786eaa4b2..c74a3e58f8af6b33e284ba54b5763a684d91bac3 100644
---- a/ipaserver/plugins/host.py
-+++ b/ipaserver/plugins/host.py
-@@ -899,7 +899,9 @@ class host_mod(LDAPUpdate):
-             old_certs = entry_attrs_old.get('usercertificate', [])
-             removed_certs = set(old_certs) - set(certs)
-             for cert in removed_certs:
--                rm_certs = api.Command.cert_find(certificate=cert)['result']
-+                rm_certs = api.Command.cert_find(
-+                    certificate=cert,
-+                    host=keys)['result']
-                 revoke_certs(rm_certs)
- 
-         if certs:
-@@ -1335,7 +1337,9 @@ class host_remove_cert(LDAPRemoveAttributeViaOption):
-         assert isinstance(dn, DN)
- 
-         for cert in options.get('usercertificate', []):
--            revoke_certs(api.Command.cert_find(certificate=cert)['result'])
-+            revoke_certs(api.Command.cert_find(
-+                certificate=cert,
-+                host=keys)['result'])
- 
-         return dn
- 
-diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py
-index be31f810275214eb28a3f6b7ed9e6dc8ec808ae0..d176adddff8d2590d64ba4059018606ff1eb8d48 100644
---- a/ipaserver/plugins/service.py
-+++ b/ipaserver/plugins/service.py
-@@ -703,7 +703,8 @@ class service_mod(LDAPUpdate):
-             removed_certs = set(old_certs) - set(certs)
-             for cert in removed_certs:
-                 rm_certs = api.Command.cert_find(
--                    certificate=cert.public_bytes(x509.Encoding.DER))['result']
-+                    certificate=cert.public_bytes(x509.Encoding.DER),
-+                    service=keys)['result']
-                 revoke_certs(rm_certs)
- 
-         if certs:
-@@ -983,7 +984,9 @@ class service_remove_cert(LDAPRemoveAttributeViaOption):
-         assert isinstance(dn, DN)
- 
-         for cert in options.get('usercertificate', []):
--            revoke_certs(api.Command.cert_find(certificate=cert)['result'])
-+            revoke_certs(api.Command.cert_find(
-+                certificate=cert,
-+                service=keys)['result'])
- 
-         return dn
- 
--- 
-2.20.1
-
diff --git a/SOURCES/0062-Update-mod_nss-cipher-list-so-there-is-overlap-with-.patch b/SOURCES/0062-Update-mod_nss-cipher-list-so-there-is-overlap-with-.patch
deleted file mode 100644
index adc804c..0000000
--- a/SOURCES/0062-Update-mod_nss-cipher-list-so-there-is-overlap-with-.patch
+++ /dev/null
@@ -1,125 +0,0 @@
-From 964d13237029e0568f56342917ae386746c0b281 Mon Sep 17 00:00:00 2001
-From: Rob Crittenden <rcritten@redhat.com>
-Date: Fri, 1 Feb 2019 10:30:40 -0500
-Subject: [PATCH] Update mod_nss cipher list so there is overlap with a 4.x
- master
-
-dogtag updated its cipher list, disabling a lot of ciphers, which
-causes an overlap problem with a RHEL 6.x IPA master.
-
-This update script adds the two available ciphers to the nss.conf
-so that creating a CA replica is possible.
-
-Signed-off-by: Rob Crittenden <rcritten@redhat.com>
-Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
----
- contrib/copy-schema-to-ca-RHEL6.py | 79 ++++++++++++++++++++++++++++++
- 1 file changed, 79 insertions(+)
-
-diff --git a/contrib/copy-schema-to-ca-RHEL6.py b/contrib/copy-schema-to-ca-RHEL6.py
-index 3ed16555e9a63867162b58fe99531db46e867a8b..2b866a52ba99f59db913a127f271c6da63a65b95 100755
---- a/contrib/copy-schema-to-ca-RHEL6.py
-+++ b/contrib/copy-schema-to-ca-RHEL6.py
-@@ -31,6 +31,12 @@ from ipaserver.install.dsinstance import DS_USER
- from ipaserver.install.cainstance import PKI_USER
- from ipapython import services
- 
-+# for mod_nss
-+from ipaserver.install.httpinstance import NSS_CONF
-+from ipaserver.install.httpinstance import HTTPInstance
-+from ipaserver.install import installutils
-+from ipapython import sysrestore
-+
- SERVERID = "PKI-IPA"
- SCHEMA_FILENAMES = (
-     "60kerberos.ldif",
-@@ -100,6 +106,77 @@ def restart_pki_ds():
-     services.service('dirsrv').restart(SERVERID)
- 
- 
-+# The ipa-3-0 set_directive() has very loose comparision of directive
-+# which would cause multiple NSSCipherSuite to be added so provide
-+# a custom function for it.
-+def set_directive(filename, directive, value, quotes=True, separator=' '):
-+    """Set a name/value pair directive in a configuration file.
-+
-+       A value of None means to drop the directive.
-+
-+       This has only been tested with nss.conf
-+    """
-+    valueset = False
-+    st = os.stat(filename)
-+    fd = open(filename)
-+    newfile = []
-+    for line in fd:
-+        if line.lstrip().startswith(directive):
-+            valueset = True
-+            if value is not None:
-+                if quotes:
-+                    newfile.append('%s%s"%s"\n' %
-+                                   (directive, separator, value))
-+                else:
-+                    newfile.append('%s%s%s\n' % (directive, separator, value))
-+        else:
-+            newfile.append(line)
-+    fd.close()
-+    if not valueset:
-+        if value is not None:
-+            if quotes:
-+                newfile.append('%s%s"%s"\n' % (directive, separator, value))
-+            else:
-+                newfile.append('%s%s%s\n' % (directive, separator, value))
-+
-+    fd = open(filename, "w")
-+    fd.write("".join(newfile))
-+    fd.close()
-+    os.chown(filename, st.st_uid, st.st_gid)  # reset perms
-+
-+
-+def update_mod_nss_cipher_suite():
-+    add_ciphers = ['ecdhe_rsa_aes_128_sha', 'ecdhe_rsa_aes_256_sha']
-+    ciphers = installutils.get_directive(NSS_CONF, 'NSSCipherSuite')
-+
-+    # Run through once to see if any of the new ciphers are there but
-+    # disabled. If they are then enable them.
-+    lciphers = ciphers.split(',')
-+    new_ciphers = []
-+    for cipher in lciphers:
-+        for add in add_ciphers:
-+            if cipher.endswith(add):
-+                if cipher.startswith('-'):
-+                    cipher = '+%s' % add
-+        new_ciphers.append(cipher)
-+
-+    # Run through again and add remaining ciphers as enabled.
-+    for add in add_ciphers:
-+        if add not in ciphers:
-+            new_ciphers.append('+%s' % add)
-+
-+    ciphers = ','.join(new_ciphers)
-+    set_directive(NSS_CONF, 'NSSCipherSuite', ciphers, False)
-+    root_logger.info('Updated Apache cipher list')
-+
-+
-+def restart_http():
-+    root_logger.info('Restarting HTTP')
-+    fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
-+    http = HTTPInstance(fstore)
-+    http.restart()
-+
-+
- def main():
-     if os.getegid() != 0:
-         sys.exit("Must be root to run this script")
-@@ -110,6 +187,8 @@ def main():
- 
-     add_ca_schema()
-     restart_pki_ds()
-+    update_mod_nss_cipher_suite()
-+    restart_http()
- 
-     root_logger.info('Schema updated successfully')
- 
--- 
-2.20.1
-
diff --git a/SOURCES/0063-Consider-configured-servers-as-valid.patch b/SOURCES/0063-Consider-configured-servers-as-valid.patch
deleted file mode 100644
index 71b417b..0000000
--- a/SOURCES/0063-Consider-configured-servers-as-valid.patch
+++ /dev/null
@@ -1,107 +0,0 @@
-From ad3022b24462cc7bc33f810c2d20b4b00006a14c Mon Sep 17 00:00:00 2001
-From: Christian Heimes <cheimes@redhat.com>
-Date: Mon, 29 Apr 2019 11:12:30 +0200
-Subject: [PATCH] Consider configured servers as valid
-
-Under some conditions, ipa config-show and several other commands were
-failing with error message:
-
-  ERROR: invalid 'PKINIT enabled server': all masters must have IPA master role enabled
-
-Amongst others the issue can be caused by a broken installation, when
-some services are left in state 'configuredServices'. The problem even
-block uninstallation or removal of replicas. Now configured servers are
-also consider valid providers for associated roles.
-
-A new test verifies that config-show works with hidden and configured HTTP
-service.
-
-Remark: The original intent of the sanity check is no longer clear to me. I
-think it was used to very that all services can be started by ipactl.
-Since ipactl starts hidden, configured, and enabled services, the new
-logic reflect the fact, too.
-
-Fixes: https://pagure.io/freeipa/issue/7929
-Signed-off-by: Christian Heimes <cheimes@redhat.com>
-Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
----
- ipaserver/servroles.py                     | 12 +++++---
- ipatests/test_integration/test_commands.py | 33 ++++++++++++++++++++++
- 2 files changed, 41 insertions(+), 4 deletions(-)
-
-diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
-index bf33923ded4ca6559fba504e1b447086e87d2083..756ce91a8164144978363f04f6abd8de18b93524 100644
---- a/ipaserver/servroles.py
-+++ b/ipaserver/servroles.py
-@@ -338,12 +338,16 @@ class ServerAttribute(LDAPBasedProperty):
-         ldap.update_entry(service_entry)
- 
-     def _get_assoc_role_providers(self, api_instance):
--        """
--        get list of all servers on which the associated role is enabled
-+        """get list of all servers on which the associated role is enabled
-+
-+        Consider a configured server as a valid provider for a
-+        role, as all services are started.
-         """
-         return [
--            r[u'server_server'] for r in self.associated_role.status(
--                api_instance) if r[u'status'] == ENABLED]
-+            r[u'server_server']
-+            for r in self.associated_role.status(api_instance)
-+            if r[u'status'] in {ENABLED,CONFIGURED}
-+        ]
- 
-     def _remove(self, api_instance, masters):
-         """
-diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
-index b2c0d5c710c9810cfd74216983f793808f4cf3c4..4237de4eea2981c52ecb664d132e6607cb2ac25d 100644
---- a/ipatests/test_integration/test_commands.py
-+++ b/ipatests/test_integration/test_commands.py
-@@ -6,6 +6,11 @@
- from __future__ import absolute_import
- 
- from ipatests.test_integration.base import IntegrationTest
-+from ipapython.dn import DN
-+
-+from ipaserver.masters import (
-+    CONFIGURED_SERVICE, ENABLED_SERVICE, HIDDEN_SERVICE
-+)
- 
- 
- class TestIPACommand(IntegrationTest):
-@@ -46,3 +51,31 @@ class TestIPACommand(IntegrationTest):
-         assert result.returncode == 0
-         assert "SELinux user map order: {}".format(
-             maporder) in result.stdout_text
-+
-+    def test_config_show_configured_services(self):
-+        # https://pagure.io/freeipa/issue/7929
-+        states = {CONFIGURED_SERVICE, ENABLED_SERVICE}
-+        dn = DN(
-+            ('cn', 'HTTP'), ('cn', self.master.hostname), ('cn', 'masters'),
-+            ('cn', 'ipa'), ('cn', 'etc'),
-+            self.master.domain.basedn  # pylint: disable=no-member
-+        )
-+
-+        conn = self.master.ldap_connect()
-+        entry = conn.get_entry(dn)  # pylint: disable=no-member
-+
-+        # original setting and all settings without state
-+        orig_cfg = list(entry['ipaConfigString'])
-+        other_cfg = [item for item in orig_cfg if item not in states]
-+
-+        try:
-+            # test with configured
-+            cfg = [CONFIGURED_SERVICE]
-+            cfg.extend(other_cfg)
-+            entry['ipaConfigString'] = cfg
-+            conn.update_entry(entry)  # pylint: disable=no-member
-+            self.master.run_command(['ipa', 'config-show'])
-+        finally:
-+            # reset
-+            entry['ipaConfigString'] = orig_cfg
-+            conn.update_entry(entry)  # pylint: disable=no-member
--- 
-2.20.1
-
diff --git a/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch b/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch
index 85463af..1868627 100644
--- a/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch
+++ b/SOURCES/1001-Change-branding-to-IPA-and-Identity-Management.patch
@@ -1,4 +1,4 @@
-From 230a9128ebe8e7a18b11e1b922e63b9e0acf9973 Mon Sep 17 00:00:00 2001
+From c70d8d9db7cdfd0ccea8970318effc433a705ece Mon Sep 17 00:00:00 2001
 From: Jan Cholasta <jcholast@redhat.com>
 Date: Tue, 14 Mar 2017 15:48:07 +0000
 Subject: [PATCH] Change branding to IPA and Identity Management
@@ -25,7 +25,9 @@ Subject: [PATCH] Change branding to IPA and Identity Management
  install/tools/man/ipa-backup.1             |   2 +-
  install/tools/man/ipa-ca-install.1         |   2 +-
  install/tools/man/ipa-cacert-manage.1      |   2 +-
+ install/tools/man/ipa-cert-fix.1           |   6 +-
  install/tools/man/ipa-compat-manage.1      |   2 +-
+ install/tools/man/ipa-crlgen-manage.1      |   2 +-
  install/tools/man/ipa-csreplica-manage.1   |   2 +-
  install/tools/man/ipa-dns-install.1        |   4 +-
  install/tools/man/ipa-kra-install.1        |   2 +-
@@ -53,11 +55,13 @@ Subject: [PATCH] Change branding to IPA and Identity Management
  install/ui/sync_otp.html                   |   2 +-
  ipaserver/advise/plugins/legacy_clients.py |   8 +-
  ipaserver/install/dns.py                   |   2 +-
+ ipaserver/install/ipa_cert_fix.py          |   2 +-
  ipaserver/install/ipa_kra_install.py       |   4 +-
  ipaserver/install/server/install.py        |   2 +-
  ipaserver/install/server/replicainstall.py |   2 +-
+ ipaserver/plugins/certprofile.py           |   2 +-
  ipaserver/plugins/sudorule.py              |   4 +-
- 53 files changed, 165 insertions(+), 120 deletions(-)
+ 57 files changed, 171 insertions(+), 126 deletions(-)
 
 diff --git a/client/man/default.conf.5 b/client/man/default.conf.5
 index f21d9d5b7a02e9c9858bb44cf3f2f4c16655901a..d6c1e42d1af3a2085451f43240d7e719143bb10b 100644
@@ -280,7 +284,7 @@ index 19e3e6832bea774244bc949ce44a27f5ebebaed0..2a92ec6aebeb0932b58dd092ba4188e1
  You may place your schema files in a subdirectory too, the code that loads
  schema files processes recursively all subdirectories of schema.d.
 diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
-index a870d136e242affe6627cd4c44a173a80a9ab1c6..f0e72b3adaa5ef27a11c11feb787019b6db71e62 100755
+index 19bd21866119b4a23f5a6a02cc8ea37c8f5d36ea..e14d80e845bc81072ace2f99987fdb3d01f90ac6 100755
 --- a/install/tools/ipa-adtrust-install
 +++ b/install/tools/ipa-adtrust-install
 @@ -141,11 +141,11 @@ def main():
@@ -298,7 +302,7 @@ index a870d136e242affe6627cd4c44a173a80a9ab1c6..f0e72b3adaa5ef27a11c11feb787019b
      # print "  * Add a SID to all users and Posix groups"
      print("")
 diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck
-index 067e47bcbf51048fd301ad48dfa29adecaa5bafb..ecb23c22da8a84a75c65b1dc4fc4889567a19cfc 100755
+index a91a2a7e7b18a9c78a1a7bb6daf59a13b72799fc..f63b6792aabbc6c08231176931703031fd9c59c7 100755
 --- a/install/tools/ipa-replica-conncheck
 +++ b/install/tools/ipa-replica-conncheck
 @@ -290,7 +290,7 @@ class PortResponder(threading.Thread):
@@ -370,7 +374,7 @@ index 99ff918789f2178c7b1132b2e7d911900430f3cf..fb6382fcdddcb7358671b67e72c72a4d
  ipa\-ca\-install \- Install a CA on a server
  .SH "SYNOPSIS"
 diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1
-index bacd56b5a89f0c5f4453a6287f15d5004148be12..ed69e843528a7f013ede283b2b813304192891ee 100644
+index 0cd34ee77e8b007073af2fbc66875c0e6c11bbfd..84fbc1a7cbe9715b8bdbd1aa9952605ed9bc5719 100644
 --- a/install/tools/man/ipa-cacert-manage.1
 +++ b/install/tools/man/ipa-cacert-manage.1
 @@ -16,7 +16,7 @@
@@ -382,6 +386,37 @@ index bacd56b5a89f0c5f4453a6287f15d5004148be12..ed69e843528a7f013ede283b2b813304
  .SH "NAME"
  ipa\-cacert\-manage \- Manage CA certificates in IPA
  .SH "SYNOPSIS"
+diff --git a/install/tools/man/ipa-cert-fix.1 b/install/tools/man/ipa-cert-fix.1
+index 3edef3118947d203d8972994d0d880850302a348..1ce655965331f0792a3ac2b32cbf1bb5bb675dfb 100644
+--- a/install/tools/man/ipa-cert-fix.1
++++ b/install/tools/man/ipa-cert-fix.1
+@@ -1,7 +1,7 @@
+ .\"
+ .\" Copyright (C) 2019  FreeIPA Contributors see COPYING for license
+ .\"
+-.TH "ipa-cert-fix" "1" "Mar 25 2019" "FreeIPA" "FreeIPA Manual Pages"
++.TH "ipa-cert-fix" "1" "Mar 25 2019" "IPA" "IPA Manual Pages"
+ .SH "NAME"
+ ipa\-cert\-fix \- Renew expired certificates
+ .SH "SYNOPSIS"
+@@ -9,7 +9,7 @@ ipa\-cert\-fix [options]
+ .SH "DESCRIPTION"
+ 
+ \fIipa-cert-fix\fR is a tool for recovery when expired certificates
+-prevent the normal operation of FreeIPA.  It should ONLY be used in
++prevent the normal operation of IPA.  It should ONLY be used in
+ such scenarios, and backup of the system, especially certificates
+ and keys, is \fBSTRONGLY RECOMMENDED\fR.
+ 
+@@ -22,7 +22,7 @@ This tool cannot renew certificates signed by external CAs.  To
+ install new, externally-signed HTTP, LDAP or KDC certificates, use
+ \fIipa-server-certinstall(1)\fR.
+ 
+-\fIipa-cert-fix\fR will examine FreeIPA and Certificate System
++\fIipa-cert-fix\fR will examine IPA and Certificate System
+ certificates and renew certificates that are expired, or close to
+ expiry (less than two weeks).  If any "shared" certificates are
+ renewed, \fIipa-cert-fix\fR will set the current server to be the CA
 diff --git a/install/tools/man/ipa-compat-manage.1 b/install/tools/man/ipa-compat-manage.1
 index f22b1743e31c3b07132acfcfdd8600544f9ace6c..26470331a127af9445c4473525434c237e23dbcf 100644
 --- a/install/tools/man/ipa-compat-manage.1
@@ -395,6 +430,19 @@ index f22b1743e31c3b07132acfcfdd8600544f9ace6c..26470331a127af9445c4473525434c23
  .SH "NAME"
  ipa\-compat\-manage \- Enables or disables the schema compatibility plugin
  .SH "SYNOPSIS"
+diff --git a/install/tools/man/ipa-crlgen-manage.1 b/install/tools/man/ipa-crlgen-manage.1
+index 1fa48cecacce57d5d59945abf57104f5886108ad..f17573bbc5118553bfaab06aabd1a0c2c790b54c 100644
+--- a/install/tools/man/ipa-crlgen-manage.1
++++ b/install/tools/man/ipa-crlgen-manage.1
+@@ -1,7 +1,7 @@
+ .\"
+ .\" Copyright (C) 2019  FreeIPA Contributors see COPYING for license
+ .\"
+-.TH "ipa-crlgen-manage" "1" "Feb 12 2019" "FreeIPA" "FreeIPA Manual Pages"
++.TH "ipa-crlgen-manage" "1" "Feb 12 2019" "IPA" "IPA Manual Pages"
+ .SH "NAME"
+ ipa\-crlgen\-manage \- Enables or disables CRL generation
+ .SH "SYNOPSIS"
 diff --git a/install/tools/man/ipa-csreplica-manage.1 b/install/tools/man/ipa-csreplica-manage.1
 index ab5bfddd884f65b6806c4e69f212324dc5b67c5f..6d039751ea4925b67240cb06fea1e678900b075e 100644
 --- a/install/tools/man/ipa-csreplica-manage.1
@@ -900,10 +948,10 @@ index 5814b6c578c250d253c8eabeff7a787f1b24f10b..36a51ca62899790da3b8788fcd8a32ff
      <!--[if IE]>
      <meta id="ie-detector">
 diff --git a/ipaserver/advise/plugins/legacy_clients.py b/ipaserver/advise/plugins/legacy_clients.py
-index 7916965dddfec7e4c2aa34b081d4c1ba6fc953a7..c0d6c73f4f3d55ac3eb3636273f475415ad1d877 100644
+index 2a56922b83417f30a5b53d875dc597bc8073fd24..0e322fe96a6cd7f9812427f3a5e758ec7d3cde00 100644
 --- a/ipaserver/advise/plugins/legacy_clients.py
 +++ b/ipaserver/advise/plugins/legacy_clients.py
-@@ -92,7 +92,7 @@ class config_redhat_sssd_before_1_9(config_base_legacy_client):
+@@ -94,7 +94,7 @@ class config_redhat_sssd_before_1_9(config_base_legacy_client):
      Legacy client configuration for Red Hat based systems, using SSSD.
      """
      description = ('Instructions for configuring a system with an old version '
@@ -912,7 +960,7 @@ index 7916965dddfec7e4c2aa34b081d4c1ba6fc953a7..c0d6c73f4f3d55ac3eb3636273f47541
                     'instructions is targeted for platforms that include '
                     'the authconfig utility, which are all Red Hat based '
                     'platforms.')
-@@ -127,7 +127,7 @@ class config_generic_linux_sssd_before_1_9(config_base_legacy_client):
+@@ -129,7 +129,7 @@ class config_generic_linux_sssd_before_1_9(config_base_legacy_client):
      using SSSD.
      """
      description = ('Instructions for configuring a system with an old version '
@@ -921,7 +969,7 @@ index 7916965dddfec7e4c2aa34b081d4c1ba6fc953a7..c0d6c73f4f3d55ac3eb3636273f47541
                     'instructions is targeted for linux systems that do not '
                     'include the authconfig utility.')
  
-@@ -182,7 +182,7 @@ class config_redhat_nss_pam_ldapd(config_base_legacy_client):
+@@ -184,7 +184,7 @@ class config_redhat_nss_pam_ldapd(config_base_legacy_client):
      using nss-pam-ldapd.
      """
      description = ('Instructions for configuring a system with nss-pam-ldapd '
@@ -930,7 +978,7 @@ index 7916965dddfec7e4c2aa34b081d4c1ba6fc953a7..c0d6c73f4f3d55ac3eb3636273f47541
                     'for platforms that include the authconfig utility, which '
                     'are all Red Hat based platforms.')
  
-@@ -350,7 +350,7 @@ class config_redhat_nss_ldap(config_base_legacy_client):
+@@ -352,7 +352,7 @@ class config_redhat_nss_ldap(config_base_legacy_client):
      using nss-ldap.
      """
      description = ('Instructions for configuring a system with nss-ldap '
@@ -940,7 +988,7 @@ index 7916965dddfec7e4c2aa34b081d4c1ba6fc953a7..c0d6c73f4f3d55ac3eb3636273f47541
                     'are all Red Hat based platforms.')
  
 diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
-index e4f73ac025dfe8aa19ef99c8d0ab9379caa32610..897c40a6c02899bfe60228dd73e5c71c0b59c3be 100644
+index 930e038e4d7629563d2cea39fe581987dd0edfef..34adfb83763c736139a333c01d0494e47ae21dd4 100644
 --- a/ipaserver/install/dns.py
 +++ b/ipaserver/install/dns.py
 @@ -150,7 +150,7 @@ def install_check(standalone, api, replica, options, hostname):
@@ -952,11 +1000,24 @@ index e4f73ac025dfe8aa19ef99c8d0ab9379caa32610..897c40a6c02899bfe60228dd73e5c71c
          print("")
          print("This includes:")
          print("  * Configure DNS (bind)")
+diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
+index fff054c230c414fdf10eef1cf1b00697a63e386a..a5871dad305b68d79dc40958c97df9a336beedb6 100644
+--- a/ipaserver/install/ipa_cert_fix.py
++++ b/ipaserver/install/ipa_cert_fix.py
+@@ -44,7 +44,7 @@ msg = """
+                           WARNING
+ 
+ ipa-cert-fix is intended for recovery when expired certificates
+-prevent the normal operation of FreeIPA.  It should ONLY be used
++prevent the normal operation of IPA.  It should ONLY be used
+ in such scenarios, and backup of the system, especially certificates
+ and keys, is STRONGLY RECOMMENDED.
+ 
 diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
-index b536685f5f1f3fccab07fd37aa001958e2d38420..1a0b96b000a4c4166054dee9d63b6f239741b40f 100644
+index 006bc92bec581e1983f11bfd75498b5484f2567a..6302c4cf0798aab420198975c01874a8bfd41d4f 100644
 --- a/ipaserver/install/ipa_kra_install.py
 +++ b/ipaserver/install/ipa_kra_install.py
-@@ -90,7 +90,7 @@ class KRAInstall(admintool.AdminTool):
+@@ -91,7 +91,7 @@ class KRAInstall(admintool.AdminTool):
          if options.uninstall:
              sys.exit(
                  'ERROR: Standalone KRA uninstallation was removed in '
@@ -965,7 +1026,7 @@ index b536685f5f1f3fccab07fd37aa001958e2d38420..1a0b96b000a4c4166054dee9d63b6f23
                  'issues.')
          else:
              return KRAInstaller
-@@ -101,7 +101,7 @@ class KRAInstaller(KRAInstall):
+@@ -102,7 +102,7 @@ class KRAInstaller(KRAInstall):
  
      INSTALLER_START_MESSAGE = '''
          ===================================================================
@@ -975,10 +1036,10 @@ index b536685f5f1f3fccab07fd37aa001958e2d38420..1a0b96b000a4c4166054dee9d63b6f23
      '''
  
 diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
-index a341408f78f24055d807ae49c8a0cda81bfb3ec4..eeeb2977a98790585b8b8d4467ee4ad0e6c2f217 100644
+index c1e593e467cdb856a4ab3251ee103f3da3386a82..78169b501809fcd4db43e433ca4a1693d0c2b92f 100644
 --- a/ipaserver/install/server/install.py
 +++ b/ipaserver/install/server/install.py
-@@ -377,7 +377,7 @@ def install_check(installer):
+@@ -387,7 +387,7 @@ def install_check(installer):
  
      print("======================================="
            "=======================================")
@@ -988,10 +1049,10 @@ index a341408f78f24055d807ae49c8a0cda81bfb3ec4..eeeb2977a98790585b8b8d4467ee4ad0
      print("This includes:")
      if setup_ca:
 diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
-index eb354f81ba6e4cbc3848f9c24338fb85cc7639ae..7e9a1ce5d8c2b8a6fe445148afd66e61553b0e07 100644
+index e13b7f18c4d4df7efde50ac9cb7d2f71bfa765cc..7f23414516e718d95ce510d20885240c6dc976b5 100644
 --- a/ipaserver/install/server/replicainstall.py
 +++ b/ipaserver/install/server/replicainstall.py
-@@ -621,7 +621,7 @@ def check_domain_level_is_supported(current):
+@@ -628,7 +628,7 @@ def check_domain_level_is_supported(current):
      above_upper_bound = current > constants.MAX_DOMAIN_LEVEL
  
      if under_lower_bound or above_upper_bound:
@@ -1000,6 +1061,19 @@ index eb354f81ba6e4cbc3848f9c24338fb85cc7639ae..7e9a1ce5d8c2b8a6fe445148afd66e61
                     "the Domain Level which is currently set for "
                     "this domain. The Domain Level needs to be "
                     "raised before installing a replica with "
+diff --git a/ipaserver/plugins/certprofile.py b/ipaserver/plugins/certprofile.py
+index 5e8dbca0469bb0234449cce09e20212886fc3f9e..ffc5fcb4c491dbf33606eae80f9092d38e672d52 100644
+--- a/ipaserver/plugins/certprofile.py
++++ b/ipaserver/plugins/certprofile.py
+@@ -57,7 +57,7 @@ PROFILE CONFIGURATION FORMAT:
+ The profile configuration format is the raw property-list format
+ used by Dogtag Certificate System.  The XML format is not supported.
+ 
+-The following restrictions apply to profiles managed by FreeIPA:
++The following restrictions apply to profiles managed by IPA:
+ 
+ - When importing a profile the "profileId" field, if present, must
+   match the ID given on the command line.
 diff --git a/ipaserver/plugins/sudorule.py b/ipaserver/plugins/sudorule.py
 index 6037938330f13a30d0ccfbedcaac59c567bda0d6..b8a0c82d394edb8744de34394895b86fae2c321f 100644
 --- a/ipaserver/plugins/sudorule.py
diff --git a/SOURCES/1002-Package-copy-schema-to-ca.py.patch b/SOURCES/1002-Package-copy-schema-to-ca.py.patch
index 617dba8..5965d23 100644
--- a/SOURCES/1002-Package-copy-schema-to-ca.py.patch
+++ b/SOURCES/1002-Package-copy-schema-to-ca.py.patch
@@ -1,4 +1,4 @@
-From aca4b01ab02eb6da70c37c55bac15ff4e56d3bb8 Mon Sep 17 00:00:00 2001
+From bf981a8e9c5149aedeb1a7b5195aa52b8daf9f79 Mon Sep 17 00:00:00 2001
 From: Jan Cholasta <jcholast@redhat.com>
 Date: Tue, 14 Mar 2017 16:07:15 +0000
 Subject: [PATCH] Package copy-schema-to-ca.py
@@ -10,10 +10,10 @@ This reverts commit f4c7f1dd8a9ce530a8291219a904686ee47e59c7.
  2 files changed, 5 insertions(+), 2 deletions(-)
 
 diff --git a/freeipa.spec.in b/freeipa.spec.in
-index 93f996c5be670e0ae374a12a85c2465b8e740927..70482ceb65639465d60b0c48fd2ccd6eaa34e097 100644
+index a18a5b4aab335ad104f1263fa3ae8b26659c3095..093df7e2bb3d11381e37a6d150858c1f1680b61d 100644
 --- a/freeipa.spec.in
 +++ b/freeipa.spec.in
-@@ -1475,6 +1475,7 @@ fi
+@@ -1489,6 +1489,7 @@ fi
  # END
  %dir %{_usr}/share/ipa
  %{_usr}/share/ipa/wsgi.py*
@@ -22,10 +22,10 @@ index 93f996c5be670e0ae374a12a85c2465b8e740927..70482ceb65639465d60b0c48fd2ccd6e
  %{_usr}/share/ipa/*.ldif
  %{_usr}/share/ipa/*.uldif
 diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
-index d6e467097808594756d947fa721b8cf10fe7d043..a52336fd71ffb44e3f7dfcc95656bd82065f41cd 100644
+index 1f22d120478a6d4019663281d3191a27a5ee09ea..6f49b8bfa88e00388aec17f26169aa3df399193d 100644
 --- a/ipaserver/install/cainstance.py
 +++ b/ipaserver/install/cainstance.py
-@@ -1416,9 +1416,11 @@ def replica_ca_install_check(config, promote):
+@@ -1579,9 +1579,11 @@ def replica_ca_install_check(config, promote):
      else:
          logger.critical(
              'The master CA directory server does not have necessary schema. '
diff --git a/SOURCES/1003-Revert-Increased-mod_wsgi-socket-timeout.patch b/SOURCES/1003-Revert-Increased-mod_wsgi-socket-timeout.patch
index 041d2c9..215dd4d 100644
--- a/SOURCES/1003-Revert-Increased-mod_wsgi-socket-timeout.patch
+++ b/SOURCES/1003-Revert-Increased-mod_wsgi-socket-timeout.patch
@@ -1,4 +1,4 @@
-From 18b3392fd5a43ffddf5662ead763835df59e1377 Mon Sep 17 00:00:00 2001
+From 2eb2979487c55b542f8c3af1a3bf3ac00fec7a57 Mon Sep 17 00:00:00 2001
 From: Jan Cholasta <jcholast@redhat.com>
 Date: Wed, 22 Jun 2016 13:53:46 +0200
 Subject: [PATCH] Revert "Increased mod_wsgi socket-timeout"
diff --git a/SOURCES/1004-Remove-csrgen.patch b/SOURCES/1004-Remove-csrgen.patch
index 6bc8a5e..37574cd 100644
--- a/SOURCES/1004-Remove-csrgen.patch
+++ b/SOURCES/1004-Remove-csrgen.patch
@@ -1,4 +1,4 @@
-From c9c12213e6c72512177009bf73bd966f13d2ed30 Mon Sep 17 00:00:00 2001
+From 1c884659987140a34ce70e1fb55eb82e8f91b70a Mon Sep 17 00:00:00 2001
 From: Jan Cholasta <jcholast@redhat.com>
 Date: Thu, 16 Mar 2017 09:44:21 +0000
 Subject: [PATCH] Remove csrgen
@@ -39,10 +39,10 @@ https://bugzilla.redhat.com/show_bug.cgi?id=1432630
  delete mode 100644 ipatests/test_ipaclient/data/test_csrgen/templates/identity_base.tmpl
 
 diff --git a/freeipa.spec.in b/freeipa.spec.in
-index 70482ceb65639465d60b0c48fd2ccd6eaa34e097..8d76217fc5a735f36d344b1b783d061b3b0f6271 100644
+index 093df7e2bb3d11381e37a6d150858c1f1680b61d..60da86b27fe603488b5172e591d4478c76c5181d 100644
 --- a/freeipa.spec.in
 +++ b/freeipa.spec.in
-@@ -245,7 +245,6 @@ BuildRequires:  python2-sssdconfig
+@@ -248,7 +248,6 @@ BuildRequires:  python2-sssdconfig
  BuildRequires:  python2-nose
  BuildRequires:  python2-paste
  BuildRequires:  python2-systemd
@@ -50,7 +50,7 @@ index 70482ceb65639465d60b0c48fd2ccd6eaa34e097..8d76217fc5a735f36d344b1b783d061b
  BuildRequires:  python2-augeas
  
  %if 0%{?with_python3}
-@@ -284,7 +283,6 @@ BuildRequires:  python3-libsss_nss_idmap
+@@ -287,7 +286,6 @@ BuildRequires:  python3-libsss_nss_idmap
  BuildRequires:  python3-nose
  BuildRequires:  python3-paste
  BuildRequires:  python3-systemd
@@ -58,7 +58,7 @@ index 70482ceb65639465d60b0c48fd2ccd6eaa34e097..8d76217fc5a735f36d344b1b783d061b
  BuildRequires:  python3-augeas
  BuildRequires:  python3-netaddr
  BuildRequires:  python3-pyasn1
-@@ -640,7 +638,6 @@ Requires: %{name}-client-common = %{version}-%{release}
+@@ -647,7 +645,6 @@ Requires: %{name}-client-common = %{version}-%{release}
  Requires: %{name}-common = %{version}-%{release}
  Requires: python2-ipalib = %{version}-%{release}
  Requires: python2-dns >= 1.15
@@ -66,7 +66,7 @@ index 70482ceb65639465d60b0c48fd2ccd6eaa34e097..8d76217fc5a735f36d344b1b783d061b
  
  %description -n python2-ipaclient
  IPA is an integrated solution to provide centrally managed Identity (users,
-@@ -663,7 +660,6 @@ Requires: %{name}-client-common = %{version}-%{release}
+@@ -670,7 +667,6 @@ Requires: %{name}-client-common = %{version}-%{release}
  Requires: %{name}-common = %{version}-%{release}
  Requires: python3-ipalib = %{version}-%{release}
  Requires: python3-dns >= 1.15
@@ -74,7 +74,7 @@ index 70482ceb65639465d60b0c48fd2ccd6eaa34e097..8d76217fc5a735f36d344b1b783d061b
  
  %description -n python3-ipaclient
  IPA is an integrated solution to provide centrally managed Identity (users,
-@@ -1609,13 +1605,6 @@ fi
+@@ -1623,13 +1619,6 @@ fi
  %{python_sitelib}/ipaclient/remote_plugins/*.py*
  %dir %{python_sitelib}/ipaclient/remote_plugins/2_*
  %{python_sitelib}/ipaclient/remote_plugins/2_*/*.py*
@@ -88,7 +88,7 @@ index 70482ceb65639465d60b0c48fd2ccd6eaa34e097..8d76217fc5a735f36d344b1b783d061b
  %{python_sitelib}/ipaclient-*.egg-info
  
  
-@@ -1640,13 +1629,6 @@ fi
+@@ -1654,13 +1643,6 @@ fi
  %dir %{python3_sitelib}/ipaclient/remote_plugins/2_*
  %{python3_sitelib}/ipaclient/remote_plugins/2_*/*.py
  %{python3_sitelib}/ipaclient/remote_plugins/2_*/__pycache__/*.py*
@@ -180,7 +180,7 @@ index d31b8fef5f2d85e1b3d5ecf425f00ec9c22ac301..00000000000000000000000000000000
 -{{ contents -}}
 -{% endmacro %}
 diff --git a/ipaclient/plugins/cert.py b/ipaclient/plugins/cert.py
-index 6d453d0d6ead9ac7d594bbb27da81de6de2b6862..b3619f30c1ca1f4e921b106d07fb5cb2219a7df0 100644
+index a1ecd9ae45b241fef0bca9b80102fef79832ebd1..eea0ca1e46b3ed46fe7d29b85c1d86dd2131567c 100644
 --- a/ipaclient/plugins/cert.py
 +++ b/ipaclient/plugins/cert.py
 @@ -21,8 +21,6 @@
@@ -202,7 +202,7 @@ index 6d453d0d6ead9ac7d594bbb27da81de6de2b6862..b3619f30c1ca1f4e921b106d07fb5cb2
  register = Registry()
  
  
-@@ -74,87 +69,12 @@ class CertRetrieveOverride(MethodOverride):
+@@ -73,87 +68,12 @@ class CertRetrieveOverride(MethodOverride):
  
  @register(override=True, no_fail=True)
  class cert_request(CertRetrieveOverride):
@@ -349,7 +349,7 @@ index 3a40fa28dc4b7748b2c570943f4a27a22c152353..6356d523e8c0ac63e8892292dd9991c9
      """
      **4100** Base class for builtin execution errors (*4100 - 4199*).
 diff --git a/ipatests/setup.py b/ipatests/setup.py
-index 68a67da5f9590e068ad0ed3e30116264d70e5bce..2464822960a389a8c489babf7b034461396f55e2 100644
+index e9a1a5be004e96a0c9f756d14ffcb0821608688a..6880f184d5acc30f962e3e481d4d62c2db7f78b8 100644
 --- a/ipatests/setup.py
 +++ b/ipatests/setup.py
 @@ -39,7 +39,6 @@ if __name__ == '__main__':
diff --git a/SOURCES/1005-Removing-filesystem-encoding-check.patch b/SOURCES/1005-Removing-filesystem-encoding-check.patch
index 21a7ec0..68b5f03 100644
--- a/SOURCES/1005-Removing-filesystem-encoding-check.patch
+++ b/SOURCES/1005-Removing-filesystem-encoding-check.patch
@@ -1,4 +1,4 @@
-From ed8ee0c41555e42f1a62edca231d116890b36b2d Mon Sep 17 00:00:00 2001
+From 86f46e3669503e47c95bbe4aab8ba121a9cb5a17 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= <tdudlak@redhat.com>
 Date: Fri, 10 Aug 2018 13:16:38 +0200
 Subject: [PATCH] Removing filesystem encoding check
@@ -62,10 +62,10 @@ index 6356d523e8c0ac63e8892292dd9991c9ee8211aa..ae940798779d20cb83b7f96a625c6fac
  # 1000 - 1999: Authentication errors
  class AuthenticationError(PublicError):
 diff --git a/ipalib/plugable.py b/ipalib/plugable.py
-index d78a607123907813d6327212ee6e85b356112791..6aad8d90c9df071b5372cae920e800daae563133 100644
+index 3a5a322f4b753302c58af9cfcb5a29f09e8350bb..535df5c007d99b73e5ff31f5fc4813c0fcf956ac 100644
 --- a/ipalib/plugable.py
 +++ b/ipalib/plugable.py
-@@ -485,11 +485,6 @@ class API(ReadOnly):
+@@ -491,11 +491,6 @@ class API(ReadOnly):
          handler.setFormatter(ipa_log_manager.Formatter(LOGGING_FORMAT_STDERR))
          root_logger.addHandler(handler)
  
@@ -78,7 +78,7 @@ index d78a607123907813d6327212ee6e85b356112791..6aad8d90c9df071b5372cae920e800da
          if self.env.mode in ('dummy', 'unit_test'):
              return  # But not if in unit-test mode
 diff --git a/ipatests/test_cmdline/test_cli.py b/ipatests/test_cmdline/test_cli.py
-index 8211c03515bf70b681da49d27ae11a4e8cb3b44d..a40b5d45ff8406c3ebbb69465e8d71d7d5bdb63a 100644
+index b660532bd6e8db964b8287845ed1b5ebbcb43b9b..60309c58f250a263c8c3d13b0b47773b2db9e4d0 100644
 --- a/ipatests/test_cmdline/test_cli.py
 +++ b/ipatests/test_cmdline/test_cli.py
 @@ -1,8 +1,6 @@
@@ -100,11 +100,10 @@ index 8211c03515bf70b681da49d27ae11a4e8cb3b44d..a40b5d45ff8406c3ebbb69465e8d71d7
  
  
  @pytest.mark.tier0
-@@ -324,21 +320,3 @@ class TestCLIParsing(object):
-                 file=goodcert_headers
+@@ -325,24 +321,6 @@ class TestCLIParsing(object):
              )
  
--
+ 
 -def test_cli_fsencoding():
 -    # https://pagure.io/freeipa/issue/5887
 -    env = {
@@ -122,6 +121,10 @@ index 8211c03515bf70b681da49d27ae11a4e8cb3b44d..a40b5d45ff8406c3ebbb69465e8d71d7
 -    out, err = p.communicate()
 -    assert p.returncode > 0, (out, err)
 -    assert b'System encoding must be UTF-8' in err, (out, err)
+-
+ 
+ IPA_NOT_CONFIGURED = b'IPA is not configured on this system'
+ IPA_CLIENT_NOT_CONFIGURED = b'IPA client is not configured on this system'
 -- 
 2.20.1
 
diff --git a/SOURCES/ipa-centos-branding.patch b/SOURCES/ipa-centos-branding.patch
deleted file mode 100644
index 673cd2f..0000000
--- a/SOURCES/ipa-centos-branding.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 99efecaf87dc1fc9517efaff441a6a7ce46444eb Mon Sep 17 00:00:00 2001
-From: Jim Perrin <jperrin@centos.org>
-Date: Wed, 11 Mar 2015 10:37:03 -0500
-Subject: [PATCH] update for new ntp server method
-
----
- ipaplatform/base/paths.py        | 1 +
- ipaserver/install/ntpinstance.py | 2 ++
- 2 files changed, 3 insertions(+)
-
-diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
-index af50262..5090062 100644
---- a/ipaplatform/base/paths.py
-+++ b/ipaplatform/base/paths.py
-@@ -99,6 +99,7 @@ class BasePathNamespace(object):
-     PKI_TOMCAT_ALIAS_DIR = "/etc/pki/pki-tomcat/alias/"
-     PKI_TOMCAT_PASSWORD_CONF = "/etc/pki/pki-tomcat/password.conf"
-     ETC_REDHAT_RELEASE = "/etc/redhat-release"
-+    ETC_CENTOS_RELEASE = "/etc/centos-release"
-     RESOLV_CONF = "/etc/resolv.conf"
-     SAMBA_KEYTAB = "/etc/samba/samba.keytab"
-     SMB_CONF = "/etc/samba/smb.conf"
-diff --git a/ipaserver/install/ntpinstance.py b/ipaserver/install/ntpinstance.py
-index c653525..4b0578b 100644
---- a/ipaserver/install/ntpinstance.py
-+++ b/ipaserver/install/ntpinstance.py
-@@ -44,6 +44,8 @@ class NTPInstance(service.Service):
-         os = ""
-         if ipautil.file_exists(paths.ETC_FEDORA_RELEASE):
-             os = "fedora"
-+        elif ipautil.file_exists(paths.ETC_CENTOS_RELEASE):
-+            os = "centos"
-         elif ipautil.file_exists(paths.ETC_REDHAT_RELEASE):
-             os = "rhel"
- 
--- 
-1.8.3.1
-
diff --git a/SPECS/ipa.spec b/SPECS/ipa.spec
index b803377..b2253d6 100644
--- a/SPECS/ipa.spec
+++ b/SPECS/ipa.spec
@@ -51,6 +51,11 @@
 %global selinux_policy_version 3.13.1-224
 %global slapi_nis_version 0.56.0-4
 %global python2_ldap_version 2.4.15
+# 10.5.9-5: https://bugzilla.redhat.com/show_bug.cgi?id=1596629
+%global pki_version 10.5.9-5
+# Fix for "Installation fails: Replica Busy"
+# https://bugzilla.redhat.com/show_bug.cgi?id=1598478
+%global ds_version 1.3.8.4-15
 %else
 # 1.15.1-7: certauth (http://krbdev.mit.edu/rt/Ticket/Display.html?id=8561)
 %global krb5_version 1.15.1-7
@@ -71,7 +76,11 @@
 %global python2_ldap_version 2.4.15
 %global python3_ldap_version 2.4.35.1-2
 %endif
-
+# 10.5.12: https://pagure.io/dogtagpki/issue/3043
+%global pki_version 10.5.12
+# Fix for "Installation fails: Replica Busy"
+# https://pagure.io/389-ds-base/issue/49818
+%global ds_version 1.3.8.8
 %endif
 
 %define krb5_base_version %(LC_ALL=C rpm -q --qf '%%{VERSION}' krb5-devel | grep -Eo '^[^.]+\.[^.]+')
@@ -84,7 +93,7 @@
 
 # Work-around fact that RPM SPEC parser does not accept
 # "Version: @VERSION@" in freeipa.spec.in used for Autoconf string replacement
-%define IPA_VERSION 4.6.4
+%define IPA_VERSION 4.6.5
 %define AT_SIGN @
 # redefine IPA_VERSION only if its value matches the Autoconf placeholder
 %if "%{IPA_VERSION}" == "%{AT_SIGN}VERSION%{AT_SIGN}"
@@ -93,7 +102,7 @@
 
 Name:           ipa
 Version:        %{IPA_VERSION}
-Release:        10%{?dist}.6
+Release:        11%{?dist}
 Summary:        The Identity, Policy and Audit system
 
 Group:          System Environment/Base
@@ -101,77 +110,66 @@ License:        GPLv3+
 URL:            http://www.freeipa.org/
 Source0:        https://releases.pagure.org/freeipa/freeipa-%{version}.tar.gz
 # RHEL spec file only: START: Change branding to IPA and Identity Management
-#Source1:        header-logo.png
-#Source2:        login-screen-background.jpg
-#Source3:        login-screen-logo.png
-#Source4:        product-name.png
+Source1:        header-logo.png
+Source2:        login-screen-background.jpg
+Source4:        product-name.png
 # RHEL spec file only: END: Change branding to IPA and Identity Management
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 # RHEL spec file only: START
-Patch0001:      0001-Use-replace-instead-of-add-to-set-new-default-ipaSEL.patch
-Patch0002:      0002-Sort-and-shuffle-SRV-record-by-priority-and-weight.patch
-Patch0003:      0003-Increase-WSGI-process-count-to-5-on-64bit.patch
-Patch0004:      0004-Always-set-ca_host-when-installing-replica.patch
-Patch0005:      0005-ipaserver-config-plugin-Increase-search-records-mini.patch
-Patch0006:      0006-Improve-and-fix-timeout-bug-in-wait_for_entry.patch
-Patch0007:      0007-Use-common-replication-wait-timeout-of-5min.patch
-Patch0008:      0008-Fix-replication-races-in-Dogtag-admin-code.patch
-Patch0009:      0009-Allow-anonymous-access-to-ParentID-attribute.patch
-Patch0010:      0010-Use-4-WSGI-workers-on-64bit-systems.patch
-Patch0011:      0011-Query-for-server-role-IPA-master.patch
-Patch0012:      0012-Only-create-DNS-SRV-records-for-ready-server.patch
-Patch0013:      0013-Delay-enabling-services-until-end-of-installer.patch
-Patch0014:      0014-ipa-client-uninstall-clean-the-state-store-when-rest.patch
-Patch0015:      0015-Fix-CA-topology-warning.patch
-Patch0016:      0016-replicainstall-DS-SSL-replica-install-pick-right-cer.patch
-Patch0017:      0017-Fix-race-condition-in-get_locations_records.patch
-Patch0018:      0018-Tune-DS-replication-settings.patch
-Patch0019:      0019-Fix-DNSSEC-install-regression.patch
-Patch0020:      0020-Handle-races-in-replica-config.patch
-Patch0021:      0021-Fix-regression-Handle-unicode-where-str-is-expected.patch
-Patch0022:	0022-In-IPA-4.4-when-updating-userpassword-with-ldapmodif.patch
-Patch0023:	0023-Move-fips_enabled-to-a-common-library-to-share-acros.patch
-Patch0024:	0024-ipasam-do-not-use-RC4-in-FIPS-mode.patch
-Patch0025:	0025-Re-open-the-ldif-file-to-prevent-error-message.patch
-Patch0026:      0026-Catch-ACIError-instead-of-invalid-credentials.patch
-Patch0027:      0027-Auto-retry-failed-certmonger-requests.patch
-Patch0028:      0028-Wait-for-client-certificates.patch
-Patch0029:      0029-Fix-KRA-replica-installation-from-CA-master.patch
-Patch0030:      0030-ipaserver-plugins-cert.py-Added-reason-to-raise-of-e.patch
-Patch0031:      0031-ipa-commands-print-IPA-is-not-configured-when-ipa-is.patch
-Patch0032:      0032-DS-replication-settings-fix-regression-with-3.3-mast.patch
-Patch0033:      0033-Disable-message-about-log-in-ipa-backup-if-IPA-is-no.patch
-Patch0034:      0034-uninstall-v-remove-Tracebacks.patch
-Patch0035:      0035-Do-not-set-ca_host-when-setup-ca-is-used.patch
-Patch0036:      0036-Fix-ipa-replica-install-when-key-not-protected-by-PI.patch
-Patch0037:      0037-Clear-next-field-when-returnining-list-elements-in-q.patch
-Patch0038:      0038-Add-cmocka-unit-tests-for-ipa-otpd-queue-code.patch
-Patch0039:      0039-ipa-server-install-do-not-perform-forwarder-validati.patch
-Patch0040:	0040-ipa-replica-install-fix-pkinit-setup.patch
-Patch0041:	0041-ipa-replica-install-properly-use-the-file-store.patch
-Patch0042:	0042-Ensure-that-public-cert-and-CA-bundle-are-readable.patch
-Patch0043:	0043-Always-make-ipa.p11-kit-world-readable.patch
-Patch0044:	0044-Make-etc-httpd-alias-world-readable-executable.patch
-Patch0045:	0045-Fix-permission-of-public-files-in-upgrader.patch
-Patch0046:	0046-Find-orphan-automember-rules.patch
-Patch0047:	0047-Add-a-shared-vault-retrieve-test.patch
-Patch0048:	0048-Add-a-Find-enabled-services-ACI-in-20-aci.update-so-.patch
-Patch0049:	0049-ipaldap.py-fix-method-creating-a-ldap-filter-for-IPA.patch
-Patch0050:	0050-ipatests-add-xmlrpc-test-for-user-host-find-certific.patch
-Patch0051:	0051-ipa-upgrade-handle-double-encoded-certificates.patch
-Patch0052:	0052-ipatests-add-upgrade-test-for-double-encoded-cacert.patch
-Patch0053:	0053-ipatests-fix-TestUpgrade-test_double_encoded_cacert.patch
-Patch0054:	0054-ipatest-add-test-for-ipa-pkinit-manage-enable-disabl.patch
-Patch0055:	0055-PKINIT-fix-ipa-pkinit-manage-enable-disable.patch
-Patch0056:	0056-replication-check-remote-ds-version-before-editing-a.patch
-Patch0057:	0057-ipa-sidgen-make-internal-fetch_attr-helper-really-in.patch
-Patch0058:	0058-replica-installation-add-master-record-only-if-in-ma.patch
-Patch0059:	0059-ipatests-add-test-for-replica-in-forward-zone.patch
-Patch0060:	0060-Add-workaround-for-slow-host-service-del.patch
-Patch0061:	0061-Optimize-cert-remove-case.patch
-Patch0062:	0062-Update-mod_nss-cipher-list-so-there-is-overlap-with-.patch
-Patch0063:	0063-Consider-configured-servers-as-valid.patch
+Patch0001:	0001-Coverity-fix-issue-in-ipa_extdom_extop.c.patch
+Patch0002:	0002-Web-UI-topology-graph-Show-FQDN-for-nodes-if-they-ha.patch
+Patch0003:	0003-ipa-replica-manage-fix-force-sync.patch
+Patch0004:	0004-Unify-and-simplify-LDAP-service-discovery.patch
+Patch0005:	0005-Use-api.env.container_masters.patch
+Patch0006:	0006-Consolidate-container_masters-queries.patch
+Patch0007:	0007-Replace-hard-coded-paths-with-path-constants.patch
+Patch0008:	0008-Support-Samba-4.9.patch
+Patch0009:	0009-Add-design-page-for-one-way-trust-to-AD-with-shared-.patch
+Patch0010:	0010-trust-allow-trust-agents-to-read-POSIX-identities-of.patch
+Patch0011:	0011-trusts-add-support-for-one-way-shared-secret-trust.patch
+Patch0012:	0012-upgrade-upgrade-existing-trust-agreements-to-new-lay.patch
+Patch0013:	0013-upgrade-add-trust-upgrade-to-actual-upgrade-code.patch
+Patch0014:	0014-RFE-ipa-client-should-setup-openldap-for-GSSAPI.patch
+Patch0015:	0015-Add-uniqueness-constraint-on-CA-ACL-name.patch
+Patch0016:	0016-Add-sysadm_r-to-default-SELinux-user-map-order.patch
+Patch0017:	0017-Extend-CALessBase-installer_server-to-accept-extra_a.patch
+Patch0018:	0018-Skip-zone-overlap-check-with-auto-reverse.patch
+Patch0019:	0019-Add-hidden-replica-feature.patch
+Patch0020:	0020-ipatests-Exercise-hidden-replica-feature.patch
+Patch0021:	0021-Simplify-and-improve-tests.patch
+Patch0022:	0022-Implement-server-state-state-enabled-hidden.patch
+Patch0023:	0023-Consider-hidden-servers-as-role-provider.patch
+Patch0024:	0024-Improve-config-show-to-show-hidden-servers.patch
+Patch0025:	0025-More-test-fixes.patch
+Patch0026:	0026-Don-t-allow-to-hide-last-server-for-a-role.patch
+Patch0027:	0027-Synchronize-hidden-state-from-IPA-master-role.patch
+Patch0028:	0028-Test-replica-installation-from-hidden-replica.patch
+Patch0029:	0029-Add-design-draft.patch
+Patch0030:	0030-Don-t-fail-if-config-show-does-not-return-servers.patch
+Patch0031:	0031-Extract-ca_renewal-cert-update-subroutine.patch
+Patch0032:	0032-cainstance-add-function-to-determine-ca_renewal-nick.patch
+Patch0033:	0033-constants-add-ca_renewal-container.patch
+Patch0034:	0034-Add-ipa-cert-fix-tool.patch
+Patch0035:	0035-ipa-cert-fix-add-man-page.patch
+Patch0036:	0036-ipa-cert-fix-use-customary-exit-statuses.patch
+Patch0037:	0037-Show-a-notification-that-sssd-needs-restarting-after.patch
+Patch0038:	0038-ipa-server-upgrade-fix-add_systemd_user_hbac.patch
+Patch0039:	0039-ipa-setup-kra-fix-python2-parameter.patch
+Patch0040:	0040-oddjob-allow-to-pass-options-to-trust-fetch-domains.patch
+Patch0041:	0041-adtrust-define-Guests-mapping-after-creating-cifs-pr.patch
+Patch0042:	0042-net-groupmap-force-using-empty-config-when-mapping-G.patch
+Patch0043:	0043-Bypass-D-BUS-interface-definition-deficiences-for-tr.patch
+Patch0044:	0044-ipactl-restart-fix-wrong-logic-when-checking-service.patch
+Patch0045:	0045-replica-install-acknowledge-ca_host-override.patch
+Patch0046:	0046-ipa-console-catch-proper-exception-when-history-file.patch
+Patch0047:	0047-upgrade-adtrust-catch-empty-result-when-retrieving-l.patch
+Patch0048:	0048-Consider-configured-servers-as-valid.patch
+Patch0049:	0049-ipa-cert-fix-handle-pki-server-cert-fix-failure.patch
+Patch0050:	0050-ipa-cert-fix-fix-spurious-renewal-master-change.patch
+Patch0051:	0051-adtrust-upgrade-fix-wrong-primary-principal-name.patch
+Patch0052:	0052-adtrust-upgrade-fix-wrong-primary-principal-name-par.patch
+Patch0053:	0053-trust-fetch-domains-make-sure-we-use-right-KDC-when-.patch
 Patch1001:      1001-Change-branding-to-IPA-and-Identity-Management.patch
 Patch1002:      1002-Package-copy-schema-to-ca.py.patch
 Patch1003:      1003-Revert-Increased-mod_wsgi-socket-timeout.patch
@@ -218,7 +216,7 @@ BuildRequires:  diffstat
 BuildRequires: java-1.7.0-openjdk-devel
 
 # 1.3.3.9: DS_Sleep (https://fedorahosted.org/389/ticket/48005)
-BuildRequires:  389-ds-base-devel >= 1.3.3.9
+BuildRequires:  389-ds-base-devel >= %{ds_version}
 BuildRequires:  svrcore-devel
 %if 0%{?rhel}
 BuildRequires:  samba-devel >= 4.7.0
@@ -288,7 +286,7 @@ BuildRequires:  python-dns >= 1.12.0-3
 BuildRequires:  jsl
 BuildRequires:  python-yubico
 # pki Python package
-BuildRequires:  pki-base-python2 >= 10.5.1-2
+BuildRequires:  pki-base-python2 >= %{pki_version}
 BuildRequires:  python-pytest-multihost
 BuildRequires:  python-pytest-sourceorder
 BuildRequires:  python-jwcrypto
@@ -327,7 +325,7 @@ BuildRequires:  python3-qrcode-core >= 5.0.0
 BuildRequires:  python3-dns >= 1.12.0-3
 BuildRequires:  python3-yubico
 # pki Python package
-BuildRequires:  pki-base-python3 >= 10.5.1-2
+BuildRequires:  pki-base-python3 >= %{pki_version}
 BuildRequires:  python3-pytest-multihost
 BuildRequires:  python3-pytest-sourceorder
 BuildRequires:  python3-jwcrypto
@@ -378,10 +376,7 @@ Requires: %{name}-client = %{version}-%{release}
 Requires: %{name}-common = %{version}-%{release}
 Requires: python2-ipaserver = %{version}-%{release}
 Requires: python-ldap >= %{python2_ldap_version}
-# 1.3.7.9-1: https://bugzilla.redhat.com/show_bug.cgi?id=1459946
-#            https://bugzilla.redhat.com/show_bug.cgi?id=1511462
-#            https://bugzilla.redhat.com/show_bug.cgi?id=1514033
-Requires: 389-ds-base >= 1.3.7.9-1
+Requires: 389-ds-base >= %{ds_version}
 Requires: openldap-clients > 2.4.35-4
 Requires: nss >= 3.14.3-12.0
 Requires: nss-tools >= 3.14.3-12.0
@@ -408,19 +403,14 @@ Requires(post): systemd-units
 Requires: selinux-policy >= %{selinux_policy_version}
 Requires(post): selinux-policy-base >= %{selinux_policy_version}
 Requires: slapi-nis >= %{slapi_nis_version}
-# Required because of: https://bugzilla.redhat.com/show_bug.cgi?id=1475238
-# related pki-core update: https://bugzilla.redhat.com/show_bug.cgi?id=1305993
-Requires: pki-ca >= 10.5.1-2
-Requires: pki-kra >= 10.5.1-2
+Requires: pki-ca >= %{pki_version}
+Requires: pki-kra >= %{pki_version}
 Requires(preun): python systemd-units
 Requires(postun): python systemd-units
 Requires: policycoreutils >= 2.1.14-37
 Requires: tar
 Requires(pre): certmonger >= 0.78.4-10
-# 1.3.7.9-1: https://bugzilla.redhat.com/show_bug.cgi?id=1459946
-#            https://bugzilla.redhat.com/show_bug.cgi?id=1511462
-#            https://bugzilla.redhat.com/show_bug.cgi?id=1514033
-Requires(pre): 389-ds-base >= 1.3.7.9-1
+Requires(pre): 389-ds-base >= %{ds_version}
 Requires: fontawesome-fonts
 Requires: open-sans-fonts
 Requires: openssl >= 1:1.0.1e-42
@@ -433,6 +423,7 @@ Requires: oddjob
 Requires: gssproxy >= 0.7.0-2
 # 1.15.2: FindByNameAndCertificate (https://pagure.io/SSSD/sssd/issue/3050)
 Requires: sssd-dbus >= 1.15.2
+Requires: system-logos >= 70.7.0
 
 Provides: %{alt_name}-server = %{version}
 Conflicts: %{alt_name}-server
@@ -487,7 +478,7 @@ Requires: dbus-python
 Requires: python-dns >= 1.12.0-3
 Requires: python-kdcproxy >= 0.3
 Requires: rpm-libs
-Requires: pki-base-python2 >= 10.5.1-2
+Requires: pki-base-python2 >= %{pki_version}
 # python-augeas >= 0.5 supports replace method
 Requires: python-augeas >= 0.5
 
@@ -522,7 +513,7 @@ Requires: python3-kdcproxy >= 0.3
 # python3-augeas >= 0.5 supports replace method
 Requires: python3-augeas >= 0.5
 Requires: rpm-libs
-Requires: pki-base-python3 >= 10.5.1-2
+Requires: pki-base-python3 >= %{pki_version}
 
 %description -n python3-ipaserver
 IPA is an integrated solution to provide centrally managed Identity (users,
@@ -982,10 +973,9 @@ cp -r %{_builddir}/freeipa-%{version} %{_builddir}/freeipa-%{version}-python3
 %endif # with_python3
 
 # RHEL spec file only: START: Change branding to IPA and Identity Management
-#cp %SOURCE1 install/ui/images/header-logo.png
-#cp %SOURCE2 install/ui/images/login-screen-background.jpg
-#cp %SOURCE3 install/ui/images/login-screen-logo.png
-#cp %SOURCE4 install/ui/images/product-name.png
+cp %SOURCE1 install/ui/images/header-logo.png
+cp %SOURCE2 install/ui/images/login-screen-background.jpg
+cp %SOURCE4 install/ui/images/product-name.png
 # RHEL spec file only: END: Change branding to IPA and Identity Management
 
 
@@ -1009,8 +999,7 @@ find \
 %configure --with-vendor-suffix=-%{release} \
            %{enable_server_option} \
            %{with_ipatests_option} \
-           %{linter_options} \
-           --with-ipaplatform=rhel
+           %{linter_options}
 
 %make_build
 
@@ -1031,8 +1020,7 @@ find \
 %configure --with-vendor-suffix=-%{release} \
            %{enable_server_option} \
            %{with_ipatests_option} \
-           %{linter_options} \
-           --with-ipaplatform=rhel
+           %{linter_options}
 popd
 %endif # with_python3
 
@@ -1111,6 +1099,10 @@ ln -s %{_bindir}/ipa-test-task-%{python2_version} %{buildroot}%{_bindir}/ipa-tes
 # remove files which are useful only for make uninstall
 find %{buildroot} -wholename '*/site-packages/*/install_files.txt' -exec rm {} \;
 
+# RHEL spec file only: START: Replace login-screen-logo.png with a symlink
+ln -sf %{_datadir}/pixmaps/fedora-gdm-logo.png %{buildroot}%{_usr}/share/ipa/ui/images/login-screen-logo.png
+# RHEL spec file only: END: Replace login-screen-logo.png with a symlink
+
 %find_lang %{gettext_domain}
 
 %if ! %{ONLY_CLIENT}
@@ -1362,8 +1354,10 @@ fi
 %{_sbindir}/ipactl
 %{_sbindir}/ipa-advise
 %{_sbindir}/ipa-cacert-manage
+%{_sbindir}/ipa-cert-fix
 %{_sbindir}/ipa-winsync-migrate
 %{_sbindir}/ipa-pkinit-manage
+%{_sbindir}/ipa-crlgen-manage
 %{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit
 %{_libexecdir}/certmonger/ipa-server-guard
 %dir %{_libexecdir}/ipa
@@ -1428,6 +1422,8 @@ fi
 %{_mandir}/man1/ipa-cacert-manage.1*
 %{_mandir}/man1/ipa-winsync-migrate.1*
 %{_mandir}/man1/ipa-pkinit-manage.1*
+%{_mandir}/man1/ipa-crlgen-manage.1*
+%{_mandir}/man1/ipa-cert-fix.1*
 
 %files -n python2-ipaserver
 %defattr(-,root,root,-)
@@ -1748,42 +1744,161 @@ fi
 
 
 %changelog
-* Mon Jul 29 2019 CentOS Sources <bugs@centos.org> - 4.6.4-10.el7.centos.6
-- Roll in CentOS Branding
-
-* Thu Jun 6 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.4-10.el7_6.6
-- Resolves: 1716882 - ERROR: invalid 'PKINIT enabled server': all masters must have IPA master role enabled
+* Fri Jun 28 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-11.el7
+- Resolves: 1723473 - ipa upgrade fails with trust entry already exists
+  - adtrust upgrade: fix wrong primary principal name, part 2
+- Resolves: 1686302 - ipa trust fetch-domains, server parameter ignored
+  - trust-fetch-domains: make sure we use right KDC when --server is specified
+
+* Wed Jun 26 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-10.el7
+- Resolves: 1723473 - ipa upgrade fails with trust entry already exists
+  - adtrust upgrade: fix wrong primary principal name
+
+* Tue Jun 4 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-9.el7
+- Resolves: 1712794 - ERROR: invalid 'PKINIT enabled server': all masters must have IPA master role enabled
   - Consider configured servers as valid
 
-* Mon Feb 4 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.4-10.el7_6.3
-- Resolves: 1672343 pki spawn fails for IPA replica install from RHEL6 IPA master
+* Mon May 13 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-8.el7
+- Resolves: 1702651 - Command ipa conole is broken
+  - ipa console: catch proper exception when history file can not be open
+- Resolves: 1704796 - Wrong CA replication topology created with two replicas
+  - replica install: acknowledge ca_host override
+- Resolves: 1708873 - Unable to upgrade ipa data: IPA version error: data needs to be upgraded (expected version '4.7.90.pre1-3.fc30', current version '4.7.2-8.fc30')
+  - upgrade: adtrust - catch empty result when retrieving list of trusts
+
+* Tue Apr 30 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-7.el7
+- Resolves: 1704227 - Wrong logic in ipactl restart leads to start instead of restart pki-tomcatd
+  - ipactl restart: fix wrong logic when checking service list
+
+* Fri Apr 19 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-6.el7
+- Resolves: 1700804 - Update Red Hat logo in IdM Server
+
+* Mon Apr 8 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-5.el7
+- Resolves: 1697242 Communication with oddjob helper fails
+  - Bypass D-BUS interface definition deficiences for trust-fetch-domains
+
+* Wed Apr 3 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-4.el7
+- Resolves: 1686302 ipa trust fetch-domains, server parameter ignored
+  - oddjob: allow to pass options to trust-fetch-domains
+- Resolves: 1695481 ipa trust-add failing with CIFS server communication error
+  - adtrust: define Guests mapping after creating cifs/ principal
+  - net groupmap: force using empty config when mapping Guests
+
+* Mon Apr 1 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-3.el7
+- Resolves: 1694623 ipa-kra-install failing with invalid 'role_servrole': must be Unicode text error
+  - ipa-setup-kra: fix python2 parameter
+- Resolves: 1694596 ipa-server-upgrade fails with ConversionError: invalid 'cn': must be Unicode text
+  - ipa-server-upgrade: fix add_systemd_user_hbac
+
+* Fri Mar 29 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-2.el7
+- Resolves: 1691334 New defect found in ipa-4.6.5-1.el7
+  - Coverity: fix issue in ipa_extdom_extop.c
+- Resolves: 1666843 ipa-replica-manage force-sync --from keeps prompting "No status yet"
+  - ipa-replica-manage: fix force-sync
+- Resolves: 1594245 [RFE] sysadm_r should be included in default SELinux user map order
+  - Add sysadm_r to default SELinux user map order
+- Resolves: 1527215 RFE: ipa client should setup openldap for GSSAPI
+  - RFE: ipa client should setup openldap for GSSAPI
+- Resolves: 1518939 RFE: Extend IPA to support unadvertised replicas
+  - Unify and simplify LDAP service discovery
+  - Use api.env.container_masters
+  - Consolidate container_masters queries
+  - Add hidden replica feature
+  - ipatests: Exercise hidden replica feature
+  - Simplify and improve tests
+  - Implement server-state --state=enabled/hidden
+  - Consider hidden servers as role provider
+  - Improve config-show to show hidden servers
+  - More test fixes
+  - Don't allow to hide last server for a role
+  - Synchronize hidden state from IPA master role
+  - Test replica installation from hidden replica
+  - Add design draft
+  - Don't fail if config-show does not return servers
+- Resolves: 1517886 double ca acl provoke console error.
+  - Add uniqueness constraint on CA ACL name
+- Resolves: 1498110 Using --auto-reverse and --allow-zone-overlap does not skip zone overlap check
+  - Extend CALessBase::installer_server to accept extra_args
+  - Skip zone overlap check with auto-reverse
+- Resolves: 1496963 [RFE] Provide an option to include FQDN in IDM topology graph
+  - Web UI (topology graph): Show FQDN for nodes if they have no common DNS zone
+- Resolves: 1345975 [RFE] Support One-Way Trust authenticated by trust secret
+  - Replace hard-coded paths with path constants
+  - Support Samba 4.9
+  - Add design page for one-way trust to AD with shared secret
+  - trust: allow trust agents to read POSIX identities of trust
+  - trusts: add support for one-way shared secret trust
+  - upgrade: upgrade existing trust agreements to new layout
+  - upgrade: add trust upgrade to actual upgrade code
+- Resolves: 1690191 [RFE] Offline Certificate Renewal System
+  - Extract ca_renewal cert update subroutine
+  - cainstance: add function to determine ca_renewal nickname
+  - constants: add ca_renewal container
+  - Add ipa-cert-fix tool
+  - ipa-cert-fix: add man page
+  - ipa-cert-fix: use customary exit statuses
+- Resolves: 1631826 Create a warning that SSSD needs restart after idrange-mod
+  - Show a notification that sssd needs restarting after idrange-mod
+
+* Wed Mar 20 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.5-1.el7
+- Resolves: 1677197 Rebase IPA to latest 4.6.x version
+- Resolves: 1690037[RFE] Add utility to promote CA replica to CRL master
+- Resolves: 1689585 Cannot install ipa-server on rhel7.7
+- Resolves: 1672184 Fix compile issue with new 389-ds
+- Resolves: 1672180 pki spawn fails for IPA replica install from RHEL6 IPA master
+- Resolves: 1669012 host_del and host_disable fails, ra.find() search for every certificates instead of the host's certificate by subject
+- Resolves: 1658701 ipa-replica-install fails migrating RHEL 6 to 7
+- Resolves: 1651834 searching for ipa users by certificate fails
+- Resolves: 1644874 IPA Upgrade failed with "unable to convert the attribute u'cACertificate;binary'"
+- Resolves: 1638545 ipa-advise command points to old URL's.
+- Resolves: 1637717 RFE: Validation and better error messages when novajoin fails because of SSL errors
+- Resolves: 1599939 'ipa vault-retrieve' is failing with "ipa: ERROR: an internal error has occurred"
+- Resolves: 1593454 In IPA WebUI, a warning appears in the background(warning message behind the dialog box).
+- Resolves: 1586268 [RFE] Red Hat Identity Manager IP SANs
+- Resolves: 1579037 Adding 3rd Party CAs to IPA results in SmartCard preparation script failure
+- Resolves: 1577967 Users with user creation/modification privileges fail to add the "--radius-username" option when creating users
+- Resolves: 1572674 ipa-cacert-manage cannot import PKCS#7 files
+- Resolves: 1562422 [RFE] Allow IPA Services to Start After the IPA Backup Has Completed
+- Resolves: 1562396 IPA numeric username breaks sudo and getent
+- Resolves: 1533228 The ipa-replica-install command failed, exception: ValidationError: invalid 'dnszoneidnsname': only master zones can contain records
+- Resolves: 1497334 ipa-server-install should prevent installations with single label domains
+- Resolves: 1493541 ipa-pkinit-manage reports a switch from local pkinit to full pkinit configuration was successful although it was not.
+- Resolves: 1485217 [RFE] Warn or adjust umask if it is too restrictive to break installation
+- Resolves: 1428690 ipa-backup does not create log file at /var/log/
+- Resolves: 1408439 ipa idoverrideuser-find view --anchor fails to return output
+- Resolves: 1390757 automember-rebuild crashes
+- Resolves: 1376024 During one step replica install the command accepts both OTP and Admin password simultaneously
+- Resolves: 1245626 ipa-client-install modifies /etc/openldap/ldap.conf in a way which is unhandy for openldap-clients
+
+* Mon Feb 04 2019 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.4-12.el7
+- Resolves: 1672180 pki spawn fails for IPA replica install from RHEL6 IPA master
   - Update mod_nss cipher list so there is overlap with a 4.x master
-- Resolves: 1672342 Fix compile issue with new 389-ds
+- Resolves: 1672184 Fix compile issue with new 389-ds
   - ipa-sidgen: make internal fetch_attr helper really internal
-- Resolves: 1672176 host_del and host_disable fails, ra.find() search for every certificates instead of the host's certificate by subject
+- Resolves: 1669012 host_del and host_disable fails, ra.find() search for every certificates instead of the host's certificate by subject
   - Add workaround for slow host/service del
   - Optimize cert remove case
-- Resolves: 1672238 The ipa-replica-install command failed, exception: ValidationError: invalid 'dnszoneidnsname': only master zones can contain record
+- Resolves: 1533228 The ipa-replica-install command failed, exception: ValidationError: invalid 'dnszoneidnsname': only master zones can contain record
   - replica installation: add master record only if in managed zone
   - ipatests: add test for replica in forward zone
 
-* Tue Dec 18 2018 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.4-10.el7_6.2
-- Resolves: 1659492 searching for ipa users by certificate fails
+* Tue Dec 18 2018 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.4-11.el7
+- Resolves: 1651834 searching for ipa users by certificate fails
   - ipaldap.py: fix method creating a ldap filter for IPACertificate
   - ipatests: add xmlrpc test for user|host-find --certificate
-- Resolves: 1659509 IPA Upgrade failed with "unable to convert the attribute u'cACertificate;binary'"
+- Resolves: 1644874 IPA Upgrade failed with "unable to convert the attribute u'cACertificate;binary'"
   - ipa upgrade: handle double-encoded certificates
   - ipatests: add upgrade test for double-encoded cacert
   - ipatests: fix TestUpgrade::test_double_encoded_cacert
-- Resolves: 1659500 'ipa vault-retrieve' is failing with "ipa: ERROR: an internal error has occurred"
+- Resolves: 1599939 'ipa vault-retrieve' is failing with "ipa: ERROR: an internal error has occurred"
   - Add a shared-vault-retrieve test
   - Add a "Find enabled services" ACI in 20-aci.update so that all users can find IPA servers and services. ACI suggested by Christian Heimes.
-- Resolves: 1659511 ipa-pkinit-manage reports a switch from local pkinit to full pkinit configuration was successful although it was not.
+- Resolves: 1493541 ipa-pkinit-manage reports a switch from local pkinit to full pkinit configuration was successful although it was not.
   - ipatest: add test for ipa-pkinit-manage enable|disable
   - PKINIT: fix ipa-pkinit-manage enable|disable
-- Resolves: 1659499 automember-rebuild crashes
+- Resolves: 1390757 automember-rebuild crashes
   - Find orphan automember rules
-- Resolves: 1660389 ipa-replica-install fails migrating RHEL 6 to 7
+- Resolves: 1658701 ipa-replica-install fails migrating RHEL 6 to 7
   - replication: check remote ds version before editing attributes
 
 * Tue Sep 18 2018 Florence Blanc-Renaud <frenaud@redhat.com> - 4.6.4-10.el7