/dev/posts/

Extract the schema from a remote LDAP server

Published:

Updated:

How to extract the schema from a remote LDAP server and use it on a OpenLDAP instance.

Why?

This might be useful as a first step for duplicating a remote LDAP directory with a custom schema or for running your own minimal instance for testing/development.

Table of content

Extracting a schema from a remote LDAP service

Locating the subschema entry

We can discover the distingued name (DN) of the schema used by one LDAP entry through its subschemaSubentry attribute:

ldapsearch ... -o ldif-wrap=no -LLL -s base \
  -b "ou=foo,dc=example,dc=com" "(objectClass=subschema)" 'subschemaSubentry'

In practice, we are discovering the address of the root schema through the subschemaSubentry attribute of the root DSE (DSA-specific Entry):

ldapsearch ... -o ldif-wrap=no -LLL -s base -b "" 'subschemaSubentry'

where the empty DN is the DN of the DSE (DSA-specific Entry)

This gives something such as:

dn:
subschemaSubentry: cn=Subschema

Fetching the subschema entry

No we can fetch the schema using this DN:

ldapsearch ... -o ldif-wrap=no -LLL -s base -b "cn=Subschema" '+' '*'

Which gives something like:

dn: cn=Subschema
structuralObjectClass: subentry
objectClass: top
objectClass: subentry
objectClass: subschema
objectClass: extensibleObject
cn: Subschema
[...]
attributeTypes: ( 2.999.33 NAME 'fooName' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} SINGLE-VALUE )
[...]
objectClasses: ( 2.999.42 NAME 'foo' SUP top AUXILIARY MAY ( fooName $ fooValue ) )

Integrating it into OpenLDAP

In order to integrate this into our own OpenLDAP server, we need to convert the schema to:

Filtering the custom types

The first step is to only select the custom definitions which are not already present in OpenLDAP. We can use something such as:

grep -F " 2.999." ldapschema.ldif > ldapschema2.ldif

assuming all the definitions we are interested in are under the 2.999 OID.

Generating the OpenLDAP schema file

We can now generate the OpenLDAP schema file:

cat ldapschema2.ldif |
    sed "s/^attributeTypes:/attributeType/" |
    sed "s/^objectClasses:/objectClass/" > custom.schema

which yields:

attributeType ( 2.999.33 NAME 'fooName' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} SINGLE-VALUE )
[...]
objectClass ( 2.999.42 NAME 'foo' SUP top AUXILIARY MAY ( fooName $ fooValue ) )

Generating the OLC LDIF

The OLC LDIF can be generated from the schema file using slaptest. Alternatively, it should be possible to generate it using some additional sed:

(
    echo 'dn: cn=custom,cn=schema,cn=config'
    echo 'objectClass: olcSchemaConfig'
    echo 'cn: custom'
    cat ldapschema2.ldif |
        sed "s/^attributeTypes:/olcAttributeTypes:/" |
        sed "s/^objectClasses:/olcObjectClasses:/"
) > custom.schema

Which yields:

dn: cn=custom,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: custom
[...]
olcAttributeTypes: ( 2.999.33 NAME 'fooName' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} SINGLE-VALUE )
[...]
olcObjectClasses: ( 2.999.42 NAME 'foo' SUP top AUXILIARY MAY ( fooName $ fooValue ) )

Running through a Docker/OCI image

We can now run the LDAP service. For example, using the osixia/openldap Docker image (using Docker or Podman):

FROM osixia/openldap:1.3.0
COPY custom.schema /container/service/slapd/assets/config/bootstrap/schema/custom.schema
podman build -t myldap .
podman run -p 127.0.0.1:8389:389 --name ldap docker.io/library/debian

Note

This Docker image only needs the .schema file and automatically generates the .ldif file from the .schema file.

We can now check that everything went OK:

ldapsearch -H ldap://127.0.0.1:8389 -D "cn=admin,dc=example,dc=com" -w "$ldap_password" \
    -o ldif-wrap=no -LLL -s base -b "" "cn=Subschema" '+' '*'

  1. This LDIF file does not use the same schema as the subschema entry. ↩︎