OpenLDAP 2.4 Server Replication
Build and Install from Source
Compile with support of the new MDB database backend that will replace Berkeley DB. The new MDB database is much faster and doesn't need tuning.
tar -xvzf openldap-2.4.38.tgz cd openldap-2.4.38 ./configure --prefix=/opt/openldap \ --sysconfdir=/etc/opt/openldap \ --localstatedir=/var/opt/openldap \ --with-subdir= \ --enable-slapd --enable-mdb --enable-monitor --enable-overlays --enable-crypt \ --with-tls --with-cyrus-sasl make depend make make install
Configure LDAP Server
Common part for both provider and consumer replica. The official OpenLDAP 2.4 admin guide still shows all examples with the old config file format. It took a while and a lot of research to find out how to do it with the new cn=config format.
slapadd -v -F /etc/opt/openldap/slapd.d -n 0 <<
dn: cn=config
objectClass: olcGlobal
cn: config
olcAttributeOptions: lang-
olcConfigFile: /etc/opt/openldap/slapd.conf.bak
olcConfigDir: /etc/opt/openldap/slapd.d
olcPidFile: /var/opt/openldap/run/slapd.pid
olcTLSCACertificateFile: /etc/pki/tls/certs/example_ca.pem
olcTLSCertificateFile: /etc/pki/tls/certs/node-cert.pem
olcTLSCertificateKeyFile: /etc/pki/tls/private/node-key.pem
olcTLSVerifyClient: never
olcLogLevel: none
olcSecurity: simple_bind=128
olcLocalSSF: 128
dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema
include: file:///etc/opt/openldap/schema/core.ldif
include: file:///etc/opt/openldap/schema/cosine.ldif
include: file:///etc/opt/openldap/schema/duaconf.ldif
include: file:///etc/opt/openldap/schema/inetorgperson.ldif
include: file:///etc/opt/openldap/schema/ppolicy.ldif
include: file:///etc/opt/openldap/schema/openldap.ldif
include: file:///etc/opt/openldap/schema/java.ldif
include: file:///root/schema/rfc2307bis.ldif
include: file:///root/schema/printing.ldif
include: file:///root/schema/solaris.ldif
include: file:///root/schema/cupsprinter.ldif
include: file:///root/schema/mozilla.ldif
dn: olcDatabase={-1}frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: {-1}frontend
olcAccess: {0}to attrs=userPassword
by self =xw
by anonymous auth
by * none
olcAccess: {1}to * by * read
olcSizelimit: size.soft=10000 size.hard=1000000
olcTimelimit: time.soft=300 time.hard=3600
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcRootDN: cn=Manager,cn=config
olcRootPW: {SSHA}ENCRYPTEDPASSWORD
olcMonitoring: FALSE
olcAccess: {0}to dn.subtree="cn=schema,cn=config"
by users read
olcAccess: {1}to * by * none
dn: olcDatabase={1}monitor,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {1}monitor
olcAddContentAcl: FALSE
olcLastMod: TRUE
olcMaxDerefDepth: 15
olcReadOnly: FALSE
olcSyncUseSubentry: FALSE
olcMonitoring: FALSE
olcAccess: {3}to dn.subtree="cn=monitor"
by dn.exact="cn=Manager,cn=config" read
by dn.exact="cn=Manager,dc=example,dc=com" read
by * none
dn: olcDatabase={2}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {2}mdb
olcMonitoring: TRUE
olcSuffix: dc=example,dc=com
olcRootDN: cn=Manager,dc=example,dc=com
olcRootPW: {SSHA}ENCRYPTEDPASSWORD
olcDbDirectory: /var/opt/openldap/example_com
olcDbMaxSize: 42949672960
olcDbIndex: objectClass pres,eq
olcDbIndex: cn pres,eq,sub
olcDbIndex: uid pres,eq
olcDbIndex: uidNumber pres,eq
olcDbIndex: gidNumber pres,eq
olcDbIndex: mail pres,eq,sub
olcDbIndex: ou pres,eq
olcDbIndex: loginShell pres,eq
olcDbIndex: sn pres,eq,sub
olcDbIndex: givenName pres,eq,sub
olcDbIndex: memberUid pres,eq
olcDbIndex: nisMapName pres,eq
olcDbIndex: nisMapEntry pres,eq
olcDbIndex: entryCSN eq
olcDbIndex: entryUUID eq
olcAccess: {0}to attrs=userPassword
by dn.exact="cn=Manager,dc=example,dc=com" write
by dn.exact="cn=Replicator,dc=example,dc=com" read
by self =xw
by anonymous auth
by * none
olcAccess: {2}to * by * read
olcLimits: {0}dn.exact="cn=Manager,dc=example,dc=com"
size.soft=unlimited size.hard=unlimited
time.soft=unlimited time.hard=unlimited
olcLimits: {1}dn.exact="cn=Replicator,dc=example,dc=com"
size.soft=unlimited size.hard=unlimited
time.soft=unlimited time.hard=unlimited
dn: olcOverlay=ppolicy,olcDatabase={2}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: ppolicy
olcPPolicyDefault: cn=default,ou=pwpolicies,dc=example,dc=com
olcPPolicyHashCleartext: TRUE
olcPPolicyUseLockout: FALSE
EOF
Start slapd in the foreground with replication debugging.
/opt/openldap/libexec/slapd -d 0x4100 -F /etc/opt/openldap/slapd.d -h "ldapi:/// ldap:/// ldaps:///"
Apply Role as Provider Replica
Perform this step only once on the provider. Skip this step if you are setting up a consumer. Create the accesslog database and enable the accesslog overlay for dc=example,dc=com. The accesslog database is used for delta-syncrepl replication. Enable the syncprov overlays for both cn=accesslog and dc=example,dc=com databases.
ldapadd -H ldaps://ldapmaster.example.com -D cn=Manager,cn=config -W <<EOF
dn: olcDatabase={3}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {3}mdb
olcMonitoring: TRUE
olcSuffix: cn=accesslog
olcRootDN: cn=accesslog
olcDbDirectory: /var/opt/openldap/accesslog
olcDbMaxSize: 42949672960
olcDbIndex: default eq
olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart
olcAddContentAcl: FALSE
olcLastMod: TRUE
olcMaxDerefDepth: 15
olcReadOnly: FALSE
olcSyncUseSubentry: FALSE
olcDbNoSync: TRUE
olcAccess: {0}to *
by dn.exact="cn=Manager,cn=config" read
by dn.exact="cn=Manager,dc=example,dc=com" read
by dn.exact="cn=Replicator,dc=example,dc=com" read
by * none
olcLimits: {0}dn.exact="cn=Manager,dc=example,dc=com"
size.soft=unlimited size.hard=unlimited
time.soft=unlimited time.hard=unlimited
olcLimits: {1}dn.exact="cn=Replicator,dc=example,dc=com"
size.soft=unlimited size.hard=unlimited
time.soft=unlimited time.hard=unlimited
dn: olcOverlay=accesslog,olcDatabase={2}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcAccessLogConfig
olcOverlay: accesslog
olcAccessLogDB: cn=accesslog
olcAccessLogOps: writes
olcAccessLogPurge: 7+00:00 1+00:00
olcAccessLogSuccess: TRUE
dn: olcOverlay=syncprov,olcDatabase={2}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: FALSE
olcSpReloadHint: TRUE
dn: olcOverlay=syncprov,olcDatabase={3}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: TRUE
olcSpReloadHint: TRUE
EOF
Feed the LDAP database with the base tree.
ldapadd -H ldaps://ldapmaster.example.com -D cn=Manager,dc=example,dc=com -W <<EOF
dn: dc=example,dc=com
objectClass: top
objectClass: dcobject
dn: cn=Manager,dc=example,dc=com
cn: Manager
objectClass: top
objectClass: organizationalrole
dn: ou=pwpolicies,dc=example,dc=com
ou: pwpolicies
objectClass: organizationalUnit
objectClass: top
dn: cn=default,ou=pwpolicies,dc=example,dc=com
cn: default
sn: Default Password Policy
objectClass: pwdPolicyChecker
objectClass: pwdPolicy
objectClass: person
objectClass: top
pwdAttribute: userPassword
pwdMinAge: 0
pwdMaxAge: 0
pwdInHistory: 0
pwdCheckQuality: 0
pwdMinLength: 6
pwdExpireWarning: 0
pwdGraceAuthNLimit: 0
pwdLockout: TRUE
pwdLockoutDuration: 3600
pwdMaxFailure: 10
pwdFailureCountInterval: 300
pwdMustChange: FALSE
pwdAllowUserChange: TRUE
pwdSafeModify: FALSE
dn: cn=Replicator,dc=example,dc=com
cn: Replicator
sn: LDAP Replication User
objectClass: person
objectClass: top
userPassword: {SSHA}ENCRYPTEDPASSWORD
EOF
Apply Role as Consumer Replica
Perform these steps on every single consumer. Change names of hosts and certificate files accordingly. If provider is unreachable: Retry interval is 60 seconds for the first 10 times and then every 300 seconds until success. Replication starts immediately after running this command.
ldapmodify -v -H ldaps://ldap1.example.com -D cn=Manager,cn=config -W <<
dn: olcDatabase={2}mdb,cn=config
changetype: modify
add: olcSyncrepl
olcSyncrepl: rid=0
provider="ldaps://ldapmaster.example.com:636"
searchbase="dc=example,dc=com"
type=refreshAndPersist
retry="60 10 300 +"
scope=sub
schemachecking=on
bindmethod=simple
binddn="cn=Replicator,dc=example,dc=com"
credentials=CLEARTEXTPASSWORD
logbase="cn=accesslog"
logfilter="(objectClass=*)"
syncdata=accesslog
tls_cert=/etc/pki/tls/certs/node-cert.pem
tls_key=/etc/pki/tls/private/node-key.pem
tls_cacert=/etc/pki/tls/certs/example-ca.pem
-
add: olcUpdateRef
olcUpdateRef: ldaps://ldapmaster.example.com:636
-
EOF
Verify Replication
When trying to modify your password on a readonly replica results in an return of a referral.
ldapmodify -v -H ldaps://ldap1.example.com -D uid=someuser,ou=people,dc=example,dc=com -W <<EOF
dn: uid=someuser,ou=people,dc=example,dc=com
changetype: modify
replace: userPassword
userPassword: {SSHA}THENEWENCRYPTEDPASSWORD
EOF
Enter LDAP Password:
modifying entry "uid=someuser,ou=people,dc=example,dc=com"
ldap_modify: Referral (10)
referrals:
ldaps://ldapmaster.example.com:636/uid=someuser,ou=people,dc=example,dc=com
Verify data integrity across all replicas. Replace CLEARTEXPASSWORD with the actual password. Bind as manger compares also the hidden userpassword attributes.
LDAPSERVERS=(ldapmaster.example.com ldap1.example.com ldap2.example.com ldap3.example.com)
for server in ${LDAPSERVERS[*]}; do
ldapsearch -LLL -H ldaps://$server -b dc=example,dc=com \
-D cn=Manager,dc=example,dc=com -w 'CLEARTEXTPASSWORD' >/tmp/$server.ldif
md5sum /tmp/$server.ldif
rm -f /tmp/$server.ldif
done
a9efca26be302f31150e8068df0c192d /tmp/ldapmaster.example.com.ldif
a9efca26be302f31150e8068df0c192d /tmp/ldap1.example.com.ldif
a9efca26be302f31150e8068df0c192d /tmp/ldap2.example.com.ldif
a9efca26be302f31150e8068df0c192d /tmp/ldap3.example.com.ldif
Repeat this after turning off one consumer replica, changing data on the provider replica and starting the consumer replica again. The checksum should have changed but be identical on all servers. Iterate through all consumer replicas if necessary. Turn off the provider replica. The consumer replicas should still serve readonly data.
If a consumer replica is out of sync, stop the slapd, erase the database files and start slapd again. A full replication will start.