diff --git a/bin/openmetadata-server-start.sh b/bin/openmetadata-server-start.sh index ac337c864341..fe62dde1c313 100644 --- a/bin/openmetadata-server-start.sh +++ b/bin/openmetadata-server-start.sh @@ -76,6 +76,30 @@ else JAVA="$JAVA_HOME/bin/java" fi +# The JVM echoes JAVA_TOOL_OPTIONS to stderr at startup ("Picked up JAVA_TOOL_OPTIONS: ..."), which +# puts any secret set there, such as a truststore password, into the container log, and plain +# arguments would show in ps instead. So pass the options in a -XX:VMOptionsFile: the JVM parses it +# exactly like JAVA_TOOL_OPTIONS, quotes and '#' included, and does not echo it. The file is opened +# on fd 3 and unlinked at once, tmpfs first, so ps only ever shows /dev/fd/3, even after musl's +# launcher re-exec. It goes ahead of our options so ours still take precedence. +JAVA_TOOL_ARGS="" +if [ -n "$JAVA_TOOL_OPTIONS" ]; then + java_tool_options_file="" + for tmp_dir in /dev/shm "${TMPDIR:-/tmp}"; do + java_tool_options_file=$(mktemp "$tmp_dir/jvm-options.XXXXXX" 2>/dev/null) && break + done + if [ -n "$java_tool_options_file" ] && + printf '%s\n' "$JAVA_TOOL_OPTIONS" > "$java_tool_options_file"; then + exec 3< "$java_tool_options_file" + JAVA_TOOL_ARGS="-XX:VMOptionsFile=/dev/fd/3" + unset JAVA_TOOL_OPTIONS + else + echo "Warning: could not write JAVA_TOOL_OPTIONS to /dev/shm or ${TMPDIR:-/tmp}," \ + "so the JVM will print it at startup" >&2 + fi + [ -z "$java_tool_options_file" ] || rm -f "$java_tool_options_file" +fi + # Set Debug options if enabled if [ "x$OPENMETADATA_DEBUG" != "x" ]; then @@ -126,7 +150,7 @@ if [ "x$DAEMON_MODE" = "xtrue" ]; then # emits what logback already writes to the rotated logs/openmetadata.log. Silence the console # so this file only collects JVM and pre-logback output. CONSOLE_LOG_LEVEL=TRACE restores it. export CONSOLE_LOG_LEVEL="${CONSOLE_LOG_LEVEL:-OFF}" - nohup $JAVA $OPENMETADATA_HEAP_OPTS $OPENMETADATA_JVM_PERFORMANCE_OPTS $OPENMETADATA_GC_LOG_OPTS -cp $CLASSPATH $OPENMETADATA_OPTS "$APP_CLASS" "server" "$@" > "$CONSOLE_OUTPUT_FILE" 2>&1 < /dev/null & + nohup $JAVA $JAVA_TOOL_ARGS $OPENMETADATA_HEAP_OPTS $OPENMETADATA_JVM_PERFORMANCE_OPTS $OPENMETADATA_GC_LOG_OPTS -cp $CLASSPATH $OPENMETADATA_OPTS "$APP_CLASS" "server" "$@" > "$CONSOLE_OUTPUT_FILE" 2>&1 < /dev/null & else - exec $JAVA $OPENMETADATA_HEAP_OPTS $OPENMETADATA_JVM_PERFORMANCE_OPTS $OPENMETADATA_GC_LOG_OPTS -cp $CLASSPATH $OPENMETADATA_OPTS "$APP_CLASS" "server" "$@" + exec $JAVA $JAVA_TOOL_ARGS $OPENMETADATA_HEAP_OPTS $OPENMETADATA_JVM_PERFORMANCE_OPTS $OPENMETADATA_GC_LOG_OPTS -cp $CLASSPATH $OPENMETADATA_OPTS "$APP_CLASS" "server" "$@" fi diff --git a/bin/openmetadata.sh b/bin/openmetadata.sh index 4553fb10a73b..8449a26c4827 100644 --- a/bin/openmetadata.sh +++ b/bin/openmetadata.sh @@ -36,7 +36,7 @@ function catalogStart { # Export CONSOLE_LOG_LEVEL=TRACE before starting to get the old behaviour back. export CONSOLE_LOG_LEVEL="${CONSOLE_LOG_LEVEL:-OFF}" cd ${CATALOG_HOME} - nohup ${JAVA} ${CATALOG_HEAP_OPTS} ${CATALOG_JVM_PERF_OPTS} ${CATALOG_DEBUG_OPTS} ${CATALOG_GC_LOG_OPTS} ${CATALOG_JMX_OPTS} -cp ${CLASSPATH} "${APP_CLASS}" "server" "$@" 2>>"${ERR_FILE}" 1>>"${OUT_FILE}" & + nohup ${JAVA} ${JAVA_TOOL_ARGS} ${CATALOG_HEAP_OPTS} ${CATALOG_JVM_PERF_OPTS} ${CATALOG_DEBUG_OPTS} ${CATALOG_GC_LOG_OPTS} ${CATALOG_JMX_OPTS} -cp ${CLASSPATH} "${APP_CLASS}" "server" "$@" 2>>"${ERR_FILE}" 1>>"${OUT_FILE}" & cd - &> /dev/null echo $! > ${PID_FILE} echo $(catalogStatus) @@ -151,6 +151,26 @@ else JAVA="${JAVA_HOME}/bin/java" fi +# As in bin/openmetadata-server-start.sh: pass JAVA_TOOL_OPTIONS in a -XX:VMOptionsFile on fd 3, +# so neither the JVM's startup echo nor ps shows it. +JAVA_TOOL_ARGS="" +if [ -n "$JAVA_TOOL_OPTIONS" ]; then + java_tool_options_file="" + for tmp_dir in /dev/shm "${TMPDIR:-/tmp}"; do + java_tool_options_file=$(mktemp "$tmp_dir/jvm-options.XXXXXX" 2>/dev/null) && break + done + if [ -n "$java_tool_options_file" ] && + printf '%s\n' "$JAVA_TOOL_OPTIONS" > "$java_tool_options_file"; then + exec 3< "$java_tool_options_file" + JAVA_TOOL_ARGS="-XX:VMOptionsFile=/dev/fd/3" + unset JAVA_TOOL_OPTIONS + else + echo "Warning: could not write JAVA_TOOL_OPTIONS to /dev/shm or ${TMPDIR:-/tmp}," \ + "so the JVM will print it at startup" >&2 + fi + [ -z "$java_tool_options_file" ] || rm -f "$java_tool_options_file" +fi + if [[ "x${CATALOG_HEAP_OPTS}" = "x" ]]; then export CATALOG_HEAP_OPTS="-Xmx2G -Xms2G" fi diff --git a/bootstrap/openmetadata-ops.sh b/bootstrap/openmetadata-ops.sh index b2d3e2db1f32..a159c6ddc3a7 100755 --- a/bootstrap/openmetadata-ops.sh +++ b/bootstrap/openmetadata-ops.sh @@ -53,6 +53,26 @@ else CLASSPATH=`mvn -pl openmetadata-service -q exec:exec -Dexec.executable=echo -Dexec.args="%classpath"` fi -${JAVA} -Dbootstrap.dir=$BOOTSTRAP_DIR -cp ${CLASSPATH} ${OPENMETADATA_SETUP_MAIN_CLASS} -c $CONFIG_FILE_PATH "$@" +# As in bin/openmetadata-server-start.sh: pass JAVA_TOOL_OPTIONS in a -XX:VMOptionsFile on fd 3, +# so neither the JVM's startup echo nor ps shows it. +JAVA_TOOL_ARGS="" +if [ -n "$JAVA_TOOL_OPTIONS" ]; then + java_tool_options_file="" + for tmp_dir in /dev/shm "${TMPDIR:-/tmp}"; do + java_tool_options_file=$(mktemp "$tmp_dir/jvm-options.XXXXXX" 2>/dev/null) && break + done + if [ -n "$java_tool_options_file" ] && + printf '%s\n' "$JAVA_TOOL_OPTIONS" > "$java_tool_options_file"; then + exec 3< "$java_tool_options_file" + JAVA_TOOL_ARGS="-XX:VMOptionsFile=/dev/fd/3" + unset JAVA_TOOL_OPTIONS + else + echo "Warning: could not write JAVA_TOOL_OPTIONS to /dev/shm or ${TMPDIR:-/tmp}," \ + "so the JVM will print it at startup" >&2 + fi + [ -z "$java_tool_options_file" ] || rm -f "$java_tool_options_file" +fi + +${JAVA} ${JAVA_TOOL_ARGS} -Dbootstrap.dir=$BOOTSTRAP_DIR -cp ${CLASSPATH} ${OPENMETADATA_SETUP_MAIN_CLASS} -c $CONFIG_FILE_PATH "$@" exit $? diff --git a/scripts/docker_smoke_test.sh b/scripts/docker_smoke_test.sh index f3230406627d..038c6e3c2754 100755 --- a/scripts/docker_smoke_test.sh +++ b/scripts/docker_smoke_test.sh @@ -207,6 +207,40 @@ case "$pid1" in esac docker rm -f "$CONTAINER" >/dev/null 2>&1 || true +# The JVM echoes JAVA_TOOL_OPTIONS to stderr before any logging starts ("Picked up +# JAVA_TOOL_OPTIONS: ..."), so a truststore password set there landed in the pod +# log. bin/openmetadata-server-start.sh hands the options to the JVM in a +# -XX:VMOptionsFile instead. The value, '#' included, must stay out of the log and +# off the JVM's command line, which every local user can read, and the options +# file must not be left behind. -XX:+PrintCommandLineFlags shows the options still +# reach the JVM: it prints the flags once the arguments are parsed. +docker run -d --name "$CONTAINER" \ + -e JAVA_TOOL_OPTIONS="-XX:+PrintCommandLineFlags -Dsmoke.secret=not-for-the-log#1" \ + "$IMAGE" >/dev/null +for _ in 1 2 3 4 5 6 7 8 9 10; do + case "$(docker logs "$CONTAINER" 2>&1 || true)" in *PrintCommandLineFlags*) break ;; esac + sleep 1 +done +sleep 1 +startup="$(docker logs "$CONTAINER" 2>&1 || true)" +cmdline="$(docker exec "$CONTAINER" /bin/sh -c 'tr "\0" " " < /proc/1/cmdline' 2>/dev/null || true)" +leftover="$(docker exec "$CONTAINER" /bin/sh -c 'ls /dev/shm /tmp 2>/dev/null' | + grep -c '^jvm-options\.' || true)" +if echo "$startup" | grep -q "not-for-the-log"; then + fail "JAVA_TOOL_OPTIONS reached the container log: $(echo "$startup" | grep -m1 "not-for-the-log")" +elif echo "$cmdline" | grep -q "not-for-the-log"; then + fail "JAVA_TOOL_OPTIONS is on the JVM's command line, where ps shows it: $cmdline" +elif ! echo "$startup" | grep -q -- "-XX:+PrintCommandLineFlags"; then + fail "JAVA_TOOL_OPTIONS never reached the JVM: $(echo "$startup" | tail -3 | tr '\n' ' ')" +elif [ -z "$cmdline" ]; then + fail "could not read /proc/1 to check the JVM's command line before the container exited" +elif [ "${leftover:-0}" -ne 0 ]; then + fail "the JAVA_TOOL_OPTIONS file was left behind in /dev/shm or /tmp" +else + pass "JAVA_TOOL_OPTIONS reaches the JVM without showing in its log or its command line" +fi +docker rm -f "$CONTAINER" >/dev/null 2>&1 || true + echo if [ "$failures" -ne 0 ]; then echo "==> $failures assertion(s) failed for $IMAGE"