Compare commits

...

11 Commits

Author SHA1 Message Date
YEVHENII SHCHERBINA 0f9d0bf710 claude-code integration with strict jail 2025-10-19 22:07:26 +00:00
Benjamin 7ba975a2d8 remove the localhost allowance, going to fix on the boundary side of things 2025-10-06 17:12:08 -05:00
Benjamin 919c1bbbc5 try changing claudes mcp port 2025-10-06 16:48:26 -05:00
Benjamin 2cf20a4bda try just localhost:8080 for the allow rule 2025-10-06 16:18:20 -05:00
Benjamin 7acb90b2b8 fmt 2025-10-06 15:47:57 -05:00
Benjamin 01c436a751 specifically allow healthz 2025-10-06 15:34:31 -05:00
Benjamin 9796e05fdc allow localhost for healthz and other reporting 2025-10-06 15:21:35 -05:00
Benjamin 2a3e76d31c it's behind exp omfg 2025-10-06 15:06:53 -05:00
Benjamin 4a1bf33b28 additional urls and agentapi required urls 2025-10-06 14:36:30 -05:00
Benjamin 00337b3bdf default unprivileged 2025-10-06 11:19:21 -05:00
Benjamin a30ef222d5 init simple integration 2025-10-06 11:00:36 -05:00
3 changed files with 103 additions and 1 deletions
@@ -192,6 +192,30 @@ variable "claude_md_path" {
default = "$HOME/.claude/CLAUDE.md"
}
variable "enable_boundary" {
type = bool
description = "Whether to enable coder boundary for network filtering"
default = false
}
variable "boundary_log_dir" {
type = string
description = "Directory for boundary logs"
default = "/tmp/boundary_logs"
}
variable "boundary_unprivileged" {
type = bool
description = "Whether to use --unprivileged flag with coder boundary (recommended for security)"
default = true
}
variable "boundary_additional_allowed_urls" {
type = list(string)
description = "Additional URLs to allow through boundary (in addition to default allowed URLs)"
default = []
}
resource "coder_env" "claude_code_md_path" {
count = var.claude_md_path == "" ? 0 : 1
@@ -222,6 +246,12 @@ resource "coder_env" "claude_api_key" {
value = var.claude_api_key
}
resource "coder_env" "mcp_server_port" {
agent_id = var.agent_id
name = "MCP_SERVER_PORT"
value = "8081"
}
locals {
# we have to trim the slash because otherwise coder exp mcp will
# set up an invalid claude config
@@ -231,6 +261,8 @@ locals {
start_script = file("${path.module}/scripts/start.sh")
module_dir_name = ".claude-module"
remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.sh"))
# Extract hostname from access_url for boundary --allow flag
coder_host = replace(replace(data.coder_workspace.me.access_url, "https://", ""), "http://", "")
}
module "agentapi" {
@@ -270,6 +302,11 @@ module "agentapi" {
ARG_PERMISSION_MODE='${var.permission_mode}' \
ARG_WORKDIR='${local.workdir}' \
ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \
ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \
ARG_BOUNDARY_LOG_DIR='${var.boundary_log_dir}' \
ARG_BOUNDARY_UNPRIVILEGED='${var.boundary_unprivileged}' \
ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS='${join(" ", var.boundary_additional_allowed_urls)}' \
ARG_CODER_HOST='${local.coder_host}' \
/tmp/start.sh
EOT
@@ -187,3 +187,29 @@ run "test_claude_code_permission_mode_validation" {
error_message = "Permission mode should be one of the valid options"
}
}
run "test_claude_code_with_boundary" {
command = plan
variables {
agent_id = "test-agent-boundary"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_log_dir = "/tmp/test-boundary-logs"
}
assert {
condition = var.enable_boundary == true
error_message = "Boundary should be enabled"
}
assert {
condition = var.boundary_log_dir == "/tmp/test-boundary-logs"
error_message = "Boundary log dir should be set correctly"
}
assert {
condition = local.coder_host != ""
error_message = "Coder host should be extracted from access URL"
}
}
@@ -15,6 +15,10 @@ ARG_DANGEROUSLY_SKIP_PERMISSIONS=${ARG_DANGEROUSLY_SKIP_PERMISSIONS:-}
ARG_PERMISSION_MODE=${ARG_PERMISSION_MODE:-}
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d)
ARG_ENABLE_BOUNDARY=${ARG_ENABLE_BOUNDARY:-false}
ARG_BOUNDARY_LOG_DIR=${ARG_BOUNDARY_LOG_DIR:-"/tmp/boundary_logs"}
ARG_BOUNDARY_UNPRIVILEGED=${ARG_BOUNDARY_UNPRIVILEGED:-true}
ARG_CODER_HOST=${ARG_CODER_HOST:-}
echo "--------------------------------"
@@ -25,6 +29,10 @@ printf "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIO
printf "ARG_PERMISSION_MODE: %s\n" "$ARG_PERMISSION_MODE"
printf "ARG_AI_PROMPT: %s\n" "$ARG_AI_PROMPT"
printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
printf "ARG_ENABLE_BOUNDARY: %s\n" "$ARG_ENABLE_BOUNDARY"
printf "ARG_BOUNDARY_LOG_DIR: %s\n" "$ARG_BOUNDARY_LOG_DIR"
printf "ARG_BOUNDARY_UNPRIVILEGED: %s\n" "$ARG_BOUNDARY_UNPRIVILEGED"
printf "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST"
echo "--------------------------------"
@@ -74,7 +82,38 @@ function start_agentapi() {
fi
fi
printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"
agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}"
if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then
mkdir -p "$ARG_BOUNDARY_LOG_DIR"
printf "Starting with coder boundary enabled\n"
# Build boundary args with conditional --unprivileged flag
BOUNDARY_ARGS=(--log-dir "$ARG_BOUNDARY_LOG_DIR")
# Add default allowed URLs
BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "$ARG_CODER_HOST")
# Add any additional allowed URLs from the variable
if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then
IFS=' ' read -ra ADDITIONAL_URLS <<< "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS"
for url in "${ADDITIONAL_URLS[@]}"; do
BOUNDARY_ARGS+=(--allow "$url")
done
fi
git clone https://github.com/coder/boundary
cd boundary
git checkout yevhenii/proxy-v3
go install ./cmd/...
BOUNDARY_ARGS+=(--proxy-port=8087)
agentapi server --allowed-hosts="*" --type claude --term-width 67 --term-height 1190 -- \
sudo -E env PATH=$PATH setpriv --inh-caps=+net_admin --ambient-caps=+net_admin --bounding-set=+net_admin /home/coder/go/bin/boundary "${BOUNDARY_ARGS[@]}" -- \
claude
#"${ARGS[@]}"
else
agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}"
fi
}
validate_claude_installation