#!/bin/bash set -e set -x CADDY_PID="" APP_PID="" # Tear down both processes together so a crash of one doesn't leave the # container running half-broken (and eventually silently unhealthy). cleanup() { trap - TERM INT [ -n "$CADDY_PID" ] && kill -TERM "$CADDY_PID" 2>/dev/null || true [ -n "$APP_PID" ] && kill -TERM "$APP_PID" 2>/dev/null || true wait 2>/dev/null || true } trap cleanup TERM INT RUN_AS_ROOT=0 if [ "$(id -u)" = "0" ]; then RUN_AS_ROOT=1 fi if [ "$RUN_AS_ROOT" = "1" ]; then # Create data directory with correct permissions for the WebSSH user if [ ! -d "/data" ]; then echo "Creating /data directory..." mkdir -p /data fi echo "Current /data permissions:" ls -la /data || true # Ensure /data is writable and owned by the WebSSH user echo "Setting permissions on /data..." chmod 755 /data chown webssh:webssh /data if [ "$(stat -c %U /data)" != "webssh" ]; then echo "WARNING: Could not change ownership of /data to webssh" echo "Current owner: $(stat -c %U /data)" echo "This may happen with bind mounts. Ensure host directory is writable." fi echo "Final /data permissions:" ls -la /data || true else echo "Not running as root (uid=$(id -u)); skipping /data permission setup." fi # Caddy (run as a non-root user below) needs a writable home for its autosave # state and, if TLS is ever enabled, ACME certificates/keys. export XDG_DATA_HOME="${XDG_DATA_HOME:-/tmp/caddy-data}" export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-/tmp/caddy-config}" mkdir -p "$XDG_DATA_HOME" "$XDG_CONFIG_HOME" if [ "$RUN_AS_ROOT" = "1" ]; then chown -R webssh:webssh "$XDG_DATA_HOME" "$XDG_CONFIG_HOME" fi # Start Caddy (serves static files and reverse-proxies to local services) as # the unprivileged webssh user; it handles untrusted network input and must # not run as root. if command -v caddy >/dev/null 2>&1; then echo "Starting Caddy..." if [ "$RUN_AS_ROOT" = "1" ]; then gosu webssh caddy run --config /etc/caddy/Caddyfile & else caddy run --config /etc/caddy/Caddyfile & fi CADDY_PID=$! else echo "Caddy binary not found; continuing without reverse proxy" fi # Start the application, also dropping privileges when possible if [ "$RUN_AS_ROOT" = "1" ]; then gosu webssh python /app/src/run.py & else python /app/src/run.py & fi APP_PID=$! # If either supervised process exits, shut the container down instead of # silently continuing in a half-working (but "healthy") state. wait -n EXIT_CODE=$? echo "A supervised process exited with code $EXIT_CODE; shutting down" cleanup exit "$EXIT_CODE"