web-dev-qa-db-ja.com

長時間実行されているプロセスとは異なるユーザーとして子プロセスを実行するPython=

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

私は現在使用しています

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

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

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

42
Peter Parente

デーモンについて述べたので、Unixライクなオペレーティングシステムで実行していると結論付けることができます。これは、これを行う方法がオペレーティングシステムの種類によって異なるため、重要です。この回答は、onlyのみに適用されますnix(Linuxおよび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 __== '__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

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

79
Walker Hale IV

os.setuid() メソッドがあります。これを使用して、このスクリプトの現在のユーザーを変更できます。

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

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

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

11
Emil Ivanov

実際、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

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

1
ETech