mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
5 hours ago 61d6274b3a5f76edbed7c3a7bd2e4c7403d69ab1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#!/usr/bin/env bash
# The contents of this file are subject to the terms of the Common Development and
# Distribution License (the License). You may not use this file except in compliance with the
# License.
#
# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
# specific language governing permission and limitations under the License.
#
# When distributing Covered Software, include this CDDL Header Notice in each file and include
# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
# Header, with the fields enclosed by brackets [] replaced by your own identifying
# information: "Portions copyright [year] [name of copyright owner]".
#
# Portions copyright 2026 3A Systems, LLC.
 
# Run the OpenDJ server
# The idea is to consolidate all of the writable DJ directories to
# a single instance directory root, and update DJ's instance.loc file to point to that root
# This allows us to to mount a data volume on that root which gives us
# persistence across restarts of OpenDJ.
# For Docker - mount a data volume on /opt/opendj/data
# For Kubernetes mount a PV
 
cd /opt/opendj
 
# The health check probes the server only once this marker is there, so that "healthy"
# means the instance is bootstrapped rather than merely listening: setup starts the
# server in the middle of the bootstrap, before the backend holding BASE_DN has been
# created. Nothing below writes it unless the step it stands for reported success, so a
# bootstrap that failed leaves the container running to be looked at, but never healthy.
# It is kept outside ./data because it records what this container has done, not what
# the volume holds - and a restart of a container replays this script over the writable
# layer the previous run left behind, so it is cleared before anything else.
BOOTSTRAP_COMPLETE=${BOOTSTRAP_COMPLETE:-/opt/opendj/.bootstrap-complete}
rm -f "$BOOTSTRAP_COMPLETE"
 
# A replicate.sh or a setup.sh killed before its EXIT trap ran leaves the root password in
# /dev/shm, and on Kubernetes that outlives the container: the pod keeps its /dev/shm across
# container restarts. It is also shared by all the containers of the pod, and another one may
# be bootstrapping right now, so only the files of this container are removed: they carry
# ADMIN_PORT in their name, which the containers of a pod cannot share as they share one
# network namespace. Containers in distinct network namespaces that share /dev/shm through
# --ipc=host, and listen on the same ADMIN_PORT, are not told apart. /tmp belongs to this
# container alone, so a password file left there is removed whatever its name.
rm -f /dev/shm/opendj-replicate."$ADMIN_PORT".*
rm -f /dev/shm/opendj-setup-password."$ADMIN_PORT".* /tmp/opendj-setup-password.*
 
# Keystores and truststores mounted as a volume (a Kubernetes Secret, say) are copied into
# the instance on every start, not only on the one that bootstraps it: the instance lives on
# a persistent volume, and a renewed certificate in the Secret has to reach it.
SECRET_VOLUME=${SECRET_VOLUME:-/var/secrets/opendj}
# Kubernetes updates a mounted Secret in place, so while the server runs the volume is
# checked again every that many seconds; 0 checks it on start only
SECRET_VOLUME_REFRESH=${SECRET_VOLUME_REFRESH:-60}
 
# Copies the key* and trust* files of the secret volume that differ from those in
# ./data/config. Each one is written next to its target and renamed over it, so the server
# never reads a file half copied, and it is readable by the server's user only: a keystore
# holds the private key, a .pin file its password. Succeeds when it copied a file.
copy_secrets() {
  local src dst tmp copied=1
  [ -d "$SECRET_VOLUME" ] || return 1
  for src in "$SECRET_VOLUME"/key* "$SECRET_VOLUME"/trust*; do
    [ -f "$src" ] || continue
    dst=./data/config/$(basename -- "$src")
    cmp -s "$src" "$dst" && continue
    if tmp=$(mktemp "$dst.XXXXXX") && cp "$src" "$tmp" && chmod 600 "$tmp" && mv -f "$tmp" "$dst"; then
      echo "Copied $(basename -- "$src") from the secret volume"
      copied=0
    else
      rm -f "$tmp"
      echo "Could not copy $(basename -- "$src") from the secret volume"
    fi
  done
  return $copied
}
 
# The files are compared one at a time, so a Secret updated in the middle of a pass can leave
# a keystore of one version next to the .pin of the other. Passes are repeated until one finds
# nothing left to copy, which puts the files of a single version back together.
sync_secrets() {
  local passes=0
  while copy_secrets && [ $((passes += 1)) -lt 5 ]; do :; done
}
 
