54

特定のイベントが発生したときにサブプロセスを使用して新しい子プロセスを生成する、長時間実行されるデーモン化された Python プロセスがあります。長期実行プロセスは、スーパー ユーザー権限を持つユーザーによって開始されます。親プロセスのスーパーユーザー権限を保持しながら、別のユーザー(たとえば、「nobody」)として実行するために生成される子プロセスが必要です。

私は現在使用しています

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

しかし、これは重いようで、きれいに死ぬことはありません。

su を使用する代わりにプログラムでこれを達成する方法はありますか? 私は os.set*uid メソッドを見ていますが、Python std ライブラリのドキュメントはその領域ではかなりまばらです。

4

5 に答える 5

92

デーモンについて言及されたので、Unix ライクなオペレーティング システムで実行されていると結論付けることができます。これを行う方法はオペレーティング システムの種類によって異なるため、これは重要です。この回答は、Linux を含むUnixと Mac OS X にのみ適用されます。

  1. 実行中のプロセスの gid と uid を設定する関数を定義します。
  2. この関数を 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

子プロセスが終了するまで親プロセスを待機させるのは、デモンストレーションのみを目的としていることに注意してください。親子で端末を共有できるようにしました。デーモンには端末がなく、子プロセスが終了するのをめったに待ちません。

于 2011-05-17T21:41:18.973 に答える
12

There is an os.setuid() method. You can use it to change the current user for this script.

1 つの解決策は、子が開始する場所でユーザー ID とグループ ID を呼び出して変更し、その後 os.exec* メソッドの 1 つを呼び出しos.setuid()新しい子を生成することです。新しくスポーンされた子は、より強力なユーザーになることができずに、より強力でないユーザーで実行されます。os.setgid()

もう 1 つは、デーモン (マスター プロセス) の起動時に実行し、新しく生成されたすべてのプロセスが同じユーザーの下で実行されるようにすることです。

詳細については、 setuid のマンページを参照してください。

于 2009-11-20T12:49:01.603 に答える
3

実際、 preexec_fn の例はうまくいきませんでした。
別のユーザーからいくつかのシェルコマンドを実行してその出力を取得するために正常に機能している私のソリューションは次のとおりです。

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

次に、プロセス stdout から読み取る必要がある場合:

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

私の場合だけでなく、役に立つことを願っています。

于 2016-06-17T21:16:23.830 に答える
-8

パスワードを必要としないため、sudo でユーザーを有効にします

username ALL=(ALL) NOPASSWD: ALL

次に、ルート関数を次のように呼び出しますsudo

import pexpect
child = pexpect.spawn('sudo apachectl restart')
for i in child: print i #if you want to see the output from the process
于 2014-04-14T07:44:30.777 に答える