如何在 shell 脚本中要求 root 权限?

How to demand root privileges in a shell script?

假设您有一个可能需要 root 权限的 shell 脚本。您不想强迫用户 运行 它作为 sudo。但是,如果它确实需要该权限,您希望提示用户输入他们的密码,而不是抱怨并强迫他们使用 sudo.

重新输入命令

您将如何在 Bash 脚本中执行此操作? sudo true 似乎有效,但感觉像是 hack。有没有更好的方法?

命令

sudo -nv

检查用户是否拥有当前 sudo 凭据 (-v),但如果访问权限已过期 (-n),将失败而不是提示。

所以这个:

if sudo -nv 2>/dev/null && sudo -v ; then
    sudo whoami
else
    echo No access
fi

将检查用户的 sudo 凭据是否是最新的,仅当不是时才提示输入密码。

可能存在竞争条件:用户的凭据可能在检查后立即过期。

正如 ghoti 在评论中指出的那样,如果 sudoers 文件设置为仅允许执行某些命令,这可能不起作用。出于这个和其他原因,请务必检查每个 sudo 命令是成功还是失败。

这是我经常做的事情。这是松散的伪代码,但应该给你思路:

# myscript -- possibly execute as sudo

if (passed -e option) then
    read variables from envfile
else
    ...

    need_root = ...

    # set variables ...

    if ($need_root && $uid != 0) then
        env [or whatever] > /tmp/envfile
        exec sudo myscript -e/tmp/envfile ...
    fi
fi

# stuff to execute as root [or not] ...

如果您计划使用 sudo 进行权限升级,您可能需要处理的一个问题是可以将 sudo 设置为允许 root 访问某些命令而不是其他命令。例如,假设您有一台 运行 VirtualBox 服务器,管理应用程序的人员与管理 OS 的人员不同。您的 sudoers 文件可能包含如下内容:

Cmnd_Alias    SAFE = /bin/true, /bin/false, /usr/bin/id, /usr/bin/who*
Cmnd_Alias    SHUTDOWN = /sbin/shutdown, /sbin/halt, /sbin/reboot
Cmnd_Alias    SU = /bin/su, /usr/bin/vi*, /usr/sbin/visudo
Cmnd_Alias    SHELLS = /bin/sh, /bin/bash, /bin/csh, /bin/tcsh
Cmnd_Alias    VBOX = /usr/bin/VBoxManage

%wheel     ALL=(ALL) ALL, !SU, !SHELLS, !SHUTDOWN
%staff     ALL=(ALL) !SU, !SHELLS, NOPASSWD: SAFE
%operator  ALL=(ALL) SAFE, SHUTDOWN
%vboxusers ALL=(ALL) NOPASSWD: VBOX

在这种情况下,vboxusers unix 组的成员将始终获得对 sudo -nv 的成功响应,因为该组存在 NOPASSWD 条目。但是该组的成员 运行 使用 VBoxManage 以外的任何其他命令都会收到密码挑战和安全警告。

所以你需要确定你需要运行的命令是否可以运行没有密码提示。如果您不知道 sudo 在您的脚本 运行ning 所在的系统上是如何配置的,规范测试是 运行 命令。 运行 sudo -nv只会告诉你是否通过认证;它不会告诉您可以访问哪些命令。


就是说,如果您可以安全地依赖 sudo 配置,例如 wheel 组中的成员身份,您就可以访问所有命令,例如:

%wheel          ALL=(ALL) ALL

然后您可以使用 sudo -nv 来测试升级能力。但是您的脚本可能有一些东西是 运行 作为 root 的,而有些东西不是。除了 sudo 之外,您可能还想考虑使用其他工具来提升权限。

一个策略可能是在需要时设置一个变量作为命令的前导,但如果您已经是 root 用户(即 运行将整个脚本置于 sudo 中),则将变量留空。

if ! which sudo >/dev/null 2>/dev/null; then
    PM_SU_CMD="su - root -c"
elif sudo -nv 2>/dev/null; then
    PM_SU_CMD="sudo"
else
    echo "ERROR: I can't get root." >&2
    exit 1
fi

当然,如果我们已经是 root,请取消设置以避免潜在的冲突:

[ `ps -o uid= $$` -eq 0 ] && unset PM_SU_CMD

(注意这是对系统进程table的查询;我们不想依赖shell的环境,因为那可以被欺骗.)

然后,某些系统实用程序可能会使用以下函数更容易获得:

# Superuser versions for commands that need root privileges
find_s         () { $PM_SU_CMD "/usr/bin/find $*"; }
mkdir_s        () { $PM_SU_CMD "/bin/mkdir -p "; }
rm_s           () { $PM_SU_CMD "/bin/rm $*"; }

然后在您的脚本中,您只需使用 _s 版本的东西,这些东西需要 运行 具有 root 权限。

这当然不是解决这个问题的唯一方法。