Files
romm/entrypoint.sh
Georges-Antoine Assi 619e03ab32 fix: stop serving private user assets via unauthenticated nginx static route
The default Docker image symlinked /romm/assets into the nginx static web
root (/assets/romm/assets), where it was served by an unauthenticated
`location /assets { try_files ... }` block. /romm/assets holds private user
data (save files, save states, screenshots, avatars) that is meant to be
accessible only through the authenticated /api/raw/assets/{path} route
(Scope.ASSETS_READ). The static symlink bypassed that protection, letting any
unauthenticated caller read another user's files given a (guessable) path.
Avatar URLs leaked the hex user ID through the same static route, making path
construction straightforward.

Fix:
- Drop the /romm/assets symlink from the Docker image build and both
  entrypoint scripts; only /romm/resources (public cover art, screenshots,
  manuals) remains statically served.
- Point the frontend avatar URLs at the authenticated /api/raw/assets/ route
  instead of /assets/romm/assets/. Browser <img> loads authenticate via the
  existing session cookie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 17:34:30 -04:00

95 lines
2.8 KiB
Bash

#!/bin/bash
set -e
echo "Starting entrypoint script..."
# Create symlinks for frontend
if [[ -L /app/frontend/assets/romm/resources ]]; then
target=$(readlink "/app/frontend/assets/romm/resources")
# If the target is not the same as ${ROMM_BASE_PATH}/resources, recreate the symbolic link.
if [[ ${target} != "${ROMM_BASE_PATH}/resources" ]]; then
rm "/app/frontend/assets/romm/resources"
ln -s "${ROMM_BASE_PATH}/resources" "/app/frontend/assets/romm/resources"
fi
elif [[ ! -e /app/frontend/assets/romm/resources ]]; then
# Ensure parent directory exists before creating symbolic link
mkdir -p "/app/frontend/assets/romm"
ln -s "${ROMM_BASE_PATH}/resources" "/app/frontend/assets/romm/resources"
fi
# Define a signal handler to propagate termination signals
function handle_termination() {
echo "Terminating child processes..."
# Kill all background jobs
# trunk-ignore(shellcheck)
kill -TERM $(jobs -p) 2>/dev/null
}
# Trap SIGTERM and SIGINT signals
trap handle_termination SIGTERM SIGINT
# Set ROMM_AUTH_SECRET_KEY if not already set
if [[ -z ${ROMM_AUTH_SECRET_KEY} ]]; then
ROMM_AUTH_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
export ROMM_AUTH_SECRET_KEY
fi
# Start all services in the background
echo "Starting backend..."
cd /app/backend
uv run python main.py &
echo "Starting RQ scheduler..."
RQ_REDIS_HOST=${REDIS_HOST:-127.0.0.1} \
RQ_REDIS_PORT=${REDIS_PORT:-6379} \
RQ_REDIS_USERNAME=${REDIS_USERNAME:-""} \
RQ_REDIS_PASSWORD=${REDIS_PASSWORD:-""} \
RQ_REDIS_DB=${REDIS_DB:-0} \
RQ_REDIS_SSL=${REDIS_SSL:-0} \
rqscheduler \
--path /app/backend \
--pid /tmp/rq_scheduler.pid &
echo "Starting RQ worker..."
# Build Redis URL properly
if [[ -n ${REDIS_PASSWORD-} ]]; then
REDIS_URL="redis${REDIS_SSL:+s}://${REDIS_USERNAME-}:${REDIS_PASSWORD}@${REDIS_HOST:-127.0.0.1}:${REDIS_PORT:-6379}/${REDIS_DB:-0}"
elif [[ -n ${REDIS_USERNAME-} ]]; then
REDIS_URL="redis${REDIS_SSL:+s}://${REDIS_USERNAME}@${REDIS_HOST:-127.0.0.1}:${REDIS_PORT:-6379}/${REDIS_DB:-0}"
else
REDIS_URL="redis${REDIS_SSL:+s}://${REDIS_HOST:-127.0.0.1}:${REDIS_PORT:-6379}/${REDIS_DB:-0}"
fi
# Set PYTHONPATH so RQ can find the tasks module
PYTHONPATH="/app/backend:${PYTHONPATH-}" rq worker \
--path /app/backend \
--pid /tmp/rq_worker.pid \
--url "${REDIS_URL}" \
--logging_level "${LOGLEVEL:-INFO}" \
high default low &
echo "Starting watcher..."
watchfiles \
--target-type command \
'uv run python watcher.py' \
/app/romm/library &
if [[ ${ENABLE_SYNC_FOLDER_WATCHER:-false} == "true" ]]; then
echo "Starting sync folder watcher..."
sync_base_path="${ROMM_BASE_PATH:-/romm}/sync"
mkdir -p "${sync_base_path}"
watchfiles \
--target-type command \
'uv run python sync_watcher.py' \
"${sync_base_path}" &
fi
# Start the frontend dev server
cd /app/frontend
npm run dev &
# Wait for all background processes
wait