Ping Identity Directory Server 9.1.0.0

We have just released version 9.1.0.0 of the Ping Identity Directory Server and related products, including Directory Proxy Server, Synchronization Server, and Metrics Engine. See the release notes for a complete overview of changes, but here’s my summary:

Known Issues

  • When updating an 8.3 or 9.0 server to the 9.1 release, the Bouncy Castle library that the server uses for certain cryptographic processing will be updated. If it later becomes necessary to revert the update and return to the older version, the newer version of the Bouncy Castle libraries will remain in place. Although this will not cause any actual problems with the server, it may cause the server to log a message during startup about multiple versions of the library in the classpath.

Summary of New Features and Enhancements

  • Updated the Directory REST API to add support for a variety of request and response controls. [more information]
  • Added the ability to streamline the process for replacing a listener certificate if the new certificate is signed by the same issuer as the current certificate. [more information]
  • Updated the replace-certificate tool to make it possible to replace the server’s listener certificate even after it has expired. [more information]
  • Added support for sanitizing access log messages as they are logged. [more information]
  • Added support for generifying message strings in access and error log messages. [more information]
  • Updated the Synchronization Server to improve support for synchronizing changes to and from PingOne, including synchronizing using custom attributes, including multi-valued attributes and JSON-formatted attributes.
  • Improved the result that the server returns when using assured replication if it detects that the operation would ultimately be reverted as a result of a replication conflict.
  • Updated the sanitize-log tool so that its sanitization processing better aligns with the server’s new support for sanitized access logging. [more information]
  • Updated the summarize-access-log tool to support JSON-formatted access log files.
  • Added support for JSON-formatted controls in LDAP requests and responses. [more information]
  • Added a docker-pre-start-config command-line tool that can be used to speed up the startup process when running in a Docker container.
  • Updated manage-profile replace-profile to add a --skipValidation argument that can be used to skip the final validation process to reduce the time required for the update to complete.
  • Updated manage-profile generate-profile to add an --excludeSetupArguments argument that can generate a profile without the setup-arguments.txt file.
  • Increased the maximum value of the on-replay-failure-wait-for-dependent-ops-timeout property in the replication domain configuration from one minute to five minutes.
  • Improved the way that the Directory REST API processes PUT operations that alter an entry’s DN in conjunction with changes to other attributes in the entry.
  • Updated the active operation monitor provider to use millisecond precision in operation timestamps and to enable parsing the string representations of the operations using the LDAP SDK’s access log API.
  • Updated the status --fullVersion output to include the version of the collect-support-data tool.
  • Updated several libraries shipped with the server to improve functionality, address defects, and improve security.

