Filesystem placeholder

Presentation

Lemonldap::NG::Common::Conf::Placeholder::Filesystem is a configuration placeholder module that reads secrets in local files. The LemonLDAP::NG configuration then holds only a reference, for example %FILE:managerPassword% instead of the LDAP password, and the value is read in the file managerPassword when the configuration is loaded, in each LemonLDAP::NG process.

It suits the platforms that deliver secrets as files: Docker secrets (/run/secrets), Kubernetes secrets mounted as a volume, systemd credentials ($CREDENTIALS_DIRECTORY), or files deployed by a configuration management tool. It replaces the former “Overlay configuration backend”.

Resolution is fail-closed: if a file can not be read, the whole configuration is rejected. Running handlers and portals keep the configuration they already loaded.

Configuration

Edit the [configuration] section of lemonldap-ng.ini:

[configuration]

configPlaceHolderModules = FILE=Lemonldap::NG::Common::Conf::Placeholder::Filesystem
filesystemPlaceholderDirectory = /run/secrets

FILE is only the prefix used in this document: choose the one you like, it is the name used in the %PREFIX:...% placeholders.

filesystemPlaceholderDirectory is the directory holding the secrets. When it is not set, $CREDENTIALS_DIRECTORY is used: a systemd service declaring LoadCredential= or SetCredential= needs no option.

Placeholder syntax

%FILE:name%

name is the path of the file, relative to the directory: password or ldap/password. Each segment of the path must start with a letter, a digit or _, and may then only contain letters, digits, _, . and -: .., absolute paths and hidden files are refused, so the configuration can not designate a file outside of the directory.

The value is the content of the file, UTF-8 encoded, without its leading and trailing spaces and newlines. A multi-line content, a private key for example, is kept as is otherwise. An empty file is an error.

A parameter declared as a hash (globalStorageOptions, macros, …) can not receive a placeholder as a whole: put one in each of its values, for example %FILE:sessionsDbPassword% for the Password key of globalStorageOptions.

Files are read again at each configuration load: a secret rotated in place is used once the configuration is reloaded (see Configuration overview).

Examples

Docker secrets are mounted in /run/secrets:

# docker-compose.yml
services:
  lemonldap-ng:
    secrets:
      - managerPassword
secrets:
  managerPassword:
    file: ./secrets/managerPassword

With systemd, a credential loaded by the service is found in $CREDENTIALS_DIRECTORY, so filesystemPlaceholderDirectory can be omitted:

# drop-in file of the service running LemonLDAP::NG
[Service]
LoadCredential=managerPassword:/etc/lemonldap-ng/secrets/managerPassword

Security

  • make the directory and its files readable only by the user running LemonLDAP::NG (mode 0400 or 0440);

  • symbolic links found in the directory are followed: this is how Kubernetes rotates the secrets of a volume. The directory belongs to the administrator of the server, not to the one of the LemonLDAP::NG configuration;

  • the Manager reads and writes the configuration in raw mode: it displays %FILE:managerPassword%, never the secret;

  • the content of a file never appears in error messages, which only give its path.