如何使用python在Linux中以不同用户身份运行部分代码

时间:2018-07-24 14:19:56

标签: python

我正在寻找一种方法来运行python代码/函数的一部分,以便在Linux中以其他用户身份运行,而不是创建另一个脚本。

例如:

def function1(args):
    # stuffs 

def function2():
    # stuffs

我想从function1调用函数function2并传递几个参数,而function1应该接受这些参数并以其他用户身份执行填充,并返回结果。由于在整个执行过程中我必须在两者之间调用某些东西,因此我不想为一小段代码创建多个脚本。基本上是试图在函数function1中连接数据库(只能以该特定用户的身份进行数据库连接),然后运行查询并获取结果。

1 个答案:

答案 0 :(得分:5)

这比您预期的要难一些。首先,Python提供了os.setuid()os.setguid()来更改正在运行的脚本的当前用户/组,您可以创建一个上下文管理器来为您出价,并自动恢复为当前执行的用户:

import os

class UnixUser(object):

    def __init__(self, uid, gid=None):
        self.uid = uid
        self.gid = gid

    def __enter__(self):
        self.cache = os.getuid(), os.getgid()  # cache the current UID and GID
        if self.gid is not None:  # GID change requested as well
            os.setgid(self.gid)
        os.setuid(self.uid)  # set the UID for the code within the `with` block

    def __exit__(self, exc_type, exc_val, exc_tb):
        # optionally, deal with the exception
        os.setuid(self.cache[0])  # revert back to the original UID
        os.setgid(self.cache[1])  # revert back to the original GID

并对其进行测试:

def test():
    print("Current UID: {}".format(os.getuid()))  # prints the UID of the executing user

test()  # executes as the current user

with UnixUser(105):
    test()  # executes as the user with UID: 105

您甚至可以创建一个简洁的装饰器来选择某些功能应始终以其他用户身份执行:

def as_unix_user(uid, gid=None):  # optional group
    def wrapper(func):
        def wrapped(*args, **kwargs):
            with UnixUser(uid, gid):
                return func(*args, **kwargs)  # execute the function
        return wrapped
    return wrapper

def test1():
    print("Current UID: {}".format(os.getuid()))  # prints the UID of the executing user

@as_unix_user(105)
def test2():
    print("Current UID: {}".format(os.getuid()))  # prints the UID of the executing user

test1()  # executes as the current user
test2()  # executes as the user with UID: 105

踢手?除了不是线程安全的,它仅在当前用户和要执行其功能的用户都具有CAP_SETUID和(可选)CAP_SETGID能力的情况下起作用。

您可以让只有一个具有这些功能的用户运行主脚本,然后在需要时进行分叉,而仅在分支的过程中更改UID / GID,从而摆脱困境:

import os

def as_unix_user(uid, gid=None):  # optional group
    def wrapper(func):
        def wrapped(*args, **kwargs):
            pid = os.fork()
            if pid == 0:  # we're in the forked process
                if gid is not None:  # GID change requested as well
                    os.setgid(gid)
                os.setuid(uid)  # set the UID for the code within the `with` block
                func(*args, **kwargs)  # execute the function
                os._exit(0)  # exit the child process
        return wrapped
    return wrapper

def test1():
    print("Current UID: {}".format(os.getuid()))  # prints the UID of the executing user

@as_unix_user(105)
def test2():
    print("Current UID: {}".format(os.getuid()))  # prints the UID of the executing user

test1()  # executes as the current user
test2()  # executes as the user with UID: 105

这里的踢腿者?您不会从派生函数中获取返回数据。如果需要,则必须将其通过管道返回到父进程,然后在父进程中等待它完成。您还需要选择一种格式来在进程之间传递数据(如果足够简单,我建议您使用JSON或使用本机pickle)...

到那时,您已经在完成subprocess模块的工作了一半,因此您最好将功能作为子进程启动并完成它。如果您必须经过这样的设计才能达到理想的效果,那么您的原始设计很可能会出错。在您的情况下-为什么不只向当前用户提供访问数据库的权限?该用户将需要具有切换到其他用户的能力,以便其他用户可以从中获得任何安全性,而这只会使您的生活变得复杂。