5. Lightweight Directory Access Protocol (LDAP) in UCS#

An LDAP server provides authenticated and controlled access to directory objects over the network. LDAP objects consist of a collection of attributes which conform to so called LDAP schemata. An in depth documentation of LDAP is beyond the scope of this document. Other resources cover this topic exhaustively, for example https://www.zytrax.com/books/ldap/ or the manual pages slapd.conf.5 and slapd.access.5.

At least it should be noted that OpenLDAP offers two fundamentally different tool sets for direct access or modification of LDAP data:

  1. The slap* commands (slapcat, etc.) are very low level, operating directly on the LDAP backend data and should only be used in rare cases, usually with the LDAP server not running.

  2. The ldap* commands (ldapsearch, etc.) on the other hand are the proper way to perform LDAP operations from the command line and their functionality can equivalently be used from all major programming languages.

On top of the raw LDAP layer, the Univention Directory Manager offers an object model on a higher level, featuring advanced object semantics (see Univention Directory Manager (UDM)). That level is usually appropriate for app developers, which should be considered before venturing down to the level of direct LDAP operations. On the other hand, for the development of new UDM extensions it is important to understand some of the essential concepts of LDAP as used in UCS.

One essential trait of LDAP as used in UCS, is the strict enforcement of LDAP schemata. An LDAP server refuses to start if an unknown LDAP attribute is referenced either in the configuration or in the backend data. This makes it critically important to install schemata on all systems. To simplify this task UCS features a built-in mechanism for automatic schema replication to all UCS hosted LDAP servers in the UCS domain (see Univention Directory Listener). The schema replication mechanism is triggered by installation of a new schema extension package on the Primary Directory Node. For redundancy it is strongly recommended to install schema extension packages also on each Backup Directory Node. This way, a Backup Directory Node can replace a Primary Directory Node in case the Primary Directory Node needs to be replaced for some reason. To simplify these tasks even further, UCS offers methods to register new LDAP schemata and associated LDAP ACLs from any UCS system.

5.1. Packaging LDAP Schema Extensions#

For some purposes, for example for app installation, it is convenient to be able to register a new LDAP schema extension from any system in the UCS domain. For this purpose, the schema extension can be stored as a special type of UDM object. The module responsible for this type of objects is called settings/ldapschema. As these objects are replicated throughout the UCS domain, the Primary Directory Node and Backup Directory Node systems listen for modifications of these objects and integrate them into their local LDAP schema directory. As noted above, this simplifies the task of keeping the schema on the Backup Directory Node systems up to date with those on the Primary Directory Node.

The commands to create the LDAP schema extension objects in UDM may be put into any join script (see Domain join). A LDAP schema extension object is created by using the UDM command line interface univention-directory-manager or its alias udm. LDAP schema extension objects can be stored anywhere in the LDAP directory, but the recommended location would be cn=ldapschema,cn=univention, below the LDAP base. Since the join script creating the attribute may be called on multiple hosts, it is a good idea to add the --ignore_exists option, which suppresses the error exit code in case the object already exists in LDAP.

The UDM module settings/ldapschema requires several parameters:

name (required)

Name of the schema extension.

data (required)

The actual schema data in bzip2 and base64 encoded format.

filename (required)

The file name the schema should be written to on Primary Directory Node and Backup Directory Node. The filename must not contain any path elements.

package (required)

Name of the Debian package which creates the object.

packageversion (required)

Version of the Debian package which creates the object. For object modifications the version number needs to increase unless the package name is modified as well.

appidentifier (optional)

The identifier of the app which creates the object. This is important to indicate that the object is required as long as the app is installed anywhere in the UCS domain. Defaults to string.

active (internal)

A boolean flag used internally by the Primary Directory Node to signal availability of the schema extension (default: FALSE).

Since many of these parameters are determined automatically by the ucs_registerLDAPExtension shell library function, it is recommended to use the shell library function to create these objects (see join.sh).

Listing 5.1 Schema registration in join script#
export UNIVENTION_APP_IDENTIFIER="appID-appVersion" ## example
. /usr/share/univention-lib/ldap.sh

ucs_registerLDAPExtension "$@" \
  --schema /path/to/appschemaextension.schema

5.2. Packaging LDAP ACL Extensions#

For some purposes, for example for app installation, it is convenient to be able to register a new LDAP ACL extension from any system in the UCS domain. For this purpose, the UCR template for an ACL extension can be stored as a special type of UDM object. The module responsible for this type of objects is called settings/ldapacl. As these objects are replicated throughout the UCS domain, the Primary Directory Node, Backup Directory Node and Replica Directory Node systems listen for modifications on these objects and integrate them into the local LDAP ACL UCR template directory. This simplifies the task of keeping the LDAP ACLs on the Backup Directory Node systems up to date with those on the Primary Directory Node.

The commands to create the LDAP ACL extension objects in UDM may be put into any join script (see Domain join). A LDAP ACL extension object is created by using the UDM command line interface univention-directory-manager or its alias udm. LDAP ACL extension objects can be stored anywhere in the LDAP directory, but the recommended location would be cn=ldapacl,cn=univention, below the LDAP base. Since the join script creating the attribute may be called on multiple hosts, it is a good idea to add the --ignore_exists option, which suppresses the error exit code in case the object already exists in LDAP.

