linux · 2023-01-31 0

chroot与namespace

一、chroot

使用 chroot 创建一个隔离环境

# 拉取 busybox 镜像
docker pull busybox:1.36.0
# 镜像打成 tar 包
docker save -o busybox_1.36.0.tar busybox:1.36.0
zxm@zxm-pc:~/chroot-dir$ mkdir -p busybox_tmp
zxm@zxm-pc:~/chroot-dir$ mkdir -p busybox
zxm@zxm-pc:~/chroot-dir$ ls
busybox  busybox_1.36.0.tar  busybox_tmp
zxm@zxm-pc:~/chroot-dir$ tar -xf busybox_1.36.0.tar -C busybox_tmp/
zxm@zxm-pc:~/chroot-dir$ ls busybox_tmp/
af2c3e96bcf1a80da1d9b57ec0adc29f73f773a4a115344b7e06aec982157a33.json  b2ba015c4eb00edaebd77acab07c710635615297303670b605a441babe0bb04a  manifest.json  repositories
zxm@zxm-pc:~/chroot-dir$ ls busybox_tmp/b2ba015c4eb00edaebd77acab07c710635615297303670b605a441babe0bb04a/
json  layer.tar  VERSION
zxm@zxm-pc:~/chroot-dir$ tar -xf busybox_tmp/b2ba015c4eb00edaebd77acab07c710635615297303670b605a441babe0bb04a/layer.tar -C busybox/
zxm@zxm-pc:~/chroot-dir$ ls busybox/
bin  dev  etc  home  lib  lib64  root  tmp  usr  var

进入容器内:

zxm@zxm-pc:~/chroot-dir$ sudo chroot busybox /bin/sh -i
/ # ls
bin    dev    etc    home   lib    lib64  root   tmp    usr    var
/ # whoami
root

挂载虚拟系统 sys、proc,它可以访问所有的网络设备,可以访问到主机上的进程及网络等信息,这表示没有任何的进程或者网络隔离,这带来的危险是我们甚至可以在容器内杀掉容器外的进程,或者通过容器来攻击主机。

/ # mkdir -p /sys
/ # mount -t sysfs sys /sys
/ # mkdir -p /proc
/ # mount -t proc proc /proc
/ # ip r
default via 192.168.1.1 dev enp4s0  metric 100 
10.0.3.0/24 dev br-bfcfe6038b6e scope link  src 10.0.3.1 
169.254.0.0/16 dev enp4s0 scope link  metric 1000 
172.16.49.0/24 dev vmnet1 scope link  src 172.16.49.1 
172.16.107.0/24 dev vmnet8 scope link  src 172.16.107.1 
172.17.0.0/16 dev docker0 scope link  src 172.17.0.1 
172.19.0.0/16 dev br-220f5959c723 scope link  src 172.19.0.1 
192.168.1.0/24 dev enp4s0 scope link  src 192.168.1.40  metric 100 
/ # ls /sys/class/net/
br-220f5959c723  br-bfcfe6038b6e  docker0          enp4s0           lo               veth256e23f      vethdc92448      vmnet1           vmnet8
/ # df -ah
Filesystem                Size      Used Available Use% Mounted on
sys                          0         0         0   0% /sys
proc                         0         0         0   0% /proc
/ # umount /sys/
/ # umount /proc/

chroot 一个只进行了文件系统隔离的容器

二、namespace

namespace 中的进程/进程组可以看起来拥有自己的独立资源,具体的“资源”表现形式取决于给它赋予了哪些 namespace

我们可以通过三个系统调用直接操作 namespace ,这三个系统调用分别是:

  • clone: 可以通过传递不同 namespace 的标志来为新的(子)进程指定其所属的 namespace
  • unshare: 允许一个进程(或线程)取消当前与其他进程(或线程)共享的执行上下文
  • setns: 进入文件描述符指定的 namespace

1.unshare

