Configurer le bac à sable des plugins

Sur cette page

Les plugins sandboxés nécessitent un runner de plateforme en plus de leur déclaration de plugin. Les installations depuis le marketplace et le registre utilisent toujours ce runner, tout comme les plugins listés sous sandboxed: []. Les plugins natifs sous plugins: [] s’exécutent dans le processus du serveur EmDash et ne bénéficient pas de l’isolation du bac à sable.

Le runner dépend de la plateforme de déploiement. Sur Cloudflare Workers, chaque plugin s’exécute comme un Dynamic Worker créé via le binding Worker Loader. Sur Node.js, le serveur lance workerd, le runtime Workers open-source, comme processus enfant et exécute chaque plugin comme un service à l’intérieur. L’option sandboxRunner de emdash() sélectionne le runner et active le catalogue du registre hébergé. Sans elle, les plugins sous sandboxed: [] ne sont pas chargés. Un registre configuré explicitement reste consultable, mais l’installation ou la mise à jour d’un plugin sandboxé échoue avec SANDBOX_NOT_AVAILABLE.

Le tableau suivant résume ce que chaque runner nécessite et applique.

Cloudflare WorkersNode.js
sandboxRunnersandbox() de @emdash-cms/cloudflare"@emdash-cms/sandbox-workerd/sandbox"
PrérequisPlan Workers Paid, un binding worker_loaders, PluginBridge exporté depuis le point d’entrée du WorkerLe package workerd
Accès à la BDLe binding D1 DB, indépendant de l’adaptateur configuréLa base de données configurée
Limites appliquéesTemps CPU, sous-requêtes, temps réelTemps réel

Cloudflare Workers

Les Dynamic Workers sont disponibles avec le plan Workers Paid. Les templates *-cloudflare incluent l’exportation du point d’entrée ci-dessous mais laissent le binding commenté, de sorte que les nouveaux projets se déploient sur le plan Workers gratuit sauf si vous activez les plugins sandboxés pendant le scaffolding.

  1. Activez le binding Worker Loader dans wrangler.jsonc. Le runner le lit sous le nom LOADER et sélectionne le bac à sable Cloudflare uniquement lorsque ce binding est présent :

    {
    	"worker_loaders": [
    		{
    			"binding": "LOADER",
    		},
    	],
    }

    Si la configuration Wrangler utilise des environnements nommés, définissez CLOUDFLARE_ENV pendant le build Astro. Le plugin Vite Cloudflare et sandbox() liront alors le même environnement. Les bindings ne sont pas hérités, ajoutez donc LOADER à chaque environnement nommé qui exécute des plugins sandboxés.

  2. Exportez PluginBridge depuis le point d’entrée du Worker et pointez main vers ce fichier. PluginBridge est le point d’entrée par lequel les plugins sandboxés accèdent au contenu, aux médias, au stockage et à l’e-mail ; le runner le recherche dans les exportations du module d’entrée :

    import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
    
    export { PluginBridge };
    
    export default {
    	...handler,
    	scheduled: createScheduledHandler(),
    } satisfies ExportedHandler;
    {
    	"main": "./src/worker.ts",
    }
  3. Sélectionnez le runner dans l’intégration emdash() :

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

Node.js

  1. Installez le runner avec workerd, qui est une dépendance peer :

    npm install @emdash-cms/sandbox-workerd workerd

    Le package workerd installe le binaire pour la plateforme actuelle (Linux, macOS et Windows sur x64 ; Linux et macOS sur arm64) via une dépendance optionnelle. Installez avec les dépendances optionnelles activées, sur la plateforme où le serveur s’exécute. Dans un build Docker multi-stage, exécutez l’installation dans une étape avec la même plateforme que l’étape de runtime.

  2. Sélectionnez le runner dans l’intégration emdash() :

    import { sqlite } from "emdash/db";
    
    emdash({
    	database: sqlite({ url: "file:./data/emdash.db" }),
    	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
    });

