From d384d20fa5d140ad66ed96ce198879c08d738d0f Mon Sep 17 00:00:00 2001 From: SAGE Date: Thu, 29 Jan 2026 17:19:43 +0800 Subject: [PATCH] Add RUN_AS_USER variable - default root, set to coder for non-root --- README.md | 71 +++++++++++++++-------------------------- railway-entrypoint.sh | 74 ++++++++++++++++++++++++++++++------------- 2 files changed, 77 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 4856fd611..162cbbca7 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,16 @@ [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/template/TEMPLATE_ID) -Cloud IDE with persistent extensions, settings, and tools. Runs as non-root user. +Cloud IDE with persistent extensions, settings, and tools. --- ## Features - **Claude Code** & **Node.js 20** pre-installed -- **Non-root execution** - runs as `coder` user (UID 1000) - Extensions persist across redeployments -- Volume permissions auto-fixed on startup +- Configurable user (root or coder) +- Shell profile auto-configured with PATH --- @@ -23,12 +23,11 @@ Cloud IDE with persistent extensions, settings, and tools. Runs as non-root user # Claude Code with auto-accept (for automation) claude --dangerously-skip-permissions +# Or use the alias +claude-auto + # Interactive mode claude - -# Node.js ready -node --version -npm --version ``` --- @@ -38,32 +37,35 @@ npm --version | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `PASSWORD` | Yes | - | Login password | +| `RUN_AS_USER` | No | `root` | Set to `coder` for non-root | | `CODER_HOME` | No | `/home/coder` | Volume mount path | -| `CODER_UID` | No | `1000` | User ID for coder | -| `CODER_GID` | No | `1000` | Group ID for coder | +| `CODER_UID` | No | `1000` | User ID (when RUN_AS_USER=coder) | +| `CODER_GID` | No | `1000` | Group ID (when RUN_AS_USER=coder) | --- -## How It Works +## Running as Non-Root (Recommended for Claude) -1. **Starts as root** - fixes volume permissions -2. **Switches to coder** - uses `gosu` for clean handoff -3. **Runs code-server** - as non-root user +Set in Railway variables: +``` +RUN_AS_USER=coder +``` -This means: -- ✅ No root permission warnings in code-server -- ✅ Existing volumes with root-owned files work fine -- ✅ Claude `--dangerously-skip-permissions` works +This enables: +- ✅ No root permission warnings +- ✅ `claude --dangerously-skip-permissions` works properly +- ✅ Better security --- -## Claude Code Authentication +## Update Behavior -After running `claude` for the first time: - -1. Follow the authentication prompts -2. Your credentials are stored in `~/.claude/` -3. They persist across redeployments (on volume) +| Component | Behavior | +|-----------|----------| +| **Volume tools** | You control - install to `~/.local/bin/` | +| **Image tools** | Auto-update on redeploy (fallback) | +| **Extensions** | Persist on volume | +| **Claude auth** | Persists in `~/.claude/` | --- @@ -80,29 +82,6 @@ Make executable: `chmod +x script.sh` --- -## Update Behavior - -| Component | Behavior | -|-----------|----------| -| **Volume tools** | You control - install to `~/.local/node/` or `~/.claude/local/` | -| **Image tools** | Auto-update on redeploy (fallback) | -| **Extensions** | Persist on volume | -| **Claude auth** | Persists on volume | - -Logs show `[volume]` or `[image]` next to each tool. - ---- - -## Troubleshooting - -| Issue | Solution | -|-------|----------| -| Permission denied | Check `CODER_UID` matches your volume owner | -| Claude not found | Run `which claude` to check PATH | -| Extensions missing | Verify volume mounted at `CODER_HOME` | - ---- - ## Credits - [code-server](https://github.com/coder/code-server) by Coder diff --git a/railway-entrypoint.sh b/railway-entrypoint.sh index d1042f6f8..3f5e0f3ae 100644 --- a/railway-entrypoint.sh +++ b/railway-entrypoint.sh @@ -3,7 +3,7 @@ set -e # ============================================================================ # VSCode Cloud IDE - Railway Entrypoint -# Handles permission fix and user switching for non-root execution +# Handles permission fix and optional user switching # ============================================================================ echo "╔══════════════════════════════════════════════════════════════════════╗" @@ -12,13 +12,16 @@ echo "╚═══════════════════════ echo "" # ============================================================================ -# CONFIGURABLE PATHS +# CONFIGURABLE PATHS AND USER # ============================================================================ CODER_HOME="${CODER_HOME:-/home/coder}" CODER_UID="${CODER_UID:-1000}" CODER_GID="${CODER_GID:-1000}" +# RUN_AS_USER: Set to "coder" to run as non-root, or "root" (default) to stay as root +RUN_AS_USER="${RUN_AS_USER:-root}" + export HOME="$CODER_HOME" export XDG_DATA_HOME="$CODER_HOME/.local/share" export XDG_CONFIG_HOME="$CODER_HOME/.config" @@ -28,12 +31,17 @@ export XDG_STATE_HOME="$CODER_HOME/.local/state" # PATH: Include ~/.local/bin where Claude installs by default export PATH="$CODER_HOME/.local/bin:$CODER_HOME/.local/node/bin:$CODER_HOME/.claude/local:$CODER_HOME/node_modules/.bin:/usr/local/bin:/usr/bin:/usr/lib/code-server/lib/vscode/bin/remote-cli:$PATH" +echo "→ Initial user: $(whoami) (UID: $(id -u))" +echo "→ RUN_AS_USER: $RUN_AS_USER" +echo "→ HOME: $HOME" + # ============================================================================ -# PERMISSION FIX (runs as root, then switches to coder) +# DIRECTORY CREATION AND PERMISSION FIX # ============================================================================ if [ "$(id -u)" = "0" ]; then - echo "→ Running initial setup as root..." + echo "" + echo "→ Running setup as root..." # Create directories if they don't exist mkdir -p "$XDG_DATA_HOME" \ @@ -50,11 +58,9 @@ if [ "$(id -u)" = "0" ]; then # ======================================================================== # SHELL PROFILE SETUP - # Ensure PATH includes ~/.local/bin for Claude and other user tools # ======================================================================== PROFILE_FILE="$HOME/.bashrc" - PATH_EXPORT='export PATH="$HOME/.local/bin:$HOME/.local/node/bin:$PATH"' if [ ! -f "$PROFILE_FILE" ] || ! grep -q '.local/bin' "$PROFILE_FILE" 2>/dev/null; then echo "→ Setting up shell profile..." @@ -82,24 +88,46 @@ fi PROFILE fi - # Fix ownership on the entire home directory - echo "→ Fixing permissions for coder user (UID: $CODER_UID)..." - chown -R "$CODER_UID:$CODER_GID" "$CODER_HOME" 2>/dev/null || true + # ======================================================================== + # USER SWITCHING (if RUN_AS_USER=coder) + # ======================================================================== - echo " ✓ Permissions fixed" - echo "" - - # Re-exec this script as coder user using gosu - echo "→ Switching to coder user..." - exec gosu "$CODER_UID:$CODER_GID" "$0" "$@" + if [ "$RUN_AS_USER" = "coder" ]; then + echo "→ Fixing permissions for coder user (UID: $CODER_UID)..." + chown -R "$CODER_UID:$CODER_GID" "$CODER_HOME" 2>/dev/null || true + echo " ✓ Permissions fixed" + + # Check if gosu is available + if command -v gosu &>/dev/null; then + echo "→ Switching to coder user via gosu..." + exec gosu "$CODER_UID:$CODER_GID" "$0" "$@" + else + echo " ⚠ gosu not found, staying as root" + fi + else + echo "→ Staying as root (set RUN_AS_USER=coder to switch)" + + # Create symlinks from /root to volume for persistence + mkdir -p /root/.local 2>/dev/null || true + for dir in ".local/share" ".local/bin" ".local/node" ".config" ".cache" ".claude"; do + target="$CODER_HOME/$dir" + link="/root/$dir" + if [ -d "$target" ] && [ ! -L "$link" ]; then + rm -rf "$link" 2>/dev/null || true + mkdir -p "$(dirname "$link")" 2>/dev/null || true + ln -sf "$target" "$link" 2>/dev/null || true + fi + done + echo " ✓ Root directories symlinked to $CODER_HOME" + fi fi # ============================================================================ -# RUNNING AS CODER USER FROM HERE +# RUNNING AS FINAL USER # ============================================================================ -echo "→ User: $(whoami) (UID: $(id -u))" -echo "→ HOME: $HOME" +echo "" +echo "→ Running as: $(whoami) (UID: $(id -u))" # ============================================================================ # FIRST RUN SETUP @@ -138,10 +166,12 @@ claude You'll need to authenticate with your Anthropic API key on first use. -## Persist Claude Authentication +## Configuration -Your Claude config at `~/.claude/` persists across redeployments. -After authenticating once, you won't need to re-authenticate. +Set these environment variables in Railway: + +- `RUN_AS_USER=coder` - Run as non-root user (recommended for Claude) +- `RUN_AS_USER=root` - Stay as root (default) Happy coding! 🚀 WELCOME @@ -171,7 +201,7 @@ echo " → npm: $(npm --version 2>/dev/null || echo 'not found')" # git echo " → git: $(git --version 2>/dev/null | cut -d' ' -f3 || echo 'not found')" -# Claude Code - show source (check ~/.local/bin first, then ~/.claude/local, then /usr/local/bin) +# Claude Code - show source if [ -x "$CODER_HOME/.local/bin/claude" ]; then echo " → claude: $(claude --version 2>/dev/null || echo 'installed') [volume ~/.local/bin]" elif [ -x "$CODER_HOME/.claude/local/claude" ]; then