URL: https://github.com/freeipa/freeipa/pull/234
Author: martbab
 Title: #234: Always use GSSAPI to set up initial replication
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/234/head:pr234
git checkout pr234
From d2bedfe398005f276196bb833a23d258522922dc Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 11 Nov 2016 10:23:49 +0100
Subject: [PATCH 1/5] Turn replication manager group into ReplicationManager
 class member

https://fedorahosted.org/freeipa/ticket/6406
---
 ipalib/constants.py              |  1 +
 ipaserver/install/replication.py | 12 ++++++------
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/ipalib/constants.py b/ipalib/constants.py
index c423117..719f307 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -125,6 +125,7 @@
     ('container_ca', DN(('cn', 'cas'), ('cn', 'ca'))),
     ('container_dnsservers', DN(('cn', 'servers'), ('cn', 'dns'))),
     ('container_custodia', DN(('cn', 'custodia'), ('cn', 'ipa'), ('cn', 'etc'))),
+    ('container_sysaccounts', DN(('cn', 'sysaccounts'), ('cn', 'etc'))),
 
     # Ports, hosts, and URIs:
     ('xmlrpc_uri', 'http://localhost:8888/ipa/xml'),
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 836be73..2e3b12f 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -50,6 +50,7 @@
 TIMEOUT = 120
 REPL_MAN_DN = DN(('cn', 'replication manager'), ('cn', 'config'))
 DNA_DN = DN(('cn', 'Posix IDs'), ('cn', 'Distributed Numeric Assignment Plugin'), ('cn', 'plugins'), ('cn', 'config'))
+REPL_MANAGERS_CN = DN(('cn', 'replication managers'))
 
 IPA_REPLICA = 1
 WINSYNC = 2
@@ -232,6 +233,8 @@ def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False, co
         # at runtime if you really want
         self.repl_man_dn = REPL_MAN_DN
         self.repl_man_cn = "replication manager"
+        self.repl_man_group_dn = DN(
+            REPL_MANAGERS_CN, api.env.container_sysaccounts, api.env.basedn)
 
     def _get_replica_id(self, conn, master_conn):
         """
@@ -438,9 +441,6 @@ def replica_config(self, conn, replica_id, replica_binddn):
         assert isinstance(replica_binddn, DN)
         dn = self.replica_dn()
         assert isinstance(dn, DN)
-        replica_groupdn = DN(
-            ('cn', 'replication managers'), ('cn', 'sysaccounts'),
-            ('cn', 'etc'), self.suffix)
 
         try:
             entry = conn.get_entry(dn)
@@ -454,9 +454,9 @@ def replica_config(self, conn, replica_id, replica_binddn):
                 mod.append((ldap.MOD_ADD, 'nsDS5ReplicaBindDN',
                             replica_binddn))
 
-            if replica_groupdn not in binddn_groups:
+            if self.repl_man_group_dn not in binddn_groups:
                 mod.append((ldap.MOD_ADD, 'nsds5replicabinddngroup',
-                            replica_groupdn))
+                            self.repl_man_group_dn))
             if mod:
                 conn.modify_s(dn, mod)
 
@@ -476,7 +476,7 @@ def replica_config(self, conn, replica_id, replica_binddn):
             nsds5replicatype=[replica_type],
             nsds5flags=["1"],
             nsds5replicabinddn=[replica_binddn],
-            nsds5replicabinddngroup=[replica_groupdn],
+            nsds5replicabinddngroup=[self.repl_man_group_dn],
             nsds5replicabinddngroupcheckinterval=["60"],
             nsds5replicalegacyconsumer=["off"],
         )

From 7802160d5953ab5e2590e024b5027f1202c088bf Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Wed, 9 Nov 2016 14:44:05 +0100
Subject: [PATCH 2/5] replication: augment setup_promote_replication method

the method that sets up initial GSSAPI replication in DL1 was augmented so
that the specified bind DN/bind password allows simple bind to remote master
using STARTTLS. The CA certificate for the connection is also configurable.

This facilitates the use of this method in DL0 where GSSAPI bind can not be
used during DS bootstrap while DM credentials are available.

https://fedorahosted.org/freeipa/ticket/6406
---
 ipaserver/install/replication.py | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 2e3b12f..6209f81 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -1602,12 +1602,16 @@ def remove_temp_replication_user(self, conn, r_hostname):
             entry['nsDS5ReplicaBindDN'].remove(replica_binddn)
         conn.update_entry(entry)
 
-    def setup_promote_replication(self, r_hostname):
+    def setup_promote_replication(self, r_hostname, r_binddn=None,
+                                  r_bindpw=None, cacert=CACERT):
         # note - there appears to be a bug in python-ldap - it does not
         # allow connections using two different CA certs
         ldap_uri = ipaldap.get_ldap_uri(r_hostname)
-        r_conn = ipaldap.LDAPClient(ldap_uri)
-        r_conn.gssapi_bind()
+        r_conn = ipaldap.LDAPClient(ldap_uri, cacert=cacert)
+        if r_bindpw:
+            r_conn.simple_bind(r_binddn, r_bindpw)
+        else:
+            r_conn.gssapi_bind()
 
         # Setup the first half
         l_id = self._get_replica_id(self.conn, r_conn)

From b4d5e9eefae3cd6a049ee3fd7e754267d7332703 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 10 Nov 2016 14:37:40 +0100
Subject: [PATCH 3/5] replication: refactor the code setting principals as
 replica bind DNs

In addition to improving the readability of
`setup_krb_princs_as_replica_binddns` method, the re-usable bits were factored
out to separate methods

https://fedorahosted.org/freeipa/ticket/6406
---
 ipaserver/install/replication.py | 47 ++++++++++++++++++----------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 6209f81..4ad6694 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -782,6 +782,22 @@ def get_replica_principal_dns(self, a, b, retries):
 
         return (a_entry[0].dn, b_entry[0].dn)
 
+    def _add_replica_bind_dn(self, conn, bind_dn):
+        rep_dn = self.replica_dn()
+        assert isinstance(rep_dn, DN)
+        try:
+            mod = [(ldap.MOD_ADD, "nsds5replicabinddn", bind_dn)]
+            conn.modify_s(rep_dn, mod)
+        except ldap.TYPE_OR_VALUE_EXISTS:
+            pass
+
+    def _add_dn_to_replication_managers(self, conn, bind_dn):
+        try:
+            mod = [(ldap.MOD_ADD, "member", bind_dn)]
+            conn.modify_s(self.repl_man_group_dn, mod)
+        except (ldap.TYPE_OR_VALUE_EXISTS, ldap.NO_SUCH_OBJECT):
+            pass
+
     def setup_krb_princs_as_replica_binddns(self, a, b):
         """
         Search the appropriate principal names so we can get
