-
Notifications
You must be signed in to change notification settings - Fork 142
feat(coder/modules/boundary): add boundary module #840
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
35C4n0r
wants to merge
57
commits into
main
Choose a base branch
from
35C4n0r/feat-boundary-module
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
57 commits
Select commit
Hold shift + click to select a range
2b459f7
feat(boundary): add boundary module with installation and configurati…
35C4n0r e4e059b
chore: bun fmt
35C4n0r 12ca1b1
feat(boundary): enhance installation with wrapper script and pre/post…
35C4n0r abba320
Merge branch 'main' into 35C4n0r/feat-boundary-module
35C4n0r 0a7aa71
feat(boundary): add tests and mock scripts for boundary module instal…
35C4n0r 8dc8c8b
docs: update README with script synchronization details for boundary …
35C4n0r d9d0b3c
fix(boundary): fix test failures caused by script path collision and …
ce15e06
chore: description fix
35C4n0r 946204d
chore(boundary): remove unused boundary-mock.sh
8b88931
fix(boundary): improve command check for 'coder' to handle errors mor…
35C4n0r f45b2e9
fix(boundary): remove unnecessary argument passing in boundary wrappe…
35C4n0r 81df58f
fix: add '--' separator in boundary wrapper scripts
f081d87
Revert "fix: add '--' separator in boundary wrapper scripts"
ec2e16f
fix(tests): update mock and test to not require '--' separator
920f954
feat(boundary): refactor boundary wrapper setup to use BOUNDARY_WRAPP…
35C4n0r d2e528d
fix(tests): rename AGENTAPI_BOUNDARY_PREFIX to BOUNDARY_WRAPPER_PATH
74c4947
fix: rename remaining AGENTAPI_BOUNDARY_PREFIX references to BOUNDARY…
b16208f
feat(boundary): update coder_utils module source and version, add dis…
35C4n0r f22770d
feat(boundary): update command execution example to include config an…
35C4n0r 06b039f
fix(tests): update sync_script_names assertions for flat list output
fef86ab
docs: set module version to 0.0.1 in README examples
9f16eca
feat(boundary): update boundary wrapper path and output name in modul…
35C4n0r dc95e45
fix(tests): update paths for scripts/ and logs/ directories, rename o…
bb21940
fix: create scripts/ directory before writing boundary wrapper
74e58dc
fix: resolve coder-no-caps path in wrapper script
cf1d1ab
refactor: move all scripts into scripts/ subdirectory
4a4670e
Merge branch 'main' into 35C4n0r/feat-boundary-module
35C4n0r 155362f
feat(boundary): update boundary script destination and simplify execu…
35C4n0r 3aa6333
feat(boundary): rename local variables for clarity in main.tf
35C4n0r fbaaab5
debug
35C4n0r 5953687
debug
35C4n0r 3bb1a3b
refactor: rename boundary-install.sh to install.sh, fix coder-utils ref
f3c2c55
bun fmt
35C4n0r 5dd0370
feat(boundary): update coder-utils source and adjust config paths in …
35C4n0r 4dd0176
docs: update README to include link for boundary installation
35C4n0r 056a111
Merge branch 'main' into 35C4n0r/feat-boundary-module
35C4n0r d79446a
refactor: convert install.sh to tftpl template
6d828df
style: run bun fmt
12183e5
feat(boundary): update coder-utils source to registry and specify ver…
35C4n0r 256953e
docs: fix version in README examples to 0.0.1
f006b83
Merge branch 'main' into 35C4n0r/feat-boundary-module
35C4n0r 3a2dbd6
docs: add config.yaml, Claude Code example, fix main.tf comments
266eb1d
feat(boundary): add boundary_config and boundary_config_path variables
cd71b21
fix(boundary): base64 encode config content for template safety
c33ae2f
fix(boundary): decode b64 at variable init, add printf debug lines
f70ac78
refactor(boundary): address review feedback from matifali
559f4ea
refactor(boundary): convert config.yaml to tftpl, inject coder_domain…
01d1800
bun fmt
35C4n0r 8c91560
test(boundary): add coverage for coder_domain auto-fill, scripts and …
123c48b
fix(boundary): validate coder boundary with a real invocation, not --…
e2b69a1
fix(boundary): only fail on license entitlement error, ignore other c…
b918896
chore(boundary): trim verbose comment
4fdad04
docs(boundary): update Claude Code example to coder_app, suppress syn…
39f0a5a
debug
35C4n0r 50db520
debug
35C4n0r f0a469b
debug
35C4n0r b15c197
debug
35C4n0r File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| --- | ||
| display_name: Boundary | ||
| description: Configures boundary for network isolation in Coder workspaces | ||
| icon: ../../../../.icons/coder.svg | ||
| verified: true | ||
| tags: [boundary, ai, agents, firewall] | ||
| --- | ||
|
|
||
| # Boundary | ||
|
|
||
| Installs [boundary](https://coder.com/docs/ai-coder/agent-firewall) for network isolation in Coder workspaces. | ||
|
|
||
| This module: | ||
|
|
||
| - Installs boundary (via coder subcommand, direct installation, or compilation from source) | ||
| - Creates a wrapper script at `$HOME/.coder-modules/coder/boundary/scripts/boundary-wrapper.sh` | ||
| - Writes a default boundary config to `$HOME/.coder-modules/coder/boundary/config/config.yaml` (customizable) | ||
| - Automatically adds your Coder deployment domain to the config allowlist | ||
| - Exports `BOUNDARY_CONFIG` as a workspace environment variable | ||
| - Provides the wrapper path, config path, and script names via outputs | ||
|
|
||
| ```tf | ||
| module "boundary" { | ||
| count = data.coder_workspace.me.start_count | ||
| source = "registry.coder.com/coder/boundary/coder" | ||
| version = "0.0.1" | ||
| agent_id = coder_agent.main.id | ||
| } | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| The module ships with a comprehensive default config based on the | ||
| [Coder dogfood allowlist](./config.yaml). It covers Anthropic services, | ||
| OpenAI services, version control, package managers, container registries, | ||
| cloud platforms, and common development tools. | ||
|
|
||
| The Coder deployment domain is automatically added to the allowlist using | ||
| `data.coder_workspace.me.access_url`. | ||
|
|
||
| By default the config is written to | ||
| `$HOME/.coder-modules/coder/boundary/config/config.yaml` and the | ||
| `BOUNDARY_CONFIG` env var points there. You can override it in two ways: | ||
|
|
||
| ### Inline config | ||
|
|
||
| Pass the full YAML content directly: | ||
|
|
||
| ```tf | ||
| module "boundary" { | ||
| count = data.coder_workspace.me.start_count | ||
| source = "registry.coder.com/coder/boundary/coder" | ||
| version = "0.0.1" | ||
| agent_id = coder_agent.main.id | ||
|
|
||
| boundary_config = <<-YAML | ||
| allowlist: | ||
| - domain=your-deployment.coder.com | ||
| - domain=api.anthropic.com | ||
| - domain=api.openai.com | ||
| log_dir: /tmp/boundary_logs | ||
| proxy_port: 8087 | ||
| log_level: warn | ||
| YAML | ||
| } | ||
| ``` | ||
|
|
||
| ### External config file | ||
|
|
||
| Point to an existing config file in the workspace. The module will not | ||
| write any config and `BOUNDARY_CONFIG` will point to your path: | ||
|
|
||
| ```tf | ||
| module "boundary" { | ||
| count = data.coder_workspace.me.start_count | ||
| source = "registry.coder.com/coder/boundary/coder" | ||
| version = "0.0.1" | ||
| agent_id = coder_agent.main.id | ||
|
|
||
| boundary_config_path = "/workspace/my-boundary-config.yaml" | ||
| } | ||
| ``` | ||
|
|
||
| > **Note:** `boundary_config` and `boundary_config_path` are mutually | ||
| > exclusive — setting both produces a validation error. | ||
|
|
||
| See the [Agent Firewall docs](https://coder.com/docs/ai-coder/agent-firewall) | ||
| for the full config reference. | ||
|
|
||
| ## Usage | ||
|
|
||
| Use the `boundary_wrapper_path` output to access the wrapper path in Terraform | ||
| and pass it to scripts that should run commands in network isolation: | ||
|
|
||
| ```tf | ||
| module "boundary" { | ||
| count = data.coder_workspace.me.start_count | ||
| source = "registry.coder.com/coder/boundary/coder" | ||
| version = "0.0.1" | ||
| agent_id = coder_agent.main.id | ||
| } | ||
|
|
||
| resource "coder_script" "my_app" { | ||
| agent_id = coder_agent.main.id | ||
| script = <<-EOT | ||
| WRAPPER="${module.boundary[0].boundary_wrapper_path}" | ||
| "$WRAPPER" -- my-command --args | ||
| EOT | ||
| } | ||
| ``` | ||
|
|
||
| ### Script Synchronization | ||
|
|
||
| The `scripts` output provides a list of script names that can be used with `coder exp sync` to coordinate script execution. This is useful when your scripts need to wait for boundary installation to complete before running. | ||
|
|
||
| The list may contain the following script names: | ||
|
|
||
| - `coder-boundary-pre_install_script` - Pre-installation script (if configured) | ||
| - `coder-boundary-install_script` - Main boundary installation script | ||
| - `coder-boundary-post_install_script` - Post-installation script (if configured) | ||
|
|
||
| ## Examples | ||
|
|
||
| ### With Claude Code | ||
|
|
||
| Use boundary alongside the `claude-code` module to run Claude in a | ||
| network-isolated environment. The `coder_app` below waits for both | ||
| modules to finish installing before launching Claude behind the boundary | ||
| wrapper. | ||
|
|
||
| ```tf | ||
| module "boundary" { | ||
| source = "registry.coder.com/coder/boundary/coder" | ||
| version = "0.0.1" | ||
| agent_id = coder_agent.main.id | ||
| } | ||
|
|
||
| module "claude_code" { | ||
| source = "registry.coder.com/coder/claude-code/coder" | ||
| version = "5.3.0" | ||
| agent_id = coder_agent.main.id | ||
| } | ||
|
|
||
| resource "coder_app" "claude_with_boundary" { | ||
| agent_id = coder_agent.main.id | ||
| slug = "claude-cli" | ||
| display_name = "Claude (Boundary)" | ||
| command = <<-EOT | ||
| # Wait for boundary and claude-code install scripts to complete. | ||
| coder exp sync want claude-boundary \ | ||
| ${join(" ", module.boundary.scripts)} \ | ||
| ${join(" ", module.claude_code.scripts)} > /dev/null 2>&1 | ||
| coder exp sync start claude-boundary > /dev/null 2>&1 | ||
|
|
||
| # Run Claude inside the boundary wrapper. | ||
| "${module.boundary.boundary_wrapper_path}" \ | ||
| --config="${module.boundary.boundary_config_path}" -- claude | ||
| EOT | ||
| } | ||
| ``` | ||
|
|
||
| ### Compile from source | ||
|
|
||
| ```tf | ||
| module "boundary" { | ||
| count = data.coder_workspace.me.start_count | ||
| source = "registry.coder.com/coder/boundary/coder" | ||
| version = "0.0.1" | ||
| agent_id = coder_agent.main.id | ||
| compile_boundary_from_source = true | ||
| boundary_version = "main" | ||
| } | ||
| ``` | ||
|
|
||
| ### Use release binary | ||
|
|
||
| ```tf | ||
| module "boundary" { | ||
| count = data.coder_workspace.me.start_count | ||
| source = "registry.coder.com/coder/boundary/coder" | ||
| version = "0.0.1" | ||
| agent_id = coder_agent.main.id | ||
| use_boundary_directly = true | ||
| boundary_version = "latest" | ||
| } | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| # Test for boundary module | ||
|
|
||
| run "plan_with_required_vars" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| } | ||
|
|
||
| # Verify BOUNDARY_CONFIG env var with default config path | ||
| assert { | ||
| condition = coder_env.boundary_config.name == "BOUNDARY_CONFIG" | ||
| error_message = "Environment variable name should be 'BOUNDARY_CONFIG'" | ||
| } | ||
|
|
||
| assert { | ||
| condition = coder_env.boundary_config.value == "$HOME/.coder-modules/coder/boundary/config/config.yaml" | ||
| error_message = "BOUNDARY_CONFIG should default to module_directory/config/config.yaml" | ||
| } | ||
|
|
||
| # Verify the boundary_wrapper_path output | ||
| assert { | ||
| condition = output.boundary_wrapper_path == "$HOME/.coder-modules/coder/boundary/scripts/boundary-wrapper.sh" | ||
| error_message = "boundary_wrapper_path output should be correct" | ||
| } | ||
|
|
||
| # Verify boundary_config_path output defaults to the managed path | ||
| assert { | ||
| condition = output.boundary_config_path == "$HOME/.coder-modules/coder/boundary/config/config.yaml" | ||
| error_message = "boundary_config_path output should default to managed config path" | ||
| } | ||
|
|
||
| # Verify the scripts output contains the install script name | ||
| assert { | ||
| condition = contains(output.scripts, "coder-boundary-install_script") | ||
| error_message = "scripts should contain the install script name" | ||
| } | ||
| } | ||
|
|
||
| run "plan_with_compile_from_source" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| compile_boundary_from_source = true | ||
| boundary_version = "main" | ||
| } | ||
|
|
||
| assert { | ||
| condition = output.boundary_wrapper_path == "$HOME/.coder-modules/coder/boundary/scripts/boundary-wrapper.sh" | ||
| error_message = "boundary_wrapper_path output should be correct" | ||
| } | ||
|
|
||
| assert { | ||
| condition = contains(output.scripts, "coder-boundary-install_script") | ||
| error_message = "scripts should contain the install script name" | ||
| } | ||
| } | ||
|
|
||
| run "plan_with_use_directly" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| use_boundary_directly = true | ||
| boundary_version = "latest" | ||
| } | ||
|
|
||
| assert { | ||
| condition = output.boundary_wrapper_path == "$HOME/.coder-modules/coder/boundary/scripts/boundary-wrapper.sh" | ||
| error_message = "boundary_wrapper_path output should be correct" | ||
| } | ||
|
|
||
| assert { | ||
| condition = contains(output.scripts, "coder-boundary-install_script") | ||
| error_message = "scripts should contain the install script name" | ||
| } | ||
| } | ||
|
|
||
| run "plan_with_custom_hooks" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| pre_install_script = "echo 'Before install'" | ||
| post_install_script = "echo 'After install'" | ||
| } | ||
|
|
||
| assert { | ||
| condition = contains(output.scripts, "coder-boundary-install_script") | ||
| error_message = "scripts should contain the install script name" | ||
| } | ||
|
|
||
| # Verify pre and post install script names are set | ||
| assert { | ||
| condition = contains(output.scripts, "coder-boundary-pre_install_script") | ||
| error_message = "scripts should contain the pre_install script name" | ||
| } | ||
|
|
||
| assert { | ||
| condition = contains(output.scripts, "coder-boundary-post_install_script") | ||
| error_message = "scripts should contain the post_install script name" | ||
| } | ||
| } | ||
|
|
||
| run "plan_with_custom_module_directory" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| module_directory = "$HOME/.coder-modules/custom/boundary" | ||
| } | ||
|
|
||
| assert { | ||
| condition = output.boundary_wrapper_path == "$HOME/.coder-modules/custom/boundary/scripts/boundary-wrapper.sh" | ||
| error_message = "boundary_wrapper_path output should use custom module directory" | ||
| } | ||
|
|
||
| # Config path should also follow the module directory | ||
| assert { | ||
| condition = output.boundary_config_path == "$HOME/.coder-modules/custom/boundary/config/config.yaml" | ||
| error_message = "boundary_config_path output should use custom module directory" | ||
| } | ||
| } | ||
|
|
||
| run "plan_with_inline_boundary_config" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| boundary_config = "allowlist:\n - domain=example.com\nlog_level: debug\n" | ||
| } | ||
|
|
||
| # BOUNDARY_CONFIG should still point to the managed path since we write | ||
| # the inline content there. | ||
| assert { | ||
| condition = coder_env.boundary_config.value == "$HOME/.coder-modules/coder/boundary/config/config.yaml" | ||
| error_message = "BOUNDARY_CONFIG should point to managed config path when using inline config" | ||
| } | ||
|
|
||
| assert { | ||
| condition = output.boundary_config_path == "$HOME/.coder-modules/coder/boundary/config/config.yaml" | ||
| error_message = "boundary_config_path output should point to managed config path" | ||
| } | ||
| } | ||
|
|
||
| run "plan_with_boundary_config_path" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| boundary_config_path = "/workspace/my-boundary-config.yaml" | ||
| } | ||
|
|
||
| # BOUNDARY_CONFIG should point to the user-provided path. | ||
| assert { | ||
| condition = coder_env.boundary_config.value == "/workspace/my-boundary-config.yaml" | ||
| error_message = "BOUNDARY_CONFIG should point to user-provided config path" | ||
| } | ||
|
|
||
| assert { | ||
| condition = output.boundary_config_path == "/workspace/my-boundary-config.yaml" | ||
| error_message = "boundary_config_path output should point to user-provided path" | ||
| } | ||
| } | ||
|
|
||
| run "plan_with_both_configs_should_fail" { | ||
| command = plan | ||
|
|
||
| variables { | ||
| agent_id = "test-agent-id" | ||
| boundary_config = "allowlist: []" | ||
| boundary_config_path = "/workspace/config.yaml" | ||
| } | ||
|
|
||
| expect_failures = [ | ||
| var.boundary_config, | ||
| ] | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.