gVisor簡介與實戰(zhàn)
gVisor的功能并不是來取代docker,而是像一個docker的低級運行時,它跟普通的container有點不同,你的app并不是真正的像一個主機的進程在運行,gVisor將應用程序加載到它自己的應用程序內存空間中并從那里運行,所以當你ps查看進程時,你不會看到有容器進程存在,你只能看到runsc進程。----Lan Lewis
gVisor是Google開源的有關容器安全的組件,是用Go語言編寫的用戶空間內核,它實現(xiàn)了大部分的Linux系統(tǒng)調用命令,包含一個叫了runsc的OCI運行時,目的是提供應用程序與主機內核之間的邊界隔離,runsc運行時已經(jīng)與Docker和Kubernetes做了集成,使運行沙箱容器sandbox變得很簡單。
與已有的sandbox技術相比,gVisor采用了一種特殊的方式,并且在技術上做了權衡,從而為容器安全領域提供了新的工具和創(chuàng)意。
為什么是gVisor?
容器不是sandbox,盡管容器已經(jīng)改變了我們開發(fā),打包,部署應用程序的方式,但是在沒有隔離的環(huán)境中運行不受信任的或者潛在的惡意代碼是有風險的。
gVisor是容器的用戶空間內核,它限制了應用程序直接訪問主機內核,同時能保持應用程序所期望的訪問結果。與大多數(shù)內核不同,gVisor不承擔或需要一組固定的物理資源,相反,它利用現(xiàn)有的主機內核功能并作為普通的用戶空間進程運行。換句話說,gVisor通過Linux實現(xiàn)Linux。
不應將gVisor與那些以加強容器抗外部威脅,提供額外的完整性檢查或限制服務訪問范圍的技術和工具混淆,我們應該時刻注意哪些數(shù)據(jù)可用于容器。
gVisor與其他容器隔離機制有何不同?
通常采用兩種其他方法提供比原始容器更強的隔離。
硬件虛擬化(如KVM和Xen),通過虛擬機監(jiān)視器(VMM)將虛擬化的硬件暴露給guest內核,這種虛擬化硬件通常是透明的(半虛擬化的),并且可以使用額外的機制來改善訪客和主機之間的可見性(例如氣球驅動器,準虛擬化螺旋鎖),在不同的虛擬機中運行容器可以提供很好的隔離性,兼容性和性能(盡管嵌套虛擬化可能會在此領域帶來挑戰(zhàn)),但對于容器,它通常需要額外的代理,并且可能需要占用更大的資源空間和更慢的啟動時間。
基于規(guī)則的執(zhí)行(如seccomp,SELinux和AppArmor)允許為應用程序或容器指定細粒度的安全策略,這些方案通常依靠主機內核中的鉤子來執(zhí)行規(guī)則,如果接口足夠?。炊x了足夠完整的策略),那么這是sandbox應用程序和保持本機性能的絕佳方式,然而,實際上,能任意為未知的應用程序可靠地定義策略是非常困難的,因此使得該方法難以應用。
gVisor提供了與上述不同的第三種隔離機制。
gVisor攔截應用程序系統(tǒng)調用,并充當訪客內核,無需通過虛擬化硬件進行翻譯,gVisor可能被認為是合并的客戶內核和VMM。這種架構允許它提供靈活的資源占用(即基于線程和內存映射的資源占用,而不是固定的客戶物理資源占用),同時降低虛擬化的固有成本。但是,這也降低了應用程序兼容性、增大了的系統(tǒng)調用開銷。
除此之外,gVisor使用基于規(guī)則的執(zhí)行來提供縱深防御(詳情如下)
gVisor的方法類似于用戶態(tài)Linux(UML),盡管UML在內部對硬件進行了虛擬化,因此提供了固定的資源占用空間。
上述方法中的每一種都有可能在不同的場景變現(xiàn)出優(yōu)越的性能,例如,硬件虛擬化會面臨實現(xiàn)高密度的挑戰(zhàn),而gVisor可能會在系統(tǒng)調用繁重的工作負載時表現(xiàn)出較差的性能。
gVisor是用Go編寫的,目的是為了避免可能困擾內核的安全缺陷。使用Go,有強類型,內置邊界檢查,沒有未初始化的變量,沒有免費使用,沒有堆棧溢出和內置競爭檢測器。 (Go的使用也有其挑戰(zhàn),而且不是免費的。)
gVisor攔截應用程序所發(fā)出的所有系統(tǒng)調用請求,為應用程序請求返回相應的結果,重要的是,gVisor并不是簡單地將應用程序的系統(tǒng)調用重定向到主機內核,相反,gVisor實現(xiàn)了大多數(shù)內核機制(信號,文件系統(tǒng),futexes,管道),并在這些機制之上構建了完整的系統(tǒng)調用處理程序。由于gVisor本身就是一個用戶空間應用程序,因此它會進行一些主機系統(tǒng)調用來支持其操作,但很像VMM,它不會允許應用程序直接控制它所做的系統(tǒng)調用。
文件系統(tǒng)訪問
為了提供縱深防御并限制主機系統(tǒng)調用接口,gVisor容器運行時通常分為兩個獨立的進程。首先,Sentry進程包含內核并負責執(zhí)行用戶代碼和處理系統(tǒng)調用。其次,擴展到沙箱之外的文件系統(tǒng)操作(不是內部proc或tmp文件,管道等)將通過9P連接發(fā)送到代理,稱為Gofer。
Gofer充當文件系統(tǒng)代理,代表應用程序打開主機文件,并將它們傳遞給Sentry進程,Sentry進程本身沒有主機文件訪問。此外,Sentry在一個空的用戶空間中運行,并且使用seccomp過濾器限制gVisor對主機進行的系統(tǒng)調用,以提供縱深防御。
網(wǎng)絡系統(tǒng)訪問
Sentry實現(xiàn)了自己的網(wǎng)絡堆棧(也用Go編寫),稱為netstack。網(wǎng)絡堆棧的所有方面都在Sentry內部處理 - 包括TCP連接狀態(tài),控制消息和數(shù)據(jù)包組裝 - 使其與主機網(wǎng)絡堆棧保持隔離。數(shù)據(jù)鏈路層數(shù)據(jù)包直接寫入由Docker或Kubernetes設置的網(wǎng)絡名稱空間內的虛擬設備。網(wǎng)絡直通模式也被支持,但其代價是減少了隔離(見下文)。
平臺
Sentry需要一個平臺來實現(xiàn)基本的上下文切換和內存映射功能?,F(xiàn)在,gVisor支持兩種平臺:
Ptrace平臺使用SYSEMU功能來執(zhí)行用戶代碼,而無需執(zhí)行主機系統(tǒng)調用。這個平臺可以在ptrace運行的任何地方運行(即使沒有嵌套虛擬化的虛擬機)。
KVM平臺(實驗)允許Sentry充當來賓操作系統(tǒng)和VMM,在兩個世界之間無縫切換。 KVM平臺可以在裸機上運行,??也可以在啟用了嵌套虛擬化的VM上運行。雖然沒有虛擬化硬件層 - 沙箱保留了一個流程模型 - gVisor利用現(xiàn)代處理器上提供的虛擬化擴展,以改善地址空間交換機的隔離和性能。
性能
影響性能的因素有這幾個方面,平臺選擇是是最直接的最大的影響,具體取決于具體的工作負載。沒有最好的平臺:Ptrace可以普遍使用,包括VM實例,但應用程序的執(zhí)行可能只是其原始級別的一小部分。除了平臺選擇之外,直通模式可能會有助于以某些隔離為代價來提高性能。
安裝
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
創(chuàng)建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集群中運行沙盒容器,但目前還不推薦用于生產(chǎn)環(huán)境。按照這些說明在Kubernetes集群中的節(jié)點上運行cri-o。構建runsc并將其放在節(jié)點上,并將其設置為/etc/crio/crio.conf中的runtime_untrusted_workload。沒有加io.kubernetes.crio.TrustedSandbox注釋(或注釋為false)的任何Pod將與runsc一起運行。目前,gVisor僅支持具有單個容器的Pod(不包括永久存在的暫停容器)。單個Pod中支持多個容器即將推出。