summaryrefslogtreecommitdiffstats
path: root/ipaserver
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2011-07-14 23:35:01 -0400
committerRob Crittenden <rcritten@redhat.com>2011-07-17 22:16:32 -0400
commit3fdca99c48f19d6af7182b69bea0ee11100a9dd7 (patch)
tree4d50a038687198d36555ca531da415a125f2c25e /ipaserver
parent2f650b60a4ce9c9b19a64b21ebe3051668efb4af (diff)
downloadfreeipa-3fdca99c48f19d6af7182b69bea0ee11100a9dd7.zip
freeipa-3fdca99c48f19d6af7182b69bea0ee11100a9dd7.tar.gz
freeipa-3fdca99c48f19d6af7182b69bea0ee11100a9dd7.tar.xz
Create tool to manage dogtag replication agreements
For the most part the existing replication code worked with the following exceptions: - Added more port options - It assumed that initial connections were done to an SSL port. Added ability to use startTLS - It assumed that the name of the agreement was the same on both sides. In dogtag one is marked as master and one as clone. A new option is added, master, the determines which side we're working on or None if it isn't a dogtag agreement. - Don't set the attribute exclude list on dogtag agreements - dogtag doesn't set a schedule by default (which is actually recommended by 389-ds). This causes problems when doing a force-sync though so if one is done we set a schedule to run all the time. Otherwise the temporary schedule can't be removed (LDAP operations error). https://fedorahosted.org/freeipa/ticket/1250
Diffstat (limited to 'ipaserver')
-rw-r--r--ipaserver/install/dsinstance.py4
-rw-r--r--ipaserver/install/replication.py92
2 files changed, 67 insertions, 29 deletions
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 9033b7b..99b0215 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -305,8 +305,8 @@ class DsInstance(service.Service):
self.fqdn,
self.dm_password)
repl.setup_replication(self.master_fqdn,
- "cn=Directory Manager",
- self.dm_password)
+ r_binddn="cn=Directory Manager",
+ r_bindpw=self.dm_password)
def __enable(self):
self.backup_state("enabled", self.is_enabled())
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 22d4e1a..da8e749 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -29,6 +29,7 @@ from ldap import modlist
from ipalib import util
from ipalib import errors
from ipapython import ipautil
+from ipalib.dn import DN
DIRMAN_CN = "cn=directory manager"
CACERT = "/etc/ipa/ca.crt"
@@ -38,6 +39,7 @@ WIN_USER_CONTAINER = "cn=Users"
IPA_USER_CONTAINER = "cn=users,cn=accounts"
PORT = 636
TIMEOUT = 120
+REPL_MAN_DN = "cn=replication manager,cn=config"
IPA_REPLICA = 1
WINSYNC = 2
@@ -108,19 +110,26 @@ def enable_replication_version_checking(hostname, realm, dirman_passwd):
else:
conn.unbind()
-class ReplicationManager:
+class ReplicationManager(object):
"""Manage replication agreements between DS servers, and sync
agreements with Windows servers"""
- def __init__(self, realm, hostname, dirman_passwd):
+ def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=False):
self.hostname = hostname
+ self.port = port
self.dirman_passwd = dirman_passwd
self.realm = realm
+ self.starttls = starttls
tmp = util.realm_to_suffix(realm)
self.suffix = ipaldap.IPAdmin.normalizeDN(tmp)
# If we are passed a password we'll use it as the DM password
# otherwise we'll do a GSSAPI bind.
- self.conn = ipaldap.IPAdmin(hostname, port=PORT, cacert=CACERT)
+ if starttls:
+ self.conn = ipaldap.IPAdmin(hostname, port=port)
+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
+ self.conn.start_tls_s()
+ else:
+ self.conn = ipaldap.IPAdmin(hostname, port=port, cacert=CACERT)
if dirman_passwd:
self.conn.do_simple_bind(bindpw=dirman_passwd)
else:
@@ -130,7 +139,7 @@ class ReplicationManager:
# these are likely constant, but you could change them
# at runtime if you really want
- self.repl_man_dn = "cn=replication manager,cn=config"
+ self.repl_man_dn = REPL_MAN_DN
self.repl_man_cn = "replication manager"
def _get_replica_id(self, conn, master_conn):
@@ -152,7 +161,7 @@ class ReplicationManager:
# Ok, either the entry doesn't exist or the attribute isn't set
# so get it from the other master
retval = -1
- dn = "cn=replication, cn=etc, %s" % self.suffix
+ dn = str(DN("cn=replication, cn=etc, %s" % self.suffix))
try:
replica = master_conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*")[0]
if not replica.getValue('nsDS5ReplicaId'):
@@ -235,7 +244,7 @@ class ReplicationManager:
conn.modify_s(dn, [(ldap.MOD_REPLACE, "userpassword", pw)])
pass
- def delete_replication_manager(self, conn, dn="cn=replication manager,cn=config"):
+ def delete_replication_manager(self, conn, dn=REPL_MAN_DN):
try:
conn.delete_s(dn)
except ldap.NO_SUCH_OBJECT:
@@ -248,13 +257,21 @@ class ReplicationManager:
return "2"
def replica_dn(self):
- return 'cn=replica, cn="%s", cn=mapping tree, cn=config' % self.suffix
+ return str(DN('cn=replica, cn="%s", cn=mapping tree, cn=config' % self.suffix))
def replica_config(self, conn, replica_id, replica_binddn):
dn = self.replica_dn()
try:
- conn.getEntry(dn, ldap.SCOPE_BASE)
+ entry = conn.getEntry(dn, ldap.SCOPE_BASE)
+ managers = entry.getValues('nsDS5ReplicaBindDN')
+ for m in managers:
+ if DN(replica_binddn) == DN(m):
+ return
+ # Add the new replication manager
+ mod = [(ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn)]
+ conn.modify_s(dn, mod)
+
# replication is already configured
return
except errors.NotFound:
@@ -409,24 +426,34 @@ class ReplicationManager:
entry.setValues("nsds7NewWinGroupSyncEnabled", 'false')
entry.setValues("nsds7WindowsDomain", windomain)
- def agreement_dn(self, hostname):
+ def agreement_dn(self, hostname, master=None):
+ """
+ IPA agreement use the same dn on both sides, dogtag does not.
+ master is not used for IPA agreements but for dogtag it will
+ tell which side we want.
+ """
cn = "meTo%s" % (hostname)
dn = "cn=%s, %s" % (cn, self.replica_dn())
return (cn, dn)
- def setup_agreement(self, a_conn, b_hostname,
+ def setup_agreement(self, a_conn, b_hostname, port=389,
repl_man_dn=None, repl_man_passwd=None,
- iswinsync=False, win_subtree=None, isgssapi=False):
- cn, dn = self.agreement_dn(b_hostname)
+ iswinsync=False, win_subtree=None, isgssapi=False,
+ master=None):
+ """
+ master is used to determine which side of the agreement we are
+ creating. This is only needed for dogtag replication agreements
+ which use a different name on each side. If master is None then
+ isn't a dogtag replication agreement.
+ """
+ cn, dn = self.agreement_dn(b_hostname, master=master)
try:
a_conn.getEntry(dn, ldap.SCOPE_BASE)
return
except errors.NotFound:
pass
- port = 389
-
# List of attributes that need to be excluded from replication.
excludes = ('memberof', 'entryusn',
'krblastsuccessfulauth',
@@ -440,9 +467,10 @@ class ReplicationManager:
entry.setValues('nsds5replicaport', str(port))
entry.setValues('nsds5replicatimeout', str(TIMEOUT))
entry.setValues('nsds5replicaroot', self.suffix)
- entry.setValues('nsds5replicaupdateschedule', '0000-2359 0123456')
- entry.setValues('nsDS5ReplicatedAttributeList',
- '(objectclass=*) $ EXCLUDE %s' % " ".join(excludes))
+ if master is None:
+ entry.setValues('nsds5replicaupdateschedule', '0000-2359 0123456')
+ entry.setValues('nsDS5ReplicatedAttributeList',
+ '(objectclass=*) $ EXCLUDE %s' % " ".join(excludes))
entry.setValues('description', "me to %s" % b_hostname)
if isgssapi:
entry.setValues('nsds5replicatransportinfo', 'LDAP')
@@ -623,11 +651,11 @@ class ReplicationManager:
haserror = 1
return haserror
- def start_replication(self, conn, hostname=None):
+ def start_replication(self, conn, hostname=None, master=None):
print "Starting replication, please wait until this has completed."
if hostname == None:
hostname = self.conn.host
- cn, dn = self.agreement_dn(hostname)
+ cn, dn = self.agreement_dn(hostname, master)
mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]
conn.modify_s(dn, mod)
@@ -640,10 +668,16 @@ class ReplicationManager:
self.replica_config(conn, replica_id, repldn)
self.setup_changelog(conn)
- def setup_replication(self, r_hostname, r_binddn=None, r_bindpw=None):
+ def setup_replication(self, r_hostname, r_port=389, r_sslport=636, r_binddn=None, r_bindpw=None, starttls=False):
# note - there appears to be a bug in python-ldap - it does not
# allow connections using two different CA certs
- r_conn = ipaldap.IPAdmin(r_hostname, port=PORT, cacert=CACERT)
+ if starttls:
+ r_conn = ipaldap.IPAdmin(r_hostname, port=r_port)
+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
+ r_conn.start_tls_s()
+ else:
+ r_conn = ipaldap.IPAdmin(r_hostname, port=r_sslport, cacert=CACERT)
+
if r_bindpw:
r_conn.do_simple_bind(binddn=r_binddn, bindpw=r_bindpw)
else:
@@ -659,15 +693,17 @@ class ReplicationManager:
self.basic_replication_setup(r_conn, r_id,
self.repl_man_dn, self.repl_man_passwd)
- self.setup_agreement(r_conn, self.conn.host,
+ self.setup_agreement(r_conn, self.conn.host, port=r_port,
repl_man_dn=self.repl_man_dn,
- repl_man_passwd=self.repl_man_passwd)
- self.setup_agreement(self.conn, r_hostname,
+ repl_man_passwd=self.repl_man_passwd,
+ master=True)
+ self.setup_agreement(self.conn, r_hostname, port=r_port,
repl_man_dn=self.repl_man_dn,
- repl_man_passwd=self.repl_man_passwd)
+ repl_man_passwd=self.repl_man_passwd,
+ master=False)
#Finally start replication
- ret = self.start_replication(r_conn)
+ ret = self.start_replication(r_conn, master=True)
if ret != 0:
raise RuntimeError("Failed to start replication")
@@ -717,7 +753,7 @@ class ReplicationManager:
logging.info("Agreement is ready, starting replication . . .")
# Add winsync replica to the public DIT
- dn = 'cn=%s,cn=replicas,cn=ipa,cn=etc,%s' % (ad_dc_name, self.suffix)
+ dn = str(DN('cn=%s,cn=replicas,cn=ipa,cn=etc,%s' % (ad_dc_name, self.suffix)))
entry = ipaldap.Entry(dn)
entry.setValues("objectclass", ["nsContainer", "ipaConfigObject"])
entry.setValues("cn", ad_dc_name)
@@ -802,6 +838,8 @@ class ReplicationManager:
dn = entry[0].dn
schedule = entry[0].nsds5replicaupdateschedule
+ if schedule is None:
+ schedule = '0000-2359 0123456'
# On the remote chance of a match. We force a synch to happen right
# now by changing the schedule to something else and quickly changing