Skip to main content

slapd.d generator

Project description

slapddgen

NOTE: This tool is very very alpha. Works for me, might work for you.

NOTE: Ensure your editor does not strip trailing whitespace if you open any of the files in cn=schema as that will break the ldif format.

slapddgen generates a slapd.d that you can load into an OpenLDAP server by pointing at it with slapd -F. This will start a server with online configuration, aka olc, meaning the configuration itself is stored in the directory server, not in slapd.conf.

Usage

The input for this tool is contained in config.json, or whatever --config_file is pointed at. There's too many things to do this with environment variables or CLI switches. Removing things from config.json is guaranteed to blow shit up, though for the modules, ACLs, indices and unique empty arrays should be fine.

Once you've installed slapddgen you can run it with a slapddgen generate --config_file=/path/to/config.json --output_dir=/path/to/write/config/to

Modifying the templates

If you know what you're doing you can modify the templates in templates/slapd.d to suit your needs. Doing so if you've pip installed slapddgen will be annoying, so in that case cloning the repository and doing a pip install -e . instead will be more helpful.

Docker

There is also an example Dockerfile that you can use to package it all up in a Docker container. This can be useful for testing purposes amongst other things.

Building

VERSION=$(git rev-parse --abbrev-ref HEAD)
docker build --no-cache=true \
    --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
    --build-arg COMMIT=$(git rev-parse HEAD) \
    --build-arg VERSION=${VERSION} \
    -t ldap:${VERSION} .

When building a release use git describe --abbrev=0 --tags instead for the VERSION.

Running

docker run -it -p 3389:389 -p 6636:636 ldap

This will expose port 389 in the container, the ldap:// URL, to the host on port 3389 (so you don't need to bind on a priviledged port) and do the same for port 636, ldaps://.

You can change the entrypoint with --entrypoint=/bin/sh. Once you've done that you can still manually launch OpenLDAP in the container by running slapd -u ldap -g ldap -d 256.

Production

When running it for production purposes it's important to ensure configuration and data can persist to disk. Otherwise you'd start up with a fresh and empty directory service every time your restart the container.

To that end, you should probably volume mount the following:

  • Configuration: /etc/openldap
  • Database: /var/lib/openldap/openldap-data

A separate user, ldapd is create which defaults to a UID of 55555. This is to ensure you can correctly map a host user and the container user so you don't get into permission issues with the volume mounts.

In order to change the UID of the ldapd user in the container you have to rebuild it and pass in --build-arg UID=<number>.

Configuration

slapddgen generates a bunch of configuration for you and assumes the following DIT layout:

- {{suffix}}
--- {{baseOU}},{{suffix}}
----- ou=accounts,{{baseOU}},{{suffix}}
----- ou=groups,{{baseOU}},{{suffix}}
----- ou=policy,{{baseOU}},{{suffix}}

You can of course change this yourself by modifying the templates.

The configuration generated uses online/dynamic configuration, aka olc, aka olcConfig. As such there is no slapd.conf to speak of and you'll have to apply ldif's with the help of ldapmodify to configure the OpenLDAP server further.

The resulting slapd.d directory can be used to start an OpenLDAP server from scratch. The following sections assume that you've run slapddgen and explain some of the things in the generated configuration.

cn=config.ldif

The following settings are defaults related to Alpine:

olcConfigFile: /etc/openldap/slapd.conf
olcConfigDir: /etc/openldap/slapd.d/
olcArgsFile: /run/openldap/slapd.args
olcPidFile: /run/openldap/slapd.pid

Of note is:

olcPasswordCryptSaltFormat: $6$rounds=50000$%.16s

This uses crypt's SHA512 with 50.000 rounds (key stretching). This relates to the olcPasswordHash setting in olcDatabase={-1}frontend.ldif as well as olcPPolicyHashCleartext in olcOverlay={2}ppolicy.ldif,olcDatabase={2}mdb,cn=config.

cn=module{0}.ldif,cn=config

Configures which modules are loaded. Of note are olcModulePath and the multitude of olcModuleLoad entries, relative to olcModulePath.

olcDatabase={-1}frontend.ldif,cn=config

The frontend database is a special, pseudo, database which contains settings that apply as defaults to all the other olcDatabases.

The two interesting entries are:

olcMonitoring: FALSE
olcPasswordHash: {CRYPT}

olcMontioring disables the monitoring overlay for all databases. It'll be enabled explicitly for olcDatabase={2}mdb.ldif. The other ensures that we always use {CRYPT} as our password hashing mechanism. Which settings are applied to password hashing is defined by olcPasswordCryptSaltFormat.

olcDatabase={0}config.ldif,cn=config

The only thing of interest here are the olcAccess entries, defining the ACLs which allow entities to read/write cn=config.

olcDatabase={1}monitor.ldif,cn=config

Like the previous section, olcAccess is the only interesting bit.

olcDatabase={2}mdb.ldif,cn=config

This one is interesting, as this defines the data storage of our actual entries in the DIT, as well as other things like ACLs.

Of interest:

olcSuffix: dc=example,dc=com
olcRootDN: cn=Manager,dc=example,dc=com
olcRootPW: {CRYPT}super-long-hash
olcMonitoring: TRUE
olcDbMaxSize: 536870912
olcDbDirectory: /var/lib/openldap/openldap-data
olcDbIndex: objectClass eq
olcDbIndex: uid eq,sub
olcDbIndex: uidNumber eq
olcDbIndex: gidNumber eq
olcDbIndex: member eq
olcDbIndex: memberof eq

