为什么 CAP_NET_ADMIN 没有足够的 ioctl(TUNSETIFF) 权限?

Tea*_*ser 8 linux tap capabilities ioctl

我正在尝试用 Rust 编写一个 tun/tap 程序。由于我不希望它以 root 身份运行,因此我已将 CAP_NET_ADMIN 添加到二进制文件的功能中:

$sudo setcap cap_net_admin=eip target/release/tunnel
$getcap target/release/tunnel
target/release/tunnel = cap_net_admin+eip
Run Code Online (Sandbox Code Playgroud)

然而,这是行不通的。我读到的所有内容都表明这是创建 tun 所需的唯一功能,但程序在 ioctl 上获得了 EPERM。在 strace 中,我看到这个错误:

openat(AT_FDCWD, "/dev/net/tun", O_RDWR|O_CLOEXEC) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
ioctl(3, TUNSETIFF, 0x7ffcdac7c7c0)     = -1 EPERM (Operation not permitted)
Run Code Online (Sandbox Code Playgroud)

我已经验证二进制文件可以在完全 root 权限下成功运行,但我不希望这需要 sudo 才能运行。为什么 CAP_NET_ADMIN 在这里不够用?

作为参考,我发现Linux version 4.15.0-45这个 ioctl 只有几种方法可以在内核中返回 EPERM(https://elixir.bootlin.com/linux/v4.15/source/drivers/net/tun.c #L2194),其中至少有一个似乎感到满意。我不知道如何探究其他人:

if (!capable(CAP_NET_ADMIN))
    return -EPERM;
...
if (tun_not_capable(tun))
    return -EPERM;
...
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
    return -EPERM;
Run Code Online (Sandbox Code Playgroud)

小智 5

我猜想您的target/release/tunnel二进制文件所在的文件系统是使用该nosuid选项安装的。这也会影响文件功能,而不仅仅是 setuid 位。

此外,您将无法像您那样跟踪 set-capability 或 setuid 二进制文件——execve()ptraced 进程调用时,内核将忽略文件功能:

$ getcap tapy
tapy = cap_net_admin+eip
$ ./tapy
tapy: {tap1}
^C
$ strace -e trace=ioctl ./tapy
ioctl(3, TUNSETIFF, 0x7ffdc5b2fef0)     = -1 EPERM (Operation not permitted)
tapy: ioctl TUNSETIFF: Operation not permitted
+++ exited with 1 +++
Run Code Online (Sandbox Code Playgroud)


Mat*_*art 2

我在编写一个 Rust 程序时遇到了同样的问题,该程序生成了一个tunctl创建和管理 TUN/TAP 接口的过程。

例如:

let tunctl_status = Command::new("tunctl")
            .args(&["-u", "user", "-t", "tap0"])
            .stdout(Stdio::null())
            .status()?;
Run Code Online (Sandbox Code Playgroud)

失败了:

$ ./target/debug/nio
TUNSETIFF: Operation not permitted
tunctl failed to create tap network device.
Run Code Online (Sandbox Code Playgroud)

即使NET_ADMIN设置了文件功能:

$ sudo setcap cap_net_admin=+ep ./target/debug/nio
$ getcap ./target/debug/nio                       
./target/debug/nio cap_net_admin=ep
Run Code Online (Sandbox Code Playgroud)

手册指出:

由于当以非 root 用户身份运行时,可继承的功能通常不会在 execve(2) 中保留,因此希望运行具有提升功能的帮助程序的应用程序应考虑使用环境功能,如下所述。

为了涵盖系统调用的情况execve(),我使用了环境功能。

环境(自 Linux 4.3 起)这是一组在非特权程序的 execve(2) 中保留的功能。环境能力集遵循以下不变量:如果不能同时允许且可继承,则任何能力都不能是环境能力。

示例解决方案:为了方便起见,我使用该caps-rs库。

$ ./target/debug/nio
TUNSETIFF: Operation not permitted
tunctl failed to create tap network device.
Run Code Online (Sandbox Code Playgroud)

最后,设置NET_ADMIN文件功能就足够了:

$ sudo setcap cap_net_admin=+ep ./target/debug/nio
Run Code Online (Sandbox Code Playgroud)