LDAP server with 389ds: Part 2 – Quality-of-life plugins

In part 1 we’ve looked at how to install 389 Directory Server and create an instance. While this left us with a fully working LDAP server there are some plugins that greatly increase quality of life and might even be considered essential.

3. Plugins

3.1. Attribute Uniqueness

Certain attributes can have duplicate values within a directory. A simple example is a user’s first name which is stored in the givenName attribute. In a directory with a sufficiently large number of users it’s more likely than not that there are two or more users with the same first name.
But how about a user’s email address? If the mail attribute wasn’t unique within the whole directory this could lead to some trouble when delivering emails to a user’s inbox. Of course, a directory administrator might be careful when creating new users and ensure that each user has their own, unique email address. But humans make mistakes and checking manually if a certain email address is not already used is cumbersome at least.

The 389 directory server has a built-in plugin that can guarantee the uniqueness of attributes across the entire directory information tree (DIT) or even just certain subtrees. It can be conveniently configured and enabled through dsconf.
Let’s say we wanted to guarantee that email addresses are unique across our whole dc=example,dc=com tree, we would run:

dsconf localhost plugin attr-uniq add "mail attribute uniqueness" --attr-name mail --subtree "dc=example,dc=com"
dsconf localhost plugin attr-uniq enable "mail attribute uniqueness"

Other very commonly unique attributes are a user’s ID (uid), uid number (the integer uniquely identifying a user, uidNumber) and gid number (the integer uniquely identifying a user’s primary group, gidNumber). This is not a exhaustive list, of course, and which attributes are unique in a directory may very well depend on the use case. But together with the mail attribute it’s probably a good starting point:

dsconf localhost plugin attr-uniq add "mail attribute uniqueness" --attr-name mail --subtree "dc=example,dc=com"
dsconf localhost plugin attr-uniq add "uid attribute uniqueness" --attr-name uid --subtree "dc=example,dc=com"
dsconf localhost plugin attr-uniq add "uidNumber attribute uniqueness" --attr-name uidNumber --subtree "dc=example,dc=com"
dsconf localhost plugin attr-uniq add "gidNumber attribute uniqueness" --attr-name gidNumber --subtree "dc=example,dc=com"
dsconf localhost plugin attr-uniq enable "mail attribute uniqueness"
dsconf localhost plugin attr-uniq enable "uid attribute uniqueness"
dsconf localhost plugin attr-uniq enable "uidNumber attribute uniqueness"
dsconf localhost plugin attr-uniq enable "gidNumber attribute uniqueness"

The attr-uniq plugin can also ensure uniqueness of values across multiple attributes. That’s called Multi Valued Uniqueness and is done by specifying multiple attribute names:

dsconf localhost plugin attr-uniq add "mail attribute uniqueness" --attr-name mail --attr-name mailAlternateAddress --subtree "dc=example,dc=com"

