| | |
| | | done |
| | | docker exec test_replica test -e /dev/shm/opendj-replicate.5444.other || { echo "::error::run.sh of test_replica removed the password file of another container"; false; } |
| | | cleanup |
| | | - name: Docker test secret volume |
| | | # a keystore mounted at SECRET_VOLUME is what LDAPS serves from the first start on, a |
| | | # renewed one - a new password included - is copied while the server runs and served |
| | | # without a restart, and the server is PID 1 of the container, stopping on SIGTERM |
| | | # (#1087, #1085); a start copies what was renewed while no container ran, over the |
| | | # instance already there, and SECRET_VOLUME_REFRESH=0 runs no watcher |
| | | shell: bash |
| | | env: |
| | | IMAGE: localhost:5000/${{ env.image_repository }}:${{ env.release_version }} |
| | | run: | |
| | | set -E |
| | | trap 'code=$?; echo "::group::container logs (test_secret)"; docker logs test_secret 2>&1 || true; echo "::endgroup::"; exit $code' ERR |
| | | SECRETS=$(mktemp -d) |
| | | chmod 777 "$SECRETS" |
| | | # a key under an alias other than setup's own, since setup binds no connection handler |
| | | # to an alias, and a truststore holding its certificate; the alias is kept by a |
| | | # renewal the server is to load while it runs, which takes a keystore only when it |
| | | # holds a key under an alias the one before it did |
| | | keystore() { |
| | | local pass=${2:-changeit} alias=${3:-$1} |
| | | docker run --rm --entrypoint keytool -v "$SECRETS":/secrets "$IMAGE" -genkeypair -alias "$alias" \ |
| | | -keyalg RSA -keysize 2048 -validity 30 -dname "CN=$1" -storetype PKCS12 \ |
| | | -keystore /secrets/keystore.new -storepass "$pass" -keypass "$pass" |
| | | docker run --rm --entrypoint keytool -v "$SECRETS":/secrets "$IMAGE" -exportcert -rfc -alias "$alias" \ |
| | | -keystore /secrets/keystore.new -storepass "$pass" -file /secrets/cert.pem |
| | | rm -f "$SECRETS/truststore.new" |
| | | docker run --rm --entrypoint keytool -v "$SECRETS":/secrets "$IMAGE" -importcert -noprompt -alias "$1" \ |
| | | -file /secrets/cert.pem -keystore /secrets/truststore.new -storetype JKS -storepass changeit |
| | | rm -f "$SECRETS/cert.pem" |
| | | printf %s "$pass" > "$SECRETS/keystore.pin" |
| | | printf changeit > "$SECRETS/truststore.pin" |
| | | mv -f "$SECRETS/truststore.new" "$SECRETS/truststore" |
| | | mv -f "$SECRETS/keystore.new" "$SECRETS/keystore" |
| | | } |
| | | served() { echo | openssl s_client -connect 127.0.0.1:1637 2>/dev/null | openssl x509 -noout -subject -nameopt RFC2253; } |
| | | healthy() { timeout 3m bash -c 'until docker inspect --format="{{json .State.Health.Status}}" test_secret | grep -q \"healthy\"; do sleep 10; done'; } |
| | | copies() { docker logs test_secret 2>&1 | grep -c "^Copied $1 from the secret volume$" || true; } |
| | | keystore secret-v1 |
| | | docker run -d --memory="512m" -e SECRET_VOLUME_REFRESH=5 -p 127.0.0.1:1637:1636 --name=test_secret \ |
| | | -v "$SECRETS":/var/secrets/opendj:ro -v test_secret_data:/opt/opendj/data "$IMAGE" |
| | | healthy |
| | | grep -q "CN=secret-v1" <<< "$(served)" |
| | | test "$(docker exec test_secret cat /proc/1/comm)" = java |
| | | test "$(docker exec test_secret stat -c %a /opt/opendj/data/config/keystore.pin)" = 600 |
| | | docker exec test_secret cmp -s /var/secrets/opendj/truststore /opt/opendj/data/config/truststore |
| | | # the bootstrap's server is stopped before the instance is marked bootstrapped |
| | | test "$(docker logs test_secret 2>&1 | grep -e '^Stopping Server' -e '^The instance is bootstrapped' | paste -sd '|' -)" \ |
| | | = "Stopping Server...|The instance is bootstrapped, the health check may probe it" |
| | | keystore secret-v2 changeit secret-v1 |
| | | timeout 1m bash -c 'until docker exec test_secret cmp -s /var/secrets/opendj/keystore /opt/opendj/data/config/keystore; do sleep 5; done' |
| | | # the server loads the keystore the watcher copied on the next handshake |
| | | grep -q "CN=secret-v2" <<< "$(served)" |
| | | # the watcher has looked at the volume every 5 s since the start, and copied the |
| | | # keystore only when it changed: on the start and once more for v2 |
| | | sleep 6 |
| | | test "$(copies keystore)" = 2 |
| | | # a new password is copied along with its keystore, and the server loads both |
| | | keystore secret-v3 changeit2 secret-v1 |
| | | timeout 1m bash -c 'until docker exec test_secret sh -c "cmp -s /var/secrets/opendj/keystore /opt/opendj/data/config/keystore && cmp -s /var/secrets/opendj/keystore.pin /opt/opendj/data/config/keystore.pin"; do sleep 5; done' |
| | | grep -q "CN=secret-v3" <<< "$(served)" |
| | | test "$(copies keystore.pin)" = 2 |
| | | start=$SECONDS |
| | | docker stop -t 60 test_secret |
| | | test $((SECONDS - start)) -lt 30 |
| | | test "$(docker inspect --format='{{.State.ExitCode}}' test_secret)" = 143 |
| | | docker start test_secret |
| | | healthy |
| | | grep -q "CN=secret-v3" <<< "$(served)" |
| | | docker stop -t 60 test_secret |
| | | docker rm test_secret |
| | | # renewed while no container runs, the keystore, its new password and its new alias |
| | | # are copied by the start, over the instance already there |
| | | keystore secret-v4 changeit3 |
| | | docker run -d --memory="512m" -e SECRET_VOLUME_REFRESH=0 -p 127.0.0.1:1637:1636 --name=test_secret \ |
| | | -v "$SECRETS":/var/secrets/opendj:ro -v test_secret_data:/opt/opendj/data "$IMAGE" |
| | | healthy |
| | | grep -q "CN=secret-v4" <<< "$(served)" |
| | | # no watcher, whose sleep is nearly always there, and nothing copied while the server runs |
| | | docker exec test_secret sh -c '! grep -sqx sleep /proc/[0-9]*/comm' |
| | | keystore secret-v5 changeit3 |
| | | sleep 15 |
| | | docker exec test_secret sh -c '! cmp -s /var/secrets/opendj/keystore /opt/opendj/data/config/keystore' |
| | | docker rm -f test_secret |
| | | docker volume rm test_secret_data |
| | | - name: Docker test bootstrap LDIFs |
| | | shell: bash |
| | | run: | |
| | |
| | | done |
| | | docker exec test_replica test -e /dev/shm/opendj-replicate.5444.other || { echo "::error::run.sh of test_replica removed the password file of another container"; false; } |
| | | cleanup |
| | | - name: Docker test secret volume |
| | | # a keystore mounted at SECRET_VOLUME is what LDAPS serves from the first start on, a |
| | | # renewed one - a new password included - is copied while the server runs and served |
| | | # without a restart, and the server is PID 1 of the container, stopping on SIGTERM |
| | | # (#1087, #1085); a start copies what was renewed while no container ran, over the |
| | | # instance already there, and SECRET_VOLUME_REFRESH=0 runs no watcher |
| | | shell: bash |
| | | env: |
| | | IMAGE: localhost:5000/${{ env.image_repository }}:${{ env.release_version }}-alpine |
| | | run: | |
| | | set -E |
| | | trap 'code=$?; echo "::group::container logs (test_secret)"; docker logs test_secret 2>&1 || true; echo "::endgroup::"; exit $code' ERR |
| | | SECRETS=$(mktemp -d) |
| | | chmod 777 "$SECRETS" |
| | | # a key under an alias other than setup's own, since setup binds no connection handler |
| | | # to an alias, and a truststore holding its certificate; the alias is kept by a |
| | | # renewal the server is to load while it runs, which takes a keystore only when it |
| | | # holds a key under an alias the one before it did |
| | | keystore() { |
| | | local pass=${2:-changeit} alias=${3:-$1} |
| | | docker run --rm --entrypoint keytool -v "$SECRETS":/secrets "$IMAGE" -genkeypair -alias "$alias" \ |
| | | -keyalg RSA -keysize 2048 -validity 30 -dname "CN=$1" -storetype PKCS12 \ |
| | | -keystore /secrets/keystore.new -storepass "$pass" -keypass "$pass" |
| | | docker run --rm --entrypoint keytool -v "$SECRETS":/secrets "$IMAGE" -exportcert -rfc -alias "$alias" \ |
| | | -keystore /secrets/keystore.new -storepass "$pass" -file /secrets/cert.pem |
| | | rm -f "$SECRETS/truststore.new" |
| | | docker run --rm --entrypoint keytool -v "$SECRETS":/secrets "$IMAGE" -importcert -noprompt -alias "$1" \ |
| | | -file /secrets/cert.pem -keystore /secrets/truststore.new -storetype JKS -storepass changeit |
| | | rm -f "$SECRETS/cert.pem" |
| | | printf %s "$pass" > "$SECRETS/keystore.pin" |
| | | printf changeit > "$SECRETS/truststore.pin" |
| | | mv -f "$SECRETS/truststore.new" "$SECRETS/truststore" |
| | | mv -f "$SECRETS/keystore.new" "$SECRETS/keystore" |
| | | } |
| | | served() { echo | openssl s_client -connect 127.0.0.1:1637 2>/dev/null | openssl x509 -noout -subject -nameopt RFC2253; } |
| | | healthy() { timeout 3m bash -c 'until docker inspect --format="{{json .State.Health.Status}}" test_secret | grep -q \"healthy\"; do sleep 10; done'; } |
| | | copies() { docker logs test_secret 2>&1 | grep -c "^Copied $1 from the secret volume$" || true; } |
| | | keystore secret-v1 |
| | | docker run -d --memory="1g" -e SECRET_VOLUME_REFRESH=5 -p 127.0.0.1:1637:1636 --name=test_secret \ |
| | | -v "$SECRETS":/var/secrets/opendj:ro -v test_secret_data:/opt/opendj/data "$IMAGE" |
| | | healthy |
| | | grep -q "CN=secret-v1" <<< "$(served)" |
| | | test "$(docker exec test_secret cat /proc/1/comm)" = java |
| | | test "$(docker exec test_secret stat -c %a /opt/opendj/data/config/keystore.pin)" = 600 |
| | | docker exec test_secret cmp -s /var/secrets/opendj/truststore /opt/opendj/data/config/truststore |
| | | # the bootstrap's server is stopped before the instance is marked bootstrapped |
| | | test "$(docker logs test_secret 2>&1 | grep -e '^Stopping Server' -e '^The instance is bootstrapped' | paste -sd '|' -)" \ |
| | | = "Stopping Server...|The instance is bootstrapped, the health check may probe it" |
| | | keystore secret-v2 changeit secret-v1 |
| | | timeout 1m bash -c 'until docker exec test_secret cmp -s /var/secrets/opendj/keystore /opt/opendj/data/config/keystore; do sleep 5; done' |
| | | # the server loads the keystore the watcher copied on the next handshake |
| | | grep -q "CN=secret-v2" <<< "$(served)" |
| | | # the watcher has looked at the volume every 5 s since the start, and copied the |
| | | # keystore only when it changed: on the start and once more for v2 |
| | | sleep 6 |
| | | test "$(copies keystore)" = 2 |
| | | # a new password is copied along with its keystore, and the server loads both |
| | | keystore secret-v3 changeit2 secret-v1 |
| | | timeout 1m bash -c 'until docker exec test_secret sh -c "cmp -s /var/secrets/opendj/keystore /opt/opendj/data/config/keystore && cmp -s /var/secrets/opendj/keystore.pin /opt/opendj/data/config/keystore.pin"; do sleep 5; done' |
| | | grep -q "CN=secret-v3" <<< "$(served)" |
| | | test "$(copies keystore.pin)" = 2 |
| | | start=$SECONDS |
| | | docker stop -t 60 test_secret |
| | | test $((SECONDS - start)) -lt 30 |
| | | test "$(docker inspect --format='{{.State.ExitCode}}' test_secret)" = 143 |
| | | docker start test_secret |
| | | healthy |
| | | grep -q "CN=secret-v3" <<< "$(served)" |
| | | docker stop -t 60 test_secret |
| | | docker rm test_secret |
| | | # renewed while no container runs, the keystore, its new password and its new alias |
| | | # are copied by the start, over the instance already there |
| | | keystore secret-v4 changeit3 |
| | | docker run -d --memory="1g" -e SECRET_VOLUME_REFRESH=0 -p 127.0.0.1:1637:1636 --name=test_secret \ |
| | | -v "$SECRETS":/var/secrets/opendj:ro -v test_secret_data:/opt/opendj/data "$IMAGE" |
| | | healthy |
| | | grep -q "CN=secret-v4" <<< "$(served)" |
| | | # no watcher, whose sleep is nearly always there, and nothing copied while the server runs |
| | | docker exec test_secret sh -c '! grep -sqx sleep /proc/[0-9]*/comm' |
| | | keystore secret-v5 changeit3 |
| | | sleep 15 |
| | | docker exec test_secret sh -c '! cmp -s /var/secrets/opendj/keystore /opt/opendj/data/config/keystore' |
| | | docker rm -f test_secret |
| | | docker volume rm test_secret_data |
| | | - name: Docker test bootstrap LDIFs |
| | | shell: bash |
| | | run: | |