Ho utilizzato Systrace per eseguire il sandbox di programmi non attendibili sia in modo interattivo che in modalità automatica. Ha un ptrace()
-based backend che ne consente l'utilizzo su un sistema Linux senza privilegi speciali, oltre a un backend molto più veloce e potente che richiede l'applicazione di patch al kernel.
È anche possibile creare una sandbox su sistemi simili a Unix usando chroot(1)
, anche se non è altrettanto facile o sicuro. I container Linux e le jail di FreeBSD sono un'alternativa migliore a chroot. Un'altra alternativa su Linux è utilizzare un framework di sicurezza come SELinux o AppArmor, che è quello che proporrei per i sistemi di produzione.
Saremmo in grado di aiutarti di più se dicessi esattamente cosa vuoi fare.
MODIFICA:
Systrace funzionerebbe per il tuo caso, ma penso che qualcosa basato sul Linux Security Model come AppArmor o SELinux sia un'alternativa più standard, e quindi preferita, a seconda della tua distribuzione.
MODIFICA 2:
Mentre chroot(1)
è disponibile sulla maggior parte (tutti?) dei sistemi simili a Unix, presenta alcuni problemi:
-
Può essere rotto. Se hai effettivamente intenzione di compilare o eseguire programmi C non attendibili sul tuo sistema, sei particolarmente vulnerabile a questo problema. E se i tuoi studenti sono come i miei, qualcuno cercherà di evadere dalla prigione.
-
Devi creare una gerarchia di file system completamente indipendente con tutto ciò che è necessario per il tuo compito. Non è necessario disporre di un compilatore nel chroot, ma dovrebbe essere incluso tutto ciò che è necessario per eseguire i programmi compilati. Sebbene ci siano utilità che aiutano in questo, non è ancora banale.
-
Devi mantenere il chroot. Poiché è indipendente, i file chroot non verranno aggiornati insieme alla tua distribuzione. Dovrai ricreare regolarmente il chroot o includere gli strumenti di aggiornamento necessari, il che richiederebbe essenzialmente che si tratti di una distribuzione Linux completa. Dovrai anche mantenere i dati di sistema e utente (password, file di input e.t.c.) sincronizzati con il sistema host.
-
chroot()
protegge solo il filesystem. Non impedisce a un programma dannoso di aprire socket di rete o a uno scritto male di risucchiare ogni risorsa disponibile.
Il problema dell'utilizzo delle risorse è comune a tutte le alternative. Le quote del filesystem impediranno ai programmi di riempire il disco. Corretto ulimit
(setrlimit()
in C) le impostazioni possono proteggere dall'uso eccessivo della memoria e da eventuali fork bomb, oltre a porre fine ai maiali della CPU. nice(1)
può abbassare la priorità di quei programmi in modo che il computer possa essere utilizzato per qualsiasi attività ritenuta più importante senza problemi.
Di recente ho scritto una panoramica delle tecniche di sandboxing in Linux. Penso che il tuo approccio più semplice sarebbe usare i contenitori Linux (lxc) se non ti dispiace biforcare e così via, che non ha molta importanza in questo ambiente. Puoi dare al processo un file system root di sola lettura, una connessione di rete di loopback isolata e puoi ancora ucciderlo facilmente e impostare limiti di memoria, ecc.
Seccomp sarà un po' difficile, dato che il codice non può nemmeno allocare memoria.
Selinux è l'altra opzione, ma penso che potrebbe essere più utile di un contenitore.
Puoi usare Qemu per testare rapidamente gli incarichi. Questa procedura di seguito richiede meno di 5 secondi sul mio laptop di 5 anni.
Supponiamo che lo studente debba sviluppare un programma che accetta interi senza segno, ciascuno sulla propria riga, finché non arriva una riga con "-1". Il programma dovrebbe quindi fare la media di tutti gli interi e produrre "Average:%f". Ecco come puoi testare il programma completamente isolato:
-
Per prima cosa, ottieni
root.bin
da Jslinux, lo useremo come userland (ha il compilatore C tcc):wget https://github.com/levskaya/jslinux-deobfuscated/raw/master/root.bin
-
Vogliamo inserire la consegna dello studente in
root.bin
, quindi configura il dispositivo loop:sudo losetup /dev/loop0 root.bin
(puoi usare fuseext2 anche per questo, ma non è molto stabile. Se si stabilizza, non avrai bisogno di root per niente di tutto questo)
-
Crea una directory vuota:
mkdir mountpoint
-
Monta
root.bin
:sudo mount /dev/loop0 mountpoint
-
Inserisci il filesystem montato:
cd mountpoint
. -
Correggi i diritti:
sudo chown -R `whoami` .
mkdir -p etc/init.d
-
vi etc/init.d
:#!/bin/sh cd /root echo READY 2>&1 > /dev/ttyS0 tcc assignment.c 2>&1 > /dev/ttyS0 ./a.out 2>&1 > /dev/ttyS0
-
chmod +x etc/init.d/rcS
-
Copia l'invio alla VM:
cp ~/student_assignment.c root/assignment.c
-
Esci dal root FS della VM:
cd ..
sudo umount mountpoint
- Ora l'immagine è pronta, dobbiamo solo eseguirla. Compilerà ed eseguirà l'invio dopo l'avvio.
mkfifo /tmp/guest_output
-
Apri un terminale separato e inizia ad ascoltare l'output dell'ospite:
dd if=/tmp/guest_output bs=1
-
In un altro terminale:
qemu-system-i386 -kernel vmlinuz-3.5.0-27-generic -initrd root.bin -monitor stdio -nographic -serial pipe:/tmp/guestoutput
(Ho appena usato il kernel di Ubuntu qui, ma molti kernel funzioneranno) -
Quando l'output del guest mostra "READY", puoi inviare le chiavi alla VM dal prompt di qemu. Ad esempio, per testare questa assegnazione, potresti fare
(qemu) sendkey 1 (qemu) sendkey 4 (qemu) sendkey ret (qemu) sendkey 1 (qemu) sendkey 0 (qemu) sendkey ret (qemu) sendkey minus (qemu) sendkey 1 (qemu) sendkey ret
-
Ora
Average = 12.000000
dovrebbe apparire sulla pipe di output del guest. In caso contrario, lo studente ha fallito. - Esci da qemu:
quit
Un programma che supera il test è qui:https://stackoverflow.com/a/14424295/309483. Basta usare tcclib.h
invece di stdio.h
.