在Python中删除根权限

时间:2010-04-23 15:40:56

标签: python linux unix permissions root

我想让Python程序开始侦听端口80,但之后执行没有root权限。有没有办法放弃root或没有它的80端口?

6 个答案:

答案 0 :(得分:55)

如果没有root权限,您将无法在端口80上打开服务器,这是对操作系统级别的限制。因此,唯一的解决方案是在打开端口后删除root权限。

以下是在Python中删除root权限的可能解决方案:Dropping privileges in Python。这是一个很好的解决方案,但您还必须向函数添加os.setgroups([])以确保不保留root用户的组成员身份。

我复制并清理了一些代码,并删除了日志记录和异常处理程序,因此您可以正确处理OSError(当不允许进程切换时会抛出它有效的UID或GID):

import os, pwd, grp

def drop_privileges(uid_name='nobody', gid_name='nogroup'):
    if os.getuid() != 0:
        # We're not root so, like, whatever dude
        return

    # Get the uid/gid from the name
    running_uid = pwd.getpwnam(uid_name).pw_uid
    running_gid = grp.getgrnam(gid_name).gr_gid

    # Remove group privileges
    os.setgroups([])

    # Try setting the new uid/gid
    os.setgid(running_gid)
    os.setuid(running_uid)

    # Ensure a very conservative umask
    old_umask = os.umask(077)

答案 1 :(得分:12)

我建议使用authbind启动Python程序,因此不必以root身份运行。

https://en.wikipedia.org/wiki/Authbind

答案 2 :(得分:7)

当我需要删除权限时,要求用户输入他/她的用户名和组并不是一个好主意。这是Tamás代码的略微修改版本,它将删除权限并切换到启动sudo命令的用户。我假设您使用的是sudo(如果没有,请使用Tamás的代码)。

#!/usr/bin/env python3

import os, pwd, grp

#Throws OSError exception (it will be thrown when the process is not allowed
#to switch its effective UID or GID):
def drop_privileges():
    if os.getuid() != 0:
        # We're not root so, like, whatever dude
        return

    # Get the uid/gid from the name
    user_name = os.getenv("SUDO_USER")
    pwnam = pwd.getpwnam(user_name)

    # Remove group privileges
    os.setgroups([])

    # Try setting the new uid/gid
    os.setgid(pwnam.pw_gid)
    os.setuid(pwnam.pw_uid)

    #Ensure a reasonable umask
    old_umask = os.umask(0o22)


#Test by running...
#./drop_privileges
#sudo ./drop_privileges
if __name__ == '__main__':
    print(os.getresuid())
    drop_privileges()
    print(os.getresuid())

答案 3 :(得分:4)

  1. systemd可以为你做,如果你通过systemd启动你的程序,systemd可以将已经打开的监听套接字交给它,它也可以在第一次连接时激活你的程序。你甚至不需要守护它。

  2. 如果您要使用独立方法,则需要CAP_NET_BIND_SERVICE(检查功能手册页)功能。这可以通过正确的命令行工具逐个程序地完成,或者通过使您的应用程序(1)成为suid root(2)启动(3)立即监听端口(4)删除权限/功能

  3. 请记住,suid root程序有很多安全注意事项(干净安全的环境,umask,特权,rlimits,所有这些都是你的程序必须正确设置的东西)。如果你可以使用像systemd这样的东西,那就更好了。

答案 4 :(得分:2)

除非你在做一些你不想成为超级用户的其他东西之后需要请求套接字,否则大部分工作都有效。

我刚才创建了一个名为tradesocket的项目。它允许您在进程之间的posix系统上来回传递套接字。我所做的是在开始时分离一个保留超级用户的进程,然后进程的其余部分将权限下降,然后从另一个进程请求套接字。

答案 5 :(得分:2)

以下是对Tamás's answer的进一步修改,并进行了以下更改:

  • 使用python-prctl module将Linux功能删除到要保留的指定功能列表。
  • 用户可以选择作为参数传递(默认为查找运行sudo的用户)。
  • 设置所有用户组和HOME
  • 可选择更改目录。

(我对使用此功能相对较新,所以我可能错过了一些东西。它可能不适用于较旧的内核(< 3.8)或禁用了文件系统功能的内核。)

def drop_privileges(user=None, rundir=None, caps=None):
    import os
    import pwd

    if caps:
        import prctl

    if os.getuid() != 0:
        # We're not root
        raise PermissionError('Run with sudo or as root user')

    if user is None:
        user = os.getenv('SUDO_USER')
        if user is None:
            raise ValueError('Username not specified')
    if rundir is None:
        rundir = os.getcwd()

    # Get the uid/gid from the name
    pwnam = pwd.getpwnam(user)

    if caps:
        prctl.securebits.keep_caps=True
        prctl.securebits.no_setuid_fixup=True

    # Set user's group privileges
    os.setgroups(os.getgrouplist(pwnam.pw_name, pwnam.pw_gid))

    # Try setting the new uid/gid
    os.setgid(pwnam.pw_gid)
    os.setuid(pwnam.pw_uid)

    os.environ['HOME'] = pwnam.pw_dir

    os.chdir(os.path.expanduser(rundir))

    if caps:
        prctl.capbset.limit(*caps)
        try:
            prctl.cap_permitted.limit(*caps)
        except PermissionError:
            pass
        prctl.cap_effective.limit(*caps)

    #Ensure a reasonable umask
    old_umask = os.umask(0o22)

可以按如下方式使用:

drop_privileges(user='www', rundir='~', caps=[prctl.CAP_NET_BIND_SERVICE])