Skip to content

Commit 0643fdf

Browse files
authored
Revise GitHub access instructions for sandbox
Updated the instructions for creating a Personal Access Token (PAT) for GitHub, emphasizing repo-scoped access and clarifying the importance of not using main SSH keys. Adjusted the section on SSH key generation to focus on PAT usage instead.
1 parent 8212126 commit 0643fdf

1 file changed

Lines changed: 89 additions & 31 deletions

File tree

docs/wsl-claude-sandbox.md

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Isolated WSL Sandbox for Claude Code
22

3-
A WSL2 Ubuntu instance configured for running `claude --dangerously-skip-permissions` with meaningful isolation from the Windows host: separate filesystem, no Windows PATH inheritance, read-only access to Windows files, no LAN reachability, sandbox-only SSH credentials.
3+
A WSL2 Ubuntu instance configured for running `claude --dangerously-skip-permissions` with meaningful isolation from the Windows host: separate filesystem, no Windows PATH inheritance, read-only access to Windows files, no LAN reachability, sandbox-only repo credentials.
44

55
This manual covers the happy path only. Adapt commands to your own usernames, paths, and project repos.
66

@@ -21,13 +21,15 @@ Why: keeps the sandbox separate from your main dev environment, so a misbehaving
2121
1. Download the Ubuntu 24.04 WSL image from <https://releases.ubuntu.com/noble/> — pick the file ending in `.wsl`.
2222

2323
2. From **PowerShell** (any window, not WSL):
24-
```powershell
24+
25+
```
2526
mkdir C:\WSL\Ubuntu-Claude
2627
wsl --install --from-file C:\path\to\downloaded.wsl --name Ubuntu-Claude
2728
```
2829

2930
3. Launch and create your user account when prompted:
30-
```powershell
31+
32+
```
3133
wsl -d Ubuntu-Claude
3234
```
3335

