跳转到内容
搜索文档

处理出站流量

最后更新 查看 MarkdownAgent 设置

出站处理程序让你使用受信任的代码拦截并修改来自沙箱的 HTTP 流量。

使用它们可以:

  • 允许或拒绝特定的源站目标
  • 安全地注入授权请求头或令牌
  • 透明地重新路由流量
  • 为出站流量添加自定义策略(例如拒绝特定 HTTP 请求)
  • 连接到 Workers 绑定,如 KV、R2 与 Durable Objects

阻止出站流量

使用 enableInternet = false 默认阻止公共互联网访问:

import { Sandbox } from "@cloudflare/sandbox";

export class MySandbox extends Sandbox {
	enableInternet = false;
}
import { Sandbox } from "@cloudflare/sandbox";

export class MySandbox extends Sandbox {
	enableInternet = false;
}

enableInternetfalse 时,只有你稍后在本页通过 allowedHosts 或出站处理程序显式允许的流量才能离开沙箱。仅端口 80443 与 DNS 可用,且 DNS 查询使用 Cloudflare 的 DNS 服务器。

按主机阻止或允许流量

你可以使用 Sandbox 类上的 allowedHostsdeniedHosts 属性过滤出站流量。

设置 allowedHosts 后,它会成为默认拒绝的允许列表。不在列表中的任何主机或 IP 都会被拒绝,只有匹配的目标才能到达 outboundoutboundByHost 处理程序。

allowedHostsdeniedHosts 也支持简单的 glob 模式,其中 * 匹配任意字符序列。

默认情况下,Sandbox 允许互联网访问,你可以设置 deniedHosts 以禁止特定主机或 IP:

import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {
	deniedHosts = ["some-nefarious-website.com", "141.101.64.0/18"];
}
import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {
	deniedHosts = ["some-nefarious-website.com", "141.101.64.0/18"];
}

你也可以默认禁用互联网访问,但允许特定主机与 IP:

import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {
	// default internet access to off unless overridden by 'allowedHosts' or outbound proxy
	enableInternet = false;

	// overrides enableInternet = false
	allowedHosts = ["allowed.com"];
}
import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {
	// default internet access to off unless overridden by 'allowedHosts' or outbound proxy
	enableInternet = false;

	// overrides enableInternet = false
	allowedHosts = ["allowed.com"];
}

定义出站处理程序

出站处理程序是可编程的出站代理,在与沙箱相同的机器上运行。它们可以访问所有 Workers 绑定。

使用 outbound 拦截所有出站 HTTP 与 HTTPS 流量:

import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {}

MySandbox.outbound = async (request, env, ctx) => {
	if (request.method !== "GET") {
		console.log(`Blocked ${request.method} to ${request.url}`);
		return new Response("Method Not Allowed", { status: 405 });
	}
	return fetch(request);
};
import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {}

MySandbox.outbound = async (
	request: Request,
	env: Env,
	ctx: OutboundHandlerContext,
) => {
	if (request.method !== "GET") {
		console.log(`Blocked ${request.method} to ${request.url}`);
		return new Response("Method Not Allowed", { status: 405 });
	}
	return fetch(request);
};

使用 outboundByHost 将特定域名或 IP 地址映射到处理函数:

import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {}

MySandbox.outboundByHost = {
	"my.worker": async (request, env, ctx) => {
		// Run arbitrary Workers logic from this hostname
		return await someWorkersFunction(request.body);
	},
};
import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {}

MySandbox.outboundByHost = {
	"my.worker": async (
		request: Request,
		env: Env,
		ctx: OutboundHandlerContext,
	) => {
		// Run arbitrary Workers logic from this hostname
		return await someWorkersFunction(request.body);
	},
};

来自沙箱对 http://my.worker 的调用会调用处理程序,该处理程序在 Workers 运行时中运行,位于沙箱之外。

deniedHostsallowedHosts 在任何出站处理程序之前评估。如果使用 allowedHosts,请在其中包含主机名,以便 outboundoutboundByHost 能够运行。outboundByHost 处理程序优先于通配的 outbound 处理程序。

安全地注入凭据

因为出站处理程序在 Workers 运行时中运行——位于沙箱之外——它们可以持有沙箱本身永远看不到的密钥。沙箱发出普通 HTTP 请求,处理程序在将其转发到上游服务之前附加凭据。

export class MySandbox extends Sandbox {}

MySandbox.outboundByHost = {
	"github.com": (request, env, ctx) => {
		const requestWithAuth = new Request(request);
		requestWithAuth.headers.set("x-auth-token", env.SECRET);
		return fetch(requestWithAuth);
	},
};
export class MySandbox extends Sandbox {}

MySandbox.outboundByHost = {
	"github.com": (request: Request, env: Env, ctx: OutboundHandlerContext) => {
		const requestWithAuth = new Request(request);
		requestWithAuth.headers.set("x-auth-token", env.SECRET);
		return fetch(requestWithAuth);
	},
};

这对智能体工作负载尤其有用,因为你无法完全信任沙箱内运行的代码。使用此模式:

  • 令牌不会暴露给沙箱。 密钥位于 Worker 环境中,永远不会传入沙箱。
  • 无需在沙箱内轮换令牌。 在 Worker 环境中轮换密钥,每个请求都会立即使用新密钥。
  • 按主机与按实例的规则。outboundByHostctx.containerId 结合,将凭据或权限限定到特定沙箱实例。