@@ -790,37 +806,16 @@ def setup_krb_princs_as_replica_binddns(self, a, b):
         as replication agents.
         """
 
-        rep_dn = self.replica_dn()
-        group_dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
-                      ('cn', 'etc'), self.suffix)
-        assert isinstance(rep_dn, DN)
         (a_dn, b_dn) = self.get_replica_principal_dns(a, b, retries=100)
         assert isinstance(a_dn, DN)
         assert isinstance(b_dn, DN)
 
-        # Add kerberos principal DNs as valid bindDNs for replication
-        try:
-            mod = [(ldap.MOD_ADD, "nsds5replicabinddn", b_dn)]
-            a.modify_s(rep_dn, mod)
-        except ldap.TYPE_OR_VALUE_EXISTS:
-            pass
-        try:
-            mod = [(ldap.MOD_ADD, "nsds5replicabinddn", a_dn)]
-            b.modify_s(rep_dn, mod)
-        except ldap.TYPE_OR_VALUE_EXISTS:
-            pass
-        # Add kerberos principal DNs as valid bindDNs to bindDN group
-        try:
-            mod = [(ldap.MOD_ADD, "member", b_dn)]
-            a.modify_s(group_dn, mod)
-        except (ldap.TYPE_OR_VALUE_EXISTS, ldap.NO_SUCH_OBJECT):
-            pass
-        try:
-            mod = [(ldap.MOD_ADD, "member", a_dn)]
-            b.modify_s(group_dn, mod)
-        except (ldap.TYPE_OR_VALUE_EXISTS, ldap.NO_SUCH_OBJECT):
-            pass
+        for conn, bind_dn in ((a, b_dn), (b, a_dn)):
+            # Add kerberos principal DNs as valid bindDNs for replication
+            self._add_replica_bind_dn(conn, bind_dn)
 
+            # Add kerberos principal DNs as valid bindDNs to bindDN group
+            self._add_dn_to_replication_managers(conn, bind_dn)
 
     def gssapi_update_agreements(self, a, b):
 

From 22e79e3a42906f08191d2ab7f9feab6e6d20a346 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 10 Nov 2016 14:42:01 +0100
Subject: [PATCH 4/5] ensure that the initial sync using GSSAPI works agains
 old masters

IPA 3.x masters neither have 'cn=replication managers' sysaccount groups set,
nor do they support adding nsds5ReplicaBinddnGroup attribute to the replica
config objects.

In order for common replication mechanism to work against
them, the replica must be ready to supply the required information to the old
master.

https://fedorahosted.org/freeipa/ticket/6406
---
 ipaserver/install/replication.py | 46 +++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 15 deletions(-)

diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 4ad6694..ba35c49 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -32,7 +32,7 @@
 from ipalib.cli import textui
 from ipalib.constants import CACERT
 from ipapython.ipa_log_manager import root_logger
-from ipapython import ipautil, ipaldap
+from ipapython import ipautil, ipaldap, kerberos
 from ipapython.admintool import ScriptError
 from ipapython.dn import DN
 from ipaplatform.paths import paths
@@ -1534,24 +1534,40 @@ def enable_agreement(self, hostname):
         except errors.EmptyModlist:
             pass
 
-    def join_replication_managers(self, conn):
+    def _add_replication_managers(self, conn):
+        entry = conn.make_entry(
+            self.repl_man_group_dn,
+            objectclass=['top', 'groupofnames'],
+            cn=['replication managers']
+        )
+        conn.add_entry(entry)
+
+    def ensure_replication_managers(self, conn, r_hostname):
         """
