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 +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 +--- + 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 -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 -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Florence Blanc-Renaud ---- - 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 -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 -Reviewed-By: Rob Crittenden ---- - 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 . - # - -+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 +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 +--- + 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 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(); + }); + + +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 @@ +
  • Utils Test Suite +
  • Build Test Suite +
  • Binding Test Suite ++
  • Topology Test Suite + + + +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 @@ ++ ++ ++ ++ Topology Test Suite ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++

    Topology Test Suite

    ++

    ++
    ++

    ++
      ++
      ++ ++ +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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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 +Date: Mon, 25 Mar 2019 14:22:59 +0100 +Subject: [PATCH] ipa-replica-manage: fix force-sync + +ipa-replica-manage force-sync --from 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 -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 -Reviewed-By: Fraser Tweedale ---- - 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 +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 +Reviewed-By: Thomas Woerner +--- + 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 +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 +Reviewed-By: Thomas Woerner +--- + 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 -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 ---- - 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 +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 +Reviewed-By: Thomas Woerner +--- + 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 -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 -Reviewed-By: Fraser Tweedale ---- - 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 +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 +Reviewed-By: Rob Crittenden +(cherry picked from commit 2391c75e3d7efcdc5c2f49defa5138fc7e6def06) + +Reviewed-By: Christian Heimes +--- + 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 -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 -Reviewed-By: Fraser Tweedale ---- - 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 -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 -Reviewed-By: Fraser Tweedale ---- - 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 +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 +(cherry picked from commit 703497532abe4189835d0a02b32f9919c889bc1c) + +Reviewed-By: Christian Heimes +--- + .../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 +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 +--- + .../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 --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 -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 -Reviewed-By: Christian Heimes ---- - 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 -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 -Reviewed-By: Tibor Dudlak ---- - 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 +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 +--- + 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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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=[, ]) - 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 +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 +--- + 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/@, 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: @ is only used ++ * for SSSD to be able to talk to AD DCs but it has to ++ * have canonical name set to $ 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/@, enabled by default */ ++ failed += !set_krb_princ(ipasam_state, tmp_ctx, ++ princ_l, NULL, ++ pwd_outgoing, trusted_dn, ++ KRB_PRINC_CREATE_DEFAULT); ++ ++ /* Second: $@, 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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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 +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 +--- + 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 +-# +-# 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 . ++# 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/@, enabled by default ++ ++ - @, 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/@, enabled by default ++ ++ - @, enabled by default and ++ used by remote trusted DCs to authenticate against us ++ ++ This principal also has krbtgt/@ 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 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 '' ++ and uidNumber/gidNumber generated automatically. Also, we ensure the ++ trusted domain object is given a SID. ++ ++ The update to 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='', ++ 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/@ 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 @ ++ 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/@ 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 @, 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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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 +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 +--- + 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 +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 +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Christian Heimes +--- + 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 -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 -Reviewed-By: Rob Crittenden ---- - 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 +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 +Reviewed-By: Christian Heimes +--- + 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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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?= +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 +Reviewed-By: Rob Crittenden +Reviewed-By: Christian Heimes +--- + 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 -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 -Reviewed-By: Christian Heimes ---- - 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 /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 +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 +Reviewed-By: Alexander Bokovoy +Reviewed-By: Christian Heimes +--- + 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 -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 -Reviewed-By: Tibor Dudlak ---- - 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 +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 +Reviewed-By: Alexander Bokovoy +Reviewed-By: Christian Heimes +--- + 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 -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 -Reviewed-By: Tibor Dudlak -Reviewed-By: Tibor Dudlak ---- - 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 +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 : +Signed-off-by: Christian Heimes +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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=[]) +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 -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 -Reviewed-By: Tibor Dudlak -Reviewed-By: Tibor Dudlak ---- - 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 -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 -Reviewed-By: Thierry Bordaz ---- - 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?= +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 +Signed-off-by: Francois Cami +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + .../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 -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 -Reviewed-By: Christian Heimes ---- - 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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + .../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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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=[, ]) + 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=[]) ++output: Output('summary', type=[, ]) ++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 -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 ---- - 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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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 -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 ---- - .../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 - #include - #include -+#include -+#include - #include - #include - #include -@@ -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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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 -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 ---- - 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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + .../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?= -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 -Reviewed-By: Christian Heimes -Reviewed-By: Rob Crittenden ---- - 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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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 -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 -Reviewed-By: Stanislav Laznicka ---- - 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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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 +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 +Reviewed-By: Francois Cami +--- + 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 -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 -Reviewed-By: Stanislav Laznicka ---- - 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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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 -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 -Reviewed-By: Rob Crittenden ---- - 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 +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 +Reviewed-By: Thomas Woerner +Reviewed-By: Francois Cami +--- + 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 -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 ---- - 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 +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 +--- + .../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 -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 ---- - 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 -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 ---- - 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 +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 +--- + .../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 -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 -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 -Reviewed-By: Christian Heimes -Reviewed-By: Christian Heimes ---- - 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 +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 +--- + 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 +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 +--- + 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 -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 ---- - 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?= -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 -Reviewed-By: Alexander Bokovoy -Reviewed-By: Christian Heimes -Reviewed-By: Rob Crittenden ---- - 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 +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 +--- + 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 -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 -Reviewed-By: Christian Heimes ---- - 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 +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 +--- + 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 -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 ---- - 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 +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 +Reviewed-By: Christian Heimes +--- + 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 . + ++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 -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 ---- - 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 -+ * -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+ -+#include -+ -+#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 +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 +--- + 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 -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 ---- - 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 +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 +--- + 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 -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=,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 -Reviewed-By: Christian Heimes ---- - 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 +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 +(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=[]) + output: Output('summary', type=[, ]) + 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 \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 \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 @@ + + + + +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 +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 +(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 -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 -Reviewed-By: Christian Heimes ---- - 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 -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 -Reviewed-By: Tibor Dudlak -Reviewed-By: Rob Crittenden -Reviewed-By: Rob Crittenden ---- - 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 +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 +(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 -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 -Reviewed-By: Tibor Dudlak -Reviewed-By: Rob Crittenden -Reviewed-By: Rob Crittenden ---- - 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 +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 +(cherry picked from commit add6180ae5c5771b0b0f1c743df069ece4256512) + +Reviewed-By: Florence Blanc-Renaud +--- + .../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 -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 -Reviewed-By: Tibor Dudlak -Reviewed-By: Rob Crittenden -Reviewed-By: Rob Crittenden ---- - 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 +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 +Reviewed-By: Christian Heimes +--- + 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 -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 -Reviewed-By: Tibor Dudlak -Reviewed-By: Rob Crittenden -Reviewed-By: Rob Crittenden ---- - 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 +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 +Reviewed-By: Alexander Bokovoy +Reviewed-By: Tibor Dudlak +Reviewed-By: Tibor Dudlak +--- + 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 -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 - -See: https://pagure.io/freeipa/issue/6476 -Signed-off-by: Thomas Woerner -Reviewed-By: Christian Heimes -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Florence Blanc-Renaud ---- - 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=[]) - output: ListOfEntries('result') - output: Output('summary', type=[, ]) - output: Output('truncated', type=[]) -+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=[]) -+output: ListOfEntries('result') -+output: Output('summary', type=[, ]) -+output: Output('truncated', type=[]) - 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 +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 +--- + 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?= -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 -Reviewed-By: Christian Heimes ---- - 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 +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 +--- + 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?= -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 -Reviewed-By: Christian Heimes ---- - 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 +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 +Reviewed-By: Alexander Bokovoy +--- + 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 +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 +--- + 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 -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 -Reviewed-By: Christian Heimes ---- - 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 +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 +--- + 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 -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 -Reviewed-By: Christian Heimes ---- - 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 +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 +--- + 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: @ is only used +- * for SSSD to be able to talk to AD DCs but it has to +- * have canonical name set to $ because +- * this is the salt used by AD DCs when using this +- * principal, otherwise authentication will fail. ++ /* Second: krbtgt/@ ++ * is only used for SSSD to be able to talk to ++ * AD DCs but it has to have canonical name set ++ * to krbtgt/ and alias it to ++ * 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 @, disabled ++ # 4. Create krbtgt/@, 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 -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 -Reviewed-By: Christian Heimes ---- - 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 +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 +--- + 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 -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 ---- - 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 -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 +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 +Reviewed-By: Florence Blanc-Renaud +--- + .../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 \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 = '' ++ _stderr = '' ++ ++ 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 -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 -Reviewed-By: Christian Heimes ---- - .../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 -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 -Reviewed-By: Christian Heimes ---- - 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 -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 -Reviewed-By: Christian Heimes ---- - 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 -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 ---- - 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 -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 -Reviewed-By: Christian Heimes ---- - 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 -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 -Reviewed-By: Christian Heimes ---- - .../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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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 -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 -Reviewed-By: Florence Blanc-Renaud ---- - 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 -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 -Reviewed-By: Alexander Bokovoy ---- - 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 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