Summary of Bug Fixes

  • Fixed an issue that could cause certain replication protocol messages to be dropped.
  • Fixed an issue that could cause a server to report missing changes and go into lockdown mode if it was restarted immediately after completing dsreplication initialize processing.
  • Fixed an issue that could prevent certain password policy functionality (including account status notification handlers) from being applied to an add operation in which an alternative policy should have been assigned using a virtual attribute rather than a real attribute.
  • Fixed an issue that could cause privileges assigned by virtual attribute to be overlooked in some cases (for example, when accessing topology-related functionality in the Admin Console).
  • Updated the server to create the esTokenizer.ping file if it does not exist but is needed. This file would not have been automatically created when upgrading a server with data encryption enabled from a pre-7.0 version to a later release with support for encrypted indexes.
  • Fixed an issue that could have incorrectly applied minimum and maximum password age constraints to users without a password.
  • Updated the JSON-formatted access logger to include the requester IP address field in disconnect, security negotiation, and client certificate log messages when appropriate.
  • Fixed an issue that prevented the server from refreshing monitor data used to detect and warn about upcoming certificate expiration. This could cause the server to continue to warn about an expiring certificate even after that certificate had been replaced. [more information]
  • Fixed issues that could prevent using the Amazon Secrets Manager, CyberArk Conjur, or HashiCorp Vault passphrase providers to obtain key and trust store PINs.
  • Fixed an issue that could cause the server to report a negative processing time in the access log for certain types of operations.
  • Updated the server to prevent inappropriate updates to the ds-pwp-modifiable-state-json operational attribute when the Modifiable Password Policy State plugin is not enabled.
  • Updated the server to prevent a user from updating their own password policy state using the ds-pwp-modifiable-state-json operational attribute.
  • Updated the server to prevent using the ds-pwp-modifiable-state-json attribute to alter a user’s password policy state in the same operation that also reset that user’s password.
  • Fixed an issue in which the dsreplication tool failed to properly normalize base DN values.
  • Fixed an issue that could prevent the Directory REST API from retrieving entries containing an attribute using the generalized time syntax whose value did not match an expected format.
  • Fixed an issue that could cause manage-profile replace-profile to fail with an error about merging configuration.
  • Updated manage-profile setup and manage-profile replace-profile to ensure that a pre-populated encryption settings database can only be provided in the post-setup files rather than in the pre-setup files, which fixes issues when using a customized cipher stream provider.
  • Updated the manage-topology add-server command to be more consistent when adding additional Synchronization Servers into a failover topology.
  • Fixed an issue in which the server could ignore certain indexes that it incorrectly believed to be redundant when evaluating search criteria.
  • Fixed an issue in which a SCIM request could return a less-appropriate error code in cases where an update violated a unique attribute constraint.
  • Fixed an issue that could cause the server to incorrectly reject a request containing a non-critical control that the requester was not allowed to use. The server will now process the operation as if that control had not been requested.
  • Fixed an issue that could allow the password policy state extended operation to create duplicate authentication failure time or grace login use time values in a user’s entry.
  • Fixed an issue that could adversely affect backward compatibility when attempting to use the legacy --useSSL or --useStartTLS arguments with the migrate-ldap-schema tool (as opposed to the newer arguments allowing you to independently specify security options for the source and target servers).
  • Fixed an issue that could prevent the server from generating an administrative alert to indicate that an outstanding alarm condition had been resolved.
  • Fixed an issue that could cause the server to report an internal error when attempting to obtain database statistics for a read-only backend.
  • Fixed an issue in the export-reversible-passwords tool that could cause it to report a timeout error when waiting for a response from the server.
  • Updated the export-reversible-passwords tool to automatically cancel an in-progress export if the tool used to invoke the export is terminated.
  • Fixed an issue that prevented the encode-password tool from working properly if the AES256 password storage scheme is enabled.
  • Updated the server to disable the index cursor entry limit by default, which is very unlikely to be needed and may interfere with the ability to effectively process certain requests.

Controls in the Directory REST API

It is now possible to use certain LDAP request and response controls through the Directory REST API. Controls should be provided as an array of JSON objects, and the following fields may be used in the JSON representation of the control:

  • oid — A mandatory string field that holds the object identifier for the control.
  • control-name — An optional string field that holds a user-friendly name for the control. Note that this is only intended for informational purposes and won’t be used in the course of actually decoding the control.
  • criticality — A mandatory Boolean field that indicates whether the control should be considered critical.
  • value-base64 — An optional string field whose values is the base64-encoded representation of the raw LDAP encoding for the value. This may be used for any type of control, including controls that the server supports but doesn’t offer a JSON-specific value encoding.
  • value-json — An optional JSON object field whose value contains zero or more fields that are specific to the type of control being represented.

At most one of the value-base64 and value-json fields may be present, and both may be omitted if the control doesn’t take a value.

For example, the following is a JSON representation of the Ping-proprietary join request control:

{ "oid":"1.3.6.1.4.1.30221.2.5.9",
  "control-name":"Join Request Control",
  "criticality":false,
  "value-json":{ "join-rule":{ "type":"dn",
                               "attribute":"manager" },
                 "base-dn-type":"use-custom-base-dn",
                 "base-dn-value":"ou=People,dc=example,dc=com",
                 "scope":"wholeSubtree",
                 "size-limit":10,
                 "attributes":[ "givenName",
                                "sn",
                                "cn",
                                "mail" ],
                 "require-match":false } }

For controls that take a value, the specific JSON encoding for that value varies from one control to another, and you should consult the documentation for a given control to understand how to properly encode and parse it.

Controls that may now be used in the Directory REST API include:

Improvements in Certificate Management

We have updated the replace-certificate tool and the corresponding support in the topology registry to add new options to make things easier when periodically replacing certificates, and especially listener certificates used for security communication with clients.

