在Python中有没有办法以特定用户身份执行命令?

3
据我所知,Python中执行系统命令的方式大约有三种:
  1. os.system(command) -> exit_status

  2. os.popen(command [, mode='r' [, bufsize]]) -> pipe

  3. commands.getoutput(command)-> string

现在我需要控制系统命令的执行器,除了像这样的方式:

os.system('su xxx;' + command)

是否还有更加优雅的方法达到同样的效果?

os.popen已被弃用,推荐使用subprocess引用 - Lenna
os.fork(); os.exec()怎么样? - tMC
3个回答

2

使用subprocess模块以不同的用户身份运行命令(实现@Julian的建议)。 这类似于@tMC的代码,但在更高的层面上:

import os
import pwd
from subprocess import check_output as qx

def change_user(uid, gid=None):
    if gid is None:
        gid = uid
    def preexec_fn():
        os.setgid(gid)
        os.setgroups([gid])
        os.setuid(uid)
    return preexec_fn

print(qx(['id']))
print(qx(['id'], preexec_fn=change_user(pwd.getpwnam('nobody').pw_uid), env={}))
print(qx(['id']))

在旧版本的Python中,您可能需要在子进程调用中添加close_fds=True以避免fd泄漏。
您可以使用cwd参数来指定运行命令的目录,例如用户的主目录。
填充env字典以为子进程设置环境变量。

2

你提到的所有方法(顺便说一下,这些方法都已被subprocess模块替代)都是生成进程的方式。你似乎在寻找setuid。你可以调用一个函数来执行此操作(例如os.setuid),或者根据脚本的功能而定,直接以提升的用户身份运行整个脚本。


0

虽然对于大多数实现来说可能过于“底层”,但对于一些人来说,了解这在更高级模块中是如何发生的可能有教育意义。

import os
import pwd

pout, pin = os.pipe()                                # Creates an anonymous pipe used to communicate with the child process

if not os.fork():                                    # The fork() syscall duplicated the process retuning 0 to the new child process.
    os.closerange(0, 2)                              # Closing stdin, stdout and stderr- they were inherited from the parent process
    os.dup2(pin, 1)                                  # Copy the input side of the IPC pipe to stdout (always fd 1)
    os.setuid(pwd.getpwnam('nobody').pw_uid)         # Change our user id to that of the user `nobody`.  Also see `setgid()`
    os.execl('/bin/whoami', 'whoami')                # Now that we've connected out stdout to the pipe connected to the parent process
                                                     #     and changed our user id to that of the user we want to run the command with
                                                     #     we can `exec()` the new process; replacing our currently running process, 
                                                     #     inheriting the env.
    os._exit(0)                                      # Since we called `exec` above, this is never eval'd, but is good form

os.wait()                                            # Back at the parent process- `wait()` will, well, wait until the child process exits.
os.close(pin)                                        # Close the input side of the pipe, the parent shouldn't write to. (bi-dirctional IPC
                                                     #     would require 2 pipes.  One in each direction
child_stdout_pipe = os.fdopen(pout, 'r')             # Open the output side of the IPC pipe
child_process_output = child_stdout_pipe.read()      # ...and read from the pipe.  This should include anything that came from the stdout of
                                                     #     the child process.  Since we closed the input side of the pipe, the `read()` 
                                                     #     will read an EOF after all the data in the pipe is returned.

print child_process_output                           # Win! (`write()`ing out the parent stdout want we found in the pipe from the child proc

根组可能仍然没有 setgidsetgroups 调用。 - jfs
这是可以的- 我在注释中提到了这一点。 - tMC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接