linux container tech - hanyong/note GitHub Wiki

Linux 容器技术

chroot

执行 chroot 需要 root 权限 (CAP_SYS_CHROOT)。

创建一个空文件夹 newroot 测试。报错如下:

$ mkdir newroot
$ cd newroot/
$ sudo chroot .
chroot: failed to run command ‘/bin/bash’: No such file or directory

chroot 默认尝试执行 ${SHELL} -i,即 /bin/bash -i,而 newroot 是一个空目录,没有 /bin/bash 这个文件。 chroot 之后的进程只能访问 newroot 下的文件, 拷贝程序到 newroot 下需要将其所有依赖文件一起拷贝过来。 为简化起见,可以拷贝静态编译版的 busybox 过来测试。 busybox 堪称嵌入式 Linux 系统的瑞士军刀,包含各种常用的 Linux 基本命令。

$ rsync -t /bin/busybox bin/
>f+++++++++ busybox

$ sudo chroot . /bin/busybox sh


BusyBox v1.22.1 (Ubuntu 1:1.22.0-15ubuntu1) built-in shell (ash)
Enter 'help' for a list of built-in commands.

/ # id
uid=0 gid=0 groups=0

为方便使用,可让 busybox 在 newroot 下安装常用命令的软连接。

mkdir -p sbin usr/sbin usr/bin
sudo chroot . /bin/busybox --install -s

chroot 之后的进程可以显式指定其他用户和组,这样可以进一步限制 chroot 之后的进程权限。

$ sudo chroot --userspec 500:500 . id
uid=500 gid=500 groups=500

用户和组可以用本机用户名和组名指定,但最终也会换成 uid/gid,内核只关心 id,不关心 name。 发现指定本机 uid 时, 会自动设置本机 uid 对应的 gid, 但 groups 不会设置(?)。

Linux 下有一些关键的系统目录,如 /proc, /sys, /dev/pts 等。 简单 chroot 后,需要访问这些目录的命令将无法正常工作,如 ps 命令需要读 /proc 信息。

$ sudo chroot --userspec $USER . mount
mount: no /proc/mounts

$ sudo chroot --userspec $USER . ps
PID   USER     COMMAND
ps: can't open '/proc': No such file or directory

$ sudo chroot --userspec $USER . tty
not a tty

如果 chroot 之后的进程有 root 权限,可以自己挂载这些目录。 挂载后 /proc, /dev 等可访问到宿主机上的相关信息 (但 mount 只能访问 newroot 目录下的挂载信息),放宽了权限。

mkdir -p proc sys dev/pts
mount -t proc proc proc/
mount -t sysfs sysfs sys/
mount -t devtmpfs devtmpfs dev/
mount -t devpts devpts dev/pts/

这些挂载是在宿主机上生效的,退出 chroot 后依然存在。 可以在宿主机上查看这些挂载:

$ mount | grep newroot
proc on /home/hanyong/newroot/proc type proc (rw,relatime)
sysfs on /home/hanyong/newroot/sys type sysfs (rw,relatime)
devtmpfs on /home/hanyong/newroot/dev type devtmpfs (rw,relatime,size=5089012k,nr_inodes=1272253,mode=755)
devpts on /home/hanyong/newroot/dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)

重新执行 chroot 以普通用户执行命令仍然可以访问这些功能:

$ sudo chroot --userspec $USER . mount
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
devtmpfs on /dev type devtmpfs (rw,relatime,size=5089012k,nr_inodes=1272253,mode=755)
devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)

$ sudo chroot --userspec $USER . ps
PID   USER     COMMAND
    1 0        {systemd} /sbin/init

$ sudo chroot --userspec $USER . tty
/dev/pts/1

用户需要在恰当的时候手动卸载这些挂载点:

umount proc/ sys/ dev/pts/ dev/

proot

proot 看起来与 chroot 类似, 但:

  1. 用户态实现,不需要 root 权限。
  2. 自动处理挂载点,使用更友好。

使用上述 newroot 目录测试如下:

$ proot -r . -w / id
uid=1001 gid=1000 groups=4,24,27,30,46,113,129,131,132,500,1000

-r 指定新的文件系统 root 目录, -w 指定 proot 后的工作目录, 因为默认的当前目录不存在, 指定系统根目录 /。 proot 后的进程以当前用户运行,因为没有 root 权限,没有切换用户的能力。

同样默认情况下 mount, ps 等命令不能正常工作。 /proc 为空目录但未挂载,ps 返回空但未报错。

$ proot -r . -w / mount
mount: no /proc/mounts

observer.hany@ali-59375n:~/tmp/newroot
$ proot -r . -w / ps
PID   USER     COMMAND

observer.hany@ali-59375n:~/tmp/newroot
$ proot -r . -w / tty
not a tty

proot 可以自动处理挂载点,使用 -b 参数指定挂载点即可。 除了标准系统目录,也可以指定自定义挂载点。 如 -b $PWD:$PWD 使 proot 后的进程也使用宿主机上的当前目录,这也是默认的工作目录。

$ proot -r . -w / -b /proc -b /sys -b /dev -b /dev/pts mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
# ... ...

$ proot -r . -w / -b /proc -b /sys -b /dev -b /dev/pts ps
PID   USER     COMMAND
    1 0        {systemd} /sbin/init
# ... ...

$ proot -r . -w / -b /proc -b /sys -b /dev -b /dev/pts tty
/dev/pts/1

$ proot -r . -b /proc -b /sys -b /dev -b /dev/pts -b $PWD:$PWD readlink -f .
/home/hanyong/newroot

为了简化用户操作, proot 提供了 -R 参数, 自动设置常用挂载点。

$ proot -R . ls
bin   dev   etc   home  proc  run   sbin  sys   tmp   usr   var

mount, ps 等看到的都是宿主机上的状态。 proot 的自动 mount 也是用户态实现的,所以宿主机上 mount 看不到这些挂载点。 proot 进程退出后这些挂载点也自动消失。