デーモンについて言及されたので、Unix ライクなオペレーティング システムで実行されていると結論付けることができます。これを行う方法はオペレーティング システムの種類によって異なるため、これは重要です。この回答は、Linux を含むUnixと 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()
このスクリプトは、次のように呼び出すことができます。
ルートとして起動...
(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)/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
子プロセスが終了するまで親プロセスを待機させるのは、デモンストレーションのみを目的としていることに注意してください。親子で端末を共有できるようにしました。デーモンには端末がなく、子プロセスが終了するのをめったに待ちません。