Adds a webui service to docker-compose that wraps Claude Code in ttyd, serving a browser-accessible terminal on port 7681. The webui reuses Dockerfile.claude (ttyd added to apt deps) with a dedicated entrypoint script that enforces WEBUI_PASSWORD before starting. Network isolation is identical to the CLI container: claude-internal only, all egress via the proxy allowlist. claude.sh gains web and web-stop commands.
138 lines
5.4 KiB
Markdown
138 lines
5.4 KiB
Markdown
# docker-claude
|
|
|
|
Runs [Claude Code](https://claude.ai/code) inside an isolated Docker environment with a proxy sidecar for controlled egress. Claude cannot reach the host filesystem or network directly.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Host machine │
|
|
│ │
|
|
│ claude.sh (control script) │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────────────────────┐ │
|
|
│ │ Docker: claude-secure │ │
|
|
│ │ │ │
|
|
│ │ ┌─────────────┐ │ │
|
|
│ │ │ claude │──┐ claude-internal │ │
|
|
│ │ │ (UID 1000) │ │ (internal: true) │ │
|
|
│ │ └─────────────┘ ├──────────────► ┌──────────┐ │ │
|
|
│ │ ┌─────────────┐ │ │ proxy │ │ │
|
|
│ │ │ webui │──┘ │ (UID 13) │ │ │
|
|
│ │ │ (UID 1000) │ └────┬─────┘ │ │
|
|
│ │ │ port 7681 │ proxy-external │ │
|
|
│ │ └─────────────┘ │ │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ internet (allowlisted) │
|
|
└──────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
- **`claude`** — Claude Code CLI, UID 1000, on `claude-internal` only
|
|
- **`webui`** — Claude Code in a browser terminal (ttyd), UID 1000, on `claude-internal` only, port 7681
|
|
- **`proxy`** — Squid forward proxy, UID 13, bridges `claude-internal` ↔ internet with egress allowlist
|
|
- **`claude-internal`** — `internal: true`; no default gateway, containers cannot reach the internet directly
|
|
- **`proxy-external`** — Standard bridge; proxy sidecar only
|
|
|
|
## Prerequisites
|
|
|
|
- Docker Engine 24+
|
|
- Docker Compose v2 plugin (`docker compose version`)
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
# 1. Clone / copy this repo
|
|
git clone <repo> docker-claude && cd docker-claude
|
|
|
|
# 2. Configure credentials
|
|
cp .env.example .env
|
|
$EDITOR .env # set ANTHROPIC_API_KEY (and WEBUI_PASSWORD if using web mode)
|
|
|
|
# 3. Make the control script executable
|
|
chmod +x claude.sh
|
|
```
|
|
|
|
## Usage
|
|
|
|
### CLI mode
|
|
|
|
```bash
|
|
# Build images, start proxy, launch Claude Code interactively
|
|
./claude.sh start
|
|
|
|
# Start proxy if needed, launch Claude Code (faster on subsequent runs)
|
|
./claude.sh run
|
|
|
|
# Mount a host directory as the workspace
|
|
WORKSPACE_DIR=$HOME/myproject ./claude.sh run
|
|
```
|
|
|
|
### Web interface
|
|
|
|
Serves Claude Code as a browser terminal via [ttyd](https://github.com/tsl0922/ttyd), protected by HTTP basic auth.
|
|
|
|
```bash
|
|
# Add to .env first:
|
|
# WEBUI_PASSWORD=your-strong-password
|
|
# WEBUI_USER=claude # optional, defaults to "claude"
|
|
|
|
./claude.sh web
|
|
# → Web interface running at http://0.0.0.0:7681
|
|
|
|
# To reach it from outside the sandbox host:
|
|
sbx ports <sandbox-name> --publish 7681:7681/tcp
|
|
|
|
# Stop web interface (keeps proxy running)
|
|
./claude.sh web-stop
|
|
```
|
|
|
|
### Other commands
|
|
|
|
```bash
|
|
./claude.sh stop # Stop and remove all containers
|
|
./claude.sh update # Rebuild images without cache
|
|
./claude.sh logs # Tail proxy logs
|
|
./claude.sh logs webui # Tail web interface logs
|
|
./claude.sh status # Show container status
|
|
./claude.sh shell # Debug bash shell in the Claude container
|
|
```
|
|
|
|
### Workspace
|
|
|
|
| Mode | Default | Override |
|
|
|---|---|---|
|
|
| CLI (`run`/`start`) | Named Docker volume (isolated) | `WORKSPACE_DIR=/path ./claude.sh run` |
|
|
| Web (`web`) | Named Docker volume (`claude-web-workspace`) | Edit `docker-compose.yml` volumes |
|
|
|
|
## Egress allowlist
|
|
|
|
Edit `proxy/squid.conf` and add domains to the `allowed_sites` ACL:
|
|
|
|
```
|
|
acl allowed_sites dstdomain api.anthropic.com
|
|
acl allowed_sites dstdomain statsig.anthropic.com
|
|
# acl allowed_sites dstdomain api.github.com
|
|
# acl allowed_sites dstdomain registry.npmjs.org
|
|
```
|
|
|
|
Rebuild after changes:
|
|
|
|
```bash
|
|
./claude.sh update
|
|
./claude.sh stop && ./claude.sh start
|
|
```
|
|
|
|
## Security controls
|
|
|
|
| Control | claude / webui | proxy |
|
|
|---|---|---|
|
|
| Non-root user | UID 1000 (`claude`) | UID 13 (`proxy`) |
|
|
| `no-new-privileges` | yes | yes |
|
|
| All capabilities dropped | yes | yes |
|
|
| Direct internet access | no (`internal` network only) | allowlisted only |
|
|
| Host filesystem | no mounts by default | none |
|
|
| Docker socket | not mounted | not mounted |
|
|
| Web auth | basic auth (ttyd `--credential`) | n/a |
|