J'ai un long processus démonifié Python qui utilise un sous-processus pour générer de nouveaux processus enfants lorsque certains événements se produisent. Le processus long est démarré par un utilisateur disposant de privilèges de super-utilisateur. J'ai besoin du les processus enfants apparaissent pour s'exécuter en tant qu'utilisateur différent (par exemple, "personne") tout en conservant les privilèges de superutilisateur pour le processus parent.
J'utilise actuellement
su -m nobody -c <program to execute as a child>
mais cela semble lourd et ne meurt pas très proprement.
Existe-t-il un moyen d'accomplir cela par programme au lieu d'utiliser su? Je regarde les méthodes os.set * uid, mais la doc dans la bibliothèque Python std lib est assez rare dans ce domaine.
Puisque vous avez mentionné un démon, je peux conclure que vous utilisez un système d'exploitation de type Unix. Cela est important, car la façon de procéder dépend du type de système d'exploitation. Cette réponse s'applique uniquement à nix, y compris Linux et Mac OS X.
subprocess.Popen utilisera le modèle fork/exec pour utiliser votre preexec_fn. Cela équivaut à appeler os.fork (), preexec_fn () (dans le processus enfant) et os.exec () (dans le processus enfant) dans cet ordre. Étant donné que os.setuid, os.setgid et preexec_fn ne sont tous pris en charge que sur Unix, cette solution n'est pas portable sur d'autres types de systèmes d'exploitation.
Le code suivant est un script (Python 2.4+) qui montre comment procéder:
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 __== '__main__':
main()
Vous pouvez invoquer ce script comme ceci:
Commencez en tant que 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
Devenez non root dans un processus enfant ...
(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
Lorsque le processus enfant se termine, nous retournons à la racine dans parent ...
(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
Remarque que le processus parent doit attendre que le processus enfant se termine uniquement à des fins de démonstration . Je l'ai fait pour que le parent et l'enfant puissent partager un terminal. Un démon n'aurait pas de terminal et attendrait rarement qu'un processus enfant se termine.
Il existe une méthode os.setuid()
. Vous pouvez l'utiliser pour changer l'utilisateur actuel de ce script.
Une solution consiste, quelque part où l'enfant commence, à appeler os.setuid()
et os.setgid()
pour changer l'ID utilisateur et de groupe et après cet appel, l'un des os.exec * méthodes pour engendrer un nouvel enfant. L'enfant nouvellement engendré fonctionnera avec l'utilisateur le moins puissant sans avoir la possibilité de redevenir plus puissant.
Une autre consiste à le faire lorsque le démon (le processus maître) démarre et que tous les processus nouvellement créés auront été exécutés sous le même utilisateur.
Pour plus d'informations, consultez la page de manuel de setuid .
En fait, l'exemple avec preexec_fn n'a pas fonctionné pour moi.
Ma solution qui fonctionne bien pour exécuter une commande Shell d'un autre utilisateur et obtenir sa sortie est:
apipe=subprocess.Popen('Sudo -u someuser /execution',Shell=True,stdout=subprocess.PIPE)
Ensuite, si vous avez besoin de lire à partir de la sortie standard du processus:
cond=True
while (cond):
line=apipe.stdout.getline()
if (....):
cond=False
J'espère, c'est utile non seulement dans mon cas.