2012-07-30 10 views
10

Estoy escribiendo un servidor web basado en Python que debería poder ejecutar "complementos" para que la funcionalidad se pueda extender fácilmente.Python: ¿Proteger los scripts/subprocesos no confiables con chroot y chjail?

Para esto consideré el enfoque para tener un número de carpetas (una para cada complemento) y un número de scripts de shell/python que llevan el nombre de nombres predefinidos para diferentes eventos que pueden ocurrir.

Un ejemplo es tener un archivo on_pdf_uploaded.py que se ejecuta cuando se carga un PDF en el servidor. Para hacer esto, utilizaría las herramientas del subproceso de Python.

Para mayor comodidad y seguridad, esto me permitiría usar variables de entorno Unix para proporcionar más información y configurar el directorio de trabajo (cwd) del proceso para que pueda acceder a los archivos correctos sin tener que encontrar su ubicación.

Como el código del complemento procede de una fuente que no es de confianza, quiero que sea lo más seguro posible. Mi idea era ejecutar el código en un subproceso, pero ponerlo en una cárcel chroot con un usuario diferente, para que no pueda acceder a ningún otro recurso en el servidor.

Lamentablemente no pude encontrar nada al respecto, y no me gustaría confiar en la secuencia de comandos que no es de confianza para meterse en la cárcel.

Además, no puedo poner el proceso principal/de llamadas en una cárcel chroot, ya que el código del complemento puede ejecutarse en múltiples procesos al mismo tiempo mientras el servidor está respondiendo otras solicitudes.

Así que aquí está la pregunta: ¿Cómo puedo ejecutar subprocesos/scripts en una cárcel chroot con privilegios mínimos para proteger el resto del servidor de daños provocados por un código defectuoso que no es de confianza?

¡Gracias!

+0

¿Es este realmente su trabajo? ¿No deberían saber qué código están ejecutando? Lo que sea ... ¿Esto ayuda? [os.chroot()] (http://docs.python.org/library/os.html#os.chroot). Además, 'os' tiene cosas buenas para meterse con el uid, etc. Entonces, cree un nuevo proceso, (os.fork()?) Luego os.setuid y luego os.execle(). – Logan

Respuesta

2

Después de crear su cárcel, debe llamar al os.chroot desde su fuente de Python para acceder a ella. Pero incluso entonces, cualquier biblioteca compartida o archivo de módulo ya abierto por el intérprete seguiría abierto, y no tengo idea de cuáles serían las consecuencias de cerrar esos archivos a través del os.close; Nunca lo intenté.

Incluso si esto funciona, la configuración de chroot es un gran problema, así que asegúrese de que el beneficio vale la pena el precio. En el peor de los casos, debe asegurarse de que todo el tiempo de ejecución de Python con todos los módulos que pretenda utilizar, así como todos los programas dependientes y bibliotecas compartidas y otros archivos desde /bin, /lib, etc., están disponibles dentro de cada sistema de archivos. Y, por supuesto, hacer esto no protegerá otros tipos de recursos, es decir, destinos de red, base de datos.

Una alternativa podría ser leer en el código que no es de confianza como una cadena y luego exec code in mynamespace donde mynamespace es un diccionario que define solo los símbolos que desea exponer al código que no es de confianza. Esto sería una especie de "cárcel" dentro de la VM de Python. Es posible que tenga que analizar la fuente buscando primero cosas como import, a menos que reemplazar la función incorporada __import__ interceptaría eso (no estoy seguro).

+0

Fui con el enfoque de carga de código dinámico porque ofrece la mejor flexibilidad de intercambio de datos y manejo de excepciones. – BastiBen

4

Quizás algo como esto?

# main.py 
subprocess.call(["python", "pluginhandler.py", "plugin", env]) 

Entonces,

# pluginhandler.py 
os.chroot(chrootpath) 
os.setgid(gid) # Important! Set GID first! See comments for details. 
os.setuid(uid) 
os.execle(programpath, arg1, arg2, ..., env) 
# or another subprocess call 
subprocess.call["python", "plugin", env]) 

EDIT: quería utilizar tenedor() pero no entendía realmente lo que hizo. Lo busqué. Nuevo código !

# main.py 
import os,sys 
somevar = someimportantdata 
pid = os.fork() 
if pid: 
    # this is the parent process... do whatever needs to be done as the parent 
else: 
    # we are the child process... lets do that plugin thing! 
    os.setgid(gid) # Important! Set GID first! See comments for details. 
    os.setuid(uid) 
    os.chroot(chrootpath) 
    import untrustworthyplugin 
    untrustworthyplugin.run(somevar) 
    sys.exit(0) 

This era útil y que prácticamente sólo se robó ese código, así que felicitaciones a ese tipo para un ejemplo decente.

+1

Esto prácticamente resolvió mis problemas. La cosa era complicada debido a la instalación de psutil dentro de un virtualenv, y permisos de usuario, pero después de algún intento/error lo arreglé. – coya

+0

@habnabit ¿puedes dar más detalles sobre tu edición? No estoy familiarizado con el motivo por el orden y creo que merece ser llamado más explícitamente en la respuesta si tiene implicaciones de seguridad. – Logan

+1

@Logan https://stackoverflow.com/a/11062896 – habnabit

Cuestiones relacionadas