我有一个 Python 脚本,它将执行许多需要 root 级别权限的操作,例如在 /etc 中移动文件、使用 apt-get 进行安装等等。我目前有:
if os.geteuid() != 0:
exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")
这是进行检查的最佳方法吗?还有其他最佳做法吗?
我有一个 Python 脚本,它将执行许多需要 root 级别权限的操作,例如在 /etc 中移动文件、使用 apt-get 进行安装等等。我目前有:
if os.geteuid() != 0:
exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")
这是进行检查的最佳方法吗?还有其他最佳做法吗?
os.geteuid
获取有效的用户 ID,这正是您想要的,所以我想不出更好的方法来执行这样的检查。不确定的一点是标题中的“root-like”:您的代码检查是否准确 root
,没有“like”,实际上我不知道“root-like but not root”是什么意思——所以,如果您的意思与“完全根”不同,也许您可以澄清一下,谢谢!
在“请求原谅比许可更容易”的原则下:
try:
os.rename('/etc/foo', '/etc/bar')
except IOError as e:
if (e[0] == errno.EPERM):
sys.exit("You need root permissions to do this, laterz!")
如果你担心你的不可移植性,你可能无论如何os.geteuid()
都不应该胡闹。/etc
您可以提示用户进行 sudo 访问:
import os, subprocess
def prompt_sudo():
ret = 0
if os.geteuid() != 0:
msg = "[sudo] password for %u:"
ret = subprocess.check_call("sudo -v -p '%s'" % msg, shell=True)
return ret
if prompt_sudo() != 0:
# the user wasn't authenticated as a sudoer, exit?
sudo -v
开关更新用户的缓存凭证(请参阅 参考资料)man sudo
。
对于 Linux:
def is_root():
return os.geteuid() == 0
我喜欢在环境变量中检查 sudo:
如果不是 os.environ.keys() 中的“SUDO_UID”: print "这个程序需要超级用户权限。" 系统退出(1)
如果你真的希望你的代码在各种 Linux 配置中保持健壮,我建议你考虑一下有人可能使用 SELinux、文件系统 ACL 或 Linux 内核中的“功能”特性的极端情况从 v. 2.2 左右开始。您的进程可能在使用 SELinux 的某个包装器下运行,或者某些 Linux 功能库(例如libcap2 libcap-ng或fscaps或elfcap)通过更奇特的东西(例如 Niels Provos 的精彩且遗憾地被低估的systrace系统)运行。
所有这些都是您的代码可能以非 root 身份运行的方式,但您的进程可能已被委派了必要的访问权限以在没有 EUID==0 的情况下执行其工作。
因此,我建议您考虑通过包装可能因权限或异常处理代码的其他问题而失败的操作,以更 Python 的方式编写代码。如果您打算执行各种操作(例如使用subprocess
模块),您可能会提供给所有此类调用添加前缀sudo
(例如,作为命令行、环境或 .rc 文件选项)。如果它以交互方式运行,您可以提供重新执行任何引发权限相关异常的命令sudo
(可选地,仅当您sudo
在 os.environ['PATH'] 上找到时)。
总的来说,大多数 Linux 和 UNIX 系统的大部分管理仍然由“root”特权用户完成。然而,这是老派,作为程序员,我们应该尝试支持更新的模型。尝试您的操作并让异常处理完成其工作允许您的代码在任何透明地允许您需要的操作的系统下工作,并且意识到并准备好使用sudo
是一个很好的接触(因为它是迄今为止最普遍的用于控制系统权限委派的工具)。
import os
def check_privileges():
if not os.environ.get("SUDO_UID") and os.geteuid() != 0:
raise PermissionError("You need to run this script with sudo or as root.")
SUDO_UID
如果脚本未使用sudo
.
我的应用程序使用以下代码:
import os
user = os.getenv("SUDO_USER")
if user is None:
print "This program need 'sudo'"
exit()
(抱歉评论框太小了)
保罗霍夫曼,你是对的,我只解决了你关于内在函数的问题的一部分,但如果它不能处理,它就不是一种有价值的脚本语言apt-get
。首选库有点冗长,但它可以完成工作:
>>> apt_get = ['/usr/bin/apt-get', 'install', 'python']
>>> p = subprocess.Popen(apt_get, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> p.wait()
100 # Houston, we have a problem.
>>> p.stderr.read()
'E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)'
'E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?\n'
ButPopen
是一个通用工具,可以为方便起见进行包装:
$ cat apt.py
import errno
import subprocess
def get_install(package):
cmd = '/usr/bin/apt-get install'.split()
cmd.append(package)
output_kw = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE}
p = subprocess.Popen(cmd, **output_kw)
status = p.wait()
error = p.stderr.read().lower()
if status and 'permission denied' in error:
raise OSError(errno.EACCES, 'Permission denied running apt-get')
# other conditions here as you require
$ python
>>> import apt
>>> apt.get_install('python')
Traceback ...
OSError: [Errno 13] Permission denied running apt-get
现在我们回到异常处理。我将拒绝评论子流程模块的类似 Java 的过度通用性。
这完全取决于您希望应用程序的便携性。如果您的意思是业务,我们必须假设管理员帐户并不总是等于 0。这意味着检查 euid 0 是不够的。问题是,在某些情况下,一个命令的行为就像您是 root 一样,而下一个命令将因权限被拒绝而失败(想想 SELinux & co.)。因此,最好在适当的时候优雅地失败并检查 EPERM errno。