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.