Property Value Substitution

Property value substitution enables you to achieve the following:

  • Define a configuration that is specific to a single instance.

    For example, set the location of the keystore on a particular host.

  • Define a configuration whose parameters vary between different environments.

    For example, change hostnames for test, development, and production environments.

Disable certain capabilities on specific servers. For example, disable a database backend and its replication agreement for one set of replicas while enabling it on another set of replicas. This makes it possible to use the same configuration for environments with different data sets.

Property value substitution uses configuration expressions to introduce variables into the server configuration. You set configuration expressions as the values of configuration properties. The effective property values can be evaluated in a number of ways.

Scope of Configuration Expressions

DS servers only resolve configuration expressions in the config/config.ldif file on LDAP attributes whose names start with ds-cfg-*. These correspond to configuration properties listed in this reference.

DS servers do not resolve configuration expressions anywhere else.

DS servers resolve expressions at startup to determine the configuration. DS commands that read the configuration in offline mode also resolve expressions at startup. When you use expressions in the configuration, you must make their values available before starting the server and also when running such commands.

Configuration expressions share their syntax and underlying implementation with other platform software. Configuration expressions have the following characteristics:

  • To distinguish them from static values, expression tokens are preceded by an ampersand and enclosed in braces.

    For example: &{listen.port}. The expression token in the example is listen.port. The . serves as the separator character.

  • You can use a default value in an expression by including it after a vertical bar following the token.

    For example, the following expression sets the default listen port value to 1389: &{listen.port|1389}.

  • A configuration property can include a mix of static values and expressions.

    For example, if hostname is set to directory, &{hostname}.example.com evaluates to directory.example.com.

  • You can define nested properties (that is, a property definition within another property definition).

    For example, if listen.port is set to &{port.prefix}389, and port.prefix is set to 2, &{listen.port} evaluates to 2389.

  • You can read the value of an expression token from a file.

    For example, if the plaintext password is stored in /path/to/password.txt, the following expression resolves to the plaintext password: &{file:/path/to/password.txt}.

    You specify the file either by its absolute path, or by a path relative to the DS instance directory. In other words, if the DS instance directory is /path/to/opendj, then /path/to/opendj/config/keystore and config/keystore reference the same file.

DS servers define the following expression tokens by default. You can use these in expressions without explicitly setting their values beforehand:

ds.instance.dir

The file system directory holding the instance files required to run an instance of a server.

By default, the files are co-located with the product tools, libraries, and configuration files. You can change the location by using the setup --instancePath option.

This evaluates to a directory, such as /path/to/my-instance.

ds.install.dir

The file system directory where the server files are installed.

This evaluates to a directory, such as /path/to/opendj.

Expression Evaluation and Order of Precedence

You must define expression values before starting the DS server that uses them. When evaluated, an expression must return the appropriate type for the configuration property. For example, the listen-port property takes an integer. If you set it using an expression, the result of the evaluated expression must be an integer. If the type is wrong, the server fails to start due to a syntax error.

If the expression cannot be resolved, and there is no default value in the configuration expression, DS also fails to start.

Expression resolvers evaluate expression tokens to literal values.

Expression resolvers get values from the following sources:

Environment Variables

You set an environment variable to hold the value. For example: export LISTEN_PORT=1389.

The environment variable name must be composed of uppercase characters and underscores. The name maps to the expression token as follows:

  • Uppercase characters are lower cased.

  • Underscores, _, are replaced with . characters.

In other words, the value of LISTEN_PORT replaces &{listen.port} in the server configuration.

Java System Properties

You set a Java system property to hold the value. Java system property names must match expression tokens exactly. In other words, the value of the listen.port system property replaces &{listen.port} in the server configuration.

Java system properties can be set in a number of ways. One way of setting system properties for DS servers is to pass them through the OPENDJ_JAVA_ARGS environment variable. For example: export OPENDJ_JAVA_ARGS="-Dlisten.port=1389".

Expressions Files

You set a key in a .json or .properties file to hold the value.

This optional mechanism is set using the DS_ENVCONFIG_DIRS environment variable, or the ds.envconfig.dirs Java system property.

Keys in .properties files must match expression tokens exactly. In other words, the value of the listen.port key replaces &{listen.port} in the server configuration. The following example properties file sets the listen port:

listen.port=1389

