主要特点和优点
所有容器上的 Linux 用户命名空间
通过增强的容器隔离,所有用户容器都利用 Linux 用户命名空间 来实现额外的隔离。这意味着容器中的 root 用户映射到 Docker Desktop Linux VM 中的非特权用户。
例如:
$ docker run -it --rm --name=first alpine
/ # cat /proc/self/uid_map
0 100000 65536
输出0 100000 65536
是 Linux 用户命名空间的签名。这意味着容器中的 root 用户 (0) 映射到 Docker Desktop Linux VM 中的非特权用户 100000,并且映射扩展到连续的 64K 用户 ID 范围。这同样适用于组 ID。
每个容器都有一个专有的映射范围,由 Sysbox 管理。例如,如果启动第二个容器,则映射范围会有所不同:
$ docker run -it --rm --name=second alpine
/ # cat /proc/self/uid_map
0 165536 65536
相反,如果没有增强容器隔离,容器的根用户实际上是主机上的根用户(也称为“真正的根”),这适用于所有容器:
$ docker run -it --rm alpine
/ # cat /proc/self/uid_map
0 0 4294967295
通过使用 Linux 用户命名空间,增强容器隔离可确保容器进程永远不会在 Linux VM 中以用户 ID 0(真正的 root)身份运行。事实上,它们从未在 Linux VM 中使用任何有效的用户 ID 运行。因此,它们的 Linux 功能仅限于容器内的资源,与常规容器相比,显着提高了隔离性,包括容器到主机的隔离和跨容器的隔离。
特权容器也受到保护
特权容器docker run --privileged ...
是不安全的,因为它们赋予容器对 Linux 内核的完全访问权限。也就是说,容器以真正的 root 身份运行,并启用所有功能,禁用 seccomp 和 AppArmor 限制,所有硬件设备都会公开。
对于希望在开发人员计算机上保护 Docker Desktop 的组织来说,特权容器是有问题的,因为它们允许容器工作负载(无论是良性还是恶意)获得对 Docker Desktop VM 内 Linux 内核的控制,从而修改安全相关设置,例如注册表访问管理和网络代理。
通过增强容器隔离,特权容器将无法再执行此操作。 Sysbox 使用的 Linux 用户命名空间和其他安全技术相结合,确保特权容器内的进程只能访问分配给该容器的资源。
笔记
增强的容器隔离不会阻止用户启动特权容器,而是通过确保他们只能修改与容器关联的资源来安全地运行它们。修改全局内核设置(例如加载内核模块或更改 BPF 设置)的特权工作负载将无法正常工作,因为它们在尝试此类操作时将收到“权限被拒绝”错误。
例如,增强容器隔离可确保特权容器无法访问通过 Berkeley 数据包过滤器 (BPF) 配置的 Linux VM 中的 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 VM 共享命名空间
启用增强容器隔离后,容器无法与主机共享 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 build--network=host
和 Docker buildx 权利(network.host
、 )。security.insecure
需要这些的构建将无法正常工作。
绑定挂载限制
启用增强容器隔离后,Docker Desktop 用户可以继续将挂载主机目录绑定到通过设置> 资源>文件共享 配置的容器中,但不再允许他们将任意 Linux 虚拟机目录绑定挂载到容器中。
这可以防止容器修改 Docker Desktop Linux VM 内的敏感文件、可以保存注册表访问管理配置、代理、docker 引擎配置等的文件。
例如,将 Docker 引擎的配置文件(/etc/docker/daemon.json
在 Linux VM 内部)绑定到容器中的以下操作受到限制,因此会失败:
$ 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 套接字安装权限。
审查敏感系统调用
增强容器隔离的另一个功能是它拦截并审查容器内的一些高度敏感的系统调用,例如mount
和
umount
。这确保了有能力执行这些系统调用的进程不能使用它们来破坏容器。
例如,具有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
目录中,因此无法在容器内将其更改为读写,即使容器进程有能力这样做。这可确保容器进程无法使用mount
、 或umount
来破坏容器的根文件系统。
但请注意,在上面的示例中,容器仍然可以在容器内创建安装,并根据需要将它们安装为只读或读写。这些挂载是允许的,因为它们发生在容器内,因此不会破坏它的根文件系统:
/ # 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 VM 中“真实”用户 ID 的非特权范围(例如,100000->165535)。
此外,每个容器在 Linux VM 中都获得唯一的真实用户 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 VM 内的范围(0->65535)。这样,即使每个容器使用独占的用户 ID 范围,现在也可以在容器之间安装或共享卷。用户无需担心容器的真实用户 ID。
请注意,虽然文件系统用户 ID 重新映射可能会导致容器访问使用真实用户 ID 0(即 root)挂载到容器中的 Linux VM 文件,但 上述限制挂载功能可确保没有 Linux VM 敏感文件可以挂载到容器中。容器。
Procfs 和 Sysfs 仿真
增强容器隔离的另一个功能是在每个容器内,部分模拟 procfs(“/proc”)和 sysfs(“/sys”)文件系统。这有几个目的,例如将敏感主机信息隐藏在容器内,以及为尚未由 Linux 内核本身命名的主机内核资源命名。
举一个简单的例子,当启用增强容器隔离时,
/proc/uptime
文件显示容器本身的正常运行时间,而不是 Docker Desktop Linux VM 的正常运行时间:
$ docker run -it --rm alpine
/ # cat /proc/uptime
5.86 5.86
相比之下,如果没有增强容器隔离,您将看到 Docker Desktop Linux VM 的正常运行时间。尽管这是一个简单的示例,但它展示了增强容器隔离如何旨在防止 Linux VM 的配置和信息泄漏到容器中,从而使破坏 VM 变得更加困难。
此外,/proc/sys
容器内还模拟了其他一些不受 Linux 内核命名的资源。每个容器都会看到每个此类资源的单独视图,并且 Sysbox 在对相应的 Linux 内核设置进行编程时会协调容器之间的值。
这样做的优点是,容器工作负载需要真正特权的容器来访问此类非命名空间内核资源,以便在启用增强容器隔离的情况下运行,从而提高安全性。