我的目标是方便地运行通过 pyenv 和 virtualenv 加载的 python Web 服务器模块。根据端口号,我可能必须使用 sudo(端口号小于 1025)。这是我到目前为止添加到我的 ~/.zshrc 中的内容:
uploadserver() {
if [ $1 = 'end' ]; then
pyenv deactivate uploadserver
elif [ $1 -lt 1025 ]; then
pyenv activate uploadserver
sudo python -m uploadserver $1
elif [ $1 -gt 1024 ]; then
pyenv activate uploadserver
python -m uploadserver $1
else
print "uploadserver <port> <start/end>"
fi
}
如果不需要 sudo,它就会执行我所希望的操作。但是,在 sudo 情况下,它会失败并显示“sudo:python:找不到命令”。我怎样才能让它发挥作用?当我手动运行命令时它也会失败,因此这似乎不是一般的脚本问题。
parrot@parrot ~$ uploadserver 80
sudo: python: command not found
(uploadserver) parrot@parrot ~$ uploadserver end
parrot@parrot ~$
即使我在虚拟环境中添加 python 的完整路径,sudo 调用也会失败:
(uploadserver) parrot@parrot ~$ sudo /home/parrot/.pyenv/shims/python
pyenv: python: 找不到命令
更新1 我想我找到了合适的解决方案。如果我将脚本中的 sudo 调用替换为下面的调用,它就可以工作。我只是不知道这是否是一个正确的解决方案。
$ sudo -E env PATH=${PATH} python -m uploadserver $1
更新2 在阅读了 Gilles 的建议“所以-停止邪恶”之后,我决定为我的 python 可执行文件设置 CAP_NET_BIND_SERVICE 选项。对于其他用户来说,这可能也不是一个合适的解决方案,但我认为在我的用例中,这样处理是可以的。为了让它工作,我必须运行以下命令:
parrot@parrot ~$ sudo setcap 'cap_net_bind_service=+ep' /home/parrot/.pyenv/versions/3.12.2/bin/python3.12
pyenv 中的 python 只是一个包装器,为此文件设置功能不起作用:
(uploadserver) parrot@parrot ~$ which python
/home/parrot/.pyenv/shims/python
(uploadserver) parrot@parrot ~$ getcap /home/parrot/.pyenv/shims/python
/home/parrot/.pyenv/shims/python cap_net_bind_service=ep
(uploadserver) parrot@parrot ~$ python -m uploadserver 80
...
PermissionError: [Errno 13] Permission denied
答案1
Sudo 删除大多数环境变量(或将其中一些变量重置为“安全”默认值PATH
)。当 sudo 规则授予对特定命令的访问权限时,这是必要的,因为在存在某些环境变量的情况下,该命令可能会以意外的方式工作,从而允许调用者执行超出预期的操作。例如,LD_PRELOAD
允许调用者以提升的权限运行任意代码。PYTHONPATH
如果命令是 Python 程序,也会如此。
在 sudo 规则允许作为目标用户执行任意命令的特定情况下,不需要此安全功能。但 sudo 默认情况下不会放宽该功能,因为这会更难做到,而且会让管理员更容易犯错误。
--preserve-env
如果 sudo 规则允许,您可以使用( ) 告诉它保留几乎所有环境变量-E
,或者您可以设置特定变量(sudo FOO=bar mycommand
功能上等同于sudo env FOO=bar mycommand
)。这样你就可以跑了sudo PATH="$PATH" python …
。
然而,这是一个坏主意,因为它会以 root 身份启动你的程序。您依赖 Python 脚本在打开特权端口后正确删除特权。您还依赖它在放弃权限之前不会执行任何意外操作,例如在解析命令行和任何配置文件时。
使用专门用于 (1) 打开端口和 (2) 在 (3) 执行服务器程序之前删除权限的工具,而不是 sudo。这可以是tcpd
,systemd 等价物,授权绑定,或许多其他可能性之一1 2。