插件沙箱

本页内容

沙箱插件在沙箱运行器提供的隔离运行时中运行。Marketplace 和注册表安装始终在沙箱中运行,emdash() 集成中 sandboxed: [] 下列出的插件也是如此。plugins: [] 下列出的插件在服务器进程中运行,不使用运行器。

运行器取决于部署平台。在 Cloudflare Workers 上,每个插件作为通过 Worker Loader 绑定创建的 Dynamic Worker 运行。在 Node.js 上,服务器启动 workerd(开源 Workers 运行时)作为子进程,并将每个插件作为其中的服务运行。emdash()sandboxRunner 选项选择运行器。没有它,sandboxed: [] 下的插件永远不会被加载,配置的 marketplace 会使构建失败并显示 “Marketplace requires sandboxRunner to be configured”。

下表总结了每个运行器的需求和强制执行的内容。

Cloudflare WorkersNode.js
sandboxRunner@emdash-cms/cloudflaresandbox()"@emdash-cms/sandbox-workerd/sandbox"
要求Workers 付费计划、worker_loaders 绑定、从 Worker 入口点导出的 PluginBridgeworkerd
数据库访问DB D1 绑定(与配置的适配器无关)配置的数据库
强制限制CPU 时间、子请求、墙钟时间墙钟时间

Cloudflare Workers

Dynamic Workers 在 Workers 付费计划 中可用。运行器需要以下绑定和入口点导出;*-cloudflare 模板附带了两者。

  1. 将 Worker Loader 绑定添加到 wrangler.jsonc。运行器以 LOADER 名称读取它:

    {
    	"worker_loaders": [
    		{
    			"binding": "LOADER",
    		},
    	],
    }
  2. 从 Worker 入口点导出 PluginBridge,并将 main 指向该文件。PluginBridge 是沙箱插件访问内容、媒体、存储和电子邮件的入口点;运行器在入口模块的导出中查找它:

    import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
    
    export { PluginBridge };
    
    export default {
    	...handler,
    	scheduled: createScheduledHandler(),
    } satisfies ExportedHandler;
    {
    	"main": "./src/worker.ts",
    }
  3. emdash() 集成中选择运行器:

    import { d1, r2, sandbox } from "@emdash-cms/cloudflare";
    
    emdash({
    	database: d1({ binding: "DB" }),
    	storage: r2({ binding: "MEDIA" }),
    	sandboxRunner: sandbox(),
    });

Node.js

  1. 与作为对等依赖的 workerd 一起安装运行器:

    npm install @emdash-cms/sandbox-workerd workerd

    workerd 包通过可选依赖为当前平台(x64 上的 Linux、macOS 和 Windows;arm64 上的 Linux 和 macOS)安装二进制文件。在服务器运行的平台上启用可选依赖进行安装。在多阶段 Docker 构建中,在与运行时阶段相同平台的阶段中运行安装。

  2. emdash() 集成中选择运行器:

    import { sqlite } from "emdash/db";
    
    emdash({
    	database: sqlite({ url: "file:./data/emdash.db" }),
    	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
    });
  3. 对于开发,安装 miniflare 作为开发依赖:

    npm install -D miniflare

    NODE_ENVdevelopmentastro dev 设置)且 miniflare 已安装时,运行器将插件传递给 Miniflare,由 Miniflare 管理自己的 workerd 进程;以下崩溃策略不适用。astro previewNODE_ENV 设置为 productionnode ./dist/server/entry.mjs 不设置;两者都使用 workerd

workerd 进程如何运行

EmDash 在站点首次请求时的初始化期间启动 workerd,在沙箱插件加载后,等待最多 10 秒让插件服务应答。从管理后台安装或更新插件会重新启动它。workerd 写入 stdout 或 stderr 的所有内容都以 [emdash:workerd] 前缀出现在服务器输出中。

插件服务在 127.0.0.1 上监听,返回服务器的通道是 Unix 域套接字(Windows 上是 127.0.0.1 TCP 端口)。无需打开入站端口。

