如何在 Kubernetes 中執行不受信任的容器

語言: CN / TW / HK

IT 世界每天都在越來越多地採用基於容器的基礎架構。但是,每個人都不清楚優點,缺點甚至侷限性。

考慮到即使是大公司也在靠近基於容器的基礎設施,但是可能的攻擊區域和資料洩露的潛在影響卻無人在意。

Docker(containerd)和 LXC 等技術並不是真正孤立的系統,因為它們與託管的作業系統共享相同的 Linux 核心。

對於潛在的攻擊者來說,在大公司內啟動他們的容器是一個千載難逢的機會。但容器技術自身能讓我們輕鬆自衛嗎?

當前的容器技術

已經重複了很多次,容器是一種打包、共享和部署應用程式的新方式,而不是所有功能都打包在一個軟體或作業系統中的單一應用程式。

目前,容器沒有利用任何新的東西,但它們是在 Linux 名稱空間和 cgroup 之上建立的演變。名稱空間建立了一個虛擬和隔離的使用者空間,併為應用程式提供其系統資源的隔離,例如檔案系統、網路和程序。這種抽象允許應用程式獨立啟動,而不會干擾在同一主機上執行的其他應用程式。

所以,多虧了名稱空間和 cgroup 的結合,我們絕對可以在一個隔離的環境中啟動許多在同一主機上執行的應用程式。

容器與虛擬機器

很明顯,與虛擬機器環境相比,容器技術解決了在隔離性、可移植性和精簡架構方面的問題。但我們不要忘記,虛擬機器允許我們隔離我們的應用程式,尤其是在核心級別,因此黑客逃離容器並破壞系統的風險遠高於逃離虛擬機器。

大多數 Linux 核心漏洞可能適用於容器,這可能允許它們升級和破壞受影響的名稱空間以及同一作業系統中的其他名稱空間。

這些安全問題導致研究人員嘗試從主機建立真正分離的名稱空間。具體稱為“沙盒”,現在有幾種解決方案可以提供這些功能:gVisor 或例如 Kata Containers。

Kubernetes 中的容器執行時

我們可以在容器編排器 Kubernetes 中更深入地研究這類技術。

Kubernetes 使用元件 kubelet 來管理容器。我們可以將其定義為負責提供給它的規範並準時準確地執行其操作的船長。

Kubelet 採用 pod 規範並使其在分配給它們的主機上作為容器執行,並且可以與任何容器執行時互動,只要它符合 OCI 標準(其實現是 RunC)

容器執行時的工作原理

RunC最初嵌入到Docker架構中,於 2015 年作為獨立工具釋出。它已成為 DevOps 團隊可以用作容器引擎的一部分的常用的、標準的、跨功能的容器執行時。

RunC 提供了與現有低階 Linux 特性互動的所有功能。它使用名稱空間和控制組來建立和執行容器程序。

在下面的段落中,我們將介紹執行時類和核心元素。還有一個 RuntimeClass 處理程式,其預設值為 RunC(對於使用 containerd 作為容器執行時的 Kubernetes 安裝)。

RuntimeClass

顧名思義,執行時類允許我們使用各種容器執行時進行操作。2014 年,Docker 是 Kubernetes 上唯一可用的執行時容器。從 Kubernetes 1.3 版開始,添加了與 Rocket (RKT) 的相容性,最後在 Kubernetes 1.5 中,引入了容器執行時 Iterface (CRI),它具有標準介面和所有容器執行時的可能性,您可以直接與此介面標準省去了開發者適應各類容器執行時的麻煩和擔心版本維護的麻煩。

事實上,CRI 允許我們將容器執行時部分與 Kubernetes 分離,最重要的是,允許 Kata Containers 和 gVisor 等技術以 containerd 的形式連線到容器執行時。

在 Kubernetes 1.14 中,RuntimeClass 再次作為內建叢集資源引入,其核心是處理程式屬性。

處理程式是指接收容器建立請求的程式,對應於容器執行時。

kind: RuntimeClass
apiVersion: node.k8s.io/v1
metadata:
name: #RuntimeClass Name
handler: #container runtime for example: runc
overhead:
podFixed:
memory: "" # 64Mi
cpu: "" # 250m
scheduling:
nodeSelector:
<key>: <value> # container-rt: gvisor
  • handler 欄位指向要使用的特定容器執行時或配置。

  • 宣告開銷允許叢集(包括排程程式)在做出有關 Pod 和資源的決策時考慮它。通過使用這些欄位,您可以使用此 RuntimeClass 指定執行 pod 的開銷,並確保在 Kubernetes 中考慮這些開銷。

  • 排程欄位用於確保 Pod 被排程在正確的節點上。

預設情況下,如果我們有一個帶有 Docker 或 containerd 的叢集,我們的處理程式是 runc,但如果我們使用 gVisor,它將是 runc。

在 Kubernetes 中使用 gVisor 隔離 Linux 主機和容器

現在我們將瞭解如何在 Kubernetes 叢集中擁有多個容器執行時,併為敏感工作負載選擇更嚴格的容器執行時。

在本教程中,我使用了之前的專案,在該專案中我使用 containerd 安裝了 Kubernetes 叢集。

https://github.com/alessandrolomanto/k8s-vanilla-containerd

初始化 Kubernetes 叢集

make vagrant-start

啟動機器後,驗證所有元件是否已啟動並執行

vagrant ssh master

kubectl get nodes
NAME      STATUS   ROLES                  AGE     VERSION

master Ready control-plane,master 7m59s v1.21.0

worker1 Ready <none> 5m50s v1.21.0

worker2 Ready <none> 3m51s v1.21.0

在 worker1 上安裝gVisor

ssh worker1 # Vagrant default password: vagrant

sudo su

安裝最新的 gVisor 版本

(
set -e
ARCH=$(uname -m)
URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
wget ${URL}/runsc ${URL}/runsc.sha512 \\
${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512
sha512sum -c runsc.sha512 \\
-c containerd-shim-runsc-v1.sha512
rm -f *.sha512
chmod a+rx runsc containerd-shim-runsc-v1
sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin
)
FINISHED --2022-04-28 07:24:44--

Total wall clock time: 5.2s

Downloaded: 4 files, 62M in 3.1s (20.2 MB/s)

runsc: OK

containerd-shim-runsc-v1: OK

配置容器執行時

cat <<EOF | sudo tee /etc/containerd/config.toml
version = 2
[plugins."io.containerd.runtime.v1.linux"]
shim_debug = true
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
EOF

重啟容器服務

sudo systemctl restart containerd

為 gVisor 安裝 RuntimeClass

cat <<EOF | kubectl apply -f -
apiVersion: node.k8s.io/v1beta1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
EOF

驗證:

[email protected]:~$ kubectl get runtimeclass

NAME HANDLER AGE

gvisor runsc 17s

使用 gVisor RuntimeClass 建立一個 Pod:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: nginx-gvisor
spec:
runtimeClassName: gvisor
containers:
- name: nginx
image: nginx
EOF

驗證 Pod 是否正在執行:

kubectl get pod nginx-gvisor -o wide
[email protected]:~$ kubectl get pod nginx-gvisor -o wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

nginx-gvisor 1/1 Running 0 31s 192.168.235.129 worker1 <none> <none>

有關更新資訊,請關注官方文件。 https://gvisor.dev/docs/user_guide/install/

結論

我們已經看到當前的容器技術存在弱隔離問題。快速修補容器和最低安全上下文特權等常見做法可以有效限制攻擊面。我們甚至應該開始像上面的教程那樣實施執行時安全措施,因為現在可能有多個容器執行時。

當然,這不是每個人都需要的東西,但是當您想要執行不受信任的容器而不以任何方式影響主機時,它肯定會派上用場。

假設你是一個容器託管服務,在同一臺主機上啟動不同客戶的容器。你會因為共享上下文而損害其他客戶嗎?開始思考如何緩解這些問題。

推薦

為什麼雲中的容器可以成為攻擊者的天堂

TeamTNT黑客組織以Kubernetes為目標,近50000個IP被攻擊

原創不易,隨手關注或者”在看“,誠摯感謝!