The UDM module settings/ldapacl requires several parameters:

name (required)

Name of the ACL extension.

data (required)

The actual ACL UCR template data in bzip2 and base64 encoded format.

filename (required)

The filename the ACL UCR template data should be written to on Primary Directory Node, Backup Directory Node and Replica Directory Node. The filename must not contain any path elements.

package (required)

Name of the Debian package which creates the object.

packageversion (required)

Version of the Debian package which creates the object. For object modifications the version number needs to increase unless the package name is modified as well.

appidentifier (optional)

The identifier of the app which creates the object. This is important to indicate that the object is required as long as the app is installed anywhere in the UCS domain. Defaults to string.

ucsversionstart (optional)

Minimal required UCS version. The UCR template for the ACL is only activated by systems with a version higher than or equal to this.

ucsversionend (optional)

Maximal required UCS version. The UCR template for the ACL is only activated by systems with a version lower or equal than this. To specify validity for the whole 4.1-x release range a value like 4.1-99 may be used.

active (internal)

A boolean flag used internally by the Primary Directory Node to signal availability of the ACL extension on the Primary Directory Node (default: FALSE).

Since many of these parameters are determined automatically by the ucs_registerLDAPExtension shell library function, it is recommended to use the shell library function to create these objects (see join.sh).

Listing 5.2 LDAP ACL registration in join script#
export UNIVENTION_APP_IDENTIFIER="appID-appVersion" ## example
. /usr/share/univention-lib/ldap.sh

ucs_registerLDAPExtension "$@" \
  --acl /path/to/appaclextension.acl

5.3. LDAP secrets#

The credentials for different UCS domain accounts are stored in plain-text files on some UCS systems. The files are named /etc/*.secret. They are owned by the user root and allow read-access for different groups.

/etc/ldap.secret for cn=admin,$ldap_base

This account has full write access to all LDAP entries. The file is only available on Primary Directory Node and Backup Directory Node systems and is owned by the group DC Backup Hosts.

/etc/machine.secret for $ldap_hostdn

Each host uses its account to get at least read-access to LDAP. Directory Nodes, for example Domain controllers, in the container cn=dc,cn=computers,$ldap_base get additional rights to access LDAP attributes. The file is available on all joined system roles and is readable only by the local root user and group.

During package installation, only the maintainer scripts (see debian/preinst, debian/prerm, debian/postinst, debian/postrm) on Primary Directory Node and Backup Directory Node can use their root permission to directly read /etc/ldap.secret. Thus only on those roles, the join scripts get automatically executed when the package is installed. On all other system roles, the join scripts need to be executed manually. This can either be done through the UMC Join module or through the command line tool univention-run-join-scripts. Both methods require appropriate credentials.

5.3.1. Password change#

To reconfirm the trust relation between UCS systems, computers need to regularly change the password associated with the machine account. This is controlled through the Univention Configuration Registry Variable server/password/change. For UCS servers this is evaluated by the script /usr/lib/univention-server/server_password_change, which is invoked nightly at 01:00 by cron.8. The interval is controlled through a second Univention Configuration Registry Variable server/password/interval, which defaults to 21 days.

The password is stored in the plain text file /etc/machine.secret. Many long running services read these credentials only on startup, which breaks when the password is changed while they are still running. Therefore, UCS provides a mechanism to invoke arbitrary commands, when the machine password is changed. This can be used for example to restart specific services.

Hook scripts should be placed in the directory /usr/lib/univention-server/server_password_change.d/. The name must consist of only digits, upper and lower ASCII characters, hyphens and underscores. The file must be executable and is called through run-parts.8. It receives one argument, which is used to distinguish three phases:

  1. Each script will be called with the argument prechange before the password is changed. If any script terminates with an exit status unequal zero, the change is aborted.

  2. A new password is generated locally using makepasswd.1. It is changed in the Univention directory service through UDM and stored in /etc/machine.secret. The old password is logged in /etc/machine.secret.old.

    If anything goes wrong in this step, the change is aborted and the changes need to be rolled back.

  3. All hook scripts are called again.

    • If the password change was successful, postchange gets passed to the hook scripts. This should complete any change prepared in the prechange phase.

    • If the password change failed for any reason, all hook scripts are called with the argument nochange. This should undo any action already done in the prechange phase.

Install this file to /usr/lib/univention-server/server_password_change.d/.

Listing 5.3 Server password change example#
#!/bin/sh
case "$1" in
prechange)
    # nothing to do before the password is changed
    exit 0
    ;;
nochange)
    # nothing to do after a failed password change
    exit 0
    ;;
postchange)
    # restart daemon after password was changed
    deb-systemd-invoke restart my-daemon
    ;;
esac

init-scripts should only be invoked indirectly through deb-systemd-invoke.1p. This is required for chroot environments and allows the policy layer to control starting and stopping in certain special situations like during an system upgrade.