
Esegue Python, JavaScript e Bash non attendibili in una jail rafforzata con limiti di 5 secondi, 128 MB, 0,5 core, PID e output.
Esecuzione di codice non attendibile applicata dal kernel — ogni payload isolato tramite cgroups v2, namespace, seccomp-BPF e un rootfs in sola lettura, e ogni esecuzione verificata in PostgreSQL.
python · javascript · bash → gabbia da 5 secondi, 128 MB, 0.5 core, 32 PID, output 512 KB
Un servizio di esecuzione con protezioni di sicurezza avanzate per codice di cui non ci si fida. I chiamanti inviano il codice sorgente tramite una piccola API REST; il demone avvia ogni payload in una gabbia effimera a livello kernel in cui è il kernel Linux stesso — non la logica applicativa — a garantire il contenimento:
pivot_root su un rootfs in sola lettura),lo disattivato + seccomp che blocca socket),PID · MOUNT · NET · IPC · UTS · USER), una rigorosa allowlist seccomp-BPF, gabbie di risorse cgroups v2 e un filesystem pivot_root in sola lettura con uno spazio scratch tmpfs da 16 MB.5 s di tempo reale, tetto di memoria di 128 MB (swap disabilitato, OOM kill istantaneo), 0.5 core CPU, immunità alle fork-bomb con pids.max = 32 e limiti di output di 512 KB su stdout/stderr.execution_runs).flowchart LR
CLI["👤 API Client<br/><i>tenant / CI job</i>"]
subgraph CP["🔐 Trusted Control Plane — Go daemon"]
API["Ingestion Daemon<br/><i>net/http :8080</i>"]
POOL["Warm Runner Pool<br/><i>16 slots · 3 s cap</i>"]
RUN["Process Runner<br/><i>watchdog + pipes</i>"]
ISO["Kernel Isolation<br/><i>cgroups v2 · namespaces · seccomp-BPF</i>"]
end
subgraph JAIL["⛓️ Ephemeral Kernel Jail — untrusted"]
INIT["sandbox_init<br/><i>exec trampoline</i>"]
GUEST["💀 Untrusted Code<br/><i>python · node · bash</i>"]
end
PG[("🗄️ PostgreSQL 16<br/><i>execution_runs</i>")]
RD[("📮 Redis 7 Queue<br/><i>sandbox:jobs</i>")]
CLI -->|"POST /api/v1/execute"| API
API -->|"validate + acquire"| POOL
POOL -->|"spawn + limits"| RUN
RUN -.->|"apply jail"| ISO
ISO ==>|"seccomp · pivot_root"| GUEST
RUN -->|"fork / exec"| INIT
INIT -->|"syscall.Exec"| GUEST
RUN -.->|"stdout / stderr ≤ 512 KB"| POOL
API -.->|"async audit INSERT"| PG
RD -.->|"BRPop drain · worker module"| POOL
style JAIL fill:#2a1215,stroke:#f87171,stroke-width:2px
style CP fill:#0d1b2a,stroke:#38bdf8,stroke-width:1px
| Componente | Responsabilità | Sorgente |
|---|---|---|
| Ingestion Daemon | Superficie REST: POST /api/v1/execute + /health; validazione delle richieste; emissione asincrona dell'audit | cmd/server/main.go |
| Warm Runner Pool | Slot di esecuzione limitati da semaforo (16) con timeout di acquisizione di 3 s → backpressure da esaurimento in stile 503 | internal/runner/pool.go |
| Process Runner | Costruisce il comando del linguaggio, collega il cgroup, trasmette le pipe tramite writer troncanti, applica il watchdog di 5 s, raccoglie le metriche del cgroup, classifica i risultati | internal/runner/process.go |
| Kernel Isolation | Limitatore cgroups v2 (memory.max, cpu.max, pids.max), flag dei namespace (CLONE_NEWPID/NET/NS/IPC/UTS/USER), compilatore di filtri seccomp-BPF | internal/isolation/ |
| sandbox_init | Trampolino figlio: pivot_root in sola lettura + tmpfs /dev//tmp, distacco dalla rete (lo down), applicazione di seccomp, quindi syscall.Exec del target | cmd/sandbox_init/main.go |
| Codice non attendibile | Il payload guest. Dispositivi host rimossi, /proc nascosto (hidepid=2), tutte le syscall non in allowlist terminate | immagini rootfs in runtimes/ |
| PostgreSQL 16 | Log di audit durevole di ogni esecuzione, incluse le violazioni di sicurezza | migrations/001_init_sandbox_schema.sql |
| Redis 7 | Coda sandbox:jobs + worker BRPop che drena verso il pool (modulo implementato, non ancora collegato al daemon — vedi Limitazioni note) | internal/queue/redis_consumer.go |
| Confine | Cosa separa | Applicazione |
|---|---|---|
| Client ↔ Daemon | Invio di payload non autenticato dal piano di controllo | validazione dello schema (tenant_id, language, code) |
| Piano di controllo ↔ Jail | Codice Go attendibile vs. payload ostile | fork in namespace privati; pivot_root; seccomp inherit-on-exec |
| Jail ↔ Limiti del kernel | Consumo di risorse del guest vs. capacità dell'host | cgroups v2 memory.max=128MB, swap.max=0, cpu.max="50000 100000", pids.max=32 |
| Jail ↔ Rete | Qualsiasi egress o raggiungibilità lato host | namespace NET privato, lo down, syscall socket terminata |
🔍 Una versione interattiva e adattiva al tema di questo diagramma (con esportazione PNG/SVG) è realizzata a partire dai candidati supportati da evidenze in
docs/archify/.
sequenceDiagram
autonumber
participant C as Client
participant A as REST API
participant P as Warm Pool
participant R as Process Runner
participant I as sandbox_init
participant G as Untrusted Code
participant DB as PostgreSQL
C->>A: POST /api/v1/execute
A->>P: acquire slot · 16 max, 3 s timeout
P->>R: Run(req, limits)
R->>I: fork under CLONE_NEWPID·NET·NS·IPC·UTS·USER
R->>R: create cgroup scope → attach PID<br/>128 MB · 0.5 CPU · 32 PIDs
I->>I: pivot_root RO rootfs · lo down · seccomp-BPF
I->>G: syscall.Exec target interpreter
G-->>R: stdout / stderr via pipes (512 KB cap)
R->>R: watchdog: SIGKILL at 5 s → exit 137
R-->>P: result + cgroup metrics (peak mem, CPU)
P-->>A: ExecutionResult
A-->>C: 200 JSON
A-)DB: async audit INSERT (non-blocking)