为啥我使用 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 的信息 我可以使用您的Makefile
和touch 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_map
和gid_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