Files
TREK/server/scripts/entrypoint.sh
T
Maurice 402a02a302 fix(docker): say what the volume-root check leaves alone, and skip the walk for a custom fsGroup
The entrypoint comment, the Helm chart README, the wiki and the ssrfGuard
note now describe what the shipped check does.
2026-11-05 14:36:22 +01:00

69 lines
3.6 KiB
Bash

#!/bin/sh
# Container start-up: check the image layout, correct ownership of the mounted
# data dirs while still root, then drop to the node user.
#
# This has to stay a file rather than an inline `sh -c` string in CMD. Container
# management UIs render Config.Cmd back into a text field and re-split it with a
# quote-aware tokenizer when you edit a container in place; a command carrying
# embedded quotes comes back mangled and the container dies with a shell syntax
# error before Node starts. A CMD of one bare token survives that round-trip.
set -e
# If the app code is missing, a volume was almost certainly mounted over /app
# (it hides the image's node_modules + dist). Fail with actionable guidance
# instead of a cryptic "Cannot find module 'tsconfig-paths/register'".
if [ ! -f /app/server/dist/index.js ] || [ ! -d /app/node_modules/tsconfig-paths ]; then
echo 'FATAL: TREK application files are missing from the image.'
echo 'A volume is likely mounted over /app, which hides the app code.'
echo 'Mount ONLY your data and uploads dirs: -v ./data:/app/data -v ./uploads:/app/uploads'
echo 'Do NOT mount a volume at /app. See https://github.com/liketrek/TREK/wiki/Troubleshooting'
exit 1
fi
# cd into server/ so tsconfig-paths/register finds tsconfig.json and
# ../node_modules resolves correctly.
cd /app/server
# Already started as a non-root user (Kubernetes runAsUser, docker --user): there
# is nothing to chown and no privilege to drop, and gosu would only fail to switch
# users. Run Node as whoever we are; the volumes are that user's to provide.
if [ "$(id -u)" != "0" ]; then
exec node --require tsconfig-paths/register dist/index.js
fi
# Hand the mounted data dirs to node. A blanket `chown -R` on every start
# rewrote, and even a search for strays still read, every inode of the volume,
# which on a large or network-backed one (NFS, CephFS, Longhorn) kept the
# container busy for minutes before Node printed its first line (#2918). So only
# the dir and what sits directly in it are looked at: a volume that is not
# node's yet (a fresh one, or one whose top level was copied in as root) shows
# there, and is handed over once. Files copied in as root further down, into a
# subdirectory node already owns, do not show and are left alone; the
# Troubleshooting page of the wiki has the one-off chown for that.
#
# Only the owning user has to be node, because that is what Node runs as and
# what gosu leaves it with. The group is left to whoever set it: a Kubernetes
# fsGroup other than 1000 gives every file its own gid, and taking that back
# here would make the kubelet's OnRootMismatch walk the volume again on the
# next start, and this script after it, every time. Only a root group (a fresh
# volume under plain docker) is handed to node as well.
#
# A volume that refuses chown altogether (NFS with root_squash) is said once
# per start instead of being walked file by file; such a pod belongs under
# runAsUser 1000, see the Helm page of the wiki. Best effort either way: a
# read-only host filesystem or a locked-down bind mount must keep booting.
for dir in /app/data /app/uploads; do
[ -d "$dir" ] || continue
if [ -n "$(find "$dir" -maxdepth 1 \( ! -user node -o -group 0 \) -print -quit 2>/dev/null)" ]; then
if chown node "$dir" 2>/dev/null; then
echo "Correcting ownership of $dir for the node user..."
chown -R -h node "$dir" 2>/dev/null || true
find "$dir" -group 0 -exec chgrp -h node {} + 2>/dev/null || true
else
echo "WARNING: $dir cannot be handed to the node user (chown refused); run the container as uid 1000 instead."
fi
fi
done
exec gosu node node --require tsconfig-paths/register dist/index.js