Configurare la sandbox dei plugin

In questa pagina

I plugin sandboxed necessitano di un runner di piattaforma oltre alla loro dichiarazione del plugin. Le installazioni dal marketplace e dal registro utilizzano sempre quel runner, così come i plugin elencati sotto sandboxed: []. I plugin nativi sotto plugins: [] vengono eseguiti nel processo del server EmDash e non ottengono l’isolamento della sandbox.

Il runner dipende dalla piattaforma di distribuzione. Su Cloudflare Workers, ogni plugin viene eseguito come un Dynamic Worker creato tramite il binding Worker Loader. Su Node.js, il server avvia workerd, il runtime Workers open-source, come processo figlio ed esegue ogni plugin come servizio al suo interno. L’opzione sandboxRunner di emdash() seleziona il runner e abilita il catalogo del registro ospitato. Senza di essa, i plugin sotto sandboxed: [] non vengono caricati. Un registro configurato esplicitamente resta consultabile, ma l’installazione o l’aggiornamento di un plugin sandboxed fallisce con SANDBOX_NOT_AVAILABLE.

La seguente tabella riassume ciò che ogni runner richiede e applica.

Cloudflare WorkersNode.js
sandboxRunnersandbox() da @emdash-cms/cloudflare"@emdash-cms/sandbox-workerd/sandbox"
RequisitiPiano Workers Paid, un binding worker_loaders, PluginBridge esportato dal punto di ingresso del WorkerIl pacchetto workerd
Accesso al DBIl binding D1 DB, indipendente dall’adattatore configuratoIl database configurato
Limiti applicatiTempo CPU, sotto-richieste, tempo realeTempo reale

Cloudflare Workers

I Dynamic Workers sono disponibili con il piano Workers Paid. I template *-cloudflare includono l’esportazione del punto di ingresso qui sotto ma lasciano il binding commentato, cosicché i nuovi progetti vengono distribuiti sul piano Workers gratuito a meno che non si abilitino i plugin sandboxed durante lo scaffolding.

  1. Abilita il binding Worker Loader in wrangler.jsonc. Il runner lo legge con il nome LOADER e seleziona la sandbox Cloudflare solo quando questo binding è presente:

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

    Se la configurazione Wrangler utilizza ambienti con nome, imposta CLOUDFLARE_ENV durante il build di Astro. Il plugin Vite di Cloudflare e sandbox() leggeranno quindi lo stesso ambiente. I binding non vengono ereditati, quindi aggiungi LOADER a ogni ambiente con nome che esegue plugin sandboxed.

  2. Esporta PluginBridge dal punto di ingresso del Worker e punta main a quel file. PluginBridge è il punto di ingresso attraverso il quale i plugin sandboxed accedono a contenuto, media, storage ed e-mail; il runner lo cerca nelle esportazioni del modulo di ingresso:

    import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
    
    export { PluginBridge };
    
    export default {
    	...handler,
    	scheduled: createScheduledHandler(),
    } satisfies ExportedHandler;
    {
    	"main": "./src/worker.ts",
    }
  3. Seleziona il runner nell’integrazione emdash():

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

Node.js

  1. Installa il runner insieme a workerd, che è una dipendenza peer:

    npm install @emdash-cms/sandbox-workerd workerd

    Il pacchetto workerd installa il binario per la piattaforma corrente (Linux, macOS e Windows su x64; Linux e macOS su arm64) tramite una dipendenza opzionale. Installa con le dipendenze opzionali abilitate, sulla piattaforma su cui il server viene eseguito. In un build Docker multi-stage, esegui l’installazione in uno stage con la stessa piattaforma dello stage di runtime.

  2. Seleziona il runner nell’integrazione emdash():

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

Il runner dichiara Miniflare come dipendenza opzionale. I gestori di pacchetti la installano per impostazione predefinita. Quando NODE_ENV è development, che astro dev imposta, il runner passa i plugin a Miniflare, che gestisce il proprio processo workerd; la politica di crash sotto non si applica. Se le dipendenze opzionali sono state omesse, il runner usa workerd al suo posto. astro preview imposta NODE_ENV a production e node ./dist/server/entry.mjs lo lascia non impostato; entrambi usano workerd.

Come si esegue il processo workerd

EmDash avvia workerd durante l’inizializzazione alla prima richiesta al sito, una volta caricati i plugin sandboxed, e attende fino a 10 secondi che i servizi dei plugin rispondano. Installare o aggiornare un plugin dall’admin lo riavvia. Tutto ciò che workerd scrive su stdout o stderr appare nell’output del server con il prefisso [emdash:workerd].

I servizi dei plugin ascoltano su 127.0.0.1, e il canale di ritorno al server è un socket di dominio Unix (una porta TCP 127.0.0.1 su Windows). Non è necessario aprire alcuna porta in ingresso.

Il processo figlio riceve solo PATH, HOME, TMPDIR, TMP, TEMP, LANG e LC_ALL dall’ambiente del server, quindi i segreti nell’ambiente del server restano fuori dalla sandbox. Per passare più variabili, imposta EMDASH_WORKERD_PASSTHROUGH_ENV su un elenco separato da virgole di nomi di variabili.

