diff --git a/entrypoint.sh b/entrypoint.sh index e0a39acb..d801326e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,10 +1,29 @@ #!/bin/sh -# Runs as root so we can fix ownership of a freshly-mounted persistent disk -# (Render mounts new disks root-owned) before dropping to the non-root app user. +# Prepares the (possibly root-owned, freshly-mounted) data disk and runs the app +# as the non-root nanobot user when the platform allows it, falling back to root +# only when privilege-dropping is not permitted. Logs each decision so a failed +# start is diagnosable in the platform's logs instead of a silent exit. +echo "[entrypoint] starting as $(id)" + dir="$HOME/.nanobot" -mkdir -p "$dir" -chown -R nanobot:nanobot "$dir" +mkdir -p "$dir" 2>&1 || echo "[entrypoint] warning: mkdir $dir failed" + +if [ "$(id -u)" != "0" ]; then + # Already non-root (platform forced a user). Can't chown; just run. + echo "[entrypoint] not root — running app as $(id -un 2>/dev/null || id -u)" + exec nanobot "$@" +fi + +# Running as root: make the mounted disk writable by the app user. +chown -R nanobot:nanobot "$dir" 2>&1 || echo "[entrypoint] warning: chown $dir failed" + +# Drop to the non-root user if the runtime grants the capability to do so +# (setpriv needs CAP_SETUID/CAP_SETGID). Otherwise stay root so the app can +# still write the root-owned disk. +if setpriv --reuid=nanobot --regid=nanobot --init-groups true 2>/dev/null; then + echo "[entrypoint] dropping privileges to nanobot via setpriv" + exec setpriv --reuid=nanobot --regid=nanobot --init-groups nanobot "$@" +fi -# Drop privileges to the non-root user and exec the app as PID 1 so signals -# (SIGTERM / graceful shutdown) reach it directly. -exec setpriv --reuid=nanobot --regid=nanobot --init-groups nanobot "$@" +echo "[entrypoint] setpriv privilege-drop not permitted — running app as root" +exec nanobot "$@" diff --git a/render.yaml b/render.yaml index 996b1e7d..70abbc2d 100644 --- a/render.yaml +++ b/render.yaml @@ -8,9 +8,11 @@ services: runtime: docker dockerfilePath: ./Dockerfile dockerContext: . - # entrypoint.sh execs `nanobot "$@"`, so this becomes: - # nanobot gateway --config /app/render-config.json - dockerCommand: gateway --config /app/render-config.json + # Render's Docker Command REPLACES the Dockerfile ENTRYPOINT (it is not + # appended to it), so invoke the entrypoint explicitly. The entrypoint + # prepares the mounted disk, drops to the non-root user when permitted, then + # execs `nanobot gateway --config /app/render-config.json`. + dockerCommand: /usr/local/bin/entrypoint.sh gateway --config /app/render-config.json plan: starter healthCheckPath: / envVars: