我正在尝试使用命名空间和 chroot 模拟,并成功创建了一个仅包含新根的环境(通过目录遍历确认),但由于某种原因,我似乎无法在其中执行任何操作它。
这是一个最小可重复的示例:
mkdir /jail
mkdir /jail/bin
mkdir /jail/usr
mkdir /jail/lib
cp /bin/bash /jail/bin
/* isoroot.c */
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sched.h> /* use clone */
#include <syscall.h>
#include <sys/mount.h>
#include <sched.h>
#ifndef pivot_root
#define pivot_root(new, old) syscall(SYS_pivot_root, new, old)
#endif
int main(int argc, char *argv[])
{
int res = 0;
const char *new_root = "/jail";
const char *old_root = "/oldroot";
(void) argc;
if (unshare(CLONE_NEWNS)) { /* Must be executed with privileges. Don't forget this, or we basically blow up the system since we'll remove the real mount */
fprintf(stderr, "unshare: %s\n", strerror(errno));
_exit(errno);
}
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { /* Prevent shared propagation */
fprintf(stderr, "mount: %s\n", strerror(errno));
_exit(errno);
}
if (mount(new_root, new_root, NULL, MS_BIND, NULL)) { /* Ensure new_root is a mount point */
fprintf(stderr, "mount: %s\n", strerror(errno));
_exit(errno);
}
if (mkdir("/jail/oldroot", 0777)) { /* should be new_root/old_root */
fprintf(stderr, "mkdir: %s\n", strerror(errno));
_exit(errno);
}
/* Mount some things we need before we dismount the old root */
/* /usr has /bin, /sbin, /lib, and /lib64 */
res = mount("/usr", "/jail/usr", NULL, MS_BIND | MS_REC | MS_RDONLY, NULL);
if (res) {
fprintf(stderr, "mount usr failed: %s\n", strerror(errno));
}
if (pivot_root(new_root, "/jail/oldroot")) { /* Actually make it the new root */
fprintf(stderr, "pivot_root: %s\n", strerror(errno));
_exit(errno);
}
if (chdir("/")) { /* Switch to new root */
fprintf(stderr, "chdir failed: %s\n", strerror(errno));
_exit(errno);
}
if (umount2(old_root, MNT_DETACH)) { /* Unmount old root */
fprintf(stderr, "umount2 failed: %s\n", strerror(errno));
_exit(errno);
}
if (rmdir(old_root)) { /* Remove old mount point */
fprintf(stderr, "rmdir failed: %s\n", strerror(errno));
_exit(errno);
}
if (access("/bin/bash", X_OK)) {
fprintf(stderr, "bash4 not found: %s\n", strerror(errno));
_exit(errno);
}
printf("Got all the way here\n");
execvp("/bin/bash", argv);
fprintf(stderr, "%s\n", strerror(errno));
exit(errno);
}
编译:
gcc -Wall -Werror -Wunused -Wextra -Wmaybe-uninitialized -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wnull-dereference -Wformat=2 -Wshadow -Wsizeof-pointer-memaccess -std=gnu99 -pthread -O0 -g -Wstack-protector -fno-omit-frame-pointer -fwrapv -D_FORTIFY_SOURCE=2 -c isoroot.c
gcc -Wall -Werror -Wunused -Wextra -Wmaybe-uninitialized -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wnull-dereference -Wformat=2 -Wshadow -Wsizeof-pointer-memaccess -std=gnu99 -pthread -O0 -g -Wstack-protector -fno-omit-frame-pointer -fwrapv -D_FORTIFY_SOURCE=2 -o isoroot *.o
看起来特别奇怪的是,最后一个
access
成功了,说明access
可以找到/bin/bash
。然而,execvp
总是失败,并出现No such file or directory
。
我假设由于
access
此时正在使用新的命名空间,exec
也会使用新的命名空间,但由于某种原因,这似乎并不成立。是否有一些技巧可以让 exec 在更改根目录后找到东西?
作为比较,这里的示例确实在 exec 成功的意义上起作用,但它似乎并没有真正旋转根,因为真正根的所有内容仍然可见:https://lkml.iu.edu/ hypermail/linux/kernel/0803.0/1805.html
chdir("/jail");
unshare(CLONE_NEWNS);
mount("/jail", "/jail", NULL, MS_BIND, NULL);
pivot_root("/jail", "/jail/old_root");
chdir("/");
mount("/old_root/bin", "bin", NULL, MS_BIND, NULL);
mount("/old_root/usr", "usr", NULL, MS_BIND, NULL);
mount("/old_root/lib", "lib", NULL, MS_BIND, NULL);
umount2("/old_root", MNT_DETACH);
exec("/busybox");
使用
ldd
,我已经确认我应该拥有运行类似/bin/sh
之类的东西所需的一切。 /bin
和 /lib
,以及 /lib64
都只是指向 中的文件夹的符号链接
# ldd /bin/bash
linux-vdso.so.1 (0x00007ffc99116000)
libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007fd041cb9000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd041cb3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd041ade000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd041e3a000)
我也尝试手动添加符号链接,以防万一,但当我在最后执行此操作时,就在 exec 之前,我只是得到“错误:文件已存在”:
if (symlink("/bin", "/usr/bin")) {
fprintf(stderr, "symlink failed: %s\n", strerror(errno));
}
if (symlink("/lib", "/usr/lib")) {
fprintf(stderr, "symlink failed: %s\n", strerror(errno));
}
if (symlink("/lib64", "/usr/lib64")) {
fprintf(stderr, "symlink failed: %s\n", strerror(errno));
}
尝试
mount
/proc
和 /lib64
失败,尽管 /bin
和 /lib
成功(尽管除 /proc
之外的所有这些都是到 /usr/<dir>
的符号链接)
我也遇到了同样的问题,有更新吗?