为什么在使用 unshare(CLONE_NEWUSER) 后我不能 运行 某些程序

Why can't I run some programs after using unshare(CLONE_NEWUSER)

我正在努力为我的构建过程添加一些限制 - 特别是检测周期。为此,我一直在尝试 user namespaces.

这是我的 'hello world' 程序:

#include <sched.h>
#include <unistd.h>

int main()
{
    if( unshare(CLONE_NEWUSER) != 0)
    {
        return -1;
    }

    execl("/bin/sh", "/bin/sh", "-e", "-c", "make", NULL);
    return 0;
}

这里是make运行生成的makefile,namespace_test.cpp是上面的文件名:

namespace_test: namespace_test.cpp
    g++ namespace_test.cpp -o ./namespace_test

当一切都是最新的(由 make 确定)时,exec 程序按预期工作:

make: 'namespace_test' is up to date.

但是如果 make 实际上 运行 是 g++ 调用我得到一个不透明的错误:

g++ namespace_test.cpp -o ./namespace_test
make: g++: Invalid argument
make: *** [Makefile:2: namespace_test] Error 127

这种行为的原因是什么?

此错误是由于我未能设置 uid_mapgid_map。我没有给出一个令人满意的、明确的、最小的错误示例,但我已经编写了一个可行的最小解决方案,我将在此处分享。请注意 int main() 是相同的,除了在 exec 之前我们首先设置 uid_map 然后是 gid_map 的目标命令(通过 setgroups 授予我们自己的权限) ).

在我的终端上 $ id 告诉我我的真实 uid 和 gid 都是 1000,所以我在地图中硬编码了它。在流程开始时查询原始ID更正确,请参阅此excellent blog post. Also instrumental in this solution is this man page

#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>

#define fatal_error(...) \
do { \
    fprintf(stderr, "namespace_test 3[1;31merror:3[0m "); \
    fprintf(stderr, __VA_ARGS__ ); \
    fprintf(stderr, "\n"); \
    exit(EXIT_FAILURE); \
} while(0)

void write_string_to_file(const char* filename, const char* str, size_t str_len)
{
    int fd = open(filename, O_RDWR);
    if(fd == -1)
    {
        fatal_error("Failed to open %s: %m", filename);
    }

    if( write(fd, str, str_len) != str_len )
    {
        fatal_error("Failed to write %s: %m", filename);
    }

    close(fd);
}

void write_uid_mapping()
{
    const char* mapping = "0 1000 1";
    write_string_to_file("/proc/self/uid_map", mapping, strlen(mapping));
}

void write_set_groups()
{
    const char* deny = "deny\n";
    write_string_to_file("/proc/self/setgroups", deny, strlen(deny));
}

void write_gid_mapping()
{
    write_set_groups();

    const char* mapping = "0 1000 1";
    write_string_to_file("/proc/self/gid_map", mapping, strlen(mapping));
}

int main()
{
    if(unshare(CLONE_NEWUSER) != 0)
    {
        fatal_error("Failed to move into new user namespace");
    }

    write_uid_mapping();
    write_gid_mapping();

    execl("/bin/sh", "/bin/sh", "-e", "-c", "make", NULL);

    return 0;
}