参数:

  • -m, --mount[=<文件>] 取消共享 mounts 名字空间
  • -u, --uts[=<文件>] 取消共享 UTS 名字空间(主机名等)
  • -i, --ipc[=<文件>] 取消共享 System V IPC 名字空间
  • -n, --net[=<file>] 取消共享网络名字空间
  • -p, --pid[=<文件>] 取消共享 pid 名字空间
  • -U, --user[=<文件>] 取消共享用户名字空间
  • -C, --cgroup[=<文件>] 取消共享 cgroup 名字空间
  • -f, --fork 在启动<程序>前 fork
  • -r, --map-root-user 将当前用户映射为 root (连带打开 --user)
  • --mount-proc[=<dir>] mount proc filesystem first (implies --mount)
  • -R, --root=<dir> run the command with root directory set to <dir>
  • -w, --wd=<dir> change working directory to <dir>
  • -S, --setuid <uid> set uid in entered namespace
  • -G, --setgid <gid> set gid in entered namespace

(1) Mount Namespace

可以用来隔离不同的进程或进程组看到的挂载点,使用 Mount Namespace 可以实现容器内只能看到自己的挂载信息,在容器内的挂载操作不会影响主机的挂载目录。

容器内:

zxm@zxm-pc:~$ sudo unshare --mount --fork /bin/bash
root@zxm-pc:/home/zxm# df -h
文件系统        容量  已用  可用 已用% 挂载点
/dev/nvme0n1p2  457G  141G  294G   33% /
udev             16G     0   16G    0% /dev
tmpfs            16G  147M   16G    1% /dev/shm
tmpfs           3.2G  2.0M  3.2G    1% /run
tmpfs           5.0M  4.0K  5.0M    1% /run/lock
tmpfs           3.2G   48K  3.2G    1% /run/user/1000
tmpfs            16G     0   16G    0% /sys/fs/cgroup
/dev/nvme0n1p1  511M  6.1M  505M    2% /boot/efi
root@zxm-pc:/home/zxm# mount -t tmpfs -o size=20m tmpfs /tmp/tmpfs
root@zxm-pc:/home/zxm# df -h
文件系统        容量  已用  可用 已用% 挂载点
/dev/nvme0n1p2  457G  141G  294G   33% /
udev             16G     0   16G    0% /dev
tmpfs            16G  147M   16G    1% /dev/shm
tmpfs           3.2G  2.0M  3.2G    1% /run
tmpfs           5.0M  4.0K  5.0M    1% /run/lock
tmpfs           3.2G   48K  3.2G    1% /run/user/1000
tmpfs            16G     0   16G    0% /sys/fs/cgroup
/dev/nvme0n1p1  511M  6.1M  505M    2% /boot/efi
tmpfs            20M     0   20M    0% /tmp/tmpfs

主机:

zxm@zxm-pc:~$ df -h
文件系统        容量  已用  可用 已用% 挂载点
udev             16G     0   16G    0% /dev
tmpfs           3.2G  2.0M  3.2G    1% /run
/dev/nvme0n1p2  457G  141G  294G   33% /
tmpfs            16G  155M   16G    1% /dev/shm
tmpfs           5.0M  4.0K  5.0M    1% /run/lock
tmpfs            16G     0   16G    0% /sys/fs/cgroup
/dev/nvme0n1p1  511M  6.1M  505M    2% /boot/efi
tmpfs           3.2G   48K  3.2G    1% /run/user/1000

(2) PID Namespace

PID Namespace 的作用是用来隔离进程。在不同的 PID Namespace 中,进程可以拥有相同的 PID 号,利用 PID Namespace 可以实现每个容器的主进程为 1 号进程,而容器内的进程在主机上却拥有不同的PID。

容器内:

zxm@zxm-pc:~$ sudo unshare --fork --pid --mount-proc /bin/bash
root@zxm-pc:/home/zxm# 
root@zxm-pc:/home/zxm# ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 18:28 pts/1    00:00:00 /bin/bash
root           8       1  0 18:28 pts/1    00:00:00 ps -ef

