He usado Systrace para aislar programas que no son de confianza tanto de forma interactiva como en modo automático. Tiene un ptrace()
backend que permite su uso en un sistema Linux sin privilegios especiales, así como un backend mucho más rápido y potente que requiere parchear el kernel.
También es posible crear un sandbox en sistemas tipo Unix usando chroot(1)
, aunque eso no es tan fácil ni seguro. Los contenedores de Linux y las cárceles de FreeBSD son una mejor alternativa a chroot. Otra alternativa en Linux es usar un marco de seguridad como SELinux o AppArmor, que es lo que yo propondría para los sistemas de producción.
Podríamos ayudarlo más si nos dijera exactamente qué es lo que quiere hacer.
EDITAR:
Systrace funcionaría para su caso, pero creo que algo basado en el modelo de seguridad de Linux como AppArmor o SELinux es una alternativa más estándar y, por lo tanto, preferida, dependiendo de su distribución.
EDICIÓN 2:
Mientras que chroot(1)
está disponible en la mayoría (¿todos?) de los sistemas tipo Unix, tiene bastantes problemas:
-
Se puede romper. Si realmente va a compilar o ejecutar programas C que no son de confianza en su sistema, es especialmente vulnerable a este problema. Y si sus alumnos son como los míos, alguien intentará escapar de la cárcel.
-
Debe crear una jerarquía de sistema de archivos completamente independiente con todo lo necesario para su tarea. No es necesario tener un compilador en el chroot, pero debe incluirse todo lo necesario para ejecutar los programas compilados. Si bien existen utilidades que ayudan con esto, aún no es trivial.
-
Tienes que mantener el chroot. Dado que es independiente, los archivos chroot no se actualizarán junto con su distribución. Tendrá que volver a crear el chroot regularmente o incluir las herramientas de actualización necesarias en él, lo que esencialmente requeriría que sea una distribución de Linux completa. También deberá mantener los datos del sistema y del usuario (contraseñas, archivos de entrada, etc.) sincronizados con el sistema host.
-
chroot()
solo protege el sistema de archivos. No evita que un programa malicioso abra sockets de red o que uno mal escrito absorba todos los recursos disponibles.
El problema del uso de recursos es común entre todas las alternativas. Las cuotas del sistema de archivos evitarán que los programas llenen el disco. Correcto ulimit
(setrlimit()
en C) la configuración puede proteger contra el uso excesivo de memoria y cualquier bomba de bifurcación, así como poner fin a los cerdos de CPU. nice(1)
puede reducir la prioridad de esos programas para que la computadora se pueda usar para cualquier tarea que se considere más importante sin problema.
Recientemente escribí una descripción general de las técnicas de sandboxing en Linux. Creo que su enfoque más fácil sería usar contenedores de Linux (lxc) si no le importa bifurcar y demás, que realmente no importan en este entorno. Puede darle al proceso un sistema de archivos raíz de solo lectura, una conexión de red de loopback aislada, y aún puede eliminarlo fácilmente y establecer límites de memoria, etc.
Seccomp va a ser un poco difícil, ya que el código ni siquiera puede asignar memoria.
Selinux es la otra opción, pero creo que podría ser más trabajo que un contenedor.
Puede usar Qemu para probar tareas rápidamente. Este procedimiento a continuación toma menos de 5 segundos en mi computadora portátil de 5 años.
Supongamos que el estudiante tiene que desarrollar un programa que tome entradas sin signo, cada una en su propia línea, hasta que llegue una línea con "-1". Luego, el programa debe promediar todos los enteros y mostrar "Promedio:%f". Así es como podría probar el programa completamente aislado:
-
Primero, obtén
root.bin
de Jslinux, lo usaremos como espacio de usuario (tiene el compilador C tcc):wget https://github.com/levskaya/jslinux-deobfuscated/raw/master/root.bin
-
Queremos poner la entrega del estudiante en
root.bin
, así que configure el dispositivo de bucle:sudo losetup /dev/loop0 root.bin
(También podría usar fuseext2 para esto, pero no es muy estable. Si se estabiliza, no necesitará root para nada de esto)
-
Haz un directorio vacío:
mkdir mountpoint
-
Monte
root.bin
:sudo mount /dev/loop0 mountpoint
-
Introduzca el sistema de archivos montado:
cd mountpoint
. -
Corregir derechos:
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
-
Copie el envío a la máquina virtual:
cp ~/student_assignment.c root/assignment.c
-
Salga del FS raíz de la VM:
cd ..
sudo umount mountpoint
- Ahora la imagen está lista, solo necesitamos ejecutarla. Compilará y ejecutará el envío después de arrancar.
mkfifo /tmp/guest_output
-
Abra una terminal separada y comience a escuchar la salida del invitado:
dd if=/tmp/guest_output bs=1
-
En otra terminal:
qemu-system-i386 -kernel vmlinuz-3.5.0-27-generic -initrd root.bin -monitor stdio -nographic -serial pipe:/tmp/guestoutput
(Acabo de usar el kernel de Ubuntu aquí, pero muchos kernels funcionarán) -
Cuando la salida del invitado muestra "LISTO", puede enviar claves a la máquina virtual desde el indicador de qemu. Por ejemplo, para probar esta asignación, podría hacer
(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
-
Ahora
Average = 12.000000
debería aparecer en la tubería de salida del huésped. Si no es así, el estudiante reprobó. - Salir de qemu:
quit
Un programa que pasa la prueba está aquí:https://stackoverflow.com/a/14424295/309483. Solo usa tcclib.h
en lugar de stdio.h
.