关键功能和优势

所有容器上的 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 用户命名空间,增强型容器隔离确保容器进程永远不会以用户 ID 0(真实 root)的形式运行在 Linux 虚拟机中。事实上,它们永远不会以 Linux 虚拟机中任何有效的用户 ID 运行。因此,它们的 Linux 功能限制在容器内的资源,与常规容器相比,隔离度显著提高,包括容器到主机和跨容器隔离。

特权容器也已得到保护

特权容器 docker run --privileged ... 不安全,因为它们赋予容器对 Linux 内核的完全访问权限。也就是说,容器以真实 root 身份运行,并启用所有功能,禁用 seccomp 和 AppArmor 限制,公开所有硬件设备,例如。

对于希望在其开发人员机器上保护 Docker Desktop 的组织来说,特权容器存在问题,因为它们允许容器工作负载(无论良性还是恶意)获取对 Docker Desktop 虚拟机中 Linux 内核的控制权,从而修改安全相关的设置,例如注册表访问管理和网络代理。

通过增强型容器隔离,特权容器不再能够做到这一点。Linux 用户命名空间与 Sysbox 使用的其他安全技术的组合确保特权容器内的进程只能访问分配给该容器的资源。

注意

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

例如,增强型容器隔离确保特权容器无法访问通过 Berkeley Packet Filters (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 引擎配置等的配置。

例如,将 Docker 引擎的配置文件(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 引擎配置的完全读写访问权限。

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

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

注意

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

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

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

但是,在将 Docker 卷挂载到容器中时,上述情况会带来问题,因为写入此类卷的文件将具有真实的用户/组 ID,因此在容器的启动/停止/重新启动期间或容器之间将无法访问,因为每个容器的真实用户 ID/组 ID 不同。

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

请注意,尽管文件系统用户 ID 重新映射可能会导致容器以真实用户 ID 0(即 root)的形式访问挂载到容器中的 Linux 虚拟机文件,但上面描述的 受限挂载功能 确保不会将任何 Linux 虚拟机敏感文件挂载到容器中。

Procfs & Sysfs 模拟

增强容器隔离的另一个特性是在每个容器内部部分模拟了 procfs("/proc")和 sysfs("/sys")文件系统。这可以实现几个目的,例如隐藏容器内部敏感的主机信息,以及对 Linux 内核本身尚未命名空间的主机内核资源进行命名空间。

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

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

相反,在没有启用增强容器隔离的情况下,你将看到 Docker Desktop Linux 虚拟机的运行时间。虽然这是一个简单的例子,但它展示了增强容器隔离如何防止 Linux 虚拟机的配置和信息泄露到容器中,从而更难入侵虚拟机。

此外,在 /proc/sys 下,一些 Linux 内核尚未命名空间的资源,也会在容器内部被模拟。每个容器都看到这些资源的不同视图,而 Sysbox 会在对相应的 Linux 内核设置编程时,协调容器之间的这些值。

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