Note that the 389ds instance has to be restarted in order for the plugin to work (even though it was enabled through dsconf:

dsctl localhost restart

3.2. Distributed Numeric Assignment

Similarly to manually checking for certain attributes to be unique it’s cumbersome to manually assign and manage uidNumber and gidNumber attributes. Fortunately this can be automatically handled by the Distributed Numeric Assignment (DNA) plugin.

The plugin settings can be configured through dsconf again:

dsconf localhost plugin dna config "Account IDs" add \
  --type gidNumber uidNumber \
  --filter "(objectclass=posixAccount)" \
  --scope ou=People,dc=example,dc=com \
  --next-value 1001 \
  --magic-regen -1

The parameters should be quite self-explanatory. Here’s a quick overview anyway:

  • --type gidNumber uidNumber
    the DNA plugin should manage the attributes gidNumber and uidNumber (from the same range)
  • --filter "(objectclass=posixAccount)"
    it should be limited to posixAccount objects
  • --scope ou=People,dc=example,dc=com
    only objects in the specified subtree should be managed
  • --next-value 1001
    the next value that’s going to be assigned
  • --magic-regen -1
    magic number to tell the DNA plugin that an object’s gidNumber or uidNumber should be automatically assigned

After enabling the plugin and restarting the 389ds instance

dsconf localhost plugin dna enable
dsctl localhost restart

the DNA plugin will automatically reassign uidNumber and gidNumber attributes if they’re -1

# dsidm -b "dc=example,dc=com" localhost user create \
  --uid eve \
  --cn Eve \
  --displayName Eve \
  --homeDirectory "/home/eve" \
  --uidNumber -1 \
  --gidNumber -1
Successfully created eve
# ldapsearch -LLL -x -b "dc=example,dc=com" "(uid=eve)" uidNumber gidNumber
dn: uid=eve,ou=people,dc=example,dc=com
uidNumber: 1001
gidNumber: 1001

You can, however, still specify uidNumber or gidNumber attributes manually by using a value that’s different from the configured magic number (-1 in the example above). The magic number itself is arbitrary and doesn’t need to be -1 at all.

For more details on the DNA plugin have a look at the Redhat Directory Server 11 (RHDS11) Administration Guide or the 389ds documentation, especially when using it in setups that use replication.

3.3. MemberOf

There are two ways how group membership of users can be stored. One can either save a list of members in each group object. That’s how it is done traditionally and is described in RFC2307:

members being stored as group attributes according to RFC2307

Or one can store each group that a given user is a member of in the user object (RFC2307bis):

group membership being stored as attribute in a user object (RFC2307bis)

Querying an LDAP server that uses RFC2307 (sometimes also referred to as posix schema) if a certain user is authorised to access a particular resource is a two-step process: The first query searches for the user object (e.g. a user named Bob) and the second query checks if the user object’s uid attribute matches one of the entries in the relevant group object (e.g. if the group blog_users has an attribute memberUID that matches Bob’s uid).
A lot of software supports this, e.g. have a look at Grafana’s LDAP auth module. It has a search_filter parameter that let’s you query for a particular user and an additional group_search_filter parameter that filters out users that are not a member of a particular group.

However, that’s not always the case, e.g. postfix or dovecot do not have an additional group filter. The advantage of the RFC2307bis schema is (among other things) that checking user authorisation can be done in a single query, for example:

ldapsearch -LLL -x -b "dc=example,dc=com" \
  "(&(uid=eve)(memberOf=cn=cncuser,ou=groups,dc=example,dc=com))" mail
dn: uid=eve,ou=people,dc=example,dc=com
mail: eve@example.com

389ds uses a combination of these two schemas so it stays compatible with both. The memberOf plugin can automatically update a user object’s memberOf attributes whenever the group membership status is changed. Once again it can be enabled through dsctl:

dsconf localhost plugin memberof enable
dsctl localhost restart

Note that additonally group members will still be listed as multi-valued attributes in the group object (according to the RFC2307 schema). So you can decide depending on the user case wether to use the memberOf filter or two queries to check if a user is authorised to access your resource.

3.4. Referential Integrity

In general, referential integrity is a property of arbitrary data structures (e.g. an object in OOP, a table in a relational database) meaning that all references to other data structures are vaild. In the context of a directory server it means that an update on one entry in the DIT is correctly reflected in all other entries that reference it.
This is most commonly an issue when removing a user or a group. When a user that’s a member of any group is removed, all corresponding member attributes of the groups need to be updated as well. Managing this manually would be a nightmare, of course, so thankfully there’s a referential integrity plugin available that takes care of this:

dsconf localhost plugin referential-integrity enable
dsctl localhost restart

That’s it regarding plugins. There are many more very useful ones available but these should cover the basics. The next part is going to look into ACLs.

2 thoughts on “LDAP server with 389ds: Part 2 – Quality-of-life plugins

  1. avatarJohannes

    Can you explain a little around how to activate rfc2307? Obviously I can enable memberOf plugin and I see the group DN is populated by member attributes (rfc2307bis). Accordingly memberOf attribute in user DN is populated. Nevertheless, the memberUid attributes in group DN remain always untouched – can’t they get synced automatically?!

  2. avatarTadeu

    I’m guessing RFC2307 isn’t exactly compatible with RFC2307bis. You use either, but not both. I’ve noticed that specifying «memberUid» as the group attribute, made MemberOf prevent 389ds from booting, due to MemberOf not liking the OID used for that field, that is neither a distinguished name nor another defined name OID. Thus, to make it work, you kind of have to adopt RFC2307bis. Then, your PosixGroup will have a «member» field, that is filled with the full distinguished name of the member object. From what I gather, SSSD deals cleanly with this. NSLCD needs configuration, but probably works fine, as well. Your mileage may vary, depending on the application you’re dealing with.

    https://github.com/389ds/389-ds-base/issues/5968

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.