First, we have added a few new subcommands to the tool, including:

  • list-topology-registry-listener-certificates — Displays a list of the listener certificates associated with a specified instance in the topology registry.
  • list-topology-registry-inter-server-certificates — Displays a list of the inter-server certificates associated with a specific instance in the topology registry.
  • add-topology-registry-listener-certificate — Adds a new certificate to the set of listener certificates for a specified instance in the topology registry, but does not alter any certificate trust stores.

Next, we have updated the server’s use of topology registry listener certificates so that a server’s listener certificate may be trusted for inter-server communication if either the listener certificate itself or one of its issuers is found in the topology registry. Previously, the listener certificate itself was required to be present in the topology registry for that certificate to be trusted, but we have now added support for trusting issuer certificates. This makes it easier to an existing certificate with a new one signed by the same issuer because you can just update the key store without the need to update the topology registry, and there is less chance that a temporary communication problem between instances could arise if a server starts using a new listener certificate before the topology registry updates have propagated to all other instances in the topology.

If you decide to use this issuer-based trust, then there are two ways that you can get the issuer certificate into the topology registry:

  • Using the new replace-certificate add-topology-registry-listener-certificate command referenced above.
  • Using the replace-certificate replace-listener-certificate command with the new --topology-registry-update-type and --trust-store-update-type arguments, which make it possible to configure which certificate(s) in the trust chain will be added to the topology registry and the local trust store, respectively.

We also addressed an oversight in the replace-certificate replace-listener-certificate command that prevented it from being used to replace a listener certificate that had already expired, as the replace certificate tool would no longer trust that expired certificate and would therefore not establish a connection to the server to make the necessary updates. While we strongly recommend always replacing certificates before they actually expire, you can now use the --ignore-current-listener-certificate-validity-window argument to indicate that processing should proceed even if the presented listener certificate is outside its validity time frame. The listener certificate or one of its issuers must still be found in the topology registry.

Finally, we fixed an issue that prevented certificate information presented in the server’s monitor data from being updated. Since there is a gauge in place that uses this monitor information to warn about an upcoming certificate expiration, this bug prevented it from detecting when the certificate had been replaced, and the server would continue to warn about the upcoming expiration even after that certificate was no longer in use. That issue has been fixed, and certificate rotation should now be automatically detected.

Sanitizing Access Log Messages

The server’s access and error logs provide an invaluable resource for understanding how the server is used and for troubleshooting problems. However, they may also sometimes contain sensitive or personally identifiable information that you don’t want to have lying around on the filesystem. In the past, we have offered a sanitize-log tool that can be used to examine log files and produce a scrubbed copy that redacts or obscures potentially sensitive information, and that tool is still available (and improved). However, we have also added the ability to sanitize log messages as they are being written to help ensure that this information isn’t recorded in the logs in the first place.

For access logging, the new sanitization processing is based largely around log field syntaxes. Each log field is now associated with one of the following syntaxes:

  • String
  • Boolean
  • DN
  • Filter
  • Integer
  • Floating-point number
  • Generalized time
  • RFC 3339 timestamp
  • JSON object
  • Comma-delimited string list