Se workerd termina inaspettatamente, il runner registra [emdash:workerd] workerd exited with <reason> e lo riavvia alla prossima invocazione, con un ritardo che inizia a 1 secondo e raddoppia fino a 30 secondi. Quando workerd crasha più di cinque volte in 60 secondi, il runner smette di riavviarlo e registra [emdash:workerd] workerd crashed 5 times in 60 seconds, giving up. Da quel momento, ogni hook e route di plugin sandboxed fallisce con Plugin sandbox unavailable for <plugin>: workerd crashed 5 times in 60 seconds and the runner stopped retrying; restart the server. Riavviare il server riavvia workerd, così come installare o aggiornare un plugin dall’admin. Un SIGTERM al server termina workerd insieme ad esso.

Limiti di risorse

Ogni runner applica lo stesso set di limiti per invocazione di plugin. I limiti sono fissi; l’integrazione emdash() non ha opzioni per essi.

LimiteValoreCloudflare WorkersNode.js
Tempo CPU50 msApplicato dal Worker Loader; il plugin lancia eccezione al raggiungimentoNon applicato
Sotto-richieste10Applicato dal Worker Loader; il plugin lancia eccezione al raggiungimentoNon applicato
Memoria128 MBNon applicato per plugin; si applica il tetto di memoria isolate della piattaformaNon applicato
Tempo reale30 sApplicato dal runnerApplicato dal runner

Quando un hook o una route supera il limite di tempo reale, l’invocazione fallisce con Plugin <id> exceeded wall-time limit of 30000ms during hook:<name> (o route:<name>). Per un hook, EmDash registra il fallimento con il prefisso EmDash: Sandboxed plugin <id> e continua la richiesta senza il risultato di quel plugin. Una route di plugin che supera il limite fallisce per il suo chiamante.

Quando il runner non è disponibile

Su Cloudflare Workers, sandbox() controlla wrangler.jsonc in fase di build. Senza un binding worker_loaders chiamato LOADER, lascia il runner non impostato e registra il seguente avviso:

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

Un runner selezionato può comunque non essere disponibile a runtime: su Cloudflare Workers quando il binding LOADER distribuito o l’esportazione PluginBridge mancano, e su Node.js quando workerd non è installato o il suo binario non funziona. EmDash registra quindi un avviso con la causa che il runner riporta dopo i due punti. Il seguente avviso viene registrato su Cloudflare Workers quando il binding manca:

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.

I plugin sotto sandboxed: [] non vengono caricati, i plugin installati dal marketplace e dal registro non vengono eseguiti, e una nuova installazione dall’admin fallisce con il codice di errore SANDBOX_NOT_AVAILABLE. Il resto del sito non è interessato.

Eseguire plugin sandboxed nel processo

Imposta sandbox: false in emdash() per eseguire i plugin sotto sandboxed: [] e i plugin marketplace installati nel processo del server, senza isolamento né limiti. È un’opzione di debug che distingue un bug in un plugin da un bug nella sandbox. La seguente configurazione disattiva la sandbox su un sito Node.js:

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

Su Cloudflare Workers, il runtime si rifiuta di avviarsi con sandbox: false is not supported in Cloudflare Workers.

Risoluzione dei problemi

Ogni voce è preceduta dal messaggio come il server lo registra, o dal codice di errore che l’admin restituisce.

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

L’adattatore Cloudflare non ha selezionato un runner sandbox perché la configurazione Wrangler in fase di build non ha un binding worker_loaders chiamato LOADER. Questa è la configurazione prevista sul piano Workers gratuito. Su un piano Workers paid, abilita il binding in wrangler.jsonc e ricostruisci il sito.

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

Il testo dopo i due punti indica la causa. Su Cloudflare Workers, the worker has no worker_loaders binding named LOADER significa che wrangler.jsonc necessita di un binding worker_loaders chiamato LOADER, e the worker entrypoint does not export PluginBridge significa che il file a cui main punta deve esportare PluginBridge. Distribuire il binding richiede il piano Workers Paid.

Su Node.js, workerd is missing or its binary does not run on this platform significa che il runner non è riuscito a eseguire workerd. Esegui il binario installato direttamente in modo che il controllo non possa scaricare un pacchetto mancante:

./node_modules/.bin/workerd --version

Su Windows, esegui node_modules\\.bin\\workerd.cmd --version. Se il comando fallisce, workerd manca in node_modules o il binario installato non funziona su questa piattaforma. Reinstalla sulla piattaforma di destinazione con le dipendenze opzionali abilitate.

”workerd failed to start within 10 seconds”

Il processo figlio è stato avviato, ma i suoi servizi di plugin non hanno risposto entro 10 secondi. Le righe con prefisso [emdash:workerd] prima di questo messaggio contengono l’output di workerd stesso, inclusi errori di configurazione e avvio. Il runner riprova alla prossima invocazione.

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

Il runner ha smesso di riavviare workerd. Le righe [emdash:workerd] workerd exited with <reason> prima di questo messaggio indicano il codice di uscita o il segnale di ogni crash. Correggi la causa, poi riavvia il server.

SANDBOX_NOT_AVAILABLE durante l’installazione di un plugin

La richiesta di installazione dell’admin è stata rifiutata perché il runner manca o non è disponibile. Quando un runner è configurato, il messaggio di errore termina con la stessa causa dell’avviso di avvio sopra. Configura il runner per la piattaforma, o correggi quella causa, e ridistribuisci.