feat(260504-bw4): add --with-ssh and --ssh-key flags to claudebox

This commit is contained in:
Christopher Mühl 2026-05-04 08:38:45 +00:00
parent 29996a2d40
commit 41ebf10458

View file

@ -4,6 +4,8 @@ DRY_RUN=false
CHECK_MODE=false
SHELL_MODE=false
GC_MODE=false
WITH_SSH=false
SSH_KEYS=()
CLAUDE_ARGS=()
while (( $# > 0 )); do
@ -13,6 +15,12 @@ while (( $# > 0 )); do
--check) CHECK_MODE=true ;;
--shell) SHELL_MODE=true ;;
--gc) GC_MODE=true ;;
--with-ssh) WITH_SSH=true ;;
--ssh-key)
shift
[[ $# -gt 0 ]] || { echo "Error: --ssh-key requires a path" >&2; exit 1; }
SSH_KEYS+=("${1/#\~/$HOME}")
;;
--) shift; CLAUDE_ARGS+=("$@"); break ;;
*) CLAUDE_ARGS+=("$1") ;;
esac
@ -20,6 +28,22 @@ while (( $# > 0 )); do
done
export SKIP_AUDIT # consumed by Plan 02 audit display
# Validate and resolve SSH key paths
for _i in "${!SSH_KEYS[@]}"; do
_key="${SSH_KEYS[$_i]}"
# Expand ~ if not already done
_key="${_key/#\~/$HOME}"
# Make absolute
if [[ "$_key" != /* ]]; then
_key="$PWD/$_key"
fi
if [[ ! -f "$_key" || ! -r "$_key" ]]; then
echo "Error: --ssh-key path does not exist or is not readable: $_key" >&2
exit 1
fi
SSH_KEYS[$_i]="$_key"
done
# Garbage-collect stale instance directories (D-11, INST-04)
gc_instances() {
local removed=0
@ -106,6 +130,30 @@ else
BOLD="" RESET="" DIM="" CYAN="" YELLOW="" GREEN="" RED=""
fi
# SSH agent validation (must be after ANSI vars are set)
if [[ "$WITH_SSH" == true ]]; then
if [[ -v SSH_AUTH_SOCK && -S "$SSH_AUTH_SOCK" ]]; then
: # agent is running, keep WITH_SSH=true
else
echo "${YELLOW}Warning: --with-ssh given but SSH_AUTH_SOCK is unset or not a socket; agent will not be forwarded.${RESET}" >&2
WITH_SSH=false
fi
fi
# Compute SSH active state
if [[ "$WITH_SSH" == true ]] || (( ${#SSH_KEYS[@]} > 0 )); then
SSH_ACTIVE=true
else
SSH_ACTIVE=false
fi
# Determine if known_hosts should be mounted
if [[ "$SSH_ACTIVE" == true && -f "$HOME/.ssh/known_hosts" ]]; then
KNOWN_HOSTS_MOUNT=true
else
KNOWN_HOSTS_MOUNT=false
fi
# Mask sensitive values (D-04)
mask_value() {
local name="$1" value="$2"
@ -290,6 +338,13 @@ for var in "${HOST_ALLOWLIST[@]}"; do
fi
done
# SSH_AUTH_SOCK: pass into sandbox when agent forwarding is active
if [[ "$WITH_SSH" == true ]]; then
ENV_ARGS+=(--setenv SSH_AUTH_SOCK "$SSH_AUTH_SOCK")
AUDIT_HOST_KEYS+=(SSH_AUTH_SOCK)
AUDIT_HOST_VALS[SSH_AUTH_SOCK]="$SSH_AUTH_SOCK"
fi
# CLAUDEBOX_EXTRA_ENV escape hatch (D-03, comma-separated)
if [[ -v CLAUDEBOX_EXTRA_ENV ]]; then
IFS=',' read -ra EXTRAS <<< "$CLAUDEBOX_EXTRA_ENV"
@ -367,6 +422,22 @@ print_audit() {
if [[ "$CREDS_MOUNT" == true ]]; then
printf ' %-12s %s (read-write)\n' "credentials" "$CREDS_FILE" >&2
fi
if [[ "$SSH_ACTIVE" == true ]]; then
if [[ "$WITH_SSH" == true ]]; then
printf ' %-12s %s (read-write, --with-ssh)\n' "agent" "$SSH_AUTH_SOCK" >&2
fi
for _key in "${SSH_KEYS[@]}"; do
_base=$(basename "$_key")
printf ' %-12s %s (read-only)\n' "ssh-key" "$_key" >&2
if [[ -f "${_key}.pub" ]]; then
printf ' %-12s %s (read-only)\n' "ssh-key" "${_key}.pub" >&2
fi
unset _base
done
if [[ "$KNOWN_HOSTS_MOUNT" == true ]]; then
printf ' %-12s %s (read-only)\n' "known_hosts" "$HOME/.ssh/known_hosts" >&2
fi
fi
echo "" >&2
@ -442,6 +513,24 @@ if [[ "$DRY_RUN" == true ]]; then
if [[ "$CREDS_MOUNT" == true ]]; then
echo " --bind $CREDS_FILE $HOME/.claude/.credentials.json \\"
fi
if [[ "$SSH_ACTIVE" == true ]]; then
echo " --dir $HOME/.ssh \\"
if [[ "$WITH_SSH" == true ]]; then
echo " --bind $SSH_AUTH_SOCK $SSH_AUTH_SOCK \\"
fi
for _dry_key in "${SSH_KEYS[@]}"; do
_dry_base=$(basename "$_dry_key")
echo " --ro-bind $_dry_key $HOME/.ssh/$_dry_base \\"
if [[ -f "${_dry_key}.pub" ]]; then
echo " --ro-bind ${_dry_key}.pub $HOME/.ssh/${_dry_base}.pub \\"
fi
unset _dry_base
done
if [[ "$KNOWN_HOSTS_MOUNT" == true ]]; then
echo " --ro-bind $HOME/.ssh/known_hosts $HOME/.ssh/known_hosts \\"
fi
fi
unset _dry_key
printf ' --ro-bind %q %s/.gitconfig \\\n' "$GITCONFIG_TMP" "$HOME"
echo " --bind $CWD $CWD \\"
echo " --chdir $CWD \\"
@ -485,6 +574,24 @@ fi
if [[ "$CREDS_MOUNT" == true ]]; then
BWRAP_ARGS+=(--bind "$CREDS_FILE" "$HOME/.claude/.credentials.json")
fi
if [[ "$SSH_ACTIVE" == true ]]; then
BWRAP_ARGS+=(--dir "$HOME/.ssh")
if [[ "$WITH_SSH" == true ]]; then
BWRAP_ARGS+=(--bind "$SSH_AUTH_SOCK" "$SSH_AUTH_SOCK")
fi
for _key in "${SSH_KEYS[@]}"; do
_base=$(basename "$_key")
BWRAP_ARGS+=(--ro-bind "$_key" "$HOME/.ssh/$_base")
if [[ -f "${_key}.pub" ]]; then
BWRAP_ARGS+=(--ro-bind "${_key}.pub" "$HOME/.ssh/${_base}.pub")
fi
unset _base
done
if [[ "$KNOWN_HOSTS_MOUNT" == true ]]; then
BWRAP_ARGS+=(--ro-bind "$HOME/.ssh/known_hosts" "$HOME/.ssh/known_hosts")
fi
fi
unset _key
BWRAP_ARGS+=(
--ro-bind "$GITCONFIG_TMP" "$HOME/.gitconfig"
--bind "$CWD" "$CWD"