Configure OpenLDAP memberOf and Referential Integrity Overlays

Configure OpenLDAP memberOf and refint overlays for groupOfNames membership, reverse membership lookup, and automatic reference cleanup.

Published

Updated

Read time 13 min read

Reviewed byDeepak Prasad

OpenLDAP groupOfNames member and memberOf relationship with referential integrity

This guide configures the OpenLDAP memberOf and referential integrity (refint) overlays for DN-based group membership.

You will learn how to:

  • Create DN-based groupOfNames groups
  • Store members using the member attribute
  • Generate reverse memberOf values automatically
  • Remove stale group references when users are deleted
  • Update group references when users are renamed
  • Test rename and deletion behavior on disposable accounts
  • Populate memberOf for existing memberships

The procedure applies to OpenLDAP 2.6 across the RHEL family, including Red Hat Enterprise Linux, Rocky Linux, AlmaLinux, Oracle Linux, and CentOS Stream. OpenLDAP removed the deprecation of slapo-memberof in OpenLDAP 2.6.8, so the overlay remains a valid 2.6 topic.

Tested on: Rocky Linux 10.2 with openldap-servers 2.6.10-1.el10_2 from EPEL 10.2.

The lab uses:

Setting Value
OpenLDAP server ldap-server.example.com
Base DN dc=example,dc=com
Users OU ou=people,dc=example,dc=com
Groups OU ou=groups,dc=example,dc=com
Group object class groupOfNames
Membership attribute member
Reverse membership memberOf
Example DN-based group cn=app-developers,ou=groups,dc=example,dc=com

The lab also contains cn=developers as an RFC2307 posixGroup with memberUid for SSSD. This lesson adds a separate groupOfNames entry so the two membership models do not collide on the same DN. If member versus memberUid terminology is new, read LDAP and OpenLDAP basics first.

Complete these lessons first:

IMPORTANT
The memberOf overlay requires DN-valued membership such as groupOfNames with member. It does not directly generate reverse membership from RFC2307 posixGroup entries that use username-based memberUid.
NOTE
This article demonstrates a single-server configuration. In a replicated deployment, configure memberof on each consumer where applications need the generated memberOf attribute. Exclude memberOf from syncrepl with exattrs, because each server maintains its own generated values. Enable olcMemberOfAddCheck: TRUE when replicated Add operations may arrive before the member entries they reference. Keep olcMemberOfDangling set to ignore when using add-check. Follow OpenLDAP provider-consumer replication or OpenLDAP multi-provider replication before applying this configuration to providers and consumers.

Understand member, memberOf, and refint

DN-based groups store membership on the group entry. The memberof overlay mirrors that relationship onto each member entry as an operational memberOf attribute.

text
Group entry
cn=app-developers,ou=groups,dc=example,dc=com
    |
    | member
    v
uid=jdoe,ou=people,dc=example,dc=com
    |
    | generated by memberOf overlay
    v
memberOf: cn=app-developers,ou=groups,dc=example,dc=com
Component Purpose
groupOfNames DN-based LDAP group
member Stores the complete DN of each group member
memberOf Reverse membership generated on the member entry
memberof overlay Maintains memberOf when group membership changes
refint overlay Updates member references after rename or deletion

Administrators modify the group’s member values. Applications can search the user’s generated memberOf values. Do not manually maintain memberOf.

memberOf is operational, so a search must request it explicitly. A query that returns only uid and cn omits memberOf even when the overlay has already generated it.

refint maintains the forward member reference. It does not write memberOf directly—that remains the job of the memberof overlay after refint fixes the group entry.

Compare the two common group models:

Group model Membership value Suitable for memberOf
groupOfNames Full user DN in member Yes
posixGroup Username in memberUid No
RFC2307bis-style group Full user DN in member Yes

POSIX account management with memberUid belongs in Manage OpenLDAP users and groups. ACL rules that reference groupOfNames membership belong in OpenLDAP ACL examples.


Back Up cn=config and Discover the MDB Database

Export cn=config before you load modules or attach overlays. Store the backup in a root-only directory:

bash
sudo install -d -m 0700 /root/openldap-memberof-backup

Dump the live configuration database to LDIF:

bash
sudo slapcat -F /etc/openldap/slapd.d -n 0 -l /root/openldap-memberof-backup/cn-config-before-memberof.ldif

The command exits silently on success when the LDIF file is written.

