让 sandbox 中的代码调用需要 token 的 API,同时将 token 留在 Worker 中。来自 sandbox 的请求会到达 Worker 中的 entrypoint,后者只允许你选择的请求并添加 token。本例中,代码会从 GitHub API 读取你的个人资料。
- 带有 Durable Object 的 Worker(该 Durable Object 使用 Durable Object 调度策略 启动 container),或使用名为
LOADER的 Worker Loader 绑定加载 Dynamic Workers 的 Worker。要创建一个,请参阅 运行 Linux 命令 或 运行 JavaScript。 - 可以读取你个人资料的 GitHub access token。
-
在
wrangler.jsonc中将 token 声明为 secret,然后生成类型:{ "secrets": { "required": ["GITHUB_TOKEN"], }, }[secrets] required = [ "GITHUB_TOKEN" ]npx wrangler typesyarn wrangler typespnpm wrangler types -
Wrangler 提示时存储 token:
npx wrangler secret put GITHUB_TOKENyarn wrangler secret put GITHUB_TOKENpnpm wrangler secret put GITHUB_TOKEN
container 会像往常一样向 api.github.com 发送请求。Durable Object 拦截发往该 hostname 的 HTTPS 请求,并将其交付给 Worker 中的 entrypoint,由后者添加 token。
-
向 Worker 添加 entrypoint,接受一个 GitHub API 请求并添加 token:
src/index.jsjs import { WorkerEntrypoint } from "cloudflare:workers"; export class GitHubGateway extends WorkerEntrypoint { async fetch(request) { const url = new URL(request.url); if ( request.method !== "GET" || url.hostname !== "api.github.com" || url.pathname !== "/user" || url.search !== "" ) { return new Response("Forbidden", { status: 403 }); } return fetch("https://api.github.com/user", { headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${this.env.GITHUB_TOKEN}`, "User-Agent": "cloudflare-sandbox", "X-GitHub-Api-Version": "2022-11-28", }, }); } }src/index.tsts import { WorkerEntrypoint } from "cloudflare:workers"; export class GitHubGateway extends WorkerEntrypoint<Env> { async fetch(request: Request): Promise<Response> { const url = new URL(request.url); if ( request.method !== "GET" || url.hostname !== "api.github.com" || url.pathname !== "/user" || url.search !== "" ) { return new Response("Forbidden", { status: 403 }); } return fetch("https://api.github.com/user", { headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${this.env.GITHUB_TOKEN}`, "User-Agent": "cloudflare-sandbox", "X-GitHub-Api-Version": "2022-11-28", }, }); } }该 entrypoint 只允许
GET /user,因此 sandbox 中的代码无法将 token 用于其他用途。只允许代码需要的每个请求,不要更多。 -
向 Durable Object 添加方法,拦截
api.github.com并运行调用它的代码:src/index.tsts export class MyContainer extends DurableObject<Env> { // ... async getUsername(): Promise<string> { const container = this.ctx.container; if (!container) { throw new Error("No container is configured"); } await container.interceptOutboundHttps( "api.github.com", this.ctx.exports.GitHubGateway, ); if (!container.running) { container.start({ image: "cloudflare/debian-trixie", entrypoint: ["sleep", "infinity"], // The container can reach only the hostnames you intercept enableInternet: false, }); } const process = await container.exec( [ "node", "--input-type=module", "--eval", `const response = await fetch("https://api.github.com/user"); if (!response.ok) throw new Error("GitHub request failed"); console.log((await response.json()).login);`, ], { env: { // The intercept terminates TLS with a certificate that the container // CA certificate signs. Node.js reads the CA certificate from this path NODE_EXTRA_CA_CERTS: "/etc/cloudflare/certs/cloudflare-containers-ca.crt", }, }, ); const output = await process.output(); if (output.exitCode !== 0) { throw new Error(new TextDecoder().decode(output.stderr)); } return new TextDecoder().decode(output.stdout).trim(); } } -
向 Worker 添加返回用户名的路由:
src/index.tsts if (url.pathname === "/username") { const sandbox = env.MY_CONTAINER.getByName("github"); return Response.json({ username: await sandbox.getUsername() }); }该路由使用独立的沙盒名称。已运行的实例会保留启动时的 Internet 访问设置,因此
enableInternet: false只对该方法启动的实例生效。 -
部署 Worker,然后向 Wrangler 打印的
workers.devURL 上的/username发送请求:npx wrangler deployyarn wrangler deploypnpm wrangler deploycurl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev/username{ "username": "octocat" }
Dynamic Worker 获得的是调用一个固定 GitHub 端点的方法,而不是 token。
-
向 Worker 添加带有使用 token 调用 GitHub 的方法的 entrypoint:
src/index.jsjs import { WorkerEntrypoint } from "cloudflare:workers"; export class GitHub extends WorkerEntrypoint { async getUsername() { const response = await fetch("https://api.github.com/user", { headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${this.env.GITHUB_TOKEN}`, "User-Agent": "cloudflare-sandbox", "X-GitHub-Api-Version": "2022-11-28", }, }); if (!response.ok) { throw new Error(`GitHub returned ${response.status}`); } const user = await response.json(); return user.login; } }src/index.tsts import { WorkerEntrypoint } from "cloudflare:workers"; export class GitHub extends WorkerEntrypoint<Env> { async getUsername(): Promise<string> { const response = await fetch("https://api.github.com/user", { headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${this.env.GITHUB_TOKEN}`, "User-Agent": "cloudflare-sandbox", "X-GitHub-Api-Version": "2022-11-28", }, }); if (!response.ok) { throw new Error(`GitHub returned ${response.status}`); } const user = await response.json<{ login: string }>(); return user.login; } } -
在 Worker 的
fetch()handler 中,将 entrypoint 作为绑定传给 Dynamic Worker。handler 需要其ctx参数以便使用ctx.exports:src/index.tsts const sandbox = env.LOADER.load({ compatibilityDate: "2026-10-01", mainModule: "code.js", modules: { "code.js": ` import { WorkerEntrypoint } from "cloudflare:workers"; export class Code extends WorkerEntrypoint { async run() { return this.env.GITHUB.getUsername(); } } `, }, env: { // Create a stub that the Dynamic Worker can receive GITHUB: ctx.exports.GitHub({ props: {} }), }, // Block every other `fetch()` and `connect()` call from the Dynamic Worker globalOutbound: null, }); const username = await sandbox .getEntrypoint<WorkerEntrypoint & { run(): Promise<string> }>("Code") .run(); return Response.json({ username });代码调用
this.env.GITHUB.getUsername(),该方法在你的 Worker 中读取 token。 -
部署 Worker,然后向 Wrangler 打印的
workers.devURL 发送请求:npx wrangler deployyarn wrangler deploypnpm wrangler deploycurl https://<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.dev{ "username": "octocat" }
请先认证对 Worker 的调用方,以免其他人使用你的 token 配额。更多信息请参阅 Sandbox 安全。
- 克隆私有仓库:Git 的相同模式。
interceptOutboundHttps:拦截其他 Container 目标。- Bindings:向 Dynamic Worker 传递其他方法。
- Egress control:限制或审计来自 Dynamic Worker 的请求。
- Secrets:轮换或管理 token。