From 24513e9cf32ba56a7ad9c9da13aab8eda7734df2 Mon Sep 17 00:00:00 2001 From: Thomas Laubrock Date: Fri, 27 Feb 2026 13:40:58 +0000 Subject: [PATCH 1/2] non-root container with s6-overlay This using s6-overlay to manage processes need to run in the container. jsonrpc2-helper is migrated into the startscript. --- Dockerfile | 68 +++++++++++++++------ entrypoint.old.sh | 43 +++++++++++++ s6-services/signal-api/run | 3 + s6-services/signal-api/type | 1 + s6-services/signal-json-rpc/run | 67 ++++++++++++++++++++ s6-services/signal-json-rpc/type | 1 + s6-services/user/contents.d/signal-api | 0 s6-services/user/contents.d/signal-json-rpc | 0 s6-services/user/type | 1 + 9 files changed, 164 insertions(+), 20 deletions(-) create mode 100755 entrypoint.old.sh create mode 100644 s6-services/signal-api/run create mode 100644 s6-services/signal-api/type create mode 100644 s6-services/signal-json-rpc/run create mode 100644 s6-services/signal-json-rpc/type create mode 100644 s6-services/user/contents.d/signal-api create mode 100644 s6-services/user/contents.d/signal-json-rpc create mode 100644 s6-services/user/type diff --git a/Dockerfile b/Dockerfile index 96e845b2..dd5c8fce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,7 @@ RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ ENV JAVA_OPTS="-Djdk.lang.Process.launchMechanism=vfork" -ENV LANG en_US.UTF-8 +ENV LANG=en_US.UTF-8 #RUN cd /tmp/ \ # && git clone https://github.com/swaggo/swag.git swag-${SWAG_VERSION} \ @@ -155,41 +155,61 @@ RUN cd /tmp/signal-cli-rest-api-src && go build -buildmode=plugin -o signal-cli- # Start a fresh container for release container -# eclipse-temurin doesn't provide a OpenJDK 21 image for armv7 (see https://github.com/adoptium/containers/issues/502). Until this -# is fixed we use the standard ubuntu image -#FROM eclipse-temurin:21-jre-jammy +FROM debian:trixie-slim -FROM ubuntu:jammy +ARG TARGETARCH # set by buildx +ARG S6_OVERLAY_VERSION=v3.2.2.0 +ARG SIGNAL_CLI_VERSION +ARG BUILD_VERSION_ARG -ENV GIN_MODE=release +# Set environment variables to keep the image clean +ENV DEBIAN_FRONTEND=noninteractive ENV PORT=8080 -ARG SIGNAL_CLI_VERSION -ARG BUILD_VERSION_ARG - ENV BUILD_VERSION=$BUILD_VERSION_ARG ENV SIGNAL_CLI_REST_API_PLUGIN_SHARED_OBJ_DIR=/usr/bin/ -RUN dpkg-reconfigure debconf --frontend=noninteractive \ - && apt-get update \ - && apt-get install -y --no-install-recommends util-linux supervisor netcat openjdk-21-jre curl locales \ +RUN apt-get update \ + && apt-get install -y --no-install-recommends netcat-traditional openjdk-21-jre curl locales xz-utils\ + && apt-get clean \ && rm -rf /var/lib/apt/lists/* +RUN if [ -z "$TARGETARCH" ]; then \ + # Fallback for older Docker versions not using BuildKit + TARGETARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/'); \ + else \ + echo "Building for architecture: $TARGETARCH"; \ + fi; + +# install s6-overlay as service control system +RUN curl -fL -o /tmp/s6-overlay-noarch.tar.xz \ + "https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" && \ + tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz && \ + if [ "$TARGETARCH" = "amd64" ]; then S6_ARCH="x86_64"; \ + elif [ "$TARGETARCH" = "arm64" ]; then S6_ARCH="aarch64"; \ + elif [ "$TARGETARCH" = "arm" ]; then S6_ARCH="arm"; \ + else S6_ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/'); fi;\ + curl -fL -o /tmp/s6-overlay-bin.tar.xz \ + "https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz" && \ + tar -C / -Jxpf /tmp/s6-overlay-bin.tar.xz && \ + rm /tmp/s6-overlay-*.tar.xz + COPY --from=buildcontainer /tmp/signal-cli-rest-api-src/signal-cli-rest-api /usr/bin/signal-cli-rest-api COPY --from=buildcontainer /opt/signal-cli-${SIGNAL_CLI_VERSION} /opt/signal-cli-${SIGNAL_CLI_VERSION} COPY --from=buildcontainer /tmp/signal-cli-${SIGNAL_CLI_VERSION}-source/build/native/nativeCompile/signal-cli /opt/signal-cli-${SIGNAL_CLI_VERSION}/bin/signal-cli-native COPY --from=buildcontainer /tmp/signal-cli-rest-api-src/scripts/jsonrpc2-helper /usr/bin/jsonrpc2-helper COPY --from=buildcontainer /tmp/signal-cli-rest-api-src/signal-cli-rest-api_plugin_loader.so /usr/bin/signal-cli-rest-api_plugin_loader.so -COPY entrypoint.sh /entrypoint.sh RUN groupadd -g 1000 signal-api \ && useradd --no-log-init -M -d /home -s /bin/bash -u 1000 -g 1000 signal-api \ && ln -s /opt/signal-cli-${SIGNAL_CLI_VERSION}/bin/signal-cli /usr/bin/signal-cli \ && ln -s /opt/signal-cli-${SIGNAL_CLI_VERSION}/bin/signal-cli-native /usr/bin/signal-cli-native \ - && mkdir -p /signal-cli-config/ \ - && mkdir -p /home/.local/share/signal-cli + && mkdir -p /home/.local/share/signal-cli \ + && chown -R signal-api:signal-api /home + +COPY --chmod=755 ./s6-services/ /etc/s6-overlay/s6-rc.d/ # remove the temporary created signal-cli-native on armv7, as GRAALVM doesn't support 32bit RUN arch="$(uname -m)"; \ @@ -201,16 +221,24 @@ RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ dpkg-reconfigure --frontend=noninteractive locales && \ update-locale LANG=en_US.UTF-8 -ENV LANG en_US.UTF-8 +ENV LANG=en_US.UTF-8 EXPOSE ${PORT} ENV SIGNAL_CLI_CONFIG_DIR=/home/.local/share/signal-cli -ENV SIGNAL_CLI_UID=1000 -ENV SIGNAL_CLI_GID=1000 -ENV SIGNAL_CLI_CHOWN_ON_STARTUP=true -ENTRYPOINT ["/entrypoint.sh"] +RUN mkdir -p /tmp/s6-runtime && chown -R signal-api:signal-api /tmp/s6-runtime /etc/s6-overlay + +USER signal-api + +# Mandatory ENV for non-root s6 +ENV S6_RUNTIME_PATH=/tmp/s6-runtime +ENV S6_READ_ONLY_ROOT=1 +ENV S6_VERBOSITY=2 + +WORKDIR /home + +ENTRYPOINT ["/init"] HEALTHCHECK --interval=20s --timeout=10s --retries=3 \ CMD curl -f http://localhost:${PORT}/v1/health || exit 1 diff --git a/entrypoint.old.sh b/entrypoint.old.sh new file mode 100755 index 00000000..05363d3f --- /dev/null +++ b/entrypoint.old.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +set -x +set -e + +[ -z "${SIGNAL_CLI_CONFIG_DIR}" ] && echo "SIGNAL_CLI_CONFIG_DIR environmental variable needs to be set! Aborting!" && exit 1; + +usermod -u ${SIGNAL_CLI_UID} signal-api +groupmod -o -g ${SIGNAL_CLI_GID} signal-api + +# Fix permissions to ensure backward compatibility if SIGNAL_CLI_CHOWN_ON_STARTUP is not set to "false" +if [ "$SIGNAL_CLI_CHOWN_ON_STARTUP" != "false" ]; then + echo "Changing ownership of ${SIGNAL_CLI_CONFIG_DIR} to ${SIGNAL_CLI_UID}:${SIGNAL_CLI_GID}" + chown ${SIGNAL_CLI_UID}:${SIGNAL_CLI_GID} -R ${SIGNAL_CLI_CONFIG_DIR} +else + echo "Skipping chown on startup since SIGNAL_CLI_CHOWN_ON_STARTUP is set to 'false'" +fi + +# Show warning on docker exec +cat <> /root/.bashrc +echo "WARNING: signal-cli-rest-api runs as signal-api (not as root!)" +echo "Run 'su signal-api' before using signal-cli!" +echo "If you want to use signal-cli directly, don't forget to specify the config directory. e.g: \"signal-cli --config ${SIGNAL_CLI_CONFIG_DIR}\"" +EOF + +cap_prefix="-cap_" +caps="$cap_prefix$(seq -s ",$cap_prefix" 0 $(cat /proc/sys/kernel/cap_last_cap))" + +# TODO: check mode +if [ "$MODE" = "json-rpc" ] +then +/usr/bin/jsonrpc2-helper +if [ -n "$JAVA_OPTS" ] ; then + echo "export JAVA_OPTS='$JAVA_OPTS'" >> /etc/default/supervisor +fi +service supervisor start +supervisorctl start all +fi + +export HOST_IP=$(hostname -I | awk '{print $1}') + +# Start API as signal-api user +exec setpriv --reuid=${SIGNAL_CLI_UID} --regid=${SIGNAL_CLI_GID} --init-groups --inh-caps=$caps signal-cli-rest-api -signal-cli-config=${SIGNAL_CLI_CONFIG_DIR} diff --git a/s6-services/signal-api/run b/s6-services/signal-api/run new file mode 100644 index 00000000..e93491c7 --- /dev/null +++ b/s6-services/signal-api/run @@ -0,0 +1,3 @@ +#!/command/with-contenv sh +# Use with-contenv to import environment variables like SIGNAL_CLI_CONFIG_DIR +exec signal-cli-rest-api -signal-cli-config="${SIGNAL_CLI_CONFIG_DIR}" \ No newline at end of file diff --git a/s6-services/signal-api/type b/s6-services/signal-api/type new file mode 100644 index 00000000..1780f9f4 --- /dev/null +++ b/s6-services/signal-api/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/s6-services/signal-json-rpc/run b/s6-services/signal-json-rpc/run new file mode 100644 index 00000000..757612b8 --- /dev/null +++ b/s6-services/signal-json-rpc/run @@ -0,0 +1,67 @@ +#!/command/with-contenv sh +# File: /etc/s6-overlay/s6-rc.d/signal-json-rpc/run + +PIPE=/tmp/sigsocket1 +PORT=6001 + +if [ "$MODE" != "json-rpc" ]; then + echo "Running as mode: $MODE - skipping json-rpc setup" + sleep infinity # do nothing, but keep service running + exit 0 +fi + +## general parameters for signal-cli +# set SIGNAL_CLI_CONFIG_DIR if not set +if [ -z "$SIGNAL_CLI_CONFIG_DIR" ]; then + SIGNAL_CLI_CONFIG_DIR="/home/.local/share/signal-cli/" +fi + +jsonRpcConfig="${SIGNAL_CLI_CONFIG_DIR%/}/jsonrpc2.yml" + +# here file to write config +cat > "$jsonRpcConfig" <: + tcp_port: $PORT + fifo_pathname: $PIPE +EOL + +# Trust Identities +trustNewIdentitiesEnv="${JSON_RPC_TRUST_NEW_IDENTITIES:-}" +trustNewIdentities="" + +if [ "$trustNewIdentitiesEnv" = "on-first-use" ]; then + trustNewIdentities=" --trust-new-identities on-first-use" +elif [ "$trustNewIdentitiesEnv" = "always" ]; then + trustNewIdentities=" --trust-new-identities always" +elif [ "$trustNewIdentitiesEnv" = "never" ]; then + trustNewIdentities=" --trust-new-identities never" +elif [ -n "$trustNewIdentitiesEnv" ]; then + # This mirrors your log.Fatal check + echo "Invalid JSON_RPC_TRUST_NEW_IDENTITIES environment variable set!" >&2 + exit 1 +fi + +## parameters for jsonrpc mode +# Attachments +ignoreAttachments="${JSON_RPC_IGNORE_ATTACHMENTS:-}" +signalCliIgnoreAttachments="" +if [ "$ignoreAttachments" = "true" ]; then + signalCliIgnoreAttachments=" --ignore-attachments" +fi + +# Stories +ignoreStories="${JSON_RPC_IGNORE_STORIES:-}" +signalCliIgnoreStories="" +if [ "$ignoreStories" = "true" ]; then + signalCliIgnoreStories=" --ignore-stories" +fi + +# Load the pipe +[ -p "$PIPE" ] || mkfifo "$PIPE" + +# Ensure permissions are correct for non-root +chmod 600 "$PIPE" + +# Launch the circular communication +exec sh -c "nc -l -p "${PORT}" < $PIPE | signal-cli --output=json --config "${SIGNAL_CLI_CONFIG_DIR}" "${trustNewIdentities}" jsonRpc ${signalCliIgnoreAttachments} ${signalCliIgnoreStories} > $PIPE" \ No newline at end of file diff --git a/s6-services/signal-json-rpc/type b/s6-services/signal-json-rpc/type new file mode 100644 index 00000000..1780f9f4 --- /dev/null +++ b/s6-services/signal-json-rpc/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/s6-services/user/contents.d/signal-api b/s6-services/user/contents.d/signal-api new file mode 100644 index 00000000..e69de29b diff --git a/s6-services/user/contents.d/signal-json-rpc b/s6-services/user/contents.d/signal-json-rpc new file mode 100644 index 00000000..e69de29b diff --git a/s6-services/user/type b/s6-services/user/type new file mode 100644 index 00000000..3a8819be --- /dev/null +++ b/s6-services/user/type @@ -0,0 +1 @@ +bundle \ No newline at end of file From ab30ba5bba35a07ca9cb9dda5d656cc85b3d63c0 Mon Sep 17 00:00:00 2001 From: Thomas Laubrock Date: Wed, 4 Mar 2026 21:18:31 +0000 Subject: [PATCH 2/2] add GIN_MODE, moved S6_OVERLAY_VERSION to the top --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index dd5c8fce..68276d5c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,8 @@ ARG SWAG_VERSION=1.16.4 ARG GRAALVM_VERSION=21.0.0 #ARG GRAALVM_VERSION=25.0.2 +ARG S6_OVERLAY_VERSION=v3.2.2.0 + ARG BUILD_VERSION_ARG=unset FROM golang:1.24-bookworm AS buildcontainer @@ -154,13 +156,12 @@ RUN cd /tmp/signal-cli-rest-api-src/scripts && go build -o jsonrpc2-helper RUN cd /tmp/signal-cli-rest-api-src && go build -buildmode=plugin -o signal-cli-rest-api_plugin_loader.so plugin_loader.go # Start a fresh container for release container - FROM debian:trixie-slim ARG TARGETARCH # set by buildx -ARG S6_OVERLAY_VERSION=v3.2.2.0 ARG SIGNAL_CLI_VERSION ARG BUILD_VERSION_ARG +ENV GIN_MODE=release # Set environment variables to keep the image clean ENV DEBIAN_FRONTEND=noninteractive