Le runner déclare Miniflare comme dépendance optionnelle. Les gestionnaires de packages l’installent par défaut. Quand NODE_ENV est development, ce que astro dev définit, le runner confie les plugins à Miniflare, qui gère son propre processus workerd ; la politique de crash ci-dessous ne s’applique pas. Si les dépendances optionnelles ont été omises, le runner utilise workerd à la place. astro preview définit NODE_ENV à production et node ./dist/server/entry.mjs le laisse non défini ; les deux utilisent workerd.

Comment le processus workerd s’exécute

EmDash démarre workerd lors de l’initialisation à la première requête vers le site, une fois les plugins sandboxés chargés, et attend jusqu’à 10 secondes que les services de plugin répondent. L’installation ou la mise à jour d’un plugin depuis l’admin le redémarre. Tout ce que workerd écrit sur stdout ou stderr apparaît dans la sortie du serveur avec le préfixe [emdash:workerd].

Les services de plugin écoutent sur 127.0.0.1, et le canal de retour vers le serveur est un socket de domaine Unix (un port TCP 127.0.0.1 sous Windows). Aucun port entrant n’a besoin d’être ouvert.

Le processus enfant ne reçoit que PATH, HOME, TMPDIR, TMP, TEMP, LANG et LC_ALL de l’environnement du serveur, de sorte que les secrets dans l’environnement du serveur restent hors du bac à sable. Pour passer plus de variables, définissez EMDASH_WORKERD_PASSTHROUGH_ENV sur une liste séparée par des virgules de noms de variables.

Si workerd se termine de manière inattendue, le runner journalise [emdash:workerd] workerd exited with <reason> et le redémarre lors de la prochaine invocation, avec un délai qui commence à 1 seconde et double jusqu’à 30 secondes. Quand workerd plante plus de cinq fois en 60 secondes, le runner arrête de le redémarrer et journalise [emdash:workerd] workerd crashed 5 times in 60 seconds, giving up. À partir de là, chaque hook et route de plugin sandboxé échoue avec Plugin sandbox unavailable for <plugin>: workerd crashed 5 times in 60 seconds and the runner stopped retrying; restart the server. Le redémarrage du serveur relance workerd, tout comme l’installation ou la mise à jour d’un plugin depuis l’admin. Un SIGTERM vers le serveur termine workerd avec lui.

Limites de ressources

Chaque runner applique le même ensemble de limites par invocation de plugin. Les limites sont fixes ; l’intégration emdash() n’a pas d’option pour elles.

LimiteValeurCloudflare WorkersNode.js
Temps CPU50 msAppliqué par le Worker Loader ; le plugin lève une exception à la limiteNon appliqué
Sous-requêtes10Appliqué par le Worker Loader ; le plugin lève une exception à la limiteNon appliqué
Mémoire128 MoNon appliqué par plugin ; le plafond mémoire d’isolate de la plateforme s’appliqueNon appliqué
Temps réel30 sAppliqué par le runnerAppliqué par le runner

Quand un hook ou une route dépasse la limite de temps réel, l’invocation échoue avec Plugin <id> exceeded wall-time limit of 30000ms during hook:<name> (ou route:<name>). Pour un hook, EmDash journalise l’échec avec le préfixe EmDash: Sandboxed plugin <id> et continue la requête sans le résultat de ce plugin. Une route de plugin qui dépasse la limite échoue pour son appelant.

Quand le runner n’est pas disponible

Sur Cloudflare Workers, sandbox() vérifie wrangler.jsonc au moment du build. Sans binding worker_loaders nommé LOADER, il laisse le runner non défini et journalise l’avertissement suivant :

[emdash] Sandboxed plugins are disabled because wrangler.jsonc has no LOADER Worker Loader binding. Worker Loader requires a Workers paid plan.