(3) UTS Namespace

UTS Namespace 主要是用来隔离主机名的,它允许每个 UTS Namespace 拥有一个独立的主机名。

zxm@zxm-pc:~$ sudo unshare --fork --uts /bin/bash 
root@zxm-pc:/home/zxm# hostname
zxm-pc
root@zxm-pc:/home/zxm# hostname -b docker-test
root@zxm-pc:/home/zxm# hostname
docker-test

(4) IPC Namespace

IPC Namespace 主要是用来隔离进程间通信的。例如 PID Namespace 和 IPC Namespace 一起使用可以实现同一 IPC Namespace 内的进程彼此可以通信,不同 IPC Namespace 的进程却不能通信。

zxm@zxm-pc:~$ sudo unshare --fork --ipc /bin/bash
root@zxm-pc:/home/zxm# ipcs -q

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息

root@zxm-pc:/home/zxm# ipcmk -Q
消息队列 id:0
root@zxm-pc:/home/zxm# ipcmk -Q
消息队列 id:1
root@zxm-pc:/home/zxm# ipcs -q

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息
0x7743ee6e 0          root       644        0            0
0xa2ffa527 1          root       644        0            0

(5) User Namespace

User Namespace 主要是用来隔离用户和用户组的。在主机上以非 root 用户运行的进程可以在一个单独的 User Namespace 中映射成 root 用户。
使用 User Namespace 可以实现进程在容器内拥有 root 权限,而在主机上却只是普通用户。
User Namesapce 的创建是可以不使用 root 权限的。

zxm@zxm-pc:~$ unshare --user -r /bin/bash
root@zxm-pc:~# id
用户id=0(root) 组id=0(root) 组=0(root),65534(nogroup)
root@zxm-pc:~# reboot
Failed to connect to bus: 不允许的操作
Failed to open initctl fifo: 权限不够
Failed to talk to init daemon.

我们在新创建的 User Namespace 内虽然是 root 用户,但是并没有权限执行 reboot 命令。这说明在隔离的 User Namespace 中,并不能获取到主机的 root 权限,也就是说 User Namespace 实现了用户和用户组的隔离。

(6) Net Namespace

Net Namespace 是用来隔离网络设备、IP 地址和端口等信息的。Net Namespace 可以让每个进程拥有自己独立的 IP 地址,端口和网卡信息。

root@zxm-pc:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether a8:a1:59:52:6d:10 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.40/24 brd 192.168.1.255 scope global noprefixroute enp4s0
       valid_lft forever preferred_lft forever
    inet6 fe80::4104:f838:4634:a65a/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: vmnet1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
    link/ether 00:50:56:c0:00:01 brd ff:ff:ff:ff:ff:ff
    inet 172.16.49.1/24 brd 172.16.49.255 scope global vmnet1
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:fec0:1/64 scope link 
       valid_lft forever preferred_lft forever
4: vmnet8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
    link/ether 00:50:56:c0:00:08 brd ff:ff:ff:ff:ff:ff
    inet 172.16.107.1/24 brd 172.16.107.255 scope global vmnet8
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:fec0:8/64 scope link 
       valid_lft forever preferred_lft forever
5: br-220f5959c723: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:a1:c3:29:04 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.1/16 brd 172.19.255.255 scope global br-220f5959c723
       valid_lft forever preferred_lft forever
6: br-bfcfe6038b6e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:79:81:1b:1e brd ff:ff:ff:ff:ff:ff
    inet 10.0.3.1/24 brd 10.0.3.255 scope global br-bfcfe6038b6e
       valid_lft forever preferred_lft forever
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:db:e1:9b:25 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
root@zxm-pc:~# unshare --net --fork /bin/bash
root@zxm-pc:~# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

namespace 或者 chroot 之类的都可以造出来一个 “容器”