We also offer a variety of sanitization types that may be used when determining how to handle a given log field, including:

  • preserve — The field will be included as-is, without any alteration or obfuscation.
  • omit — The field will be omitted from the log message entirely, without either the field name or its value present.
  • redact-entire-value — The field name will be included in the log message, but the entire value will be redacted such that all values for a given syntax will be the same. We actually try to preserve the original syntax when doing this when possible, using the following behavior:

    • String, Boolean, and string list fields will have their values replaced with the text “{REDACTED}”.
    • DN fields will have their values replaced with the text “redacted={REDACTED}”.
    • Filter fields will have their values replaced with the text “(redacted={REDACTED})”.
    • Integer fields will have their values replaced with the text “-999999999999999999”.
    • Floating-point fields will have their values replaced with the text “-999999.999999”.
    • Generalized time fields will have their values replaced with the text “99990101000000.000Z”.
    • RFC 3339 timestamp fields will have their values replaced with the text “9999-01-01T00:00:00.000Z”.
    • JSON object fields will have their values replaced with the text “{ "redacted":"{REDACTED}" }”.
  • redact-value-components — This behavior is similar to redact-entire-value, but it applies specifically to DN, filter, and JSON object values. In such cases, the redaction will be only applied to components within the value, rather than to the entire value itself. For DNs and filters, this means that only attribute values will be redacted while the rest of the text (including attribute names and punctuation) will be preserved, and for JSON objects it means that only field values will be redacted (and field names and punctuation will be preserved). For example, a DN of “uid=jdoe,ou=People,dc=example,dc=com” would become “uid={REDACTED},ou={REDACTED},dc={REDACTED},dc={REDACTED}”. You can optionally even configure this on a per-attribute or per-JSON-field basis, so that only certain attributes or fields (or all but a specified set of attributes or fields) will have their values redacted, and other attributes or fields will have their values preserved.
  • tokenize-entire-value — The field name will be included in the log message, but the value will be replaced with a token that is generated from the value but cannot be directly reversed back to its original value. This is similar to redaction, but different values will result in different tokens, and the same value will consistently result in the same token. This means that it’s possible to identify cases in which the same value appears across multiple log messages, even if you can’t tell what that value actually is. As with redaction, we attempt to preserve the original field syntax when possible, as follows:

    • String, Boolean, and string list fields will have their values tokenized with the text “{TOKENIZED:tokenValue}”, where tokenValue will be generated from the original value.
    • DN fields will have their values replaced with “tokenized={TOKENIZED:tokenValue}”.
    • Filter fields will have their values replaced with “(tokenized={TOKENIZED:tokenValue})”.
    • Integer fields will have their values replaced with a number that starts with “-999999999” and is followed by a sequence of nine additional digits generated from the original value.
    • Floating-point fields will have their values replaced with a number that starts with “-999999.” and is followed by a sequence of six additional digits generated from the original value.
    • Generalized time and RFC 3339 timestamp values will have their values replaced with a timestamp that uses the year 8888, but with other components of the timestamp generated from the original timestamp.
    • JSON object fields will have their values replaced with the text “{ "tokenized":"{TOKENIZED:tokenValue}" }”.
  • tokenize-value-components — This behavior is similar to redact-value-components, but the individual components will be tokenized rather than redacted. As with redact-value-components, this primarily applies to DNs, filters, and JSON objects.

The server now provides a log field syntax configuration object for each of the aforementioned syntaxes, and that object will include a default-behavior field that allows you to indicate that all values with that syntax should be treated in a given way. For example, if you configure the DN log field syntax with a default-behavior value of tokenize-value-components, then all DNs written to a text-formatted or JSON-formatted access log will automatically have their attribute values tokenized while the rest of the DN will remain intact.

For even more control over how individual log fields are treated, you can use log field behavior configuration objects. These objects allow you to choose the behavior to use on a field-by-field basis, and you can configure different behaviors for different loggers if desired.

If you’d rather still have access log messages written with all of the information intact, but still have the ability to sanitize those logs after the fact, then you can continue to use the sanitize-log tool to accomplish this, and we have updated this tool to provide improved behavior that is more closely aligned with the server’s support for sanitized logging. This includes:

  • It now knows about all predefined log fields that the server may use, and we have preselected default behaviors for each of those fields in a manner that we feel provides the best balance between privacy and usefulness.
  • It now uses the same syntax-aware redaction and tokenization logic that the server uses so that field values are more likely to conform to their original syntax.
  • You can customize the behavior that the tool exhibits on a per-syntax basis, or you can point it at a log field behavior configuration object.

Generified Log Message Strings

Sanitized logging and the sanitize-log tool provide very powerful mechanisms for removing or obscuring sensitive or identifiable information in log messages, but there are some access log fields that may have such a wide range of values that it’s not necessarily feasible to treat them in a “one size fits all” kind of way, especially while retaining the utility of that field. These kinds of fields include:

  • The diagnostic message that is returned to the client in an LDAP response.
  • An additional info message that is meant to be recorded in the access log for an operation but not returned to the client.
  • An authentication failure reason that is meant to explain why an authentication attempt failed.
  • A disconnect reason that is meant to provide the reason that a client connection was closed.

It’s possible that the values of these fields could occasionally include sensitive or identifiable information, but their content can also be very helpful in troubleshooting problems or understanding the server’s behavior in specific situations. As such, you would lose substantial benefit by configuring those fields to be redacted.

Further, while a field-based approach works well for sanitizing access log messages for the most part, it doesn’t really make any sense for error log messages, as the core content of an error log message appears in the same field for all messages.