# The server loads a keystore or truststore again, together with its .pin file, on the first
# handshake after either file has changed (#1095), so a store copied while the server runs is
# served without a restart, a new password included - a keystore as long as it keeps an alias of
# the one before it, else from the next start. A handshake that comes between the copy of a store
# and that of its .pin finds a pair it cannot open: the server keeps the store it loaded last,
# logs that it could not load the new one, and loads it once the second file is copied.
watch_secrets() {
  while sleep "$SECRET_VOLUME_REFRESH"; do
    sync_secrets
  done
}
 
# Both kinds of start end here, with the server as PID 1 of the container: that is the
# process the container runtime sends SIGTERM to, and the server stops cleanly on it.
start_server() {
  if [ -d "$SECRET_VOLUME" ]; then
    echo "Secret volume is present. Will copy any keystores and truststore"
    sync_secrets
    if [[ $SECRET_VOLUME_REFRESH =~ ^[0-9]+$ ]] && [ "$SECRET_VOLUME_REFRESH" -gt 0 ]; then
      watch_secrets &
    elif ! [[ $SECRET_VOLUME_REFRESH =~ ^0+$ ]]; then
      echo "SECRET_VOLUME_REFRESH=$SECRET_VOLUME_REFRESH is not a whole number of seconds above 0, the secret volume is copied on start only"
    fi
  fi
  echo "Starting OpenDJ"
  exec ./bin/start-ds --nodetach
}
 
#if default data folder exists do not change it
if [ ! -d ./db ]; then
  echo "/opt/opendj/data" >/opt/opendj/instance.loc && \
  mkdir -p /opt/opendj/data/lib/extensions
fi
 
# Instance dir does exist? We start opendj without detach
if [ -d ./data/config ]; then
  # nothing is bootstrapped here, the instance is already there - but a half-migrated one
  # is not ready to serve either, so the marker follows the upgrade
  if sh ./upgrade -n; then
    touch "$BOOTSTRAP_COMPLETE"
  else
    echo "Upgrade failed, this container will not report itself healthy"
  fi
  start_server
fi
 
# If we are here, opendj is not installed & we need to run setup
echo "Instance data Directory is empty. Creating new DJ instance"
 
export BASE_DN=${BASE_DN:-"dc=example,dc=com"}
echo "BASE DN is ${BASE_DN}"
 
export ROOT_PASSWORD=${ROOT_PASSWORD:-password}
 
BOOTSTRAP=${BOOTSTRAP:-/opt/opendj/bootstrap/setup.sh}
echo "Running $BOOTSTRAP"
BOOTSTRAPPED=true
if ! sh "${BOOTSTRAP}"; then
  BOOTSTRAPPED=false
  echo "$BOOTSTRAP failed, this container will not report itself healthy"
fi
 
# Check if OPENDJ_REPLICATION_TYPE var is set. If it is - replicate to that server
if [ -n "${MASTER_SERVER}" ] && [ -n "${OPENDJ_REPLICATION_TYPE}" ]; then
  if ! /opt/opendj/bootstrap/replicate.sh; then
    BOOTSTRAPPED=false
    echo "Replication setup failed, this container will not report itself healthy"
  fi
fi
 
# Setup has usually left the server running in the background. It is stopped here and
# started again below with exec, so that the server is PID 1 on the first start just as
# on a restart: a shell as PID 1 without a SIGTERM handler never receives the signal, and
# the container would then be killed at the end of the stop timeout instead of stopping
# the server. Left running, it would also keep the certificate it was set up with rather
# than the one on the secret volume, which start_server copies in before it starts the
# server. It is stopped before the marker below is written, so that the health check
# never reports the server of the bootstrap healthy just before it goes down. stop-ds
# exits 0 when the server is not running. When it fails the server may still be stopping,
# so the start below is tried anyway: it either runs the server or fails on the lock of
# the one still there.
echo "Stopping the server started by the bootstrap"
if ! ./bin/stop-ds; then
  echo "Could not stop the server started by the bootstrap, starting OpenDJ may fail"
fi
 
# Everything the instance was asked to be set up with - its backend, its base entry, its
# replication - is in place from here on, so the health check may start probing the server
if [ "$BOOTSTRAPPED" = true ]; then
  touch "$BOOTSTRAP_COMPLETE"
  echo "The instance is bootstrapped, the health check may probe it"
fi
 
start_server