认证代理允许沙箱代码调用外部 API(OpenAI、Anthropic、GitHub 等)而无需硬编码凭证。在沙箱上配置后,代理 sidecar 会自动使用您工作区中的密钥或在代理配置中提供的只写凭证,将认证头注入匹配出站请求。
出口和网络访问控制
相同的 proxy_config 注入凭证的机制也可以控制沙箱可以访问的目标。
默认出口策略
默认情况下(未配置 access_control ):
- 允许访问任何主机的 HTTP 和 HTTPS(端口 80 和 443)。 出站 HTTP(S) 会透明地通过代理路由,在那里您的
rules和callbacks注入凭证。 - 所有其他原始 TCP 连接均被阻止。 非 HTTP 端口的连接(如数据库(
psql/dbt5432 上的 PostgreSQL)、SSH(22)、Redis(6379)等——除非明确允许,否则将被丢弃。
这意味着原始协议被阻止并非因为代理"无法理解"它们——而是默认被阻止,并通过 access_control.
允许和拒绝列表
添加一个 access_control 对象到 proxy_config ,包含 **以下之一** an allow_list **or** a deny_list (不能同时设置——如果两者都设置了,请求将被拒绝):
| 模式 | 行为 |
|---|---|
allow_list | **Default-deny.** Only listed destinations are reachable—including HTTP/HTTPS. If you set an allow_list,您还必须列出沙箱所需的每个 HTTP(S) 主机。 |
deny_list | **Default-allow for HTTP/HTTPS.** 所有 HTTP(S) 主机保持可访问,列出的除外。一个 deny_list 无法打开原始 TCP 端口。 |
模式语法
每个 allow_list/deny_list 条目使用以下形式:
| 模式 | 含义 |
|---|---|
host | 裸主机 → 端口 **仅 80 和 443** (HTTP/S). |
host:PORT | 精确主机 PORT. :22 仅授予 22, **不是** additive with 80/443—list the host twice if you need both. |
*.example.com | Glob 模式(RFC 1034 风格)。顶级域名(example.com) is **不** 包含。 |
~regex | 不透明正则匹配;不解析端口后缀。 |
1.2.3.4 / 10.0.0.0/8 | 字面 IP 或 CIDR。CIDR **不能** carry a port (HTTP/S only in allow mode; block all ports in deny mode). |
[::1]:22 | IPv6 字面量在指定端口时使用带括号的形式。 |
连接数据库(原始 TCP)
要使沙箱代码能够访问外部 PostgreSQL 数据库 psql, dbt,或任何驱动程序,请在对应端口上将主机列入白名单。因为 allow_list 默认为拒绝,还需列出沙箱需要访问的所有 HTTP(S) 主机:
curl -X POST "$LANGSMITH_ENDPOINT/v2/sandboxes/boxes" \
-H "x-api-key: $LANGSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "db-sandbox",
"wait_for_ready": true,
"proxy_config": {
"access_control": {
"allow_list": [
"db.example.com:5432",
"api.openai.com"
]
}
}
}'
与 db.example.com:5432 的连接在 TCP 层直接透传,不会被拦截,因此 PostgreSQL 协议——以及 TLS、主机密钥检查和在其之上的任何其他端到端协议——均能正常工作。
通过 SDK 配置
from langsmith.sandbox import SandboxClient
client = SandboxClient()
client.create_sandbox(
name="db-sandbox",
proxy_config={
"access_control": {
"allow_list": ["db.example.com:5432", "api.openai.com"]
}
},
)
const client = new SandboxClient();
await client.createSandbox({
name: "db-sandbox",
proxyConfig: {
access_control: {
allow_list: ["db.example.com:5432", "api.openai.com"],
},
},
});
配置身份验证代理规则
在创建沙箱时添加一个 proxy_config ,或通过修补其来更新现有沙箱 proxy_config。每条规则指定:
| 字段 | 描述 |
|---|---|
match_hosts | 要拦截的主机(支持通配符,如 *.github.com) |
match_paths | 要匹配的路径(留空 = 所有路径) |
headers | 要注入的头部,每个包含一个 name, type和 value |
no_proxy | 完全绕过代理的主机(例如 localhost) |
头部类型
每个头部都有一个 type 来控制其值的存储和显示方式:
| 类型 | 描述 |
|---|---|
workspace_secret | 使用 {KEY} 语法引用工作区密钥。在应用代理配置时解析。 |
plaintext | 值按原样存储和返回。用于非敏感头部。 |
opaque | 只写。值在静态时加密,且永远不会通过 API 返回。 |
AWS 请求身份验证
当沙箱代码需要使用 AWS SDK 或 CLI 调用 AWS 服务时,请使用 AWS 身份验证规则。代理将真实的 AWS 凭据保留在沙箱外部,然后使用 AWS SigV4 对支持的出站 HTTPS 请求进行签名。
这在代理代码需要检查 S3 对象、调用 Bedrock 或使用其他支持的 AWS HTTPS 端点时非常有用,同时不会在沙箱文件、环境变量、Shell 历史记录或日志中暴露长期有效的 AWS 访问密钥。沙箱接收兼容的 AWS 凭据占位符以使 SDK 凭据检测正常工作,而代理使用配置的凭据对出站请求进行签名。
AWS 身份验证规则与头部注入规则不同:
- - 设置
typetoaws. - - 将凭据放在
awsobject. - - 不要设置
match_hosts,match_paths, orheaders;AWS 主机匹配已内置到代理中。 - - 每个沙箱最多配置一个 AWS 身份验证规则。
curl -X POST "$LANGSMITH_ENDPOINT/v2/sandboxes/boxes" \
-H "x-api-key: $LANGSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "aws-sandbox",
"wait_for_ready": true,
"proxy_config": {
"rules": [
{
"name": "aws",
"type": "aws",
"enabled": true,
"aws": {
"access_key_id": {
"type": "workspace_secret",
"value": "{AWS_ACCESS_KEY_ID}"
},
"secret_access_key": {
"type": "workspace_secret",
"value": "{AWS_SECRET_ACCESS_KEY}"
}
}
}
]
}
}'
通过 SDK 配置 AWS 身份验证
from langsmith.sandbox import (
SandboxClient,
aws_auth,
proxy_config,
workspace_secret,
)
client = SandboxClient()
client.create_sandbox(
name="aws-sandbox",
proxy_config=proxy_config(
rules=[
aws_auth(
access_key_id=workspace_secret("AWS_ACCESS_KEY_ID"),
secret_access_key=workspace_secret("AWS_SECRET_ACCESS_KEY"),
)
]
),
)
SandboxClient,
awsAuth,
proxyConfig,
workspaceSecret,
} from "langsmith/sandbox";
const client = new SandboxClient();
await client.createSandbox({
name: "aws-sandbox",
proxyConfig: proxyConfig({
rules: [
awsAuth({
accessKeyId: workspaceSecret("AWS_ACCESS_KEY_ID"),
secretAccessKey: workspaceSecret("AWS_SECRET_ACCESS_KEY"),
}),
],
}),
});
沙箱就绪后,在沙箱内正常使用 AWS SDK 或 CLI。SDK 或 CLI 可以发现占位符 AWS 环境变量,代理会对出站 AWS 请求应用真实的 SigV4 签名。
GCP 请求身份验证
当沙盒代码需要使用 Google SDK 或 CLI 调用 Google API 时,请使用 GCP 身份验证规则。代理将服务帐号 JSON 保存在沙盒外部,然后对与沙盒代理匹配的 Google API 主机的出站 HTTPS 请求进行身份验证。
当代理代码需要检查 GCS 对象或调用其他 Google API,但不想在沙盒文件、环境变量、shell 历史记录或日志中暴露服务帐号 JSON 时,此功能非常有用。沙盒会收到兼容性凭证以便 SDK 凭证检测正常工作,同时代理使用配置的服务帐号对出站请求进行身份验证。
GCP 身份验证规则与 header 注入规则不同:
- - 设置
typetogcp. - - 将凭证放在
gcp.service_account_json. - - 设置
gcp.scopes为非空的 OAuth scope 列表。 - - 代理自动匹配 Google API 主机,并使用配置的服务帐号对这些请求进行身份验证。
- - 每个沙盒最多配置一个启用的 GCP 身份验证规则。
SDK gcp_auth 和 gcpAuth 辅助函数以相同方式构建此规则。
curl -X POST "$LANGSMITH_ENDPOINT/v2/sandboxes/boxes" \
-H "x-api-key: $LANGSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "gcp-sandbox",
"wait_for_ready": true,
"proxy_config": {
"rules": [
{
"name": "gcp",
"type": "gcp",
"enabled": true,
"gcp": {
"service_account_json": {
"type": "workspace_secret",
"value": "{GCP_SERVICE_ACCOUNT_JSON}"
},
"scopes": [
"https://www.googleapis.com/auth/devstorage.read_only"
]
}
}
]
}
}'
通过 SDK 配置 GCP 身份验证
from langsmith.sandbox import (
SandboxClient,
gcp_auth,
proxy_config,
workspace_secret,
)
client = SandboxClient()
client.create_sandbox(
name="gcp-sandbox",
proxy_config=proxy_config(
rules=[
gcp_auth(
service_account_json=workspace_secret("GCP_SERVICE_ACCOUNT_JSON"),
scopes=["https://www.googleapis.com/auth/devstorage.read_only"],
)
]
),
)
SandboxClient,
gcpAuth,
proxyConfig,
workspaceSecret,
} from "langsmith/sandbox";
const client = new SandboxClient();
await client.createSandbox({
name: "gcp-sandbox",
proxyConfig: proxyConfig({
rules: [
gcpAuth({
serviceAccountJson: workspaceSecret("GCP_SERVICE_ACCOUNT_JSON"),
scopes: ["https://www.googleapis.com/auth/devstorage.read_only"],
}),
],
}),
});
沙盒就绪后,在沙盒内正常使用 Google SDK 或 CLI 发起支持的 Google API 请求。SDK 或 CLI 可以发现兼容性凭证,代理应用真实的 GCP 身份验证而不将服务帐号 JSON 暴露在沙盒内。
单 API 示例
创建一个自动向出站请求注入 OpenAI API 密钥的沙盒:
curl -X POST "$LANGSMITH_ENDPOINT/v2/sandboxes/boxes" \
-H "x-api-key: $LANGSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "openai-sandbox",
"wait_for_ready": true,
"proxy_config": {
"rules": [
{
"name": "openai-api",
"match_hosts": ["api.openai.com"],
"headers": [
{
"name": "Authorization",
"type": "workspace_secret",
"value": "Bearer {OPENAI_API_KEY}"
}
]
}
]
}
}'
沙盒现在可以调用 OpenAI,无需设置 API 密钥——代理会自动注入。
多 API 示例
添加多个规则以同时向多个服务进行身份验证:
curl -X POST "$LANGSMITH_ENDPOINT/v2/sandboxes/boxes" \
-H "x-api-key: $LANGSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "multi-api-sandbox",
"wait_for_ready": true,
"proxy_config": {
"rules": [
{
"name": "openai-api",
"match_hosts": ["api.openai.com"],
"headers": [
{
"name": "Authorization",
"type": "workspace_secret",
"value": "Bearer {OPENAI_API_KEY}"
}
]
},
{
"name": "anthropic-api",
"match_hosts": ["api.anthropic.com"],
"headers": [
{
"name": "x-api-key",
"type": "workspace_secret",
"value": "{ANTHROPIC_API_KEY}"
},
{
"name": "anthropic-version",
"type": "plaintext",
"value": "2023-06-01"
}
]
},
{
"name": "github-api",
"match_hosts": ["api.github.com"],
"match_paths": ["/repos/*", "/user"],
"headers": [
{
"name": "Authorization",
"type": "workspace_secret",
"value": "Bearer {GITHUB_TOKEN}"
}
]
}
],
"no_proxy": ["localhost", "127.0.0.1"]
}
}'
GitHub 示例
Open SWE 通过在沙盒外部生成短期 GitHub App 安装令牌,然后使用只写方式修补沙盒来对 GitHub 访问进行身份验证。 opaque 代理规则。这使短期 GitHub 访问令牌远离沙盒文件系统和部署环境变量。
配置两条规则:
| 主机 | Header |
|---|---|
api.github.com | Authorization: Bearer <github-token> 用于 gh 和 REST API 调用 |
github.com, *.github.com | Authorization: Basic <base64("x-access-token:<github-token>")> 用于通过 HTTPS 进行的 Git 操作,如 clone、fetch 和 push |
from typing import Any
def github_proxy_rules(github_token: str) -> list[dict[str, Any]]:
basic_auth = base64.b64encode(
f"x-access-token:{github_token}".encode()
).decode()
return [
{
"name": "github-api",
"match_hosts": ["api.github.com"],
"headers": [
{
"name": "Authorization",
"type": "opaque",
"value": f"Bearer {github_token}",
}
],
},
{
"name": "github",
"match_hosts": ["github.com", "*.github.com"],
"headers": [
{
"name": "Authorization",
"type": "opaque",
"value": f"Basic {basic_auth}",
}
],
},
]
def configure_github_proxy(sandbox_name: str, github_token: str) -> None:
endpoint = os.environ.get(
"LANGSMITH_ENDPOINT", "https://api.smith.langchain.com"
)
response = httpx.patch(
f"{endpoint}/v2/sandboxes/boxes/{sandbox_name}",
headers={"x-api-key": os.environ["LANGSMITH_API_KEY"]},
json={"proxy_config": {"rules": github_proxy_rules(github_token)}},
timeout=30.0,
)
response.raise_for_status()
在 configure_github_proxy 创建或重新连接到沙盒后调用。由于 GitHub App 安装令牌会过期,因此在为新运行重用沙盒时需要刷新代理配置。
在沙盒内,当 CLI 在发送请求前需要本地凭证时,设置一个非密钥占位符令牌:
GH_TOKEN=dummy gh repo view langchain-ai/langchain
GH_TOKEN=dummy gh pr list --repo langchain-ai/langchain
GH_TOKEN=dummy gh repo clone langchain-ai/langchain
占位符仅满足 gh CLI 的本地检查。代理注入真实的 Authorization header 到出站请求中。
通过 SDK 配置
from langsmith.sandbox import SandboxClient
client = SandboxClient()
client.create_sandbox(
name="openai-sandbox",
proxy_config={
"rules": [
{
"name": "openai-api",
"match_hosts": ["api.openai.com"],
"headers": [
{
"name": "Authorization",
"type": "workspace_secret",
"value": "Bearer {OPENAI_API_KEY}",
}
],
}
]
},
)
const client = new SandboxClient();
await client.createSandbox({
name: "openai-sandbox",
proxyConfig: {
rules: [
{
name: "openai-api",
match_hosts: ["api.openai.com"],
headers: [
{
name: "Authorization",
type: "workspace_secret",
value: "Bearer {OPENAI_API_KEY}",
},
],
},
],
},
});
回调凭证示例
静态 workspace_secret 规则在应用代理配置时从您的工作区拉取凭证, opaque 规则允许您的应用修补短期凭证(如 GitHub 令牌示例。对于必须在代理时由您自己的服务解析的凭据,请使用 **回调**。代理向您提供的 URL 发送 POST 请求,您的端点返回要注入的标头,代理会缓存结果。
回调与规则一起配置在 proxy_config:
| 字段 | 描述 |
|---|---|
match_hosts | 要拦截的主机(语法与规则相同;支持通配符如 *.github.com). |
url | 您的回调端点。必须是可从代理访问的 http:// or https:// URL。 |
request_headers | 附加到代理 → 回调请求的标头,例如您的端点用于验证请求的 HMAC 或共享密钥。仅支持 plaintext 和 opaque 类型(不支持 workspace_secret). |
ttl_seconds | 解析后的标头在重新调用回调之前的缓存时间。必须介于 60 到 3600 之间。 |
静态规则优先。 如果 rules 中有任何规则匹配该主机,则该主机会跳过回调。在规则内采用首次匹配优先原则;如果多个回调匹配,相同原则也适用于回调之间。
回调约定
当缓存未命中且需要为匹配的主机解析凭据时,代理会发出以下请求:
POST <callback.url>
Content-Type: application/json
<request_headers from your config, attached verbatim>
{"host": "api.example.com", "port": 443}
您的端点必须响应 2xx 并返回 JSON 正文:
{
"headers": {
"Authorization": "Bearer <token>",
"X-Org-Id": "..."
}
}
代理将响应中的每个标头注入沙箱的出站请求,并缓存响应 ttl_seconds。任何非 2xx 响应、传输错误或格式错误的 JSON 都会导致失败关闭:沙箱的请求被拒绝,并返回 502 callback resolution failed (不注入标头,不缓存响应)。
示例
当您的 OAuth 令牌由您自己的服务按需生成时,请使用回调:
curl -X POST "$LANGSMITH_ENDPOINT/v2/sandboxes/boxes" \
-H "x-api-key: $LANGSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"snapshot_id": "<snapshot-uuid>",
"name": "callback-sandbox",
"wait_for_ready": true,
"proxy_config": {
"callbacks": [
{
"match_hosts": ["api.github.com", "*.githubusercontent.com"],
"url": "https://auth.your-app.example.com/sandbox-credentials",
"request_headers": [
{
"name": "X-Integrator-Secret",
"type": "opaque",
"value": "<shared-secret-your-endpoint-verifies>"
}
],
"ttl_seconds": 300
}
]
}
}'
通过 SDK 配置
from langsmith.sandbox import SandboxClient
client = SandboxClient()
client.create_sandbox(
name="callback-sandbox",
proxy_config={
"callbacks": [
{
"match_hosts": ["api.github.com", "*.githubusercontent.com"],
"url": "https://auth.your-app.example.com/sandbox-credentials",
"request_headers": [
{
"name": "X-Integrator-Secret",
"type": "opaque",
"value": "<shared-secret-your-endpoint-verifies>",
}
],
"ttl_seconds": 300,
}
]
},
)
const client = new SandboxClient();
await client.createSandbox({
name: "callback-sandbox",
proxyConfig: {
callbacks: [
{
match_hosts: ["api.github.com", "*.githubusercontent.com"],
url: "https://auth.your-app.example.com/sandbox-credentials",
request_headers: [
{
name: "X-Integrator-Secret",
type: "opaque",
value: "<shared-secret-your-endpoint-verifies>",
},
],
ttl_seconds: 300,
},
],
},
});