otro importar ejecutar desde como archivo python fork subprocess setuid

python - importar - Ejecutar procesos secundarios como usuarios diferentes de un proceso de larga ejecuciĆ³n



como importar un archivo en python (4)

Tengo un proceso Python daemonizado y de larga ejecución que usa un subproceso para engendrar nuevos procesos hijos cuando ocurren ciertos eventos. El proceso de ejecución larga lo inicia un usuario con privilegios de superusuario. Necesito que el hijo procese para que se ejecute como un usuario diferente (por ejemplo, "nadie") mientras conserva los privilegios de superusuario para el proceso principal.

Actualmente estoy usando

su -m nobody -c <program to execute as a child>

pero parece pesado y no muere muy limpiamente.

¿Hay alguna manera de lograr esto programáticamente en lugar de usar su? Estoy mirando los métodos os.set * uid, pero el documento en Python std lib es bastante escaso en esa área.


Como mencionaste un daemon, puedo concluir que estás ejecutando un sistema operativo similar a Unix. Esto importa, porque cómo hacerlo depende del sistema operativo tipo. Esta respuesta se aplica solo a Unix , incluidos Linux y Mac OS X.

  1. Defina una función que establecerá el gid y el uid del proceso en ejecución.
  2. Pase esta función como el parámetro preexec_fn para subprocesar.Popen

subproceso.Popen usará el modelo fork / exec para usar su preexec_fn. Eso es equivalente a llamar a os.fork (), preexec_fn () (en el proceso hijo) y os.exec () (en el proceso hijo) en ese orden. Como os.setuid, os.setgid y preexec_fn solo son compatibles con Unix, esta solución no es portátil para otros tipos de sistemas operativos.

El siguiente código es un script (Python 2.4+) que demuestra cómo hacer esto:

import os import pwd import subprocess import sys def main(my_args=None): if my_args is None: my_args = sys.argv[1:] user_name, cwd = my_args[:2] args = my_args[2:] pw_record = pwd.getpwnam(user_name) user_name = pw_record.pw_name user_home_dir = pw_record.pw_dir user_uid = pw_record.pw_uid user_gid = pw_record.pw_gid env = os.environ.copy() env[ ''HOME'' ] = user_home_dir env[ ''LOGNAME'' ] = user_name env[ ''PWD'' ] = cwd env[ ''USER'' ] = user_name report_ids(''starting '' + str(args)) process = subprocess.Popen( args, preexec_fn=demote(user_uid, user_gid), cwd=cwd, env=env ) result = process.wait() report_ids(''finished '' + str(args)) print ''result'', result def demote(user_uid, user_gid): def result(): report_ids(''starting demotion'') os.setgid(user_gid) os.setuid(user_uid) report_ids(''finished demotion'') return result def report_ids(msg): print ''uid, gid = %d, %d; %s'' % (os.getuid(), os.getgid(), msg) if __name__ == ''__main__'': main()

Puede invocar este script de esta manera:

Comience como root ...

(hale)/tmp/demo$ sudo bash --norc (root)/tmp/demo$ ls -l total 8 drwxr-xr-x 2 hale wheel 68 May 17 16:26 inner -rw-r--r-- 1 hale staff 1836 May 17 15:25 test-child.py

No ser root en un proceso secundario ...

(root)/tmp/demo$ python test-child.py hale inner /bin/bash --norc uid, gid = 0, 0; starting [''/bin/bash'', ''--norc''] uid, gid = 0, 0; starting demotion uid, gid = 501, 20; finished demotion (hale)/tmp/demo/inner$ pwd /tmp/demo/inner (hale)/tmp/demo/inner$ whoami hale

Cuando el proceso secundario finaliza, volvemos a la raíz en el padre ...

(hale)/tmp/demo/inner$ exit exit uid, gid = 0, 0; finished [''/bin/bash'', ''--norc''] result 0 (root)/tmp/demo$ pwd /tmp/demo (root)/tmp/demo$ whoami root

Tenga en cuenta que al esperar el proceso principal para que el proceso hijo salga , solo tiene fines demostrativos . Hice esto para que el padre y el niño pudieran compartir una terminal. Un daemon no tendría terminal y rara vez esperaría para que un proceso secundario salga.


En realidad, el ejemplo con preexec_fn no funcionó para mí.
Mi solución que está funcionando bien para ejecutar un comando de shell de otro usuario y obtener su resultado es:

apipe=subprocess.Popen(''sudo -u someuser /execution'',shell=True,stdout=subprocess.PIPE)

Luego, si necesita leer desde el proceso stdout:

cond=True while (cond): line=apipe.stdout.getline() if (....): cond=False

Espero, es útil no solo en mi caso.


Hay un método os.setuid() . Puede usarlo para cambiar el usuario actual de este script.

Una solución es, en algún lugar donde el niño comienza, llamar a os.setuid() y os.setgid() para cambiar la identificación del usuario y del grupo, y luego llamar a uno de los métodos os.exec* para engendrar un nuevo hijo. El niño recién engendrado se ejecutará con el usuario menos poderoso sin la capacidad de volverse uno más poderoso nuevamente.

Otro es hacerlo cuando se inicia el daemon (el proceso maestro) y luego todos los procesos recién generados se ejecutarán bajo el mismo usuario.

Para obtener información, consulte la página de manual de setuid .


Permitir que el usuario en sudo no requiera contraseña

username ALL=(ALL) NOPASSWD: ALL

Luego llame a la función raíz con sudo , por ejemplo:

import pexpect child = pexpect.spawn(''sudo apachectl restart'') for i in child: print i #if you want to see the output from the process