wok*_*oky 6 linux users namespace uid
我正在学习 Linux 用户命名空间,并且我观察到一种对我来说并不完全清楚的奇怪行为。
我在初始用户命名空间中创建了一系列 UID,我可以通过newuidmap命令将子用户命名空间中的 UID 映射到这些 UID 。这些是我的设置:
$ grep '^woky:' /etc/subuid
woky:200000:10000
$ id -u
1000
Run Code Online (Sandbox Code Playgroud)
然后我尝试创建一个新的用户命名空间并将其 UID 范围映射[0-10000)到[200000-210000)父用户命名空间中:
第一个终端:
$ PS1='% ' unshare -U bash
% echo $$
1337
% id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)
Run Code Online (Sandbox Code Playgroud)第二个终端:
$ ps -p 1337 -o uid
UID
1000
$ newuidmap 1337 0 200000 10000
$ ps -p 1337 -o uid
UID
1000
Run Code Online (Sandbox Code Playgroud)第一个终端:
% id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)
Run Code Online (Sandbox Code Playgroud)因此,即使newuidmap成功完成,新用户命名空间内部和外部的 UID 也没有更改。
然后我找到了下面这篇文章 http://www.itinken.com/blog/2016/Sep/exploring-unprivileged-containers/让我大开眼界。我已经尝试过前面的场景,但使用以下test-unshare.py脚本,我从文章中获取并稍作修改,而不是unshare命令:
#!/usr/bin/python3
import os
from cffi import FFI
CLONE_NEWUSER = 0x10000000
ffi = FFI()
ffi.cdef('int unshare(int flags);')
libc = ffi.dlopen(None)
libc.unshare(CLONE_NEWUSER)
print("user id = %d, process id = %d" % (os.getuid(), os.getpid()))
input("Press Enter to continue...")
# The uid must be set to 0 to avoid loosing capabilities when creating the shell.
os.setuid(0)
os.execlp('/bin/bash', 'bash')
Run Code Online (Sandbox Code Playgroud)
第一个终端:
$ python3 ./test-unshare.py
user id = 65534, process id = 1337
Press Enter to continue...
Run Code Online (Sandbox Code Playgroud)第二个终端:
$ ps -p 1337 -o uid
UID
1000
$ newuidmap 1337 0 200000 10000
$ ps -p 1337 -o uid
UID
1000
Run Code Online (Sandbox Code Playgroud)第一个终端:
<Enter>
bash: /home/woky/.bashrc: Permission denied
bash-4.4# id
uid=0(root) gid=65534(nobody) groups=65534(nobody)
Run Code Online (Sandbox Code Playgroud)第二个终端:
$ ps -p 1337 -o uid
UID
200000
Run Code Online (Sandbox Code Playgroud)现在看起来就像我从一开始就期望的那样。现在我关于为什么第一个例子中的 UID 没有改变的理论如下:
在unshare所谓的execve(2)运行/bin/bash而无需首先调用setuid(2)。现在 shell 失去了它的所有功能(如 中提到的user_namespaces(7))并且不能从 65534 更改它的 UID。在第二种情况下,进程将它的 UID 更改为 0,因为它有这样做的功能,Linux 将它映射到新的 200000 之外用户命名空间(根据/proc/1337/uid_map哪个newuidmap写的)。这意味着新用户命名空间中的第一个进程必须调用 setuid(START_UID)否则它会在execve(2).
这是正确的吗?
文章说我的第一个示例(相当于第一个示例中的 Python 代码)如下:
如果您只是尝试一下,您可能会发现它并没有真正起作用,这是因为在执行 shell 之前已经设置了 uid 映射。
但是我无法从手册页中的信息得出结论,手册页也没有明确说明setuid(2)需要在新用户命名空间的第一个进程中调用。
但是,在这种情况下,新用户命名空间中的进程不必调用setuid(2),但其 UID 已更改:
第一个终端:
$ PS1='% ' unshare -U bash
% echo $$
1337
% id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)
Run Code Online (Sandbox Code Playgroud)第二个终端:
$ ps -p 1337 -o uid
UID
1000
$ echo '500000 1000 1' >/proc/1337/uid_map
$ ps -p 1337 -o uid
UID
1000
Run Code Online (Sandbox Code Playgroud)第一个终端:
% id
uid=500000 gid=65534(nobody) groups=65534(nobody)
Run Code Online (Sandbox Code Playgroud)请深入解释所有情况。
当我试图了解该/etc/subuid文件的用途时,我的旅程就开始了。它被 Docker 和 LXC 使用,但只有很少的文档解释它。对不起,冗长。我花了很长时间才理解它,我仍然没有完全理解它,所以我把我知道的都收集在这里。
奖励:解释/etc/subuid,它与用户命名空间的关系,为什么 Docker 和 LXC 需要它,以及为什么它是 Linux 发行版上的通用接口。手册页很简短,互联网上的文章大多记录了如何在 LXC/Docker 中工作。(实际解释在newuidmap(1))。
看看https://unix.stackexchange.com/a/110245/301641
\n\n看看这个答案,你可能会看到一些 UID 为无人,因为它们没有映射。
\n\n情况1\xef\xbc\x9a\n“(65534)nobody inside, 1000(woky)outside”作为初始值,newuidmap之后仍然没有得到地图(只有[outside200000,outside210000)被映射,但需要outside1000待映射,超出范围)。所以一切都没有改变。
\n\n情况2:\n“(65534)nobody inside, 1000(woky)outside”作为初始值,newuidmap之后仍然没有得到map(只有[outside200000,outside210000)被映射,但需要outside1000被映射,out范围)。但是你在获取map之后就setuid(inside0)(注意,在写入uid_map之前你永远不能setuid),它在map中,所以UID从溢出值变成了正常的映射值(outside200000,inside0)。
\n\n情况3:\n“(65534)nobody inside, 1000(woky)outside”作为初始值,newuidmap得到映射后(outside1000得到映射),所以UID从溢出值变为正常映射值(outside1000,inside500000)。
\n