openwebui-claude-code/sandbox/Dockerfile

89 lines
4.0 KiB
Docker

FROM ghcr.io/open-webui/open-terminal:latest
# Pin claude-code CLI so two clean builds from the same source produce
# identical behaviour. Override at build time:
# docker compose build --build-arg CLAUDE_CODE_VERSION=X.Y.Z open-terminal
# When bumping the default here, keep the image rebuild + test cycle
# together — the stream-json schema can change between versions.
ARG CLAUDE_CODE_VERSION=2.1.114
# Base image runs as uid=1000(user) with npm prefix /usr, which requires
# root to write. Switch to root for the global install, then back so the
# container's runtime user and entrypoint stay identical to upstream.
USER root
RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
&& claude --version
# bubblewrap + socat are the Linux backends for Claude Code's built-in
# `/sandbox` feature. Without them, Claude falls back to unsandboxed bash —
# our managed-settings.json sets failIfUnavailable=true so that silent
# degradation becomes a hard error instead. git is needed for the skill
# vendoring step below.
RUN apt-get update \
&& apt-get install -y --no-install-recommends bubblewrap socat git ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Native deps for the document skills:
# libreoffice-{core,writer,calc,impress} — headless `soffice` for .doc↔.docx
# conversion, tracked-change acceptance, xlsx recalc, and pptx thumbs.
# poppler-utils — pdftoppm binary + pdf2image Python backend.
# fonts-dejavu — baseline sans/serif so rendered PDFs aren't blank boxes.
# The LibreOffice subpackages total ~500 MB; installing the core meta
# package would add Java + GUI deps we never use. Pandoc (docx→markdown
# reading path) is already in the base image.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libreoffice-core libreoffice-writer libreoffice-calc \
libreoffice-impress \
poppler-utils fonts-dejavu \
&& rm -rf /var/lib/apt/lists/*
# Python libraries used by the skill scripts + standard data-science
# tooling so ad-hoc analysis doesn't require a per-turn `pip install`
# (which would exercise the egress allowlist every time and slow the
# first response). Installed system-wide into the base image's Python
# 3.12 under /usr/local (not PEP 668 managed, so no --break-system
# flag needed).
RUN pip3 install --no-cache-dir \
pypdf pdfplumber pdf2image reportlab Pillow \
openpyxl python-docx python-pptx \
pandas numpy matplotlib \
lxml defusedxml \
'markitdown[pptx]'
# Node package for the docx skill's "Creating New Documents" path —
# docx-js is preferred over python-docx when generating richly formatted
# documents from scratch (tables of contents, footnotes, columns, etc.).
RUN npm install -g docx
# Vendor a curated set of Anthropic skills into /opt/claude-skills. Each
# chat's CLAUDE_CONFIG_DIR/.claude/skills/ symlinks here at runtime, so all
# users share a read-only copy without per-chat duplication. To update the
# set, change the sparse-checkout list and rebuild — running chats pick up
# the new set on their next turn (symlinks re-point).
ARG CLAUDE_SKILLS="docx pdf pptx xlsx frontend-design"
RUN set -eux; \
mkdir -p /opt/claude-skills; \
tmp="$(mktemp -d)"; \
git clone --depth 1 --filter=blob:none --sparse \
https://github.com/anthropics/skills.git "$tmp"; \
cd "$tmp"; \
paths=""; for s in $CLAUDE_SKILLS; do paths="$paths skills/$s"; done; \
git sparse-checkout set $paths; \
for s in $CLAUDE_SKILLS; do \
cp -r "skills/$s" "/opt/claude-skills/$s"; \
done; \
cd /; rm -rf "$tmp"; \
ls /opt/claude-skills/
# Managed (enterprise) settings live at the system-wide path and take
# precedence over per-user and per-project settings. This is how we pin
# Bash egress + WebFetch allowlists: users can extend via their own
# settings, but cannot downgrade these rules.
COPY managed-settings.json /etc/claude-code/managed-settings.json
RUN chmod 0644 /etc/claude-code/managed-settings.json
COPY cleanup.sh /opt/cleanup.sh
RUN chmod +x /opt/cleanup.sh
USER user