@@ -39,13 +41,13 @@ Why: turns off Windows PATH inheritance (so the sandbox can't run `notepad.exe`
3941

4042
Inside the sandbox:
4143

42-
```bash
44+
```
4345
sudo nano /etc/wsl.conf
4446
```
4547

4648
Replace the contents with (substitute your username for `frankray`):
4749

48-
```ini
50+
```
4951
[boot]
5052
systemd=true
5153
@@ -71,7 +73,7 @@ Note: `interop.enabled = true` is required for VS Code's WSL extension to instal
7173

7274
Apply by exiting and running from PowerShell:
7375

74-
```powershell
76+
```
7577
wsl --shutdown
7678
```
7779

@@ -85,13 +87,13 @@ Why: `automount.enabled = true` mounts all Windows drives. If you have drives wi
8587

8688
Create a systemd unit (substitute your drive letter for `d`):
8789

88-
```bash
90+
```
8991
sudo nano /etc/systemd/system/unmount-mnt-d.service
9092
```
9193

9294
Paste:
9395

94-
```ini
96+
```
9597
[Unit]
9698
Description=Unmount /mnt/d to keep it invisible to the sandbox
9799
After=local-fs.target
@@ -108,7 +110,7 @@ WantedBy=multi-user.target
108110

109111
Enable:
110112

111-
```bash
113+
```
112114
sudo systemctl daemon-reload
113115
sudo systemctl enable unmount-mnt-d.service
114116
```
@@ -125,15 +127,15 @@ Why: by default the sandbox can reach your home router, NAS, other machines on y
125127

126128
First, find your WSL gateway IP (changes per-machine):
127129

128-
```bash
130+
```
129131
ip route | awk '/default/ {print $3}'
130132
```
131133

132134
Substitute the result wherever you see `172.26.80.1` below.
133135

134136
Install and configure UFW:
135137

136-
```bash
138+
```
137139
sudo apt update
138140
sudo apt install -y ufw
139141
@@ -163,14 +165,18 @@ Note: if WSL renumbers its virtual network (rare, but happens on some Windows up
163165

164166
Why: minimal stack for .NET development plus Claude Code.
165167

166-
```bash
168+
```
167169
sudo apt update
168170
sudo apt install -y curl git build-essential ca-certificates
169171
170172
# .NET SDK (Ubuntu 24.04 ships .NET in its own repos — no Microsoft repo needed)
171173
sudo apt install -y dotnet-sdk-8.0
172174
dotnet --version
173175
176+
# GitHub CLI (needed for PR workflows from the sandbox)
177+
sudo apt install -y gh
178+
gh --version
179+
174180
# Claude Code (Anthropic's official installer)
175181
curl -fsSL https://claude.ai/install.sh | bash
176182
claude --version
@@ -180,40 +186,89 @@ First run of `claude` will walk you through authentication via browser.
180186

181187
---
182188

183-
## Step 6: Generate a sandbox-only SSH key and clone
189+
## Step 6: Create a repo-scoped PAT and clone
190+
191+
Why: don't give the sandbox access to your whole GitHub account. A fine-grained Personal Access Token scoped to a single repo means a compromised sandbox can only damage that one repo — every other repo on your account is invisible to it. This single token handles both git operations (clone, fetch, push over HTTPS) **and** `gh` CLI operations (PR create, comment) — no separate SSH key needed.
192+
193+
### 6a. Generate the PAT on GitHub
194+
195+
In your browser, go to <https://github.com/settings/personal-access-tokens/new> and fill in:
196+
197+
- **Token name:** `Claude Sandbox - <repo name>` (e.g. `Claude Sandbox - NetPace`)
198+
- **Expiration:** 90 days is reasonable. (Avoid "No expiration" — rotation is good hygiene.)
199+
- **Repository access:** select **"Only select repositories"** → pick the single repo you want the sandbox to work on.
184200

185-
Why: never copy your main SSH keys into the sandbox. Generate a fresh key here; if it's ever compromised, you revoke only the sandbox's access.
201+
Under **Repository permissions**, set these (leave everything else as "No access"):
186202

187-
```bash
188-
ssh-keygen -t ed25519 -C "claude-sandbox" -f ~/.ssh/id_ed25519
189-
cat ~/.ssh/id_ed25519.pub
203+
- **Contents:** Read and write — git push/pull
204+
- **Pull requests:** Read and write — `gh pr create`
205+
- **Issues:** Read and write — `gh pr comment` (PR comments are issue comments via the API)
206+
- **Metadata:** Read-only (auto-selected)
207+
208+
Click **Generate token**. Copy the `github_pat_...` value shown — GitHub only displays it once.
209+
210+
### 6b. Configure the sandbox to use the PAT
211+
212+
In the sandbox, store the token as an environment variable that `gh` picks up automatically:
213+
214+
```
215+
echo 'export GH_TOKEN="github_pat_xxxxx"' >> ~/.bashrc
216+
source ~/.bashrc
190217
```
191218

192-
Copy the output line. On GitHub: **Settings → SSH and GPG keys → New SSH key** → paste → save.
219+
(Replace `github_pat_xxxxx` with your actual token.)
193220

194-
Test:
221+
Verify `gh` is using it:
195222

196-
```bash
197-
ssh -T git@github.com
223+
```
224+
gh auth status
198225
```
199226

200-
Configure git identity (use your GitHub noreply email to keep your real email private):
227+
Should report `Logged in to github.com account <you> (GH_TOKEN)` with the token starting `github_pat_`.
201228

202-
```bash
229+
Configure git to use `gh` as its credential helper for HTTPS:
230+
231+
```
232+
gh auth setup-git
233+
```
234+
235+
### 6c. Configure git identity
236+
237+
Use your GitHub noreply email to keep your real email private:
238+
239+
```
203240
git config --global user.name "yourusername"
204241
git config --global user.email "<id>+yourusername@users.noreply.github.com"
205242
```
206243

207244
Find your noreply address at <https://github.com/settings/emails>.
208245

209-
Clone your project:
246+
### 6d. Clone the project
210247

211-
```bash
248+
```
212249
mkdir -p ~/projects
213250
cd ~/projects
214-
git clone git@github.com:youruser/yourrepo.git
251+
git clone https://github.com/youruser/yourrepo.git
215252
```
216253

254+
Note the HTTPS URL — the PAT authenticates over HTTPS, not SSH. No SSH keys are involved.
255+
256+
### 6e. Verify isolation
257+
258+
Confirm the sandbox can't reach any other private repo:
259+
260+
```
261+
git clone https://github.com/youruser/<some-other-private-repo>.git /tmp/test
262+
```
263+
264+
Should fail with `403 - Write access to repository not granted` or `Repository not found`. That failure is the proof your isolation is working — clean up with `rm -rf /tmp/test`.
265+
266+
Note: cloning a *public* repo will still succeed (anyone on the internet can clone public repos, with or without auth). The real test of isolation is whether private repos are reachable.
267+
268+
### 6f. Token rotation
269+
270+
The PAT expires on the date you chose. When that day comes, GitHub will email you. To renew: generate a new PAT at the same URL with the same settings, then edit the `GH_TOKEN` line in `~/.bashrc` and `source ~/.bashrc`. Under a minute's work.
271+
217272
---
218273

219274
## Step 7: Connect VS Code
@@ -226,7 +281,7 @@ Why: VS Code's WSL extension installs a server inside the sandbox and connects t
226281
4. `File → Open Folder``/home/<username>/projects/<repo>` → Open.
227282
5. When prompted, install the **C# Dev Kit** (Microsoft) **into WSL: Ubuntu-Claude**, not locally.
228283

229-
The integrated terminal (`Ctrl+\``) runs inside the sandbox. Confirm by checking the prompt shows your sandbox hostname.
284+
The integrated terminal (`Ctrl+``) runs inside the sandbox. Confirm by checking the prompt shows your sandbox hostname.
230285

231286
---
232287

@@ -236,29 +291,32 @@ Why: rollback insurance. If Claude trashes the instance later, restore from a sn
236291

237292
From PowerShell:
238293

239-
```powershell
294+
```
240295
mkdir C:\WSL\backups -ErrorAction SilentlyContinue
241296
wsl --export Ubuntu-Claude C:\WSL\backups\ubuntu-claude-final.tar
242297
```
243298

244299
Recommended snapshots to keep:
300+
245301
- **Baseline** — after Step 4 (isolation done, no tooling yet). Useful for rebuilds with different tooling.
246302
- **Working** — after Step 5 (tooling installed). Useful if a later config change breaks things.
247303
- **Final** — after Step 7 (fully configured). Your day-to-day rollback point.
248304

249305
Restore any snapshot with:
250306

251-
```powershell
307+
```
252308
wsl --shutdown
253309
wsl --unregister Ubuntu-Claude
254310
wsl --import Ubuntu-Claude C:\WSL\Ubuntu-Claude C:\WSL\backups\<snapshot>.tar
255311
```
256312

313+
Note: snapshots include `~/.bashrc` with the PAT in it. Treat the tar files as sensitive — anyone who can read them gets the token (scoped to your one repo, but still). Don't sync them to cloud storage, share them, or check them into version control.
314+
257315
---
258316

259317
## Step 9: Run Claude
260318

261-
```bash
319+
```
262320
cd ~/projects/<repo>
263321
claude --dangerously-skip-permissions
264322
```
@@ -267,7 +325,7 @@ claude --dangerously-skip-permissions
267325

268326
## Operational notes
269327

270-
**Don't put real credentials in this sandbox.** No AWS/Azure/GCP CLI logins, no production API keys, no SSH keys from your main account. The SSH key created here should be sandbox-only.
328+
**Don't add other credentials to this sandbox.** No AWS/Azure/GCP CLI logins, no production API keys, no SSH keys, no `gh auth login` (which would replace your scoped PAT with an account-wide OAuth token). The PAT created in step 6 should be the *only* credential.
271329

272330
**Don't `cd` into mounted Windows directories for project work.** Files at `/mnt/c/...` are read-only and slow over 9P. Keep work in `~/projects/`.
273331

0 commit comments

Comments
 (0)