From 72a8755399488ed88a839f123e34da11b7337bf7 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Mon, 18 May 2026 10:23:37 -0400 Subject: [PATCH] docker: bootstrap config and run rootless --- docker-compose.yml | 6 +++++- docker-entrypoint.sh | 38 ++++++++++++++++++++++++++++++++++++++ dockerfile | 32 ++++++++++++++++++++++++++++---- 3 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 docker-entrypoint.sh diff --git a/docker-compose.yml b/docker-compose.yml index 1048993..3465abc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,10 @@ services: pymc-repeater: - build: . + build: + context: . + args: + PUID: ${PUID:-1000} + PGID: ${PGID:-1000} container_name: pymc-repeater restart: unless-stopped ports: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..02b9380 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -eu + +INSTALL_DIR="${INSTALL_DIR:-/opt/pymc_repeater}" +CONFIG_DIR="${CONFIG_DIR:-/etc/pymc_repeater}" +CONFIG_PATH="${PYMC_REPEATER_CONFIG:-${CONFIG_DIR}/config.yaml}" +EXAMPLE_PATH="${CONFIG_DIR}/config.yaml.example" +BUNDLED_EXAMPLE_PATH="${INSTALL_DIR}/config.yaml.example" +RUNTIME_USER="${USER:-repeater}" +RUNTIME_UID="${PUID:-unknown}" +RUNTIME_GID="${PGID:-unknown}" + +mkdir -p "${CONFIG_DIR}" + +copy_or_die() { + src="$1" + dest="$2" + if ! cp "${src}" "${dest}"; then + echo "Failed to initialize ${dest} from ${src}." >&2 + echo "If you are bind-mounting ./config.yaml, ensure the host path is writable by ${RUNTIME_USER} (${RUNTIME_UID}:${RUNTIME_GID})." >&2 + exit 1 + fi +} + +if [ ! -f "${EXAMPLE_PATH}" ] && [ -f "${BUNDLED_EXAMPLE_PATH}" ]; then + copy_or_die "${BUNDLED_EXAMPLE_PATH}" "${EXAMPLE_PATH}" +fi + +if [ -d "${CONFIG_PATH}" ]; then + if [ ! -s "${CONFIG_PATH}/config.yaml" ] && [ -f "${EXAMPLE_PATH}" ]; then + copy_or_die "${EXAMPLE_PATH}" "${CONFIG_PATH}/config.yaml" + fi + CONFIG_PATH="${CONFIG_PATH}/config.yaml" +elif [ ! -s "${CONFIG_PATH}" ] && [ -f "${EXAMPLE_PATH}" ]; then + copy_or_die "${EXAMPLE_PATH}" "${CONFIG_PATH}" +fi + +exec python3 -m repeater.main --config "${CONFIG_PATH}" diff --git a/dockerfile b/dockerfile index c32b466..c789d05 100644 --- a/dockerfile +++ b/dockerfile @@ -1,15 +1,23 @@ FROM python:3.12-slim-bookworm ARG PACKAGE_VERSION=1.0.5 +ARG USER=repeater +ARG GROUP=repeater +ARG PUID=15888 +ARG PGID=15888 ENV INSTALL_DIR=/opt/pymc_repeater \ CONFIG_DIR=/etc/pymc_repeater \ DATA_DIR=/var/lib/pymc_repeater \ + HOME_DIR=/home/${USER} \ + PATH=/home/${USER}/.local/bin:${PATH} \ PYTHONUNBUFFERED=1 \ - SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER=${PACKAGE_VERSION} + SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER=${PACKAGE_VERSION} \ + PUID=${PUID} \ + PGID=${PGID} # Install runtime dependencies only -RUN apt-get update && apt-get install -y \ +RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \ libffi-dev \ python3-rrdtool \ jq \ @@ -21,20 +29,36 @@ RUN apt-get update && apt-get install -y \ python3-dev \ && rm -rf /var/lib/apt/lists/* +# Create the group and user in order to run without root privileges +RUN groupadd --gid "$PGID" "$GROUP" \ + && useradd --uid "$PUID" --gid "$PGID" --home-dir "$HOME_DIR" --create-home --shell /usr/bin/bash "$USER" + # Create runtime directories -RUN mkdir -p ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR} +RUN mkdir -p ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR} \ + && chown -R "$USER":"$GROUP" ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR} ${HOME_DIR} WORKDIR ${INSTALL_DIR} # Copy source COPY repeater ./repeater COPY pyproject.toml . +COPY config.yaml.example . COPY radio-presets.json . COPY radio-settings.json . +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +# Switch to the unprivileged runtime user +USER ${USER} # Install package RUN pip install --no-cache-dir . +USER root + +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +USER ${USER} + EXPOSE 8000 -ENTRYPOINT ["python3", "-m", "repeater.main", "--config", "/etc/pymc_repeater/config.yaml"] +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]