chroot,即 change root directory (更改 root 目录)。在 linux 系统中,系统默认的目录结构都是以 /,即以根 (root) 开始的。而在使用 chroot 以后,系统的目录结构将以指定的位置做为 / 位置。html
chroot NEWROOT [COMMAND [ARG]...]node
具体用法请参考本文的 demo。linux
增长了系统的安全性,限制了用户的权力:
在通过 chroot 以后,在新根下将访问不到旧系统的根目录结构和文件,这样就加强了系统的安全性。通常会在用户登陆前应用 chroot,把用户的访问能力控制在必定的范围以内。docker
创建一个与原系统隔离的系统目录结构,方便用户的开发:
使用 chroot 后,系统读取的是新根下的目录和文件,这是一个与原系统根下文件不相关的目录结构。在这个新的环境中,能够用来测试软件的静态编译以及一些与系统不相关的独立开发。shell
切换系统的根目录位置,引导 Linux 系统启动以及急救系统等:
chroot 的做用就是切换系统的根位置,而这个做用最为明显的是在系统初始引导磁盘的处理过程当中使用,从初始 RAM 磁盘 (initrd) 切换系统的根位置并执行真正的 init,本文的最后一个 demo 会详细的介绍这种用法。centos
busybox 包含了丰富的工具,咱们能够把这些工具放置在一个目录下,而后经过 chroot 构造出一个 mini 系统。简单起见咱们直接使用 docker 的 busybox 镜像打包的文件系统。先在当前目录下建立一个目录 rootfs:安全
$ mkdir rootfs
而后把 busybox 镜像中的文件释放到这个目录中:bash
$ (docker export $(docker create busybox) | tar -C rootfs -xvf -)
经过 ls 命令查看 rootfs 文件夹下的内容:app
$ ls rootfs
万事俱备,让咱们开始吧!函数
执行 chroot 后的 ls 命令
$ sudo chroot rootfs /bin/ls
虽然输出结果与刚才执行的 ls rootfs 命令形同,可是此次运行的命令倒是 rootfs/bin/ls。
运行 chroot 后的 pwd 命令
$ sudo chroot rootfs /bin/pwd
哈,pwd 命令真把 rootfs 目录当根目录了!
不带命令执行 chroot
$ sudo chroot rootfs
此次出错了,由于找不到 /bin/bash。咱们知道 busybox 中是不包含 bash 的,可是 chroot 命令为何会找 bash 命令呢? 原来,若是不给 chroot 指定执行的命令,默认它会执行 '${SHELL} -i',而个人系统中 ${SHELL} 为 /bin/bash。
既然 busybox 中没有 bash,咱们只好指定 /bin/sh 来执行 shell 了。
$ sudo chroot rootfs /bin/sh
运行 sh 是没有问题的,而且咱们打印出了当前进程的 PID。
虽然咱们作了好几个实验,可是确定会有朋友心存疑问,怎么能证实咱们运行的命令就是在 chroot 目录后的路径中呢?
其实,咱们能够经过 /proc 目录下的文件检查进程的中的根目录,好比咱们能够经过下面的代码检查上面运行的 /bin/sh 命令的根目录(请在另一个 shell 中执行):
$ pid=$(pidof -s sh) $ sudo ls -ld /proc/$pid/root
输出中的内容明确的指出 PID 为 46644 的进程的根目录被映射到了 /tmp/rootfs 目录。
下面咱们尝试本身实现一个 chroot 程序,代码中涉及到两个函数,分别是 chroot() 函数和 chdir() 函数,其实真正的 chroot 命令也是经过调用它们实现的:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { if(argc<2){ printf("Usage: chroot NEWROOT [COMMAND...] \n"); return 1; } if(chroot(argv[1])) { perror("chroot"); return 1; } if(chdir("/")) { perror("chdir"); return 1; } if(argc == 2) { // hardcode /bin/sh for my busybox tools. argv[0] = (char *)"/bin/sh"; argv[1] = (char *) "-i"; argv[2] = NULL; } else { argv += 2; } execvp (argv[0], argv); printf("chroot: cannot run command `%s`\n", *argv); return 0; }
把上面的代码保存到文件 mychroot.c 文件中,并执行下面的命令进行编译:
$ gcc -Wall mychroot.c -o mychroot
mychroot 的用法和 chroot 基本相同:
$ sudo ./mychroot ./rootfs
特别之处是咱们的 mychroot 在没有传递命令的状况下执行了 /bin/sh,缘由固然是为了支持咱们的 busybox 工具集,笔者在代码中 hardcode 了默认的 shell:
argv[0] = (char *)"/bin/sh";
从代码中咱们也能够看到,实现 chroot 命令的核心逻辑其实并不复杂。
忘记了 root 密码该怎么办?接下来的 demo 将演示如何经过 chroot 命令从新设置 centos7 中被忘记了的 root 密码。
systemd 的管理机制中,rescure 模式和 emeryency 模式是没法直接取得 root 权限的,须要使用 root 密码才能进入 rescure 和 emeryency 环境。因此咱们须要经过其余方式来设置 root 密码。咱们能够为内核的启动指定 "rd.break" 参数,从而让系统在启动的早期停下来,此时咱们能够经过使用 root 权限并结合 chroot 命令完成设置 root 密码的操做。下面咱们一块儿来看具体的操做过程。
在系统启动过程当中进入开机菜单时按下字母键 e 进程开机菜单的编辑模式:
这就是系统的开机菜单,按下 e 后进入编辑界面:
找到以 "linux16 /vmlinuz-" 开头的行。若是默认没有看到该行,须要按向下键把它滚动出来。
而后定位到该行结尾处,输入一个空格和字符串 "rd.break",以下图所示:
接着按下 ctrl + x 以该设置继续启动,启动过程当中操做系统会停下来,这是系统启动过程当中的一个很是早的时间点:
因此系统的根目录还挂载在 RAM disk 上(就是内存中的一个文件系统),咱们能够经过 mount 命令检查系统当前挂载的文件系统,下面是咱们比较关心的两条:
上图中 mount 命令输出的第一行说明此时的根目录在一个 RAM disk 中, 即 rootfs。
图中输出的第二行说明咱们的文件系统此时被挂载到了 /sysroot 目录,而且是只读的模式:
/dev/mapper/centos-root on /sysroot type xfs (ro,relatime,attr2,inode64,noquota)
而在咱们正常登录系统的状况下,系统根目录的挂载状况以下:
/dev/mapper/centos-root on / type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
该时间点的最大优点是咱们具备 root 权限!因此让咱们开始设置新的 root 密码吧。
先经过下面的命令把 /sysroot 从新挂载为可读写的模式:
switch_root:/# mount -o remount,rw /sysroot
而后用下面 chroot 命令把根目录切换到咱们原来的环境中:
switch_root:/# chroot /sysroot
此时能够理解为:咱们以 root 权限登陆了原来的系统,修改密码就很容易了!用下面的命令为 root 用户设置新的密码:
sh-4.2# echo "new_root_pw" | passwd --stdin root
接下来还要处理 SELinux 相关的问题。因为当前的环境中 SELinux 并未启动,因此咱们对文件的修改可能形成文件的 context 不正确。为了确保开机时从新设定 SELinux context,必須在根目录下添加隐藏文件 .autorelabel:
sh-4.2# touch /.autorelabel
最后从 chroot 中退出,并重启系统:
sh-4.2# exit switch_root:/# reboot
从新进入登录界面时就可使用刚才设置的密码以 root 登录了!
chroot 是一个颇有意思的命令,咱们能够用它来简单的实现文件系统的隔离。但在一个容器技术繁荣的时代,用 chroot 来进行资源的隔离实在是 low 了点。因此 chroot 的主要用途仍是集中在系统救援、维护等一些特殊的场景中。