Ldap replication with syncrepl and ssl

In this post I’m going to describe how I use ldap replication to sync user accounts from my web server to my home server.
On my home server I’m going to setup a ldap server as well. As the user accounts on the “web server” are already stored in a ldap it seems logical to use ldap replication to keep both servers in sync. The ldap on the “web server” (my rented server running mail server, web server, onwcloud etc.) will be used as master, the home server will be the slave. It seems now to be common to talk about provider and consumer instead of master and slave. By the way I consider these terms to be more apropriate for the situation they describe.

This is a post from my old blog http://tech.cbjck.de. It has been moved here and slightly edited for better readability. It's also been adjusted to the new layout.
The content however is old and might be outdated.


I’ve found a quite good tutorial here: http://documentation.fusiondirectory.org/en/documentation/replication_syncrepl. Here just a short write up off my steps:
Both servers are running debian, but this should work similar on every linux system running openldap >2.4

Setup up ldap replication on the provider

First create a directory for the accesslog:

mkdir -p /var/lib/ldap/accesslog
cp /var/lib/ldap/DB_CONFIG /var/lib/ldap/accesslog
chown -R openldap:openldap /var/lib/ldap/accesslog

Then create a user synchronisator. For example create a file synchronisator.ldif:

dn: cn=synchronisator,dc=example,dc=com
changetype: add
objectClass: top
objectClass: person
cn: synchronisator
sn: synchronisator
description: LDAP synchronisator
userPassword: {SSHA}8nrao8svJN4tfAHRAMJOSOFr+hqWato4

Then add these changes to the ldap:

ldapadd -D cn=admin,cn=config -W -f synchronisator.ldif

We’ll need some indexes and modules for replication. To enables these create a file provider.ldif:

# Add indexes/modify ACL to the frontend db.
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: entryCSN eq
-
add: olcDbIndex
olcDbIndex: entryUUID eq
-
add: olcAccess
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=example,dc=com" write by dn="cn=synchronisator,dc=example,dc=com" read by * none

#Load the syncprov and accesslog modules.
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov
-
add: olcModuleLoad
olcModuleLoad: accesslog

# Accesslog database definitions
dn: olcDatabase={2}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {2}hdb
olcDbDirectory: /var/lib/ldap/accesslog
olcSuffix: cn=accesslog
olcRootDN: cn=admin,dc=example,dc=com
olcDbIndex: default eq
olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart
olcAccess: to * by dn="cn=synchronisator,dc=example,dc=com" write

# Accesslog db syncprov.
dn: olcOverlay=syncprov,olcDatabase={2}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: TRUE
olcSpReloadHint: TRUE
# syncrepl Provider for primary db
dn: olcOverlay=syncprov,olcDatabase={1}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: TRUE

# accesslog overlay definitions for primary db
dn: olcOverlay=accesslog,olcDatabase={1}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcAccessLogConfig
olcOverlay: accesslog
olcAccessLogDB: cn=accesslog
olcAccessLogOps: writes
olcAccessLogSuccess: TRUE
# scan the accesslog DB every day, and purge entries older than 7 days
olcAccessLogPurge: 07+00:00 01+00:00

In my case line 11 has to look like

olcAccess: {0}to attrs=userPassword,shadowLastChange by dn="cn=dovecot,dc=cbjck,dc=de" read by dn="cn=synchronisator,dc=cbjck,dc=de" read by self write by anonymous auth by dn="cn=admin,dc=cbjck,dc=de" write by * none

as dovecot as well has to be able to read user passwords.

Apply these changes by

ldapadd -D cn=admin,cn=config -W -f provider.ldif

Now the provider side should already be working. However we can’t test it without a consumer.

Set up ldap replication on the consumer

Installation

As I did set up my home server ldap from scratch I’ll write down the complete steps.
First install slapd:

aptitude install slapd ldap-utils

Basic configuration

Then do the basic configuration by

dpkg-reconfigure slapd

