Abort on invalid allowlist lines; refuse launch dirs exposing $HOME or the allowlist
This commit is contained in:
@@ -15,6 +15,8 @@ agent-sandbox --exec bash # a shell in the sandbox instead of claude
|
||||
- `$PWD` is mounted read-write at the same path. Files you create there are
|
||||
owned by your host user. Nothing else from the host is mounted, and SELinux
|
||||
labels on your files are never changed (`label=disable`).
|
||||
- It refuses to launch from `$HOME`, any parent of it, or any directory
|
||||
containing the allowlist.
|
||||
- Claude's home is the `agent-sandbox-home` podman volume (login, config and
|
||||
nested container images live here). All sessions share it.
|
||||
- `podman` works inside the sandbox (nested rootless). The host podman socket is
|
||||
|
||||
+24
-2
@@ -61,7 +61,9 @@ router_config() {
|
||||
local -A ip=()
|
||||
local hosts tcp="" forwards="0.0.0.0:443:$rip:443 0.0.0.0:80:$rip:80"
|
||||
|
||||
hosts=$(allowlist_entries "$allowlist" | awk '{print $1}' | uniq)
|
||||
local entries
|
||||
entries=$(allowlist_entries "$allowlist") || return
|
||||
hosts=$(awk '{print $1}' <<<"$entries" | uniq)
|
||||
for host in $hosts; do
|
||||
[[ $host == .* ]] && continue
|
||||
ip[$host]=127.1.$((i / 250)).$((i % 250 + 1))
|
||||
@@ -72,7 +74,7 @@ router_config() {
|
||||
tcp+=" server { listen $rip:$((PORT_BASE + j)); set \$up $host:$port; proxy_pass \$up; }"$'\n'
|
||||
forwards+=" ${ip[$host]}:$port:$rip:$((PORT_BASE + j))"
|
||||
j=$((j + 1))
|
||||
done < <(allowlist_entries "$allowlist")
|
||||
done <<<"$entries"
|
||||
|
||||
{
|
||||
printf '%s\n' no-resolv no-hosts bind-interfaces "listen-address=$rip" "address=/#/"
|
||||
@@ -137,6 +139,24 @@ EOF
|
||||
echo "$forwards"
|
||||
}
|
||||
|
||||
# True if path $1 is $2 or lies under it.
|
||||
inside() {
|
||||
[[ $1 == "$2" || $1 == "${2%/}"/* ]]
|
||||
}
|
||||
|
||||
# Refuse launch dirs that would mount $HOME or the allowlist read-write.
|
||||
check_launch_dir() {
|
||||
local pwd home dir
|
||||
pwd=$(realpath "$PWD")
|
||||
home=$(realpath -m "$HOME")
|
||||
inside "$home" "$pwd" && die "refusing to mount $pwd: it is or contains \$HOME"
|
||||
for dir in "$CONFIG_DIR" "$(dirname "$ALLOWLIST")"; do
|
||||
dir=$(realpath -m "$dir")
|
||||
inside "$dir" "$pwd" && die "refusing to mount $pwd: it contains $dir"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
main() {
|
||||
local cmd=(claude "$@")
|
||||
if [[ ${1:-} == --exec ]]; then
|
||||
@@ -144,6 +164,7 @@ main() {
|
||||
cmd=("$@")
|
||||
fi
|
||||
[[ $PWD == *:* ]] && die "cannot mount a directory containing ':'"
|
||||
check_launch_dir
|
||||
|
||||
if [[ ! -f $ALLOWLIST ]]; then
|
||||
local default
|
||||
@@ -154,6 +175,7 @@ main() {
|
||||
cp "$default" "$ALLOWLIST"
|
||||
echo "agent-sandbox: created $ALLOWLIST from defaults" >&2
|
||||
fi
|
||||
allowlist_entries "$ALLOWLIST" >/dev/null || exit 1
|
||||
|
||||
local session router tmp subnet rip forwards
|
||||
session=agent-sandbox-$(date +%s)-$$
|
||||
|
||||
@@ -70,4 +70,32 @@ done
|
||||
|
||||
check default-allowlist-parses 0 "$( (allowlist_entries allowed-hosts >/dev/null) 2>&1; echo $?)"
|
||||
|
||||
f=$(list 'ok.example.com' 'bad host' 'z.example.com')
|
||||
check router-config-fails-on-bad-line 1 "$( (router_config "$f" "$(mktemp -d)" 10.89.7.2) >/dev/null 2>&1; echo $?)"
|
||||
|
||||
# Launch through main with a stub podman that logs its calls.
|
||||
work=$(mktemp -d)
|
||||
mkdir -p "$work/bin" "$work/home/proj" "$work/other"
|
||||
printf '%s\n' '#!/bin/sh' "echo \"\$*\" >>$work/podman.log" \
|
||||
'case "$*" in "network inspect"*) echo 10.89.7.0/24 ;; esac' >"$work/bin/podman"
|
||||
chmod +x "$work/bin/podman"
|
||||
launch() {
|
||||
local dir=$1
|
||||
shift
|
||||
rm -f "$work/podman.log"
|
||||
(cd "$dir" && env PATH="$work/bin:$PATH" HOME="$work/home" XDG_RUNTIME_DIR="$work" "$@" \
|
||||
"$OLDPWD/agent-sandbox" --exec true) >/dev/null 2>&1
|
||||
echo "$? $([[ -s $work/podman.log ]] && echo podman || echo none)"
|
||||
}
|
||||
good=$(list 'api.anthropic.com')
|
||||
check launch-from-home-subdir "0 podman" "$(launch "$work/home/proj" AGENT_SANDBOX_ALLOWLIST="$good")"
|
||||
check launch-refuses-bad-line "1 none" "$(launch "$work/home/proj" AGENT_SANDBOX_ALLOWLIST="$(list 'ok.example.com' 'bad host')")"
|
||||
check launch-refuses-home "1 none" "$(launch "$work/home" AGENT_SANDBOX_ALLOWLIST="$good")"
|
||||
check launch-refuses-home-ancestor "1 none" "$(launch "$work" AGENT_SANDBOX_ALLOWLIST="$good")"
|
||||
check launch-refuses-root "1 none" "$(launch / AGENT_SANDBOX_ALLOWLIST="$good")"
|
||||
ln -s "$work/home" "$work/homelink"
|
||||
check launch-refuses-home-symlink "1 none" "$(launch "$work/homelink" AGENT_SANDBOX_ALLOWLIST="$good")"
|
||||
check launch-refuses-config-parent "1 none" "$(launch "$work/other" XDG_CONFIG_HOME="$work/other/cfg" AGENT_SANDBOX_ALLOWLIST="$good")"
|
||||
check launch-refuses-allowlist-dir "1 none" "$(launch "$work/home/proj" AGENT_SANDBOX_ALLOWLIST="$work/home/proj/hosts")"
|
||||
|
||||
((fails == 0)) || { echo "$fails failed"; exit 1; }
|
||||
|
||||
Reference in New Issue
Block a user