跳转到内容
搜索文档

出口控制 (Egress control)

最后更新 查看 MarkdownAgent 设置

当您在动态 Worker 中运行不受信任或人工智能生成的代码时,您需要控制它在网络上可以访问的内容。您可能想要:

  • 阻止所有出站访问,使动态 Worker 只能使用您提供给它的绑定
  • 将出站请求限制为特定的允许目标集
  • 将凭证注入出站请求中,而不向动态 Worker 暴露机密
  • 记录或审核每个出站请求以实现可观察性

get() 返回或传递给 load()WorkerCode 对象中的 globalOutbound 选项控制所有这些。它会拦截动态 Worker 发出的每个 fetch()connect() 调用。

阻止所有出站访问

globalOutbound 设置为 null,使动态 Worker 与网络完全隔离:

return {
	mainModule: "index.js",
	modules: { "index.js": code },
	globalOutbound: null,
};

这会导致动态 Worker 中的任何 fetch()connect() 请求抛出异常。

在此模式下,您仍然可以使用绑定赋予 Dynamic Worker 直接访问特定资源和服务的权限。这是设计沙盒最干净、最安全的方法:阻止 Internet,然后通过绑定建设性地提供特定功能。

话虽如此,如果您需要与直接在您的 Dynamic Worker 沙盒中运行的现有 HTTP 客户端库提供兼容性,那么阻止 fetch() 可能是不可行的,您可能更喜欢拦截请求。

拦截出站请求

要拦截出站请求,请在充当网关的 loader Worker 中定义一个 WorkerEntrypoint 类。动态 Worker 发出的每个 fetch()connect() 调用都会通过此网关,而不是直接访问网络。使用 globalOutboundctx.exports 将网关传递给动态 Worker:

import { WorkerEntrypoint } from "cloudflare:workers";

export class HttpGateway extends WorkerEntrypoint {
	async fetch(request) {
		// Every outbound fetch() from the dynamic Worker arrives here.
		// Inspect, modify, block, or forward the request.
		return fetch(request);
	}
}

export default {
	async fetch(request, env, ctx) {
		const worker = env.LOADER.get("my-worker", async () => {
			return {
				compatibilityDate: "$today",
				mainModule: "index.js",
				modules: { "index.js": code },

				// Pass the gateway as a service binding.
				// The dynamic Worker's fetch() and connect() calls
				// are routed through HttpGateway instead of going
				// to the network directly.
				globalOutbound: ctx.exports.HttpGateway(),
			};
		});

		return worker.getEntrypoint().fetch(request);
	},
};

从这里开始,您可以向网关添加任何逻辑,例如限制目标、注入凭证或记录请求。

注入凭证

一种常见的模式是将凭证附加到出站请求,因此动态 Worker 永远看不到机密。类似于自定义绑定,您可以使用 ctx.props 将每租户或每个请求上下文传递给网关。

动态 Worker 正常调用 fetch()HttpGateway 拦截请求,从 loader Worker 的环境附加令牌,然后转发它。动态 Worker 永远无法访问 API_TOKEN

import { WorkerEntrypoint } from "cloudflare:workers";

export class HttpGateway extends WorkerEntrypoint {
	async fetch(request) {
		let url = new URL(request.url);
		const headers = new Headers(request.headers);

		// For requests to api.example.com, inject credentials.
		if (url.hostname === "api.example.com") {
			headers.set("Authorization", `Bearer ${this.env.API_TOKEN}`);
			headers.set("X-Tenant-Id", this.ctx.props.tenantId);
		}

		return fetch(request, { headers });
	}
}

export default {
	async fetch(request, env, ctx) {
		const tenantId = getTenantFromRequest(request);

		const worker = env.LOADER.get(`tenant:${tenantId}`, async () => {
			return {
				mainModule: "index.js",
				modules: {
					"index.js": `
						export default {
							async fetch() {
								const resp = await fetch("https://api.example.com/data");
								return new Response(await resp.text());
							},
						};
					`,
				},
				globalOutbound: ctx.exports.HttpGateway({
					props: { tenantId },
				}),
			};
		});

		return worker.getEntrypoint().fetch(request);
	},
};

这篇文档对您有帮助吗?