feat(webui): add browser terminal interface via ttyd
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.
This commit is contained in:
parent
c01102b641
commit
9b8562b746
7 changed files with 209 additions and 92 deletions
|
|
@ -7,3 +7,7 @@ ANTHROPIC_API_KEY=sk-ant-...
|
||||||
# Optional: mount a host directory as /workspace inside the Claude container.
|
# Optional: mount a host directory as /workspace inside the Claude container.
|
||||||
# If unset, a named Docker volume is used (fully isolated from the host).
|
# If unset, a named Docker volume is used (fully isolated from the host).
|
||||||
# WORKSPACE_DIR=/absolute/path/to/your/project
|
# WORKSPACE_DIR=/absolute/path/to/your/project
|
||||||
|
|
||||||
|
# Web interface credentials (required for ./claude.sh web)
|
||||||
|
# WEBUI_USER=claude
|
||||||
|
# WEBUI_PASSWORD=changeme
|
||||||
|
|
|
||||||
31
CLAUDE.md
31
CLAUDE.md
|
|
@ -8,35 +8,40 @@ This file provides context and guidance for working with this project.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
Two containers managed by Docker Compose:
|
Three containers managed by Docker Compose:
|
||||||
|
|
||||||
- **`claude`** — Claude Code CLI, non-root (UID 1000), isolated to an internal-only Docker network
|
- **`claude`** — Claude Code CLI, non-root (UID 1000), isolated to an internal-only Docker network
|
||||||
|
- **`webui`** — Claude Code as a browser terminal (ttyd on port 7681), same image as `claude`, non-root (UID 1000), same network isolation, basic auth required
|
||||||
- **`proxy`** — Squid forward proxy, non-root (UID 13), bridges the internal network to the internet with an egress allowlist
|
- **`proxy`** — Squid forward proxy, non-root (UID 13), bridges the internal network to the internet with an egress allowlist
|
||||||
|
|
||||||
Key Docker network property: `claude-internal` has `internal: true`, meaning Docker adds no default gateway. The `claude` container physically cannot reach the internet without going through the `proxy` container.
|
Key Docker network property: `claude-internal` has `internal: true`, meaning Docker adds no default gateway. The `claude` and `webui` containers physically cannot reach the internet without going through the `proxy` container.
|
||||||
|
|
||||||
|
The `webui` service reuses `Dockerfile.claude`. Its entrypoint (`webui-entrypoint.sh`) starts `ttyd --credential user:pass claude` instead of `claude` directly.
|
||||||
|
|
||||||
## File Structure
|
## File Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
docker-claude/
|
docker-claude/
|
||||||
├── claude.sh # Control script: start / stop / run / update / logs / status / shell
|
├── claude.sh # Control script: start/stop/run/web/web-stop/update/logs/status/shell
|
||||||
├── docker-compose.yml # Service definitions and network topology
|
├── docker-compose.yml # Service definitions and network topology
|
||||||
├── Dockerfile.claude # Claude Code container (node:20-slim, UID 1000)
|
├── Dockerfile.claude # Claude Code + ttyd container (node:20-slim, UID 1000)
|
||||||
├── Dockerfile.proxy # Squid proxy sidecar (ubuntu:22.04, UID 13)
|
├── Dockerfile.proxy # Squid proxy sidecar (ubuntu:22.04, UID 13)
|
||||||
|
├── webui-entrypoint.sh # Entrypoint for webui service: starts ttyd wrapping claude
|
||||||
├── proxy/
|
├── proxy/
|
||||||
│ └── squid.conf # Squid ACL config — egress allowlist lives here
|
│ └── squid.conf # Squid ACL config — egress allowlist lives here
|
||||||
├── .env.example # Template for ANTHROPIC_API_KEY
|
├── .env.example # Template for ANTHROPIC_API_KEY, WEBUI_PASSWORD, etc.
|
||||||
├── .gitignore # Excludes .env and logs
|
├── .gitignore # Excludes .env and logs
|
||||||
├── .dockerignore # Keeps .env out of build context
|
├── .dockerignore # Keeps .env out of build context
|
||||||
└── README.md # User documentation
|
└── README.md # User documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development Workflow
|
## Development Workflow
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod +x claude.sh
|
chmod +x claude.sh
|
||||||
cp .env.example .env # set ANTHROPIC_API_KEY
|
cp .env.example .env # set ANTHROPIC_API_KEY (and WEBUI_PASSWORD for web mode)
|
||||||
./claude.sh start # build + start proxy + launch Claude interactively
|
./claude.sh start # build + start proxy + launch Claude interactively (CLI)
|
||||||
|
./claude.sh web # build + start proxy + webui (browser terminal on :7681)
|
||||||
./claude.sh update # rebuild images (no cache) after upstream updates
|
./claude.sh update # rebuild images (no cache) after upstream updates
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
bash \
|
bash \
|
||||||
|
ttyd \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Entrypoint used by the webui service (ttyd wrapping claude)
|
||||||
|
COPY --chmod=755 webui-entrypoint.sh /usr/local/bin/webui-entrypoint.sh
|
||||||
|
|
||||||
# Create non-root user
|
# Create non-root user
|
||||||
RUN groupadd -g 1000 claude \
|
RUN groupadd -g 1000 claude \
|
||||||
&& useradd -u 1000 -g claude -m -s /bin/bash claude
|
&& useradd -u 1000 -g claude -m -s /bin/bash claude
|
||||||
|
|
|
||||||
134
README.md
134
README.md
|
|
@ -5,34 +5,36 @@ Runs [Claude Code](https://claude.ai/code) inside an isolated Docker environment
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────┐
|
||||||
│ Host machine │
|
│ Host machine │
|
||||||
│ │
|
│ │
|
||||||
│ claude.sh (control script) │
|
│ claude.sh (control script) │
|
||||||
│ │ │
|
│ │ │
|
||||||
│ ▼ │
|
│ ▼ │
|
||||||
│ ┌─────────────────────────────────────────────┐ │
|
│ ┌──────────────────────────────────────────────────┐ │
|
||||||
│ │ Docker: claude-secure │ │
|
│ │ Docker: claude-secure │ │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ │ ┌─────────────┐ claude-internal │ │
|
│ │ ┌─────────────┐ │ │
|
||||||
│ │ │ claude │◄─────(internal only)───► │ │
|
│ │ │ claude │──┐ claude-internal │ │
|
||||||
│ │ │ (UID 1000) │ │ │ │
|
│ │ │ (UID 1000) │ │ (internal: true) │ │
|
||||||
│ │ └─────────────┘ ┌──────┴──────┐ │ │
|
│ │ └─────────────┘ ├──────────────► ┌──────────┐ │ │
|
||||||
│ │ │ proxy │ │ │
|
│ │ ┌─────────────┐ │ │ proxy │ │ │
|
||||||
│ │ │ (UID 13) │ │ │
|
│ │ │ webui │──┘ │ (UID 13) │ │ │
|
||||||
│ │ └──────┬──────┘ │ │
|
│ │ │ (UID 1000) │ └────┬─────┘ │ │
|
||||||
│ │ proxy-external │ │
|
│ │ │ port 7681 │ proxy-external │ │
|
||||||
│ └─────────────────────────────────────────────┘ │
|
│ │ └─────────────┘ │ │ │
|
||||||
│ │ │
|
│ └──────────────────────────────────────────────────┘ │
|
||||||
│ ▼ │
|
│ │ │
|
||||||
│ internet (allowlisted) │
|
│ ▼ │
|
||||||
└─────────────────────────────────────────────────────┘
|
│ internet (allowlisted) │
|
||||||
|
└──────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`claude` container** — Claude Code, runs as UID 1000, on `claude-internal` only (no internet route)
|
- **`claude`** — Claude Code CLI, UID 1000, on `claude-internal` only
|
||||||
- **`proxy` container** — Squid forward proxy, runs as UID 13, bridges `claude-internal` ↔ internet, enforces egress allowlist
|
- **`webui`** — Claude Code in a browser terminal (ttyd), UID 1000, on `claude-internal` only, port 7681
|
||||||
- **`claude-internal`** — Docker bridge with `internal: true`; Docker adds no default gateway, so containers on this network cannot reach the internet directly
|
- **`proxy`** — Squid forward proxy, UID 13, bridges `claude-internal` ↔ internet with egress allowlist
|
||||||
- **`proxy-external`** — Standard bridge; the proxy sidecar uses this for controlled outbound access
|
- **`claude-internal`** — `internal: true`; no default gateway, containers cannot reach the internet directly
|
||||||
|
- **`proxy-external`** — Standard bridge; proxy sidecar only
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|
@ -45,9 +47,9 @@ Runs [Claude Code](https://claude.ai/code) inside an isolated Docker environment
|
||||||
# 1. Clone / copy this repo
|
# 1. Clone / copy this repo
|
||||||
git clone <repo> docker-claude && cd docker-claude
|
git clone <repo> docker-claude && cd docker-claude
|
||||||
|
|
||||||
# 2. Configure your API key
|
# 2. Configure credentials
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
$EDITOR .env # set ANTHROPIC_API_KEY
|
$EDITOR .env # set ANTHROPIC_API_KEY (and WEBUI_PASSWORD if using web mode)
|
||||||
|
|
||||||
# 3. Make the control script executable
|
# 3. Make the control script executable
|
||||||
chmod +x claude.sh
|
chmod +x claude.sh
|
||||||
|
|
@ -55,62 +57,77 @@ chmod +x claude.sh
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### CLI mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build images, start proxy, launch Claude Code interactively
|
# Build images, start proxy, launch Claude Code interactively
|
||||||
./claude.sh start
|
./claude.sh start
|
||||||
|
|
||||||
# Same as start but skips image rebuild (faster on subsequent runs)
|
# Start proxy if needed, launch Claude Code (faster on subsequent runs)
|
||||||
./claude.sh run
|
./claude.sh run
|
||||||
|
|
||||||
# Stop and remove all containers (proxy + any running sessions)
|
# Mount a host directory as the workspace
|
||||||
./claude.sh stop
|
|
||||||
|
|
||||||
# Rebuild images without cache (e.g. after Claude Code updates)
|
|
||||||
./claude.sh update
|
|
||||||
|
|
||||||
# Tail proxy access logs
|
|
||||||
./claude.sh logs
|
|
||||||
|
|
||||||
# Show container status
|
|
||||||
./claude.sh status
|
|
||||||
|
|
||||||
# Open a debug bash shell inside the Claude container
|
|
||||||
./claude.sh shell
|
|
||||||
```
|
|
||||||
|
|
||||||
### Working with host files
|
|
||||||
|
|
||||||
By default, Claude's workspace is a named Docker volume (`claude-secure-workspace`) — fully isolated from the host.
|
|
||||||
|
|
||||||
To mount a specific host directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
WORKSPACE_DIR=$HOME/myproject ./claude.sh run
|
WORKSPACE_DIR=$HOME/myproject ./claude.sh run
|
||||||
```
|
```
|
||||||
|
|
||||||
The directory is mounted at `/workspace` inside the container.
|
### 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
|
## Egress allowlist
|
||||||
|
|
||||||
Edit `proxy/squid.conf` and add domains to the `allowed_sites` ACL:
|
Edit `proxy/squid.conf` and add domains to the `allowed_sites` ACL:
|
||||||
|
|
||||||
```squid
|
```
|
||||||
acl allowed_sites dstdomain api.anthropic.com
|
acl allowed_sites dstdomain api.anthropic.com
|
||||||
acl allowed_sites dstdomain statsig.anthropic.com
|
acl allowed_sites dstdomain statsig.anthropic.com
|
||||||
# acl allowed_sites dstdomain api.github.com # uncomment if needed
|
# acl allowed_sites dstdomain api.github.com
|
||||||
# acl allowed_sites dstdomain registry.npmjs.org
|
# acl allowed_sites dstdomain registry.npmjs.org
|
||||||
```
|
```
|
||||||
|
|
||||||
Rebuild the proxy after changes:
|
Rebuild after changes:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -p claude-secure build proxy
|
./claude.sh update
|
||||||
./claude.sh stop && ./claude.sh start
|
./claude.sh stop && ./claude.sh start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Security controls
|
## Security controls
|
||||||
|
|
||||||
| Control | Claude container | Proxy container |
|
| Control | claude / webui | proxy |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Non-root user | UID 1000 (`claude`) | UID 13 (`proxy`) |
|
| Non-root user | UID 1000 (`claude`) | UID 13 (`proxy`) |
|
||||||
| `no-new-privileges` | yes | yes |
|
| `no-new-privileges` | yes | yes |
|
||||||
|
|
@ -118,3 +135,4 @@ docker compose -p claude-secure build proxy
|
||||||
| Direct internet access | no (`internal` network only) | allowlisted only |
|
| Direct internet access | no (`internal` network only) | allowlisted only |
|
||||||
| Host filesystem | no mounts by default | none |
|
| Host filesystem | no mounts by default | none |
|
||||||
| Docker socket | not mounted | not mounted |
|
| Docker socket | not mounted | not mounted |
|
||||||
|
| Web auth | basic auth (ttyd `--credential`) | n/a |
|
||||||
|
|
|
||||||
71
claude.sh
71
claude.sh
|
|
@ -118,43 +118,76 @@ cmd_shell() {
|
||||||
dc run --rm --entrypoint /bin/bash $(workspace_flag) claude
|
dc run --rm --entrypoint /bin/bash $(workspace_flag) claude
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_web() {
|
||||||
|
check_deps
|
||||||
|
load_env
|
||||||
|
if [[ -z "${WEBUI_PASSWORD:-}" ]]; then
|
||||||
|
error "WEBUI_PASSWORD is not set. Add it to .env before starting the web interface."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
info "Building images..."
|
||||||
|
dc build
|
||||||
|
info "Starting proxy and web interface..."
|
||||||
|
dc up -d webui
|
||||||
|
local port=7681
|
||||||
|
info "Web interface is up → http://0.0.0.0:${port}"
|
||||||
|
info "Credentials: ${WEBUI_USER:-claude} / [WEBUI_PASSWORD]"
|
||||||
|
warn "To reach it from outside this host, publish the port:"
|
||||||
|
warn " sbx ports <sandbox-name> --publish ${port}:${port}/tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_web_stop() {
|
||||||
|
check_deps
|
||||||
|
info "Stopping web interface..."
|
||||||
|
dc stop webui
|
||||||
|
dc rm -f webui
|
||||||
|
}
|
||||||
|
|
||||||
cmd_help() {
|
cmd_help() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $(basename "$0") <command> [args]
|
Usage: $(basename "$0") <command> [args]
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
start [args] Build images, start proxy, launch Claude Code
|
start [args] Build images, start proxy, launch Claude Code (CLI)
|
||||||
run [args] Start proxy if needed, launch Claude Code
|
run [args] Start proxy if needed, launch Claude Code (CLI)
|
||||||
stop Stop and remove all containers
|
web Build images, start proxy + web interface (browser terminal)
|
||||||
update Rebuild images without cache
|
web-stop Stop the web interface (keeps proxy running)
|
||||||
logs [svc] Tail logs (default: proxy)
|
stop Stop and remove all containers
|
||||||
status Show container status
|
update Rebuild images without cache
|
||||||
shell Open a bash shell in the Claude container (debug)
|
logs [svc] Tail logs (default: proxy)
|
||||||
help Show this message
|
status Show container status
|
||||||
|
shell Open a bash shell in the Claude container (debug)
|
||||||
|
help Show this message
|
||||||
|
|
||||||
Environment variables:
|
Environment variables (set in .env or shell):
|
||||||
ANTHROPIC_API_KEY Required. Set in .env or exported in your shell.
|
ANTHROPIC_API_KEY Required for all modes.
|
||||||
WORKSPACE_DIR Optional. Absolute path to mount as /workspace.
|
WORKSPACE_DIR Optional (CLI mode). Host path to mount as /workspace.
|
||||||
Defaults to a named Docker volume (fully isolated).
|
Defaults to a named Docker volume (fully isolated).
|
||||||
|
WEBUI_USER Web interface username (default: claude).
|
||||||
|
WEBUI_PASSWORD Required for web mode. Basic auth password.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
./claude.sh start
|
./claude.sh start
|
||||||
WORKSPACE_DIR=\$HOME/myproject ./claude.sh run
|
WORKSPACE_DIR=\$HOME/myproject ./claude.sh run
|
||||||
|
./claude.sh web
|
||||||
./claude.sh logs proxy
|
./claude.sh logs proxy
|
||||||
|
./claude.sh logs webui
|
||||||
./claude.sh shell
|
./claude.sh shell
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# ─── Dispatch ─────────────────────────────────────────────────────────────────
|
# ─── Dispatch ─────────────────────────────────────────────────────────────────
|
||||||
case "${1:-help}" in
|
case "${1:-help}" in
|
||||||
start) shift; cmd_start "$@" ;;
|
start) shift; cmd_start "$@" ;;
|
||||||
stop) cmd_stop ;;
|
stop) cmd_stop ;;
|
||||||
run) shift; cmd_run "$@" ;;
|
run) shift; cmd_run "$@" ;;
|
||||||
update) cmd_update ;;
|
web) cmd_web ;;
|
||||||
logs) shift; cmd_logs "${1:-}" ;;
|
web-stop) cmd_web_stop ;;
|
||||||
status) cmd_status ;;
|
update) cmd_update ;;
|
||||||
shell) cmd_shell ;;
|
logs) shift; cmd_logs "${1:-}" ;;
|
||||||
help|-h|--help) cmd_help ;;
|
status) cmd_status ;;
|
||||||
|
shell) cmd_shell ;;
|
||||||
|
help|-h|--help) cmd_help ;;
|
||||||
*)
|
*)
|
||||||
error "Unknown command: ${1}"
|
error "Unknown command: ${1}"
|
||||||
cmd_help
|
cmd_help
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.proxy
|
dockerfile: Dockerfile.proxy
|
||||||
networks:
|
networks:
|
||||||
- claude-internal # reachable by the claude container
|
- claude-internal # reachable by claude and webui containers
|
||||||
- proxy-external # has outbound internet access
|
- proxy-external # has outbound internet access
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
security_opt:
|
security_opt:
|
||||||
|
|
@ -21,7 +21,7 @@ services:
|
||||||
- /var/spool/squid
|
- /var/spool/squid
|
||||||
- /var/log/squid
|
- /var/log/squid
|
||||||
|
|
||||||
# ─── Claude Code container ─────────────────────────────────────────────────
|
# ─── Claude Code CLI container ─────────────────────────────────────────────
|
||||||
# No direct internet access. All egress routes through the proxy sidecar.
|
# No direct internet access. All egress routes through the proxy sidecar.
|
||||||
# Run via "docker compose run --rm claude" (managed by claude.sh).
|
# Run via "docker compose run --rm claude" (managed by claude.sh).
|
||||||
claude:
|
claude:
|
||||||
|
|
@ -48,6 +48,41 @@ services:
|
||||||
# Workspace is injected by claude.sh via --volume flag at run time.
|
# Workspace is injected by claude.sh via --volume flag at run time.
|
||||||
# Default: named Docker volume. Override: set WORKSPACE_DIR on the host.
|
# Default: named Docker volume. Override: set WORKSPACE_DIR on the host.
|
||||||
|
|
||||||
|
# ─── Claude Code web interface ─────────────────────────────────────────────
|
||||||
|
# Serves Claude Code as a browser terminal via ttyd (port 7681).
|
||||||
|
# Protected by HTTP basic auth — set WEBUI_USER / WEBUI_PASSWORD in .env.
|
||||||
|
# Network isolation is identical to the CLI container.
|
||||||
|
webui:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.claude
|
||||||
|
entrypoint: ["/usr/local/bin/webui-entrypoint.sh"]
|
||||||
|
depends_on:
|
||||||
|
proxy:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- claude-internal # only — no route to the internet
|
||||||
|
environment:
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||||
|
- HTTP_PROXY=http://proxy:3128
|
||||||
|
- HTTPS_PROXY=http://proxy:3128
|
||||||
|
- ALL_PROXY=http://proxy:3128
|
||||||
|
- NO_PROXY=localhost,127.0.0.1
|
||||||
|
- WEBUI_USER=${WEBUI_USER:-claude}
|
||||||
|
- WEBUI_PASSWORD=${WEBUI_PASSWORD:-}
|
||||||
|
- WEBUI_PORT=7681
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:7681:7681"
|
||||||
|
volumes:
|
||||||
|
- claude-web-workspace:/workspace
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
# Internal-only: Docker adds no default gateway → no direct internet route
|
# Internal-only: Docker adds no default gateway → no direct internet route
|
||||||
claude-internal:
|
claude-internal:
|
||||||
|
|
@ -57,3 +92,7 @@ networks:
|
||||||
# External: standard bridge with internet access (proxy only)
|
# External: standard bridge with internet access (proxy only)
|
||||||
proxy-external:
|
proxy-external:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Persistent workspace for the web interface
|
||||||
|
claude-web-workspace:
|
||||||
|
|
|
||||||
14
webui-entrypoint.sh
Normal file
14
webui-entrypoint.sh
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Entrypoint for the webui service.
|
||||||
|
# Wraps Claude Code in ttyd (terminal-over-WebSocket) with basic auth.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
: "${WEBUI_PASSWORD:?WEBUI_PASSWORD must be set in .env}"
|
||||||
|
WEBUI_USER="${WEBUI_USER:-claude}"
|
||||||
|
WEBUI_PORT="${WEBUI_PORT:-7681}"
|
||||||
|
|
||||||
|
exec ttyd \
|
||||||
|
--port "${WEBUI_PORT}" \
|
||||||
|
--writable \
|
||||||
|
--credential "${WEBUI_USER}:${WEBUI_PASSWORD}" \
|
||||||
|
claude
|
||||||
Loading…
Add table
Add a link
Reference in a new issue