JSON expression files can contain nested objects. JSON field names map to expression tokens as follows:

  • The JSON path name matches the expression token.

  • The . character serves as the JSON path separator character.

The following example JSON file sets the listen address and listen port:

{
    "listen": {
      "address": "192.168.0.10",
      "port": "1389"
    }
}

In other words, the value of the listen/port field replaces &{listen.port} in the server configuration.

In order to use expression files, set the environment variable, DS_ENVCONFIG_DIRS, or the Java system property, ds.envconfig.dirs, to a comma-separated list of the directories containing the expression files.

The following constraints apply when using expression files:

  • Although DS browses the directories in the specified order, within a directory DS scans the files in a non-deterministic order.

  • DS reads all files with .json and .properties extensions.

  • DS does not scan subdirectories.

  • Do not define the same configuration token more than once in a file, as you cannot know in advance which value will be used.

  • You cannot define the same configuration token in more than one file in a single directory. If the same token occurs in more than one file in a single directory, an error occurs.

  • If the same token occurs once in several files which are located in different directories, the first value that DS reads is used.

The preceding list reflects the order of precedence:

  • Environment variables override system properties, default token settings, and settings in any expression files.

  • System properties override default token settings, and any settings in expression files.

  • If DS_ENVCONFIG_DIRS or ds.envconfig.dirs is set, then the server uses settings found in expression files.

  • Default token settings (ds.config.dir, ds.instance.dir, ds.install.dir).

For an embedded DS server, it is possible to change the expression resolvers, in the server configuration.

Use Multivalued Expressions

A single expression token can evaluate to multiple property values. Such expressions are useful with multivalued properties.

For example, suppose you choose to set a connection handler’s ssl-cipher-suite property. Instead of listing cipher suites individually, you use an ssl.cipher.suites token that takes multiple values.

The following example commands set the token value in the environment, stop the server, use the expression in the LDAP connection handler configuration while the server is offline, and then start the server again:

$ export SSL_CIPHER_SUITES=\
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
$ stop-ds --quiet
$ dsconfig \
 set-connection-handler-prop \
 --offline \
 --handler-name LDAPS \
 --add ssl-protocol:TLSv1.2 \
 --add ssl-cipher-suite:'&{ssl.cipher.suites}' \
 --no-prompt
$ start-ds --quiet

Multiple values are separated by commas in environment variables, system properties, and properties files. They are formatted as arrays in JSON files.

Use one of the following alternatives to set the value of the ssl.cipher.suites token. In each case, when the server evaluates &{ssl.cipher.suites}, the result is the following property values:

ssl-cipher-suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
ssl-cipher-suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
ssl-cipher-suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
ssl-cipher-suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
ssl-cipher-suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV

Environment Variable

export SSL_CIPHER_SUITES=\
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\
TLS_EMPTY_RENEGOTIATION_INFO_SCSV

System Property

export OPENDJ_JAVA_ARGS="-Dssl.cipher.suites=\
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\
TLS_EMPTY_RENEGOTIATION_INFO_SCSV"

Properties File

ssl.cipher.suites=\
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\
TLS_EMPTY_RENEGOTIATION_INFO_SCSV

JSON File

{
    "ssl.cipher.suites": [
        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
        "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
    ]
}

Alternative JSON file that sets ssl.protocol as well:

{
    "ssl": {
        "protocol": "TLSv1.2",
        "cipher.suites": [
            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
            "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
        ]
    }
}

In order to fully use the settings in this file, you would have to change the example to include the additional expression: --add ssl-protocol:'&{ssl.protocol}'.

Debug Expressions

You can debug configuration expressions. Create a debug target for org.forgerock.config.resolvers. The following example demonstrates the process:

$ dsconfig \
 create-debug-target \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword password \
 --publisher-name "File-Based Debug Logger" \
 --type generic \
 --target-name org.forgerock.config.resolvers \
 --set enabled:true \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt
$ dsconfig \
 set-log-publisher-prop \
 --hostname localhost \
 --port 4444 \
 --bindDN uid=admin \
 --bindPassword password \
 --publisher-name "File-Based Debug Logger" \
 --set enabled:true \
 --usePkcs12TrustStore /path/to/opendj/config/keystore \
 --trustStorePassword:file /path/to/opendj/config/keystore.pin \
 --no-prompt
$ stop-ds --restart --quiet

When the server starts, it logs debugging messages for configuration expressions. Do not leave debug logging enabled in production systems.