为什么在使用 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_map
和 gid_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;
}
我正在努力为我的构建过程添加一些限制 - 特别是检测周期。为此,我一直在尝试 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_map
和 gid_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;
}