Skip to content

Commit

Permalink
Issue 4209 - RFE - add bootstrap credentials to repl agreement
Browse files Browse the repository at this point in the history
Description:  When using Bind DN Groups for a replication agreement
              authentication there are cases where the group is not
              present, or is outdated.  In such cases having bootstrap
              credentials can allow replication to start working again.
              New replication sessions will always try and use the
              default credentials first.

relates: #4209

Reviewed by: firstyear & tbordaz(Thanks!)
  • Loading branch information
mreynolds389 committed Sep 22, 2020
1 parent 30e7ba6 commit 84cac9e
Show file tree
Hide file tree
Showing 8 changed files with 460 additions and 60 deletions.
118 changes: 118 additions & 0 deletions dirsrvtests/tests/suites/replication/repl_agmt_bootstrap_test.py
@@ -0,0 +1,118 @@
import logging
import pytest
import os
import time
from lib389._constants import DEFAULT_SUFFIX
from lib389.topologies import topology_m2 as topo
from lib389.replica import BootstrapReplicationManager, Replicas
from lib389.idm.user import TEST_USER_PROPERTIES, UserAccounts, UserAccount
from lib389.idm.group import Group

DEBUGGING = os.getenv("DEBUGGING", default=False)
if DEBUGGING:
logging.getLogger(__name__).setLevel(logging.DEBUG)
else:
logging.getLogger(__name__).setLevel(logging.INFO)
log = logging.getLogger(__name__)

BOOTSTRAP_MGR_DN = 'uid=replication manager,cn=config'
BOOTSTRAP_MGR_PWD = 'boostrap_manager_password'
BIND_GROUP_DN = 'cn=replication_managers,' + DEFAULT_SUFFIX


def test_repl_agmt_bootstrap_credentials(topo):
"""Test that the agreement bootstrap credentials works if the default
credentials fail for some reason.
:id: 38c8095c-d958-415a-b602-74854b7882b3
:setup: 2 Master Instances
:steps:
1. Change the bind dn group member passwords
2. Verify replication is not working
3. Create a new repl manager on master 2 for bootstrapping
4. Add bootstrap credentials to agmt on master 1
5. Verify replication is now working with bootstrap creds
6. Trigger new repl session and default credentials are used first
:expectedresults:
1. Success
2. Success
3. Success
4. Success
5. Success
6. Success
"""

# Gather all of our objects for the test
m1 = topo.ms["master1"]
m2 = topo.ms["master2"]
master1_replica = Replicas(m1).get(DEFAULT_SUFFIX)
master2_replica = Replicas(m2).get(DEFAULT_SUFFIX)
master2_users = UserAccounts(m2, DEFAULT_SUFFIX)
m1_agmt = master1_replica.get_agreements().list()[0]
num_of_original_users = len(master2_users.list())

# Change the member's passwords which should break replication
bind_group = Group(m2, dn=BIND_GROUP_DN)
members = bind_group.list_members()
for member_dn in members:
member = UserAccount(m2, dn=member_dn)
member.replace('userPassword', 'not_right')
time.sleep(3)
m1_agmt.pause()
m1_agmt.resume()

# Verify replication is not working, a new user should not be replicated
users = UserAccounts(m1, DEFAULT_SUFFIX)
test_user = users.ensure_state(properties=TEST_USER_PROPERTIES)
time.sleep(3)
assert len(master2_users.list()) == num_of_original_users

# Create a repl manager on replica
repl_mgr = BootstrapReplicationManager(m2, dn=BOOTSTRAP_MGR_DN)
mgr_properties = {
'uid': 'replication manager',
'cn': 'replication manager',
'userPassword': BOOTSTRAP_MGR_PWD,
}
repl_mgr.create(properties=mgr_properties)

# Update master 2 config
master2_replica.remove_all('nsDS5ReplicaBindDNGroup')
master2_replica.remove_all('nsDS5ReplicaBindDnGroupCheckInterval')
master2_replica.replace('nsDS5ReplicaBindDN', BOOTSTRAP_MGR_DN)

# Add bootstrap credentials to master1 agmt, and restart agmt
m1_agmt.replace('nsds5ReplicaBootstrapTransportInfo', 'LDAP')
m1_agmt.replace('nsds5ReplicaBootstrapBindMethod', 'SIMPLE')
m1_agmt.replace('nsds5ReplicaBootstrapCredentials', BOOTSTRAP_MGR_PWD)
m1_agmt.replace('nsds5ReplicaBootstrapBindDN', BOOTSTRAP_MGR_DN)
m1_agmt.pause()
m1_agmt.resume()

# Verify replication is working. The user should have been replicated
time.sleep(3)
assert len(master2_users.list()) > num_of_original_users

