Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8bc494
add http token parsing
bcpeinhardt Sep 22, 2025
18aa4a4
add parsing hosts
bcpeinhardt Sep 22, 2025
3fb4ef0
parse path
bcpeinhardt Sep 22, 2025
ec944b6
update names to reflect we're pattern parsing
bcpeinhardt Sep 22, 2025
62edfb2
wildcard tests
bcpeinhardt Sep 22, 2025
3de4875
implement top level matching
bcpeinhardt Sep 22, 2025
72502e5
don't reverse host pattern while parsing
bcpeinhardt Sep 22, 2025
8e999bd
use the logger
bcpeinhardt Sep 22, 2025
9e92f3c
update proxy server tests to match new syntax
bcpeinhardt Sep 22, 2025
e400285
remove trailing * support for hostname
bcpeinhardt Sep 22, 2025
b83d3cf
update usage in error messages
bcpeinhardt Sep 22, 2025
fe417b8
update cli examples
bcpeinhardt Sep 22, 2025
f743e6a
adding curl test to debug separate issue in ci
bcpeinhardt Sep 22, 2025
0e868c9
Revert "adding curl test to debug separate issue in ci"
bcpeinhardt Sep 22, 2025
16cb468
Merge branch 'main' into bcpeinhardt/simple-allow-impl
bcpeinhardt Sep 24, 2025
26e2916
update e2e test to use key/value syntax
bcpeinhardt Sep 24, 2025
ced3bc8
remove superfluous interface from old impl
bcpeinhardt Sep 25, 2025
3a578be
split engine and rules code into separate files to make easier to rea…
bcpeinhardt Sep 25, 2025
959e4bd
mutate rest in parse allow rule to make pattern clearer
bcpeinhardt Sep 25, 2025
bd82e3d
mutate rest throughout rules
bcpeinhardt Sep 25, 2025
812b5f8
don't allow comma separated rules
bcpeinhardt Sep 25, 2025
f17d095
Merge branch 'main' into bcpeinhardt/simple-allow-impl
bcpeinhardt Sep 25, 2025
c5ecf98
remove custom parsed types in favor of stringly typed api
bcpeinhardt Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ curl -fsSL https://raw.githubusercontent.com/coder/boundary/main/install.sh | ba

```bash
# Allow only requests to github.com
boundary --allow "github.com" -- curl https://github.com
boundary --allow "domain=github.com" -- curl https://github.com

# Allow full access to GitHub issues API, but only GET/HEAD elsewhere on GitHub
boundary \
--allow "github.com/api/issues/*" \
--allow "GET,HEAD github.com" \
--allow "domain=github.com path=/api/issues/*" \
--allow "method=GET,HEAD domain=github.com" \
-- npm install

# Default deny-all: everything is blocked unless explicitly allowed
Expand All @@ -41,25 +41,29 @@ boundary -- curl https://example.com

### Format
```text
--allow "pattern" # All HTTP methods allowed
--allow "METHOD[,METHOD] pattern" # Specific methods only
--allow "key=value [key=value ...]"
```

**Keys:**
- `method` - HTTP method(s), comma-separated (GET, POST, etc.)
- `domain` - Domain/hostname pattern
- `path` - URL path pattern

### Examples
```bash
boundary --allow "github.com" -- git pull
boundary --allow "*.github.com" -- npm install # GitHub subdomains
boundary --allow "api.*" -- ./app # Any API domain
boundary --allow "GET,HEAD api.github.com" -- curl https://api.github.com
boundary --allow "domain=github.com" -- git pull
boundary --allow "domain=*.github.com" -- npm install # GitHub subdomains
boundary --allow "method=GET,HEAD domain=api.github.com" -- curl https://api.github.com
boundary --allow "method=POST domain=api.example.com path=/users" -- ./app
```

Wildcards: `*` matches any characters. All traffic is denied unless explicitly allowed.

## Logging

```bash
boundary --log-level info --allow "*" -- npm install # Show all requests
boundary --log-level debug --allow "github.com" -- git pull # Debug info
boundary --log-level info --allow "method=*" -- npm install # Show all requests
boundary --log-level debug --allow "domain=github.com" -- git pull # Debug info
```

**Log Levels:** `error`, `warn` (default), `info`, `debug`
Expand All @@ -70,10 +74,10 @@ When you can't or don't want to run with sudo privileges, use `--unprivileged`:

```bash
# Run without network isolation (uses HTTP_PROXY/HTTPS_PROXY environment variables)
boundary --unprivileged --allow "github.com" -- npm install
boundary --unprivileged --allow "domain=github.com" -- npm install

