主要功能和优势

所有容器上的Linux用户命名空间

使用增强的容器隔离,所有用户容器都利用Linux用户命名空间实现额外隔离。这意味着容器中的root用户映射到Docker Desktop Linux虚拟机中的非特权用户。

例如

$ docker run -it --rm --name=first alpine
/ # cat /proc/self/uid_map
         0     100000      65536

输出0 100000 65536是Linux用户命名空间的签名。这意味着容器中的root用户(0)映射到Docker Desktop Linux虚拟机中的非特权用户100000,并且映射扩展到64K用户ID的连续范围。组ID也适用相同规则。

每个容器都获得一个独占的映射范围,由Sysbox管理。例如,如果启动第二个容器,则映射范围不同。

$ docker run -it --rm --name=second alpine
/ # cat /proc/self/uid_map
         0     165536      65536

相反,如果没有增强的容器隔离,容器的root用户实际上是主机的root用户(又称“真实root用户”),这适用于所有容器。

$ docker run -it --rm alpine
/ # cat /proc/self/uid_map
         0       0     4294967295

通过使用Linux用户命名空间,增强的容器隔离确保容器进程永远不会在Linux虚拟机中以用户ID 0(真实root用户)运行。实际上,它们永远不会以Linux虚拟机中的任何有效用户ID运行。因此,它们的Linux功能仅限于容器内的资源,与普通容器相比,显著提高了隔离性,包括容器到主机和跨容器隔离。

特权容器也受到保护

特权容器docker run --privileged ...是不安全的,因为它们允许容器完全访问Linux内核。也就是说,容器以启用所有功能的真实root用户身份运行,seccomp和AppArmor限制被禁用,所有硬件设备都被暴露,等等。

旨在保护开发人员机器上Docker Desktop的组织面临特权容器的挑战。这些容器,无论运行良性还是恶意工作负载,都可以控制Docker Desktop虚拟机内的Linux内核,例如可能更改与安全相关的设置,例如注册表访问管理和网络代理。

使用增强的容器隔离,特权容器将无法再执行此操作。Linux用户命名空间和Sysbox使用的其他安全技术的组合确保特权容器内的进程只能访问分配给该容器的资源。

注意

增强的容器隔离不会阻止用户启动特权容器,而是通过确保它们只能修改与容器关联的资源来安全地运行它们。修改全局内核设置(例如加载内核模块或更改Berkeley Packet Filters (BPF)设置)的特权工作负载将无法正常工作,因为它们在尝试此类操作时会收到“权限被拒绝”错误。

例如,增强的容器隔离确保特权容器无法访问通过BPF配置的Linux虚拟机中的Docker Desktop网络设置。

$ docker run --privileged djs55/bpftool map show
Error: can't get next map: Operation not permitted

相反,如果没有增强的容器隔离,特权容器可以轻松做到这一点。

$ docker run --privileged djs55/bpftool map show
17: ringbuf  name blocked_packets  flags 0x0
        key 0B  value 0B  max_entries 16777216  memlock 0B
18: hash  name allowed_map  flags 0x0
        key 4B  value 4B  max_entries 10000  memlock 81920B
20: lpm_trie  name allowed_trie  flags 0x1
        key 8B  value 8B  max_entries 1024  memlock 16384B

请注意,一些高级容器工作负载需要特权容器,例如Docker-in-Docker、Kubernetes-in-Docker等。使用增强的容器隔离,您仍然可以运行这些工作负载,但比以前安全得多。

容器无法与Linux虚拟机共享命名空间

启用增强的容器隔离后,容器无法与主机共享Linux命名空间(例如,PID、网络、uts等),因为这实际上会破坏隔离。

例如,共享PID命名空间会失败。

$ docker run -it --rm --pid=host alpine
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: error in the container spec: invalid or unsupported container spec: sysbox containers can't share namespaces [pid] with the host (because they use the linux user-namespace for isolation): unknown.

同样,共享网络命名空间也会失败。

$ docker run -it --rm --network=host alpine
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: error in the container spec: invalid or unsupported container spec: sysbox containers can't share a network namespace with the host (because they use the linux user-namespace for isolation): unknown.

此外,用于禁用容器上用户命名空间的--userns=host标志将被忽略。

$ docker run -it --rm --userns=host alpine
/ # cat /proc/self/uid_map
         0     100000      65536

最后,Docker构建--network=host和Docker buildx授权(network.hostsecurity.insecure)不允许。需要这些的构建将无法正常工作。

绑定挂载限制

启用增强的容器隔离后,Docker Desktop用户可以继续将主机目录绑定安装到容器中(通过**设置**>**资源**>**文件共享**配置),但他们不再允许将任意Linux虚拟机目录绑定安装到容器中。

这可以防止容器修改 Docker Desktop Linux 虚拟机内的敏感文件,这些文件可能包含注册表访问管理、代理、Docker Engine 配置等的配置。

例如,以下将 Docker Engine 的配置文件 (Linux 虚拟机内的/etc/docker/daemon.json) 绑定挂载到容器的操作受到限制,因此会失败。

$ docker run -it --rm -v /etc/docker/daemon.json:/mnt/daemon.json alpine
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: error in the container spec: can't mount /etc/docker/daemon.json because it's configured as a restricted host mount: unknown

相反,如果没有启用增强型容器隔离,此挂载操作会成功,并允许容器完全读写访问 Docker Engine 的配置。