# Finally check if the default credentials are used on the next repl
# session. Clear out the logs, and disable log buffering. Then
# trigger a replication update/session.
m1_agmt.pause()
m2.stop()
m2.deleteLog(m2.accesslog) # Clear out the logs
m2.start()
m2.config.set('nsslapd-accesslog-logbuffering', 'off')
m1_agmt.resume()
test_user.delete()
time.sleep(3)

# We know if the default credentials are used it will fail (err=49)
results = m2.ds_access_log.match('.* err=49 .*')
assert len(results) > 0


if __name__ == '__main__':
# Run isolated
# -s for DEBUG mode
CURRENT_FILE = os.path.realpath(__file__)
pytest.main(["-s", CURRENT_FILE])

1 change: 1 addition & 0 deletions ldap/ldif/template-dse.ldif.in
Expand Up @@ -238,6 +238,7 @@ nsslapd-plugintype: reverpwdstoragescheme
nsslapd-pluginenabled: on
nsslapd-pluginarg0: nsmultiplexorcredentials
nsslapd-pluginarg1: nsds5ReplicaCredentials
nsslapd-pluginarg2: nsds5ReplicaBootstrapCredentials
nsslapd-pluginid: aes-storage-scheme
nsslapd-pluginprecedence: 1

Expand Down
10 changes: 9 additions & 1 deletion ldap/schema/01core389.ldif
Expand Up @@ -315,6 +315,14 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2084 NAME 'nsSymmetricKey' DESC 'A symme
attributeTypes: ( 2.16.840.1.113730.3.1.2364 NAME 'nsds5replicaLastInitStatusJSON' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.2365 NAME 'nsds5replicaLastUpdateStatusJSON' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.2370 NAME 'nsslapd-enable-upgrade-hash' DESC 'Upgrade password hash on bind' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN '389 Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.602 NAME 'entrydn' DESC 'Internal database attribute for the entry DN' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.603 NAME 'dncomp' DESC 'Internal database attribute for each DN component' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION USAGE directoryOperation X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.604 NAME 'parentid' DESC 'Internal database attribute for the parent ID of the entry' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.605 NAME 'entryid' DESC 'Internal database attribute for the ID of the entry' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.2371 NAME 'nsDS5ReplicaBootstrapBindDN' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.2372 NAME 'nsDS5ReplicaBootstrapCredentials' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.2373 NAME 'nsDS5ReplicaBootstrapBindMethod' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.2374 NAME 'nsDS5ReplicaBootstrapTransportInfo' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
#
# objectclasses
#
Expand All @@ -326,7 +334,7 @@ objectClasses: ( 2.16.840.1.113730.3.2.110 NAME 'nsMappingTree' DESC 'Netscape d
objectClasses: ( 2.16.840.1.113730.3.2.104 NAME 'nsContainer' DESC 'Netscape defined objectclass' SUP top MUST ( CN ) X-ORIGIN 'Netscape Directory Server' )
objectClasses: ( 2.16.840.1.113730.3.2.108 NAME 'nsDS5Replica' DESC 'Replication configuration objectclass' SUP top MUST ( nsDS5ReplicaRoot $ nsDS5ReplicaId ) MAY (cn $ nsds5ReplicaPreciseTombstonePurging $ nsds5ReplicaCleanRUV $ nsds5ReplicaAbortCleanRUV $ nsDS5ReplicaType $ nsDS5ReplicaBindDN $ nsDS5ReplicaBindDNGroup $ nsState $ nsDS5ReplicaName $ nsDS5Flags $ nsDS5Task $ nsDS5ReplicaReferral $ nsDS5ReplicaAutoReferral $ nsds5ReplicaPurgeDelay $ nsds5ReplicaTombstonePurgeInterval $ nsds5ReplicaChangeCount $ nsds5ReplicaLegacyConsumer $ nsds5ReplicaProtocolTimeout $ nsds5ReplicaBackoffMin $ nsds5ReplicaBackoffMax $ nsds5ReplicaReleaseTimeout $ nsDS5ReplicaBindDnGroupCheckInterval ) X-ORIGIN 'Netscape Directory Server' )
objectClasses: ( 2.16.840.1.113730.3.2.113 NAME 'nsTombstone' DESC 'Netscape defined objectclass' SUP top MAY ( nstombstonecsn $ nsParentUniqueId $ nscpEntryDN ) X-ORIGIN 'Netscape Directory Server' )
objectClasses: ( 2.16.840.1.113730.3.2.103 NAME 'nsDS5ReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsds5ReplicaCleanRUVNotified $ nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5ReplicaEnabled $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5ReplicaStripAttrs $ nsds5replicaSessionPauseTime $ nsds5ReplicaProtocolTimeout $ nsds5ReplicaFlowControlWindow $ nsds5ReplicaFlowControlPause $ nsDS5ReplicaWaitForAsyncResults $ nsds5ReplicaIgnoreMissingChange) X-ORIGIN 'Netscape Directory Server' )
objectClasses: ( 2.16.840.1.113730.3.2.103 NAME 'nsDS5ReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsds5ReplicaCleanRUVNotified $ nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicatedAttributeListTotal $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5ReplicaEnabled $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5ReplicaStripAttrs $ nsds5replicaSessionPauseTime $ nsds5ReplicaProtocolTimeout $ nsds5ReplicaFlowControlWindow $ nsds5ReplicaFlowControlPause $ nsDS5ReplicaWaitForAsyncResults $ nsds5ReplicaIgnoreMissingChange $ nsDS5ReplicaBootstrapBindDN $ nsDS5ReplicaBootstrapCredentials $ nsDS5ReplicaBootstrapBindMethod $ nsDS5ReplicaBootstrapTransportInfo ) X-ORIGIN 'Netscape Directory Server' )
objectClasses: ( 2.16.840.1.113730.3.2.39 NAME 'nsslapdConfig' DESC 'Netscape defined objectclass' SUP top MAY ( cn ) X-ORIGIN 'Netscape Directory Server' )
objectClasses: ( 2.16.840.1.113730.3.2.317 NAME 'nsSaslMapping' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSaslMapRegexString $ nsSaslMapBaseDNTemplate $ nsSaslMapFilterTemplate ) MAY ( nsSaslMapPriority ) X-ORIGIN 'Netscape Directory Server' )
objectClasses: ( 2.16.840.1.113730.3.2.43 NAME 'nsSNMP' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSNMPEnabled ) MAY ( nsSNMPOrganization $ nsSNMPLocation $ nsSNMPContact $ nsSNMPDescription $ nsSNMPName $ nsSNMPMasterHost $ nsSNMPMasterPort ) X-ORIGIN 'Netscape Directory Server' )
Expand Down
14 changes: 12 additions & 2 deletions ldap/servers/plugins/replication/repl5.h
Expand Up @@ -157,6 +157,10 @@ extern const char *type_replicaBackoffMin;
extern const char *type_replicaBackoffMax;
extern const char *type_replicaPrecisePurge;
extern const char *type_replicaIgnoreMissingChange;
extern const char *type_nsds5ReplicaBootstrapBindDN;
extern const char *type_nsds5ReplicaBootstrapCredentials;
extern const char *type_nsds5ReplicaBootstrapBindMethod;
extern const char *type_nsds5ReplicaBootstrapTransportInfo;

/* Attribute names for windows replication agreements */
extern const char *type_nsds7WindowsReplicaArea;
Expand Down Expand Up @@ -385,9 +389,13 @@ int agmt_replicate_now(Repl_Agmt *ra);
char *agmt_get_hostname(const Repl_Agmt *ra);
int agmt_get_port(const Repl_Agmt *ra);
uint32_t agmt_get_transport_flags(const Repl_Agmt *ra);
uint32_t agmt_get_bootstrap_transport_flags(const Repl_Agmt *ra);
char *agmt_get_binddn(const Repl_Agmt *ra);
char *agmt_get_bootstrap_binddn(const Repl_Agmt *ra);
struct berval *agmt_get_credentials(const Repl_Agmt *ra);
struct berval *agmt_get_bootstrap_credentials(const Repl_Agmt *ra);
int agmt_get_bindmethod(const Repl_Agmt *ra);
int64_t agmt_get_bootstrap_bindmethod(const Repl_Agmt *ra);
Slapi_DN *agmt_get_replarea(const Repl_Agmt *ra);
int agmt_is_fractional(const Repl_Agmt *ra);
int agmt_is_fractional_attr(const Repl_Agmt *ra, const char *attrname);
Expand All @@ -404,9 +412,11 @@ int agmt_set_ignoremissing_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_busywaittime_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_pausetime_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_credentials_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int32_t agmt_set_bootstrap_credentials_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_binddn_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_bind_method_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_transportinfo_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int32_t agmt_set_bootstrap_binddn_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_bind_method_from_entry(Repl_Agmt *ra, const Slapi_Entry *e, PRBool bootstrap);
int agmt_set_transportinfo_from_entry(Repl_Agmt *ra, const Slapi_Entry *e, PRBool bootstrap);
int agmt_set_port_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
int agmt_set_host_from_entry(Repl_Agmt *ra, const Slapi_Entry *e);
const char *agmt_get_long_name(const Repl_Agmt *ra);
Expand Down

0 comments on commit 84cac9e

Please sign in to comment.