-        Create a pseudo user to use for replication.
+        Ensure that the 'cn=replication managers,cn=sysaccounts' group exists
+        and contains the principals for master and remote replica
+
+        On FreeIPA 3.x masters lacking support for nsds5ReplicaBinddnGroup
+        attribute, add replica bind DN directly into the replica entry.
         """
-        dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
-                ('cn', 'etc'), self.suffix)
-        mydn = DN(('krbprincipalname', 'ldap/%s@%s' % (self.hostname,
-                                                       self.realm)),
-                  ('cn', 'services'), ('cn', 'accounts'), self.suffix)
+        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)
 
-        entry = conn.get_entry(dn)
-        if mydn not in entry['member']:
-            entry['member'].append(mydn)
+        mydn, remote_dn = tuple(
+            DN(('krbprincipalname', unicode(p)), services_dn) for p in (
+                my_princ, remote_princ))
 
         try:
-            conn.update_entry(entry)
-        except errors.EmptyModlist:
-            pass
+            conn.get_entry(self.repl_man_group_dn)
+        except errors.NotFound:
+            self._add_replica_bind_dn(conn, mydn)
+            self._add_replication_managers(conn)
+
+        self._add_dn_to_replication_managers(conn, mydn)
+        self._add_dn_to_replication_managers(conn, remote_dn)
 
     def add_temp_sasl_mapping(self, conn, r_hostname):
         """
@@ -1616,7 +1632,7 @@ def setup_promote_replication(self, r_hostname, r_binddn=None,
         # Now setup the other half
         r_id = self._get_replica_id(r_conn, r_conn)
         self.basic_replication_setup(r_conn, r_id, self.repl_man_dn, None)
-        self.join_replication_managers(r_conn)
+        self.ensure_replication_managers(r_conn, r_hostname)
 
         self.setup_agreement(r_conn, self.hostname, isgssapi=True)
         self.setup_agreement(self.conn, r_hostname, isgssapi=True)

From 26aaf2966bd1acf869154ccb5ad585d52efde34a Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Wed, 9 Nov 2016 14:48:56 +0100
Subject: [PATCH 5/5] Use common procedure to setup initial replication in both
 domain levels

Set up initial replication using GSSAPI also in domin level 0. For this to
work, the supplied DM password is used to connect to remote master and set up
agreements. The workflow is unchanged in DL1 where GSSAPI bind as host or
admin is used.

This obsoletes the conversion of replication agreements to GSSAPI made in DL0
during KDC installation.

https://fedorahosted.org/freeipa/ticket/6406
---
 ipaserver/install/dsinstance.py  | 25 ++++++++++++++++++++-----
 ipaserver/install/krbinstance.py |  3 ---
 2 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index a604010..f76378e 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -410,6 +410,16 @@ def create_replica(self, realm_name, master_fqdn, fqdn,
 
 
     def __setup_replica(self):
+        """
+        Setup initial replication between replica and remote master.
+        GSSAPI is always used as a replication bind method. Note, however,
+        that the bind method for the replication differs between domain levels:
+            * in domain level 0, Directory Manager credentials are used to bind
+              to remote master
+            * in domain level 1, GSSAPI using admin/privileged host credentials
+              is used (we do not have access to masters' DM password in this
+              stage)
+        """
         replication.enable_replication_version_checking(
             self.realm,
             self.dm_password)
@@ -421,12 +431,17 @@ def __setup_replica(self):
         repl = replication.ReplicationManager(self.realm,
                                               self.fqdn,
                                               self.dm_password, conn=conn)
-        if self.promote:
-            repl.setup_promote_replication(self.master_fqdn)
+
+        if self.dm_password is not None and not self.promote:
+            bind_dn = DN(('cn', 'Directory Manager'))
+            bind_pw = self.dm_password
         else:
-            repl.setup_replication(self.master_fqdn,
-                                   r_binddn=DN(('cn', 'Directory Manager')),
-                                   r_bindpw=self.dm_password)
+            bind_dn = bind_pw = None
+
+        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 __configure_sasl_mappings(self):
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index b7ae38f..b5cfd79 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -180,9 +180,6 @@ def create_replica(self, realm_name,
         self.step("adding the password extension to the directory", self.__add_pwd_extop_module)
         if setup_pkinit:
             self.step("installing X509 Certificate for PKINIT", self.__setup_pkinit)
-        if not promote:
-            self.step("enable GSSAPI for replication",
-                      self.__convert_to_gssapi_replication)
 
         self.__common_post_setup()
 
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to