Salta ai contenuti

Configurare il renderer Chrome per NextPDF Artisan

Il bridge avvia e controlla un processo Chrome/Chromium locale tramite chrome-php/chrome. Questa pagina descrive come configurare tale runtime affinché il rendering completi correttamente e chiarisce le decisioni relative a container e sandbox.

BrowserPool crea un chrome-php/chromeBrowserFactory (eventualmente con un percorso esplicito del binario) e avvia Chrome con un insieme fisso di flag: headless: true, keepAlive: true, windowSize: [1200, 800], sendSyncDefaultTimeout: renderTimeout * 1000 e i flag personalizzati elencati nella pagina /integrations/artisan/configuration/. Il bridge controlla poi il processo avviato tramite il Chrome DevTools Protocol. Non si collega a un’istanza di Chrome separata già in esecuzione tramite una porta di debug remoto, quindi non esiste alcun endpoint di rete da esporre o autenticare. Chrome viene eseguito come processo figlio del worker PHP. Il test tests/Unit/Artisan/BrowserPoolTest.php::getBrowserCreatesAndReusesInstanceWithExpectedOptions verifica esattamente queste opzioni di avvio.

Installare una build di Chrome o Chromium eseguibile dall’utente che esegue il worker:

Terminal window
# Debian / Ubuntu
apt-get install -y chromium
# RHEL / Fedora
dnf install -y chromium
# Alpine (containers)
apk add --no-cache chromium nss freetype harfbuzz ttf-freefont

Verificare che possa essere eseguito in modalità headless dallo stesso utente:

Terminal window
chromium --headless --dump-dom about:blank

Un codice di uscita 0 con un DOM vuoto indica che il binario e le relative librerie condivise sono presenti. Un codice di uscita diverso da zero corrisponde allo stesso errore che il bridge espone come ChromeRenderException. Risolverlo prima a questo livello.

Il rilevamento automatico (predefinito in chrome-php/chrome) funziona quando il binario si trova in un percorso standard. Per ottenere un comportamento deterministico in produzione, specificarlo esplicitamente:

$config = new ChromeRendererConfig(
chromeBinaryPath: '/usr/bin/chromium',
);

oppure tramite una configurazione basata su array:

$config = ChromeRendererConfig::fromArray([
'chrome_binary' => '/usr/bin/chromium',
]);

In un container, la sandbox del sistema operativo di Chrome spesso non si inizializza quando il processo viene eseguito come root / PID 1 senza capability aggiuntive del kernel. Sono disponibili due opzioni:

  1. Mantenere la sandbox (consigliato). Eseguire il worker come utente non root e concedere al container le capability richieste dalla sandbox di Chrome (di norma SYS_ADMIN o un profilo seccomp che consenta la creazione di user namespace). In questo modo l’isolamento di processo di Chrome rimane intatto.
  2. Disabilitare la sandbox. Impostare no_sandbox: true. Chrome viene avviato con --no-sandbox. Questo rimuove la sandbox di isolamento di processo di Chrome: una reale riduzione del contenimento, non un flag puramente cosmetico. Utilizzarlo solo dove la sandbox non può essere abilitata, eseguire Chrome come utente non root all’interno di un container vincolato e trattare il deployment come se richiedesse un livello di fiducia più elevato nell’input. Le barriere di rete del bridge (CSP + blocco CDP) restano comunque attive, ma non sostituiscono l’isolamento di processo. Ciò è in linea con le indicazioni OWASP ASVS su privilegio minimo e isolamento per il rendering di contenuti non attendibili.

La descrizione completa dei confini, ovvero ciò che la sandbox protegge e ciò che non protegge, si trova nella pagina /integrations/artisan/security-and-operations/. Questa pagina non afferma che disabilitare la sandbox sia sicuro.

FROM php:8.4-cli
RUN apt-get update && apt-get install -y --no-install-recommends \
chromium fonts-liberation \
&& rm -rf /var/lib/apt/lists/*
RUN useradd -m -u 10001 worker
USER worker
ENV CHROME_BINARY=/usr/bin/chromium
# Set CHROME_NO_SANDBOX=1 only if the sandbox cannot be enabled in your runtime.

Eseguire il worker come worker (uid 10001), non come root. Il bridge applica già il flag --disable-dev-shm-usage, che evita l’arresto anomalo dovuto a un /dev/shm troppo piccolo, un caso comune nei container senza ulteriori ottimizzazioni.

Il bridge blocca il recupero dei font remoti (--disable-remote-fonts e CSP). Installare i font necessari a livello di sistema operativo oppure incorporarli come URI data: con sorgenti @font-face all’interno di defaultCss o nell’HTML. L’output CJK richiede un pacchetto di font CJK (ad esempio fonts-noto-cjk) installato nell’immagine.

Utilizzare questo probe autonomo per verificare l’intero percorso del bridge senza passare dall’applicazione host:

chrome-health.php
<?php
declare(strict_types=1);
use NextPDF\Artisan\ChromeHtmlRenderer;
use NextPDF\Artisan\ChromeRendererConfig;
require __DIR__ . '/vendor/autoload.php';
$renderer = new ChromeHtmlRenderer(
ChromeRendererConfig::fromArray([
'chrome_binary' => getenv('CHROME_BINARY') ?: null,
'no_sandbox' => (bool) getenv('CHROME_NO_SANDBOX'),
]),
);
$result = $renderer->render('<p>ok</p>', 200.0, 0.0);
fwrite(STDOUT, strlen($result->getPdfData()) > 0 ? "CHROME_OK\n" : "CHROME_EMPTY\n");
$renderer->close();

CHROME_OK conferma avvio, rendering e importazione. Se viene sollevata un’eccezione, questa indica l’errore preciso. Confrontare l’errore con la pagina /integrations/artisan/troubleshooting/. Integrarlo come controllo di readiness nei deployment orchestrati.

  • Eseguire Chrome come utente non root dedicato.
  • Applicare un limite di memoria a livello di host; il bridge contiene la crescita con un riavvio ogni 100 rendering, ma è comunque necessario un tetto massimo a livello di host.
  • Abbinare render_timeout a un budget di richiesta a monte su qualsiasi percorso raggiungibile da input non attendibile.
  • Non esporre alcuna porta di debug remoto di Chrome. Il bridge non ne utilizza alcuna e una porta CDP aperta costituisce un canale di controllo non autenticato.
SintomoCausa probabileDove cercare
ChromeNotAvailableExceptionchrome-php/chrome non installato/integrations/artisan/install/
ChromeRenderException al primo renderingBinario mancante / la sandbox non riesce a inizializzarsiQuesta pagina; /integrations/artisan/troubleshooting/
PDF vuotoNessun riquadro visibile / arresto anomalo di Chrome/integrations/artisan/troubleshooting/
Immagini remote vuoteRete bloccata per scelta progettuale/integrations/artisan/security-and-operations/
Picco di latenza periodicoRiavvio ogni 100 rendering/integrations/artisan/production-usage/
  • /integrations/artisan/install/
  • /integrations/artisan/configuration/
  • /integrations/artisan/security-and-operations/
  • /integrations/artisan/troubleshooting/
  • /integrations/artisan/production-usage/