由于您提到了一个守护进程,我可以推断出您正在运行类Unix操作系统。这很重要,因为如何做这取决于操作系统的类型。此答案仅适用于Unix,包括Linux和Mac OS X。
- 定义一个函数,该函数将设置正在运行的进程的gid和uid。
- 将此函数作为preexec_fn参数传递给subprocess.Popen
subprocess.Popen将使用fork/exec模型来使用您的preexec_fn。这相当于按顺序调用os.fork()、preexec_fn()(在子进程中)和os.exec()(在子进程中)。由于os.setuid、os.setgid和preexec_fn都仅受支持于Unix,因此此解决方案不可移植到其他类型的操作系统。
以下代码是一个脚本(Python 2.4+),演示了如何执行此操作:
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()
你可以这样调用这个脚本:
以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
如何在子进程中降低权限,避免成为root用户?
(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
当子进程退出时,父进程会回到根目录。
(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
注意,让父进程等待子进程退出仅供演示目的。我这样做是为了让父进程和子进程可以共享一个终端。守护程序将没有终端,也很少等待子进程退出。
SubprocessError Exception occurred in preexec_fn
,请确保在def result()
函数中,os.setgid(user_gid)
在os.setuid(user_uid)
之前 。在运行Python 3.7.7
的Fedora
中,顺序无关紧要(即两种方式都可以工作);但是在运行Python 3.7.7
的Ubuntu Bionic
上,这一点很重要! 真奇怪。所以请安全起见,只使用上述顺序。希望这能帮助其他搜索者。 - NYCeyes