From 9b14fd3adc96c0cc9056b4be60a27cecb353cdf1 Mon Sep 17 00:00:00 2001 From: Yevhenii Shcherbina Date: Fri, 23 Jan 2026 12:56:36 -0500 Subject: [PATCH] feat: add boundary premium feature (#21589) Source code changes: - Added a wrapper for the boundary subcommand that checks feature entitlement before executing the underlying command. - Added a helper that returns the Boundary version using the runtime/debug package, which reads this information from the go.mod file. - Added FeatureBoundary to the corresponding enum. - Move boundary command from AGPL to enterprise. `NOTE`: From now on, the Boundary version will be specified in go.mod instead of being defined in AI modules. --- cli/exp_boundary.go | 12 - cli/exp_boundary_test.go | 33 -- cli/root.go | 7 +- codersdk/deployment.go | 3 + docs/manifest.json | 5 + docs/reference/cli/boundary.md | 147 +++++++++ docs/reference/cli/index.md | 1 + enterprise/cli/boundary.go | 86 +++++ enterprise/cli/boundary_test.go | 305 ++++++++++++++++++ enterprise/cli/root.go | 1 + enterprise/cli/testdata/coder_--help.golden | 2 + .../cli/testdata/coder_boundary_--help.golden | 57 ++++ go.mod | 5 +- go.sum | 9 +- site/src/api/typesGenerated.ts | 2 + 15 files changed, 624 insertions(+), 51 deletions(-) delete mode 100644 cli/exp_boundary.go delete mode 100644 cli/exp_boundary_test.go create mode 100644 docs/reference/cli/boundary.md create mode 100644 enterprise/cli/boundary.go create mode 100644 enterprise/cli/boundary_test.go create mode 100644 enterprise/cli/testdata/coder_boundary_--help.golden diff --git a/cli/exp_boundary.go b/cli/exp_boundary.go deleted file mode 100644 index a465e06eda..0000000000 --- a/cli/exp_boundary.go +++ /dev/null @@ -1,12 +0,0 @@ -package cli - -import ( - boundarycli "github.com/coder/boundary/cli" - "github.com/coder/serpent" -) - -func (*RootCmd) boundary() *serpent.Command { - cmd := boundarycli.BaseCommand() // Package coder/boundary/cli exports a "base command" designed to be integrated as a subcommand. - cmd.Use += " [args...]" // The base command looks like `boundary -- command`. Serpent adds the flags piece, but we need to add the args. - return cmd -} diff --git a/cli/exp_boundary_test.go b/cli/exp_boundary_test.go deleted file mode 100644 index 228214e465..0000000000 --- a/cli/exp_boundary_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package cli_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - boundarycli "github.com/coder/boundary/cli" - "github.com/coder/coder/v2/cli/clitest" - "github.com/coder/coder/v2/pty/ptytest" - "github.com/coder/coder/v2/testutil" -) - -// Actually testing the functionality of coder/boundary takes place in the -// coder/boundary repo, since it's a dependency of coder. -// Here we want to test basically that integrating it as a subcommand doesn't break anything. -func TestBoundarySubcommand(t *testing.T) { - t.Parallel() - ctx := testutil.Context(t, testutil.WaitShort) - - inv, _ := clitest.New(t, "exp", "boundary", "--help") - pty := ptytest.New(t).Attach(inv) - - go func() { - err := inv.WithContext(ctx).Run() - assert.NoError(t, err) - }() - - // Expect the --help output to include the short description. - // We're simply confirming that `coder boundary --help` ran without a runtime error as - // a good chunk of serpents self validation logic happens at runtime. - pty.ExpectMatch(boundarycli.BaseCommand().Short) -} diff --git a/cli/root.go b/cli/root.go index 4557834a57..5a09cad853 100644 --- a/cli/root.go +++ b/cli/root.go @@ -151,7 +151,6 @@ func (r *RootCmd) AGPLExperimental() []*serpent.Command { r.promptExample(), r.rptyCommand(), r.syncCommand(), - r.boundary(), } } @@ -333,6 +332,12 @@ func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, err // support links. return } + if cmd.Name() == "boundary" { + // The boundary command is integrated from the boundary package + // and has YAML-only options (e.g., allowlist from config file) + // that don't have flags or env vars. + return + } merr = errors.Join( merr, xerrors.Errorf("option %q in %q should have a flag or env", opt.Name, cmd.FullName()), diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 90915b13b3..fa103750db 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -90,6 +90,7 @@ const ( FeatureManagedAgentLimit FeatureName = "managed_agent_limit" FeatureWorkspaceExternalAgent FeatureName = "workspace_external_agent" FeatureAIBridge FeatureName = "aibridge" + FeatureBoundary FeatureName = "boundary" ) var ( @@ -119,6 +120,7 @@ var ( FeatureManagedAgentLimit, FeatureWorkspaceExternalAgent, FeatureAIBridge, + FeatureBoundary, } // FeatureNamesMap is a map of all feature names for quick lookups. @@ -163,6 +165,7 @@ func (n FeatureName) AlwaysEnable() bool { FeatureMultipleOrganizations: true, FeatureWorkspacePrebuilds: true, FeatureWorkspaceExternalAgent: true, + FeatureBoundary: true, }[n] } diff --git a/docs/manifest.json b/docs/manifest.json index cb52f9f98c..bbc0a0598a 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1344,6 +1344,11 @@ "description": "Toggle auto-update policy for a workspace", "path": "reference/cli/autoupdate.md" }, + { + "title": "boundary", + "description": "Network isolation tool for monitoring and restricting HTTP/HTTPS requests", + "path": "reference/cli/boundary.md" + }, { "title": "coder", "path": "reference/cli/index.md" diff --git a/docs/reference/cli/boundary.md b/docs/reference/cli/boundary.md new file mode 100644 index 0000000000..0c99605d8d --- /dev/null +++ b/docs/reference/cli/boundary.md @@ -0,0 +1,147 @@ + +# boundary + +Network isolation tool for monitoring and restricting HTTP/HTTPS requests + +## Usage + +```console +coder boundary [flags] [args...] +``` + +## Description + +```console +boundary creates an isolated network environment for target processes, intercepting HTTP/HTTPS traffic through a transparent proxy that enforces user-defined allow rules. +``` + +## Options + +### --config + +| | | +|-------------|-------------------------------| +| Type | yaml-config-path | +| Environment | $BOUNDARY_CONFIG | + +Path to YAML config file. + +### --allow + +| | | +|-------------|------------------------------| +| Type | string | +| Environment | $BOUNDARY_ALLOW | + +Allow rule (repeatable). These are merged with allowlist from config file. Format: "pattern" or "METHOD[,METHOD] pattern". + +### -- + +| | | +|------|---------------------------| +| Type | string-array | +| YAML | allowlist | + +Allowlist rules from config file (YAML only). + +### --log-level + +| | | +|-------------|----------------------------------| +| Type | string | +| Environment | $BOUNDARY_LOG_LEVEL | +| YAML | log_level | +| Default | warn | + +Set log level (error, warn, info, debug). + +### --log-dir + +| | | +|-------------|--------------------------------| +| Type | string | +| Environment | $BOUNDARY_LOG_DIR | +| YAML | log_dir | + +Set a directory to write logs to rather than stderr. + +### --proxy-port + +| | | +|-------------|--------------------------| +| Type | int | +| Environment | $PROXY_PORT | +| YAML | proxy_port | +| Default | 8080 | + +Set a port for HTTP proxy. + +### --pprof + +| | | +|-------------|------------------------------| +| Type | bool | +| Environment | $BOUNDARY_PPROF | +| YAML | pprof_enabled | + +Enable pprof profiling server. + +### --pprof-port + +| | | +|-------------|-----------------------------------| +| Type | int | +| Environment | $BOUNDARY_PPROF_PORT | +| YAML | pprof_port | +| Default | 6060 | + +Set port for pprof profiling server. + +### --configure-dns-for-local-stub-resolver + +| | | +|-------------|--------------------------------------------------------------| +| Type | bool | +| Environment | $BOUNDARY_CONFIGURE_DNS_FOR_LOCAL_STUB_RESOLVER | +| YAML | configure_dns_for_local_stub_resolver | + +Configure DNS for local stub resolver (e.g., systemd-resolved). Only needed when /etc/resolv.conf contains nameserver 127.0.0.53. + +### --jail-type + +| | | +|-------------|----------------------------------| +| Type | string | +| Environment | $BOUNDARY_JAIL_TYPE | +| YAML | jail_type | +| Default | nsjail | + +Jail type to use for network isolation. Options: nsjail (default), landjail. + +### --disable-audit-logs + +| | | +|-------------|----------------------------------| +| Type | bool | +| Environment | $DISABLE_AUDIT_LOGS | +| YAML | disable_audit_logs | + +Disable sending of audit logs to the workspace agent when set to true. + +### --log-proxy-socket-path + +| | | +|-------------|----------------------------------------------------------| +| Type | string | +| Environment | $CODER_AGENT_BOUNDARY_LOG_PROXY_SOCKET_PATH | +| Default | /tmp/boundary-audit.sock | + +Path to the socket where the boundary log proxy server listens for audit logs. + +### --version + +| | | +|------|-------------------| +| Type | bool | + +Print version information and exit. diff --git a/docs/reference/cli/index.md b/docs/reference/cli/index.md index 35c9bc1636..de3a5c2cb8 100644 --- a/docs/reference/cli/index.md +++ b/docs/reference/cli/index.md @@ -65,6 +65,7 @@ Coder — A tool for provisioning self-hosted development environments with Terr | [support](./support.md) | Commands for troubleshooting issues with a Coder deployment. | | [server](./server.md) | Start a Coder server | | [provisioner](./provisioner.md) | View and manage provisioner daemons and jobs | +| [boundary](./boundary.md) | Network isolation tool for monitoring and restricting HTTP/HTTPS requests | | [features](./features.md) | List Enterprise features | | [licenses](./licenses.md) | Add, delete, and list licenses | | [groups](./groups.md) | Manage groups | diff --git a/enterprise/cli/boundary.go b/enterprise/cli/boundary.go new file mode 100644 index 0000000000..104b2c6de2 --- /dev/null +++ b/enterprise/cli/boundary.go @@ -0,0 +1,86 @@ +package cli + +import ( + "net/http" + "os" + "runtime/debug" + + "golang.org/x/xerrors" + + boundarycli "github.com/coder/boundary/cli" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/serpent" +) + +func isChild() bool { + return os.Getenv("CHILD") == "true" +} + +func getBoundaryVersion() string { + const boundaryModulePath = "github.com/coder/boundary" + + buildInfo, ok := debug.ReadBuildInfo() + if !ok { + return "unknown" + } + + for _, module := range buildInfo.Deps { + if module.Path == boundaryModulePath { + return module.Version + } + } + + return "unknown" +} + +func (r *RootCmd) verifyLicense(inv *serpent.Invocation) error { + client, err := r.InitClient(inv) + if err != nil { + return err + } + + entitlements, err := client.Entitlements(inv.Context()) + if cerr, ok := codersdk.AsError(err); ok && cerr.StatusCode() == http.StatusNotFound { + return xerrors.Errorf("your deployment appears to be an AGPL deployment, so you cannot use the boundary command") + } else if err != nil { + return xerrors.Errorf("failed to get entitlements: %w", err) + } + + feature := entitlements.Features[codersdk.FeatureBoundary] + if feature.Entitlement == codersdk.EntitlementNotEntitled { + return xerrors.Errorf("your license is not entitled to use the boundary feature") + } + if !feature.Enabled { + // Feature is entitled but disabled (shouldn't happen for FeatureBoundary + // since it's in AlwaysEnable(), but handle it gracefully). + return xerrors.Errorf("the boundary feature is disabled in your deployment configuration") + } + + return nil +} + +func (r *RootCmd) boundary() *serpent.Command { + version := getBoundaryVersion() + cmd := boundarycli.BaseCommand(version) // Package coder/boundary/cli exports a "base command" designed to be integrated as a subcommand. + cmd.Use += " [args...]" // The base command looks like `boundary -- command`. Serpent adds the flags piece, but we need to add the args. + + // Wrap the handler to check for FeatureBoundary entitlement. + originalHandler := cmd.Handler + cmd.Handler = func(inv *serpent.Invocation) error { + // Boundary re-executes itself with CHILD=true to run the target process + // inside a jailed network namespace. Skip the license check for child + // processes since the parent already verified entitlement. + if isChild() { + return originalHandler(inv) + } + + if err := r.verifyLicense(inv); err != nil { + return err + } + + // Call the original handler if entitlement check passes. + return originalHandler(inv) + } + + return cmd +} diff --git a/enterprise/cli/boundary_test.go b/enterprise/cli/boundary_test.go new file mode 100644 index 0000000000..ac92c5bfe2 --- /dev/null +++ b/enterprise/cli/boundary_test.go @@ -0,0 +1,305 @@ +package cli_test + +import ( + "net/http" + "net/http/httptest" + "net/http/httputil" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + boundarycli "github.com/coder/boundary/cli" + "github.com/coder/coder/v2/cli/clitest" + "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/coderd/httpapi" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/enterprise/coderd/coderdenttest" + "github.com/coder/coder/v2/enterprise/coderd/license" + "github.com/coder/coder/v2/pty/ptytest" + "github.com/coder/coder/v2/testutil" +) + +// Actually testing the functionality of coder/boundary takes place in the +// coder/boundary repo, since it's a dependency of coder. +// Here we want to test basically that integrating it as a subcommand doesn't break anything. +func TestBoundarySubcommand(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + inv, _ := newCLI(t, "boundary", "--help") + pty := ptytest.New(t).Attach(inv) + + go func() { + err := inv.WithContext(ctx).Run() + assert.NoError(t, err) + }() + + // Expect the --help output to include the short description. + // We're simply confirming that `coder boundary --help` ran without a runtime error as + // a good chunk of serpents self validation logic happens at runtime. + pty.ExpectMatch(boundarycli.BaseCommand("dev").Short) +} + +func TestBoundaryLicenseVerification(t *testing.T) { + t.Parallel() + + t.Run("EntitledAndEnabled", func(t *testing.T) { + t.Parallel() + + client, _ := coderdenttest.New(t, &coderdenttest.Options{ + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureBoundary: 1, + }, + }, + }) + + inv, conf := newCLI(t, "boundary", "--version") + //nolint:gocritic // requires owner + clitest.SetupConfig(t, client, conf) + + ctx := testutil.Context(t, testutil.WaitShort) + err := inv.WithContext(ctx).Run() + // Should succeed - boundary --version should work with valid license. + require.NoError(t, err) + }) + + t.Run("NotEntitled", func(t *testing.T) { + t.Parallel() + + // Create a proxy server that returns entitlements without boundary feature. + client, _ := coderdenttest.New(t, &coderdenttest.Options{ + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + // No FeatureBoundary + }, + }, + }) + + proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/api/v2/entitlements" { + res := codersdk.Entitlements{ + Features: map[codersdk.FeatureName]codersdk.Feature{}, + Warnings: []string{}, + Errors: []string{}, + HasLicense: true, + Trial: false, + RequireTelemetry: false, + } + // Set boundary to not entitled, all other features to entitled. + for _, feature := range codersdk.FeatureNames { + if feature == codersdk.FeatureBoundary { + // Explicitly set boundary to not entitled. + res.Features[feature] = codersdk.Feature{ + Entitlement: codersdk.EntitlementNotEntitled, + Enabled: false, + } + } else { + res.Features[feature] = codersdk.Feature{ + Entitlement: codersdk.EntitlementEntitled, + Enabled: true, + } + } + } + httpapi.Write(r.Context(), w, http.StatusOK, res) + return + } + + // Otherwise, proxy the request to the real API server. + rp := httputil.NewSingleHostReverseProxy(client.URL) + tp := &http.Transport{} + defer tp.CloseIdleConnections() + rp.Transport = tp + rp.ServeHTTP(w, r) + })) + defer proxy.Close() + + proxyURL, err := url.Parse(proxy.URL) + require.NoError(t, err) + proxyClient := codersdk.New(proxyURL) + proxyClient.SetSessionToken(client.SessionToken()) + t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections) + + inv, conf := newCLI(t, "boundary", "--version") + clitest.SetupConfig(t, proxyClient, conf) + + ctx := testutil.Context(t, testutil.WaitShort) + err = inv.WithContext(ctx).Run() + require.Error(t, err) + require.ErrorContains(t, err, "your license is not entitled to use the boundary feature") + }) + + t.Run("FeatureDisabled", func(t *testing.T) { + t.Parallel() + + // Create a proxy server that returns entitlements with boundary disabled. + client, _ := coderdenttest.New(t, &coderdenttest.Options{ + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureBoundary: 1, + }, + }, + }) + + proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/api/v2/entitlements" { + res := codersdk.Entitlements{ + Features: map[codersdk.FeatureName]codersdk.Feature{}, + Warnings: []string{}, + Errors: []string{}, + HasLicense: true, + Trial: false, + RequireTelemetry: false, + } + for _, feature := range codersdk.FeatureNames { + if feature == codersdk.FeatureBoundary { + // Feature is entitled but disabled. + res.Features[feature] = codersdk.Feature{ + Entitlement: codersdk.EntitlementEntitled, + Enabled: false, + } + } else { + res.Features[feature] = codersdk.Feature{ + Entitlement: codersdk.EntitlementEntitled, + Enabled: true, + } + } + } + httpapi.Write(r.Context(), w, http.StatusOK, res) + return + } + + // Otherwise, proxy the request to the real API server. + rp := httputil.NewSingleHostReverseProxy(client.URL) + tp := &http.Transport{} + defer tp.CloseIdleConnections() + rp.Transport = tp + rp.ServeHTTP(w, r) + })) + defer proxy.Close() + + proxyURL, err := url.Parse(proxy.URL) + require.NoError(t, err) + proxyClient := codersdk.New(proxyURL) + proxyClient.SetSessionToken(client.SessionToken()) + t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections) + + inv, conf := newCLI(t, "boundary", "--version") + clitest.SetupConfig(t, proxyClient, conf) + + ctx := testutil.Context(t, testutil.WaitShort) + err = inv.WithContext(ctx).Run() + require.Error(t, err) + require.ErrorContains(t, err, "the boundary feature is disabled in your deployment configuration") + }) + + t.Run("AGPLDeployment", func(t *testing.T) { + t.Parallel() + + // Create an AGPL server (no enterprise features). + client := coderdtest.New(t, &coderdtest.Options{}) + + proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/api/v2/entitlements" { + // AGPL deployments return 404 for entitlements endpoint. + w.WriteHeader(http.StatusNotFound) + return + } + + // Otherwise, proxy the request to the real API server. + rp := httputil.NewSingleHostReverseProxy(client.URL) + tp := &http.Transport{} + defer tp.CloseIdleConnections() + rp.Transport = tp + rp.ServeHTTP(w, r) + })) + defer proxy.Close() + + proxyURL, err := url.Parse(proxy.URL) + require.NoError(t, err) + proxyClient := codersdk.New(proxyURL) + proxyClient.SetSessionToken(client.SessionToken()) + t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections) + + inv, conf := newCLI(t, "boundary", "--version") + clitest.SetupConfig(t, proxyClient, conf) + + ctx := testutil.Context(t, testutil.WaitShort) + err = inv.WithContext(ctx).Run() + require.Error(t, err) + require.ErrorContains(t, err, "your deployment appears to be an AGPL deployment") + }) +} + +// TestBoundaryChildProcessSkipsCheck verifies that when CHILD=true, the license +// check is skipped. This simulates boundary re-executing itself to run the +// target process. We use a proxy that would fail the license check to verify +// it's skipped. +func TestBoundaryChildProcessSkipsCheck(t *testing.T) { + // Cannot use t.Parallel() with t.Setenv(). + client, _ := coderdenttest.New(t, &coderdenttest.Options{ + LicenseOptions: &coderdenttest.LicenseOptions{ + Features: license.Features{ + // No FeatureBoundary - would normally fail + }, + }, + }) + + proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/api/v2/entitlements" { + // Return not entitled for boundary - this would normally cause failure. + res := codersdk.Entitlements{ + Features: map[codersdk.FeatureName]codersdk.Feature{}, + Warnings: []string{}, + Errors: []string{}, + HasLicense: true, + Trial: false, + RequireTelemetry: false, + } + for _, feature := range codersdk.FeatureNames { + if feature == codersdk.FeatureBoundary { + res.Features[feature] = codersdk.Feature{ + Entitlement: codersdk.EntitlementNotEntitled, + Enabled: false, + } + } else { + res.Features[feature] = codersdk.Feature{ + Entitlement: codersdk.EntitlementEntitled, + Enabled: true, + } + } + } + httpapi.Write(r.Context(), w, http.StatusOK, res) + return + } + + // Otherwise, proxy the request to the real API server. + rp := httputil.NewSingleHostReverseProxy(client.URL) + tp := &http.Transport{} + defer tp.CloseIdleConnections() + rp.Transport = tp + rp.ServeHTTP(w, r) + })) + defer proxy.Close() + + proxyURL, err := url.Parse(proxy.URL) + require.NoError(t, err) + proxyClient := codersdk.New(proxyURL) + proxyClient.SetSessionToken(client.SessionToken()) + t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections) + + inv, conf := newCLI(t, "boundary", "--version") + clitest.SetupConfig(t, proxyClient, conf) + + // Set CHILD=true to simulate boundary re-execution. This should skip the + // license check, so the command should succeed even though the proxy would + // return "not entitled". + t.Setenv("CHILD", "true") + + ctx := testutil.Context(t, testutil.WaitShort) + err = inv.WithContext(ctx).Run() + // Should succeed because license check is skipped for child processes. + require.NoError(t, err) +} diff --git a/enterprise/cli/root.go b/enterprise/cli/root.go index 78858ef48d..baba6830e6 100644 --- a/enterprise/cli/root.go +++ b/enterprise/cli/root.go @@ -18,6 +18,7 @@ func (r *RootCmd) enterpriseOnly() []*serpent.Command { agplcli.ExperimentalCommand(append(r.AGPLExperimental(), r.enterpriseExperimental()...)), // New commands that don't exist in AGPL: + r.boundary(), r.workspaceProxy(), r.features(), r.licenses(), diff --git a/enterprise/cli/testdata/coder_--help.golden b/enterprise/cli/testdata/coder_--help.golden index e199e8cc27..b421002bc8 100644 --- a/enterprise/cli/testdata/coder_--help.golden +++ b/enterprise/cli/testdata/coder_--help.golden @@ -15,6 +15,8 @@ USAGE: SUBCOMMANDS: aibridge Manage AI Bridge. + boundary Network isolation tool for monitoring and restricting + HTTP/HTTPS requests external-workspaces Create or manage external workspaces features List Enterprise features groups Manage groups diff --git a/enterprise/cli/testdata/coder_boundary_--help.golden b/enterprise/cli/testdata/coder_boundary_--help.golden new file mode 100644 index 0000000000..f3c8c87f34 --- /dev/null +++ b/enterprise/cli/testdata/coder_boundary_--help.golden @@ -0,0 +1,57 @@ +coder v0.0.0-devel + +USAGE: + coder boundary [flags] [args...] + + Network isolation tool for monitoring and restricting HTTP/HTTPS requests + + boundary creates an isolated network environment for target processes, + intercepting HTTP/HTTPS traffic through a transparent proxy that enforces + user-defined allow rules. + +OPTIONS: + --allow string, $BOUNDARY_ALLOW + Allow rule (repeatable). These are merged with allowlist from config + file. Format: "pattern" or "METHOD[,METHOD] pattern". + + string-array + Allowlist rules from config file (YAML only). + + --config yaml-config-path, $BOUNDARY_CONFIG + Path to YAML config file. + + --configure-dns-for-local-stub-resolver bool, $BOUNDARY_CONFIGURE_DNS_FOR_LOCAL_STUB_RESOLVER + Configure DNS for local stub resolver (e.g., systemd-resolved). Only + needed when /etc/resolv.conf contains nameserver 127.0.0.53. + + --disable-audit-logs bool, $DISABLE_AUDIT_LOGS + Disable sending of audit logs to the workspace agent when set to true. + + --jail-type string, $BOUNDARY_JAIL_TYPE (default: nsjail) + Jail type to use for network isolation. Options: nsjail (default), + landjail. + + --log-dir string, $BOUNDARY_LOG_DIR + Set a directory to write logs to rather than stderr. + + --log-level string, $BOUNDARY_LOG_LEVEL (default: warn) + Set log level (error, warn, info, debug). + + --log-proxy-socket-path string, $CODER_AGENT_BOUNDARY_LOG_PROXY_SOCKET_PATH (default: /tmp/boundary-audit.sock) + Path to the socket where the boundary log proxy server listens for + audit logs. + + --pprof bool, $BOUNDARY_PPROF + Enable pprof profiling server. + + --pprof-port int, $BOUNDARY_PPROF_PORT (default: 6060) + Set port for pprof profiling server. + + --proxy-port int, $PROXY_PORT (default: 8080) + Set a port for HTTP proxy. + + --version bool + Print version information and exit. + +——— +Run `coder --help` for a list of global options. diff --git a/go.mod b/go.mod index beb91d7af0..13f8ec270c 100644 --- a/go.mod +++ b/go.mod @@ -453,7 +453,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect gopkg.in/ini.v1 v1.67.0 // indirect howett.net/plist v1.0.0 // indirect - kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 // indirect + kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 // indirect sigs.k8s.io/yaml v1.5.0 // indirect ) @@ -475,7 +475,7 @@ require ( github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 github.com/coder/aibridge v0.3.1-0.20260121122740-e164b504fc52 github.com/coder/aisdk-go v0.0.9 - github.com/coder/boundary v0.0.1-alpha + github.com/coder/boundary v0.6.0 github.com/coder/preview v1.0.4 github.com/danieljoos/wincred v1.2.3 github.com/dgraph-io/ristretto/v2 v2.3.0 @@ -541,6 +541,7 @@ require ( github.com/jackmordaunt/icns/v3 v3.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/landlock-lsm/go-landlock v0.0.0-20251103212306-430f8e5cd97c // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect diff --git a/go.sum b/go.sum index 88d32b6873..df875709db 100644 --- a/go.sum +++ b/go.sum @@ -931,8 +931,8 @@ github.com/coder/aibridge v0.3.1-0.20260121122740-e164b504fc52 h1:UcsOXQH881tXPp github.com/coder/aibridge v0.3.1-0.20260121122740-e164b504fc52/go.mod h1:x45BE/NNDesDN1eWy4bsg81QsL6ou7xXPIeQr0ePETQ= github.com/coder/aisdk-go v0.0.9 h1:Vzo/k2qwVGLTR10ESDeP2Ecek1SdPfZlEjtTfMveiVo= github.com/coder/aisdk-go v0.0.9/go.mod h1:KF6/Vkono0FJJOtWtveh5j7yfNrSctVTpwgweYWSp5M= -github.com/coder/boundary v0.0.1-alpha h1:6shUQ2zkrWrfbgVcqWvpV2ibljOQvPvYqTctWBkKoUA= -github.com/coder/boundary v0.0.1-alpha/go.mod h1:d1AMFw81rUgrGHuZzWdPNhkY0G8w7pvLNLYF0e3ceC4= +github.com/coder/boundary v0.6.0 h1:DfYVBIH8/6EBfg9I0qz7rX2jo+4blUx4P4amd13nib8= +github.com/coder/boundary v0.6.0/go.mod h1:jEXVbTGQP9JFoXkyzsnitj2rsWJuTt+VVej1Yzr2CkQ= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41 h1:SBN/DA63+ZHwuWwPHPYoCZ/KLAjHv5g4h2MS4f2/MTI= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41/go.mod h1:I9ULxr64UaOSUv7hcb3nX4kowodJCVS7vt7VVJk/kW4= github.com/coder/clistat v1.2.0 h1:37KJKqiCllJsRvWqTHf3qiLIXX0JB6oqE5oxcqgdLkY= @@ -1544,6 +1544,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U= github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= +github.com/landlock-lsm/go-landlock v0.0.0-20251103212306-430f8e5cd97c h1:QcKqiunpt7hooa/xIx0iyepA6Cs2BgKexaYOxHvHNCs= +github.com/landlock-lsm/go-landlock v0.0.0-20251103212306-430f8e5cd97c/go.mod h1:stwyhp9tfeEy3A4bRJLdOEvjW/CetRJg/vcijNG8M5A= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -2847,8 +2849,9 @@ k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJ k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 h1:Th2b8jljYqkyZKS3aD3N9VpYsQpHuXLgea+SZUIfODA= kernel.org/pub/linux/libs/security/libcap/cap v1.2.73/go.mod h1:hbeKwKcboEsxARYmcy/AdPVN11wmT/Wnpgv4k4ftyqY= -kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 h1:SEAEUiPVylTD4vqqi+vtGkSnXeP2FcRO3FoZB1MklMw= kernel.org/pub/linux/libs/security/libcap/psx v1.2.73/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 h1:Z06sMOzc0GNCwp6efaVrIrz4ywGJ1v+DP0pjVkOfDuA= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.77/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 29bae4f057..a4c41ea053 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -2110,6 +2110,7 @@ export type FeatureName = | "advanced_template_scheduling" | "appearance" | "audit_log" + | "boundary" | "browser_only" | "connection_log" | "control_shared_ports" @@ -2136,6 +2137,7 @@ export const FeatureNames: FeatureName[] = [ "advanced_template_scheduling", "appearance", "audit_log", + "boundary", "browser_only", "connection_log", "control_shared_ports",