diff --git a/CHANGELOG.md b/CHANGELOG.md index 80576a8a..9e709fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.4.8] - 2026-05-15 + +### Security + +- **Replaced `gosu` with `setpriv` in `entrypoint.sh` and dropped the `gosu` package from both GPU and CPU images.** Clears 50 Go stdlib CVEs (3 CRITICAL, 19 HIGH, 26 MEDIUM, 2 LOW) that Ubuntu 24.04's `gosu 1.17` carries because it was compiled against Go stdlib 1.22.2. `setpriv` ships with `util-linux` in the base image, has no Go runtime, and is the upstream-recommended gosu alternative. The privilege-drop flags match gosu's defaults: `--reuid=minuspod --regid=minuspod --init-groups --inh-caps=-all`. The two remaining torch CVEs (local DoS, LOW + MEDIUM) need a torch 2.6.0 -> 2.8.0 upgrade and are tracked separately. + ## [2.4.7] - 2026-05-15 ### Changed diff --git a/Dockerfile b/Dockerfile index 6fbe2d8a..29d051aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,8 +30,9 @@ FROM nvidia/cuda:12.9.1-runtime-ubuntu24.04 # Install Python 3.11 from deadsnakes PPA and system dependencies # Ubuntu 24.04 ships Python 3.12; we use deadsnakes to keep Python 3.11 -# gosu is used by entrypoint.sh to drop privileges after the root-only -# chown step that migrates the data volume on first boot. +# setpriv (from util-linux, present in the base image) is used by +# entrypoint.sh to drop privileges after the root-only chown step that +# migrates the data volume on first boot. RUN apt-get update && apt-get install -y --no-install-recommends \ software-properties-common \ && add-apt-repository ppa:deadsnakes/ppa \ @@ -41,7 +42,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ python3.11-venv \ ffmpeg \ curl \ - gosu \ libsndfile1 \ libchromaprint-tools \ && apt-get upgrade -y \ @@ -49,7 +49,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ /usr/lib/python3/dist-packages/PyJWT* \ /usr/lib/python3/dist-packages/jwt* \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ - && gosu nobody true + && setpriv --reuid=nobody --regid=nogroup --init-groups true # Set python3.11 as default, create venv for all pip installs # Venv avoids pip 26+ "uninstall-no-record-file" errors with system packages @@ -108,7 +108,7 @@ COPY entrypoint.sh /app/ # Set permissions - use find to recursively set permissions on subdirectories # IMPORTANT: glob pattern *.py does NOT match files in subdirectories! # Create a non-root minuspod user (UID/GID 1000) that entrypoint.sh drops -# privileges to via gosu. The container still starts as root so the +# privileges to via setpriv. The container still starts as root so the # entrypoint can chown the data volume on first boot; no app code runs # as root. UID/GID are overridable at runtime with APP_UID/APP_GID. RUN find ./src -type f -name '*.py' -exec chmod 644 {} \; && \ diff --git a/Dockerfile.cpu b/Dockerfile.cpu index 688b7941..61cd29d3 100644 --- a/Dockerfile.cpu +++ b/Dockerfile.cpu @@ -53,8 +53,9 @@ FROM ubuntu:24.04 # Install Python 3.11 from deadsnakes PPA and system dependencies # Ubuntu 24.04 ships Python 3.12; we use deadsnakes to keep Python 3.11 -# gosu is used by entrypoint.sh to drop privileges after the root-only -# chown step that migrates the data volume on first boot. +# setpriv (from util-linux, present in the base image) is used by +# entrypoint.sh to drop privileges after the root-only chown step that +# migrates the data volume on first boot. RUN apt-get update && apt-get install -y --no-install-recommends \ software-properties-common \ && add-apt-repository ppa:deadsnakes/ppa \ @@ -64,7 +65,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ python3.11-venv \ ffmpeg \ curl \ - gosu \ libsndfile1 \ libchromaprint-tools \ && apt-get upgrade -y \ @@ -72,7 +72,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ /usr/lib/python3/dist-packages/PyJWT* \ /usr/lib/python3/dist-packages/jwt* \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ - && gosu nobody true + && setpriv --reuid=nobody --regid=nogroup --init-groups true # Set python3.11 as default, create venv for all pip installs # Venv avoids pip 26+ "uninstall-no-record-file" errors with system packages @@ -133,7 +133,7 @@ COPY entrypoint.sh /app/ # Set permissions - use find to recursively set permissions on subdirectories # IMPORTANT: glob pattern *.py does NOT match files in subdirectories! # Create a non-root minuspod user (UID/GID 1000) that entrypoint.sh drops -# privileges to via gosu. The container still starts as root so the +# privileges to via setpriv. The container still starts as root so the # entrypoint can chown the data volume on first boot; no app code runs # as root. UID/GID are overridable at runtime with APP_UID/APP_GID. RUN find ./src -type f -name '*.py' -exec chmod 644 {} \; && \ diff --git a/README.md b/README.md index 8a21030b..04687ea3 100644 --- a/README.md +++ b/README.md @@ -1297,7 +1297,7 @@ All data is stored in the `./data` directory: ### Container user -Runs as UID 1000 (`minuspod`). First boot chowns the data volume, then drops privileges via `gosu`. Override with `APP_UID` / `APP_GID` if your host volume belongs to a different UID, or bypass entirely with `docker run --user `. +Runs as UID 1000 (`minuspod`). First boot chowns the data volume, then drops privileges via `setpriv` (from `util-linux`, present in the base image). Override with `APP_UID` / `APP_GID` if your host volume belongs to a different UID, or bypass entirely with `docker run --user `. ### Database backup sensitivity diff --git a/docker-compose.cpu.yml b/docker-compose.cpu.yml index 52887722..e3068cec 100644 --- a/docker-compose.cpu.yml +++ b/docker-compose.cpu.yml @@ -25,7 +25,7 @@ services: # dockerfile: Dockerfile.cpu platform: linux/amd64 # The container ships a non-root `minuspod` user at UID/GID 1000 - # (entrypoint.sh drops root via gosu after fixing volume ownership). + # (entrypoint.sh drops root via setpriv after fixing volume ownership). # Override here to pin a specific APP_UID/APP_GID if your volume is # owned by a different user -- e.g. `user: "2000:2000"`. # user: "1000:1000" diff --git a/docker-compose.yml b/docker-compose.yml index 33d68186..5e56811c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ services: image: ttlequals0/minuspod:${MINUSPOD_VERSION:-latest} platform: linux/amd64 # The container ships a non-root `minuspod` user at UID/GID 1000 - # (entrypoint.sh drops root via gosu after fixing volume ownership). + # (entrypoint.sh drops root via setpriv after fixing volume ownership). # Override here to pin a specific APP_UID/APP_GID if your volume is # owned by a different user -- e.g. `user: "2000:2000"`. Leave unset # to use the image default (1000:1000). diff --git a/entrypoint.sh b/entrypoint.sh index e0018355..45d745dc 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -6,8 +6,8 @@ # from a host path owned by a different UID (common on first run after a # docker compose pull, or after an operator recreates the volume). We fix # ownership, then drop privileges to the unprivileged minuspod user via -# gosu. Only root needs to live long enough to run this script; gunicorn -# runs as UID 1000 (or APP_UID / APP_GID if overridden). +# setpriv (util-linux). Only root needs to live long enough to run this +# script; gunicorn runs as UID 1000 (or APP_UID / APP_GID if overridden). # set -euo pipefail @@ -47,7 +47,8 @@ if [[ "$(id -u)" == "0" ]]; then chown "${APP_UID}:${APP_GID}" /app/gunicorn.conf.py 2>/dev/null || true cd /app/src - exec gosu minuspod gunicorn -c /app/gunicorn.conf.py main_app:app + exec setpriv --reuid=minuspod --regid=minuspod --init-groups --inh-caps=-all -- \ + gunicorn -c /app/gunicorn.conf.py main_app:app fi # Non-root invocation path (operator used --user). Run directly. diff --git a/openapi.yaml b/openapi.yaml index 80b1b351..d737e2db 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -12,7 +12,7 @@ info: - Monitor system status and trigger cleanup operations - Manage cross-episode ad patterns with network and podcast scope - Submit corrections to improve ad detection accuracy - version: 2.4.7 + version: 2.4.8 contact: name: MinusPod license: diff --git a/version.py b/version.py index 999d130b..6ce19833 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -__version__ = "2.4.7" +__version__ = "2.4.8"