Discover the MDB database DN for your suffix instead of assuming olcDatabase={2}mdb,cn=config:

bash
MDB_DN=$(
  sudo ldapsearch \
    -Q \
    -LLL \
    -Y EXTERNAL \
    -H ldapi:/// \
    -b cn=config \
    '(&(objectClass=olcMdbConfig)(olcSuffix=dc=example,dc=com))' dn |
  awk '/^dn: / {sub(/^dn: /, ""); print; exit}'
)

Print the discovered value so later LDIF files use the correct parent DN:

bash
printf 'MDB database DN: %s\n' "$MDB_DN"

Stop if discovery returned nothing. An empty $MDB_DN produces overlay LDIF with a missing parent DN:

bash
if [[ -z "$MDB_DN" ]]; then
    echo "No MDB database was found for dc=example,dc=com" >&2
    exit 1
fi

Sample output on the lab server:

output
MDB database DN: olcDatabase={2}mdb,cn=config

List existing module loads and overlays before you add new ones:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  '(|(objectClass=olcModuleList)(objectClass=olcOverlayConfig))' \
  dn olcModuleLoad olcOverlay

Sample output on the lab server before this lesson adds memberof and refint:

output
dn: cn=module{0},cn=config
olcModuleLoad: {0}syncprov.la
olcModuleLoad: {1}ppolicy.so

dn: olcOverlay={0}syncprov,olcDatabase={2}mdb,cn=config
olcOverlay: {0}syncprov

dn: olcOverlay={1}ppolicy,olcDatabase={2}mdb,cn=config
olcOverlay: {1}ppolicy

Your server may show different ordering prefixes or fewer overlays. Treat the discovered $MDB_DN and module-list DN as authoritative.


Configure the memberOf and refint Overlays

Confirm the installed package ships the overlay modules:

bash
rpm -ql openldap-servers | grep -E '/(memberof|refint).*\.(so|la)$'

Sample output:

output
/usr/lib64/openldap/memberof.la
/usr/lib64/openldap/memberof.so
/usr/lib64/openldap/refint.la
/usr/lib64/openldap/refint.so

Load only modules that are not already present. Discover the module-list DN first:

bash
MODULE_DN=$(
  sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config '(objectClass=olcModuleList)' dn |
  awk '/^dn: / {sub(/^dn: /, ""); print; exit}'
)

Fail fast when no module-list entry exists:

bash
if [[ -z "$MODULE_DN" ]]; then
    echo "No olcModuleList entry was found under cn=config" >&2
    exit 1
fi

When memberof.la is missing from that entry, add it with ldapmodify through the LDAPI socket:

bash
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
dn: $MODULE_DN
changetype: modify
add: olcModuleLoad
olcModuleLoad: memberof.la
EOF

Repeat the same pattern for refint.la when it is not already loaded.

Loading a module makes the overlay available; it does not attach the overlay to a database. Module management patterns are covered in OpenLDAP cn=config and MDB explained.

Configure memberOf

Attach the memberof overlay to the discovered MDB database. Replace $MDB_DN with the value from the previous section:

ldif
dn: olcOverlay=memberof,REPLACE_WITH_MDB_DN
objectClass: olcOverlayConfig
objectClass: olcMemberOfConfig
olcOverlay: memberof
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf
olcMemberOfDangling: ignore
Setting Role
olcMemberOfGroupOC Group object class that carries member
olcMemberOfMemberAD Forward membership attribute
olcMemberOfMemberOfAD Generated reverse attribute
olcMemberOfDangling Controls behavior when a member DN does not exist
olcMemberOfDangling value Behaviour
ignore Allows the operation and may leave a dangling group reference
drop Discards changes that would produce dangling references
error Rejects the modification with an LDAP error

ignore is reasonable for this lab, particularly because olcMemberOfAddCheck requires the default ignore behavior.

Apply the overlay with LDAPI:

bash
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f memberof-overlay.ldif

A successful add prints the new overlay DN and exits without further output.

Configure referential integrity

The refint overlay performs internal modifications as the database olcRootDN. Confirm that value is set on the target MDB database before you attach the overlay:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b "$MDB_DN" \
  -s base \
  olcRootDN

Sample output on the lab server:

output
dn: olcDatabase={2}mdb,cn=config
olcRootDN: cn=admin,dc=example,dc=com