子进程仅从服务器环境接收 PATHHOMETMPDIRTMPTEMPLANGLC_ALL,因此服务器环境中的密钥不会进入沙箱。要传递更多变量,将 EMDASH_WORKERD_PASSTHROUGH_ENV 设置为逗号分隔的变量名列表。

如果 workerd 意外退出,运行器记录 [emdash:workerd] workerd exited with <reason> 并在下次调用时重新启动,延迟从 1 秒开始,翻倍至 30 秒。当 workerd 在 60 秒内崩溃超过五次时,运行器停止重启并记录 [emdash:workerd] workerd crashed 5 times in 60 seconds, giving up。此后,所有沙箱插件钩子和路由都会失败并显示 Plugin sandbox unavailable for <plugin>: workerd is not running,直到服务器重启。向服务器发送 SIGTERM 也会终止 workerd

资源限制

每个运行器对每次插件调用应用相同的限制集。限制是固定的;emdash() 集成没有相关选项。

限制Cloudflare WorkersNode.js
CPU 时间50 ms由 Worker Loader 强制;插件达到限制时抛出未强制
子请求10由 Worker Loader 强制;插件达到限制时抛出未强制
内存128 MB不按插件强制;平台的隔离区内存上限适用未强制
墙钟时间30 s由运行器强制由运行器强制

当钩子或路由超过墙钟时间限制时,调用失败并显示 Plugin <id> exceeded wall-time limit of 30000ms during hook:<name>(或 route:<name>)。对于钩子,EmDash 以 EmDash: Sandboxed plugin <id> 前缀记录失败,并在没有该插件结果的情况下继续处理请求。超过限制的插件路由对其调用者失败。

当运行器不可用时

配置的运行器仍可能不可用:在 Cloudflare Workers 上缺少 worker_loaders 绑定或 PluginBridge 导出时,在 Node.js 上 workerd 未安装或其二进制文件无法运行时。EmDash 在运行时启动时记录以下警告:

EmDash: Plugin sandbox is configured but not available on this platform. Sandboxed plugins will not be loaded. If using @emdash-cms/sandbox-workerd/sandbox, ensure workerd is installed.

sandboxed: [] 下的插件不会被加载,已安装的 marketplace 和注册表插件不会运行,从管理后台进行的新安装会失败并显示错误代码 SANDBOX_NOT_AVAILABLE。站点的其余部分不受影响。

在进程内运行沙箱插件

emdash() 中设置 sandbox: false 以在没有隔离或限制的情况下在服务器进程中运行 sandboxed: [] 下的插件和已安装的 marketplace 插件。这是一个调试选项,用于区分插件中的错误和沙箱中的错误。以下配置在 Node.js 站点上关闭沙箱:

emdash({
	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
	sandbox: false,
});

在 Cloudflare Workers 上,运行时拒绝以 sandbox: false is not supported in Cloudflare Workers 启动。

故障排除

每个条目以服务器记录的消息或管理后台返回的错误代码为标题。

“Plugin sandbox is configured but not available on this platform”

在 Cloudflare Workers 上,检查两个要求:wrangler.jsonc 有一个名为 LOADERworker_loaders 绑定,main 指向导出 PluginBridge 的文件。部署绑定需要 Workers 付费计划。

在 Node.js 上,运行运行器使用的二进制文件:

npx workerd --version

如果命令失败,workerdnode_modules 中缺失或安装的二进制文件无法在此平台上运行。在目标平台上启用可选依赖重新安装。

“workerd failed to start within 10 seconds”

子进程已启动,但其插件服务未在 10 秒内应答。此消息之前的 [emdash:workerd] 前缀行包含 workerd 本身的输出,包括配置和启动错误。运行器将在下次调用时重试。

“workerd crashed 5 times in 60 seconds, giving up”

运行器已停止重启 workerd。此消息之前的 [emdash:workerd] workerd exited with <reason> 行指出每次崩溃的退出代码或信号。修复原因,然后重启服务器。

安装插件时出现 SANDBOX_NOT_AVAILABLE

由于运行器缺失或不可用,管理后台的安装请求被拒绝。为平台配置运行器,或修复上述启动警告的原因,然后重新部署。