Commit Graph

6 Commits

Author SHA1 Message Date
Thomas Friedel 05d53ade55 feat(sandbox): install document + DS tooling for vendored skills
The docx/pdf/pptx/xlsx skills' scripts assume a working set of Python
libraries (pypdf, pdfplumber, reportlab, openpyxl, lxml, defusedxml,
Pillow, markitdown) and native tools (soffice headless, pdftoppm). Without
them, first-turn invocations of those skills fail with ImportError — and
the egress allowlist's pypi.org entry can't save us because pip install
on every turn adds latency and exercises an attack surface we'd rather
not hit on the critical path.

Bakes everything into the image:
  - libreoffice-{core,writer,calc,impress}: drops the GUI/Java bloat of
    the meta package while keeping headless conversion for all three
    office formats. Adds ~500 MB vs ~800+ MB for the full meta.
  - poppler-utils + fonts-dejavu: pdftoppm binary, pdf2image backend,
    and baseline glyph set so rendered PDFs aren't empty boxes.
  - pip: pypdf pdfplumber pdf2image reportlab Pillow openpyxl
    python-docx python-pptx pandas numpy matplotlib lxml defusedxml
    markitdown[pptx]. Also covers the explicit DS ask (pandas/numpy/
    matplotlib) so ad-hoc analysis doesn't hit pip on first use.
  - npm -g docx: the JS library the docx skill prefers for generating
    richly formatted new documents.

Final image is ~4.6 GB (up from ~1 GB). LibreOffice is the dominant term.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:49:08 +02:00
Thomas Friedel 5c41f57f0b feat(sandbox): vendor Anthropic document + frontend skills into image
Bakes docx, pdf, pptx, xlsx, and frontend-design skills from
github.com/anthropics/skills into /opt/claude-skills/ at image build time
(sparse-checkout, shallow clone). On each chat turn the pipe symlinks them
into $CLAUDE_CONFIG_DIR/skills/ via the new ensure_skills() method.

Symlinks (not copies) mean:
  - zero per-chat disk cost regardless of user count;
  - image rebuilds that add or update a skill propagate automatically to
    existing chats on their next turn (ln -sfn replaces stale targets);
  - skills are read-only from the user's perspective — any attempt to
    mutate them hits /opt/claude-skills, which is root-owned.

The skill list is surfaced as a valve (SKILLS) so operators can disable
individual skills without a rebuild; it must remain a subset of what the
Dockerfile CLAUDE_SKILLS arg pulled in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:44:20 +02:00
Thomas Friedel 6af5180513 feat(sandbox): extend managed allowlist with build + dataset egress domains
Widens the Bash-subprocess egress allowlist for common agent workflows:
Anthropic direct (api, statsig), Ubuntu mirrors, NodeSource/deb for Node,
crates.io (Rust), yarn, full npm + pypi groups, GitHub release assets,
and wildcards for geoboundaries, googleapis, jsdelivr — covers dataset
fetches and CDN-backed library assets that previously failed silently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:39:34 +02:00
Thomas Friedel c5f6764470 feat(sandbox): enable Claude Code built-in sandbox for Bash + WebFetch egress
Closes #10. Until now the sandbox container had unrestricted outbound NAT
on sandbox-net — a compromised agent could curl arbitrary hosts, pip/npm
install malicious packages, or exfiltrate files via WebFetch. The
anthropic-proxy path-allowlist (#6) guarded only the credential-injection
proxy, not general egress.

Two layers added, both shipped as managed (enterprise) settings so user
and project settings cannot downgrade them:

- sandbox.*  → bubblewrap + local proxy gating Bash subprocesses (and all
  their children: pip, npm, git, curl). enableWeakerNestedSandbox=true
  because we run inside Docker without privileged user namespaces;
  accepted because the Docker container boundary is unchanged and this
  adds new egress filtering that previously did not exist.
- permissions.* → deny-by-default WebFetch with explicit domain allows
  (WebFetch bypasses the OS sandbox and needs its own allowlist).

failIfUnavailable=true + allowUnsandboxedCommands=false close the two
silent-fallback paths: if bubblewrap/socat install breaks, the container
fails loud instead of serving unsandboxed bash; and the
dangerouslyDisableSandbox escape hatch is disabled so sandbox violations
can't be bypassed via a permission prompt in our unattended deployment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:31:07 +02:00
Thomas Friedel 3cec4dc871 feat(sandbox): pin CLI, reap orphans, isolate chats, cleanup script
Addresses four hardening items in one change:

- #8 Pin @anthropic-ai/claude-code to 2.1.114 via Dockerfile ARG.
  `docker compose build --build-arg CLAUDE_CODE_VERSION=X.Y.Z` upgrades.

- #2 Move `client.kill()` from an `except CancelledError` branch into a
  `finally` guarded by an `exited_cleanly` flag. Any abnormal exit path
  (httpx timeout, generator GC'd, unexpected exception) now reaps the
  open-terminal child — previously only asyncio cancellation did, so
  dropped browsers left claude billing tokens until EXECUTE_TIMEOUT.

- #9 Per-chat CLAUDE_CONFIG_DIR=~/chat-<id>/.claude. Two concurrent
  chats for the same OWUI user no longer race each other on a shared
  ~/.claude/. Safe to shard now that credentials live in the proxy
  rather than .credentials.json.

- #1 sandbox/cleanup.sh installed at /opt/cleanup.sh. Not auto-run;
  README documents dry-run-by-default usage via `docker compose exec`.
  CHAT_TTL_DAYS and SESSION_TTL_DAYS control retention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:20:29 +02:00
Thomas Friedel 7e7fa4cb77 feat: sandboxed Claude Code pipe via open-terminal
Adds `claude_agent_pipe_sandboxed.py`, a self-contained OpenWebUI pipe
that shells `claude --output-format stream-json` inside an open-webui/
open-terminal container instead of running the Claude Agent SDK
in-process. Each OWUI user gets a dedicated Linux account (mapped via
the X-User-Id header) with a per-chat workspace directory that persists
across turns and whose generated PNG/PDF/CSV artifacts are auto-inlined
into the chat.

Scaffolding under `sandbox/`:
- Dockerfile extending ghcr.io/open-webui/open-terminal with Claude
  Code pre-installed system-wide (inherited by every provisioned user)
- docker-compose.yml + README covering standalone deployment
- Async HTTP client + stream-json runner for programmatic use

`sync_pipe.py` pushes the pipe to a running OWUI via the admin
functions API so iteration doesn't require manual repaste.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:02:01 +02:00