# Useful in containers or restricted environments
boundary --unprivileged --allow "*.npmjs.org" --allow "registry.npmjs.org" -- npm install
boundary --unprivileged --allow "domain=*.npmjs.org" --allow "domain=registry.npmjs.org" -- npm install
```

**Unprivileged Mode:**
Expand Down
4 changes: 2 additions & 2 deletions boundary.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
"github.com/coder/boundary/audit"
"github.com/coder/boundary/jail"
"github.com/coder/boundary/proxy"
"github.com/coder/boundary/rules"
"github.com/coder/boundary/rulesengine"
)

type Config struct {
RuleEngine rules.Evaluator
RuleEngine rulesengine.Engine
Auditor audit.Auditor
TLSConfig *tls.Config
Logger *slog.Logger
Expand Down
10 changes: 5 additions & 5 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/coder/boundary"
"github.com/coder/boundary/audit"
"github.com/coder/boundary/jail"
"github.com/coder/boundary/rules"
"github.com/coder/boundary/rulesengine"
"github.com/coder/boundary/tls"
"github.com/coder/boundary/util"
"github.com/coder/serpent"
Expand All @@ -38,10 +38,10 @@ func NewCommand() *serpent.Command {
// may be called something different when used as a subcommand / there will be a leading binary (i.e. `coder boundary` vs. `boundary`).
cmd.Long += `Examples:
# Allow only requests to github.com
boundary --allow "github.com" -- curl https://github.com
boundary --allow "domain=github.com" -- curl https://github.com

# Monitor all requests to specific domains (allow only those)
boundary --allow "github.com/api/issues/*" --allow "GET,HEAD github.com" -- npm install
boundary --allow "domain=github.com path=/api/issues/*" --allow "method=GET,HEAD domain=github.com" -- npm install

# Block everything by default (implicit)`

Expand Down Expand Up @@ -114,14 +114,14 @@ func Run(ctx context.Context, config Config, args []string) error {
}

// Parse allow rules
allowRules, err := rules.ParseAllowSpecs(config.AllowStrings)
allowRules, err := rulesengine.ParseAllowSpecs(config.AllowStrings)
if err != nil {
logger.Error("Failed to parse allow rules", "error", err)
return fmt.Errorf("failed to parse allow rules: %v", err)
}

// Create rule engine
ruleEngine := rules.NewRuleEngine(allowRules, logger)
ruleEngine := rulesengine.NewRuleEngine(allowRules, logger)