A root password is not required for these internal operations, but the MDB database must have an olcRootDN configured. The configured DN does not need to exist as an entry in the directory.

If the search returns no olcRootDN, configure one on the MDB database before attaching refint.

Attach a separate refint overlay on the same database:

ldif
dn: olcOverlay=refint,REPLACE_WITH_MDB_DN
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
olcOverlay: refint
olcRefintAttribute: member
olcRefintNothing: cn=empty-group-placeholder,ou=service-accounts,dc=example,dc=com

olcRefintAttribute must use DN syntax. List only forward references such as member. Do not include memberOf; that attribute is generated by memberof, not maintained as a writable reference.

olcRefintNothing is a placeholder DN that refint can insert when the last real member would otherwise be removed from a groupOfNames entry. Create that entry first:

ldif
dn: cn=empty-group-placeholder,ou=service-accounts,dc=example,dc=com
objectClass: top
objectClass: organizationalRole
cn: empty-group-placeholder
description: Placeholder DN for refint when a groupOfNames would otherwise have zero members

Add the placeholder with an administrator bind over StartTLS, then attach the refint overlay through LDAPI:

bash
ldapadd -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -f empty-group-placeholder.ldif
bash
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f refint-overlay.ldif

Verify both overlays are attached to the MDB database:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b "$MDB_DN" \
  '(objectClass=olcOverlayConfig)' \
  dn olcOverlay

You should see memberof and refint listed under the same olcDatabase={…}mdb DN as your suffix.

Confirm the loaded modules and attached overlays together:

bash
sudo ldapsearch \
  -Q \
  -LLL \
  -Y EXTERNAL \
  -H ldapi:/// \
  -b cn=config \
  '(|(objectClass=olcModuleList)(objectClass=olcOverlayConfig))' \
  dn olcModuleLoad olcOverlay

Sample output after this lesson’s configuration on the lab server:

output
dn: cn=module{0},cn=config
olcModuleLoad: {0}syncprov.la
olcModuleLoad: {1}ppolicy.so
olcModuleLoad: {2}memberof.la
olcModuleLoad: {3}refint.la

dn: olcOverlay={0}syncprov,olcDatabase={2}mdb,cn=config
olcOverlay: {0}syncprov

dn: olcOverlay={1}ppolicy,olcDatabase={2}mdb,cn=config
olcOverlay: {1}ppolicy

dn: olcOverlay={2}memberof,olcDatabase={2}mdb,cn=config
olcOverlay: {2}memberof

dn: olcOverlay={3}refint,olcDatabase={2}mdb,cn=config
olcOverlay: {3}refint

Create and Verify a groupOfNames Group

Create a DN-based group with at least one member value:

ldif
dn: cn=app-developers,ou=groups,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
cn: app-developers
description: Application development team
member: uid=jdoe,ou=people,dc=example,dc=com

Add the group over StartTLS:

bash
ldapadd -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -f app-developers-group.ldif

Sample output:

output
adding new entry "cn=app-developers,ou=groups,dc=example,dc=com"

Confirm the forward membership on the group entry:

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "cn=app-developers,ou=groups,dc=example,dc=com" \
  -s base \
  -LLL \
  cn member

Sample output:

output
dn: cn=app-developers,ou=groups,dc=example,dc=com
cn: app-developers
member: uid=jdoe,ou=people,dc=example,dc=com

Request memberOf explicitly on the member entry:

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "uid=jdoe,ou=people,dc=example,dc=com" \
  -s base \
  -LLL \
  uid memberOf

Sample output:

output
dn: uid=jdoe,ou=people,dc=example,dc=com
uid: jdoe
memberOf: cn=app-developers,ou=groups,dc=example,dc=com

Compare with a search that omits memberOf from the requested attribute list:

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "uid=jdoe,ou=people,dc=example,dc=com" \
  -s base \
  -LLL \
  uid cn

Sample output:

output
dn: uid=jdoe,ou=people,dc=example,dc=com
cn: John Doe
uid: jdoe

The overlay generated memberOf, but the second query never asked for it.

Create a disposable test user

Before you add another group member, create a throwaway account for the rename and deletion tests later in this guide:

ldif
dn: uid=memberof-test,ou=people,dc=example,dc=com
objectClass: top
objectClass: inetOrgPerson
uid: memberof-test
cn: memberOf Test User
sn: Test User

Add the entry:

bash
ldapadd -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -f memberof-test-user.ldif