olcSuffix defines the default suffix for all entries. olcRootDN is the DN of this database "root" user, and can modify anything regardless of ACLs. The olcRootPW is the password of the olcRootDN, generated with slappasswd -h '{CRYPT}' -c '$6$rounds=50000$%.16s'.

olcMonitoring enables monitoring for this database and olcDbDirectory contains the path on disk where this database is stored.

The olcDbIndex entries define the indices OpenLDAP will maintain for us and help speed up search operations.

olcDbMaxSize defines the size of the memory map that will be allocated for the database. It cannot grow past this value at runtime though you can set it to a higher value and then restart. The value is in bytes, our default is half a gibibyte (GiB).

As usual there's also a number of olcAccess entries governing access to the data.

olcOverlay={0}memberof.ldif,olcDatabase={2}mdb,cn=config

Here we store the configuration for the memberof overlay. The memberof overlay adds a virtual attribute memberOf to any entry in the DIT storing what things this DN is a member of.

Imagine you have three groupOfNames that refer to a user in the member attribute. The memberOf attribute for that user will then contain the DNs of these three groupOfNames. Note that memberOf will only contain the immediate/direct memberships, not any inherited ones (it will not recursively expand the member attributes).

The settings of interest are:

olcMemberOfDangling: error
olcMemberOfDanglingError: constraintViolation
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf

olcMemberOfDangling specifies that we'll return an error in case a modification would result in a dangling reference, returning an error of type olcMemberOfDanglingError.

olcMemberOfRefInt specifies that we use the referential integrity overlay (see the next section).

olcMemberOfGroupOC specifies which objectClass we monitor for dangling references. This is usually only useful for gorups and we use groupOfNames for groups. olcMemberOfAD specifies the attribute used to store members of a group in, that's member and olcMemberOfMemberOfAD specifes the attribute in which the relation is stored, memberOf.

olcOverlay={1}refint.ldif,olcDatabase={2}mdb,cn=config

This configures the referential integrity overlay. Referential integrity will take care of updating any references to a DN stored in the specified attributes should that DN get renamed/moved.

Of note:

olcRefintAttribute: owner
olcRefintAttribute: manager
olcRefintAttribute: memberOf
olcRefintAttribute: member
olcRefintAttribute: roleOccupant
olcRefintModifiersName: cn=Manager,dc=example,dc=com

The olcRefintAttributes specify which attributes we want to enforce referential integrity for. This can only every be attributes that store a distinguished name.

olcRefintModifiersName specifes what we'll store in the modifiersName operational attribute for any entry that gets updated by the referential integrity overlay.

olcOverlay={2}ppolicy.ldif,olcDatabase={2}mdb,cn=config

The password policy overlay allows to configure certain minimum requirements for passwords (like those stored in the userPassword attribute).

Of note:

olcPPolicyHashCleartext: TRUE
olcPPolicyDefault: cn=password,ou=policy,dc=example,dc=com
olcPPolicyUseLockout: FALSE

olcPPolicyHashCleartext means that the server will automatically hash any password sent in as clear text using olcPasswordHash.

olcPPolicyDefault defines an entry in the DIT where we store our default password policy on which we can set other settings. The entry should have objectClass: pwdPolicy. Its settings are documented here.

You can create additional/different password policies in the ou=policy part of the tree and attach them to a user by adding the pwdPolicySubentry attribute on them, pointing to a different policy.

olcPPolicyUseLockout instructs the server whether it should return an InvalidCredentials (FALSE) when attempting to bind or an AccountLocked (TRUE) instead. This configuration defaults to FALSE in order to not be able to use this feature to enumerate accounts.

olcOverlay={3}unique.ldif,olcDatabase={2}mdb,cn=config

The unique overlay allows enforcing uniqueness contraints of attributes on a part of the tree (or the whole tree). Though the RDN is always enforced to be unique (so you can't have two people with the same uid for example) other attributes are not, most crucially things like uidNumber, gidNumber or homeDirectory.

You can create constraints by adding an olcUniqueURI entry of the form: ldap:///[base dn]?[attributes...]?scope[?filter].

For example, if you wanted to enforce unique gidNumbers you could do: ldap:///?gidNumber?sub. Unfortunately this will enforce gidNumber constraints across both posixAccount and posixGroup so you won't be able to create a posixAccount with gidNumber set to a posixGroups GID, which is undesirable. Instead do something like this:

olcUniqueURI: ldap:///ou=groups,ou=example,dc=example,dc=com?gidNumber?sub
olcUniqueURI: ldap:///ou=accounts,ou=example,dc=example,dc=com?gidNumber?sub
olcUniqueURI: ldap:///ou=example,dc=example,dc=com?uidNumber?sub

Now the constraint will be enforced independently on these parts of the tree, not as a whole.

cn=schema.ldif,cn=config

Nothing of relevance to see or change here. It just needs to exist. Once the server has been started this entry will be updated automatically with the server's internal object classes and attributes (mostly all the olc* object classes and attributes).

cn=*.ldif,cn=schema,cn=config

Contains the schema files loaded by the server.

Updating this is rather tricky. The "easiest" way is to generate a slapd.conf with the include statements for the schemas and then dump it out using slapdest -f /path/to/slapd.conf -F /path/to/slapd.d.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slapddgen-0.1.1.tar.gz (24.6 kB view hashes)

Uploaded Source

Built Distribution

slapddgen-0.1.1-py3-none-any.whl (8.4 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page