我正在使用 unshare 来创建每个进程的安装,它工作得很好
unshare -m --map-root-user
但是,在创建了我的绑定安装之后
mount --bind src dst
我想将 UID 更改为我的原始用户,以便whoami
(和其他)像我的用户名一样回显echo $USER
。
我已经尝试过的答案 使用 unshare 模拟 chroot
然而,su – user1
之后做chroot /
,我得到
su: Authentication failure
(Ignored)
setgid: Invalid argument
我已经在 Ubuntu 18.04 Beta、Debian Stretch、openSUSE-Leap-42.3 上对此进行了测试。全部都是一样。我想自从这个答案起作用以来,内核中发生了一些变化。
什么是有效且正确的方法来做到这一点(当然没有被真实的根)?
答案1
这unshare(1)
命令无法做到这一点:
-r, --map-root-user
[...] 作为一个纯粹的便利功能,它不支持更复杂的用例,例如映射多个 UID 和 GID 范围。
补充组(如果有的话)(video
, ...)无论如何都会丢失(或映射到nogroup
)。
通过再次更改为第二个新用户命名空间,可以恢复映射。这需要一个自定义程序,因为unshare(1)
不会这样做。这是一个非常简约的 C 程序作为概念证明(仅限一个用户:uid/gid 1000/1000,零故障检查)。我们称它为revertuid.c
:
#define _GNU_SOURCE
#include <sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd;
unshare(CLONE_NEWUSER);
fd=open("/proc/self/setgroups",O_WRONLY);
write(fd,"deny",4);
close(fd);
fd=open("/proc/self/uid_map",O_WRONLY);
write(fd,"1000 0 1",8);
close(fd);
fd=open("/proc/self/gid_map",O_WRONLY);
write(fd,"1000 0 1",8);
close(fd);
execvp(argv[1],argv+1);
}
它只是对 所做的映射进行反向映射unshare -r -m
,这是不可避免的,以便能够成为 root 并使用mount
,如下所示:
$ strace unshare -r -m /bin/sleep 1 2>&1 |sed -n '/^unshare/,/^execve/p'
unshare(CLONE_NEWNS|CLONE_NEWUSER) = 0
open("/proc/self/setgroups", O_WRONLY) = 3
write(3, "deny", 4) = 4
close(3) = 0
open("/proc/self/uid_map", O_WRONLY) = 3
write(3, "0 1000 1", 8) = 8
close(3) = 0
open("/proc/self/gid_map", O_WRONLY) = 3
write(3, "0 1000 1", 8) = 8
close(3) = 0
execve("/bin/sleep", ["/bin/sleep", "1"], [/* 18 vars */]) = 0
所以这给出了:
user@stretch-amd64:~$ gcc -o revertuid revertuid.c
user@stretch-amd64:~$ mkdir -p /tmp/src /tmp/dst
user@stretch-amd64:~$ touch /tmp/src/file
user@stretch-amd64:~$ ls /tmp/dst
user@stretch-amd64:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
user@stretch-amd64:~$ unshare -r -m
root@stretch-amd64:~# mount --bind /tmp/src /tmp/dst
root@stretch-amd64:~# ls /tmp/dst
file
root@stretch-amd64:~# exec ./revertuid bash
user@stretch-amd64:~$ ls /tmp/dst
file
user@stretch-amd64:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
或者更短:
user@stretch-amd64:~$ unshare -r -m sh -c 'mount --bind /tmp/src /tmp/dst; exec ./revertuid bash'
user@stretch-amd64:~$ ls /tmp/dst
file
在内核 3.19 之后,行为可能会发生变化,如所示user_namespaces(7)
:
这/proc/[pid]/setgroups该文件是在 Linux 3.19 中添加的,但被向后移植到许多早期的稳定内核系列中,因为它解决了安全问题。该问题涉及具有“rwx---rwx”等权限的文件。
答案2
注意:由于 bwrap 目前在比 util-linux > 2.39.1 更多的地方可用,这个答案使用 bwrap对于 Ubuntu 23.10 之前的系统可能值得考虑。 (另一方面,此解决方案可以正确传播 ^C 而 bwrap 则不能)
然而,与 bwrap 不同的是,此解决方案基于 unshare,可以正确传播 ^C 以进行交互式 CLI 调用。
你不能改变UID,但使用现代 util-linux,您可以以非 root 身份开始,然后用于nsenter
获取 root 权限并进行挂载。
有必要使用 unshare 启动命名空间,等待它准备好,以 fake-root 身份执行一些挂载,当挂载准备就绪时,让命名空间运行其任务。
这需要两次同步,在执行挂载之前等待命名空间准备好,并在运行进程之前等待挂载准备好。
用于:
in-unshare [bind-mount] ... [--] command [arg] ...
例如
in-unshare /build-dir=$PWD/build -- make build
# **Note:** this avoids an implicit exec with: `"$@" ; exit $?` which is necessary for proper signal propagation on ^C
# Who would have thought an extra waiting process layer would be so useful?
in-unshare() (
local session session_pid mounts
while test $# != 0
do case "$1" in
*=*) mounts+=("$1") ; shift ;;
--) shift ; break ;;
*) break;
esac
done
coproc mounter {
# session_pid will be the same as PPID but we need to wait until namespaces are setup
read -r session_pid || return $?
exec 0<&-
# mount as "root" via nsenter
for mount in "${mounts[@]}"
do # quit on error without writing to stdout
nsenter -U -m --target $session_pid mount "${mount#*=}" "${mount%=*}" -o rbind || return $?
done
# signal that mounts are setup
echo $?
}
exec {out}>>/dev/fd/${mounter[1]} {in}</dev/fd/${mounter[0]}
# note avoiding implict exec with "$@" ; exit $? is neccessary for proper signal shutdown on ^C
# who would have thought an extra waiting process layer would be so useful
exec unshare --mount --user --map-user=$(id -u) --map-group=$(id -g) --map-users=auto --map-groups=auto --keep-caps --setgroups allow /bin/bash --noprofile --norc -c "echo \$\$ >&${out} && exec ${out}>&- || exit \$? ; read -u ${in} && exec ${in}<&- && \"\$@\" ; exit $?" unshare "$@"
exit $?
)