我有一个有趣的用例。我在容器中运行 systemd 来模拟虚拟机以进行测试。我可以很好地运行 systemd,但我需要我的默认用户不是 root,这样在容器中执行的任何操作都会获得正确的用户,然后才能通过 sudo 成为 root。
我的ENTRYPOINT
是sudo /usr/lib/systemd/systemd --system --unit=multi-user.target
,它对大多数事情都适用。然而,由于sudo
调用,systemd 没有获得 PID 1,而某些下游 systemd 单元依赖于 PID 1。
根据文档:
工艺模型
当 sudo 运行命令时,它会调用 fork(2),如上所述设置执行环境,并在子进程中调用 execve 系统调用。主 sudo 进程等待命令完成,然后将命令的退出状态传递给安全策略的 close 函数并退出。如果配置了 I/O 日志记录插件或者安全策略明确请求它,则会创建一个新的伪终端(“pty”),并使用第二个 sudo 进程在用户现有 pty 和新 pty 之间中继作业控制信号pty 正在运行该命令。这个额外的过程使得可以暂停和恢复该命令。如果没有它,该命令将处于 POSIX 术语中的“孤立进程组”,并且不会接收任何作业控制信号。作为一种特殊情况,如果策略插件没有定义 close 函数并且不需要 pty,则 sudo 将直接执行命令,而不是先调用 fork(2)。 sudoers 策略插件仅在启用 I/O 日志记录、需要 pty 或启用 pam_session 或 pam_setcred 选项时定义关闭函数。请注意,使用 PAM 的系统上默认启用 pam_session 和 pam_setcred。
有没有办法告诉 sudo 将其自身替换为它运行的子进程,类似于exec
Bash?还有其他更好的方法吗?看来我需要为这一个命令更改“策略插件”,但我不确定如何执行此操作。
答案1
我解决了这个问题一个名为 escalator 的小型 Rust 应用程序。它使用suid
可执行位以 root 身份执行,然后setuid
在setgid
执行传递给它的内容之前真正成为 root,用子进程替换自身。
答案2
如果您可以控制命令docker run
行,那么您可以考虑向其传递一个--user
选项(例如--user=0:0
),而不是sudo
在 ENTRYPOINT 命令中使用。
在这种情况下,您可以使用 ENTRYPOINT 设置为/usr/lib/systemd/systemd --system --unit=multi-user.target
,这会将 systemd 作为 PID 1 运行,同时仍然保留 USER 设置以在您的命令中生效docker exec
。
看覆盖用户在docker run
参考手册中。
相反,如果您无法控制命令docker run
行,则可以尝试传递--user
命令docker exec
,将 USER 保留为容器定义中的 root。这有一些缺点,例如您必须将该选项传递给所有调用,并且您需要找出要传递它的 user:group (或 uid:gid)(root 很简单,始终为 0:0。)