JVM | Platform | Status |
---|---|---|
OpenJDK (Temurin) Current | Linux | |
OpenJDK (Temurin) LTS | Linux | |
OpenJDK (Temurin) Current | Windows | |
OpenJDK (Temurin) LTS | Windows |
Better ActiveMQ Artemis OCI images.
The existing Artemis OCI images have a number of problems that are addressed
by the adelaide
images.
-
The images expect users to generate configuration files and then use them in the broker instances. This makes the container the "owner" of the configuration files instead of the files being a read-only input to the container, and makes it difficult to keep them in version control. Additionally, the images seem to be designed under the assumption that a user might want to run more than one broker in a container instance. Nobody sensible wants to do this. The
adelaide
images expose a single/data
volume for the single broker instance supported by the container, and expose a single/data/etc
volume to allow for configuration files to be read-only mounted in the container, and managed externally. -
The images expect to be run under Docker, and therefore do the usual idiotic "run as a separate UID inside the container" dance. This is a fundamental design flaw of Docker and is one of many reasons to exclusively use
podman
instead. Theadelaide
images do not do any manipulation of UIDs or GIDs; if you run theadelaide
image as UID 0 on the host, Artemis will run as UID 0 inside the container. Simply runpodman
as a non-root user on the host, and everything inside the container is guaranteed to run without privileges of any kind. Theadelaide
images do not support Docker; this is a feature and not a bug. -
The
adelaide
images are free of any platform-specific complexity. Noone sensible is hosting message broker containers on any platform other than Linux. -
The
adelaide
images are designed to be run with an entirely read-only filesystem (aside from the read/write mounted broker instance directory). This is intended to allow for greater reliability and security.
- Runs rootless with a read-only root directory!
- Version control your broker configuration files!
- Easy TLS reloading for ACME.
- ISC license.
Run podman
as an unprivileged user, with an invocation similar to:
podman run \
--read-only \
--volume '/path/to/host/tls:/tls:ro,z' \
--volume '/path/to/broker/data:/data:Z,rw' \
--volume '/path/to/broker/etc:/data/etc:Z,ro' \
--publish '...:61616:61616/tcp' \
quay.io/io7mcom/adelaide:${VERSION}
Naturally, the exact directories you mount, and the exact TCP ports you publish is dependent on your broker configuration.
Use the following volume mounts:
Mount | Description |
---|---|
/data |
Persistent broker data (the "broker instance") |
/data/etc |
Broker configuration files (such as broker.xml ) |
/tls |
A directory containing keystores. |
The /data
mount contains the persistent state of the one-and-only broker
instance used by the container.
The /data/etc
mount should be mounted read-only and should contain your
version-controlled broker configuration files (such as broker.xml
).
The /tls
mount should contain keystores and truststores.
For extra security, run podman run
with the --read-only
option; the images
are designed such that no part of the filesystem except for /data
is required
to be writable.
The image provides a /broker-tls-reload.sh
script that can be executed
inside the container to instruct Artemis to reload TLS certificates. We'll
assume that the broker container is being run under the _artemis
user
account on the host throughout this example.
# /broker-tls-reload.sh
usage: user password broker-name acceptor-name
We'll start by assuming that we have some client on the host that knows how to write PEM-formatted certificates into some directory (any ACME client can do this). We'll then periodically (hourly is sufficient) convert those PEM-formatted certificates to a PKCS12 formatted keystore and write the keystore to a path that is visible to the broker running inside the container. We'll then tell the broker to reload its own keystore. The steps are as follows:
- Configure your broker to read a keystore from
/tls/brokerKeystore.pkcs12
. For example, in thebroker.xml
:
<acceptor name="artemis">
tcp://0.0.0.0:60000?protocols=AMQP;sslEnabled=true;keyStorePath=/tls/brokerKeystore.p12;keyStorePassword=changeit;trustStorePath=/opt/java/openjdk/lib/security/cacerts;trustStorePassword=changeit
</acceptor>
- Write a script to generate
/tls/brokerKeystore.p12
from outside of the container. For the sake of example, we'll refer to this script as/usr/local/bin/regenerate-keystore.sh
on the host. For example, if an ACME client is placing certificates into the host directory/etc/certificates/example.com
, and the directory/containers/messaging01/tls
is mounted at/tls
in the container, then it is straightforward to write a script to produce a PKCS12 keystore:
#!/bin/sh -ex
CERTIFICATE_BASE="/etc/certificates/example.com"
OUTPUT="/containers/messaging01/tls"
openssl pkcs12 \
-export \
-out "${OUTPUT}/brokerKeystore.p12.tmp" \
-in "${CERTIFICATE_BASE}/full_chain.pem" \
-inkey "${CERTIFICATE_BASE}/private.key" \
-passout "pass:changeit"
chown _artemis:_artemis "${OUTPUT}/brokerKeystore.p12.tmp"
mv "${OUTPUT}/brokerKeystore.p12.tmp" "${OUTPUT}/brokerKeystore.p12"
- Set up a service to periodically call the script to generate the keystore,
and then call
/broker-tls-reload.sh
inside the container to reload certificates. In this example, we assume that the container is calledmessaging01
, and it exposes a broker calledMessaging01
with an acceptor calledartemis
. It also has an admin user calledgrouch
with a passwordsome-very-long-password-here
. Naturally, all of these values will likely be different for your particular broker installation.
[Unit]
Description=Messaging01 TLS Service
[Service]
Type=oneshot
User=_artemis
Group=_artemis
ExecStart=+/bin/sh /usr/local/bin/regenerate-keystore.sh
ExecStart=/usr/bin/podman \
exec \
-i \
-t \
messaging01 \
/broker-tls-reload.sh \
grouch \
some-very-long-password-here \
Messaging01 \
artemis
[Install]
WantedBy=multi-user.target
[Unit]
Description=Messaging01 TLS timer
[Timer]
OnCalendar=*-*-* *:00/59:00
Persistent=true
[Install]
WantedBy=timers.target