代码之家  ›  专栏  ›  技术社区  ›  Jeremy Meo

在python中删除根权限

  •  44
  • Jeremy Meo  · 技术社区  · 16 年前

    我想让一个python程序在端口80上开始监听,但是之后在没有根权限的情况下执行。有没有一种方法可以删除根目录或者在没有根目录的情况下获取端口80?

    6 回复  |  直到 8 年前
        1
  •  55
  •   Tamás    14 年前

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

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

    我复制并清理了一点代码,删除了日志记录和异常处理程序,让您自行处理。 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)
    
        2
  •  12
  •   Allen    14 年前

    我建议使用 authbind 要启动您的python程序,所以没有一个程序必须以根用户身份运行。

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

        3
  •  7
  •   Sandeep Datta    12 年前

    当我需要删除权限时,请用户输入其用户名和组不是一个好主意。这里有一个稍微修改过的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())
    
        4
  •  4
  •   Rudd-O    14 年前
    1. SystemD可以帮你做,如果你通过SystemD启动你的程序,SystemD可以把已经打开的监听插座交给它,它也可以在第一次连接时激活你的程序。你甚至不需要监控它。

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

    记住,suid根程序有很多安全考虑因素(干净和安全的环境、umask、特权、限制,所有这些都是您的程序必须正确设置的东西)。如果你能使用像systemd这样的东西,那就更好了。

        5
  •  2
  •   Brantley Harris    14 年前

    除非在做了一些你不想成为超级用户的事情之后,你需要请求套接字,否则大多数情况下都可以工作。

    我做了一个叫做 tradesocket 不久前。它允许您在进程之间来回传递POSIX系统上的套接字。我所做的是在一开始就关闭一个保持超级用户状态的进程,然后进程的其余部分在权限中下降,然后从另一个请求套接字。

        6
  •  2
  •   Craig McQueen Dr. Watson    8 年前

    以下是对 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])