在非交互式脚本中,没有终端就无法让 sudo 持续工作

在非交互式脚本中,没有终端就无法让 sudo 持续工作

我编写了一个 shell 脚本,该脚本仅在需要时才需要 sudo 访问权限。它使用 Zenity 作为 GUI 并且无需终端即可运行。该脚本运行良好,但我遇到了一个问题。它使用 Zenity 询问用户密码并将其存储在变量中。我不喜欢密码以纯文本形式存储在变量中的方式,因此想重写它以使其更安全。请注意,密码不存储在脚本中,而是根据需要存储在变量中。

然后我考虑使用 OpenSSL 用随机生成的密钥加密密码,我测试过这种方法有效。这个想法唯一的问题是密码和密钥是一起的,所以任何人只要查看我的脚本源并了解如何解码它即可。提取变量内容并显示它。这有点像在你的门上安装一个密钥代码并混淆电表箱中的解锁代码。只是一种威慑,但信息仍然存在。也许我只是有点过于迂腐了。:-)

我发现我可以将密码直接从 Zenity 传输到 sudo 并使用-vS选项进行验证。太好了。中间仍然有一个纯文本管道,但至少它没有存储在进程中。我这样做很好。所以我修改了我的脚本以设置密码标志。如果设置了标志,则凭据得到确认。

我测试了它,发现它完全可以在终端上运行。因此,我需要测试它在没有终端的情况下正常运行。但它开始失败。

当我想验证凭证时,我的问题就开始了。每当需要另一个 root 操作时,它都会再次验证凭证,以防它们超时。但我发现它们失败了。我的脚本不断要求再次输入密码。这是在使用“-vS”登录并验证密码几秒钟后发生的。这毫无意义。

然后我发现所有其他 sudo 操作都失败了。我需要执行的所有其他文件操作也从 sudo 返回失败。看来我又被密码纯文本困住了。此时,-S如果我每次运行命令时都给它一个密码,使用选项就可以正常工作。

以下是我检查并验证密码凭证的功能:

get_password()
{
    if [ $password -eq 1 ]; then
        sudo -vn
        if [ $? -ne 0 ]; then
            password=0
        fi
    fi

    if [ $password -eq 0 ]; then
        zenity --title="$wt" --width=$ww --password | sudo -vS
        if [ $? -ne 0 ]; then
            zenity \
            --title="$wt" \
            --width=$qw \
            --error \
            --text="Error:\n\nThe password entered is incorrect."

            return 1
        else
            password=1
        fi
    fi

    return 0
}

以下是获得 root 访问权限后调用的一些文件例程:

remove_file()
{
   sudo rm "$1"
}

remove_folder()
{
   sudo rm -R "$1"
}

当我发现问题时,我测试了几个不同版本的 sudo,它们在不同的 Linux 安装上表现不同。我搜索了 sudo 错误,但没有找到任何与我遇到的行为相关的特定问题。如果有一些我可以依赖的一致行为就好了。但我在返回代码中发现了以下差异:

  • 1.8.3p1-1:须藤-vS:0,须藤-vn:1,须藤cmd:1
  • 1.8.9p5-1:sudo -vS:0,sudo -vn:0,sudo cmd:1
  • 1.8.21p2-3:sudo -vS:0,sudo -vn:0,sudo cmd:0
  • 1.9.13p3-3:sudo -vS:0,sudo -vn:0,sudo cmd:1

如您所见,没有一致性!最早的版本仅通过 stdin 验证密码,其他所有版本均失败。其余版本都通过验证。但只有一个版本可以以 root 身份运行命令,而且它不是最新的版本。除此之外,我发现在最新的版本中,它可以与 sudo 一起使用 rm 删除文件,但不能删除文件夹。这太令人困惑了!

那么有没有办法解决这个问题?我应该传递什么选项?我读过手册并查阅过指南。我没有看到任何能准确描述这一点的内容。我试过使用-n选项,但没有成功。我甚至试过发送一个假密码,-S但它不喜欢。我对此有点困惑,否则我就不会问了。我真的不想回到纯文本密码。

对于 sudoers 配置,这将是默认的。我不需要也不想修改它。这也需要在其他人的系统上运行,所以我认为不需要以这种方式进行任何修改。该脚本被打包为 deb。因此一旦安装,应该能够按标准运行。

事实上,根据我所做的研究,我本来会写一个关于sudo在后台脚本中使用而不存储纯文本密码的教程,但由于我无法让它始终如一地发挥作用,所以我暂时不会这么做。:-D

答案1

经过考虑和测试,我发现没有办法让这个功能在各个版本之间透明地工作。除非以某种形式存储凭证,否则无法避免这种情况,除非依赖 sudo 的特定工作版本。

凭证选项可以存储在可见的变量中,隐藏在文件中,可以使用 keyctl 找到或存储在内核钥匙串中。后者看起来最安全,因为敏感数据可以在每个进程级别受到保护。尽管如果它很重要,它仍然是明文形式。因此也可以加密以获得额外的安全性。

然后可以使用 askpass 选项在需要时检索凭据。这将允许最透明的操作。可以使用外部脚本来传输凭据。然后可以首先验证凭据。从那时起,sudo 就可以正常使用,如果再次需要凭据,它可以根据需要使用 askpass,而脚本可以发出兼容的简单 sudo 命令。

相关内容