Un docker container per eseguire codice python in modalità sicura (da node.js…)
Se ci troviamo a interagire con un agente autonomo AI e vogliamo dargli la possibilità di eseguire codice, è molto salutare isolare l’ambiente dove il codice sarà eseguito in una sandbox.
Dopo tutto il codice potrebbe anche eseguire attività malevole o distruttive.
Ergo: meno contatto ha con l’esterno e meglio è per tutti.
L’idea è quindi di offrire all’agent un docker container con i moduli utilizzabili e avviare il container mappando una unità di rete:
qui metteremo uno script da eseguire e gli eventuali file necessari 8dati, innagini, audio, ecc.) e riceveremo gli output.
Il primo passo è creare un Dockerfile che definisca l’ambiente in cui il codice Python sarà eseguito. Possiamo partire da un’immagine ufficiale Python e installare alcune librerie statistiche:
Ecco un esempio:
# Base image Python leggera:
FROM python:3.11-slim
# Imposta encoding e evita output bufferizzato:
ENV PYTHONUNBUFFERED=1
ENV PYTHONIOENCODING=utf-8
# Imposto la cartella di lavoro DENTRO al container (sarà quella che mapperemo con una directory locale)
WORKDIR /sandbox
# Aggiorno pip e installo librerie base di analisi e statistiche
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir \
pandas \
numpy \
matplotlib \
seaborn \
scikit-learn \
statsmodels \
weasyprint \
openpyxl
# Punto di ingresso la bash:
CMD ["bash"]
Con il Dockerfile pronto, possiamo costruire l’immagine (inutile dire che dobbiamo avere docker installato e che funzioni con i container linux…):
docker build -t python-sandbox .
Nota: importante il “punto finale” da non dimenticare; il comando va eseguito da un CMD/Powershell/Bash/sh mentre si è nella dir cove c’è il file Dockerfile.
Ora siamo pronti per eseguire il container (qui viene creata anche la cartella locale, se c’è già basta mapparla):
mkdir -p ./localsandbox
docker run --rm -it --network none -v $(pwd)/localsandbox:/sandbox python-sandbox
–rm si utilizza per indicare che alla fine dell’esecuzione il cotainer deve essere rimosso;
-v $(pwd)/localsandbox:/sandobox mappa la cartella locale <directorycorrente>/localsandbox dentro il container nella cartella /sandbox del container, così possiamo passare script e dati e leggere il/i file risultato
–network none isola il container disabilitando le risorse di rete.
Ora possiamo scrivere uno script python dentro ./localsandbox e il container potrà eseguirlo senza contatto diretto con il resto del sistema.
Come fare questo da node.js, per esempio?
Io utilizzo spawnSync che è un export di un modulo:
import { spawnSync } from 'child_process';
Così posso creare il comando “a pezzi”:
const res = spawnSync('docker', [
'run', '--rm', '--network', 'none',
'-v', `<qui ci va il percorso della directory locale da mappare>:/sandbox:rw`,
'python-sandbox:latest',
'python', `/sandbox/<qui ci va lo script python da eseguire, solo il nome del file messo nella dir>`
], { encoding: 'utf-8' });
res sarà il risultato dell’esecuzione dello script.
Per la gestione dell’esito, potremmo fare una cosa più o meno così:
if (res.error) {
// questo è proprio un errore di lancio di docker:
console.error(res.error.message);
} else if (res.status !== 0) {
// qui, invece, si cattura l'output di esecuzione di python:
// per esempio lo stack di chiamata
// e l'errore di esecuzione:
console.log('errore interno', res.stderr || res.stdout);
} else {
// da qui l'esecuzione continua se non ci sono stati errori...
Non male vero?