为啥我使用 unshare(CLONE_NEWUSER) 后无法运行某些程序

Posted

技术标签:

【中文标题】为啥我使用 unshare(CLONE_NEWUSER) 后无法运行某些程序【英文标题】:Why can't I run some programs after using unshare(CLONE_NEWUSER)为什么我使用 unshare(CLONE_NEWUSER) 后无法运行某些程序 【发布时间】:2021-09-04 07:27:40 【问题描述】:

我正在努力为我的构建过程添加一些限制 - 特别是检测周期。为了实现这一点,我一直在尝试使用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 确定)时,执行的程序按预期工作:

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_map 和 gid_map。如果是这样的话,我会报告一个答案,我想通了。 在这两种情况下对我来说都可以正常工作(最新和触摸后),无论如何你有更多关于错误 127 here 的信息 我可以使用您的Makefiletouch namespace_test.cpp 进行编译并运行调用make 的程序,在这两种情况下都可以正常工作:pastebin.com/1402LdRc 这很有趣,也许我会比较其他发行版中的行为。不过,Docker 和 podman 确实在这台机器上工作,所以我认为我的小测试并不完全正确。 如果有帮助,那是我机器上g++ -v namespace_test.cpp -o namespace_test 的输出:pastebin.com/zDJy4WWS (Ubuntu 21.04) 【参考方案1】:

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

在我的终端上$ id 告诉我我的真实 uid 和 gid 都是 1000,所以我在地图中硬编码了它。在流程开始时查询原始 id 更正确,请参阅excellent blog post。 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 \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;

【讨论】:

以上是关于为啥我使用 unshare(CLONE_NEWUSER) 后无法运行某些程序的主要内容,如果未能解决你的问题,请参考以下文章

Docker基础 Linux内核命名空间之 ipc namespace

云原生基础 docker & harbor

新的电脑拉取 or clone 代码出错解决方法

为啥我不能使用类名找到元素?

为啥使用 glTranslatef?为啥不直接更改渲染坐标?

为啥我不能使用 populateViewHolder 覆盖方法?