chroot 命令小记

4,473 阅读2分钟

什么是 chroot

chroot 最早是作为系统调用引入 1979 年的 Unix V7 系统,目的是为了将当前进程及其子进程的 root 目录重定向到某个指定目录。1982 年,chroot 功能被加入到 BSD 中,后经 20 多年,FreeBSD 团队引入虚拟化技术的概念,在原本的 chroot 机制上,开发了新的 jail 机制。

简单来说:一个正在运行的进程经过 chroot 操作后,其根目录将被显式映射为某个指定目录,它将不能够对该指定目录之外的文件进行访问动作。这是一种非常简单的资源隔离化操作,类似于现在 Linux 的 Mount Namespace 功能。当年 Docker 刚开源的时候,有个人就利用 Linux 下 chroot 命令,用 100 多行的 Bash 代码实现了一个模拟版的 Docker

chroot 的使用

在现今的 Linux 上,chroot 既是一个 CLI 工具(chroot(8)),又是一个系统调用(chroot(2))。

  • 使用 chroot(8) 命令

    我们可以利用 Linux 下的 chroot(8) 命令来创建出一种类似于进入某个隔离容器内部的效果。

    chroot(8) 的用法很简单,格式如下所示:

    chroot [OPTION] NEWROOT [COMMAND [ARGS]...]
    

    COMMAND 指的是切换 root 目录后需要执行的命令,如果没有指定,默认是 ${SHELL} -i,大部分情况是 /bin/bash。执行 chroot(8) 需要使用 root 权限。

    简单地,我们可以这样使用:

    $ sudo chroot /path/to/new/root /bin/bash
    

    下面就让我们来建造我们的监狱(jail)(备注:基于 Ubuntu 16.04)。

    1. 创建对应的新的根目录

      $ J=$HOME/jail
      $ mkdir -p $J
      $ mkdir -p $J/{bin,lib/x86_64-linux-gnu,lib64,etc,var}
      
    2. 将几个必要的命令工具 copy 到 bin/

      $ sudo cp -vf /bin/{bash,ls} $J/bin
      
    3. 将步骤 2 中可执行命令依赖的动态库 copy 到 jail/

      $ list=`ldd /bin/ls | egrep -o '/lib.*\.[0-9]'`
      $ for i in $list; do sudo cp -vf $i $J/$i; done
      
      $ list=`ldd /bin/bash | egrep -o '/lib.*\.[0-9]'`
      $ for i in $list; do sudo cp $i -vf $J/$i; done
      
    4. 执行 chroot 命令

      $ sudo chroot $J /bin/bash
      bash-4.3# ls
      bin  etc  lib  lib64  var
      bash-4.3# cd /
      bash-4.3# ls
      bin  etc  lib  lib64  var
      bash-4.3# cd ..
      bash-4.3# ls
      bin  etc  lib  lib64  var
      

      无论我们如何改变目录,其根目录都被隔离在 $J 中,执行 exit 命令可退出这一环境;

  • 使用 chroot(2) 系统调用

    chroot(2) 的原型是:

    #include <unistd.h>
    
    int chroot(const char *path);
    

    chroot() 将调用进程及其子进程的根目录指定为 path。执行该调用需要使用 root 权限。

    如以下代码所示:

    #include <stdio.h>
    #include <error.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    char *const patv[] = "/home/test/jail"; # 如上文实验所述目录
    char *const argv[] = {"/bin/bash", NULL};
    
    int
    main(void) {
        if (chroot(path) != 0) {
            perror("chroot error");
        	  exit(1);
        }
        chdir("/");                 // 忽略返回值
        execvp("/bin/bash", argv);  // 忽略返回值
        return 0;
    }
    

    编译和运行代码:

    $ gcc test_chroot.c -o test_chroot
    $ ./test_chroot # 非 root 用户执行命令
    chroot error: Operation not permitted
    $ sudo ./test_chroot
    bash-4.3#
    
  • 如何获知某个进程是否处于 chroot 监禁

    可通过查看进程的 /proc/<pid>/root 来查看对应进程是否处于 chroot 监禁中,如上文,其 chroot 下 bash 的执行进程为 15768,则有:

    $ sudo ls -ld /proc/15768/root
    lrwxrwxrwx 1 root root 0 Apr 17 22:47 /proc/15768/root -> /home/test/jail
    

    可见其根目录已经被修改为 /home/test/jail

chroot 的安全问题

chroot 机制从一开始就并非安全,存在很多安全漏洞,有不少「越狱」(jailbreak)的手段。

参考文档