CAP_NET_ADMINに十分なioctl(TUNSETIFF)権限がないのはなぜですか?

CAP_NET_ADMINに十分なioctl(TUNSETIFF)権限がないのはなぜですか?

私は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

しかし、これはうまくいきません。私が読んだすべての内容は、これが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)

完全な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) そして、そのうちの少なくとも1人は満足しているようです。他の人を調べる方法がわからない。

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;

答え1

target/release/tunnel私はあなたのバイナリを持つファイルシステムがnosuidこのオプションでマウントされていると思います。これは setuid ビットだけでなくファイル機能にも影響します。

また、可能な限り set-capability または setuid バイナリを追跡することはできません。カーネルは、dプロセスexecve()で呼び出されたときにファイル機能を無視します。ptrace

$ 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 +++

答え2

tunctlTUN / TAPインターフェースを作成および管理するためのプロセスを生成するRustプログラムの作成中に、同じ問題に直面しました。

たとえば、

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

失敗した:

$ ./target/debug/nio
TUNSETIFF: Operation not permitted
tunctl failed to create tap network device.

NET_ADMINファイル機能が設定されていても:

$ sudo setcap cap_net_admin=+ep ./target/debug/nio
$ getcap ./target/debug/nio                       
./target/debug/nio cap_net_admin=ep

マニュアルには次のように記載されています。

非 root ユーザーとして実行する場合、通常継承可能な機能は execve(2) で維持されないため、拡張機能を使用してヘルパーを実行するアプリケーションは、以下に説明するように環境機能の使用を考慮する必要があります。

execve()システムコールのケースに対処するために環境関数を使用しました。

環境(Linux 4.3以降)これは、権限のないプログラムのためにexecve(2)に予約されている一連の機能です。環境機能セットは、次の不変性に従います。つまり、許可されず継承できない機能は環境機能にはなりません。

ソリューション例:便宜上、caps-rs図書館。

// Check if `NET_ADMIN` is in permitted set.
let perm_net_admin = caps::has_cap(None, CapSet::Permitted, Capability::CAP_NET_ADMIN);
match perm_net_admin {
    Ok(is_in_perm) => {
        if !is_in_perm {
            eprintln!("Error: The capability 'NET_ADMIN' is not in the permitted set!");
            std::process::exit(1)
        }
    }
    Err(e) => {
        eprintln!("Error: {:?}", e);
        std::process::exit(1)
    }
}


// Note: The ambient capability set obeys the invariant that no capability can ever be ambient if it is not both permitted and inheritable.
caps::raise(
    None,
    caps::CapSet::Inheritable,
    caps::Capability::CAP_NET_ADMIN,
)
.unwrap_or_else(fail_due_to_caps_err);

caps::raise(None, caps::CapSet::Ambient, caps::Capability::CAP_NET_ADMIN)
    .unwrap_or_else(fail_due_to_caps_err);

最後に、NET_ADMINファイル機能を設定するだけで十分です。

$ sudo setcap cap_net_admin=+ep ./target/debug/nio

答え3

CAP_NET_ADMIN実際には、ネットワークI / Oを盗聴して読み取るためにネットワーク設定(インターフェイスなど)を変更できますが、そのiptables機能も追加する必要があります。CAP_NET_RAWCAP_NET_ADMIN

彼らは一緒にあなたが望む能力を提供します。

関連情報