Skip to content

Commit a35986d

Browse files
feat: initial boundary integration with claude code (#455)
Closes # ## Description <!-- Briefly describe what this PR does and why --> ## Type of Change - [ ] New module - [ ] Bug fix - [x] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information <!-- Delete this section if not applicable --> **Path:** `registry/[namespace]/modules/[module-name]` **New version:** `v1.0.0` **Breaking change:** [ ] Yes [ ] No ## Testing & Validation - [x] Tests pass (`bun test`) - [x] Code formatted (`bun run fmt`) - [ ] Changes tested locally ## Related Issues <!-- Link related issues or write "None" if not applicable --> --------- Co-authored-by: YEVHENII SHCHERBINA <[email protected]>
1 parent e34320c commit a35986d

File tree

4 files changed

+134
-2
lines changed

4 files changed

+134
-2
lines changed

.github/typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Hashi = "Hashi"
55
HashiCorp = "HashiCorp"
66
mavrickrishi = "mavrickrishi" # Username
77
mavrick = "mavrick" # Username
8+
inh = "inh" # Option in setpriv command
89

910
[files]
1011
extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive

registry/coder/modules/claude-code/main.tf

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,42 @@ variable "claude_md_path" {
192192
default = "$HOME/.claude/CLAUDE.md"
193193
}
194194

195+
variable "enable_boundary" {
196+
type = bool
197+
description = "Whether to enable coder boundary for network filtering"
198+
default = false
199+
}
200+
201+
variable "boundary_version" {
202+
type = string
203+
description = "Boundary version, valid git reference should be provided (tag, commit, branch)"
204+
default = "main"
205+
}
206+
207+
variable "boundary_log_dir" {
208+
type = string
209+
description = "Directory for boundary logs"
210+
default = "/tmp/boundary_logs"
211+
}
212+
213+
variable "boundary_log_level" {
214+
type = string
215+
description = "Log level for boundary process"
216+
default = "WARN"
217+
}
218+
219+
variable "boundary_additional_allowed_urls" {
220+
type = list(string)
221+
description = "Additional URLs to allow through boundary (in addition to default allowed URLs)"
222+
default = []
223+
}
224+
225+
variable "boundary_proxy_port" {
226+
type = string
227+
description = "Port for HTTP Proxy used by Boundary"
228+
default = "8087"
229+
}
230+
195231
resource "coder_env" "claude_code_md_path" {
196232
count = var.claude_md_path == "" ? 0 : 1
197233

@@ -229,6 +265,8 @@ locals {
229265
start_script = file("${path.module}/scripts/start.sh")
230266
module_dir_name = ".claude-module"
231267
remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.sh"))
268+
# Extract hostname from access_url for boundary --allow flag
269+
coder_host = replace(replace(data.coder_workspace.me.access_url, "https://", ""), "http://", "")
232270

233271
# Required prompts for the module to properly report task status to Coder
234272
report_tasks_system_prompt = <<-EOT
@@ -299,6 +337,13 @@ module "agentapi" {
299337
ARG_PERMISSION_MODE='${var.permission_mode}' \
300338
ARG_WORKDIR='${local.workdir}' \
301339
ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \
340+
ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \
341+
ARG_BOUNDARY_VERSION='${var.boundary_version}' \
342+
ARG_BOUNDARY_LOG_DIR='${var.boundary_log_dir}' \
343+
ARG_BOUNDARY_LOG_LEVEL='${var.boundary_log_level}' \
344+
ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS='${join(" ", var.boundary_additional_allowed_urls)}' \
345+
ARG_BOUNDARY_PROXY_PORT='${var.boundary_proxy_port}' \
346+
ARG_CODER_HOST='${local.coder_host}' \
302347
/tmp/start.sh
303348
EOT
304349

registry/coder/modules/claude-code/main.tftest.hcl

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,32 @@ run "test_claude_code_permission_mode_validation" {
188188
}
189189
}
190190

191+
run "test_claude_code_with_boundary" {
192+
command = plan
193+
194+
variables {
195+
agent_id = "test-agent-boundary"
196+
workdir = "/home/coder/boundary-test"
197+
enable_boundary = true
198+
boundary_log_dir = "/tmp/test-boundary-logs"
199+
}
200+
201+
assert {
202+
condition = var.enable_boundary == true
203+
error_message = "Boundary should be enabled"
204+
}
205+
206+
assert {
207+
condition = var.boundary_log_dir == "/tmp/test-boundary-logs"
208+
error_message = "Boundary log dir should be set correctly"
209+
}
210+
211+
assert {
212+
condition = local.coder_host != ""
213+
error_message = "Coder host should be extracted from access URL"
214+
}
215+
}
216+
191217
run "test_claude_code_system_prompt" {
192218
command = plan
193219

@@ -267,4 +293,4 @@ run "test_claude_report_tasks_disabled" {
267293
condition = endswith(trimspace(coder_env.claude_code_system_prompt.value), "</system>")
268294
error_message = "System prompt should end with </system>"
269295
}
270-
}
296+
}

registry/coder/modules/claude-code/scripts/start.sh

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ ARG_DANGEROUSLY_SKIP_PERMISSIONS=${ARG_DANGEROUSLY_SKIP_PERMISSIONS:-}
1717
ARG_PERMISSION_MODE=${ARG_PERMISSION_MODE:-}
1818
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
1919
ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d)
20+
ARG_ENABLE_BOUNDARY=${ARG_ENABLE_BOUNDARY:-false}
21+
ARG_BOUNDARY_VERSION=${ARG_BOUNDARY_VERSION:-"main"}
22+
ARG_BOUNDARY_LOG_DIR=${ARG_BOUNDARY_LOG_DIR:-"/tmp/boundary_logs"}
23+
ARG_BOUNDARY_LOG_LEVEL=${ARG_BOUNDARY_LOG_LEVEL:-"WARN"}
24+
ARG_BOUNDARY_PROXY_PORT=${ARG_BOUNDARY_PROXY_PORT:-"8087"}
25+
ARG_CODER_HOST=${ARG_CODER_HOST:-}
2026

2127
echo "--------------------------------"
2228

@@ -27,6 +33,12 @@ printf "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIO
2733
printf "ARG_PERMISSION_MODE: %s\n" "$ARG_PERMISSION_MODE"
2834
printf "ARG_AI_PROMPT: %s\n" "$ARG_AI_PROMPT"
2935
printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
36+
printf "ARG_ENABLE_BOUNDARY: %s\n" "$ARG_ENABLE_BOUNDARY"
37+
printf "ARG_BOUNDARY_VERSION: %s\n" "$ARG_BOUNDARY_VERSION"
38+
printf "ARG_BOUNDARY_LOG_DIR: %s\n" "$ARG_BOUNDARY_LOG_DIR"
39+
printf "ARG_BOUNDARY_LOG_LEVEL: %s\n" "$ARG_BOUNDARY_LOG_LEVEL"
40+
printf "ARG_BOUNDARY_PROXY_PORT: %s\n" "$ARG_BOUNDARY_PROXY_PORT"
41+
printf "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST"
3042

3143
echo "--------------------------------"
3244

@@ -35,6 +47,14 @@ echo "--------------------------------"
3547
# avoid exiting if the script fails
3648
bash "/tmp/remove-last-session-id.sh" "$(pwd)" 2> /dev/null || true
3749

50+
function install_boundary() {
51+
# Install boundary from public github repo
52+
git clone https://github.com/coder/boundary
53+
cd boundary
54+
git checkout $ARG_BOUNDARY_VERSION
55+
go install ./cmd/...
56+
}
57+
3858
function validate_claude_installation() {
3959
if command_exists claude; then
4060
printf "Claude Code is installed\n"
@@ -76,7 +96,47 @@ function start_agentapi() {
7696
fi
7797
fi
7898
printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"
79-
agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}"
99+
100+
if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then
101+
install_boundary
102+
103+
mkdir -p "$ARG_BOUNDARY_LOG_DIR"
104+
printf "Starting with coder boundary enabled\n"
105+
106+
# Build boundary args with conditional --unprivileged flag
107+
BOUNDARY_ARGS=(--log-dir "$ARG_BOUNDARY_LOG_DIR")
108+
# Add default allowed URLs
109+
BOUNDARY_ARGS+=(--allow "*anthropic.com" --allow "registry.npmjs.org" --allow "*sentry.io" --allow "claude.ai" --allow "$ARG_CODER_HOST")
110+
111+
# Add any additional allowed URLs from the variable
112+
if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then
113+
IFS=' ' read -ra ADDITIONAL_URLS <<< "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS"
114+
for url in "${ADDITIONAL_URLS[@]}"; do
115+
BOUNDARY_ARGS+=(--allow "$url")
116+
done
117+
fi
118+
119+
# Set HTTP Proxy port used by Boundary
120+
BOUNDARY_ARGS+=(--proxy-port $ARG_BOUNDARY_PROXY_PORT)
121+
122+
# Set log level for boundary
123+
BOUNDARY_ARGS+=(--log-level $ARG_BOUNDARY_LOG_LEVEL)
124+
125+
# Remove --dangerously-skip-permissions from ARGS when using boundary (it doesn't work with elevated permissions)
126+
# Create a new array without the dangerous permissions flag
127+
CLAUDE_ARGS=()
128+
for arg in "${ARGS[@]}"; do
129+
if [ "$arg" != "--dangerously-skip-permissions" ]; then
130+
CLAUDE_ARGS+=("$arg")
131+
fi
132+
done
133+
134+
agentapi server --allowed-hosts="*" --type claude --term-width 67 --term-height 1190 -- \
135+
sudo -E env PATH=$PATH setpriv --inh-caps=+net_admin --ambient-caps=+net_admin --bounding-set=+net_admin boundary "${BOUNDARY_ARGS[@]}" -- \
136+
claude "${CLAUDE_ARGS[@]}"
137+
else
138+
agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}"
139+
fi
80140
}
81141

82142
validate_claude_installation

0 commit comments

Comments
 (0)