Skip to content

Docker image: keystores from SECRET_VOLUME are copied only on the first start, so a renewed certificate never reaches the server #1087

Description

@vharseko

Problem

Keystores and truststores from SECRET_VOLUME (/var/secrets/opendj by default) are copied into ./data/config only on the first start, when the instance is bootstrapped (run.sh#L80-L88). When ./data/config already exists, the script execs the server before reaching that step (L44-L53).

So a renewed certificate in the mounted Secret never reaches a server with a persistent volume. A restart keeps serving the old one until it expires. On Kubernetes, where cert-manager renews the Secret regularly, this means TLS fails once the first certificate expires.

The volume is also hard to use from Kubernetes: only JKS/PKCS12 files named key* / trust* are picked up. A kubernetes.io/tls Secret (cert-manager) holds PEM files (tls.crt, tls.key, ca.crt), which OpenDJ 4.x cannot use directly (it has no PEM key manager).

Expected

  • Every start refreshes the keystore/truststore from SECRET_VOLUME, before the server starts, when the volume is present.
  • Optionally, a tls.crt/tls.key/ca.crt set is converted to PKCS12 at that point, so a cert-manager Secret can be mounted as is. The keystore PIN comes from a file, never from the log.

Notes

Needed for the TLS part of the Helm chart proposal (discussion #1079).

Analysis (2026-09-24)

Measured with openidentityplatform/opendj:latest (5.1.2, JDK 25), a JKS keystore + keystore.pin mounted at /var/secrets/opendj and a named data volume:

step certificate served on LDAPS
first start, secret v1 CN=localhost, O=OpenDJ RSA Self-Signed Certificate
restart CN=secret-v1
secret replaced with v2, restart CN=secret-v1 (the keystore on the volume is still v1)

The first start does not serve the mounted certificate either. The copy (run.sh#L80-L88) runs after setup.sh, which has already started the server. By then the LDAPS connection handler has built its SSLContext from the self-signed keystore, and the script goes on to tail -f (#1085). The mounted certificate is served only after the first restart.

Copying on every start is not enough on Kubernetes. cert-manager renews the Secret, and nothing restarts the pod. A connection handler builds its SSLContext once, when it is initialized or its own configuration changes (LDAPConnectionHandler#configureSSL). After cp of a new keystore into config/, LDAPS keeps the old certificate. Any dsconfig set-connection-handler-prop on the handler makes it serve the new one at once, so reloading works without a restart, but only with admin credentials. The server side is #1095.

Not a problem, checked:

  • The LDAPS handler has no ssl-cert-nickname (only the administration connector, admin-cert, and replication, ads-certificate, have one), so a keystore whose key is under any alias works, e.g. certificate as cert-manager names it.
  • The cn=JKS provider reads a PKCS12 file named keystore.
  • The key* / trust* globs do not match admin-keystore, admin-truststore or ads-truststore. The volume affects LDAPS and StartTLS only.

On the optional PEM part: openssl is in the Ubuntu based image but not in the Alpine one, and keytool cannot import a PEM private key. cert-manager can add a PKCS12 keystore to the Secret it issues (spec.keystores.pkcs12, password from passwordSecretRef), and a projected volume can mount it as keystore, with the password as keystore.pin. That needs no conversion in the image.

Fix:

  1. Image, [#1087] Copy the secret volume on every start of the Docker image, and run the server as PID 1 after the bootstrap #1100: copy the volume before every start; on the first start, stop the server setup started and exec start-ds like every later start (this is also the fix for Docker image: a freshly bootstrapped container ignores SIGTERM and is killed without stopping the server #1085); keep checking the volume while the server runs and copy changed files by rename; document the cert-manager setup.
  2. Server, A renewed key store or trust store file is not used until the server is restarted #1095 / [#1095] Load a file based key store or trust store again when the file changes #1101: file based key and trust manager providers load the file again when it changes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions