容器安全
gVisor简介与实战
gVisor的功能并不是来取代docker,而是像一个docker的低级运行时,它跟普通的container有点不同,你的app并不是真正的像一个主机的进程在运行,gVisor将应用程序加载到它自己的应用程序内存空间中并从那里运行,所以当你ps查看进程时,你不会看到有容器进程存在,你只能看到runsc进程。----Lan Lewis
gVisor是Google开源的有关容器安全的组件,是用Go语言编写的用户空间内核,它实现了大部分的Linux系统调用命令,包含一个叫了runsc的OCI运行时,目的是提供应用程序与主机内核之间的边界隔离,runsc运行时已经与Docker和Kubernetes做了集成,使运行沙箱容器sandbox变得很简单。
与已有的sandbox技术相比,gVisor采用了一种特殊的方式,并且在技术上做了权衡,从而为容器安全领域提供了新的工具和创意。
为什么是gVisor?
容器不是sandbox,尽管容器已经改变了我们开发,打包,部署应用程序的方式,但是在没有隔离的环境中运行不受信任的或者潜在的恶意代码是有风险的。
gVisor是容器的用户空间内核,它限制了应用程序直接访问主机内核,同时能保持应用程序所期望的访问结果。与大多数内核不同,gVisor不承担或需要一组固定的物理资源,相反,它利用现有的主机内核功能并作为普通的用户空间进程运行。换句话说,gVisor通过Linux实现Linux。
不应将gVisor与那些以加强容器抗外部威胁,提供额外的完整性检查或限制服务访问范围的技术和工具混淆,我们应该时刻注意哪些数据可用于容器。
gVisor与其他容器隔离机制有何不同?
通常采用两种其他方法提供比原始容器更强的隔离。
硬件虚拟化(如KVM和Xen),通过虚拟机监视器(VMM)将虚拟化的硬件暴露给guest内核,这种虚拟化硬件通常是透明的(半虚拟化的),并且可以使用额外的机制来改善访客和主机之间的可见性(例如气球驱动器,准虚拟化螺旋锁),在不同的虚拟机中运行容器可以提供很好的隔离性,兼容性和性能(尽管嵌套虚拟化可能会在此领域带来挑战),但对于容器,它通常需要额外的代理,并且可能需要占用更大的资源空间和更慢的启动时间。
image基于规则的执行(如seccomp,SELinux和AppArmor)允许为应用程序或容器指定细粒度的安全策略,这些方案通常依靠主机内核中的钩子来执行规则,如果接口足够小(即定义了足够完整的策略),那么这是sandbox应用程序和保持本机性能的绝佳方式,然而,实际上,能任意为未知的应用程序可靠地定义策略是非常困难的,因此使得该方法难以应用。
imagegVisor提供了与上述不同的第三种隔离机制。
gVisor拦截应用程序系统调用,并充当访客内核,无需通过虚拟化硬件进行翻译,gVisor可能被认为是合并的客户内核和VMM。这种架构允许它提供灵活的资源占用(即基于线程和内存映射的资源占用,而不是固定的客户物理资源占用),同时降低虚拟化的固有成本。但是,这也降低了应用程序兼容性、增大了的系统调用开销。
image除此之外,gVisor使用基于规则的执行来提供纵深防御(详情如下)
gVisor的方法类似于用户态Linux(UML),尽管UML在内部对硬件进行了虚拟化,因此提供了固定的资源占用空间。
上述方法中的每一种都有可能在不同的场景变现出优越的性能,例如,硬件虚拟化会面临实现高密度的挑战,而gVisor可能会在系统调用繁重的工作负载时表现出较差的性能。
gVisor是用Go编写的,目的是为了避免可能困扰内核的安全缺陷。使用Go,有强类型,内置边界检查,没有未初始化的变量,没有免费使用,没有堆栈溢出和内置竞争检测器。 (Go的使用也有其挑战,而且不是免费的。)
gVisor拦截应用程序所发出的所有系统调用请求,为应用程序请求返回相应的结果,重要的是,gVisor并不是简单地将应用程序的系统调用重定向到主机内核,相反,gVisor实现了大多数内核机制(信号,文件系统,futexes,管道),并在这些机制之上构建了完整的系统调用处理程序。由于gVisor本身就是一个用户空间应用程序,因此它会进行一些主机系统调用来支持其操作,但很像VMM,它不会允许应用程序直接控制它所做的系统调用。
文件系统访问
为了提供纵深防御并限制主机系统调用接口,gVisor容器运行时通常分为两个独立的进程。首先,Sentry进程包含内核并负责执行用户代码和处理系统调用。其次,扩展到沙箱之外的文件系统操作(不是内部proc或tmp文件,管道等)将通过9P连接发送到代理,称为Gofer。
imageGofer充当文件系统代理,代表应用程序打开主机文件,并将它们传递给Sentry进程,Sentry进程本身没有主机文件访问。此外,Sentry在一个空的用户空间中运行,并且使用seccomp过滤器限制gVisor对主机进行的系统调用,以提供纵深防御。
网络系统访问
Sentry实现了自己的网络堆栈(也用Go编写),称为netstack。网络堆栈的所有方面都在Sentry内部处理 - 包括TCP连接状态,控制消息和数据包组装 - 使其与主机网络堆栈保持隔离。数据链路层数据包直接写入由Docker或Kubernetes设置的网络名称空间内的虚拟设备。网络直通模式也被支持,但其代价是减少了隔离(见下文)。
平台
Sentry需要一个平台来实现基本的上下文切换和内存映射功能。现在,gVisor支持两种平台:
Ptrace平台使用SYSEMU功能来执行用户代码,而无需执行主机系统调用。这个平台可以在ptrace运行的任何地方运行(即使没有嵌套虚拟化的虚拟机)。
KVM平台(实验)允许Sentry充当来宾操作系统和VMM,在两个世界之间无缝切换。 KVM平台可以在裸机上运行,也可以在启用了嵌套虚拟化的VM上运行。虽然没有虚拟化硬件层 - 沙箱保留了一个流程模型 - gVisor利用现代处理器上提供的虚拟化扩展,以改善地址空间交换机的隔离和性能。
性能
影响性能的因素有这几个方面,平台选择是是最直接的最大的影响,具体取决于具体的工作负载。没有最好的平台:Ptrace可以普遍使用,包括VM实例,但应用程序的执行可能只是其原始级别的一小部分。除了平台选择之外,直通模式可能会有助于以某些隔离为代价来提高性能。
安装
gVisor只能在x86_64 Linux 3.17+上运行。另外,gVisor仅支持沙箱内的x86_64二进制文件(不能运行32位二进制文件)。
下载最新的build
下载runsc的链接:here
wget https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc
chmod +x runsc
sudo mv runsc /usr/local/bin
配置docker使用runsc
在/etc/docker/daemon.json中参加配置如下,如果没有这个文件则需要新建。
{
"runtimes": {
"runsc": {
"path": "/usr/local/bin/runsc"
}
}
}
重启docker
sudo systemctl restart docker
查看帮助文档
root@hm-alone:~# runsc --help
Usage: runsc <flags> <subcommand> <subcommand args>
Subcommands:
create create a secure container
delete delete resources held by a container
events display container events such as OOM notifications, cpu, memory, and IO usage statistics
exec execute new process inside the container
flags describe all known top-level flags
gofer launch a gofer process that server files over 9P protocol (internal use only)
help describe subcommands and their syntax
kill sends a signal to the sandbox
list list contaners started by runsc with the given root
ps ps displays the processes running inside a container
run create and run a secure container
start start a secure container
state get the state of a sandbox
Subcommands for internal use only:
boot launch a sandbox process (internal use only)
gofer launch a gofer process that server files over 9P protocol (internal use only)
Use "runsc flags" for a list of top-level flags
创建container
root@hm-alone:~# docker run --runtime=runsc -it ubuntu /bin/bash
root@fbab7608cfb1:/#
root@hm-alone:~# ps aux | grep runsc
root 4561 0.0 0.1 240660 18440 pts/0 Sl+ 05:17 0:00 docker run --runtime=runsc -it ubuntu /bin/bash
root 4723 0.0 0.0 7520 3064 ? Sl 05:17 0:00 docker-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/fbab7608cfb18d3a2b82151ed9ea26355a3621a8736086a8ccee4213d2659356 -address /var/run/docker/containerd/docker-containerd.sock -containerd-binary /usr/bin/docker-containerd -runtime-root /var/run/docker/runtime-runsc
root 4855 0.0 0.0 19812 9128 ? Sl 05:17 0:00 /usr/local/bin/runsc --log=/run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/fbab7608cfb18d3a2b82151ed9ea26355a3621a8736086a8ccee4213d2659356/log.json --log-format=json --root=/var/run/docker/runtime-runsc/moby gofer --bundle /var/run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/fbab7608cfb18d3a2b82151ed9ea26355a3621a8736086a8ccee4213d2659356 --io-fds=3 --io-fds=4 --io-fds=5 --io-fds=6
root 4861 1.4 0.1 58732 18372 pts/1 Ssl+ 05:17 0:00 /usr/local/bin/runsc --log=/run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/fbab7608cfb18d3a2b82151ed9ea26355a3621a8736086a8ccee4213d2659356/log.json --log-format=json --root=/var/run/docker/runtime-runsc/moby boot --bundle /var/run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/fbab7608cfb18d3a2b82151ed9ea26355a3621a8736086a8ccee4213d2659356 --controller-fd=3 --console=true --io-fds=4 --io-fds=5 --io-fds=6 --io-fds=7
root 4876 0.0 0.0 4 4 pts/1 tl+ 05:17 0:00 [runsc]
root 5344 0.0 0.0 5688 592 pts/1 tl+ 05:17 0:00 [runsc]
root 5364 0.0 0.0 4 4 pts/1 tl+ 05:17 0:00 [runsc]
root 5368 0.0 0.0 4 4 pts/1 tl+ 05:17 0:00 [runsc]
root 5373 0.0 0.0 4 4 pts/1 tl+ 05:17 0:00 [runsc]
root 8080 0.0 0.0 14512 936 pts/2 S+ 05:18 0:00 grep --color=auto runsc
使用kubernetes
gVisor可以使用cri-o在Kubernetes集群中运行沙盒容器,但目前还不推荐用于生产环境。按照这些说明在Kubernetes集群中的节点上运行cri-o。构建runsc并将其放在节点上,并将其设置为/etc/crio/crio.conf
中的runtime_untrusted_workload
。没有加io.kubernetes.crio.TrustedSandbox
注释(或注释为false)的任何Pod将与runsc一起运行。目前,gVisor仅支持具有单个容器的Pod(不包括永久存在的暂停容器)。单个Pod中支持多个容器即将推出。