Un runner sélectionné peut toujours être indisponible au runtime : sur Cloudflare Workers quand le binding LOADER déployé ou l’exportation PluginBridge manque, et sur Node.js quand workerd n’est pas installé ou que son binaire ne s’exécute pas. EmDash journalise alors un avertissement avec la cause que le runner rapporte après les deux-points. L’avertissement suivant est journalisé sur Cloudflare Workers quand le binding manque :

EmDash: Plugin sandbox is configured but not available on this platform: the worker has no worker_loaders binding named LOADER. Sandboxed plugins will not be loaded.

Les plugins sous sandboxed: [] ne sont pas chargés, les plugins installés du marketplace et du registre ne s’exécutent pas, et une nouvelle installation depuis l’admin échoue avec le code d’erreur SANDBOX_NOT_AVAILABLE. Le reste du site n’est pas affecté.

Exécuter les plugins sandboxés dans le processus

Définissez sandbox: false dans emdash() pour exécuter les plugins sous sandboxed: [] et les plugins marketplace installés dans le processus du serveur, sans isolation ni limites. C’est une option de débogage qui distingue un bug dans un plugin d’un bug dans le bac à sable. La configuration suivante désactive le bac à sable sur un site Node.js :

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

Sur Cloudflare Workers, le runtime refuse de démarrer avec sandbox: false is not supported in Cloudflare Workers.

Dépannage

Chaque entrée est précédée du message tel que le serveur le journalise, ou du code d’erreur que l’admin renvoie.

« [emdash] Sandboxed plugins are disabled because wrangler.jsonc has no LOADER Worker Loader binding »

L’adaptateur Cloudflare n’a pas sélectionné de runner de bac à sable car la configuration Wrangler au moment du build n’a pas de binding worker_loaders nommé LOADER. C’est la configuration attendue sur le plan Workers gratuit. Sur un plan Workers paid, activez le binding dans wrangler.jsonc et reconstruisez le site.

« Plugin sandbox is configured but not available on this platform »

Le texte après les deux-points nomme la cause. Sur Cloudflare Workers, the worker has no worker_loaders binding named LOADER signifie que wrangler.jsonc a besoin d’un binding worker_loaders nommé LOADER, et the worker entrypoint does not export PluginBridge signifie que le fichier vers lequel main pointe doit exporter PluginBridge. Le déploiement du binding nécessite le plan Workers Paid.

Sur Node.js, workerd is missing or its binary does not run on this platform signifie que le runner n’a pas pu exécuter workerd. Exécutez le binaire installé directement pour que la vérification ne puisse pas télécharger un package manquant :

./node_modules/.bin/workerd --version

Sous Windows, exécutez node_modules\\.bin\\workerd.cmd --version. Si la commande échoue, workerd est absent de node_modules ou le binaire installé ne s’exécute pas sur cette plateforme. Réinstallez sur la plateforme cible avec les dépendances optionnelles activées.

« workerd failed to start within 10 seconds »

Le processus enfant a démarré, mais ses services de plugin n’ont pas répondu dans les 10 secondes. Les lignes préfixées [emdash:workerd] avant ce message contiennent la sortie de workerd lui-même, y compris les erreurs de configuration et de démarrage. Le runner réessaie lors de la prochaine invocation.

« workerd crashed 5 times in 60 seconds, giving up »

Le runner a cessé de redémarrer workerd. Les lignes [emdash:workerd] workerd exited with <reason> avant ce message nomment le code de sortie ou le signal de chaque crash. Corrigez la cause, puis redémarrez le serveur.

SANDBOX_NOT_AVAILABLE lors de l’installation d’un plugin

La requête d’installation de l’admin a été refusée car le runner est manquant ou indisponible. Quand un runner est configuré, le message d’erreur se termine par la même cause que l’avertissement de démarrage ci-dessus. Configurez le runner pour la plateforme, ou corrigez cette cause, et redéployez.