首页 > 解决方案 > 为什么我使用 unshare(CLONE_NEWUSER) 后无法运行某些程序

问题描述

我正在努力为我的构建过程添加一些限制——特别是检测周期。为了实现这一点,我一直在尝试用户命名空间

这是我的“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'd 程序按预期工作:

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

这种行为的原因是什么?

标签: c++linuxlinux-namespaces

解决方案


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

在我的终端上$ id通知我,我的真实 uid 和 gid 都是 1000,所以我在地图中硬编码了它。在流程开始时查询原始 id 更正确,请参阅这篇出色的博客文章此手册页也有助于此解决方案。

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

#define fatal_error(...) \
do { \
    fprintf(stderr, "namespace_test \033[1;31merror:\033[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;
}

推荐阅读