Mount ~/specs read-write; add bubblewrap/socat (partial fix)
~/.claude/CLAUDE.md (already carried into the container) tells agents to
write specs to ~/specs/ instead of a project-local path - that instruction
was already in effect inside the sandbox, but nothing mounted the
directory it points at, so an agent following it would write into a path
that vanishes with the container. Bind-mounted read-write at the fixed
host path ~/specs (auto-created if missing), same pattern as the shared
scratch space but not configurable - it's tied to the CLAUDE.md
convention, not a per-run choice.
Also adds bubblewrap/socat, which Claude Code checks for before offering
its own internal per-command sandboxing and /remote-control. This clears
the "Sandbox disabled: dependencies are missing" warning, but doesn't
actually fix /remote-control: bwrap still can't create a namespace on this
host ("kernel does not allow non-privileged user namespaces"), and even
adding CAP_SYS_ADMIN to the container only got further to a mount-
propagation permission error, not further progress. Making it actually
work would need privilege escalation approaching full --privileged mode,
which would undo most of this session's hardening - not pursued. Kept the
packages anyway since they at least make the startup warning accurate
rather than noise, in case a future decision revisits this tradeoff.
All 22 existing tests still pass.
This commit is contained in:
@@ -58,6 +58,12 @@ fi
|
||||
SANDBOX_SHARE_DIR="${SANDBOX_SHARE_DIR:-$HOME/.local/share/claude-sandbox/shared}"
|
||||
mkdir -p "$SANDBOX_SHARE_DIR"
|
||||
|
||||
# ~/.claude/CLAUDE.md (carried into the container) tells agents to write
|
||||
# specs to ~/specs/ rather than a project-local path - make sure that
|
||||
# actually exists before it's bind-mounted, for a fresh machine that hasn't
|
||||
# used the convention yet.
|
||||
mkdir -p "$HOME/specs"
|
||||
|
||||
export WORKSPACE_DIR
|
||||
export SANDBOX_SHARE_DIR
|
||||
export SSH_AUTH_SOCK
|
||||
|
||||
@@ -8,8 +8,13 @@ ENV TZ="$TZ"
|
||||
ARG CLAUDE_CODE_VERSION=latest
|
||||
|
||||
# Install basic development tools, iptables/ipset (for the egress firewall),
|
||||
# openssh-client (for git-over-ssh and agent forwarding), and the toolchain
|
||||
# nvim's plugin config (config/nvim, telescope/treesitter/mason) expects.
|
||||
# openssh-client (for git-over-ssh and agent forwarding), the toolchain
|
||||
# nvim's plugin config (config/nvim, telescope/treesitter/mason) expects, and
|
||||
# bubblewrap/socat, which Claude Code's own internal per-command sandboxing
|
||||
# (~/.claude/settings.json's "sandbox" block - separate from and in addition
|
||||
# to this container) and /remote-control both need to even be offered,
|
||||
# without which Claude Code detects the environment can't support them and
|
||||
# silently disables both rather than erroring when used.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
less \
|
||||
git \
|
||||
@@ -42,6 +47,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
cowsay \
|
||||
fortune-mod \
|
||||
fortunes-min \
|
||||
bubblewrap \
|
||||
socat \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
|
||||
&& ln -s "$(command -v fdfind)" /usr/local/bin/fd
|
||||
|
||||
|
||||
@@ -83,6 +83,13 @@ touching the actual project checkout. It persists across sessions since it's
|
||||
a real directory, not a container volume. Set `SANDBOX_SHARE_DIR` to point
|
||||
it somewhere else.
|
||||
|
||||
`~/specs` is also bind-mounted read-write, at the fixed host path `~/specs`
|
||||
(created automatically if it doesn't exist yet) - not configurable like
|
||||
`SANDBOX_SHARE_DIR`, since it exists to make the global `CLAUDE.md`
|
||||
convention of writing specs to `~/specs/` (also carried into the container)
|
||||
actually work there instead of writing into a path that vanishes with the
|
||||
session.
|
||||
|
||||
## Sandbox self-awareness
|
||||
|
||||
A `SessionStart` hook (`~/.claude/hooks/sandbox-context.sh`, registered in
|
||||
|
||||
@@ -61,6 +61,13 @@ services:
|
||||
# since most of its content must stay writable for normal operation.
|
||||
# See README Security notes for this residual risk.
|
||||
- ${HOME}/.claude.json:/home/node/.claude.json
|
||||
# ~/.claude/CLAUDE.md (mounted above) tells agents to write specs to
|
||||
# ~/specs/ rather than a project-local path - that instruction is
|
||||
# already in effect inside the container (CLAUDE.md carries over), so
|
||||
# the directory it points at needs to actually exist and be writable
|
||||
# here too, or an agent following it just writes into an ephemeral
|
||||
# path inside the container that vanishes with the session.
|
||||
- ${HOME}/specs:/home/node/specs
|
||||
# ~/.claude/hooks is a symlink into ~/dotfiles/config/claude/hooks (an
|
||||
# absolute host path); mount dotfiles at the same path so it resolves
|
||||
# in the container too. Read-only: hooks are only ever read/executed
|
||||
|
||||
Reference in New Issue
Block a user