如何从POSIX shell脚本中放弃root权限?

5
存在哪些便携式选项可以放弃root权限,并从shell脚本以不同的用户身份执行给定命令?
经过一番研究,以下是几个不可行的选项:
- `su $USER -c "$COMMAND"` 使用PAM栈并创建新的cgroup(在systemd下运行时)。在用户命名空间中失败,因为旧版本的Linux上审核调用返回-EPERM。 - `sudo -u $USER $COMMAND` 在许多系统上未默认安装。 - `start-stop-daemon --pidfile /dev/null --start --chuid $USER --startas /bin/sh -- -c "$COMMAND"` 非常难以使用且仅适用于Debian系统。 - `chpst -u $USER $COMMAND` 在许多系统上缺少。 - `runuser -u $USER -- $COMMAND` 适用于`su`不适用的情况,但需要较新的util-linux。

1
关于第1部分的内容,这是systemd的又一次失误吗?我喜欢听听这些事情。 - Kaz
1
@Kaz 相关的讨论从 https://lists.debian.org/debian-devel/2014/05/msg00371.html 开始。 - Helmut Grohne
2个回答

3
如果你需要POSIX,那么su是你唯一的选择(除非你想编写C程序)。su有几个优点(或者不是,这取决于你的要求):
  • 它是一个系统工具,不会忘记在Linux 3.42中引入的新咖啡UID(用于饮料饮用目的),也不会在降低用户权限之前放弃用户组权限或忘记功能。
  • 它将特权设置为已知状态:用户ID,该用户从用户和组数据库记录的组,没有额外的功能。
  • 它记录日志条目。
  • 再次说明,它是完全标准的,保证在最破损的系统上都可用。

现在在实践中,一些系统不是POSIX——就像这个旧版本的Linux,在用户命名空间中失败了。这就是问题所在。

如果你想要在实践中得到某些相当可移植性的东西(在非嵌入式平台上),并且给你更大的控制权,请使用Perl(或Python,安装较少)。最好使用一个可靠的模块:Privilege::Drop

perl -e 'use Privileges::Drop; drop_uid_gid(123, 456); exec("/path/to/command", "--option", "an argument")'

Privilege::Drop 会确保正确执行(放弃附加组,检查错误)。但是它可能不完全;例如,它不知道能力。

如果您必须手动执行此操作,请注意以下几点:

  • 在放弃用户权限之前,请先放弃组权限。
  • 要放弃附加组,请设置$) = "456 456",其中456是目标GID($) = 456只会设置EGID而不影响附加组)。
  • 检查(E)[UG]ID并在失败时中止。

如果su是正确的方法,那么让su再次工作需要什么?升级内核很容易,但systemd部分仍然有问题。 - Helmut Grohne
1
@HelmutGrohne 我不知道。我建议在 [unix.se] 上询问,那里有一些systemd的爱好者(我不是)。 - Gilles 'SO- stop being evil'

3
对于那些在上下文中阅读此文,相关的“POSIX shell脚本”的含义是“在任意Linux系统上运行的POSIX sh脚本”,而不是“在仅保证由POSIX保证的工具的系统上运行的sh脚本”,有更多的选项可用。
借鉴自UNIX & Linux问题如何在shell脚本中放弃root权限的一个非常好的答案:
  • Modern util-linux has setpriv, which can be used in the manner of:

    setpriv --reuid=user --regid=group --init-groups --inh-caps=-all yourcommand
    
这篇由@JdeBP写的绝佳文章《不要滥用su来降低权限》也值得一读。

如果你只关心“简单”,而不关心“安全”,我建议系统管理(或在跨越权限边界的代码维护上下文中进行开发)不是一个好的职业选择。你之前读过哪篇文章?我认为JdeBP那篇相当有说服力。 - Charles Duffy
1
su -c '...' 在启动 shell 时增加了复杂性(因此,存在错误的空间),而在直接使用 execv 的参数列表链时不存在这种情况;它使得安全地对被调用的代码进行参数化比应该更难,因为需要知道如何为目标 shell 转义命令。但更重要的是,上升然后下降会放弃之前可能已经完成的作为受限制的 root 形式运行进程的任何先前的封装工作,而不是未封装的 root。请记住,今天有能力、安全上下文等,不仅仅是传统模型。 - Charles Duffy
1
...并且 shell 启动本身引入了新的潜在攻击面;如果作为非特权用户运行的进程只是以不涉及交互式或登录 shell 的方式调用,那么涉及到点文件等的攻击就不会出现。完全不涉及 shell,ENV / BASH_ENV 等也不会受到影响。 - Charles Duffy
我的意思是“易于正确实现”。例如,不指定--reset-env就降低权限,然后执行一些自动化的git拉取操作会导致git尝试在/root中查找配置。使用su可以自行正确运行。为了确保我理解得正确:如果我只想从完全有能力的root转到某个非特权用户,那么su本身是否存在任何错误/不安全之处?还是由于复杂性引入了更大的攻击面? - lucidbrot
1
如果你从完全可用的根目录开始,那么这样做就会变得“不那么错误”,但它仍然会强制你启动一个 shell 并涉及到 PAM 中涉及的所有可配置移动部件。启动 shell 不一定是“不安全”的,但它肯定会增加攻击面,因此最好在容易避免的情况下避免使用;如果正在参数化被调用的命令,则应该加倍注意。同样,正确配置的 PAM 规则可以增强安全性,但仍然会增加在后台发生的事情数量。使用一个详细说明其操作的工具可以让移动部件保持在视野中。 :) - Charles Duffy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接