Add another member

Add the test user to cn=app-developers with ldapmodify:

bash
ldapmodify -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -f add-member.ldif

Use an LDIF such as:

ldif
dn: cn=app-developers,ou=groups,dc=example,dc=com
changetype: modify
add: member
member: uid=memberof-test,ou=people,dc=example,dc=com

Search the new user for the generated reverse link, but keep the membership in place for the rename and deletion tests in the next section. Deleting the disposable user later should cause refint to remove the group reference automatically.

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "uid=memberof-test,ou=people,dc=example,dc=com" \
  -s base \
  -LLL \
  uid memberOf

Test Referential Integrity

Run rename and deletion tests with disposable accounts. Do not experiment on production identities.

Rename a user

Rename the test user you added to cn=app-developers with ldapmodrdn. The -r flag removes the old RDN value so the entry does not retain both uid values:

bash
ldapmodrdn -x -ZZ -r \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  "uid=memberof-test,ou=people,dc=example,dc=com" \
  "uid=memberof-renamed"

Verify that the group’s member value now points at the new DN:

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "cn=app-developers,ou=groups,dc=example,dc=com" \
  -s base \
  -LLL \
  member

Sample output after rename on the lab server:

output
dn: cn=app-developers,ou=groups,dc=example,dc=com
member: uid=jdoe,ou=people,dc=example,dc=com
member: uid=memberof-renamed,ou=people,dc=example,dc=com

Confirm the renamed user still carries the generated memberOf value:

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "uid=memberof-renamed,ou=people,dc=example,dc=com" \
  -s base \
  -LLL \
  uid memberOf

Sample output:

output
dn: uid=memberof-renamed,ou=people,dc=example,dc=com
uid: memberof-renamed
memberOf: cn=app-developers,ou=groups,dc=example,dc=com

Delete a user

Delete the test user:

bash
ldapdelete -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  "uid=memberof-renamed,ou=people,dc=example,dc=com"

Verify that the deleted DN no longer appears in the group:

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "cn=app-developers,ou=groups,dc=example,dc=com" \
  -s base \
  -LLL \
  member

Sample output:

output
dn: cn=app-developers,ou=groups,dc=example,dc=com
member: uid=jdoe,ou=people,dc=example,dc=com

Delete the final group member

When the directory entry referenced by the final real member value is deleted, refint must remove that DN from the group. Because groupOfNames requires at least one member, olcRefintNothing supplies the configured placeholder DN. This behavior applies to cleanup performed by refint; directly deleting the final member value from the group can still fail schema validation.

Version note: The tested OpenLDAP 2.6.10 package predates the upstream 2.6.11 fix for a memberof and refint interaction during subtree renames. The leaf-user rename tested above is valid, but avoid moving or renaming complete OUs on 2.6.10 without dedicated testing. Prefer OpenLDAP 2.6.11 or later when subtree rename behavior is required.

The new-membership, rename, and deletion tests now confirm that both overlays work on this database. You can proceed with backfilling memberships that existed before memberof was enabled.


Populate memberOf for Existing Groups

Enabling the overlay does not backfill memberOf for memberships that already existed. The overlay reacts to member changes; it does not scan the entire directory on startup.

Do not manually add memberOf values to user entries.

Recommended backfill workflow:

  1. Back up the directory with OpenLDAP backup and restore.
  2. Export every groupOfNames entry and its member values.
  3. Process one group at a time.
  4. Remove and re-add each existing member value through normal LDAP modifications.
  5. Keep at least one valid member, or a documented placeholder, in single-member groups.
  6. Verify the generated memberOf values.
  7. Remove any temporary placeholder memberships.

List current DN-based groups:

bash
ldapsearch -x -ZZ \
  -H ldap://ldap-server.example.com \
  -D "cn=admin,dc=example,dc=com" \
  -W \
  -b "ou=groups,dc=example,dc=com" \
  -LLL \
  "(objectClass=groupOfNames)" \
  dn member

Sample output:

output
dn: cn=app-developers,ou=groups,dc=example,dc=com
member: uid=jdoe,ou=people,dc=example,dc=com

Re-touch one membership in a single ldapmodify operation:

ldif
dn: cn=app-developers,ou=groups,dc=example,dc=com
changetype: modify
delete: member
member: uid=jdoe,ou=people,dc=example,dc=com
-
add: member
member: uid=jdoe,ou=people,dc=example,dc=com

