나는 Rust로 tun/tap 프로그램을 작성하려고 합니다. 루트로 실행되는 것을 원하지 않기 때문에 바이너리 기능에 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)
전체 루트 권한으로 바이너리가 성공적으로 실행될 수 있음을 확인했지만 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;
답변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
tunctl
TUN/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
설명서에는 다음과 같이 명시되어 있습니다.
루트가 아닌 사용자로 실행할 때 일반적으로 상속 가능한 기능은 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_RAW
CAP_NET_ADMIN
그들은 함께 당신이 원하는 능력을 제공할 것입니다.