To address these concerns, we have introduced the ability to log generified values of these strings, using the generify-message-strings-when-possible property in the configuration for each logger. If this property is set to true, then the server will log the generic format string for the associated message rather than the processed message that it would normally include.

For example, if a simple bind attempt fails because the target user doesn’t exist, the access log message for that operation might include an authentication failure reason like:

Unable to bind to the Directory Server as user uid=jdoe,ou=People,dc=example,dc=com because no such user exists in the server.

However, if the logger is configured to report the generic version of the message, then it would instead log:

Unable to bind to the Directory Server as user %s because no such user exists in the server.

The generic version of the message doesn’t specifically identify the user, thereby offering better privacy than the un-generified version, but at least it’s still useful enough to understand the reason that the authentication attempt failed.

JSON-Formatted LDAP Request and Response Controls

The Ping Identity Directory Server provides support for a very wide range of request and response controls. This includes not only standard controls defined in RFCs and Internet Drafts, but also many controls that we’ve defined ourselves for enhanced functionality. These controls can be easily accessed through the UnboundID LDAP SDK for Java, but they aren’t as readily available for use in applications written with other LDAP APIs, especially in languages that don’t run on the JVM. Even though the LDAP SDK documentation describes the format of the encoded values for these controls, generating or decoding those values may still be a substantial challenge, typically requiring a library for working with ASN.1 BER and knowing how to use it properly.

To help address that, we’ve introduced the ability to encode LDAP request controls as JSON objects, and to indicate that LDAP response controls should also be encoded as JSON objects. JSON is much easier to work with than ASN.1 BER across a variety of programming languages, and it doesn’t require as much expertise to understand how to properly generate or parse JSON objects.

The primary interface to this functionality is through the JSON-formatted request control, which has an OID of “1.3.6.1.4.1.30221.2.5.64” and a value that is the string representation of a JSON object that encapsulates the JSON representations of the request controls to send to the server (or you can omit the value if you don’t have any request controls to send but want all response controls to be returned using a JSON encoding). If the request includes a JSON-formatted request control, and if there are any response controls to be returned, then the server will return them in a JSON-formatted response control, which has an OID of “1.3.6.1.4.1.30221.2.5.65” and a value that uses the same format as the request control. Note that while you don’t need to use the UnboundID LDAP SDK for Java to generate or parse these controls, its Javadoc documentation (especially the documentation for the toJSONControl method for the controls in question) may be useful in understanding what fields may be used in these objects.

The value of a JSON-formatted request or response control is simply a JSON object that has a single controls field whose value is an array of the JSON objects that make up the individual request or response controls. For example, the following JSON object represents the encoded value for a JSON-formatted request control that embeds both a retain identity request control and a get authorization entry request control:

{ "controls":[
  { "oid":"1.3.6.1.4.1.30221.2.5.3",
    "control-name":"Retain Identity Request Control",
    "criticality":true },
  { "oid":"1.3.6.1.4.1.30221.2.5.6",
    "control-name":"Get Authorization Entry Request Control",
    "criticality":false,
    "value-json":{
      "include-authentication-entry":true,
      "include-authorization-entry":true,
      "attributes":[ "uid", "givenName", "sn", "cn", "mail" ] } } ] }

For controls that are supported in both LDAP and the Directory REST API, the JSON encoding that we use is the same in either case. However, the Directory Server supports a number of additional controls for LDAP requests and responses that it doesn’t currently support through the Directory REST API. In additional to all of the controls we currently support in the Directory REST API, we provide JSON encodings for each of the following additional controls in LDAP requests:

  • Account usable request and response controls
  • Administrative operation request control
  • Authorization identity request and response controls
  • Extended schema info request control
  • Get authorization entry request and response controls
  • Get backend set ID request and response controls
  • Get password policy state issues request and response controls
  • Get recent login history request and response controls
  • Get server ID request and response controls
  • Get user resource limits request and response controls
  • Hard delete request control
  • Override search limits request control
  • Password policy request and response controls
  • Replication repair request control
  • Retain identity request control
  • Return conflict entries request control
  • Route to backend set request control
  • Route to server request control
  • Server-side sort request and response controls
  • Simple paged results control
  • Soft delete request control
  • Soft-deleted entry access request control
  • Subentries request control
  • Subtree delete request control
  • Suppress operational attribute update request control
  • Undelete request control
  • Virtual list view request and response controls