Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update RabbitMQ to 3.7.10 and 3.7.11-rc.2 #5364

Merged
merged 1 commit into from
Jan 30, 2019

Conversation

tianon
Copy link
Member

@tianon tianon commented Jan 29, 2019

See docker-library/rabbitmq#297 👍 🎉

cc @gerhard @michaelklishin (this is the second half of the image update which gets it to https://hub.docker.com/_/rabbitmq -- see https://github.com/docker-library/official-images for more details on the process/program/flow)

@yosifkit
Copy link
Member

Diff:
diff --git a/_bashbrew-arches b/_bashbrew-arches
index d39cbf2..26774a2 100644
--- a/_bashbrew-arches
+++ b/_bashbrew-arches
@@ -1,29 +1,27 @@
-rabbitmq:3.6 @ amd64
-rabbitmq:3.6 @ arm32v5
-rabbitmq:3.6 @ arm32v7
-rabbitmq:3.6 @ arm64v8
-rabbitmq:3.6 @ i386
-rabbitmq:3.6 @ ppc64le
-rabbitmq:3.6 @ s390x
-rabbitmq:3.6-alpine @ amd64
-rabbitmq:3.6-alpine @ arm32v6
-rabbitmq:3.6-alpine @ arm64v8
-rabbitmq:3.6-alpine @ i386
-rabbitmq:3.6-alpine @ ppc64le
-rabbitmq:3.6-alpine @ s390x
-rabbitmq:3.6-management @ amd64
-rabbitmq:3.6-management @ arm32v5
-rabbitmq:3.6-management @ arm32v7
-rabbitmq:3.6-management @ arm64v8
-rabbitmq:3.6-management @ i386
-rabbitmq:3.6-management @ ppc64le
-rabbitmq:3.6-management @ s390x
-rabbitmq:3.6-management-alpine @ amd64
-rabbitmq:3.6-management-alpine @ arm32v6
-rabbitmq:3.6-management-alpine @ arm64v8
-rabbitmq:3.6-management-alpine @ i386
-rabbitmq:3.6-management-alpine @ ppc64le
-rabbitmq:3.6-management-alpine @ s390x
+rabbitmq:3.7-rc @ amd64
+rabbitmq:3.7-rc @ arm32v7
+rabbitmq:3.7-rc @ arm64v8
+rabbitmq:3.7-rc @ i386
+rabbitmq:3.7-rc @ ppc64le
+rabbitmq:3.7-rc @ s390x
+rabbitmq:3.7-rc-alpine @ amd64
+rabbitmq:3.7-rc-alpine @ arm32v6
+rabbitmq:3.7-rc-alpine @ arm64v8
+rabbitmq:3.7-rc-alpine @ i386
+rabbitmq:3.7-rc-alpine @ ppc64le
+rabbitmq:3.7-rc-alpine @ s390x
+rabbitmq:3.7-rc-management @ amd64
+rabbitmq:3.7-rc-management @ arm32v7
+rabbitmq:3.7-rc-management @ arm64v8
+rabbitmq:3.7-rc-management @ i386
+rabbitmq:3.7-rc-management @ ppc64le
+rabbitmq:3.7-rc-management @ s390x
+rabbitmq:3.7-rc-management-alpine @ amd64
+rabbitmq:3.7-rc-management-alpine @ arm32v6
+rabbitmq:3.7-rc-management-alpine @ arm64v8
+rabbitmq:3.7-rc-management-alpine @ i386
+rabbitmq:3.7-rc-management-alpine @ ppc64le
+rabbitmq:3.7-rc-management-alpine @ s390x
 rabbitmq:alpine @ amd64
 rabbitmq:alpine @ arm32v6
 rabbitmq:alpine @ arm64v8
@@ -31,14 +29,12 @@ rabbitmq:alpine @ i386
 rabbitmq:alpine @ ppc64le
 rabbitmq:alpine @ s390x
 rabbitmq:latest @ amd64
-rabbitmq:latest @ arm32v5
 rabbitmq:latest @ arm32v7
 rabbitmq:latest @ arm64v8
 rabbitmq:latest @ i386
 rabbitmq:latest @ ppc64le
 rabbitmq:latest @ s390x
 rabbitmq:management @ amd64
-rabbitmq:management @ arm32v5
 rabbitmq:management @ arm32v7
 rabbitmq:management @ arm64v8
 rabbitmq:management @ i386
diff --git a/_bashbrew-list b/_bashbrew-list
index 93ce6bb..153f200 100644
--- a/_bashbrew-list
+++ b/_bashbrew-list
@@ -2,22 +2,22 @@ rabbitmq:3
 rabbitmq:3-alpine
 rabbitmq:3-management
 rabbitmq:3-management-alpine
-rabbitmq:3.6
-rabbitmq:3.6-alpine
-rabbitmq:3.6-management
-rabbitmq:3.6-management-alpine
-rabbitmq:3.6.16
-rabbitmq:3.6.16-alpine
-rabbitmq:3.6.16-management
-rabbitmq:3.6.16-management-alpine
 rabbitmq:3.7
 rabbitmq:3.7-alpine
 rabbitmq:3.7-management
 rabbitmq:3.7-management-alpine
-rabbitmq:3.7.8
-rabbitmq:3.7.8-alpine
-rabbitmq:3.7.8-management
-rabbitmq:3.7.8-management-alpine
+rabbitmq:3.7-rc
+rabbitmq:3.7-rc-alpine
+rabbitmq:3.7-rc-management
+rabbitmq:3.7-rc-management-alpine
+rabbitmq:3.7.10
+rabbitmq:3.7.10-alpine
+rabbitmq:3.7.10-management
+rabbitmq:3.7.10-management-alpine
+rabbitmq:3.7.11-rc.2
+rabbitmq:3.7.11-rc.2-alpine
+rabbitmq:3.7.11-rc.2-management
+rabbitmq:3.7.11-rc.2-management-alpine
 rabbitmq:alpine
 rabbitmq:latest
 rabbitmq:management
diff --git a/rabbitmq_3.6-alpine/Dockerfile b/rabbitmq_3.6-alpine/Dockerfile
deleted file mode 100644
index 21c791b..0000000
diff --git a/rabbitmq_3.6/Dockerfile b/rabbitmq_3.6/Dockerfile
deleted file mode 100644
index 930be7e..0000000
diff --git a/rabbitmq_3.7-rc-alpine/Dockerfile b/rabbitmq_3.7-rc-alpine/Dockerfile
new file mode 100644
index 0000000..0fff6f3
--- /dev/null
+++ b/rabbitmq_3.7-rc-alpine/Dockerfile
@@ -0,0 +1,233 @@
+# Alpine Linux is not officially supported by the RabbitMQ team -- use at your own risk!
+FROM alpine:3.8
+
+# grab su-exec for easy step-down from root
+# bash for docker-entrypoint.sh
+RUN apk add --no-cache 'su-exec>=0.2' bash
+
+# Default to a PGP keyserver that pgp-happy-eyeballs recognizes, but allow for substitutions locally
+ARG PGP_KEYSERVER=ha.pool.sks-keyservers.net
+# If you are building this image locally and are getting `gpg: keyserver receive failed: No data` errors,
+# run the build with a different PGP_KEYSERVER, e.g. docker build --tag rabbitmq:3.7 --build-arg PGP_KEYSERVER=pgpkeys.eu 3.7/ubuntu
+# For context, see https://github.com/docker-library/official-images/issues/4252
+
+# Using the latest OpenSSL LTS release, with support until September 2023 - https://www.openssl.org/source/
+ENV OPENSSL_VERSION 1.1.1a
+ENV OPENSSL_SOURCE_SHA256="fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41"
+# https://www.openssl.org/community/omc.html
+ENV OPENSSL_PGP_KEY_ID="0x8657ABB260F056B1E5190839D9C4D26D0E604491"
+
+# Use the latest stable Erlang/OTP release (https://github.com/erlang/otp/tags)
+ENV OTP_VERSION 21.2.3
+# TODO add PGP checking when the feature will be added to Erlang/OTP's build system
+# http://erlang.org/pipermail/erlang-questions/2019-January/097067.html
+ENV OTP_SOURCE_SHA256="109a5722e398bdcd3aeb4f4833cde90bf441a9c014006439643aab550a770923"
+
+# Install dependencies required to build Erlang/OTP from source
+# http://erlang.org/doc/installation_guide/INSTALL.html
+# autoconf: Required to configure Erlang/OTP before compiling
+# dpkg-dev: Required to set up host & build type when compiling Erlang/OTP
+# gnupg: Required to verify OpenSSL artefacts
+# libncurses5-dev: Required for Erlang/OTP new shell & observer_cli - https://github.com/zhongwencool/observer_cli
+# m4: Required for Erlang/OTP HiPE support
+RUN set -eux; \
+	\
+	apk add --no-cache --virtual .build-deps \
+		autoconf \
+		ca-certificates \
+		dpkg-dev dpkg \
+		gcc \
+		gnupg \
+		libc-dev \
+		linux-headers \
+		m4 \
+		make \
+		ncurses-dev \
+		wget \
+	; \
+	\
+	OPENSSL_SOURCE_URL="https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz"; \
+	OPENSSL_PATH="/usr/local/src/openssl-$OPENSSL_VERSION"; \
+	OPENSSL_CONFIG_DIR=/usr/local/etc/ssl; \
+	\
+# /usr/local/src doesn't exist in Alpine by default
+	mkdir /usr/local/src; \
+	\
+# Required by the crypto & ssl Erlang/OTP applications
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz" "$OPENSSL_SOURCE_URL"; \
+	export GNUPGHOME="$(mktemp -d)"; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$OPENSSL_PGP_KEY_ID"; \
+	gpg --batch --verify "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_PATH.tar.gz"; \
+	gpgconf --kill all; \
+	rm -rf "$GNUPGHOME"; \
+	echo "$OPENSSL_SOURCE_SHA256 *$OPENSSL_PATH.tar.gz" | sha256sum -c -; \
+	mkdir -p "$OPENSSL_PATH"; \
+	tar --extract --file "$OPENSSL_PATH.tar.gz" --directory "$OPENSSL_PATH" --strip-components 1; \
+	\
+# Configure OpenSSL for compilation
+	cd "$OPENSSL_PATH"; \
+	./config --openssldir="$OPENSSL_CONFIG_DIR"; \
+# Compile, install OpenSSL, verify that the command-line works & development headers are present
+	make -j "$(getconf _NPROCESSORS_ONLN)"; \
+	make install_sw install_ssldirs; \
+	cd ..; \
+	rm -rf "$OPENSSL_PATH"*; \
+# use Alpine's CA certificates
+	rmdir "$OPENSSL_CONFIG_DIR/certs" "$OPENSSL_CONFIG_DIR/private"; \
+	ln -sf /etc/ssl/certs /etc/ssl/private "$OPENSSL_CONFIG_DIR"; \
+# smoke test
+	openssl version; \
+	\
+	OTP_SOURCE_URL="https://github.com/erlang/otp/archive/OTP-$OTP_VERSION.tar.gz"; \
+	OTP_PATH="/usr/local/src/otp-$OTP_VERSION"; \
+	\
+# Download, verify & extract OTP_SOURCE
+	mkdir -p "$OTP_PATH"; \
+	wget --progress dot:giga --output-document "$OTP_PATH.tar.gz" "$OTP_SOURCE_URL"; \
+	echo "$OTP_SOURCE_SHA256 *$OTP_PATH.tar.gz" | sha256sum -c -; \
+	tar --extract --file "$OTP_PATH.tar.gz" --directory "$OTP_PATH" --strip-components 1; \
+	\
+# Configure Erlang/OTP for compilation, disable unused features & applications
+# http://erlang.org/doc/applications.html
+# ERL_TOP is required for Erlang/OTP makefiles to find the absolute path for the installation
+	cd "$OTP_PATH"; \
+	export ERL_TOP="$OTP_PATH"; \
+	./otp_build autoconf; \
+	CFLAGS="$(dpkg-buildflags --get CFLAGS)"; export CFLAGS; \
+	hostArch="$(dpkg-architecture --query DEB_HOST_GNU_TYPE)"; \
+	buildArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
+	./configure \
+		--host="$hostArch" \
+		--build="$buildArch" \
+		--disable-dynamic-ssl-lib \
+		--disable-sctp \
+		--disable-silent-rules \
+		--enable-clock-gettime \
+		--enable-hipe \
+		--enable-hybrid-heap \
+		--enable-kernel-poll \
+		--enable-shared-zlib \
+		--enable-smp-support \
+		--enable-threads \
+		--with-microstate-accounting=extra \
+		--without-common_test \
+		--without-debugger \
+		--without-dialyzer \
+		--without-diameter \
+		--without-edoc \
+		--without-erl_docgen \
+		--without-erl_interface \
+		--without-et \
+		--without-eunit \
+		--without-ftp \
+		--without-jinterface \
+		--without-megaco \
+		--without-observer \
+		--without-odbc \
+		--without-reltool \
+		--without-ssh \
+		--without-tftp \
+		--without-wx \
+	; \
+# Compile & install Erlang/OTP
+	make -j "$(getconf _NPROCESSORS_ONLN)" GEN_OPT_FLGS="-O2 -fno-strict-aliasing"; \
+	make install; \
+	cd ..; \
+	rm -rf "$OTP_PATH"* /usr/local/lib/erlang/lib/*/src; \
+	\
+	runDeps="$( \
+		scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \
+			| tr ',' '\n' \
+			| sort -u \
+			| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
+	)"; \
+	apk add --no-cache --virtual .otp-run-deps $runDeps; \
+	apk del --no-network .build-deps; \
+	\
+# Check that OpenSSL still works after purging build dependencies
+	openssl version; \
+# Check that Erlang/OTP crypto & ssl were compiled against OpenSSL correctly
+	erl -noshell -eval 'io:format("~p~n~n~p~n~n", [crypto:supports(), ssl:versions()]), init:stop().'
+
+ENV RABBITMQ_DATA_DIR=/var/lib/rabbitmq
+# Create rabbitmq system user & group, fix permissions & allow root user to connect to the RabbitMQ Erlang VM
+RUN set -eux; \
+	addgroup -g 101 -S rabbitmq; \
+	adduser -u 100 -S -h "$RABBITMQ_DATA_DIR" -G rabbitmq rabbitmq; \
+	mkdir -p "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chown -fR rabbitmq:rabbitmq "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chmod 777 "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	ln -sf "$RABBITMQ_DATA_DIR/.erlang.cookie" /root/.erlang.cookie
+
+# Use the latest stable RabbitMQ release (https://www.rabbitmq.com/download.html)
+ENV RABBITMQ_VERSION 3.7.11-rc.2
+# https://www.rabbitmq.com/signatures.html#importing-gpg
+ENV RABBITMQ_PGP_KEY_ID="0x0A9AF2115F4687BD29803A206B73A36E6026DFCA"
+ENV RABBITMQ_HOME=/opt/rabbitmq
+
+# Add RabbitMQ to PATH, send all logs to TTY
+ENV PATH=$RABBITMQ_HOME/sbin:$PATH \
+	RABBITMQ_LOGS=- RABBITMQ_SASL_LOGS=-
+
+# Install RabbitMQ
+RUN set -eux; \
+	\
+	apk add --no-cache --virtual .build-deps \
+		ca-certificates \
+		gnupg \
+		wget \
+		xz \
+	; \
+	\
+	RABBITMQ_SOURCE_URL="https://github.com/rabbitmq/rabbitmq-server/releases/download/v$RABBITMQ_VERSION/rabbitmq-server-generic-unix-$RABBITMQ_VERSION.tar.xz"; \
+	RABBITMQ_PATH="/usr/local/src/rabbitmq-$RABBITMQ_VERSION"; \
+	\
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz" "$RABBITMQ_SOURCE_URL"; \
+	\
+	export GNUPGHOME="$(mktemp -d)"; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$RABBITMQ_PGP_KEY_ID"; \
+	gpg --batch --verify "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_PATH.tar.xz"; \
+	gpgconf --kill all; \
+	rm -rf "$GNUPGHOME"; \
+	\
+	mkdir -p "$RABBITMQ_HOME"; \
+	tar --extract --file "$RABBITMQ_PATH.tar.xz" --directory "$RABBITMQ_HOME" --strip-components 1; \
+	rm -rf "$RABBITMQ_PATH"*; \
+# Do not default SYS_PREFIX to RABBITMQ_HOME, leave it empty
+	grep -qE '^SYS_PREFIX=\$\{RABBITMQ_HOME\}$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	sed -i 's/^SYS_PREFIX=.*$/SYS_PREFIX=/' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	grep -qE '^SYS_PREFIX=$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	chown -R rabbitmq:rabbitmq "$RABBITMQ_HOME"; \
+	\
+	apk del .build-deps; \
+	\
+# verify assumption of no stale cookies
+	[ ! -e "$RABBITMQ_DATA_DIR/.erlang.cookie" ]; \
+# Ensure RabbitMQ was installed correctly by running a few commands that do not depend on a running server, as the rabbitmq user
+# If they all succeed, it's safe to assume that things have been set up correctly
+	su-exec rabbitmq rabbitmqctl help; \
+	su-exec rabbitmq rabbitmqctl list_ciphers; \
+	su-exec rabbitmq rabbitmq-plugins list; \
+# no stale cookies
+	rm "$RABBITMQ_DATA_DIR/.erlang.cookie"
+
+# Added for backwards compatibility - users can simply COPY custom plugins to /plugins
+RUN ln -sf /opt/rabbitmq/plugins /plugins
+
+# set home so that any `--user` knows where to put the erlang cookie
+ENV HOME $RABBITMQ_DATA_DIR
+# Hint that the data (a.k.a. home dir) dir should be separate volume
+VOLUME $RABBITMQ_DATA_DIR
+
+# warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
+# Setting all environment variables that control language preferences, behaviour differs - https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable
+# https://docs.docker.com/samples/library/ubuntu/#locales
+ENV LANG=C.UTF-8 LANGUAGE=C.UTF-8 LC_ALL=C.UTF-8
+
+COPY docker-entrypoint.sh /usr/local/bin/
+ENTRYPOINT ["docker-entrypoint.sh"]
+
+EXPOSE 4369 5671 5672 25672
+CMD ["rabbitmq-server"]
diff --git a/rabbitmq_3.6-alpine/docker-entrypoint.sh b/rabbitmq_3.7-rc-alpine/docker-entrypoint.sh
similarity index 69%
rename from rabbitmq_3.6-alpine/docker-entrypoint.sh
rename to rabbitmq_3.7-rc-alpine/docker-entrypoint.sh
index 28c401c..aea0160 100755
--- a/rabbitmq_3.6-alpine/docker-entrypoint.sh
+++ b/rabbitmq_3.7-rc-alpine/docker-entrypoint.sh
@@ -198,87 +198,95 @@ if [ "${RABBITMQ_ERLANG_COOKIE:-}" ]; then
 	chmod 600 "$cookieFile"
 fi
 
-# prints "$2$1$3$1...$N"
-join() {
-	local sep="$1"; shift
-	local out; printf -v out "${sep//%/%%}%s" "$@"
-	echo "${out#$sep}"
-}
-indent() {
-	if [ "$#" -gt 0 ]; then
-		echo "$@"
-	else
-		cat
-	fi | sed 's/^/\t/g'
+configBase="${RABBITMQ_CONFIG_FILE:-/etc/rabbitmq/rabbitmq}"
+oldConfigFile="$configBase.config"
+newConfigFile="$configBase.conf"
+
+shouldWriteConfig="$haveConfig"
+if [ -n "$shouldWriteConfig" ] && [ -f "$oldConfigFile" ]; then
+	{
+		echo "error: Docker configuration environment variables specified, but old-style (Erlang syntax) configuration file '$oldConfigFile' exists"
+		echo "  Suggested fixes: (choose one)"
+		echo "   - remove '$oldConfigFile'"
+		echo "   - remove any Docker-specific 'RABBITMQ_...' environment variables"
+		echo "   - convert '$oldConfigFile' to the newer sysctl format ('$newConfigFile'); see https://www.rabbitmq.com/configure.html#config-file"
+	} >&2
+	exit 1
+fi
+if [ -z "$shouldWriteConfig" ] && [ ! -f "$oldConfigFile" ] && [ ! -f "$newConfigFile" ]; then
+	# no config files, we should write one
+	shouldWriteConfig=1
+fi
+
+# http://stackoverflow.com/a/2705678/433558
+sed_escape_lhs() {
+	echo "$@" | sed -e 's/[]\/$*.^|[]/\\&/g'
 }
-rabbit_array() {
-	echo -n '['
-	case "$#" in
-		0) echo -n ' ' ;;
-		1) echo -n " $1 " ;;
-		*)
-			local vals="$(join $',\n' "$@")"
-			echo
-			indent "$vals"
-	esac
-	echo -n ']'
+sed_escape_rhs() {
+	echo "$@" | sed -e 's/[\/&]/\\&/g'
 }
-rabbit_string() {
+rabbit_set_config() {
+	local key="$1"; shift
 	local val="$1"; shift
-	# fire up erlang directly to have it do the proper escaping for us
-	erl -noinput -eval 'io:format("~p\n", init:get_plain_arguments()), init:stop().' -- "$val"
+
+	[ -e "$newConfigFile" ] || touch "$newConfigFile"
+
+	local sedKey="$(sed_escape_lhs "$key")"
+	local sedVal="$(sed_escape_rhs "$val")"
+	sed -ri \
+		"s/^[[:space:]]*(${sedKey}[[:space:]]*=[[:space:]]*)\S.*\$/\1${sedVal}/" \
+		"$newConfigFile"
+	if ! grep -qE "^${sedKey}[[:space:]]*=" "$newConfigFile"; then
+		echo "$key = $val" >> "$newConfigFile"
+	fi
+}
+rabbit_comment_config() {
+	local key="$1"; shift
+
+	[ -e "$newConfigFile" ] || touch "$newConfigFile"
+
+	local sedKey="$(sed_escape_lhs "$key")"
+	sed -ri \
+		"s/^[[:space:]]*#?[[:space:]]*(${sedKey}[[:space:]]*=[[:space:]]*\S.*)\$/# \1/" \
+		"$newConfigFile"
 }
 rabbit_env_config() {
 	local prefix="$1"; shift
 
-	local ret=()
 	local conf
 	for conf; do
 		local var="rabbitmq${prefix:+_$prefix}_$conf"
 		var="${var^^}"
 
-		local val="${!var:-}"
+		local key="$conf"
+		case "$prefix" in
+			ssl) key="ssl_options.$key" ;;
+			management_ssl) key="management.listener.ssl_opts.$key" ;;
+		esac
 
-		local rawVal=
+		local val="${!var:-}"
+		local rawVal="$val"
 		case "$conf" in
-			verify|fail_if_no_peer_cert|depth)
-				[ "$val" ] || continue
-				rawVal="$val"
-				;;
-
-			hipe_compile)
-				[ "$val" ] && rawVal='true' || rawVal='false'
-				;;
-
-			cacertfile|certfile|keyfile)
-				[ "$val" ] || continue
-				rawVal="$(rabbit_string "$val")"
+			fail_if_no_peer_cert|hipe_compile)
+				case "${val,,}" in
+					false|no|0|'') rawVal='false' ;;
+					true|yes|1|*) rawVal='true' ;;
+				esac
 				;;
 
-			*)
-				[ "$val" ] || continue
-				rawVal="<<$(rabbit_string "$val")>>"
-				;;
+			vm_memory_high_watermark) continue ;; # handled separately
 		esac
-		[ "$rawVal" ] || continue
 
-		ret+=( "{ $conf, $rawVal }" )
+		if [ -n "$rawVal" ]; then
+			rabbit_set_config "$key" "$rawVal"
+		else
+			rabbit_comment_config "$key"
+		fi
 	done
-
-	join $'\n' "${ret[@]}"
 }
 
-shouldWriteConfig="$haveConfig"
-if [ ! -f /etc/rabbitmq/rabbitmq.config ]; then
-	shouldWriteConfig=1
-fi
-
 if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
-	fullConfig=()
-
-	rabbitConfig=(
-		"{ loopback_users, $(rabbit_array) }"
-	)
+	rabbit_set_config 'loopback_users.guest' 'false'
 
 	# determine whether to set "vm_memory_high_watermark" (based on cgroups)
 	memTotalKb=
@@ -300,12 +308,13 @@ if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
 	if [ -n "$memLimitB" ]; then
 		# if we have a cgroup memory limit, let's inform RabbitMQ of what it is (so it can calculate vm_memory_high_watermark properly)
 		# https://github.com/rabbitmq/rabbitmq-server/pull/1234
-		rabbitConfig+=( "{ total_memory_available_override_value, $memLimitB }" )
+		rabbit_set_config 'total_memory_available_override_value' "$memLimitB"
 	fi
+	# https://www.rabbitmq.com/memory.html#memsup-usage
 	if [ "${RABBITMQ_VM_MEMORY_HIGH_WATERMARK:-}" ]; then
 		# https://github.com/docker-library/rabbitmq/pull/105#issuecomment-242165822
 		vmMemoryHighWatermark="$(
-			awk '
+			echo "$RABBITMQ_VM_MEMORY_HIGH_WATERMARK" | awk '
 				/^[0-9]*[.][0-9]+$|^[0-9]+([.][0-9]+)?%$/ {
 					perc = $0;
 					if (perc ~ /%$/) {
@@ -316,93 +325,64 @@ if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
 						printf "error: invalid percentage for vm_memory_high_watermark: %s (must be > 0%%, <= 100%%)\n", $0 > "/dev/stderr";
 						exit 1;
 					}
-					printf "%0.03f\n", perc;
+					printf "vm_memory_high_watermark.relative %0.03f\n", perc;
 					next;
 				}
 				/^[0-9]+$/ {
-					printf "{ absolute, %s }\n", $0;
+					printf "vm_memory_high_watermark.absolute %s\n", $0;
 					next;
 				}
 				/^[0-9]+([.][0-9]+)?[a-zA-Z]+$/ {
-					printf "{ absolute, \"%s\" }\n", $0;
+					printf "vm_memory_high_watermark.absolute %s\n", $0;
 					next;
 				}
 				{
 					printf "error: unexpected input for vm_memory_high_watermark: %s\n", $0;
 					exit 1;
 				}
-			' <(echo "$RABBITMQ_VM_MEMORY_HIGH_WATERMARK")
+			'
 		)"
 		if [ "$vmMemoryHighWatermark" ]; then
-			# https://www.rabbitmq.com/memory.html#memsup-usage
-			rabbitConfig+=( "{ vm_memory_high_watermark, $vmMemoryHighWatermark }" )
+			vmMemoryHighWatermarkKey="${vmMemoryHighWatermark%% *}"
+			vmMemoryHighWatermarkVal="${vmMemoryHighWatermark#$vmMemoryHighWatermarkKey }"
+			rabbit_set_config "$vmMemoryHighWatermarkKey" "$vmMemoryHighWatermarkVal"
+			case "$vmMemoryHighWatermarkKey" in
+				# make sure we only set one or the other
+				'vm_memory_high_watermark.absolute') rabbit_comment_config 'vm_memory_high_watermark.relative' ;;
+				'vm_memory_high_watermark.relative') rabbit_comment_config 'vm_memory_high_watermark.absolute' ;;
+			esac
 		fi
 	fi
 
 	if [ "$haveSslConfig" ]; then
-		IFS=$'\n'
-		rabbitSslOptions=( $(rabbit_env_config 'ssl' "${sslConfigKeys[@]}") )
-		unset IFS
-
-		rabbitConfig+=(
-			"{ tcp_listeners, $(rabbit_array) }"
-			"{ ssl_listeners, $(rabbit_array 5671) }"
-			"{ ssl_options, $(rabbit_array "${rabbitSslOptions[@]}") }"
-		)
+		rabbit_set_config 'listeners.ssl.default' 5671
+		rabbit_env_config 'ssl' "${sslConfigKeys[@]}"
 	else
-		rabbitConfig+=(
-			"{ tcp_listeners, $(rabbit_array 5672) }"
-			"{ ssl_listeners, $(rabbit_array) }"
-		)
+		rabbit_set_config 'listeners.tcp.default' 5672
 	fi
 
-	IFS=$'\n'
-	rabbitConfig+=( $(rabbit_env_config '' "${rabbitConfigKeys[@]}") )
-	unset IFS
-
-	fullConfig+=( "{ rabbit, $(rabbit_array "${rabbitConfig[@]}") }" )
+	rabbit_env_config '' "${rabbitConfigKeys[@]}"
 
 	# if management plugin is installed, generate config for it
 	# https://www.rabbitmq.com/management.html#configuration
-	if [ "$(rabbitmq-plugins list -m -e rabbitmq_management)" ]; then
-		rabbitManagementConfig=()
-
+	if [ "$(rabbitmq-plugins list -q -m -e rabbitmq_management)" ]; then
 		if [ "$haveManagementSslConfig" ]; then
-			IFS=$'\n'
-			rabbitManagementSslOptions=( $(rabbit_env_config 'management_ssl' "${sslConfigKeys[@]}") )
-			unset IFS
-
-			rabbitManagementListenerConfig+=(
-				'{ port, 15671 }'
-				'{ ssl, true }'
-				"{ ssl_opts, $(rabbit_array "${rabbitManagementSslOptions[@]}") }"
-			)
+			rabbit_set_config 'management.listener.port' 15671
+			rabbit_set_config 'management.listener.ssl' 'true'
+			rabbit_env_config 'management_ssl' "${sslConfigKeys[@]}"
 		else
-			rabbitManagementListenerConfig+=(
-				'{ port, 15672 }'
-				'{ ssl, false }'
-			)
+			rabbit_set_config 'management.listener.port' 15672
+			rabbit_set_config 'management.listener.ssl' 'false'
 		fi
-		rabbitManagementConfig+=(
-			"{ listener, $(rabbit_array "${rabbitManagementListenerConfig[@]}") }"
-		)
 
 		# if definitions file exists, then load it
 		# https://www.rabbitmq.com/management.html#load-definitions
 		managementDefinitionsFile='/etc/rabbitmq/definitions.json'
-		if [ -f "${managementDefinitionsFile}" ]; then
+		if [ -f "$managementDefinitionsFile" ]; then
 			# see also https://github.com/docker-library/rabbitmq/pull/112#issuecomment-271485550
-			rabbitManagementConfig+=(
-				"{ load_definitions, \"$managementDefinitionsFile\" }"
-			)
+			rabbit_set_config 'management.load_definitions' "$managementDefinitionsFile"
 		fi
-
-		fullConfig+=(
-			"{ rabbitmq_management, $(rabbit_array "${rabbitManagementConfig[@]}") }"
-		)
 	fi
-
-	echo "$(rabbit_array "${fullConfig[@]}")." > /etc/rabbitmq/rabbitmq.config
 fi
 
 combinedSsl='/tmp/rabbitmq-ssl/combined.pem'
diff --git a/rabbitmq_3.6-management-alpine/Dockerfile b/rabbitmq_3.7-rc-management-alpine/Dockerfile
similarity index 96%
rename from rabbitmq_3.6-management-alpine/Dockerfile
rename to rabbitmq_3.7-rc-management-alpine/Dockerfile
index 578f07c..b5487f0 100644
--- a/rabbitmq_3.6-management-alpine/Dockerfile
+++ b/rabbitmq_3.7-rc-management-alpine/Dockerfile
@@ -1,4 +1,4 @@
-FROM rabbitmq:3.6-alpine
+FROM rabbitmq:3.7-rc-alpine
 
 RUN rabbitmq-plugins enable --offline rabbitmq_management
 
diff --git a/rabbitmq_3.6-management/Dockerfile b/rabbitmq_3.7-rc-management/Dockerfile
similarity index 86%
rename from rabbitmq_3.6-management/Dockerfile
rename to rabbitmq_3.7-rc-management/Dockerfile
index dcb3d0a..ef76cc8 100644
--- a/rabbitmq_3.6-management/Dockerfile
+++ b/rabbitmq_3.7-rc-management/Dockerfile
@@ -1,4 +1,4 @@
-FROM rabbitmq:3.6
+FROM rabbitmq:3.7-rc
 
 RUN rabbitmq-plugins enable --offline rabbitmq_management
 
@@ -21,9 +21,7 @@ RUN set -eux; \
 	' -- /plugins/rabbitmq_management-*.ez > /usr/local/bin/rabbitmqadmin; \
 	[ -s /usr/local/bin/rabbitmqadmin ]; \
 	chmod +x /usr/local/bin/rabbitmqadmin; \
-	apt-get update; \
-	apt-get install -y --no-install-recommends python; \
-	rm -rf /var/lib/apt/lists/*; \
+	apt-get update; apt-get install -y --no-install-recommends python; rm -rf /var/lib/apt/lists/*; \
 	rabbitmqadmin --version
 
 EXPOSE 15671 15672
diff --git a/rabbitmq_3.7-rc/Dockerfile b/rabbitmq_3.7-rc/Dockerfile
new file mode 100644
index 0000000..725f9d6
--- /dev/null
+++ b/rabbitmq_3.7-rc/Dockerfile
@@ -0,0 +1,246 @@
+# The official Canonical Ubuntu Bionic image is ideal from a security perspective,
+# especially for the enterprises that we, the RabbitMQ team, have to deal with
+FROM ubuntu:18.04
+
+# grab gosu for easy step-down from root
+RUN set -eux; \
+	apt-get update; \
+	apt-get install -y --no-install-recommends gosu; \
+	rm -rf /var/lib/apt/lists/*; \
+# verify that the binary works
+	gosu nobody true
+
+# Default to a PGP keyserver that pgp-happy-eyeballs recognizes, but allow for substitutions locally
+ARG PGP_KEYSERVER=ha.pool.sks-keyservers.net
+# If you are building this image locally and are getting `gpg: keyserver receive failed: No data` errors,
+# run the build with a different PGP_KEYSERVER, e.g. docker build --tag rabbitmq:3.7 --build-arg PGP_KEYSERVER=pgpkeys.eu 3.7/ubuntu
+# For context, see https://github.com/docker-library/official-images/issues/4252
+
+# Using the latest OpenSSL LTS release, with support until September 2023 - https://www.openssl.org/source/
+ENV OPENSSL_VERSION 1.1.1a
+ENV OPENSSL_SOURCE_SHA256="fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41"
+# https://www.openssl.org/community/omc.html
+ENV OPENSSL_PGP_KEY_ID="0x8657ABB260F056B1E5190839D9C4D26D0E604491"
+
+# Use the latest stable Erlang/OTP release (https://github.com/erlang/otp/tags)
+ENV OTP_VERSION 21.2.3
+# TODO add PGP checking when the feature will be added to Erlang/OTP's build system
+# http://erlang.org/pipermail/erlang-questions/2019-January/097067.html
+ENV OTP_SOURCE_SHA256="109a5722e398bdcd3aeb4f4833cde90bf441a9c014006439643aab550a770923"
+
+# Install dependencies required to build Erlang/OTP from source
+# http://erlang.org/doc/installation_guide/INSTALL.html
+# autoconf: Required to configure Erlang/OTP before compiling
+# dpkg-dev: Required to set up host & build type when compiling Erlang/OTP
+# gnupg: Required to verify OpenSSL artefacts
+# libncurses5-dev: Required for Erlang/OTP new shell & observer_cli - https://github.com/zhongwencool/observer_cli
+# m4: Required for Erlang/OTP HiPE support
+RUN set -eux; \
+	\
+	savedAptMark="$(apt-mark showmanual)"; \
+	apt-get update; \
+	apt-get install --yes --no-install-recommends \
+		autoconf \
+		ca-certificates \
+		dpkg-dev \
+		gcc \
+		gnupg \
+		libncurses5-dev \
+		m4 \
+		make \
+		wget \
+	; \
+	rm -rf /var/lib/apt/lists/*; \
+	\
+	OPENSSL_SOURCE_URL="https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz"; \
+	OPENSSL_PATH="/usr/local/src/openssl-$OPENSSL_VERSION"; \
+	OPENSSL_CONFIG_DIR=/usr/local/etc/ssl; \
+	\
+# Required by the crypto & ssl Erlang/OTP applications
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz" "$OPENSSL_SOURCE_URL"; \
+	export GNUPGHOME="$(mktemp -d)"; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$OPENSSL_PGP_KEY_ID"; \
+	gpg --batch --verify "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_PATH.tar.gz"; \
+	gpgconf --kill all; \
+	rm -rf "$GNUPGHOME"; \
+	echo "$OPENSSL_SOURCE_SHA256 *$OPENSSL_PATH.tar.gz" | sha256sum --check --strict -; \
+	mkdir -p "$OPENSSL_PATH"; \
+	tar --extract --file "$OPENSSL_PATH.tar.gz" --directory "$OPENSSL_PATH" --strip-components 1; \
+	\
+# Configure OpenSSL for compilation
+	cd "$OPENSSL_PATH"; \
+	./config --openssldir="$OPENSSL_CONFIG_DIR"; \
+# Compile, install OpenSSL, verify that the command-line works & development headers are present
+	make -j "$(getconf _NPROCESSORS_ONLN)"; \
+	make install_sw install_ssldirs; \
+	cd ..; \
+	rm -rf "$OPENSSL_PATH"*; \
+	ldconfig; \
+# use Debian's CA certificates
+	rmdir "$OPENSSL_CONFIG_DIR/certs" "$OPENSSL_CONFIG_DIR/private"; \
+	ln -sf /etc/ssl/certs /etc/ssl/private "$OPENSSL_CONFIG_DIR"; \
+# smoke test
+	openssl version; \
+	\
+	OTP_SOURCE_URL="https://github.com/erlang/otp/archive/OTP-$OTP_VERSION.tar.gz"; \
+	OTP_PATH="/usr/local/src/otp-$OTP_VERSION"; \
+	\
+# Download, verify & extract OTP_SOURCE
+	mkdir -p "$OTP_PATH"; \
+	wget --progress dot:giga --output-document "$OTP_PATH.tar.gz" "$OTP_SOURCE_URL"; \
+	echo "$OTP_SOURCE_SHA256 *$OTP_PATH.tar.gz" | sha256sum --check --strict -; \
+	tar --extract --file "$OTP_PATH.tar.gz" --directory "$OTP_PATH" --strip-components 1; \
+	\
+# Configure Erlang/OTP for compilation, disable unused features & applications
+# http://erlang.org/doc/applications.html
+# ERL_TOP is required for Erlang/OTP makefiles to find the absolute path for the installation
+	cd "$OTP_PATH"; \
+	export ERL_TOP="$OTP_PATH"; \
+	./otp_build autoconf; \
+	CFLAGS="$(dpkg-buildflags --get CFLAGS)"; export CFLAGS; \
+	hostArch="$(dpkg-architecture --query DEB_HOST_GNU_TYPE)"; \
+	buildArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
+	./configure \
+		--host="$hostArch" \
+		--build="$buildArch" \
+		--disable-dynamic-ssl-lib \
+		--disable-sctp \
+		--disable-silent-rules \
+		--enable-clock-gettime \
+		--enable-hipe \
+		--enable-hybrid-heap \
+		--enable-kernel-poll \
+		--enable-shared-zlib \
+		--enable-smp-support \
+		--enable-threads \
+		--with-microstate-accounting=extra \
+		--without-common_test \
+		--without-debugger \
+		--without-dialyzer \
+		--without-diameter \
+		--without-edoc \
+		--without-erl_docgen \
+		--without-erl_interface \
+		--without-et \
+		--without-eunit \
+		--without-ftp \
+		--without-jinterface \
+		--without-megaco \
+		--without-observer \
+		--without-odbc \
+		--without-reltool \
+		--without-ssh \
+		--without-tftp \
+		--without-wx \
+	; \
+# Compile & install Erlang/OTP
+	make -j "$(getconf _NPROCESSORS_ONLN)" GEN_OPT_FLGS="-O2 -fno-strict-aliasing"; \
+	make install; \
+	cd ..; \
+	rm -rf "$OTP_PATH"* /usr/local/lib/erlang/lib/*/src; \
+	\
+# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
+	apt-mark auto '.*' > /dev/null; \
+	[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \
+	find /usr/local -type f -executable -exec ldd '{}' ';' \
+		| awk '/=>/ { print $(NF-1) }' \
+		| sort -u \
+		| xargs -r dpkg-query --search \
+		| cut -d: -f1 \
+		| sort -u \
+		| xargs -r apt-mark manual \
+	; \
+	apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+	\
+# Check that OpenSSL still works after purging build dependencies
+	openssl version; \
+# Check that Erlang/OTP crypto & ssl were compiled against OpenSSL correctly
+	erl -noshell -eval 'io:format("~p~n~n~p~n~n", [crypto:supports(), ssl:versions()]), init:stop().'
+
+ENV RABBITMQ_DATA_DIR=/var/lib/rabbitmq
+# Create rabbitmq system user & group, fix permissions & allow root user to connect to the RabbitMQ Erlang VM
+RUN set -eux; \
+	groupadd --gid 999 --system rabbitmq; \
+	useradd --uid 999 --system --home-dir "$RABBITMQ_DATA_DIR" --gid rabbitmq rabbitmq; \
+	mkdir -p "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chown -fR rabbitmq:rabbitmq "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chmod 777 "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	ln -sf "$RABBITMQ_DATA_DIR/.erlang.cookie" /root/.erlang.cookie
+
+# Use the latest stable RabbitMQ release (https://www.rabbitmq.com/download.html)
+ENV RABBITMQ_VERSION 3.7.11-rc.2
+# https://www.rabbitmq.com/signatures.html#importing-gpg
+ENV RABBITMQ_PGP_KEY_ID="0x0A9AF2115F4687BD29803A206B73A36E6026DFCA"
+ENV RABBITMQ_HOME=/opt/rabbitmq
+
+# Add RabbitMQ to PATH, send all logs to TTY
+ENV PATH=$RABBITMQ_HOME/sbin:$PATH \
+	RABBITMQ_LOGS=- RABBITMQ_SASL_LOGS=-
+
+# Install RabbitMQ
+RUN set -eux; \
+	\
+	savedAptMark="$(apt-mark showmanual)"; \
+	apt-get update; \
+	apt-get install --yes --no-install-recommends \
+		ca-certificates \
+		gnupg \
+		wget \
+		xz-utils \
+	; \
+	rm -rf /var/lib/apt/lists/*; \
+	\
+	RABBITMQ_SOURCE_URL="https://github.com/rabbitmq/rabbitmq-server/releases/download/v$RABBITMQ_VERSION/rabbitmq-server-generic-unix-$RABBITMQ_VERSION.tar.xz"; \
+	RABBITMQ_PATH="/usr/local/src/rabbitmq-$RABBITMQ_VERSION"; \
+	\
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz" "$RABBITMQ_SOURCE_URL"; \
+	\
+	export GNUPGHOME="$(mktemp -d)"; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$RABBITMQ_PGP_KEY_ID"; \
+	gpg --batch --verify "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_PATH.tar.xz"; \
+	gpgconf --kill all; \
+	rm -rf "$GNUPGHOME"; \
+	\
+	mkdir -p "$RABBITMQ_HOME"; \
+	tar --extract --file "$RABBITMQ_PATH.tar.xz" --directory "$RABBITMQ_HOME" --strip-components 1; \
+	rm -rf "$RABBITMQ_PATH"*; \
+# Do not default SYS_PREFIX to RABBITMQ_HOME, leave it empty
+	grep -qE '^SYS_PREFIX=\$\{RABBITMQ_HOME\}$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	sed -i 's/^SYS_PREFIX=.*$/SYS_PREFIX=/' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	grep -qE '^SYS_PREFIX=$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	chown -R rabbitmq:rabbitmq "$RABBITMQ_HOME"; \
+	\
+	apt-mark auto '.*' > /dev/null; \
+	apt-mark manual $savedAptMark; \
+	apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+	\
+# verify assumption of no stale cookies
+	[ ! -e "$RABBITMQ_DATA_DIR/.erlang.cookie" ]; \
+# Ensure RabbitMQ was installed correctly by running a few commands that do not depend on a running server, as the rabbitmq user
+# If they all succeed, it's safe to assume that things have been set up correctly
+	gosu rabbitmq rabbitmqctl help; \
+	gosu rabbitmq rabbitmqctl list_ciphers; \
+	gosu rabbitmq rabbitmq-plugins list; \
+# no stale cookies
+	rm "$RABBITMQ_DATA_DIR/.erlang.cookie"
+
+# Added for backwards compatibility - users can simply COPY custom plugins to /plugins
+RUN ln -sf /opt/rabbitmq/plugins /plugins
+
+# set home so that any `--user` knows where to put the erlang cookie
+ENV HOME $RABBITMQ_DATA_DIR
+# Hint that the data (a.k.a. home dir) dir should be separate volume
+VOLUME $RABBITMQ_DATA_DIR
+
+# warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
+# Setting all environment variables that control language preferences, behaviour differs - https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable
+# https://docs.docker.com/samples/library/ubuntu/#locales
+ENV LANG=C.UTF-8 LANGUAGE=C.UTF-8 LC_ALL=C.UTF-8
+
+COPY docker-entrypoint.sh /usr/local/bin/
+ENTRYPOINT ["docker-entrypoint.sh"]
+
+EXPOSE 4369 5671 5672 25672
+CMD ["rabbitmq-server"]
diff --git a/rabbitmq_3.6/docker-entrypoint.sh b/rabbitmq_3.7-rc/docker-entrypoint.sh
similarity index 69%
rename from rabbitmq_3.6/docker-entrypoint.sh
rename to rabbitmq_3.7-rc/docker-entrypoint.sh
index 7201e98..a1bdb3d 100755
--- a/rabbitmq_3.6/docker-entrypoint.sh
+++ b/rabbitmq_3.7-rc/docker-entrypoint.sh
@@ -198,87 +198,95 @@ if [ "${RABBITMQ_ERLANG_COOKIE:-}" ]; then
 	chmod 600 "$cookieFile"
 fi
 
-# prints "$2$1$3$1...$N"
-join() {
-	local sep="$1"; shift
-	local out; printf -v out "${sep//%/%%}%s" "$@"
-	echo "${out#$sep}"
-}
-indent() {
-	if [ "$#" -gt 0 ]; then
-		echo "$@"
-	else
-		cat
-	fi | sed 's/^/\t/g'
+configBase="${RABBITMQ_CONFIG_FILE:-/etc/rabbitmq/rabbitmq}"
+oldConfigFile="$configBase.config"
+newConfigFile="$configBase.conf"
+
+shouldWriteConfig="$haveConfig"
+if [ -n "$shouldWriteConfig" ] && [ -f "$oldConfigFile" ]; then
+	{
+		echo "error: Docker configuration environment variables specified, but old-style (Erlang syntax) configuration file '$oldConfigFile' exists"
+		echo "  Suggested fixes: (choose one)"
+		echo "   - remove '$oldConfigFile'"
+		echo "   - remove any Docker-specific 'RABBITMQ_...' environment variables"
+		echo "   - convert '$oldConfigFile' to the newer sysctl format ('$newConfigFile'); see https://www.rabbitmq.com/configure.html#config-file"
+	} >&2
+	exit 1
+fi
+if [ -z "$shouldWriteConfig" ] && [ ! -f "$oldConfigFile" ] && [ ! -f "$newConfigFile" ]; then
+	# no config files, we should write one
+	shouldWriteConfig=1
+fi
+
+# http://stackoverflow.com/a/2705678/433558
+sed_escape_lhs() {
+	echo "$@" | sed -e 's/[]\/$*.^|[]/\\&/g'
 }
-rabbit_array() {
-	echo -n '['
-	case "$#" in
-		0) echo -n ' ' ;;
-		1) echo -n " $1 " ;;
-		*)
-			local vals="$(join $',\n' "$@")"
-			echo
-			indent "$vals"
-	esac
-	echo -n ']'
+sed_escape_rhs() {
+	echo "$@" | sed -e 's/[\/&]/\\&/g'
 }
-rabbit_string() {
+rabbit_set_config() {
+	local key="$1"; shift
 	local val="$1"; shift
-	# fire up erlang directly to have it do the proper escaping for us
-	erl -noinput -eval 'io:format("~p\n", init:get_plain_arguments()), init:stop().' -- "$val"
+
+	[ -e "$newConfigFile" ] || touch "$newConfigFile"
+
+	local sedKey="$(sed_escape_lhs "$key")"
+	local sedVal="$(sed_escape_rhs "$val")"
+	sed -ri \
+		"s/^[[:space:]]*(${sedKey}[[:space:]]*=[[:space:]]*)\S.*\$/\1${sedVal}/" \
+		"$newConfigFile"
+	if ! grep -qE "^${sedKey}[[:space:]]*=" "$newConfigFile"; then
+		echo "$key = $val" >> "$newConfigFile"
+	fi
+}
+rabbit_comment_config() {
+	local key="$1"; shift
+
+	[ -e "$newConfigFile" ] || touch "$newConfigFile"
+
+	local sedKey="$(sed_escape_lhs "$key")"
+	sed -ri \
+		"s/^[[:space:]]*#?[[:space:]]*(${sedKey}[[:space:]]*=[[:space:]]*\S.*)\$/# \1/" \
+		"$newConfigFile"
 }
 rabbit_env_config() {
 	local prefix="$1"; shift
 
-	local ret=()
 	local conf
 	for conf; do
 		local var="rabbitmq${prefix:+_$prefix}_$conf"
 		var="${var^^}"
 
-		local val="${!var:-}"
+		local key="$conf"
+		case "$prefix" in
+			ssl) key="ssl_options.$key" ;;
+			management_ssl) key="management.listener.ssl_opts.$key" ;;
+		esac
 
-		local rawVal=
+		local val="${!var:-}"
+		local rawVal="$val"
 		case "$conf" in
-			verify|fail_if_no_peer_cert|depth)
-				[ "$val" ] || continue
-				rawVal="$val"
-				;;
-
-			hipe_compile)
-				[ "$val" ] && rawVal='true' || rawVal='false'
-				;;
-
-			cacertfile|certfile|keyfile)
-				[ "$val" ] || continue
-				rawVal="$(rabbit_string "$val")"
+			fail_if_no_peer_cert|hipe_compile)
+				case "${val,,}" in
+					false|no|0|'') rawVal='false' ;;
+					true|yes|1|*) rawVal='true' ;;
+				esac
 				;;
 
-			*)
-				[ "$val" ] || continue
-				rawVal="<<$(rabbit_string "$val")>>"
-				;;
+			vm_memory_high_watermark) continue ;; # handled separately
 		esac
-		[ "$rawVal" ] || continue
 
-		ret+=( "{ $conf, $rawVal }" )
+		if [ -n "$rawVal" ]; then
+			rabbit_set_config "$key" "$rawVal"
+		else
+			rabbit_comment_config "$key"
+		fi
 	done
-
-	join $'\n' "${ret[@]}"
 }
 
-shouldWriteConfig="$haveConfig"
-if [ ! -f /etc/rabbitmq/rabbitmq.config ]; then
-	shouldWriteConfig=1
-fi
-
 if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
-	fullConfig=()
-
-	rabbitConfig=(
-		"{ loopback_users, $(rabbit_array) }"
-	)
+	rabbit_set_config 'loopback_users.guest' 'false'
 
 	# determine whether to set "vm_memory_high_watermark" (based on cgroups)
 	memTotalKb=
@@ -300,12 +308,13 @@ if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
 	if [ -n "$memLimitB" ]; then
 		# if we have a cgroup memory limit, let's inform RabbitMQ of what it is (so it can calculate vm_memory_high_watermark properly)
 		# https://github.com/rabbitmq/rabbitmq-server/pull/1234
-		rabbitConfig+=( "{ total_memory_available_override_value, $memLimitB }" )
+		rabbit_set_config 'total_memory_available_override_value' "$memLimitB"
 	fi
+	# https://www.rabbitmq.com/memory.html#memsup-usage
 	if [ "${RABBITMQ_VM_MEMORY_HIGH_WATERMARK:-}" ]; then
 		# https://github.com/docker-library/rabbitmq/pull/105#issuecomment-242165822
 		vmMemoryHighWatermark="$(
-			awk '
+			echo "$RABBITMQ_VM_MEMORY_HIGH_WATERMARK" | awk '
 				/^[0-9]*[.][0-9]+$|^[0-9]+([.][0-9]+)?%$/ {
 					perc = $0;
 					if (perc ~ /%$/) {
@@ -316,93 +325,64 @@ if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
 						printf "error: invalid percentage for vm_memory_high_watermark: %s (must be > 0%%, <= 100%%)\n", $0 > "/dev/stderr";
 						exit 1;
 					}
-					printf "%0.03f\n", perc;
+					printf "vm_memory_high_watermark.relative %0.03f\n", perc;
 					next;
 				}
 				/^[0-9]+$/ {
-					printf "{ absolute, %s }\n", $0;
+					printf "vm_memory_high_watermark.absolute %s\n", $0;
 					next;
 				}
 				/^[0-9]+([.][0-9]+)?[a-zA-Z]+$/ {
-					printf "{ absolute, \"%s\" }\n", $0;
+					printf "vm_memory_high_watermark.absolute %s\n", $0;
 					next;
 				}
 				{
 					printf "error: unexpected input for vm_memory_high_watermark: %s\n", $0;
 					exit 1;
 				}
-			' <(echo "$RABBITMQ_VM_MEMORY_HIGH_WATERMARK")
+			'
 		)"
 		if [ "$vmMemoryHighWatermark" ]; then
-			# https://www.rabbitmq.com/memory.html#memsup-usage
-			rabbitConfig+=( "{ vm_memory_high_watermark, $vmMemoryHighWatermark }" )
+			vmMemoryHighWatermarkKey="${vmMemoryHighWatermark%% *}"
+			vmMemoryHighWatermarkVal="${vmMemoryHighWatermark#$vmMemoryHighWatermarkKey }"
+			rabbit_set_config "$vmMemoryHighWatermarkKey" "$vmMemoryHighWatermarkVal"
+			case "$vmMemoryHighWatermarkKey" in
+				# make sure we only set one or the other
+				'vm_memory_high_watermark.absolute') rabbit_comment_config 'vm_memory_high_watermark.relative' ;;
+				'vm_memory_high_watermark.relative') rabbit_comment_config 'vm_memory_high_watermark.absolute' ;;
+			esac
 		fi
 	fi
 
 	if [ "$haveSslConfig" ]; then
-		IFS=$'\n'
-		rabbitSslOptions=( $(rabbit_env_config 'ssl' "${sslConfigKeys[@]}") )
-		unset IFS
-
-		rabbitConfig+=(
-			"{ tcp_listeners, $(rabbit_array) }"
-			"{ ssl_listeners, $(rabbit_array 5671) }"
-			"{ ssl_options, $(rabbit_array "${rabbitSslOptions[@]}") }"
-		)
+		rabbit_set_config 'listeners.ssl.default' 5671
+		rabbit_env_config 'ssl' "${sslConfigKeys[@]}"
 	else
-		rabbitConfig+=(
-			"{ tcp_listeners, $(rabbit_array 5672) }"
-			"{ ssl_listeners, $(rabbit_array) }"
-		)
+		rabbit_set_config 'listeners.tcp.default' 5672
 	fi
 
-	IFS=$'\n'
-	rabbitConfig+=( $(rabbit_env_config '' "${rabbitConfigKeys[@]}") )
-	unset IFS
-
-	fullConfig+=( "{ rabbit, $(rabbit_array "${rabbitConfig[@]}") }" )
+	rabbit_env_config '' "${rabbitConfigKeys[@]}"
 
 	# if management plugin is installed, generate config for it
 	# https://www.rabbitmq.com/management.html#configuration
-	if [ "$(rabbitmq-plugins list -m -e rabbitmq_management)" ]; then
-		rabbitManagementConfig=()
-
+	if [ "$(rabbitmq-plugins list -q -m -e rabbitmq_management)" ]; then
 		if [ "$haveManagementSslConfig" ]; then
-			IFS=$'\n'
-			rabbitManagementSslOptions=( $(rabbit_env_config 'management_ssl' "${sslConfigKeys[@]}") )
-			unset IFS
-
-			rabbitManagementListenerConfig+=(
-				'{ port, 15671 }'
-				'{ ssl, true }'
-				"{ ssl_opts, $(rabbit_array "${rabbitManagementSslOptions[@]}") }"
-			)
+			rabbit_set_config 'management.listener.port' 15671
+			rabbit_set_config 'management.listener.ssl' 'true'
+			rabbit_env_config 'management_ssl' "${sslConfigKeys[@]}"
 		else
-			rabbitManagementListenerConfig+=(
-				'{ port, 15672 }'
-				'{ ssl, false }'
-			)
+			rabbit_set_config 'management.listener.port' 15672
+			rabbit_set_config 'management.listener.ssl' 'false'
 		fi
-		rabbitManagementConfig+=(
-			"{ listener, $(rabbit_array "${rabbitManagementListenerConfig[@]}") }"
-		)
 
 		# if definitions file exists, then load it
 		# https://www.rabbitmq.com/management.html#load-definitions
 		managementDefinitionsFile='/etc/rabbitmq/definitions.json'
-		if [ -f "${managementDefinitionsFile}" ]; then
+		if [ -f "$managementDefinitionsFile" ]; then
 			# see also https://github.com/docker-library/rabbitmq/pull/112#issuecomment-271485550
-			rabbitManagementConfig+=(
-				"{ load_definitions, \"$managementDefinitionsFile\" }"
-			)
+			rabbit_set_config 'management.load_definitions' "$managementDefinitionsFile"
 		fi
-
-		fullConfig+=(
-			"{ rabbitmq_management, $(rabbit_array "${rabbitManagementConfig[@]}") }"
-		)
 	fi
-
-	echo "$(rabbit_array "${fullConfig[@]}")." > /etc/rabbitmq/rabbitmq.config
 fi
 
 combinedSsl='/tmp/rabbitmq-ssl/combined.pem'
diff --git a/rabbitmq_alpine/Dockerfile b/rabbitmq_alpine/Dockerfile
index 857cc5b..55cfbc4 100644
--- a/rabbitmq_alpine/Dockerfile
+++ b/rabbitmq_alpine/Dockerfile
@@ -1,92 +1,230 @@
+# Alpine Linux is not officially supported by the RabbitMQ team -- use at your own risk!
 FROM alpine:3.8
 
-# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
-RUN addgroup -S rabbitmq && adduser -S -h /var/lib/rabbitmq -G rabbitmq rabbitmq
-
 # grab su-exec for easy step-down from root
-RUN apk add --no-cache 'su-exec>=0.2'
+# bash for docker-entrypoint.sh
+RUN apk add --no-cache 'su-exec>=0.2' bash
+
+# Default to a PGP keyserver that pgp-happy-eyeballs recognizes, but allow for substitutions locally
+ARG PGP_KEYSERVER=ha.pool.sks-keyservers.net
+# If you are building this image locally and are getting `gpg: keyserver receive failed: No data` errors,
+# run the build with a different PGP_KEYSERVER, e.g. docker build --tag rabbitmq:3.7 --build-arg PGP_KEYSERVER=pgpkeys.eu 3.7/ubuntu
+# For context, see https://github.com/docker-library/official-images/issues/4252
 
-RUN apk add --no-cache \
-# Bash for docker-entrypoint
-		bash \
-# Procps for rabbitmqctl
-		procps \
-# Erlang for RabbitMQ
-		erlang-asn1 \
-		erlang-hipe \
-		erlang-crypto \
-		erlang-eldap \
-		erlang-inets \
-		erlang-mnesia \
-		erlang \
-		erlang-os-mon \
-		erlang-public-key \
-		erlang-sasl \
-		erlang-ssl \
-		erlang-syntax-tools \
-		erlang-xmerl
+# Using the latest OpenSSL LTS release, with support until September 2023 - https://www.openssl.org/source/
+ENV OPENSSL_VERSION 1.1.1a
+ENV OPENSSL_SOURCE_SHA256="fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41"
+# https://www.openssl.org/community/omc.html
+ENV OPENSSL_PGP_KEY_ID="0x8657ABB260F056B1E5190839D9C4D26D0E604491"
 
-# get logs to stdout (thanks @dumbbell for pushing this upstream! :D)
-ENV RABBITMQ_LOGS=- RABBITMQ_SASL_LOGS=-
-# https://github.com/rabbitmq/rabbitmq-server/commit/53af45bf9a162dec849407d114041aad3d84feaf
+# Use the latest stable Erlang/OTP release (https://github.com/erlang/otp/tags)
+ENV OTP_VERSION 21.2.3
+# TODO add PGP checking when the feature will be added to Erlang/OTP's build system
+# http://erlang.org/pipermail/erlang-questions/2019-January/097067.html
+ENV OTP_SOURCE_SHA256="109a5722e398bdcd3aeb4f4833cde90bf441a9c014006439643aab550a770923"
+
+# Install dependencies required to build Erlang/OTP from source
+# http://erlang.org/doc/installation_guide/INSTALL.html
+# autoconf: Required to configure Erlang/OTP before compiling
+# dpkg-dev: Required to set up host & build type when compiling Erlang/OTP
+# gnupg: Required to verify OpenSSL artefacts
+# libncurses5-dev: Required for Erlang/OTP new shell & observer_cli - https://github.com/zhongwencool/observer_cli
+# m4: Required for Erlang/OTP HiPE support
+RUN set -eux; \
+	\
+	apk add --no-cache --virtual .build-deps \
+		autoconf \
+		ca-certificates \
+		dpkg-dev dpkg \
+		gcc \
+		gnupg \
+		libc-dev \
+		linux-headers \
+		m4 \
+		make \
+		ncurses-dev \
+		wget \
+	; \
+	\
+	OPENSSL_SOURCE_URL="https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz"; \
+	OPENSSL_PATH="/usr/local/src/openssl-$OPENSSL_VERSION"; \
+	OPENSSL_CONFIG_DIR=/usr/local/etc/ssl; \
+	\
+# /usr/local/src doesn't exist in Alpine by default
+	mkdir /usr/local/src; \
+	\
+# Required by the crypto & ssl Erlang/OTP applications
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz" "$OPENSSL_SOURCE_URL"; \
+	export GNUPGHOME="$(mktemp -d)"; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$OPENSSL_PGP_KEY_ID"; \
+	gpg --batch --verify "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_PATH.tar.gz"; \
+	gpgconf --kill all; \
+	rm -rf "$GNUPGHOME"; \
+	echo "$OPENSSL_SOURCE_SHA256 *$OPENSSL_PATH.tar.gz" | sha256sum -c -; \
+	mkdir -p "$OPENSSL_PATH"; \
+	tar --extract --file "$OPENSSL_PATH.tar.gz" --directory "$OPENSSL_PATH" --strip-components 1; \
+	\
+# Configure OpenSSL for compilation
+	cd "$OPENSSL_PATH"; \
+	./config --openssldir="$OPENSSL_CONFIG_DIR"; \
+# Compile, install OpenSSL, verify that the command-line works & development headers are present
+	make -j "$(getconf _NPROCESSORS_ONLN)"; \
+	make install_sw install_ssldirs; \
+	cd ..; \
+	rm -rf "$OPENSSL_PATH"*; \
+# use Alpine's CA certificates
+	rmdir "$OPENSSL_CONFIG_DIR/certs" "$OPENSSL_CONFIG_DIR/private"; \
+	ln -sf /etc/ssl/certs /etc/ssl/private "$OPENSSL_CONFIG_DIR"; \
+# smoke test
+	openssl version; \
+	\
+	OTP_SOURCE_URL="https://github.com/erlang/otp/archive/OTP-$OTP_VERSION.tar.gz"; \
+	OTP_PATH="/usr/local/src/otp-$OTP_VERSION"; \
+	\
+# Download, verify & extract OTP_SOURCE
+	mkdir -p "$OTP_PATH"; \
+	wget --progress dot:giga --output-document "$OTP_PATH.tar.gz" "$OTP_SOURCE_URL"; \
+	echo "$OTP_SOURCE_SHA256 *$OTP_PATH.tar.gz" | sha256sum -c -; \
+	tar --extract --file "$OTP_PATH.tar.gz" --directory "$OTP_PATH" --strip-components 1; \
+	\
+# Configure Erlang/OTP for compilation, disable unused features & applications
+# http://erlang.org/doc/applications.html
+# ERL_TOP is required for Erlang/OTP makefiles to find the absolute path for the installation
+	cd "$OTP_PATH"; \
+	export ERL_TOP="$OTP_PATH"; \
+	./otp_build autoconf; \
+	CFLAGS="$(dpkg-buildflags --get CFLAGS)"; export CFLAGS; \
+	hostArch="$(dpkg-architecture --query DEB_HOST_GNU_TYPE)"; \
+	buildArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
+	./configure \
+		--host="$hostArch" \
+		--build="$buildArch" \
+		--disable-dynamic-ssl-lib \
+		--disable-sctp \
+		--disable-silent-rules \
+		--enable-clock-gettime \
+		--enable-hipe \
+		--enable-hybrid-heap \
+		--enable-kernel-poll \
+		--enable-shared-zlib \
+		--enable-smp-support \
+		--enable-threads \
+		--with-microstate-accounting=extra \
+		--without-common_test \
+		--without-debugger \
+		--without-dialyzer \
+		--without-diameter \
+		--without-edoc \
+		--without-erl_docgen \
+		--without-erl_interface \
+		--without-et \
+		--without-eunit \
+		--without-ftp \
+		--without-jinterface \
+		--without-megaco \
+		--without-observer \
+		--without-odbc \
+		--without-reltool \
+		--without-ssh \
+		--without-tftp \
+		--without-wx \
+	; \
+# Compile & install Erlang/OTP
+	make -j "$(getconf _NPROCESSORS_ONLN)" GEN_OPT_FLGS="-O2 -fno-strict-aliasing"; \
+	make install; \
+	cd ..; \
+	rm -rf "$OTP_PATH"* /usr/local/lib/erlang/lib/*/src; \
+	\
+	runDeps="$( \
+		scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \
+			| tr ',' '\n' \
+			| sort -u \
+			| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
+	)"; \
+	apk add --no-cache --virtual .otp-run-deps $runDeps; \
+	apk del --no-network .build-deps; \
+	\
+# Check that OpenSSL still works after purging build dependencies
+	openssl version; \
+# Check that Erlang/OTP crypto & ssl were compiled against OpenSSL correctly
+	erl -noshell -eval 'io:format("~p~n~n~p~n~n", [crypto:supports(), ssl:versions()]), init:stop().'
 
-ENV RABBITMQ_HOME /opt/rabbitmq
-ENV PATH $RABBITMQ_HOME/sbin:$PATH
+ENV RABBITMQ_DATA_DIR=/var/lib/rabbitmq
+# Create rabbitmq system user & group, fix permissions & allow root user to connect to the RabbitMQ Erlang VM
+RUN set -eux; \
+	addgroup -g 101 -S rabbitmq; \
+	adduser -u 100 -S -h "$RABBITMQ_DATA_DIR" -G rabbitmq rabbitmq; \
+	mkdir -p "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chown -fR rabbitmq:rabbitmq "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chmod 777 "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	ln -sf "$RABBITMQ_DATA_DIR/.erlang.cookie" /root/.erlang.cookie
 
-# gpg: key 6026DFCA: public key "RabbitMQ Release Signing Key <info@rabbitmq.com>" imported
-ENV RABBITMQ_GPG_KEY 0A9AF2115F4687BD29803A206B73A36E6026DFCA
+# Use the latest stable RabbitMQ release (https://www.rabbitmq.com/download.html)
+ENV RABBITMQ_VERSION 3.7.10
+# https://www.rabbitmq.com/signatures.html#importing-gpg
+ENV RABBITMQ_PGP_KEY_ID="0x0A9AF2115F4687BD29803A206B73A36E6026DFCA"
+ENV RABBITMQ_HOME=/opt/rabbitmq
 
-ENV RABBITMQ_VERSION 3.7.8
-ENV RABBITMQ_GITHUB_TAG v3.7.8
+# Add RabbitMQ to PATH, send all logs to TTY
+ENV PATH=$RABBITMQ_HOME/sbin:$PATH \
+	RABBITMQ_LOGS=- RABBITMQ_SASL_LOGS=-
 
-RUN set -ex; \
+# Install RabbitMQ
+RUN set -eux; \
 	\
 	apk add --no-cache --virtual .build-deps \
 		ca-certificates \
 		gnupg \
-		libressl \
 		wget \
 		xz \
 	; \
 	\
-	wget -O rabbitmq-server.tar.xz.asc "https://github.com/rabbitmq/rabbitmq-server/releases/download/$RABBITMQ_GITHUB_TAG/rabbitmq-server-generic-unix-${RABBITMQ_VERSION}.tar.xz.asc"; \
-	wget -O rabbitmq-server.tar.xz     "https://github.com/rabbitmq/rabbitmq-server/releases/download/$RABBITMQ_GITHUB_TAG/rabbitmq-server-generic-unix-${RABBITMQ_VERSION}.tar.xz"; \
+	RABBITMQ_SOURCE_URL="https://github.com/rabbitmq/rabbitmq-server/releases/download/v$RABBITMQ_VERSION/rabbitmq-server-generic-unix-$RABBITMQ_VERSION.tar.xz"; \
+	RABBITMQ_PATH="/usr/local/src/rabbitmq-$RABBITMQ_VERSION"; \
+	\
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz" "$RABBITMQ_SOURCE_URL"; \
 	\
 	export GNUPGHOME="$(mktemp -d)"; \
-	gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$RABBITMQ_GPG_KEY"; \
-	gpg --batch --verify rabbitmq-server.tar.xz.asc rabbitmq-server.tar.xz; \
-	command -v gpgconf && gpgconf --kill all || :; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$RABBITMQ_PGP_KEY_ID"; \
+	gpg --batch --verify "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_PATH.tar.xz"; \
+	gpgconf --kill all; \
 	rm -rf "$GNUPGHOME"; \
 	\
 	mkdir -p "$RABBITMQ_HOME"; \
-	tar \
-		--extract \
-		--verbose \
-		--file rabbitmq-server.tar.xz \
-		--directory "$RABBITMQ_HOME" \
-		--strip-components 1 \
-	; \
-	rm -f rabbitmq-server.tar.xz*; \
-	\
-# update SYS_PREFIX (first making sure it's set to what we expect it to be)
+	tar --extract --file "$RABBITMQ_PATH.tar.xz" --directory "$RABBITMQ_HOME" --strip-components 1; \
+	rm -rf "$RABBITMQ_PATH"*; \
+# Do not default SYS_PREFIX to RABBITMQ_HOME, leave it empty
 	grep -qE '^SYS_PREFIX=\$\{RABBITMQ_HOME\}$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
-	sed -ri 's!^(SYS_PREFIX=).*$!\1!g' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	sed -i 's/^SYS_PREFIX=.*$/SYS_PREFIX=/' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
 	grep -qE '^SYS_PREFIX=$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	chown -R rabbitmq:rabbitmq "$RABBITMQ_HOME"; \
 	\
-	apk del .build-deps
-
-# set home so that any `--user` knows where to put the erlang cookie
-ENV HOME /var/lib/rabbitmq
+	apk del .build-deps; \
+	\
+# verify assumption of no stale cookies
+	[ ! -e "$RABBITMQ_DATA_DIR/.erlang.cookie" ]; \
+# Ensure RabbitMQ was installed correctly by running a few commands that do not depend on a running server, as the rabbitmq user
+# If they all succeed, it's safe to assume that things have been set up correctly
+	su-exec rabbitmq rabbitmqctl help; \
+	su-exec rabbitmq rabbitmqctl list_ciphers; \
+	su-exec rabbitmq rabbitmq-plugins list; \
+# no stale cookies
+	rm "$RABBITMQ_DATA_DIR/.erlang.cookie"
 
-RUN mkdir -p /var/lib/rabbitmq /etc/rabbitmq /var/log/rabbitmq /tmp/rabbitmq-ssl \
-	&& chown -R rabbitmq:rabbitmq /var/lib/rabbitmq /etc/rabbitmq /var/log/rabbitmq /tmp/rabbitmq-ssl \
-	&& chmod -R 777 /var/lib/rabbitmq /etc/rabbitmq /var/log/rabbitmq /tmp/rabbitmq-ssl
-VOLUME /var/lib/rabbitmq
+# Added for backwards compatibility - users can simply COPY custom plugins to /plugins
+RUN ln -sf /opt/rabbitmq/plugins /plugins
 
-# add a symlink to the .erlang.cookie in /root so we can "docker exec rabbitmqctl ..." without gosu
-RUN ln -sf /var/lib/rabbitmq/.erlang.cookie /root/
+# set home so that any `--user` knows where to put the erlang cookie
+ENV HOME $RABBITMQ_DATA_DIR
+# Hint that the data (a.k.a. home dir) dir should be separate volume
+VOLUME $RABBITMQ_DATA_DIR
 
-RUN ln -sf "$RABBITMQ_HOME/plugins" /plugins
+# warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
+# Setting all environment variables that control language preferences, behaviour differs - https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable
+# https://docs.docker.com/samples/library/ubuntu/#locales
+ENV LANG=C.UTF-8 LANGUAGE=C.UTF-8 LC_ALL=C.UTF-8
 
 COPY docker-entrypoint.sh /usr/local/bin/
 ENTRYPOINT ["docker-entrypoint.sh"]
diff --git a/rabbitmq_alpine/docker-entrypoint.sh b/rabbitmq_alpine/docker-entrypoint.sh
index a2856bc..aea0160 100755
--- a/rabbitmq_alpine/docker-entrypoint.sh
+++ b/rabbitmq_alpine/docker-entrypoint.sh
@@ -365,7 +365,7 @@ if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
 
 	# if management plugin is installed, generate config for it
 	# https://www.rabbitmq.com/management.html#configuration
-	if [ "$(rabbitmq-plugins list -m -e rabbitmq_management)" ]; then
+	if [ "$(rabbitmq-plugins list -q -m -e rabbitmq_management)" ]; then
 		if [ "$haveManagementSslConfig" ]; then
 			rabbit_set_config 'management.listener.port' 15671
 			rabbit_set_config 'management.listener.ssl' 'true'
diff --git a/rabbitmq_latest/Dockerfile b/rabbitmq_latest/Dockerfile
index 3620998..543354e 100644
--- a/rabbitmq_latest/Dockerfile
+++ b/rabbitmq_latest/Dockerfile
@@ -1,145 +1,245 @@
-FROM debian:stretch-slim
+# The official Canonical Ubuntu Bionic image is ideal from a security perspective,
+# especially for the enterprises that we, the RabbitMQ team, have to deal with
+FROM ubuntu:18.04
 
+# grab gosu for easy step-down from root
 RUN set -eux; \
 	apt-get update; \
-	apt-get install -y --no-install-recommends \
-		gnupg \
-		dirmngr \
-	; \
-	rm -rf /var/lib/apt/lists/*
-
-# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
-RUN groupadd -r rabbitmq && useradd -r -d /var/lib/rabbitmq -m -g rabbitmq rabbitmq
-
-# grab gosu for easy step-down from root
-ENV GOSU_VERSION 1.10
+	apt-get install -y --no-install-recommends gosu; \
+	rm -rf /var/lib/apt/lists/*; \
+# verify that the binary works
+	gosu nobody true
+
+# Default to a PGP keyserver that pgp-happy-eyeballs recognizes, but allow for substitutions locally
+ARG PGP_KEYSERVER=ha.pool.sks-keyservers.net
+# If you are building this image locally and are getting `gpg: keyserver receive failed: No data` errors,
+# run the build with a different PGP_KEYSERVER, e.g. docker build --tag rabbitmq:3.7 --build-arg PGP_KEYSERVER=pgpkeys.eu 3.7/ubuntu
+# For context, see https://github.com/docker-library/official-images/issues/4252
+
+# Using the latest OpenSSL LTS release, with support until September 2023 - https://www.openssl.org/source/
+ENV OPENSSL_VERSION 1.1.1a
+ENV OPENSSL_SOURCE_SHA256="fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41"
+# https://www.openssl.org/community/omc.html
+ENV OPENSSL_PGP_KEY_ID="0x8657ABB260F056B1E5190839D9C4D26D0E604491"
+
+# Use the latest stable Erlang/OTP release (https://github.com/erlang/otp/tags)
+ENV OTP_VERSION 21.2.3
+# TODO add PGP checking when the feature will be added to Erlang/OTP's build system
+# http://erlang.org/pipermail/erlang-questions/2019-January/097067.html
+ENV OTP_SOURCE_SHA256="109a5722e398bdcd3aeb4f4833cde90bf441a9c014006439643aab550a770923"
+
+# Install dependencies required to build Erlang/OTP from source
+# http://erlang.org/doc/installation_guide/INSTALL.html
+# autoconf: Required to configure Erlang/OTP before compiling
+# dpkg-dev: Required to set up host & build type when compiling Erlang/OTP
+# gnupg: Required to verify OpenSSL artefacts
+# libncurses5-dev: Required for Erlang/OTP new shell & observer_cli - https://github.com/zhongwencool/observer_cli
+# m4: Required for Erlang/OTP HiPE support
 RUN set -eux; \
 	\
-	fetchDeps=' \
+	savedAptMark="$(apt-mark showmanual)"; \
+	apt-get update; \
+	apt-get install --yes --no-install-recommends \
+		autoconf \
 		ca-certificates \
+		dpkg-dev \
+		gcc \
+		gnupg \
+		libncurses5-dev \
+		m4 \
+		make \
 		wget \
-	'; \
-	apt-get update; \
-	apt-get install -y --no-install-recommends $fetchDeps; \
+	; \
 	rm -rf /var/lib/apt/lists/*; \
 	\
-	dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
-	wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
-	wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
+	OPENSSL_SOURCE_URL="https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz"; \
+	OPENSSL_PATH="/usr/local/src/openssl-$OPENSSL_VERSION"; \
+	OPENSSL_CONFIG_DIR=/usr/local/etc/ssl; \
 	\
-# verify the signature
+# Required by the crypto & ssl Erlang/OTP applications
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$OPENSSL_PATH.tar.gz" "$OPENSSL_SOURCE_URL"; \
 	export GNUPGHOME="$(mktemp -d)"; \
-	gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
-	gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
-	command -v gpgconf && gpgconf --kill all || :; \
-	rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$OPENSSL_PGP_KEY_ID"; \
+	gpg --batch --verify "$OPENSSL_PATH.tar.gz.asc" "$OPENSSL_PATH.tar.gz"; \
+	gpgconf --kill all; \
+	rm -rf "$GNUPGHOME"; \
+	echo "$OPENSSL_SOURCE_SHA256 *$OPENSSL_PATH.tar.gz" | sha256sum --check --strict -; \
+	mkdir -p "$OPENSSL_PATH"; \
+	tar --extract --file "$OPENSSL_PATH.tar.gz" --directory "$OPENSSL_PATH" --strip-components 1; \
 	\
-	chmod +x /usr/local/bin/gosu; \
-# verify that the binary works
-	gosu nobody true; \
+# Configure OpenSSL for compilation
+	cd "$OPENSSL_PATH"; \
+	./config --openssldir="$OPENSSL_CONFIG_DIR"; \
+# Compile, install OpenSSL, verify that the command-line works & development headers are present
+	make -j "$(getconf _NPROCESSORS_ONLN)"; \
+	make install_sw install_ssldirs; \
+	cd ..; \
+	rm -rf "$OPENSSL_PATH"*; \
+	ldconfig; \
+# use Debian's CA certificates
+	rmdir "$OPENSSL_CONFIG_DIR/certs" "$OPENSSL_CONFIG_DIR/private"; \
+	ln -sf /etc/ssl/certs /etc/ssl/private "$OPENSSL_CONFIG_DIR"; \
+# smoke test
+	openssl version; \
 	\
-	apt-get purge -y --auto-remove $fetchDeps
-
-# RabbitMQ 3.6.15+ requires Erlang 19.3+ (and Stretch only has 19.2); https://www.rabbitmq.com/which-erlang.html
-# so we'll pull Erlang from Buster instead (not using Erlang Solutions since their multiarch support is extremely limited)
-RUN set -eux; \
-# add buster sources.list
-	sed 's/stretch/buster/g' /etc/apt/sources.list \
-		| tee /etc/apt/sources.list.d/buster.list; \
-# update apt-preferences such that we get only erlang* from buster (and erlang* only from buster)
-	{ \
-		echo 'Package: *'; \
-		echo 'Pin: release n=buster*'; \
-		echo 'Pin-Priority: 1'; \
-		echo; \
-		echo 'Package: erlang*'; \
-		echo 'Pin: release n=buster*'; \
-		echo 'Pin-Priority: 999'; \
-		echo; \
-		echo 'Package: erlang*'; \
-		echo 'Pin: release n=stretch*'; \
-		echo 'Pin-Priority: -10'; \
-	} | tee /etc/apt/preferences.d/buster-erlang
-
-# install Erlang
-RUN set -eux; \
-	apt-get update; \
-# "erlang-base-hipe" is optional (and only supported on a few arches)
-# so, only install it if it's available for our current arch
-	if apt-cache show erlang-base-hipe 2>/dev/null | grep -q 'Package: erlang-base-hipe'; then \
-		apt-get install -y --no-install-recommends \
-			erlang-base-hipe \
+	OTP_SOURCE_URL="https://github.com/erlang/otp/archive/OTP-$OTP_VERSION.tar.gz"; \
+	OTP_PATH="/usr/local/src/otp-$OTP_VERSION"; \
+	\
+# Download, verify & extract OTP_SOURCE
+	mkdir -p "$OTP_PATH"; \
+	wget --progress dot:giga --output-document "$OTP_PATH.tar.gz" "$OTP_SOURCE_URL"; \
+	echo "$OTP_SOURCE_SHA256 *$OTP_PATH.tar.gz" | sha256sum --check --strict -; \
+	tar --extract --file "$OTP_PATH.tar.gz" --directory "$OTP_PATH" --strip-components 1; \
+	\
+# Configure Erlang/OTP for compilation, disable unused features & applications
+# http://erlang.org/doc/applications.html
+# ERL_TOP is required for Erlang/OTP makefiles to find the absolute path for the installation
+	cd "$OTP_PATH"; \
+	export ERL_TOP="$OTP_PATH"; \
+	./otp_build autoconf; \
+	CFLAGS="$(dpkg-buildflags --get CFLAGS)"; export CFLAGS; \
+	hostArch="$(dpkg-architecture --query DEB_HOST_GNU_TYPE)"; \
+	buildArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
+	./configure \
+		--host="$hostArch" \
+		--build="$buildArch" \
+		--disable-dynamic-ssl-lib \
+		--disable-sctp \
+		--disable-silent-rules \
+		--enable-clock-gettime \
+		--enable-hipe \
+		--enable-hybrid-heap \
+		--enable-kernel-poll \
+		--enable-shared-zlib \
+		--enable-smp-support \
+		--enable-threads \
+		--with-microstate-accounting=extra \
+		--without-common_test \
+		--without-debugger \
+		--without-dialyzer \
+		--without-diameter \
+		--without-edoc \
+		--without-erl_docgen \
+		--without-erl_interface \
+		--without-et \
+		--without-eunit \
+		--without-ftp \
+		--without-jinterface \
+		--without-megaco \
+		--without-observer \
+		--without-odbc \
+		--without-reltool \
+		--without-ssh \
+		--without-tftp \
+		--without-wx \
 	; \
-	fi; \
-# we start with "erlang-base-hipe" because it and "erlang-base" (non-hipe) are exclusive
-	apt-get install -y --no-install-recommends \
-		erlang-asn1 \
-		erlang-crypto \
-		erlang-eldap \
-		erlang-inets \
-		erlang-mnesia \
-		erlang-nox \
-		erlang-os-mon \
-		erlang-public-key \
-		erlang-ssl \
-		erlang-xmerl \
+# Compile & install Erlang/OTP
+	make -j "$(getconf _NPROCESSORS_ONLN)" GEN_OPT_FLGS="-O2 -fno-strict-aliasing"; \
+	make install; \
+	cd ..; \
+	rm -rf "$OTP_PATH"* /usr/local/lib/erlang/lib/*/src; \
+	\
+# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
+	apt-mark auto '.*' > /dev/null; \
+	[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \
+	find /usr/local -type f -executable -exec ldd '{}' ';' \
+		| awk '/=>/ { print $(NF-1) }' \
+		| sort -u \
+		| xargs -r dpkg-query --search \
+		| cut -d: -f1 \
+		| sort -u \
+		| xargs -r apt-mark manual \
 	; \
-	rm -rf /var/lib/apt/lists/*
-
-# get logs to stdout (thanks @dumbbell for pushing this upstream! :D)
-ENV RABBITMQ_LOGS=- RABBITMQ_SASL_LOGS=-
-# https://github.com/rabbitmq/rabbitmq-server/commit/53af45bf9a162dec849407d114041aad3d84feaf
-
-# /usr/sbin/rabbitmq-server has some irritating behavior, and only exists to "su - rabbitmq /usr/lib/rabbitmq/bin/rabbitmq-server ..."
-ENV PATH /usr/lib/rabbitmq/bin:$PATH
-
-# gpg: key 6026DFCA: public key "RabbitMQ Release Signing Key <info@rabbitmq.com>" imported
-ENV RABBITMQ_GPG_KEY 0A9AF2115F4687BD29803A206B73A36E6026DFCA
-
-ENV RABBITMQ_VERSION 3.7.8
-ENV RABBITMQ_GITHUB_TAG v3.7.8
-ENV RABBITMQ_DEBIAN_VERSION 3.7.8-1
+	apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+	\
+# Check that OpenSSL still works after purging build dependencies
+	openssl version; \
+# Check that Erlang/OTP crypto & ssl were compiled against OpenSSL correctly
+	erl -noshell -eval 'io:format("~p~n~n~p~n~n", [crypto:supports(), ssl:versions()]), init:stop().'
 
+ENV RABBITMQ_DATA_DIR=/var/lib/rabbitmq
+# Create rabbitmq system user & group, fix permissions & allow root user to connect to the RabbitMQ Erlang VM
+RUN set -eux; \
+	groupadd --gid 999 --system rabbitmq; \
+	useradd --uid 999 --system --home-dir "$RABBITMQ_DATA_DIR" --gid rabbitmq rabbitmq; \
+	mkdir -p "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chown -fR rabbitmq:rabbitmq "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	chmod 777 "$RABBITMQ_DATA_DIR" /etc/rabbitmq /tmp/rabbitmq-ssl /var/log/rabbitmq; \
+	ln -sf "$RABBITMQ_DATA_DIR/.erlang.cookie" /root/.erlang.cookie
+
+# Use the latest stable RabbitMQ release (https://www.rabbitmq.com/download.html)
+ENV RABBITMQ_VERSION 3.7.10
+# https://www.rabbitmq.com/signatures.html#importing-gpg
+ENV RABBITMQ_PGP_KEY_ID="0x0A9AF2115F4687BD29803A206B73A36E6026DFCA"
+ENV RABBITMQ_HOME=/opt/rabbitmq
+
+# Add RabbitMQ to PATH, send all logs to TTY
+ENV PATH=$RABBITMQ_HOME/sbin:$PATH \
+	RABBITMQ_LOGS=- RABBITMQ_SASL_LOGS=-
+
+# Install RabbitMQ
 RUN set -eux; \
 	\
+	savedAptMark="$(apt-mark showmanual)"; \
 	apt-get update; \
-	apt-get install -y --no-install-recommends ca-certificates wget; \
+	apt-get install --yes --no-install-recommends \
+		ca-certificates \
+		gnupg \
+		wget \
+		xz-utils \
+	; \
+	rm -rf /var/lib/apt/lists/*; \
 	\
-	wget -O rabbitmq-server.deb.asc "https://github.com/rabbitmq/rabbitmq-server/releases/download/$RABBITMQ_GITHUB_TAG/rabbitmq-server_${RABBITMQ_DEBIAN_VERSION}_all.deb.asc"; \
-	wget -O rabbitmq-server.deb     "https://github.com/rabbitmq/rabbitmq-server/releases/download/$RABBITMQ_GITHUB_TAG/rabbitmq-server_${RABBITMQ_DEBIAN_VERSION}_all.deb"; \
+	RABBITMQ_SOURCE_URL="https://github.com/rabbitmq/rabbitmq-server/releases/download/v$RABBITMQ_VERSION/rabbitmq-server-generic-unix-$RABBITMQ_VERSION.tar.xz"; \
+	RABBITMQ_PATH="/usr/local/src/rabbitmq-$RABBITMQ_VERSION"; \
 	\
-	apt-get purge -y --auto-remove ca-certificates wget; \
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_SOURCE_URL.asc"; \
+	wget --progress dot:giga --output-document "$RABBITMQ_PATH.tar.xz" "$RABBITMQ_SOURCE_URL"; \
 	\
 	export GNUPGHOME="$(mktemp -d)"; \
-	gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$RABBITMQ_GPG_KEY"; \
-	gpg --batch --verify rabbitmq-server.deb.asc rabbitmq-server.deb; \
-	command -v gpgconf && gpgconf --kill all || :; \
+	gpg --batch --keyserver "$PGP_KEYSERVER" --recv-keys "$RABBITMQ_PGP_KEY_ID"; \
+	gpg --batch --verify "$RABBITMQ_PATH.tar.xz.asc" "$RABBITMQ_PATH.tar.xz"; \
+	gpgconf --kill all; \
 	rm -rf "$GNUPGHOME"; \
 	\
-	apt install -y --no-install-recommends ./rabbitmq-server.deb; \
-	dpkg -l | grep rabbitmq-server; \
-	rm -f rabbitmq-server.deb*; \
+	mkdir -p "$RABBITMQ_HOME"; \
+	tar --extract --file "$RABBITMQ_PATH.tar.xz" --directory "$RABBITMQ_HOME" --strip-components 1; \
+	rm -rf "$RABBITMQ_PATH"*; \
+# Do not default SYS_PREFIX to RABBITMQ_HOME, leave it empty
+	grep -qE '^SYS_PREFIX=\$\{RABBITMQ_HOME\}$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	sed -i 's/^SYS_PREFIX=.*$/SYS_PREFIX=/' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	grep -qE '^SYS_PREFIX=$' "$RABBITMQ_HOME/sbin/rabbitmq-defaults"; \
+	chown -R rabbitmq:rabbitmq "$RABBITMQ_HOME"; \
 	\
-	rm -rf /var/lib/apt/lists/*
-
-# warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
-ENV LANG C.UTF-8
+	apt-mark auto '.*' > /dev/null; \
+	apt-mark manual $savedAptMark; \
+	apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+	\
+# verify assumption of no stale cookies
+	[ ! -e "$RABBITMQ_DATA_DIR/.erlang.cookie" ]; \
+# Ensure RabbitMQ was installed correctly by running a few commands that do not depend on a running server, as the rabbitmq user
+# If they all succeed, it's safe to assume that things have been set up correctly
+	gosu rabbitmq rabbitmqctl help; \
+	gosu rabbitmq rabbitmqctl list_ciphers; \
+	gosu rabbitmq rabbitmq-plugins list; \
+# no stale cookies
+	rm "$RABBITMQ_DATA_DIR/.erlang.cookie"
+
+# Added for backwards compatibility - users can simply COPY custom plugins to /plugins
+RUN ln -sf /opt/rabbitmq/plugins /plugins
 
 # set home so that any `--user` knows where to put the erlang cookie
-ENV HOME /var/lib/rabbitmq
-
-RUN mkdir -p /var/lib/rabbitmq /etc/rabbitmq /var/log/rabbitmq /tmp/rabbitmq-ssl \
-	&& chown -R rabbitmq:rabbitmq /var/lib/rabbitmq /etc/rabbitmq /var/log/rabbitmq /tmp/rabbitmq-ssl \
-	&& chmod -R 777 /var/lib/rabbitmq /etc/rabbitmq /var/log/rabbitmq /tmp/rabbitmq-ssl
-VOLUME /var/lib/rabbitmq
+ENV HOME $RABBITMQ_DATA_DIR
+# Hint that the data (a.k.a. home dir) dir should be separate volume
+VOLUME $RABBITMQ_DATA_DIR
 
-# add a symlink to the .erlang.cookie in /root so we can "docker exec rabbitmqctl ..." without gosu
-RUN ln -sf /var/lib/rabbitmq/.erlang.cookie /root/
-
-RUN ln -sf "/usr/lib/rabbitmq/lib/rabbitmq_server-$RABBITMQ_VERSION/plugins" /plugins
+# warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
+# Setting all environment variables that control language preferences, behaviour differs - https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable
+# https://docs.docker.com/samples/library/ubuntu/#locales
+ENV LANG=C.UTF-8 LANGUAGE=C.UTF-8 LC_ALL=C.UTF-8
 
 COPY docker-entrypoint.sh /usr/local/bin/
-RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat
 ENTRYPOINT ["docker-entrypoint.sh"]
 
 EXPOSE 4369 5671 5672 25672
diff --git a/rabbitmq_latest/docker-entrypoint.sh b/rabbitmq_latest/docker-entrypoint.sh
index 38406a5..a1bdb3d 100755
--- a/rabbitmq_latest/docker-entrypoint.sh
+++ b/rabbitmq_latest/docker-entrypoint.sh
@@ -365,7 +365,7 @@ if [ "$1" = 'rabbitmq-server' ] && [ "$shouldWriteConfig" ]; then
 
 	# if management plugin is installed, generate config for it
 	# https://www.rabbitmq.com/management.html#configuration
-	if [ "$(rabbitmq-plugins list -m -e rabbitmq_management)" ]; then
+	if [ "$(rabbitmq-plugins list -q -m -e rabbitmq_management)" ]; then
 		if [ "$haveManagementSslConfig" ]; then
 			rabbit_set_config 'management.listener.port' 15671
 			rabbit_set_config 'management.listener.ssl' 'true'
diff --git a/rabbitmq_management/Dockerfile b/rabbitmq_management/Dockerfile
index 4d77a07..759f48e 100644
--- a/rabbitmq_management/Dockerfile
+++ b/rabbitmq_management/Dockerfile
@@ -21,9 +21,7 @@ RUN set -eux; \
 	' -- /plugins/rabbitmq_management-*.ez > /usr/local/bin/rabbitmqadmin; \
 	[ -s /usr/local/bin/rabbitmqadmin ]; \
 	chmod +x /usr/local/bin/rabbitmqadmin; \
-	apt-get update; \
-	apt-get install -y --no-install-recommends python; \
-	rm -rf /var/lib/apt/lists/*; \
+	apt-get update; apt-get install -y --no-install-recommends python; rm -rf /var/lib/apt/lists/*; \
 	rabbitmqadmin --version
 
 EXPOSE 15671 15672

@yosifkit
Copy link
Member

Build test of #5364; 0c904ba; amd64 (rabbitmq):

$ bashbrew build rabbitmq:3.7.11-rc.2
Building bashbrew/cache:9f237e6890744afb3088a644c9333e12577de1c9731441881e5613b85c5476ee (rabbitmq:3.7.11-rc.2)
Tagging rabbitmq:3.7.11-rc.2
Tagging rabbitmq:3.7-rc

$ test/run.sh rabbitmq:3.7.11-rc.2
testing rabbitmq:3.7.11-rc.2
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed


$ bashbrew build rabbitmq:3.7.11-rc.2-management
Building bashbrew/cache:34e13cdc842ed3a6f63c1a139585468ccb67159bfa26aa2a1bcf94b4bbf47381 (rabbitmq:3.7.11-rc.2-management)
Tagging rabbitmq:3.7.11-rc.2-management
Tagging rabbitmq:3.7-rc-management

$ test/run.sh rabbitmq:3.7.11-rc.2-management
testing rabbitmq:3.7.11-rc.2-management
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed


$ bashbrew build rabbitmq:3.7.11-rc.2-alpine
Building bashbrew/cache:f8c38f5f353cb9b84196617df43e5eeeb13f44d39e0d493a7840fd7c5df154c9 (rabbitmq:3.7.11-rc.2-alpine)
Tagging rabbitmq:3.7.11-rc.2-alpine
Tagging rabbitmq:3.7-rc-alpine

$ test/run.sh rabbitmq:3.7.11-rc.2-alpine
testing rabbitmq:3.7.11-rc.2-alpine
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed


$ bashbrew build rabbitmq:3.7.11-rc.2-management-alpine
Building bashbrew/cache:40fac34ef686031de8e5c52c6acd95795d27dfd6c48412331024083b127a3e98 (rabbitmq:3.7.11-rc.2-management-alpine)
Tagging rabbitmq:3.7.11-rc.2-management-alpine
Tagging rabbitmq:3.7-rc-management-alpine

$ test/run.sh rabbitmq:3.7.11-rc.2-management-alpine
testing rabbitmq:3.7.11-rc.2-management-alpine
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed


$ bashbrew build rabbitmq:3.7.10
Building bashbrew/cache:01b85768c8aa96bfc2fdea19b0b8950caf1a9a989fa70c632cac3e6e30195437 (rabbitmq:3.7.10)
Tagging rabbitmq:3.7.10
Tagging rabbitmq:3.7
Tagging rabbitmq:3
Tagging rabbitmq:latest

$ test/run.sh rabbitmq:3.7.10
testing rabbitmq:3.7.10
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed


$ bashbrew build rabbitmq:3.7.10-management
Building bashbrew/cache:955e56cd9cb2b1eca06475efa659de971f35d599ddcc4173396090d75c4562fe (rabbitmq:3.7.10-management)
Tagging rabbitmq:3.7.10-management
Tagging rabbitmq:3.7-management
Tagging rabbitmq:3-management
Tagging rabbitmq:management

$ test/run.sh rabbitmq:3.7.10-management
testing rabbitmq:3.7.10-management
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed


$ bashbrew build rabbitmq:3.7.10-alpine
Building bashbrew/cache:95f9230f07a65ae7f312adc83fe75a651c9f67227b3601209b738abe1e96c65a (rabbitmq:3.7.10-alpine)
Tagging rabbitmq:3.7.10-alpine
Tagging rabbitmq:3.7-alpine
Tagging rabbitmq:3-alpine
Tagging rabbitmq:alpine

$ test/run.sh rabbitmq:3.7.10-alpine
testing rabbitmq:3.7.10-alpine
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed


$ bashbrew build rabbitmq:3.7.10-management-alpine
Building bashbrew/cache:accf44b02f6e1bfc86c340911eca0619c48660a286de3c1982c9262503812ef3 (rabbitmq:3.7.10-management-alpine)
Tagging rabbitmq:3.7.10-management-alpine
Tagging rabbitmq:3.7-management-alpine
Tagging rabbitmq:3-management-alpine
Tagging rabbitmq:management-alpine

$ test/run.sh rabbitmq:3.7.10-management-alpine
testing rabbitmq:3.7.10-management-alpine
	'utc' [1/5]...passed
	'cve-2014--shellshock' [2/5]...passed
	'no-hard-coded-passwords' [3/5]...passed
	'override-cmd' [4/5]...passed
	'rabbitmq-basics' [5/5].....passed

@yosifkit yosifkit merged commit 8997037 into docker-library:master Jan 30, 2019
@yosifkit yosifkit deleted the rabbitmq branch January 30, 2019 01:08
@BorisSK
Copy link

BorisSK commented Jan 31, 2019

Hi guys,

first of all thanks for maintaining the docker images of RMQ for us.

I would like to give you friendly feedback regarding last micro update of 3.7.8-management to 3.7.10-management. This micro release unfortunately is not backward compatible:

  • default plugin path has changed (from /usr/lib/rabbitmq/plugins to /opt/rabbitmq/plugins)
bpilka@bpilka-laptop:~$ docker exec rmq3710 rabbitmqctl eval 'application:get_env(rabbit, plugins_dir).'
{ok,"/opt/rabbitmq/plugins"}
bpilka@bpilka-laptop:~$ docker exec rmq378 rabbitmqctl eval 'application:get_env(rabbit, plugins_dir).'
{ok,"/usr/lib/rabbitmq/plugins:/usr/lib/rabbitmq/lib/rabbitmq_server-3.7.8/plugins"}
  • symlink of docker-entrypoint.sh was removed
 COPY docker-entrypoint.sh /usr/local/bin/
-RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat
 ENTRYPOINT ["docker-entrypoint.sh"]

It took me few hours today to fix related issued in our infra, so maybe this will save time of someone else.

@gerhard
Copy link

gerhard commented Feb 1, 2019

Hi @BorisSK, thanks for the ping 😉

Why is the default RabbitMQ plugin path important for you? My understanding is that /plugins is the public interface to RabbitMQ plugins path: https://github.com/docker-library/rabbitmq/blob/4320a3fdbf9f189ee29dc03abf3df82180459f12/3.7/ubuntu/Dockerfile#L229-L230

Why is /docker-entrypoint.sh important to you?

@BorisSK
Copy link

BorisSK commented Feb 4, 2019

hi Gerhard,

no problem. :) We use RMQ image is as follows:

  • Dockerfile snippet:
FROM rabbitmq:3.7-management
.
.
.
# Install Delayed Message Exchange plugin 
WORKDIR /opt/rabbitmq/plugins
RUN curl -f -s -m 1 \
  https://dl.bintray.com/rabbitmq/community-plugins/3.7.x/rabbitmq_delayed_message_exchange/rabbitmq_delayed_message_exchange-20171201-3.7.x.zip \
  -o rabbitmq_delayed_message_exchange-20171201-3.7.x.zip
RUN unzip rabbitmq_delayed_message_exchange-20171201-3.7.x.zip
RUN rm -f rabbitmq_delayed_message_exchange-20171201-3.7.x.zip
RUN rabbitmq-plugins enable rabbitmq_delayed_message_exchange

# We need to run additional commands once the server is running, hence init.sh used
ADD init.sh /
ADD config_rabbit.sh /
RUN chmod +x /init.sh /config_rabbit.sh
ENTRYPOINT ["/init.sh"]
  • init.sh snippet :
#!/bin/bash

# we add users, declare queues, bindings etc. in this script
/config_rabbit.sh &

# Launch
/usr/local/bin/docker-entrypoint.sh rabbitmq-server

To answer your questions:

  • change of the default plugin path is causing {:plugins_not_found, [:rabbitmq_delayed_message_exchange]} error when attempting to enable it in Dockerfile
  • as location of docker-entrypoint.sh has changed, init.sh was failing as well

@gerhard
Copy link

gerhard commented Feb 4, 2019

What do you think about this Dockerfile change for installing community plugins?

# Install Delayed Message Exchange plugin 
- WORKDIR /opt/rabbitmq/plugins
+ WORKDIR /plugins
RUN curl -f -s -m 1 \
  https://dl.bintray.com/rabbitmq/community-plugins/3.7.x/rabbitmq_delayed_message_exchange/rabbitmq_delayed_message_exchange-20171201-3.7.x.zip \
  -o rabbitmq_delayed_message_exchange-20171201-3.7.x.zip

/plugins is part of image's public interface. /var/lib/rabbitmq/plugins & /opt/rabbitmq/plugins are internal, private details.

As for the docker-entrypoint.sh, this feels less coupled to the upstream image:

# We need to run additional commands once the server is running, hence init.sh used
- ADD init.sh /
+ # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy
+ COPY init_rabbit.sh /usr/local/bin/
- ADD config_rabbit.sh /
+ COPY config_rabbit.sh /usr/local/bin/
- RUN chmod +x /init.sh /config_rabbit.sh
+ RUN cd /usr/local/bin/ && chmod +x init_rabbit.sh config_rabbit.sh
- ENTRYPOINT ["/init.sh"]
+ ENTRYPOINT ["init_rabbit.sh"]

Then in init_rabbit.sh:

#!/bin/bash

# we add users, declare queues, bindings etc. in this script
- /config_rabbit.sh &
+ config_rabbit.sh &

# Launch
- /usr/local/bin/docker-entrypoint.sh rabbitmq-server
+ docker-entrypoint.sh rabbitmq-server

What do you think?

@BorisSK
Copy link

BorisSK commented Feb 4, 2019

Surely, it's possible to fix the issue the way you describe. Indeed, I've done something similar. My point is, that I would not expect that such change will be required. I would think that PATCH update will be backward-compatible without any end-user modifications needed. That's all. :)

@gerhard
Copy link

gerhard commented Feb 4, 2019

The version that the Docker images use are tracking the product that they wrap - in this case RabbitMQ, they are not meant to be used as semver for the image itself.

It's difficult to capture public vs private interface in something like a Docker image, but we should certainly try. From everything I've learned here, I am proposing the following changes:

  1. Document how to add community plugins, after Enabling Plugins
  2. Document how to extend the Docker image, mention $PATH instead of absolute locations

Does the above sound reasonable?

@BorisSK
Copy link

BorisSK commented Feb 4, 2019

Thank you Gerhard, that sounds good.

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

Successfully merging this pull request may close these issues.

5 participants