在 clone(2)、unshare(2) 和 setns(2) 期间将更改哪些 process/thread 功能集?

Which process/thread capabilities sets will be changed during clone(2), unshare(2), and setns(2)?

user_namespaces(7) 的手册页说:

The child process created by clone(2) with the CLONE_NEWUSER flag starts out with a complete set of capabilities in the new user namespace. Likewise, a process that creates a new user namespace using unshare(2) or joins an existing user namespace using setns(2) gains a full set of capabilities in that namespace.

不幸的是,手册页没有说明哪些能力集(或集)会受到影响:一个或多个 effective caps 集,permitted caps set,inheritable caps set,等等。所以我的问题是:哪些功能集将受到 clone(2)、unshare( 2), 和 setns(2)?

注意:user_namespaces(7)的示例部分似乎表明有效和允许的能力集将被完全启用,而继承的能力全部被丢弃。但是,没有明确的迹象表明这是实际实施的行为。此外,没有迹象表明环境上限是否受到影响;并且我假设边界上限不受影响,尤其是因为边界上限只能下降上限。

为了了解 setns(2) 和 unshare(2) 对功能的影响,我创建了以下 Python 3 个小脚本。确保在尝试 运行 之前安装包 nsenterunshare (pip3 install nsenter, ...)。

setns(2)

# usernscaps.py: dump all capabilities sets of this process
# when entering a specific (grand)child user namespace.
from nsenter import Namespace
import sys

def dumpcaps(s):
    print(s)
    with open('/proc/self/status', 'r') as st:
        for line in st:
            if line.startswith('Cap'):
                print(line.rstrip())

if len(sys.argv) != 2:
    print('usage: usernscaps.py <PID>')
    exit(1)

dumpcaps('initial:')
try:
    with Namespace('/proc/%d/ns/user' % int(sys.argv[1]), 'user'):
        entered = True
        dumpcaps('after setns:')
except PermissionError:
    # Switching back to our original user namespace isn't allowed, so ignore the exception.
    try:
        entered
    except NameError:
        print('no permission to enter user namespace')        

作为一个普通的非特权用户,让我们创建一个新的用户名space,这个用户名将归我们所有,并用休眠进程保持打开状态(注意: 我们把它放到后台):

unshare -U bash -c "readlink /proc/self/ns/user && sleep infinity" &

接下来,运行 上面的 Python 脚本 usernscaps.py,并告诉它使用 setns(2) 输入我们新创建的用户 space,然后最后转储功能集:

python3 usernscaps.py $(lsns -t user | grep "infinity" | awk '{ print  }')

即使对于我们的非特权用户和进程,在 setns(2):

之后
initial:
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
after setns:
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

这似乎表明 setns(2) 实际上不仅提供了 有效上限 的全套功能,还提供了 允许的上限(这是有道理的,因为有效上限必须随时受允许上限的限制)。不过,它似乎并没有达到继承的上限。

克隆(2)

与之前的脚本类似,但这次取消共享(2)ing。

# usernsunsharecaps.py: dump all capabilities sets of this process
# upon unsharing the user namespace.
import unshare
import sys

def dumpcaps(s):
    print(s)
    with open('/proc/self/status', 'r') as st:
        for line in st:
            if line.startswith('Cap'):
                print(line.rstrip())

dumpcaps('initial:')
unshare.unshare(unshare.CLONE_NEWUSER)
dumpcaps('after unshare:')

简单地运行它python3 usernsunsharecaps.py:

initial:
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
after unshare:
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

因此,这也会在取消共享后在新用户名space 中提供完整的 permittedeffective 功能。