当然,主机文件的绑定挂载仍然照常工作。例如,假设用户配置 Docker Desktop 共享其$HOME目录,她可以将其绑定挂载到容器中。

$ docker run -it --rm -v $HOME:/mnt alpine
/ #

注意

默认情况下,增强型容器隔离不允许将 Docker Engine 套接字 (/var/run/docker.sock) 绑定挂载到容器中,因为这样做实际上赋予了容器对 Docker Engine 的控制权,从而破坏了容器隔离。但是,由于某些合法用例需要此功能,因此可以为受信任的容器镜像放宽此限制。请参阅 Docker 套接字挂载权限

审核敏感系统调用

增强型容器隔离的另一个功能是它会拦截并审查容器内的一些高度敏感的系统调用,例如mountumount。这确保具有执行这些系统调用能力的进程无法利用它们来破坏容器。

例如,拥有CAP_SYS_ADMIN能力的容器(需要执行mount系统调用)无法使用该能力将只读绑定挂载更改为读写挂载。

$ docker run -it --rm --cap-add SYS_ADMIN -v $HOME:/mnt:ro alpine
/ # mount -o remount,rw /mnt /mnt
mount: permission denied (are you root?)

由于$HOME目录已作为只读方式挂载到容器的/mnt目录中,因此即使容器进程具有执行此操作的能力,也无法从容器内部将其更改为读写模式。这确保容器进程无法使用mountumount来破坏容器的根文件系统。

但是请注意,在前面的示例中,容器仍然可以在容器内创建挂载点,并根据需要将其挂载为只读或读写。这些挂载是允许的,因为它们发生在容器内,因此不会破坏其根文件系统。

/ # mkdir /root/tmpfs
/ # mount -t tmpfs tmpfs /root/tmpfs
/ # mount -o remount,ro /root/tmpfs /root/tmpfs

/ # findmnt | grep tmpfs
├─/root/tmpfs    tmpfs      tmpfs    ro,relatime,uid=100000,gid=100000

/ # mount -o remount,rw /root/tmpfs /root/tmpfs
/ # findmnt | grep tmpfs
├─/root/tmpfs    tmpfs      tmpfs    rw,relatime,uid=100000,gid=100000

此功能与用户命名空间一起,确保即使容器进程拥有所有 Linux 功能,也无法用来破坏容器。

最后,增强型容器隔离以一种不会影响大多数情况下容器性能的方式执行系统调用审查。它拦截在大多数容器工作负载中很少使用的控制路径系统调用,但不拦截数据路径系统调用。

文件系统用户ID映射

如前所述,ECI 在所有容器上启用 Linux 用户命名空间。这确保容器的用户 ID 范围 (0->64K) 映射到 Docker Desktop Linux 虚拟机中未特权的“真实”用户 ID 范围 (例如,100000->165535)。

此外,每个容器在 Linux 虚拟机中获得一个独占的真实用户 ID 范围(例如,容器 0 可以映射到 100000->165535,容器 2 映射到 165536->231071,容器 3 映射到 231072->296607,依此类推)。组 ID 也适用相同规则。此外,如果容器停止并重新启动,则不能保证它会像以前一样获得相同的映射。这是设计使然,进一步提高了安全性。

但是,当将 Docker 卷挂载到容器中时,这会带来问题。写入此类卷的文件具有真实的 user/group-ID,因此由于每个容器不同的真实 user-ID/group-ID,在容器启动/停止/重启之间或容器之间将无法访问。

为了解决此问题,Sysbox 使用“文件系统用户 ID 重新映射”,方法是通过 Linux 内核的 ID 映射挂载功能(于 2021 年添加)或替代的shiftsfs模块。这些技术将文件系统访问从容器的真实用户 ID(例如,范围 100000->165535)映射到 Docker Desktop 的 Linux 虚拟机内的范围 (0->65535)。这样,即使每个容器使用独占的用户 ID 范围,也可以跨容器挂载或共享卷。用户无需担心容器的真实用户 ID。

尽管文件系统用户 ID 重新映射可能会导致容器使用真实用户 ID 0 访问挂载到容器中的 Linux 虚拟机文件,但受限挂载功能确保敏感的 Linux 虚拟机文件无法挂载到容器中。

Procfs和sysfs模拟

增强型容器隔离的另一个功能是,在每个容器内部,/proc/sys文件系统都进行了部分仿真。这有多种用途,例如隐藏容器内的敏感主机信息,以及为 Linux 内核本身尚未进行命名空间化的主机内核资源进行命名空间化。

一个简单的例子是,启用增强型容器隔离后,/proc/uptime文件显示的是容器本身的运行时间,而不是 Docker Desktop Linux 虚拟机的运行时间。

$ docker run -it --rm alpine
/ # cat /proc/uptime
5.86 5.86

相反,如果没有启用增强型容器隔离,您将看到 Docker Desktop Linux 虚拟机的运行时间。虽然这是一个简单的例子,但它显示了增强型容器隔离如何旨在防止 Linux 虚拟机的配置和信息泄漏到容器中,从而使其更难以破坏虚拟机。

此外,Linux 内核尚未进行命名空间化的/proc/sys下的其他一些资源也在容器内进行了仿真。每个容器都能看到每个此类资源的单独视图,并且 Sysbox 在对相应的 Linux 内核设置进行编程时会在容器之间协调这些值。

这具有以下优势:使原本需要真正特权容器才能访问此类非命名空间内核资源的容器工作负载能够在启用增强型容器隔离的情况下运行,从而提高安全性。