After the modify succeeds, search the user again for memberOf and confirm the group DN appears.

olcMemberOfAddCheck checks each online Add operation for existing groups that already reference the new DN. This commonly helps when replication or ldapadd creates entries out of order. It does not make slapadd process the overlay because slapadd bypasses normal overlay operations. It is not a substitute for a planned backfill of entries that were already present before the overlay existed.

Keep any automated backfill script short, run it against a backup copy first, and verify a sample of users and groups before you touch production data.


Troubleshoot memberOf and refint

Symptom Likely cause Recommended action
memberOf is not returned The attribute was not requested explicitly Repeat the search with memberOf in the attribute list
New membership does not generate memberOf The overlay is missing or attached to the wrong database Query overlays under $MDB_DN and reapply memberof on the suffix database
Existing users have no memberOf Memberships predate the overlay Backfill by re-touching each member value
posixGroup membership is ignored memberUid stores usernames, not DNs Use groupOfNames or RFC2307bis member for this overlay
User deletion leaves stale member values refint is missing or not monitoring member Add refint with olcRefintAttribute: member on the same database
Rename does not update group membership refint is attached to the wrong database or attribute Confirm olcRefintAttribute uses DN syntax and matches your group model
Last-member deletion fails groupOfNames requires at least one member Configure olcRefintNothing with a documented placeholder DN
Module cannot be loaded Wrong filename or module already loaded Compare rpm -ql openldap-servers output with olcModuleLoad
No such object (32) during overlay add Wrong numbered MDB DN Rediscover $MDB_DN from cn=config instead of copying an example

Parse the offline configuration when a module or overlay change fails at startup:

bash
sudo slaptest -F /etc/openldap/slapd.d -u

A successful check prints config file testing succeeded.

Review recent slapd messages when membership changes fail at runtime:

bash
sudo journalctl -u slapd -n 100 --no-pager

References


Summary

You configured DN-based groupOfNames groups with member forward references, enabled the memberof overlay to generate operational memberOf values, added refint to clean up member references after renames and deletions, verified both overlays on disposable test data, backfilled existing memberships by re-touching member values, and confirmed the behavior with explicit ldapsearch attribute requests.


Frequently Asked Questions

1. What is the OpenLDAP memberOf overlay?

The memberof overlay watches groupOfNames entries and maintains a generated memberOf attribute on each member entry when DN-valued member attributes change. Administrators modify member on the group; applications search memberOf on the user.

2. Why is memberOf not returned by a normal LDAP search?

memberOf is an operational attribute. Unless your client requests it explicitly or your default attribute list includes operational attributes, ldapsearch omits it even when the overlay has generated the value.

3. Can memberOf work with posixGroup and memberUid?

No for the standard memberof overlay. It expects DN-valued member attributes on groupOfNames or similar group object classes, not username strings in memberUid on posixGroup entries.

4. What is the difference between member and memberOf?

member is stored on the group entry and contains each member DN. memberOf is generated on the member entry and lists the groups that reference that DN.

5. Why is the refint overlay needed?

refint updates forward DN-valued references such as member when a referenced entry is renamed or deleted. memberof maintains the reverse memberOf attribute but does not rewrite member values after a modrdn.

6. Does enabling the overlay populate existing memberships?

Not automatically for relationships that existed before the overlay was enabled. Re-touch each member value with ldapmodify or run a planned backfill so the overlay processes every existing membership.

7. What happens when a user is renamed or deleted?

With refint configured for member, group references are rewritten when a user is renamed and removed when a user is deleted. The memberof overlay continues maintaining reverse memberOf values on entries that still exist.

8. Why can a groupOfNames group not be empty?

groupOfNames requires at least one member. When refint removes the final reference after a referenced entry is deleted, it can insert the configured olcRefintNothing placeholder so the group remains schema-valid.

9. Should I modify memberOf directly?

No. memberOf is maintained by the overlay. Change membership by adding or removing member values on the group entry.

10. How does memberOf behave with OpenLDAP replication?

memberOf maintenance is local and is not replicated. Configure memberof on each consumer where applications need generated memberOf values, exclude memberOf from syncrepl with exattrs, enable olcMemberOfAddCheck when replicated Add operations may arrive before member entries, and keep olcMemberOfDangling at ignore when using add-check. Follow the replication guides before applying this to providers and consumers.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …