以与长时间运行的 Python 进程不同的用户身份运行子进程
我有一个长时间运行的守护进程 Python 进程,当某些事件发生时,它使用子进程生成新的子进程。长时间运行的进程由具有超级用户权限的用户启动。我需要它生成的子进程以不同的用户(例如“nobody”)运行,同时保留父进程的超级用户权限。
我目前正在使用,
su -m nobody -c <program to execute as a child>
但这看起来很重量级,并且死得不是很干净。
有没有办法以编程方式完成此操作而不是使用 su?我正在查看 os.set*uid 方法,但 Python 标准库中的文档在该区域非常稀疏。
I've got a long running, daemonized Python process that uses subprocess to spawn new child processes when certain events occur. The long running process is started by a user with super user privileges. I need the child processes it spawns to run as a different user (e.g., "nobody") while retaining the super user privileges for the parent process.
I'm currently using
su -m nobody -c <program to execute as a child>
but this seems heavyweight and doesn't die very cleanly.
Is there a way to accomplish this programmatically instead of using su? I'm looking at the os.set*uid methods, but the doc in the Python std lib is quite sparse in that area.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
既然你提到了守护进程,我可以断定你正在类 Unix 操作系统上运行。这很重要,因为如何做到这一点取决于操作系统的类型。这个答案仅适用于Unix,包括Linux和Mac OS X。
subprocess.Popen 将使用 fork/exec 模型来使用您的 preexec_fn。这相当于按顺序调用 os.fork()、preexec_fn()(在子进程中)和 os.exec()(在子进程中)。由于 os.setuid、os.setgid 和 preexec_fn 都仅在 Unix 上受支持,因此该解决方案无法移植到其他类型的操作系统。
以下代码是一个脚本(Python 2.4+),演示了如何执行此操作:
您可以像这样调用此脚本:
以 root 身份启动...
在子进程中成为非 root...
当子进程退出时,我们返回到父进程的根目录...
注意让父进程等待子进程退出仅用于演示目的。我这样做是为了让父母和孩子可以共享一个终端。守护进程没有终端,并且很少等待子进程退出。
Since you mentioned a daemon, I can conclude that you are running on a Unix-like operating system. This matters, because how to do this depends on the kind operating system. This answer applies only to Unix, including Linux, and Mac OS X.
subprocess.Popen will use the fork/exec model to use your preexec_fn. That is equivalent to calling os.fork(), preexec_fn() (in the child process), and os.exec() (in the child process) in that order. Since os.setuid, os.setgid, and preexec_fn are all only supported on Unix, this solution is not portable to other kinds of operating systems.
The following code is a script (Python 2.4+) that demonstrates how to do this:
You can invoke this script like this:
Start as root...
Become non-root in a child process...
When the child process exits, we go back to root in parent ...
Note that having the parent process wait around for the child process to exit is for demonstration purposes only. I did this so that the parent and child could share a terminal. A daemon would have no terminal and would seldom wait around for a child process to exit.
新版本的 Python(3.9 及以上)支持
user
和group
选项开箱即用:新版本还提供了
subprocess.run
功能。它是subprocess.Popen
的简单包装。当suprocess.Popen
在后台运行命令时,subprocess.run
运行命令并等待命令完成。因此我们还可以这样做:
The new versions of Python (3.9 onwards) support
user
andgroup
option out of the box:The new versions also provide a
subprocess.run
function. It is a simple wrapper aroundsubprocess.Popen
. Whilesuprocess.Popen
runs the commands in the background,subprocess.run
runs the commands and wait for their completion.Thus we can also do:
有一个
os.setuid()
方法。您可以使用它来更改此脚本的当前用户。一种解决方案是,在子进程启动的地方,调用 os.setuid() 和 os.setgid() 来更改用户和组 ID,然后调用其中之一os.exec* 方法来生成一个新的子进程。新生成的子级将与较弱的用户一起运行,而无法再次成为更强大的用户。
另一种方法是在守护进程(主进程)启动时执行此操作,然后所有新生成的进程将在同一用户下运行。
有关信息,请参阅 setuid 手册页。
There is an
os.setuid()
method. You can use it to change the current user for this script.One solution is, somewhere where the child starts, to call
os.setuid()
andos.setgid()
to change the user and group id and after that call one of the os.exec* methods to spawn a new child. The newly spawned child will run with the less powerful user without the ability to become a more powerful one again.Another is to do it when the daemon (the master process) starts and then all newly spawned processes will have run under the same user.
For information look at the manpage for setuid.
实际上, preexec_fn 的示例对我不起作用。
我的解决方案可以很好地从另一个用户运行一些 shell 命令并获取其输出:
然后,如果您需要从进程 stdout 中读取:
希望,它不仅在我的情况下很有用。
Actually, example with preexec_fn did not work for me.
My solution that is working fine to run some shell command from another user and get its output is:
Then, if you need to read from the process stdout:
Hope, it is useful not only in my case.