Uruchom procesy potomne jako inny użytkownik niż długo działający proces
Mam długo działający, demonizowany proces Pythona, który używa podprocesu do wywoływania nowych procesów potomnych, gdy wystąpią pewne zdarzenia. Długotrwały proces jest uruchamiany przez użytkownika z uprawnieniami super user. Proces potomny musi być uruchomiony jako inny użytkownik (np. "nikt"), zachowując uprawnienia super użytkownika dla procesu macierzystego.
Obecnie używam
su -m nobody -c <program to execute as a child>
Ale to wydaje się ciężkie i nie umiera zbyt czysto.
Czy jest sposób na osiągnąć to programowo zamiast używać su? Patrzę na system operacyjny.set * UID methods, ale doc w Python std lib jest dość rzadki w tej dziedzinie.
4 answers
Skoro wspomniałeś o demonie, mogę wywnioskować, że działasz na Uniksopodobnym systemie operacyjnym. Ma to znaczenie, ponieważ jak to zrobić zależy od rodzaju systemu operacyjnego. Ta odpowiedź dotyczy tylko do Unix , W tym Linux i Mac OS X.
- Zdefiniuj funkcję, która ustawi gid i uid uruchomionego procesu.
- przekaż tę funkcję jako parametr preexec_fn do podprocesu.Popen
Podproces.Popen użyje forka / exec model do użycia preexec_fn. Jest to równoznaczne z wywołaniem systemu operacyjnego.fork (), preexec_fn () (w procesie potomnym) i os.exec () (w procesie potomnym) w tej kolejności. Od os.setuid, os.setgid i preexec_fn są obsługiwane tylko w systemie Unix, rozwiązanie to nie jest przenośne dla innych systemów operacyjnych.
Poniższy kod jest skryptem (Python 2.4+), który pokazuje, jak to zrobić:
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()
Możesz wywołać ten skrypt w następujący sposób:
Zacznij jako 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
Staje się non-root w procesie potomnym...
(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
Gdy proces potomny zakończy się, wracamy do roota w rodzicu ...
(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
Zauważ , że proces macierzysty czeka na zakończenie procesu potomnego tylko do celów demonstracyjnych . Zrobiłem to, aby rodzic i dziecko mogli dzielić terminal. Demon nie miałby terminala i rzadko czekałby na zakończenie procesu potomnego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-17 21:41:18
Istnieje os.setuid()
metoda. Możesz go użyć do zmiany bieżącego użytkownika dla tego skryptu.
Jednym z rozwiązań jest, gdzieś, gdzie dziecko zaczyna, wywołanie os.setuid()
i os.setgid()
, aby zmienić identyfikator użytkownika i grupy, a następnie wywołanie jednego z systemu operacyjnego.exec * Metody rodzenia nowego dziecka. Nowo narodzone dziecko będzie działać z mniej potężnym Użytkownikiem bez możliwości ponownego stawania się potężniejszym.
Innym jest to, aby to zrobić, gdy daemon (proces główny) rozpocznie się i wtedy wszystkie nowo powstałe procesy będą uruchamiane przez tego samego użytkownika.
Aby uzyskać informacje, zajrzyj na stronę podręcznika setuid .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-21 17:37:07
Właściwie, przykład z preexec_fn nie zadziałał dla mnie.
Moje rozwiązanie, które działa poprawnie, aby uruchomić jakieś polecenie powłoki od innego użytkownika i uzyskać jego wyjście to:
apipe=subprocess.Popen('sudo -u someuser /execution',shell=True,stdout=subprocess.PIPE)
Następnie, jeśli chcesz odczytać z procesu stdout:
cond=True
while (cond):
line=apipe.stdout.getline()
if (....):
cond=False
Nadzieja, przydaje się nie tylko w moim przypadku.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-17 21:16:23
Włącz użytkownika w sudo jako nie wymagającego hasła
username ALL=(ALL) NOPASSWD: ALL
Następnie wywołaj funkcję root za pomocą sudo
, np.:
import pexpect
child = pexpect.spawn('sudo apachectl restart')
for i in child: print i #if you want to see the output from the process
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-14 08:03:55