Claude Code Isolation and Burner Workflow 260211: Difference between revisions
Justinaquino (talk | contribs) Add session notes 260222: firejail incompatibility fix, simplified launcher script |
Justinaquino (talk | contribs) Rewrite: remove firejail, simplified csb.sh, persistent container workflow with save points |
||
| Line 1: | Line 1: | ||
= | = Claude Code Isolation with Distrobox — Burner Workflow = | ||
== Overview == | == Overview == | ||
This guide documents how to run Claude Code in an isolated environment using | This guide documents how to run Claude Code in an isolated environment using Distrobox containers. The core idea: | ||
* Each project or task lives in its own '''container''' — isolated from the host system. | |||
* Containers are '''persistent'''. You enter them, do work, and come back later. | |||
* At any point you can '''save the current state''' as an image — a snapshot you can restore from or clone. | |||
* A '''golden image''' (or template) is a clean, pre-configured base you clone new containers from. | |||
* You delete containers and images on your own schedule, when you no longer need them. | |||
This protects against malicious prompt injection by limiting what Claude Code can access | Think of it like save points in a game: you can keep playing from where you left off, and save whenever you want a checkpoint. | ||
This protects against malicious prompt injection by limiting what Claude Code can access — any damage from a bad agent run stays inside the container and does not touch the host. | |||
== Command Context == | == Command Context == | ||
| Line 27: | Line 32: | ||
* A Linux host (Fedora, Ubuntu, Arch, etc.) | * A Linux host (Fedora, Ubuntu, Arch, etc.) | ||
* [https://github.com/89luca89/distrobox Distrobox] installed on the host | * [https://github.com/89luca89/distrobox Distrobox] installed on the host | ||
* Podman installed on the host | |||
* A Claude Code account and API access | * A Claude Code account and API access | ||
== Step 1: Install Distrobox | == One-Time Setup: Create the Golden Image == | ||
Run these steps once. The result is a golden image — a clean, pre-configured base you will clone all future work containers from. | |||
=== Step 1: Install Distrobox === | |||
'''[HOST]''' | '''[HOST]''' | ||
sudo apt install distrobox # Debian/Ubuntu | sudo apt install distrobox # Debian/Ubuntu | ||
| Line 37: | Line 47: | ||
yay -S distrobox # Arch (AUR) | yay -S distrobox # Arch (AUR) | ||
== Step 2: Create | === Step 2: Create and Enter the Base Container === | ||
'''[HOST]''' Create a home directory for the base container. Run these as two separate commands: | |||
mkdir -p ~/sandbox-homes/claude-base | |||
distrobox create --name claude- | distrobox create --name claude-base --image ubuntu:24.04 --home ~/sandbox-homes/claude-base | ||
'''[HOST]''' Enter the container: | '''[HOST]''' Enter the container: | ||
distrobox enter claude- | distrobox enter claude-base | ||
== Step 3: Install Claude Code | === Step 3: Install Claude Code === | ||
'''[DISTROBOX]''' | '''[DISTROBOX]''' | ||
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - | |||
sudo apt install -y nodejs | |||
npm install -g @anthropic-ai/claude-code | npm install -g @anthropic-ai/claude-code | ||
'''[DISTROBOX]''' Log in and verify | '''[DISTROBOX]''' Log in and verify: | ||
claude | claude | ||
Complete the authentication flow. Your credentials are stored inside the container. | |||
=== Step 4: Add the Launcher Script === | |||
'''[DISTROBOX]''' Create a project directory and the launcher script: | |||
mkdir -p ~/project | |||
nano ~/project/csb.sh | |||
Contents: | |||
<syntaxhighlight lang="bash"> | |||
= | |||
#!/bin/bash | #!/bin/bash | ||
WORK_DIR="$(cd "$(dirname "$0")" && pwd)" | WORK_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
cd "$WORK_DIR" | |||
echo "Starting Claude Code..." | |||
echo " Working directory: $WORK_DIR" | |||
echo "Starting Claude Code | |||
echo " | |||
echo "" | echo "" | ||
exec claude "$@" | |||
exec | </syntaxhighlight> | ||
</ | |||
'''[DISTROBOX]''' Make it executable: | '''[DISTROBOX]''' Make it executable: | ||
chmod +x ~/ | chmod +x ~/project/csb.sh | ||
== Step | === Step 5: Save as the Golden Image === | ||
'''[HOST]''' | '''[HOST]''' Exit the container, then stop and commit it: | ||
exit | |||
distrobox stop claude-base | |||
podman container commit claude-base localhost/claude-golden:latest | |||
'''[HOST]''' Verify: | |||
podman image ls | grep claude-golden | |||
You now have a golden image. The base container can be kept or deleted — the image is self-contained. | |||
== Daily Workflow == | |||
== | === Starting a New Container === | ||
When you start a new project or task, clone a fresh container from the golden image. | |||
'''[HOST]''' | '''[HOST]''' Run these as two separate commands: | ||
mkdir -p ~/sandbox-homes/my-project | |||
distrobox create --name my-project --image localhost/claude-golden:latest --home ~/sandbox-homes/my-project | |||
'''[HOST]''' Enter it: | |||
distrobox enter my-project | |||
'''[DISTROBOX]''' Launch Claude Code: | |||
cd ~/project | |||
./csb.sh | |||
=== Continuing an Existing Container === | |||
If the container already exists and you want to pick up where you left off, just enter it again: | |||
'''[HOST]''' | |||
distrobox enter my-project | |||
'''[ | '''[DISTROBOX]''' | ||
cd ~/project | |||
./csb.sh | |||
No setup needed — the container retains its state between sessions. | |||
=== Saving a Checkpoint (Save Point) === | |||
At any point — before a risky change, after a milestone, or just when you want a snapshot — you can save the current container state as an image. | |||
'''[HOST]''' Stop the container: | |||
distrobox stop my-project | |||
'''[HOST]''' Commit to a named image: | |||
podman container commit my-project localhost/my-project-checkpoint-1:latest | |||
You can | You can create as many checkpoints as you want with different names: | ||
podman container commit my-project localhost/my-project-checkpoint-2:latest | |||
'''[HOST]''' Restart the container after saving: | |||
distrobox enter my-project | |||
=== Restoring from a Checkpoint === | |||
If something goes wrong and you want to roll back to a previous save point: | |||
'''[HOST]''' Delete the current container: | |||
distrobox rm my-project | |||
rm -rf ~/sandbox-homes/my-project | |||
'''[HOST]''' Create a new container from the checkpoint image: | |||
mkdir -p ~/sandbox-homes/my-project | |||
distrobox create --name my-project --image localhost/my-project-checkpoint-1:latest --home ~/sandbox-homes/my-project | |||
=== Promoting a Container to the Golden Image === | |||
If a container has a set of tools, config, or improvements you want all future containers to inherit, promote it to the golden image: | |||
'''[HOST]''' | |||
distrobox stop my-project | |||
podman container commit my-project localhost/claude-golden:latest | |||
From now on, new containers cloned from <code>localhost/claude-golden:latest</code> will include those changes. | |||
=== Managing Images === | |||
'''[HOST]''' List all saved images: | |||
podman image ls | |||
'''[HOST]''' Delete an image you no longer need: | |||
podman image rm localhost/my-project-checkpoint-1:latest | |||
'''[HOST]''' List all containers (running and stopped): | |||
podman ps -a | |||
'''[HOST]''' Delete a container and its burner home when you are done with a project: | |||
distrobox rm my-project | |||
rm -rf ~/sandbox-homes/my-project | |||
== What Distrobox Isolation Provides == | |||
{| class="wikitable" | {| class="wikitable" | ||
| Line 259: | Line 217: | ||
! Surface !! Isolated? !! Notes | ! Surface !! Isolated? !! Notes | ||
|- | |- | ||
| Host home directory || ✅ Yes || | | Host home directory || ✅ Yes || Container uses its own burner home via <code>--home</code>; real <code>~</code> is never touched | ||
|- | |- | ||
| | | Host filesystem via <code>/run/host</code> || ⚠️ Partial || Mounted read-write by default. Add <code>--additional-flags "--mount type=bind,source=/,target=/run/host,ro"</code> at container creation to make it read-only | ||
|- | |- | ||
| System packages || ✅ Yes || Container uses its own overlay filesystem | |||
|- | |- | ||
| | | Network || ❌ No || Container shares the host network namespace. Claude Code requires network access to reach the Anthropic API | ||
|- | |- | ||
| | | Linux kernel || ❌ No || Rootless containers share the host kernel (acceptable for most threat models) | ||
|- | |- | ||
| | | X11/Wayland display || ❌ No || GUI apps render on the host desktop | ||
|} | |} | ||
== | == Why the Burner Concept == | ||
The Burner Workflow is designed to give Claude Code extensive permissions — auto-allow mode, running system commands, installing packages — without risking your actual computer. | |||
* '''Safety with high permissions:''' If Claude Code runs <code>rm -rf</code> or installs 50 packages, your main system is untouched. The damage stays inside the container. | |||
* '''Dependency hygiene:''' Agents often install tools to complete tasks. Distrobox keeps this inside the box. Delete the container when you are done with the project. | |||
* '''Save points for risky work:''' Before letting an agent attempt something uncertain, commit a checkpoint. If it breaks the container, roll back and try a different approach. | |||
* '''Better integration than Docker:''' Unlike raw Docker, Distrobox integrates naturally with your terminal environment while still keeping the execution environment separate. | |||
* '''Safety with | |||
* '''Dependency | |||
* '''Better | |||
=== Can You Skip Distrobox? === | === Can You Skip Distrobox? === | ||
* '''Yes, if:''' You are just testing Claude Code | * '''Yes, if:''' You are just testing Claude Code and will manually approve every command (the default safe mode). | ||
* '''No, if:''' You want to | * '''No, if:''' You want to use autonomous mode — skipping permission prompts or letting the agent freely install tools. In that case, skipping Distrobox is dangerous and defeats the purpose of this guide. | ||
== References == | == References == | ||
* [https://github.com/89luca89/distrobox Distrobox] | * [https://github.com/89luca89/distrobox Distrobox] | ||
* [https:// | * [https://claude.ai/code Claude Code] | ||
== Session Notes 260222 — Testing & Fixes == | == Session Notes 260222 — Testing & Fixes == | ||
| Line 329: | Line 257: | ||
When distrobox is created with <code>--home</code> pointing to a burner directory, the container's <code>$HOME</code> becomes that directory — not the real user home. Any launcher script variables using <code>$HOME</code> (like <code>CLAUDE_DIR</code> and <code>NVM_DIR</code>) will resolve to wrong paths. | When distrobox is created with <code>--home</code> pointing to a burner directory, the container's <code>$HOME</code> becomes that directory — not the real user home. Any launcher script variables using <code>$HOME</code> (like <code>CLAUDE_DIR</code> and <code>NVM_DIR</code>) will resolve to wrong paths. | ||
'''Fix:''' Hardcode absolute paths in the launcher script | '''Fix:''' Hardcode absolute paths in the launcher script if needed. | ||
=== 3. Firejail is incompatible with distrobox <code>--home</code> workflow === | === 3. Firejail is incompatible with distrobox <code>--home</code> workflow === | ||
Firejail fails with <code>no suitable ...bin/claude executable found</code> inside distrobox when using a custom <code>--home</code> directory. The cause is firejail's whitelist mode blocking Node.js runtime dependencies that Claude Code requires | Firejail fails with <code>no suitable ...bin/claude executable found</code> inside distrobox when using a custom <code>--home</code> directory. The cause is firejail's whitelist mode blocking Node.js runtime dependencies that Claude Code requires. | ||
'''Fix:''' Drop firejail | '''Fix:''' Drop firejail. Distrobox with <code>--home</code> provides sufficient filesystem isolation for the burner workflow. The simplified <code>csb.sh</code> above is the current recommended launcher. | ||
=== 4. Backup script may produce duplicate image files === | === 4. Backup script may produce duplicate image files === | ||
Revision as of 07:36, 22 February 2026
Claude Code Isolation with Distrobox — Burner Workflow
Overview
This guide documents how to run Claude Code in an isolated environment using Distrobox containers. The core idea:
- Each project or task lives in its own container — isolated from the host system.
- Containers are persistent. You enter them, do work, and come back later.
- At any point you can save the current state as an image — a snapshot you can restore from or clone.
- A golden image (or template) is a clean, pre-configured base you clone new containers from.
- You delete containers and images on your own schedule, when you no longer need them.
Think of it like save points in a game: you can keep playing from where you left off, and save whenever you want a checkpoint.
This protects against malicious prompt injection by limiting what Claude Code can access — any damage from a bad agent run stays inside the container and does not touch the host.
Command Context
Every command in this guide is prefixed with where it must be run:
| Prefix | Meaning |
|---|---|
[HOST] |
Run this in a terminal on your normal Linux desktop, outside any container |
[DISTROBOX] |
Run this inside the Distrobox container after entering it |
Prerequisites
- A Linux host (Fedora, Ubuntu, Arch, etc.)
- Distrobox installed on the host
- Podman installed on the host
- A Claude Code account and API access
One-Time Setup: Create the Golden Image
Run these steps once. The result is a golden image — a clean, pre-configured base you will clone all future work containers from.
Step 1: Install Distrobox
[HOST]
sudo apt install distrobox # Debian/Ubuntu sudo dnf install distrobox # Fedora yay -S distrobox # Arch (AUR)
Step 2: Create and Enter the Base Container
[HOST] Create a home directory for the base container. Run these as two separate commands:
mkdir -p ~/sandbox-homes/claude-base
distrobox create --name claude-base --image ubuntu:24.04 --home ~/sandbox-homes/claude-base
[HOST] Enter the container:
distrobox enter claude-base
Step 3: Install Claude Code
[DISTROBOX]
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - sudo apt install -y nodejs npm install -g @anthropic-ai/claude-code
[DISTROBOX] Log in and verify:
claude
Complete the authentication flow. Your credentials are stored inside the container.
Step 4: Add the Launcher Script
[DISTROBOX] Create a project directory and the launcher script:
mkdir -p ~/project nano ~/project/csb.sh
Contents:
#!/bin/bash
WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$WORK_DIR"
echo "Starting Claude Code..."
echo " Working directory: $WORK_DIR"
echo ""
exec claude "$@"
[DISTROBOX] Make it executable:
chmod +x ~/project/csb.sh
Step 5: Save as the Golden Image
[HOST] Exit the container, then stop and commit it:
exit
distrobox stop claude-base podman container commit claude-base localhost/claude-golden:latest
[HOST] Verify:
podman image ls | grep claude-golden
You now have a golden image. The base container can be kept or deleted — the image is self-contained.
Daily Workflow
Starting a New Container
When you start a new project or task, clone a fresh container from the golden image.
[HOST] Run these as two separate commands:
mkdir -p ~/sandbox-homes/my-project
distrobox create --name my-project --image localhost/claude-golden:latest --home ~/sandbox-homes/my-project
[HOST] Enter it:
distrobox enter my-project
[DISTROBOX] Launch Claude Code:
cd ~/project ./csb.sh
Continuing an Existing Container
If the container already exists and you want to pick up where you left off, just enter it again:
[HOST]
distrobox enter my-project
[DISTROBOX]
cd ~/project ./csb.sh
No setup needed — the container retains its state between sessions.
Saving a Checkpoint (Save Point)
At any point — before a risky change, after a milestone, or just when you want a snapshot — you can save the current container state as an image.
[HOST] Stop the container:
distrobox stop my-project
[HOST] Commit to a named image:
podman container commit my-project localhost/my-project-checkpoint-1:latest
You can create as many checkpoints as you want with different names:
podman container commit my-project localhost/my-project-checkpoint-2:latest
[HOST] Restart the container after saving:
distrobox enter my-project
Restoring from a Checkpoint
If something goes wrong and you want to roll back to a previous save point:
[HOST] Delete the current container:
distrobox rm my-project rm -rf ~/sandbox-homes/my-project
[HOST] Create a new container from the checkpoint image:
mkdir -p ~/sandbox-homes/my-project distrobox create --name my-project --image localhost/my-project-checkpoint-1:latest --home ~/sandbox-homes/my-project
Promoting a Container to the Golden Image
If a container has a set of tools, config, or improvements you want all future containers to inherit, promote it to the golden image:
[HOST]
distrobox stop my-project podman container commit my-project localhost/claude-golden:latest
From now on, new containers cloned from localhost/claude-golden:latest will include those changes.
Managing Images
[HOST] List all saved images:
podman image ls
[HOST] Delete an image you no longer need:
podman image rm localhost/my-project-checkpoint-1:latest
[HOST] List all containers (running and stopped):
podman ps -a
[HOST] Delete a container and its burner home when you are done with a project:
distrobox rm my-project rm -rf ~/sandbox-homes/my-project
What Distrobox Isolation Provides
| Surface | Isolated? | Notes |
|---|---|---|
| Host home directory | ✅ Yes | Container uses its own burner home via --home; real ~ is never touched
|
Host filesystem via /run/host |
⚠️ Partial | Mounted read-write by default. Add --additional-flags "--mount type=bind,source=/,target=/run/host,ro" at container creation to make it read-only
|
| System packages | ✅ Yes | Container uses its own overlay filesystem |
| Network | ❌ No | Container shares the host network namespace. Claude Code requires network access to reach the Anthropic API |
| Linux kernel | ❌ No | Rootless containers share the host kernel (acceptable for most threat models) |
| X11/Wayland display | ❌ No | GUI apps render on the host desktop |
Why the Burner Concept
The Burner Workflow is designed to give Claude Code extensive permissions — auto-allow mode, running system commands, installing packages — without risking your actual computer.
- Safety with high permissions: If Claude Code runs
rm -rfor installs 50 packages, your main system is untouched. The damage stays inside the container. - Dependency hygiene: Agents often install tools to complete tasks. Distrobox keeps this inside the box. Delete the container when you are done with the project.
- Save points for risky work: Before letting an agent attempt something uncertain, commit a checkpoint. If it breaks the container, roll back and try a different approach.
- Better integration than Docker: Unlike raw Docker, Distrobox integrates naturally with your terminal environment while still keeping the execution environment separate.
Can You Skip Distrobox?
- Yes, if: You are just testing Claude Code and will manually approve every command (the default safe mode).
- No, if: You want to use autonomous mode — skipping permission prompts or letting the agent freely install tools. In that case, skipping Distrobox is dangerous and defeats the purpose of this guide.
References
Session Notes 260222 — Testing & Fixes
1. mkdir -p and distrobox create must be run as separate commands
Pasting them as a single line fails silently. Always run them separately on the host.
2. $HOME resolves incorrectly inside --home containers
When distrobox is created with --home pointing to a burner directory, the container's $HOME becomes that directory — not the real user home. Any launcher script variables using $HOME (like CLAUDE_DIR and NVM_DIR) will resolve to wrong paths.
Fix: Hardcode absolute paths in the launcher script if needed.
3. Firejail is incompatible with distrobox --home workflow
Firejail fails with no suitable ...bin/claude executable found inside distrobox when using a custom --home directory. The cause is firejail's whitelist mode blocking Node.js runtime dependencies that Claude Code requires.
Fix: Drop firejail. Distrobox with --home provides sufficient filesystem isolation for the burner workflow. The simplified csb.sh above is the current recommended launcher.
4. Backup script may produce duplicate image files
Manual podman save and a skip-duplicates backup script may use different filename conventions (e.g. imagename_latest.tar.gz vs localhost_imagename_latest.tar.gz), resulting in duplicate files on the backup destination. Check for and remove duplicates after any manual save.
5. @reboot cron needs sleep 30
Network filesystem mounts (e.g. GVFS SMB) are not ready immediately on boot. Without a sleep delay, backup scripts triggered via @reboot cron will fail silently with a "destination not mounted" error. Add sleep 30 before the backup command.