在C/C++程序中配置调用需要CAP_NET_ADMIN的tc qdisc shell命令的权限

问题描述 投票:0回答:1

我有一个 C++ 程序,打算通过调用 (iproute2) tc shell 命令来设置排队规则。

示例代码:

#include <iostream>
#include <cstdlib>

int main() {
    const char* cmd = "tc qdisc add dev veno1 root fq_codel";

    int result = std::system(cmd);
    if (result == 0) {
        std::cout << "qdisc configured successfully" << std::endl;
    } else {
        std::cerr << "failed to configure qdisc" << std::endl;
    }

    return 0;
}

编译并运行程序后,输出为

RTNETLINK answers: Operation not permitted
Failed to configure qdisc

这是有道理的,因为 tc qdisc add 是有特权的。我正在尝试找到一种方法来授予程序管理 qdisc 的权限,而无需以 sudo 身份运行。

tc qdisc add
需要CAP_NET_ADMIN,因此以下工作有效:

sudo setcap cap_net_admin+ep /sbin/tc

但这显然不仅限于首选的程序。但是,

sudo setcap cap_net_admin+eip ./myprogram
不起作用,导致与上面相同的输出,这意味着该功能未继承/传递给 system() -shell 中的
tc
。是否有某种方法可以配置功能或以其他方式在程序内执行命令,以便
tc
具有 CAP_NET_ADMIN 功能而无需 sudo?

旁注:预期用途是与 TAPRIO qdisc 一起使用,并构成程序操作的一小部分,因此如果可能的话,我希望避免在 libnl 中执行此操作。

c++ shell traffic linux-capabilities
1个回答
0
投票

感谢@3CxEZiVlQ 为我指明了正确的方向。以下是一些设置和清除可继承能力和环境能力集的工作示例,如下:

环境(自 Linux 4.3 起)

这是在非特权程序的 execve(2) 中保留的一组功能。环境能力集遵循以下不变量:如果不能同时允许且可继承,则任何能力都不能是环境能力。 环境能力集可以使用prctl(2)直接修改。如果相应的允许或可继承能力被降低,则环境能力会自动降低。

#include <iostream>

#include <sys/capability.h>
#include <sys/prctl.h>

#include <unistd.h> // For sleep

void setInheritance() {
    cap_t caps = cap_get_proc();

    if (caps == NULL) throw std::runtime_error("Failed: cap_get_proc()");
        
    cap_value_t capList[] = {CAP_NET_ADMIN};

    if (cap_set_flag(caps, CAP_INHERITABLE, 1, capList, CAP_SET) == -1)
        throw std::runtime_error("Failed: cap_set_flag() set inheritable");

    if (cap_set_proc(caps) == -1) throw std::runtime_error("Failed: cap_set_proc()");
    
    if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_ADMIN, 0, 0) == -1)
        throw std::runtime_error("Failed: prctl() ambient raise");

    caps = cap_get_proc();
    if (caps == NULL) throw std::runtime_error("Failed: cap_get_proc()");
    std:: cout << "Capabilities after setInheritance(): " << cap_to_text(caps, NULL) << '\n';
}

void clearInheritance() {
    cap_t caps = cap_get_proc();

    if (caps == NULL) throw std::runtime_error("Failed: cap_get_proc()");
        
    cap_value_t capList[] = {CAP_NET_ADMIN};

    if (cap_set_flag(caps, CAP_INHERITABLE, 1, capList, CAP_CLEAR) == -1)
        throw std::runtime_error("Failed: cap_set_flag() clear inheritable");

    if (cap_set_proc(caps) == -1) throw std::runtime_error("Failed: cap_set_proc()");
}

int main() {
    setInheritance(); // Add CAP_NET_ADMIN inheritance to capabilities
    
    if (system("tc qdisc add dev veno1 root fq_codel") == 0) {
        std::cout << "qdisc configured successfully" << std::endl;
    } else {
        std::cerr << "failed to configure qdisc" << std::endl;
    }

    sleep(2);

    if (system("tc qdisc del dev veno1 root") == 0) {
        std::cout << "qdisc deleted successfully" << std::endl;
    } else {
        std::cerr << "failed to delete qdisc" << std::endl;
    }

    sleep(2);

    clearInheritance(); // Remove CAP_NET_ADMIN from capabilities, removing ambient

    // Should fail - Operation not permitted
    if (system("tc qdisc add dev veno1 root fq_codel") == 0) {
        std::cout << "qdisc configured successfully" << std::endl;
    } else {
        std::cerr << "failed to configure qdisc" << std::endl;
    }

    return 0;
}

运行程序:

$ g++ setqdisc.cc -lcap -o setqdisc
$ sudo setcap cap_net_admin+p ./setqdisc
$ ./setqdisc
Capabilities after setInheritance(): cap_net_admin=ip
qdisc configured successfully
qdisc deleted successfully
RTNETLINK answers: Operation not permitted
failed to configure qdisc

请注意,提供二进制 Permission 功能就足够了(

setcap
+p

© www.soinside.com 2019 - 2024. All rights reserved.