在Python中删除Root权限
我希望有一个Python程序开始在端口80上侦听,但是在没有root权限的情况下执行。 有没有办法删除根目录或没有它的端口80?
如果没有root权限,您将无法在端口80上打开服务器,这是操作系统级别的限制。 所以唯一的解决方法是在打开端口后删除root权限。
以下是在Python中删除 root权限的一个可能的解决scheme:在Python中删除权限 。 这通常是一个很好的解决scheme,但是您还必须将os.setgroups([])
添加到该函数,以确保不保留根用户的组成员身份。
我复制并清理了一些代码,并删除了日志logging和exception处理程序,以便由您来正确处理OSError
(当进程不允许切换其有效的UID或GID时,将抛出该exception):
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)
我build议使用authbind
来启动你的Python程序,所以它们都不能以root身份运行。
当我需要删除权限时,要求用户input他/她的用户名和组并不是一个好主意。 这是一个稍微修改过的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())
-
systemd可以为你做,如果你通过systemd启动你的程序,systemd可以把已经打开的监听套件交给它,也可以在第一次连接时激活你的程序。 而且你甚至不需要守护它。
-
如果要使用独立方法,则需要CAP_NET_BIND_SERVICE(检查function手册页)的function。 这可以通过使用正确的命令行工具逐个程序地完成,或者通过使您的应用程序(1)成为suid root(2)启动(3)立即侦听端口(4)丢弃权限/能力。
请记住,suid root程序带有许多安全考虑因素(干净和安全的环境,umask,权限,rlimits,所有这些都是你的程序必须正确设置的东西)。 如果你可以使用类似systemd的东西,那就更好了。
大部分工作,除非你需要在你做一些你不想成为超级用户的东西之后请求套接字。
前段时间我做了一个叫做tradesocket的项目。 它允许你在进程之间在posix系统上来回传递套接字。 我所做的是在开始时分离一个进程,保持超级用户,其余的进程下降权限,然后从另一个请求套接字。
以下是对Tamás答案的进一步修改,并做了以下修改:
- 使用
python-prctl
模块将Linuxfunction删除到要保留的指定function列表。 - 用户可以select作为parameter passing(它默认查找运行
sudo
的用户)。 - 它设置所有用户的组和
HOME
。 - 它可以select更改目录。
(但是,我对于使用这个function还是比较陌生的,所以我可能错过了一些东西,在旧的内核(<3.8)或禁用了文件系统function的内核上可能不起作用。
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])