diff --git a/.github/workflows/image-smoke-test.yml b/.github/workflows/image-smoke-test.yml index 7d7c04b0..6c56ff53 100644 --- a/.github/workflows/image-smoke-test.yml +++ b/.github/workflows/image-smoke-test.yml @@ -21,6 +21,7 @@ jobs: - openshift - drop-never - diagnostic + - puid-mismatch-warning steps: - name: Check out code @@ -166,6 +167,49 @@ jobs: echo "[smoke] PASS" ' + - name: "Smoke: PUID set + started non-root prints a warning but continues" + if: matrix.mode == 'puid-mismatch-warning' + run: | + mkdir -p test-storage test-cache + sudo chown -R 1500:1500 test-storage test-cache + + set +e + docker run --rm \ + --user 1500:1500 \ + -e PUID=1500 -e PGID=1500 \ + -v "$(pwd)/test-storage:/var/www/html/storage" \ + -v "$(pwd)/test-cache:/var/www/html/bootstrap/cache" \ + solidtime-smoke:test \ + sh -c ' + set -e + echo "[smoke] running as 1500 (user: directive wins)" + [ "$(id -u)" = "1500" ] + echo "[smoke] storage is writable as 1500" + touch /var/www/html/storage/framework/cache/data/test-file + echo "[smoke] container completed successfully" + ' \ + >stdout.log 2>stderr.log + exit_code=$? + set -e + + echo "[smoke] exit code: $exit_code" + echo "--- stderr ---" + cat stderr.log + echo "--- end stderr ---" + + if [ "$exit_code" -ne 0 ]; then + echo "Expected the entrypoint to continue (warning is non-fatal)." + exit 1 + fi + + for needle in "PUID/PGID is set but the container started as UID" "remove any 'user:' directive" "Continuing as UID"; do + if ! grep -q "$needle" stderr.log; then + echo "Missing warning fragment: $needle" + exit 1 + fi + done + echo "[smoke] PASS" + - name: "Smoke: diagnostic error path (read-only storage mount)" if: matrix.mode == 'diagnostic' run: | diff --git a/docker/prod/deployment/start-container b/docker/prod/deployment/start-container index c00038c1..e2e3d50e 100644 --- a/docker/prod/deployment/start-container +++ b/docker/prod/deployment/start-container @@ -13,10 +13,13 @@ set -e # # Env vars: # PUID, PGID UID/GID for the application user. Defaults 1000:1000. -# SOLIDTIME_DROP_PRIVILEGES auto (default) | always | never -# auto: if started as root, drop privileges; otherwise just exec. -# always: if started as root, drop privileges (errors if not root). -# never: never drop. Run as whatever UID/GID was started. +# Only takes effect when the container starts as root +# (which is the image's default — if you set a +# `user:` directive in compose, PUID/PGID are ignored +# and a startup warning is printed). +# SOLIDTIME_DROP_PRIVILEGES auto (default) | never +# auto: if started as root, drop privileges to APP_USER; otherwise just exec. +# never: never drop privileges. Run as whatever UID/GID was started. # ============================================================================ APP_USER="octane" @@ -40,7 +43,6 @@ WRITABLE_PATHS=( ) case "${DROP_PRIVS}" in - always) SHOULD_DROP=1 ;; never) SHOULD_DROP=0 ;; auto) if [ "$(id -u)" = "0" ]; then @@ -51,14 +53,28 @@ case "${DROP_PRIVS}" in ;; *) echo "[entrypoint] ERROR: invalid SOLIDTIME_DROP_PRIVILEGES='${DROP_PRIVS}'" >&2 - echo "[entrypoint] Valid values: auto (default), always, never" >&2 + echo "[entrypoint] Valid values: auto (default), never" >&2 exit 1 ;; esac -if [ "${DROP_PRIVS}" = "always" ] && [ "$(id -u)" != "0" ] && [ "${SOLIDTIME_PRIVILEGES_DROPPED:-0}" != "1" ]; then - echo "[entrypoint] ERROR: SOLIDTIME_DROP_PRIVILEGES=always requires the container to start as root" >&2 - exit 1 +# Warn if PUID/PGID are set but the container started non-root. PUID/PGID only +# take effect during the drop-privileges flow, which requires starting as root. +# A common cause is leaving `user:` in the compose file alongside PUID env vars. +if { [ -n "${PUID}" ] || [ -n "${PGID}" ]; } \ + && [ "$(id -u)" != "0" ] \ + && [ "${SOLIDTIME_PRIVILEGES_DROPPED:-0}" != "1" ]; then + cat >&2 <