【问题标题】:What is the best way for checking if the user of a script has root-like privileges?检查脚本用户是否具有类似 root 权限的最佳方法是什么?
【发布时间】:2011-02-17 21:53:03
【问题描述】:

我有一个 Python 脚本,它将执行许多需要 root 级别权限的操作,例如在 /etc 中移动文件、使用 apt-get 进行安装等等。我目前有:

if os.geteuid() != 0:
    exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")

这是进行检查的最佳方式吗?还有其他最佳做法吗?

【问题讨论】:

    标签: python root privileges


    【解决方案1】:

    os.geteuid 得到有效的用户 id,这正是你想要的,所以我想不出更好的方法来执行这样的检查。不确定的一点是标题中的“root-like”:您的代码检查 exactly root,没有“喜欢”它,实际上我不知道什么是“root- like but not root”的意思是——所以,如果你的意思不同于“exactly root”,也许你可以澄清一下,谢谢!

    【讨论】:

    • 我假设 Paul 担心不同的系统为管理员使用不同的 uid。通常 id(root) == 0,但这不是必须的,在某些系统上它确实不相等。
    • @Stan:那么 EAFP 确实会更好
    • 有效 UID == 0 表示“root”的假设在 UNIX 代码中根深蒂固(包括在大多数类似 UNIX 的内核源代码中)。从技术上讲,在 Linux 下该假设不一定正确。 Linux“功能”模型允许系统运行通过进程继承(lcap2 包装器)或可能通过扩展文件系统属性委派的更细粒度的控制。 SELinux 特性也可能对这些假设造成严重破坏。对于 99% 的系统, geteuid() == 0 就足够了;其余的尝试:...除了:是你的朋友。
    【解决方案2】:

    根据“请求原谅比许可更容易”的原则:

    try:
        os.rename('/etc/foo', '/etc/bar')
    except IOError as e:
        if (e[0] == errno.EPERM):
           sys.exit("You need root permissions to do this, laterz!")
    

    如果您担心os.geteuid() 的不可移植性,您可能不应该对/etc 感到困惑。

    【讨论】:

    • 这是最便携的方式,甚至可以在更奇特的情况下工作,例如 SELinux 限制 root 权限。
    • 这实际上可能会更糟,具体取决于具体情况。嗯,但弗洛里安有道理。
    • @Longpoke - 您指的是用户可能在 /etc 中具有重命名权限但 - 说无法从该路径中删除文件的情况?如果这就是您的意思,那么尝试提前确定用户确实具有哪些功能是愚蠢的差事。更好地为“事务”语义构建代码,如果 C 失败,A 和 B 可以回滚。
    • @msw:我最初认为,“你应该检查你是否是 root 并退出”,以避免任何事务性后果,但 SELinux、ACL 和功能肯定会打破这一点,所以我猜猜真正做到正确的唯一方法是您的建议。
    • 有时您需要在开始进行更改之前知道任务的所有部分是否都会成功。我不想执行第 1、2 和 3 步,只是为了发现我无法执行第 4 步,并希望我能撤回已完成的操作。
    【解决方案3】:

    这完全取决于您希望应用的便携性。如果您的意思是业务,我们必须假设管理员帐户并不总是等于 0。这意味着检查 euid 0 是不够的。问题是,在某些情况下,一个命令的行为就像您是 root 一样,而下一个命令会因 permission denied 而失败(想想 SELinux & co.)。因此,最好在适当的时候优雅地失败并检查 EPERM errno。

    【讨论】:

      【解决方案4】:

      如果您真的希望您的代码在各种 Linux 配置中保持健壮,我建议您考虑可能有人使用 SELinux、文件系统 ACL 或已经存在的“功能”特性的极端情况自 v. 2.2 左右以来的 Linux 内核。您的进程可能在使用 SELinux 的某个包装器或某些 Linux 功能库下运行,例如 libcap2 libcap-ngfscapselfcap systrace系统。

      所有这些都是您的代码可能以非 root 身份运行的方式,但您的进程可能已被委派了必要的访问权限以在没有 EUID==0 的情况下执行其工作。

      因此,我建议您考虑更多地以 Python 方式编写代码,通过包装可能因权限或异常处理代码的其他问题而失败的操作。如果您打算执行各种操作(例如,使用 subprocess 模块),您可以提供在所有此类调用前加上 sudo(例如,作为命令行、环境或 .rc 文件选项)。如果它以交互方式运行,您可以使用sudo 重新执行任何引发权限相关异常的命令(可选地,仅当您在 os.environ['PATH'] 上找到sudo 时)。

      总的来说,大多数 Linux 和 UNIX 系统的大部分管理工作仍然由“root”特权用户完成。然而,这是老派,作为程序员,我们应该尝试支持更新的模型。尝试你的操作并让异常处理完成它的工作允许你的代码在任何透明地允许你需要的操作的系统下工作,并且知道并准备好使用sudo是一个很好的接触(到目前为止,用于控制系统权限委派的最广泛工具)。

      【讨论】:

        【解决方案5】:

        回答第二部分问题

        (抱歉评论框太小了)

        Paul Hoffman,你是对的,我只解决了你关于内在函数的问题的一部分,但如果它不能处理 apt-get,它就不是一种有价值的脚本语言。首选库有点冗长,但可以完成工作:

        >>> apt_get = ['/usr/bin/apt-get', 'install', 'python']
        >>> p = subprocess.Popen(apt_get, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        >>> p.wait()
        100                 # Houston, we have a problem.
        >>> p.stderr.read()
        'E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)'
        'E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?\n'
        

        但是Popen是一个通用的工具,为了方便可以包装一下:

        $ cat apt.py
        import errno
        import subprocess
        
        def get_install(package):
            cmd = '/usr/bin/apt-get install'.split()
            cmd.append(package)
            output_kw = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE}
            p = subprocess.Popen(cmd, **output_kw)
            status = p.wait()
            error = p.stderr.read().lower()
            if status and 'permission denied' in error:
                raise OSError(errno.EACCES, 'Permission denied running apt-get')
            # other conditions here as you require
        $ python
        >>> import apt
        >>> apt.get_install('python')
        Traceback ...
        OSError: [Errno 13] Permission denied running apt-get
        

        现在我们回到异常处理。我将拒绝评论子流程模块类似 Java 的过度通用性。

        【讨论】:

        • 将其编辑到您的其他答案中不是更有意义吗?
        【解决方案6】:

        我喜欢检查环境变量中的 sudo:

        如果不是 os.environ.keys() 中的“SUDO_UID”: print "这个程序需要超级用户权限。" 系统退出(1)

        【讨论】:

        • 这个简单整洁。谢谢!
        • 不错。 os.environ.keys() 里面还有我想要的其他信息!
        • 这不适用于root
        • 不需要.keys(),直接用if not 'SUDO_UID' in os.environ: -- 谢谢!
        【解决方案7】:

        您可以提示用户进行 sudo 访问:

        import os, subprocess
        
        def prompt_sudo():
            ret = 0
            if os.geteuid() != 0:
                msg = "[sudo] password for %u:"
                ret = subprocess.check_call("sudo -v -p '%s'" % msg, shell=True)
            return ret
        
        if prompt_sudo() != 0:
            # the user wasn't authenticated as a sudoer, exit?
        

        sudo -v 开关更新用户的缓存凭据(请参阅man sudo)。

        【讨论】:

        • 澄清一下,这可以让您检查用户是否具有字面意义上的 root 权限,但如果您正在运行的程序实例没有 root 权限,它不会给予它发射。您可以为此尝试this
        【解决方案8】:

        我的应用程序使用此代码:

        import os
        user = os.getenv("SUDO_USER")
        if user is None:
            print "This program need 'sudo'"
            exit()
        

        【讨论】:

        • 错误代码。这里抛出的错误是TypeError,因为如果你不是以root身份运行,os.getenv("SUDO_USER")返回None,它的类型是NoneType,你不能连接strNoneType。如果你想使用os.getenv("SUDO_USER")来解决问题,你应该这样做:if os.getenv("SUDO_USER") == None: print "This program need 'sudo'"; exit()
        • 感谢更新,此代码将仅测试 sudo,而不是 root 访问。因为它不检查 root,所以如果您以 root 身份登录(或者您像我一样编写了一个 cronjob),您仍然需要使用 sudo 调用脚本,因为 sudo 不会在 root 上询问密码,我能够修复我的 cronjob只需用 sudo 调用我的 python 脚本。
        • 聚会有点晚了,但这确实很有用!当与os.getenv('HOME') 之类的内容结合使用时,这将告诉您当前用户是root 还是sudo,这通常是您的脚本需要知道的。
        【解决方案9】:

        对于 Linux:

        def is_root():
            return os.geteuid() == 0
        

        【讨论】:

          【解决方案10】:
          import os
          
          def check_privileges():
          
              if not os.environ.get("SUDO_UID") and os.geteuid() != 0:
                  raise PermissionError("You need to run this script with sudo or as root.")
          

          如果脚本未使用sudo 运行,则SUDO_UID 不可用。

          【讨论】:

          • 这回答了这个问题,因为我们正在寻找“类似root”的权限,但是在这种情况下为什么需要if 的第二部分呢?检查SUDO_UID的存在还不够吗?
          猜你喜欢
          • 2012-12-18
          • 1970-01-01
          • 2011-05-03
          • 2011-01-28
          • 2010-12-08
          • 1970-01-01
          • 2018-03-19
          • 2012-12-09
          • 2011-10-28
          相关资源
          最近更新 更多