From de8b7c368d9b1f88b0de157338a34f0e69f6ab56 Mon Sep 17 00:00:00 2001 From: ezl-keygraph Date: Mon, 16 Mar 2026 17:09:54 +0530 Subject: [PATCH] fix: resolve Docker bind mount permission errors on Linux Use entrypoint-based UID remapping instead of --user flag so the container's pentest user matches the host UID/GID, keeping bind-mounted volumes writable. Git config moved to --system level to survive remapping. --- Dockerfile | 17 +++++++++-------- apps/cli/src/commands/workspaces.ts | 3 ++- apps/cli/src/docker.ts | 8 +++++++- entrypoint.sh | 18 ++++++++++++++++++ 4 files changed, 36 insertions(+), 10 deletions(-) create mode 100755 entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 6abbae1..b3dbdc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -123,10 +123,15 @@ RUN gem install addressable COPY --from=builder /usr/lib/python3.*/site-packages /usr/lib/python3.12/site-packages COPY --from=builder /usr/bin/schemathesis /usr/bin/ -# Create non-root user for security +# Create non-root user RUN addgroup -g 1001 pentest && \ adduser -u 1001 -G pentest -s /bin/bash -D pentest +# System-level git config (survives UID remapping in entrypoint) +RUN git config --system user.email "agent@localhost" && \ + git config --system user.name "Pentest Agent" && \ + git config --system --add safe.directory '*' + # Set working directory WORKDIR /app @@ -148,8 +153,8 @@ RUN mkdir -p /app/sessions /app/deliverables /app/repos /app/workspaces && \ chmod 777 /tmp/.npm && \ chown -R pentest:pentest /app -# Switch to non-root user -USER pentest +COPY entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh # Set environment variables ENV NODE_ENV=production @@ -162,9 +167,5 @@ ENV HOME=/tmp ENV XDG_CACHE_HOME=/tmp/.cache ENV XDG_CONFIG_HOME=/tmp/.config -# Configure Git identity and trust all directories -RUN git config --global user.email "agent@localhost" && \ - git config --global user.name "Pentest Agent" && \ - git config --global --add safe.directory '*' - +ENTRYPOINT ["/app/entrypoint.sh"] CMD ["node", "apps/worker/dist/temporal/worker.js"] diff --git a/apps/cli/src/commands/workspaces.ts b/apps/cli/src/commands/workspaces.ts index e9c8b49..3a9aa33 100644 --- a/apps/cli/src/commands/workspaces.ts +++ b/apps/cli/src/commands/workspaces.ts @@ -3,6 +3,7 @@ */ import { execFileSync } from 'node:child_process'; +import os from 'node:os'; import { getWorkerImage } from '../docker.js'; import { getWorkspacesDir } from '../home.js'; @@ -24,7 +25,7 @@ export function workspaces(version: string): void { 'node', 'apps/worker/dist/temporal/workspaces.js', ], - { stdio: 'inherit' }, + { stdio: 'inherit', ...(os.platform() === 'win32' && { env: { ...process.env, MSYS_NO_PATHCONV: '1' } }) }, ); } catch { console.error('ERROR: Failed to list workspaces. Is the Docker image available?'); diff --git a/apps/cli/src/docker.ts b/apps/cli/src/docker.ts index f411995..1afc168 100644 --- a/apps/cli/src/docker.ts +++ b/apps/cli/src/docker.ts @@ -207,6 +207,11 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess { // Add host flag for Linux args.push(...addHostFlag()); + // UID remapping for Linux bind mounts + if (os.platform() === 'linux' && process.getuid && process.getgid) { + args.push('-e', `SHANNON_HOST_UID=${process.getuid()}`, '-e', `SHANNON_HOST_GID=${process.getgid()}`); + } + // Volume mounts args.push('-v', `${opts.workspacesDir}:/app/workspaces`); args.push('-v', `${opts.repo.hostPath}:${opts.repo.containerPath}`); @@ -257,7 +262,8 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess { args.push('--pipeline-testing'); } - return spawn('docker', args, { stdio: 'pipe' }); + // Prevent MSYS/Git Bash from converting Unix paths (e.g. /repos/my-repo) to Windows paths + return spawn('docker', args, { stdio: 'pipe', ...(os.platform() === 'win32' && { env: { ...process.env, MSYS_NO_PATHCONV: '1' } }) }); } /** diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..7ff6206 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -euo pipefail + +TARGET_UID="${SHANNON_HOST_UID:-}" +TARGET_GID="${SHANNON_HOST_GID:-}" +CURRENT_UID=$(id -u pentest 2>/dev/null || echo "") + +if [ -n "$TARGET_UID" ] && [ "$TARGET_UID" != "$CURRENT_UID" ]; then + deluser pentest 2>/dev/null || true + delgroup pentest 2>/dev/null || true + + addgroup -g "$TARGET_GID" pentest + adduser -u "$TARGET_UID" -G pentest -s /bin/bash -D pentest + + chown -R pentest:pentest /app/sessions /app/deliverables /app/workspaces +fi + +exec su pentest -c "exec $*"