Make sure to set the following:

  • set the base dn, eg: dc=cbjck,dc=de
  • set the organisation (ie the o= field of the base record
  • passwort for admin (dn cn=admin,dc=cbjck,dc=de)
  • set backend to hdb (default)
  • do not allow LDAPv2

Next edit /etc/ldap/ldap.conf to contain the following:

BASE    dc=example,dc=com
URI     ldap://homeserver.localdomain
TLS_REQCERT = never

<h3>Gain access to cn=config</h3>
Create a file <code>config.ldif</code>:

dn: olcDatabase={-1}frontend,cn=config
changetype: modify
delete: olcAccess

dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=admin,cn=config

dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: {SSHA}8S1oP5BG2cYEpxQYcc2YbQxwUBZwxR7v

dn: olcDatabase={0}config,cn=config
changetype: modify
delete: olcAccess

Of course you will replace the password hash (this one is test.pw) by something really strong. You can use

slappasswd

to create it.

Apply these changes:

ldapadd -Y EXTERNAL -H ldapi:/// -f config.ldif

Now you can use the bind dn cn=admin,cn=config to edit cn=config.

Clean ldap consumer

To make sure that nothing is in the way for replication we do some cleaning:

/etc/init.d/slapd stop
rm -f /var/lib/ldap/*.* /var/lib/ldap/alock
rm -rf /etc/ldap/slapd.d/cn=config/oclDatabase*hdb*
/etc/init.d/slapd start

Set up syncrepl

This has turned out to be a bit complicated as I want the connection between provider and consumer (or master and slave) to be encrypted. It took me quite a wile to figure out how this works. Watching /var/log/syslog on both machines and adjusting the slapd loglevel (see eg man slapd.conf) is a good idea. My provider ssl setup is described here, the following works for me on the consumer.

Create a file consumer.ldif:

#Load the syncprov module.
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov

# syncrepl specific indices
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: entryUUID eq
-
add: olcSyncRepl
olcSyncRepl: rid=001 provider=ldaps://ldap.example.com:636 bindmethod=simple binddn="cn=synchronisator,dc=cbjck,dc=de" credentials=password searchbase="dc=example,dc=com" starttls=yes tls_reqcert=never tls_cacert=/etc/ssl/certs/ca.pem logbase="cn=accesslog" logfilter="(&amp;(objectClass=auditWriteObject)(reqResult=0))" schemachecking=on type=refreshAndPersist retry="60 +" syncdata=accesslog
-
add: olcUpdateRef
olcUpdateRef: ldaps://ldap.example.com:636</pre>
and apply the changes:
<pre>ldapadd -D cn=admin,cn=config -W -H ldapi:/// -f consumer.ldif

Test

Check if things are working. You can try

ldapsearch -LLL -D "cn=admin,dc=example,dc=com" -W -H ldap://127.0.0.1 -b dc=example,dc=com

Problems and errors should also appear in /var/log/syslog.
In my case problems occured as the provider had schemas the consumer didn’t have. The solution comes in the next part.

Set up schema replication

As already noted before replication does not work properly unless both provider and consumer have the same schemas. Fortunatly there is no need to sync this manually, we can use syncrepl for this as well.

On the provider

Create a file provider_schema_repl.ldif:

dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcAccess
olcAccess: to dn.subtree="cn=schema,cn=config" by dn="cn=synchronisator,dc=example,dc=com" read

dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov

This allows the user synchronisator to read the configs in cn=schema,cn=config.
Apply the changes:

ldapadd -D cn=admin,cn=config -W -f provider_schema_repl.ldif

On the consumer

Create a file consumer_schema_repl.ldif:

dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=002 provider=ldaps://server.cbjck.de bindmethod=simple binddn="cn=synchronisator,dc=cbjck,dc=de" credentials=password starttls=critical tls_reqcert=never tls_cacert=/etc/ssl/certs/sub.class2.server.ca.pem searchbase="cn=schema,cn=config" type=refreshAndPersist retry="60 +"

Add the changes to the LDAP:

ldapadd -D cn=admin,cn=config -W -H ldapi:/// -f consumer_schema_repl.ldif

Now it’s time to lean back and watch your consumer to populate.

Testing

Try

ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config

on provider and consumer. Watch out that numResponses is same.

4 Comments

  1. Pingback: ldap user login | cbjck.de

  2. Hi, there seem’s to be a bug:
    rm -rf /etc/ldap/slapd.d/cn=config/oclDatabase*hdb*
    doesn’t work because “oclDatabase” should be “olcDatabase”

    But if you correct this typo and remove those files, applying consumer.ldif doesn’t work.

    • Thanks for your comment.

      I see two possibilities:

      • either I’ve used the rm command as printed and didn’t delete anything and it works.
      • or I did delete those files and it somehow worked for me.
      • As it’s quite a long time ago I last did this I don’t remember. My guess would be I didn’t delete the files. Maybe you also try that.
        In any case: do you get any error message?

  3. warning !
    if you are a big directory change olcLimits on the master
    replication may not work when this happens, entries such as the following appear the log:
    slapd[209]: do_syncrep2: rid=001 LDAP_RES_SEARCH_RESULT (4) Size limit exceeded
    slapd[209]: do_syncrep2: rid=001 (4) Size limit exceeded
    (in the slave)

    and
    conn=1013 op=1 SEARCH RESULT tag=101 err=4 nentries=500 text=
    (in the master)

    This can occur if a replica is created when there are more than 500 objects in the LDAP.
    500 is the default maximum number of objects that can returned in a search.

    Change the value :
    dn: olcDatabase={-1}frontend,cn=config
    changetype: modify
    replace: olcSizeLimit
    olcSizeLimit: 50000

Leave a Reply

Your email address will not be published. Required fields are marked *