其他容器项目

基于虚拟化或者独立内核的安全容器项目。

  • Kata Containers的本质就是一个精简的轻量级虚拟机,所以它的特点,就是“像虚拟机一样安全,像容器一样敏捷”。
  • gVisor项目给容器进程配置一个用Go语言实现的,运行在用户态的、极小的“独立内核”。这个内核对容器进程暴露Linux内核ABI,扮演着“Guest Kernel”的角色,从而达到了将容器和宿主机隔离的目的。

这两种容器实现的本质,都是给进程分配一个独立的操作系统内核,从而避免了让容器共享宿主机的内核。这样,容器进程能够看到的攻击面,就从整个宿主机内核编程了一个极小的、独立的、以容器为单位的内核,从而有效解决了容器进程发生“逃逸”或者多去整个宿主机的控制权的问题

image

区别在于:

  1. Kata Container使用的是传统的虚拟化及时,通过虚拟硬件模拟出来了一台“小虚拟机”,然后在整个小虚拟机里安装一个裁剪后的Linux内核来实现强隔离。
  2. gVisor的做法更激进,直接使用Go语言模拟出了一个运行在用户态的操作系统内核,然后通过这个模拟的内核来代替容器进程想宿主机发起有限的、可控的系统调用。

0.1. 设计原理

0.1.1. Kata Containers

工作原理如下图所示:

image

Kata Containers的本质是一个轻量化虚拟机,所以启动一个Kata Containers之后,就会看到一个正常的虚拟机在运行,所以一个标准的虚拟机管理程序(VMM,Virtual Machine Manager)是运行Kata Containers必备的一个组件,如图中的Qemu。

使用了虚拟机作为进程的隔离环境之后,Kata Containers原生就带有Pod的概念,这个Kata Containers启动的虚拟机就是一个pod,用户定义的容器,就是运行在这个轻量级虚拟机里的进程。

在具体实现上,Kata Container的虚拟机里会有一个特殊的Init进程负责管理虚拟机里面的用户容器,并且职位这些容器开启Mount Namespace,所以这些用户容器直接,原生就是共享Network以及其他Namespace的。

为了跟上层编排框架如kubernetes对接,Kata Containers项目会启动一系列跟用户容器对应的shim进程,来负责操作这些用户容器的生命周期。这些操作实际上还是要靠虚拟机里的Init进程来帮忙做到。

具体的架构上,Kata Containers的实现方式同一个正常的虚拟机非常类似,原理如下图所示:

image

当Kata Containers运行起来之后,虚拟机里的用户进程(容器),实际上只能看到虚拟机里的、被裁剪过的Guest Kernel,已经通过Hypervisor虚拟出来的硬件设备。为了能够对这个虚拟机的I/O性能进行优化,Kata Containers也会通过vhost技术(如vhost-user)来实现Guest和Host之间高效的网络通信,并且使用PCI Passthrough(PCI 穿透)技术来让Guest里的进程直接访问宿主机上的物理设备。这些架构设计与实现,其实与常规的虚拟机优化手段基本一致。

0.1.2. gVisor

相比于Kata Containers,gVisor的设计更激进,原理如下图所示:

image

gVisor工作的核心在于它为应用进程、也就是用户容器,启动一个名叫Sentry的进程。而Sentry进程的主要职责,就是提供一个传统的操作系统内核的能力,运行用户程序,执行系统调用。Sentry并不是使用Go语言重新实现了一个完整的Linux内核,只是一个多应用进程“冒充”内核的系统组件。

在这种设计思想下,Sentry需要自己实现一个完整的Linux 内核网络栈,以便处理应用进程的通信请求,然后把封装好的二层帧直接发送给kubernetes设置的Pod的Network Namespace即可。

Sentry对于Volume的操作,需要通过9P协议交给Gofer代理进程来完成。Gofer会代替应用进程直接操作数组局上的文件,并依靠seccomp机制将自己的能力现在在最小集,从而防止恶意应用进程通过Gofer来从容器中“逃逸”出去。

具体实现上,gVisor的Sentry进程有两种不同的实现方式,如下图所示:

image

0.1.2.1. 方式一

使用Ptrace机制来拦截用户应用的系统调用(system call),然后把这些系统调用交给Sentry来进行处理。这个过程对应用进程来说是完全透明的。

Sentry接下来就扮演操作系统的角色,在用户态执行用户程序,然后仅在需要的时候,才想宿主机发起Sentry自己所需要执行的系统调用。这就是gVisor对用户应用进程进行强隔离的主要手段。不过Ptrace进行系统拦截的性能太差,仅供demo使用。

0.1.2.2. 方式二

这种方式更具有普适性,如下图所示:

image

在这个实现里,Sentry使用KVM进行系统调用的拦截,这个性能比Ptrace好很多,为了能够做到这一点,Sentry进程必须扮演Guset Kernel的角色,负责执行用户程序,发起系统调用。而这些系统调用被KVM拦截后,还要继续交给Sentry进行处理,只是这个时候,Sentry就切换成了一个普通的宿主机进程的角色,来向宿主机发起它所需要的系统调用。

在这种实现里,Sentry并不会真的像虚拟机那样去虚拟出硬件设备、安装Guest操作系统,它只是借助KVM进行系统调用的拦截,以及处理地址空间切换等细节

0.1.3. Firecracker

Firecracker安全容器项目。这个项目的核心是一个用Rust语言编写的VMM(虚拟机管理器),所有Firecracker和Kata Containers的本质是一样的,不过Kata Containers默认使用的VMM是Qemu,而Firecracker则是自己编写的VMM。所以理论上Kata Containers也可以使用Firecracker运行起来。

0.2. 对比

类别性能启动速度和占用资源系统调用支持
KataContainers差不多强一点(系统调用密集的应用,如重I/O,重网络的应用)
gVisor(基于KVM)差不多强一点(基于用户态内核),频繁拦截系统调用会出现性能急剧下降的情况低(使用Sentry模拟内核,只是Linux系统调用的一个子集)

gVisor这种在用户态运行一个操作系统内核,来为应用进程提供强隔离的思路,是未来安全容器进一步演化的一个非常有前途的方向。gVisor使用User Mode Linux(UML)技术在用户态运行起了一个真正的Linux Kernel来为应用进程提供强隔离,从而避免了重新实现Linux Kernel带来的各种麻烦。这个方向应该是安全容器进化的未来,比Unikernels这种不适合实际场景中使用的思路靠谱。

上次修改: 14 April 2020