在 Python 中删除 Root 权限
- 2024-11-13 08:36:00
- admin 原创
- 19
问题描述:
我想让 Python 程序开始监听端口 80,但之后以无 root 权限执行。有没有办法放弃 root 权限或获取端口 80?
解决方案 1:
如果没有 root 权限,您将无法在端口 80 上打开服务器,这是操作系统级别的限制。因此,唯一的解决方案是在打开端口后放弃 root 权限。
以下是在 Python 中删除 root 权限的一种可能解决方案:在 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)
解决方案 2:
我建议使用authbind
来启动你的 Python 程序,这样就不需要以 root 身份运行了。
https://en.wikipedia.org/wiki/Authbind
解决方案 3:
systemd 可以为您做到这一点,如果您通过 systemd 启动您的程序,systemd 可以将已经打开的监听套接字交给它,并且它也可以在第一次连接时激活您的程序。您甚至不需要对其进行守护进程化。
如果您要采用独立方法,则需要 CAP_NET_BIND_SERVICE 功能(检查功能手册页)。这可以在程序中逐个使用正确的命令行工具完成,或者通过让您的应用程序 (1) 成为 suid root (2) 启动 (3) 监听端口 (4) 立即放弃权限/功能。
请记住,suid root 程序带有许多安全注意事项(干净安全的环境、umask、权限、rlimits,所有这些都是您的程序必须正确设置的内容)。如果您可以使用类似 systemd 的东西,那就更好了。
解决方案 4:
每当我需要放弃权限时,要求用户输入其用户名和组并不是一个好主意。这是 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())
解决方案 5:
以下是对Tamás 的回答的进一步改编,并进行了以下更改:
使用该
python-prctl
模块将 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])
解决方案 6:
除非您在做了一些您不想以超级用户身份执行的事情后需要请求套接字,否则大部分方法都是可行的。
不久前我做了一个叫tradesocket的项目。它允许你在 posix 系统上在进程之间来回传递套接字。我所做的就是在开始时分离一个保持超级用户的进程,其余进程的权限降低,然后从另一个进程请求套接字。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件