// Create auditor
auditor := audit.NewLogAuditor(logger)
Expand Down
4 changes: 2 additions & 2 deletions e2e_tests/boundary_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ func TestBoundaryIntegration(t *testing.T) {

// Start boundary process with sudo
boundaryCmd := exec.CommandContext(ctx, "/tmp/boundary-test",
"--allow", "dev.coder.com",
"--allow", "jsonplaceholder.typicode.com",
"--allow", "domain=dev.coder.com",
"--allow", "domain=jsonplaceholder.typicode.com",
"--log-level", "debug",
"--", "bash", "-c", "sleep 10 && echo 'Test completed'")

Expand Down
12 changes: 6 additions & 6 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import (
"sync/atomic"

"github.com/coder/boundary/audit"
"github.com/coder/boundary/rules"
"github.com/coder/boundary/rulesengine"
)

// Server handles HTTP and HTTPS requests with rule-based filtering
type Server struct {
ruleEngine rules.Evaluator
ruleEngine rulesengine.Engine
auditor audit.Auditor
logger *slog.Logger
tlsConfig *tls.Config
Expand All @@ -33,7 +33,7 @@ type Server struct {
// Config holds configuration for the proxy server
type Config struct {
HTTPPort int
RuleEngine rules.Evaluator
RuleEngine rulesengine.Engine
Auditor audit.Auditor
Logger *slog.Logger
TLSConfig *tls.Config
Expand Down Expand Up @@ -254,8 +254,8 @@ Request: %s %s
Host: %s

To allow this request, restart boundary with:
--allow "%s" # Allow all methods to this host
--allow "%s %s" # Allow only %s requests to this host
--allow "domain=%s" # Allow all methods to this host
--allow "method=%s domain=%s" # Allow only %s requests to this host

For more help: https://github.com/coder/boundary
`,
Expand Down Expand Up @@ -639,7 +639,7 @@ func (p *Server) constructFullURL(req *http.Request, hostname string) string {

// writeBlockedResponseStreaming writes a blocked response directly to the TLS connection
func (p *Server) writeBlockedResponseStreaming(tlsConn *tls.Conn, req *http.Request) {
response := fmt.Sprintf("HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n🚫 Request Blocked by Boundary\n\nRequest: %s %s\nHost: %s\n\nTo allow this request, restart boundary with:\n --allow \"%s\"\n",
response := fmt.Sprintf("HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n🚫 Request Blocked by Boundary\n\nRequest: %s %s\nHost: %s\n\nTo allow this request, restart boundary with:\n --allow \"domain=%s\"\n",
req.Method, req.URL.Path, req.Host, req.Host)
_, _ = tlsConn.Write([]byte(response))
}
Expand Down
14 changes: 7 additions & 7 deletions proxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/coder/boundary/audit"
"github.com/coder/boundary/rules"
"github.com/coder/boundary/rulesengine"
)

// mockAuditor is a simple mock auditor for testing
Expand All @@ -35,13 +35,13 @@ func TestProxyServerBasicHTTP(t *testing.T) {
}))

// Create test rules (allow all for testing)
testRules, err := rules.ParseAllowSpecs([]string{"*"})
testRules, err := rulesengine.ParseAllowSpecs([]string{"method=*"})
if err != nil {
t.Fatalf("Failed to parse test rules: %v", err)
}

// Create rule engine
ruleEngine := rules.NewRuleEngine(testRules, logger)
ruleEngine := rulesengine.NewRuleEngine(testRules, logger)

// Create mock auditor
auditor := &mockAuditor{}
Expand Down Expand Up @@ -116,13 +116,13 @@ func TestProxyServerBasicHTTPS(t *testing.T) {
}))

// Create test rules (allow all for testing)
testRules, err := rules.ParseAllowSpecs([]string{"*"})
testRules, err := rulesengine.ParseAllowSpecs([]string{"method=*"})
if err != nil {
t.Fatalf("Failed to parse test rules: %v", err)
}

// Create rule engine
ruleEngine := rules.NewRuleEngine(testRules, logger)
ruleEngine := rulesengine.NewRuleEngine(testRules, logger)

// Create mock auditor
auditor := &mockAuditor{}
Expand Down Expand Up @@ -210,13 +210,13 @@ func TestProxyServerCONNECT(t *testing.T) {
}))

// Create test rules (allow all for testing)
testRules, err := rules.ParseAllowSpecs([]string{"*"})
testRules, err := rulesengine.ParseAllowSpecs([]string{"method=*"})
if err != nil {
t.Fatalf("Failed to parse test rules: %v", err)
}

// Create rule engine
ruleEngine := rules.NewRuleEngine(testRules, logger)
ruleEngine := rulesengine.NewRuleEngine(testRules, logger)

// Create mock auditor
auditor := &mockAuditor{}
Expand Down
Loading
Loading