docs: add docs for boundary rules engine (#21471)

Closes: https://github.com/coder/boundary/issues/146

- added docs for rules engine
- move all boundary-related docs under new `boundary` directory
This commit is contained in:
Yevhenii Shcherbina
2026-01-09 15:04:51 -05:00
committed by GitHub
parent a09d85cc26
commit 1bfd776cb4
7 changed files with 112 additions and 7 deletions
+109
View File
@@ -0,0 +1,109 @@
# Agent Boundary
Agent Boundaries are process-level firewalls that restrict and audit what autonomous programs, such as AI agents, can access and use.
![Screenshot of Agent Boundaries blocking a process](../../images/guides/ai-agents/boundary.png)Example of Agent Boundaries blocking a process.
## Supported Agents
Agent Boundaries support the securing of any terminal-based agent, including your own custom agents.
## Features
Agent Boundaries offer network policy enforcement, which blocks domains and HTTP verbs to prevent exfiltration, and writes logs to the workspace.
## Getting Started with Boundary
The easiest way to use Agent Boundaries is through existing Coder modules, such as the [Claude Code module](https://registry.coder.com/modules/coder/claude-code). It can also be ran directly in the terminal by installing the [CLI](https://github.com/coder/boundary).
## Configuration
Boundary is configured using a `config.yaml` file. This allows you to maintain allow lists and share detailed policies with teammates.
In your Terraform module, enable Boundary with minimal configuration:
```tf
module "claude-code" {
source = "dev.registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
enable_boundary = true
boundary_version = "v0.5.2"
}
```
Create a `config.yaml` file in your template directory with your policy:
```yaml
allowlist:
- "domain=google.com"
- "method=GET,HEAD domain=api.github.com"
- "method=POST domain=api.example.com path=/users,/posts"
log_dir: /tmp/boundary_logs
proxy_port: 8087
log_level: warn
```
Add a `coder_script` resource to mount the configuration file into the workspace filesystem:
```tf
resource "coder_script" "boundary_config_setup" {
agent_id = coder_agent.dev.id
display_name = "Boundary Setup Configuration"
run_on_start = true
script = <<-EOF
#!/bin/sh
mkdir -p ~/.config/coder_boundary
echo '${base64encode(file("${path.module}/config.yaml"))}' | base64 -d > ~/.config/coder_boundary/config.yaml
chmod 600 ~/.config/coder_boundary/config.yaml
EOF
}
```
Boundary automatically reads `config.yaml` from `~/.config/coder_boundary/` when it starts, so everyone who launches Boundary manually inside the workspace picks up the same configuration without extra flags. This is especially convenient for managing extensive allow lists in version control.
### Configuration Parameters
- `boundary_version` defines what version of Boundary is being applied. This is set to `v0.2.0`, which points to the v0.2.0 release tag of `coder/boundary`.
- `log_dir` is the directory where log files are written to when the workspace spins up.
- `log_level` defines the verbosity at which requests are logged. Boundary uses the following verbosity levels:
- `WARN`: logs only requests that have been blocked by Boundary
- `INFO`: logs all requests at a high level
- `DEBUG`: logs all requests in detail
- `proxy_port` defines the port used by the HTTP proxy.
- `allowlist` defines the URLs that the agent can access, in addition to the default URLs required for the agent to work. Rules use the format `"key=value [key=value ...]"`:
- `domain=github.com` - allows the domain and all its subdomains
- `domain=*.github.com` - allows only subdomains (the specific domain is excluded)
- `method=GET,HEAD domain=api.github.com` - allows specific HTTP methods for a domain
- `method=POST domain=api.example.com path=/users,/posts` - allows specific methods, domain, and paths
- `path=/api/v1/*,/api/v2/*` - allows specific URL paths
For detailed information about the rules engine and how to construct allowlist rules, see the [rules engine documentation](./rules-engine.md).
You can also run Agent Boundaries directly in your workspace and configure it per template. You can do so by installing the [binary](https://github.com/coder/boundary) into the workspace image or at start-up. You can do so with the following command:
```hcl
curl -fsSL https://raw.githubusercontent.com/coder/boundary/main/install.sh | bash
```
## Jail Types
Boundary supports two different jail types for process isolation, each with different characteristics and requirements:
1. **nsjail** - Uses Linux namespaces for isolation. This is the default jail type and provides network namespace isolation. See [nsjail documentation](./nsjail.md) for detailed information about runtime requirements and Docker configuration.
2. **landjail** - Uses Landlock V4 for network isolation. This provides network isolation through the Landlock Linux Security Module (LSM) without requiring network namespace capabilities. See [landjail documentation](./landjail.md) for implementation details.
The choice of jail type depends on your security requirements, available Linux capabilities, and runtime environment. Both nsjail and landjail provide network isolation, but they use different underlying mechanisms. nsjail uses Linux namespaces, while landjail uses Landlock V4. Landjail may be preferred in environments where namespace capabilities are limited or unavailable.
## Implementation Comparison: Namespaces+iptables vs Landlock V4
| Aspect | Namespace Jail (Namespaces + veth-pair + iptables) | Landlock V4 Jail |
|-------------------------------|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------|
| **Privileges** | Requires `CAP_NET_ADMIN` | ✅ No special capabilities required |
| **Docker seccomp** | ❌ Requires seccomp profile modifications or sysbox-runc | ✅ Works without seccomp changes |
| **Kernel requirements** | Linux 3.8+ (widely available) | ❌ Linux 6.7+ (very new, limited adoption) |
| **Bypass resistance** | ✅ Strong - transparent interception prevents bypass | ❌ **Medium - can bypass by connecting to `evil.com:<HTTP_PROXY_PORT>`** |
| **Process isolation** | ✅ PID namespace (processes can't see/kill others); **implementation in-progress** | ❌ No PID namespace (agent can kill other processes) |
| **Non-TCP traffic control** | ✅ Can block/control UDP via iptables; **implementation in-progress** | ❌ No control over UDP (data can leak via UDP) |
| **Application compatibility** | ✅ Works with ANY application (transparent interception) | ❌ Tools without `HTTP_PROXY` support will be blocked |
+12
View File
@@ -0,0 +1,12 @@
# landjail Jail Type
landjail is Boundary's alternative jail type that uses Landlock V4 for network isolation.
## Overview
Boundary uses Landlock V4 to enforce network restrictions:
- All `bind` syscalls are forbidden
- All `connect` syscalls are forbidden except to the port that is used by http proxy
This provides network isolation without requiring network namespace capabilities or special Docker permissions.
+85
View File
@@ -0,0 +1,85 @@
# nsjail Jail Type
nsjail is Boundary's default jail type that uses Linux namespaces to provide process isolation. It creates unprivileged network namespaces to control and monitor network access for processes running under Boundary.
## Overview
nsjail leverages Linux namespace technology to isolate processes at the network level. When Boundary runs with nsjail, it creates a separate network namespace for the isolated process, allowing Boundary to intercept and filter all network traffic according to the configured policy.
This jail type requires Linux capabilities to create and manage network namespaces, which means it has specific runtime requirements when running in containerized environments like Docker.
## Architecture
<img width="1228" height="604" alt="Boundary" src="https://github.com/user-attachments/assets/1b7c8c5b-7b8f-4adf-8795-325bd28715c6" />
## Runtime & Permission Requirements for Running the Boundary in Docker
This section describes the Linux capabilities and runtime configurations required to run the Agent Boundary with nsjail inside a Docker container. Requirements vary depending on the OCI runtime and the seccomp profile in use.
### 1. Default `runc` runtime with `CAP_NET_ADMIN`
When using Docker's default `runc` runtime, the Boundary requires the container to have `CAP_NET_ADMIN`. This is the minimal capability needed for configuring virtual networking inside the container.
Docker's default seccomp profile may also block certain syscalls (such as `clone`) required for creating unprivileged network namespaces. If you encounter these restrictions, you may need to update or override the seccomp profile to allow these syscalls.
[see Docker Seccomp Profile Considerations](#docker-seccomp-profile-considerations)
### 2. Default `runc` runtime with `CAP_SYS_ADMIN` (testing only)
For development or testing environments, you may grant the container `CAP_SYS_ADMIN`, which implicitly bypasses many of the restrictions in Docker's default seccomp profile.
- The Boundary does not require `CAP_SYS_ADMIN` itself.
- However, Docker's default seccomp policy commonly blocks namespace-related syscalls unless `CAP_SYS_ADMIN` is present.
- Granting `CAP_SYS_ADMIN` enables the Boundary to run without modifying the seccomp profile.
⚠️ Warning: `CAP_SYS_ADMIN` is extremely powerful and should not be used in production unless absolutely necessary.
### 3. `sysbox-runc` runtime with `CAP_NET_ADMIN`
When using the `sysbox-runc` runtime (from Nestybox), the Boundary can run with only:
- `CAP_NET_ADMIN`
The sysbox-runc runtime provides more complete support for unprivileged user namespaces and nested containerization, which typically eliminates the need for seccomp profile modifications.
## Docker Seccomp Profile Considerations
Docker's default seccomp profile frequently blocks the `clone` syscall, which is required by the Boundary when creating unprivileged network namespaces. If the `clone` syscall is denied, the Boundary will fail to start.
To address this, you may need to modify or override the seccomp profile used by your container to explicitly allow the required `clone` variants.
You can find the default Docker seccomp profile for your Docker version here (specify your docker version):
https://github.com/moby/moby/blob/v25.0.13/profiles/seccomp/default.json#L628-L635
If the profile blocks the necessary `clone` syscall arguments, you can provide a custom seccomp profile that adds an allow rule like the following:
```json
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW"
}
```
This example unblocks the clone syscall entirely.
### Example: Overriding the Docker Seccomp Profile
To use a custom seccomp profile, start by downloading the default profile for your Docker version:
https://github.com/moby/moby/blob/v25.0.13/profiles/seccomp/default.json#L628-L635
Save it locally as seccomp-v25.0.13.json, then insert the clone allow rule shown above (or add "clone" to the list of allowed syscalls).
Once updated, you can run the container with the custom seccomp profile:
```bash
docker run -it \
--cap-add=NET_ADMIN \
--security-opt seccomp=seccomp-v25.0.13.json \
test bash
```
This instructs Docker to load your modified seccomp profile while granting only the minimal required capability (`CAP_NET_ADMIN`).
+98
View File
@@ -0,0 +1,98 @@
# Rules Engine Documentation
## Overview
The `rulesengine` package provides a flexible rule-based filtering system for HTTP/HTTPS requests. Rules use a simple key-value syntax with support for wildcards and multiple values.
### Basic Syntax
Rules follow the format: `key=value [key=value ...]` with three supported keys:
- **`method`**: HTTP method(s) - Any HTTP method (e.g., `GET`, `POST`, `PUT`, `DELETE`), `*` (all methods), or comma-separated list
- **`domain`**: Domain/hostname pattern - `github.com`, `*.example.com`, `*` (all domains)
- **`path`**: URL path pattern - `/api/users`, `/api/*/users`, `*` (all paths), or comma-separated list
**Key behavior**:
- If a key is omitted, it matches all values
- Multiple key-value pairs in one rule are separated by whitespace
- Multiple rules in the allowlist are OR'd together (OR logic)
- Default deny: if no rule matches, the request is denied
**Examples**:
```yaml
allowlist:
- domain=github.com # All methods, all paths for github.com
- method=GET,POST domain=api.example.com # GET/POST to api.example.com
- domain=api.example.com path=/users,/posts # Multiple paths
- method=GET domain=github.com path=/api/* # All three keys
```
---
## Wildcard Symbol for Domains
The `*` wildcard matches domain labels (parts separated by dots).
| Pattern | Matches | Does NOT Match |
|----------------|------------------------------------------------------------------|--------------------------------------------------------------------------|
| `*` | All domains | - |
| `github.com` | `github.com`, `api.github.com`, `v1.api.github.com` (subdomains) | `github.io` (diff domain) |
| `*.github.com` | `api.github.com`, `v1.api.github.com` (1+ subdomain levels) | `github.com` (base domain) |
| `api.*.com` | `api.github.com`, `api.google.com` | `api.v1.github.com` (`*` in the middle matches exactly one domain label) |
| `*.*.com` | `api.example.com`, `api.v1.github.com` | - |
| `api.*` | ❌ **ERROR** - Cannot end with `*` | - |
**Important**:
- Patterns without `*` at the start automatically match subdomains
- `*.example.com` matches one or more subdomain levels
- Domain patterns **cannot end with asterisk**
---
## Wildcard Symbol for Paths
The `*` wildcard matches path segments (parts separated by slashes).
| Pattern | Matches | Does NOT Match |
|----------------|------------------------------------------------------------|-----------------------------------------|
| `*` | All paths | - |
| `/api/users` | `/api/users` | `/api/users/123` (subpaths don't match) |
| `/api/*` | `/api/users`, `/api/posts` | `/api` |
| `/api/*/users` | `/api/v1/users`, `/api/v2/users` | `/api/users`, `/api/v1/v2/users` |
| `/*/users` | `/api/users`, `/v1/users` | `/api/v1/users` |
| `/api/v1/*` | `/api/v1/users`, `/api/v1/users/123/details` (1+ segments) | `/api/v1` |
**Important**:
- `*` matches **exactly one segment** (except at the end)
- `*` at the **end** matches **one or more segments** (special behavior)
- `*` must match an entire segment (cannot be part of a segment like `/api/user*`)
---
## Special Meaning of Wildcard at Beginning and End
| Position | Domain | Path |
|------------|---------------------|-----------------------|
| Beginning | 1+ subdomain levels | Exactly 1 segment |
| Middle | Exactly 1 label | Exactly 1 segment |
| End | ❌ Not allowed | 1+ segments (special) |
| Standalone | All domains | All paths |
---
## Multipath
Specify multiple paths in a single rule by separating them with commas:
```yaml
allowlist:
- domain=api.example.com path=/users,/posts,/comments
- domain=api.example.com path=/api,/api/*
```
`NOTE`: The pattern `/api/*` does not include the base path `/api`.
To match both, use `path=/api,/api/*`.