此处,ctx.containerId 从 KV 查找按实例的密钥:

export class MySandbox extends Sandbox {}

MySandbox.outboundByHost = {
	"my-internal-vcs.dev": async (request, env, ctx) => {
		const authKey = await env.KEYS.get(ctx.containerId);

		const requestWithAuth = new Request(request);
		requestWithAuth.headers.set("x-auth-token", authKey);
		return fetch(requestWithAuth);
	},
};
export class MySandbox extends Sandbox {}

MySandbox.outboundByHost = {
	"my-internal-vcs.dev": async (
		request: Request,
		env: Env,
		ctx: OutboundHandlerContext,
	) => {
		const authKey = await env.KEYS.get(ctx.containerId);

		const requestWithAuth = new Request(request);
		requestWithAuth.headers.set("x-auth-token", authKey);
		return fetch(requestWithAuth);
	},
};

HTTPS 流量

沙箱默认拦截 HTTPS 流量——Sandbox 类上将 interceptHttps 设为 true

当 HTTPS 拦截处于活动状态时,沙箱启动后会在 /etc/cloudflare/certs/cloudflare-containers-ca.crt 创建临时 CA 文件。

无论发行版如何,Sandbox 运行时都会尽力自动信任此 CA。启动时,它会检查各大 Linux 系列中常见的系统 CA 捆绑位置,并配置常见的 CA 环境变量,使 Node.js、curl、Python requests 与 Git 等运行时自动信任该证书。

非 HTTP 流量

出站处理程序仅拦截 HTTP 与 HTTPS 流量。端口 80443 以外的流量永远不会通过 outboundoutboundByHost 路由。

如果设置 enableInternet = false,该流量会被拒绝。DNS 查询是唯一例外,但它们仅发往 Cloudflare 的 DNS 服务器。这可防止使用任意 DNS 目标进行数据外泄。

在运行时更改策略

使用 outboundHandlers 定义命名处理程序,然后使用 setOutboundByHost() 在运行时将它们分配给特定主机。你也可以使用 setOutboundHandler() 全局应用处理程序。

你还可以使用 setOutboundByHosts()setAllowedHosts()setDeniedHosts()allowHost()denyHost()removeAllowedHost()removeDeniedHost() 管理运行时策略。

这使受信任的 Worker 能够持有凭据,而无需将其暴露给不受信任的沙箱:

import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {}

MySandbox.outboundHandlers = {
	authenticatedGithub: async (request, env, ctx) => {
		const githubToken = env.GITHUB_TOKEN;
		return authenticateGitHttpsRequest(request, githubToken, ctx.containerId);
	},
};
import { Sandbox, ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };

export class MySandbox extends Sandbox {}

MySandbox.outboundHandlers = {
	authenticatedGithub: async (
		request: Request,
		env: Env,
		ctx: OutboundHandlerContext,
	) => {
		const githubToken = env.GITHUB_TOKEN;
		return authenticateGitHttpsRequest(request, githubToken, ctx.containerId);
	},
};

从 Worker 以编程方式将处理程序应用于主机:

import { Sandbox, ContainerProxy, getSandbox } from "@cloudflare/sandbox";
export { ContainerProxy };

export default {
	async fetch(request, env) {
		const sandbox = getSandbox(env.Sandbox, "agent-session");

		// Give the sandbox access to github.com during setup
		await sandbox.setOutboundByHost("github.com", "authenticatedGithub");
		await sandbox.exec("node setup.js");

		// Remove access once setup is complete
		await sandbox.removeOutboundByHost("github.com");
	},
};
import { Sandbox, ContainerProxy, getSandbox } from "@cloudflare/sandbox";
export { ContainerProxy };

export default {
	async fetch(request: Request, env: Env) {
		const sandbox = getSandbox(env.Sandbox, "agent-session");

		// Give the sandbox access to github.com during setup
		await sandbox.setOutboundByHost("github.com", "authenticatedGithub");
		await sandbox.exec("node setup.js");

		// Remove access once setup is complete
		await sandbox.removeOutboundByHost("github.com");
	},
};

处理程序优先级

请求按此顺序评估:

  1. 首先检查 deniedHosts。匹配的主机或 IP 会立即被拒绝。
  2. 接下来检查 allowedHosts。设置后,不在列表中的任何主机或 IP 都会被拒绝。匹配的主机继续到出站处理程序;若未设置处理程序,则出口到公共互联网。
  3. 使用 setOutboundByHost() 设置的实例级规则在类级 outboundByHost 规则之前检查。
  4. 按主机处理程序始终优先于通配处理程序,因此 outboundByHostoutbound 之前运行。
  5. 使用 setOutboundHandler() 设置的实例级处理程序在类级 outbound 处理程序之前检查。
  6. 如果没有处理程序匹配,当请求匹配了 allowedHostsenableInternet = true 时,仍可出口到公共互联网。否则会被拒绝。

本地开发

wrangler dev 支持出站拦截。会在沙箱的网络命名空间中生成一个 sidecar 进程。它应用 TPROXY 规则,将匹配的流量路由到本地 Workerd 实例,以镜像生产行为。

相关资源

这篇文档对您有帮助吗?