Ubuntu – 非root用户可以在chroot jail中运行进程吗?

非root用户可以在Ubuntu上运行chroot进程吗?

在Linux上, chroot(2)系统调用只能由具有特权的进程进行。 该进程所需的function是CAP_SYS_CHROOT。

你不能作为一个用户chroot的原因很简单。 假设你有一个setuid程序,比如sudo,如果你被允许做某事,它会检查/ etc / sudoers。 现在把它放在你自己的/ etc / sudo的chroot chroot中。 突然你有一个即时特权升级。

可以devise一个程序来chroot自己并运行它作为setuid进程,但这通常被认为是糟糕的devise。 chroot的额外安全性不会引起setuid的安全问题。

@ imz – IvanZakharyaschev对Pehrs的回答评论说,引入命名空间可能是可能的,但是这并没有经过testing并作为回答发布。 是的,这确实使非root用户可以使用chroot。

给定一个静态链接的dash和一个静态链接的busybox以及一个以非root用户身份运行的bash shell:

 $ mkdir root $ cp /path/to/dash root $ cp /path/to/busybox root $ unshare -r bash -c 'chroot root /dash -c "/busybox ls -al /"' total 2700 drwxr-xr-x 2 0 0 4096 Dec 2 19:16 . drwxr-xr-x 2 0 0 4096 Dec 2 19:16 .. drwxr-xr-x 1 0 0 1905240 Dec 2 19:15 busybox drwxr-xr-x 1 0 0 847704 Dec 2 19:15 dash 

该名称空间中的根用户标识映射到该名称空间之外的非root用户标识,反之亦然,这就是为什么系统将当前用户拥有的文件显示为拥有用户标识0的原因。常规的ls -al root ,而不是unshare ,确实将它们显示为由当前用户拥有。


注意:众所周知,能够使用chroot进程能够脱离chroot 。 由于unshare -r会将chroot权限授予普通用户,因此如果在chroot环境中允许这样做,则会存在安全风险。 事实上,这是不被允许的,并失败:

取消共享:取消共享失败:不允许操作

它与unshare(2)文档相匹配(为粗鲁的胆怯而道歉,但是看起来像这样):

EPERM (自Linux 3.9开始)

CLONE_NEWUSER在 标志 中指定,调用者处于chroot环境(即调用者的根目录与它所在的安装名称空间的根目录不匹配)。

现在,您希望看到LXC(Linux容器)而不是chroot / BSD监狱。 它位于chroot和虚拟机之间,为您提供了大量的安全控制和一般的可configuration性。 我相信所有你需要运行它作为一个用户将是拥有必要的文件/设备的组成员,但也可能涉及function/系统权限。 不pipe怎样,它应该是非常可行的,因为LXC在SELinux等被添加到Linux内核之后已经很久了。

另外,请记住,您只能以root用户身份编写脚本,但使用sudo为用户提供安全的权限来运行这些脚本(如果您喜欢,则不需要密码,但要确保脚本是安全的)。

不。如果我记得正确的话,有一些内核级别的事情,chroot可以阻止它。 我不记得那是什么东西。 在debuggingGentoo的Catalyst Build工具时(和gentoo上的chroot和ubuntu上的chroot相同),我调查了它。 虽然有可能在没有密码的情况下发生,但这样的事情仍然存在潜在安全漏洞的领域,并确保你知道你在做什么。

fakeroot / fakechroot的组合为简单的需求提供了一个chroot的模拟,例如生成tar文件,其中文件看起来是由root所有的。 Fakechroot manpage是http://linux.die.net/man/1/fakechroot

虽然你没有得到任何新的权限,但是如果你在调用之前拥有一个目录(例如假发行版)

 fakechroot fakeroot chroot ~/fake-distro some-command 

它现在寻找像你一样的命令,拥有假发行版中的所有东西。

看起来用用户名空间实际上可以在没有root的情况下进行chroot操作。 这是一个示例程序,certificate这是可能的。 我只开始了解linux名称空间是如何工作的,所以我不完全确定这个代码是否是最佳实践。

另存为user_chroot.cc 。 使用g++ -o user_chroot user_chroot.cc编译g++ -o user_chroot user_chroot.cc 。 用法是./user_chroot /path/to/new_rootfs

 // references: // [1]: http://man7.org/linux/man-pages/man7/user_namespaces.7.html // [2]: http://man7.org/linux/man-pages/man2/unshare.2.html #include <sched.h> #include <sys/types.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <cerrno> #include <cstdio> #include <cstring> int main(int argc, char** argv) { if(argc < 2) { printf("Usage: %s <rootfs>\n", argv[0]); } int uid = getuid(); int gid = getgid(); printf("Before unshare, uid=%d, gid=%d\n", uid, gid); // First, unshare the user namespace and assume admin capability in the // new namespace int err = unshare(CLONE_NEWUSER); if(err) { printf("Failed to unshare user namespace\n"); return 1; } // write a uid/gid map char file_path_buf[100]; int pid = getpid(); printf("My pid: %d\n", pid); sprintf(file_path_buf, "/proc/%d/uid_map", pid); int fd = open(file_path_buf, O_WRONLY); if(fd == -1) { printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno, strerror(errno)); } else { printf("Writing : %s (fd=%d)\n", file_path_buf, fd); err = dprintf(fd, "%d %d 1\n", uid, uid); if(err == -1) { printf("Failed to write contents [%d]: %s\n", errno, strerror(errno)); } close(fd); } sprintf(file_path_buf, "/proc/%d/setgroups", pid); fd = open(file_path_buf, O_WRONLY); if(fd == -1) { printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno, strerror(errno)); } else { dprintf(fd, "deny\n"); close(fd); } sprintf(file_path_buf, "/proc/%d/gid_map", pid); fd = open(file_path_buf, O_WRONLY); if(fd == -1) { printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno, strerror(errno)); } else { printf("Writing : %s (fd=%d)\n", file_path_buf, fd); err = dprintf(fd, "%d %d 1\n", gid, gid); if(err == -1) { printf("Failed to write contents [%d]: %s\n", errno, strerror(errno)); } close(fd); } // Now chroot into the desired directory err = chroot(argv[1]); if(err) { printf("Failed to chroot\n"); return 1; } // Now drop admin in our namespace err = setresuid(uid, uid, uid); if(err) { printf("Failed to set uid\n"); } err = setresgid(gid, gid, gid); if(err) { printf("Failed to set gid\n"); } // and start a shell char argv0[] = "bash"; char* new_argv[] = { argv0, NULL }; err = execvp("/bin/bash", new_argv); if(err) { perror("Failed to start shell"); return -1; } } 

我已经testing了这个最小rootfs生成与multistrap(作为非root)执行。 一些系统文件(如/etc/passwd/etc/groups )从主机rootfs复制到guest虚拟机rootfs中。