|
| 1 | +#!/bin/bash |
| 2 | +# Inner script for `just claude`: runs inside a private mount namespace |
| 3 | +# (created by `unshare -m` from the justfile recipe). Mounts tmpfs over |
| 4 | +# the locations VS Code uses for host bridges, builds a Claude-only |
| 5 | +# /root/.gitconfig, then exec's claude with PR_SET_PDEATHSIG so it dies |
| 6 | +# if its parent shell does. Requires CAP_SYS_ADMIN — granted via |
| 7 | +# --cap-add=SYS_ADMIN in devcontainer.json's runArgs. See |
| 8 | +# README-CLAUDE.md for the full sandbox model. |
| 9 | +set -euo pipefail |
| 10 | + |
| 11 | +# VS Code drops IPC sockets (vscode-ipc-*.sock, vscode-git-*.sock, |
| 12 | +# vscode-ssh-auth-*.sock, vscode-remote-containers-ipc-*.sock) and the |
| 13 | +# vscode-remote-containers-*.js credential shim in /tmp, plus more in |
| 14 | +# /run/user/<uid>/. Replacing those directories with tmpfs in Claude's |
| 15 | +# namespace makes them invisible. Outside the namespace (the user's |
| 16 | +# regular terminal) VS Code keeps using them normally. |
| 17 | +mount -t tmpfs tmpfs /tmp |
| 18 | +if [ -d /run/user ]; then |
| 19 | + mount -t tmpfs tmpfs /run/user |
| 20 | +fi |
| 21 | + |
| 22 | +# Mask credential directories the user may bind-mount from the host for |
| 23 | +# their own use from non-Claude terminals (e.g. ~/.ssh for SSH-based |
| 24 | +# git push). Claude sees an empty tmpfs; the user's regular shell sees |
| 25 | +# the originals. |
| 26 | +for d in /root/.ssh /root/.gnupg /root/.aws /root/.azure /root/.gcloud /root/.docker; do |
| 27 | + if [ -d "$d" ]; then |
| 28 | + mount -t tmpfs tmpfs "$d" |
| 29 | + fi |
| 30 | +done |
| 31 | +# .netrc is a single file, not a dir — mask via bind to /dev/null. |
| 32 | +if [ -e /root/.netrc ]; then |
| 33 | + mount --bind /dev/null /root/.netrc |
| 34 | +fi |
| 35 | + |
| 36 | +# Build a Claude-only /root/.gitconfig containing the in-container |
| 37 | +# credential helpers (gh / glab) and HTTPS rewrites — and nothing else |
| 38 | +# the user has on the host (no SSH url rewrites, no host-specific |
| 39 | +# helpers). User identity is read from the original gitconfig BEFORE |
| 40 | +# we bind over it, so commits Claude makes are still attributed. |
| 41 | +git_name=$(git config --get user.name 2>/dev/null || true) |
| 42 | +git_email=$(git config --get user.email 2>/dev/null || true) |
| 43 | +gh_path=$(command -v gh || echo /usr/bin/gh) |
| 44 | +glab_path=$(command -v glab || echo /usr/local/bin/glab) |
| 45 | +cat > /etc/claude-gitconfig <<EOF |
| 46 | +[user] |
| 47 | + name = $git_name |
| 48 | + email = $git_email |
| 49 | +[safe] |
| 50 | + directory = * |
| 51 | +[url "https://github.com/"] |
| 52 | + insteadOf = git@github.com: |
| 53 | +[url "https://gitlab.diamond.ac.uk/"] |
| 54 | + insteadOf = git@gitlab.diamond.ac.uk: |
| 55 | +[credential "https://github.com"] |
| 56 | + helper = |
| 57 | + helper = !$gh_path auth git-credential |
| 58 | +[credential "https://gitlab.diamond.ac.uk"] |
| 59 | + helper = |
| 60 | + helper = !$glab_path auth git-credential |
| 61 | +EOF |
| 62 | +mount --bind /etc/claude-gitconfig /root/.gitconfig |
| 63 | + |
| 64 | +# IS_SANDBOX=1 is the canary `.claude/hooks/sandbox-check.sh` keys off. |
| 65 | +# Env-blanks: SSH_AUTH_SOCK / VSCODE_GIT_IPC_HANDLE / VSCODE_IPC_HOOK_CLI |
| 66 | +# all point into /tmp (already tmpfs in this namespace), but blanking |
| 67 | +# the vars stops Claude from even *trying* the path. GIT_ASKPASS and |
| 68 | +# VSCODE_GIT_ASKPASS_* point under /.vscode-server which the namespace |
| 69 | +# does NOT mask — blanking them is the actual defence against Claude |
| 70 | +# triggering the VS Code "log in to GitHub" popup. BROWSER points at a |
| 71 | +# host helper that opens URLs in the user's browser — blanked so |
| 72 | +# Claude cannot drive the user's browser. |
| 73 | +exec setpriv --pdeathsig SIGKILL env \ |
| 74 | + SSH_AUTH_SOCK= \ |
| 75 | + GIT_ASKPASS= \ |
| 76 | + VSCODE_GIT_IPC_HANDLE= \ |
| 77 | + VSCODE_GIT_ASKPASS_NODE= \ |
| 78 | + VSCODE_GIT_ASKPASS_MAIN= \ |
| 79 | + VSCODE_IPC_HOOK_CLI= \ |
| 80 | + BROWSER= \ |
| 81 | + IS_SANDBOX=1 \ |
| 82 | + claude --dangerously-skip-permissions |
0 commit comments