修復 Kubernetes 叢集中的漏洞。 DevOpsConf 的報告和文字記錄

南橋解決方案架構師兼 Slurm 教師 Pavel Selivanov 在 DevOpsConf 2019 上發表了演講。本次演講是 Kubernetes 深入課程「Slurm Mega」主題之一的一部分。

Slurm 基礎:Kubernetes 簡介 18 月 20 日至 XNUMX 日在莫斯科舉行。
Slurm Mega:深入了解 Kubernetes — 莫斯科,22 月 24 日至 XNUMX 日。
Slurm Online:兩門 Kubernetes 課程 始終可用。

剪輯下方是報告的文字記錄。

同事們以及所有同情他們的人們都在下午好。 今天我要講的是安全。

我看到今天大廳裡有很多保全。 如果我使用的安全領域術語與您的習慣不完全一樣,我會提前向您道歉。

大約六個月前,我碰巧遇到了一個公共 Kubernetes 集群。 公共意味著有第 n 個命名空間;在這些命名空間中,有在其命名空間中隔離的使用者。 所有這些用戶都屬於不同的公司。 嗯,假設這個群集應該用作 CDN。 也就是說,他們給你一個集群,給你一個用戶,你去那裡你的命名空間,部署你的前端。

我以前的公司試著銷售這樣的服務。 我被要求戳一下集群,看看這個解決方案是否合適。

我來到了這個集群。 我被賦予了有限的權利和有限的命名空間。 那裡的人明白什麼是安全。 他們了解了 Kubernetes 中基於角色的存取控制 (RBAC),並對其進行了扭曲,這樣我就無法從部署中單獨啟動 Pod。 我不記得我試圖透過啟動一個沒有部署的 Pod 來解決什麼問題,但我真的只想啟動一個 Pod。 為了好運,我決定看看我在集群中擁有哪些權利,我能做什麼,不能做什麼,以及他們在那裡搞砸了什麼。 同時,我會告訴你他們在 RBAC 中配置錯誤的地方。

碰巧的是,在兩分鐘內,我收到了他們集群的管理員,查看了所有相鄰的命名空間,看到了已經購買了服務並部署的公司正在運行的生產前端。 我幾乎無法阻止自己走到某人的前面並在主頁上發表一些髒話。

我將透過範例告訴您我是如何做到這一點以及如何保護自己免受這種情況的侵害。

但首先,讓我先介紹一下我自己。 我叫帕維爾·塞利瓦諾夫。 我是南橋的建築師。 我了解 Kubernetes、DevOps 和各種奇特的東西。 我和南橋工程師正在建造這一切,我正在提供諮詢服務。

除了我們的主要活動之外,我們最近還啟動了名為 Slurms 的專案。 我們正在努力將我們使用 Kubernetes 的能力帶給大眾,教會其他人也使用 K8s。

今天我要講什麼? 報告的主題很明顯——關於 Kubernetes 叢集的安全性。 但我想說的是,這個主題非常大——因此我想立即澄清我絕對不會談論的內容。 我不會談論那些已經在網路上使用過上百次的陳腔濫調術語。 各種 RBAC 和證書。

我將討論 Kubernetes 叢集中的安全性讓我和我的同事感到痛苦的地方。 我們在提供 Kubernetes 叢集的提供者和來找我們的客戶中都看到了這些問題。 甚至來自其他顧問管理公司的客戶。 也就是說,這場悲劇的規模其實是非常大的。

今天我主要講三點:

  1. 使用者權限與 Pod 權限。 使用者權限和 Pod 權限不是一回事。
  2. 收集有關集群的資訊。 我將展示您可以從集群中收集所需的所有信息,而無需在此集群中擁有特殊權限。
  3. 對叢集的 DoS 攻擊。 如果我們無法收集訊息,無論如何我們都可以放置一個集群。 我將討論針對叢集控制元素的 DoS 攻擊。

我要提到的另一件事是我對所有這些進行了測試,我可以肯定地說這一切都有效。

我們以使用 Kubespray 安裝 Kubernetes 叢集為基礎。 如果有人不知道的話,這實際上是 Ansible 的一組角色。 我們在工作中經常使用它。 好處是你可以把它滾到任何地方——你可以把它滾到鐵片上或雲中的某個地方。 原則上,一種安裝方法適用於所有情況。

在這個叢集中,我將擁有 Kubernetes v1.14.5。 我們將考慮的整個 Cube 叢集被分割為多個命名空間,每個命名空間屬於一個單獨的團隊,而團隊的成員可以存取每個命名空間。 他們不能去不同的命名空間,只能去他們自己的命名空間。 但有一個管理員帳戶擁有整個叢集的權限。

修復 Kubernetes 叢集中的漏洞。 DevOpsConf 的報告和文字記錄

我承諾我們要做的第一件事就是獲得叢集的管理員權限。 我們需要一個專門準備的 pod 來破壞 Kubernetes 叢集。 我們需要做的就是將其應用到 Kubernetes 叢集中。

kubectl apply -f pod.yaml

該 Pod 將到達 Kubernetes 叢集的主節點之一。 此後,叢集將愉快地向我們傳回一個名為 admin.conf 的檔案。 在Cube中,這個檔案儲存了所有管理員證書,同時配置了叢集API。 我認為,98% 的 Kubernetes 叢集的管理員存取權限就是如此簡單。

我再說一遍,這個 pod 是由叢集中的一位開發人員創建的,他有權將他的建議部署到一個小型命名空間中,它全部由 RBAC 限制。 他沒有權利。 但無論如何,證書還是被退回了。

現在介紹一個專門準備的豆莢。 我們在任何圖像上運行它。 我們以 debian:jessie 為例。

我們有這個東西:

tolerations:
-   effect: NoSchedule 
    operator: Exists 
nodeSelector: 
    node-role.kubernetes.io/master: "" 

什麼是寬容? Kubernetes 叢集中的 master 通常會被標記為「污點」。 而這個「感染」的本質就是說pod不能分配給master節點。 但沒有人願意在任何豆莢中表明它對「感染」具有耐受性。 容忍部分只是說,如果某個節點有 NoSchedule,那麼我們的節點可以容忍這種感染 - 並且沒有問題。

進一步說,我們的下屬不僅要寬容,還要專門針對主人。 因為大師們擁有我們需要的最美味的東西——所有的證書。 因此,我們說 nodeSelector - 我們在 master 上有一個標準標籤,它允許您從叢集中的所有節點中準確地選擇那些作為 master 的節點。

有了這兩段,他就一定能到大師的境界了。 他將被允許住在那裡。

但僅僅來到大師身邊對我們來說還不夠。 這不會給我們任何東西。 接下來我們有兩件事:

hostNetwork: true 
hostPID: true 

我們指定我們啟動的 pod 將位於核心命名空間、網路命名空間和 PID 命名空間。 一旦 pod 在 master 上啟動,它將能夠看到該節點的所有真實的、即時的接口,偵聽所有流量並查看所有進程的 PID。

然後就是一些小事了。 拿起 etcd 並閱讀你想要的內容。

最有趣的是 Kubernetes 功能,它預設存在。

volumeMounts:
- mountPath: /host 
  name: host 
volumes:
- hostPath: 
    path: / 
    type: Directory 
  name: host 

它的本質是,我們可以在我們啟動的 pod 中說,即使沒有這個叢集的權限,我們想要建立一個 hostPath 類型的磁碟區。 這意味著從我們將啟動的主機獲取路徑 - 並將其作為磁碟區。 然後我們稱之為名稱:主機。 我們將整個 hostPath 掛載到 pod 內。 在此範例中,到 /host 目錄。

我再重複一遍。 我們告訴 pod 來到 master,取得那裡的 hostNetwork 和 hostPID - 並將 master 的整個根目錄掛載到這個 pod 中。

你知道在 Debian 中我們運行著 bash,並且這個 bash 在 root 下運行。 也就是說,我們只是在 master 上取得了 root 權限,而 Kubernetes 叢集中沒有任何權限。

那麼整個任務就是進入子目錄/host /etc/kubernetes/pki,如果我沒記錯的話,在那裡取得叢集的所有主證書,並相應地成為叢集管理員。

如果您這樣看,這些是 pod 中最危險的一些權限 - 無論用戶擁有什麼權限:
修復 Kubernetes 叢集中的漏洞。 DevOpsConf 的報告和文字記錄

如果我有權利在叢集的某個命名空間中執行某個 pod,那麼該 pod 預設就具有這些權限。 我可以運行特權 Pod,這些通常都是所有權限,實際上是節點上的 root。

我最喜歡的是 Root 用戶。 Kubernetes 有這個 Run As Non-Root 選項。 這是一種針對駭客的保護。 你知道什麼是「摩爾達維亞病毒」嗎? 如果你突然成為駭客並來到我的 Kubernetes 集群,那麼我們這些可憐的管理員會問:「請在你的 Pod 中指明你將使用哪個 Pod 來攻擊我的集群,以非 root 身份運行。 否則,如果你在 root 下運行你的 pod 中的進程,你就很容易黑我。 請保護好自己,遠離自己。”

在我看來,主機路徑磁碟區是從 Kubernetes 叢集取得所需結果的最快方法。

但這一切該怎麼辦呢?

任何遇到 Kubernetes 的普通管理員都應該想到:「是的,我告訴過你,Kubernetes 不起作用。 裡面有洞。 整個立方體都是廢話。” 事實上,有文檔這樣的東西,如果你看那裡,有一個部分 Pod 安全策略.

這是一個 yaml 物件 - 我們可以在 Kubernetes 叢集中建立它 - 它專門在 pod 的描述中控制安全方面。 也就是說,事實上,它控制著啟動時 pod 中的任何 hostNetwork、hostPID、某些磁碟區類型的使用權限。 借助 Pod 安全策略,這一切都可以被描述。

關於 Pod 安全性原則最有趣的事情是,在 Kubernetes 叢集中,所有 PSP 安裝程式不僅沒有以任何方式進行描述,而且預設只是停用的。 Pod 安全性策略是使用准入插件啟用的。

好的,讓我們將 Pod 安全策略部署到叢集中,假設我們在命名空間中有一些服務 Pod,只有管理員可以存取這些服務 Pod。 假設在所有其他情況下,pod 的權利有限。 因為很可能開發人員不需要在叢集中執行特權 Pod。

我們似乎一切都很好。 而且我們的 Kubernetes 叢集不可能在兩分鐘內被駭客攻擊。

有一個問題。 如果您有 Kubernetes 集群,那麼很可能會在您的集群上安裝監控功能。 我甚至預測,如果你的叢集有監控,它將被稱為 Prometheus。

我要告訴您的內容對於 Prometheus 操作員和以其純粹形式交付的 Prometheus 都有效。 問題是,如果我不能這麼快讓管理員進入集群,那麼這意味著我需要進行更多檢查。 我可以藉助你的監控進行搜尋。

可能每個人都讀過關於 Habré 的相同文章,並且監控位於監控命名空間中。 Helm Chart 對每個人來說叫法都大致相同。 我猜如果你安裝了 stable/prometheus,你最終會得到大致相同的名稱。 而且很可能我甚至不必猜測叢集中的 DNS 名稱。 因為它是標準的。

修復 Kubernetes 叢集中的漏洞。 DevOpsConf 的報告和文字記錄

接下來我們有一個特定的 dev ns,您可以在其中執行特定的 pod。 然後從這個 Pod 可以輕鬆執行以下操作:

$ curl http://prometheus-kube-state-metrics.monitoring 

prometheus-kube-state-metrics 是 Prometheus 導出器之一,它從 Kubernetes API 本身收集指標。 那裡有很多數據,你的叢集中正在運行什麼,它是什麼,你遇到了什麼問題。

舉個簡單的例子:

kube_pod_container_info{namespace=“kube-system”,pod=“kube-apiserver-k8s-1”,container=“kube-apiserver”,image=

“gcr.io/google-containers/kube-apiserver:v1.14.5”

,image_id=»docker-pullable://gcr.io/google-containers/kube- apiserver@sha256:e29561119a52adad9edc72bfe0e7fcab308501313b09bf99df4a96 38ee634989″,container_id=»docker://7cbe7b1fea33f811fdd8f7e0e079191110268f2 853397d7daf08e72c22d3cf8b»} 1

透過從非特權 Pod 發出簡單的捲曲請求,您可以獲得以下資訊。 如果您不知道正在運行哪個版本的 Kubernetes,它會很容易地告訴您。

最有趣的是,除了造訪 kube-state-metrics 之外,您還可以輕鬆地直接存取 Prometheus 本身。 您可以從那裡收集指標。 您甚至可以從那裡建立指標。 即使從理論上講,您也可以從 Prometheus 中的叢集建立這樣的查詢,這只會將其關閉。 並且您的叢集監控將完全停止工作。

這裡出現的問題是是否有任何外部監控監控您的監控。 我剛剛獲得了在 Kubernetes 叢集中進行操作的機會,並且沒有給自己帶來任何後果。 你甚至不知道我在那裡工作,因為不再有任何監控。

就像 PSP 一樣,問題在於所有這些花哨的技術——Kubernetes、Prometheus——它們根本不起作用,而且漏洞百出。 並不真地。

有這麼一件事—— 網路政策.

如果您是普通管理員,那麼您很可能知道網路策略,這只是另一個 yaml,叢集中已經有很多這樣的 yaml。 而且有些網路策略是絕對不需要的。 即使你讀到 Network Policy 是什麼,它是 Kubernetes 的 yaml 防火牆,它允許你限制命名空間之間、pod 之間的存取權限,那麼你肯定會認為 Kubernetes 中 yaml 格式的防火牆是基於下一個抽象…不,不。 這絕對是沒有必要的。

即使您沒有告訴安全專家,使用 Kubernetes,您也​​可以建立一個非常簡單且非常細粒度的防火牆。 如果他們還不知道這一點並且不打擾您:「好吧,給我,給我...」那麼無論如何,您需要網路策略來阻止對某些可以從叢集中拉出的服務位置的存取未經任何授權。

正如我給出的範例所示,您可以從 Kubernetes 叢集中的任何命名空間提取 kube 狀態指標,而無需任何權限。 網路策略已關閉從所有其他命名空間到監視命名空間的訪問,就是這樣:沒有訪問,沒有問題。 在所有現有的圖表中,無論是標準的 Prometheus 或 Operator 中的 Prometheus,helm 值中都只有一個選項來簡單地為它們啟用網路策略。 您只需將其打開,它們就會工作。

這裡確實有一個問題。 作為一個普通的留著鬍子的管理員,您很可能認為不需要網路策略。 在閱讀了有關 Habr 等資源的各種文章後,您認為 flannel(尤其是主機閘道模式)是您可以選擇的最佳選擇。

怎麼辦呢?

您可以嘗試重新部署 Kubernetes 叢集中的網路解決方案,嘗試將其替換為更強大的解決方案。 例如,對於同一個印花布。 但我想說,在 Kubernetes 工作集群中更改網路解決方案的任務非常重要。 我解決了兩次(不過,都是理論上的),但我們甚至在 Slurms 中展示瞭如何做到這一點。 對於我們的學生,我們展示瞭如何更改 Kubernetes 叢集中的網路解決方案。 原則上可以盡量保證生產集群不宕機。 但你可能不會成功。

而問題其實解決起來也很簡單。 集群中有證書,並且您知道您的證書將在一年後過期。 好吧,通常是在集群中使用證書的正常解決方案 - 我們為什麼要擔心,我們會在附近建立一個新集群,讓舊集群腐爛,然後重新部署所有內容。 確實,當它腐爛時,我們將不得不坐一天,但這是一個新的簇。

當你提出一個新的叢集時,同時插入 Calico 而不是 flannel。

如果您的憑證已頒發一百年並且您不打算重新部署叢集怎麼辦? 有一種叫做 Kube-RBAC-Proxy 的東西。 這是一個非常酷的開發,它允許您將自己作為 sidecar 容器嵌入到 Kubernetes 叢集中的任何 pod 中。 而它實際上是透過Kubernetes本身的RBAC來為這個pod添加授權的。

有一個問題。 先前,這個 Kube-RBAC-Proxy 解決方案內建於營運商的 Prometheus 中。 但後來他就走了。 現在,現代版本依賴您擁有網路策略並使用它們關閉它的事實。 因此我們必須稍微重寫一下圖表。 事實上,如果你去 這個儲存庫,有如何使用它作為 sidecar 的範例,並且圖表將必須最少地重寫。

還有一個小問題。 Prometheus 並不是唯一一個向任何人提供其指標的公司。 我們所有的 Kubernetes 叢集元件也能夠回傳自己的指標。

但正如我已經說過的,如果你無法訪問集群並收集信息,那麼你至少會造成一些傷害。

因此,我將快速展示兩種破壞 Kubernetes 叢集的方法。

當我告訴你這件事時,你會笑,這是兩個現實生活中的案例。

方法一。 資源枯竭。

讓我們啟動另一個特殊的 Pod。 它將有一個這樣的部分。

resources: 
    requests: 
        cpu: 4 
        memory: 4Gi 

如您所知,請求數是主機上為具有請求的特定 Pod 保留的 CPU 和記憶體量。 如果我們在 Kubernetes 叢集中有四核心主機,並且有四個 CPU Pod 帶著請求到達那裡,這意味著沒有更多帶有請求的 Pod 能夠到達該主機。

如果我運行這樣的 pod,那麼我將運行以下命令:

$ kubectl scale special-pod --replicas=...

那麼其他人將無法部署到 Kubernetes 叢集。 因為所有節點都會耗盡請求。 因此我將停止您的 Kubernetes 叢集。 如果我在晚上這樣做,我可以停止部署很長一段時間。

如果我們再看一下 Kubernetes 文檔,我們會看到這個叫做 Limit Range 的東西。 它為集群物件設定資源。 你可以在 yaml 中寫一個 Limit Range 對象,將其應用到某些命名空間 - 然後在這個命名空間中你可以說你有 pod 的預設、最大和最小資源。

借助這樣的東西,我們可以限制團隊特定產品命名空間中的用戶在其 Pod 上指示各種令人討厭的事情的能力。 但不幸的是,即使你告訴用戶他們無法啟動請求多個 CPU 的 pod,但有一個如此美妙的縮放命令,或者他們可以透過儀表板進行縮放。

這就是第二種方法的由來。 我們發射了 11 個 Pod。 那是一百一十億。 這並不是因為我想出了這樣的數字,而是因為我親眼所見。

真實的故事。 傍晚時分,我正要離開辦公室。 我看到一群開發人員坐在角落裡,瘋狂地用筆記型電腦做些事情。 我走到他們面前問:“你們怎麼了?”

早些時候,晚上九點左右,一位開發商正準備回家。 我決定:“我現在將我的應用程式縮小到一個。” 我按了一下,但網速有點慢。 他又按了一個,又按了一個,然後按下了Enter鍵。 我盡我所能地探尋一切。 然後網路誕生了——一切都開始縮小到這個數字。

確實,這個故事並不是發生在 Kubernetes 上;當時是 Nomad。 結果是,在我們嘗試阻止 Nomad 持續嘗試擴容一個小時後,Nomad 回答說他不會停止擴容,也不會做任何其他事情。 “我累了,我走了。” 他蜷縮起來。

當然,我也嘗試在 Kubernetes 上做同樣的事情。 Kubernetes 對 1 億個 Pod 並不滿意,他說:「我不能。 超過內部護齒套。” 但 000 個 Pod 就可以。

為了應對十億,立方體並沒有退出。 他真的開始擴展了。 這個過程越深入,他創造新豆莢所需的時間就越多。 但這個過程仍在繼續。 唯一的問題是,如果我可以在我的命名空間中無限制地啟動pod,那麼即使沒有請求和限制,我也可以啟動許多帶有某些任務的pod,在這些任務的幫助下,節點將開始在記憶體和CPU 中累加。 當我啟動這麼多 Pod 時,它們的資訊應該進入存儲,即 etcd。 當太多資訊到達那裡時,儲存開始返回太慢——Kubernetes 開始變得遲鈍。

還有一個問題...如您所知,Kubernetes 控制元素不是一個核心元素,而是多個元件。 特別是有控制器管理器、調度器等。 所有這些人都會同時開始做不必要的、愚蠢的工作,隨著時間的推移,這些工作將開始花費越來越多的時間。 控制器管理器將建立新的 Pod。 調度程序將嘗試為它們尋找新的節點。 您很可能很快就會用完叢集中的新節點。 Kubernetes 叢集的工作速度將開始變得越來越慢。

但我決定走得更遠。 如你所知,在 Kubernetes 中有一種叫做服務的東西。 嗯,預設情況下,在您的叢集中,該服務很可能使用 IP 表來運作。

例如,如果您執行 XNUMX 億個 pod,然後使用腳本強制 Kubernetes 建立新服務:

for i in {1..1111111}; do
    kubectl expose deployment test --port 80  
        --overrides="{"apiVersion": "v1", 
           "metadata": {"name": "nginx$i"}}"; 
done 

在叢集的所有節點上,越來越多的新 iptables 規則將幾乎同時產生。 而且,每個服務都會產生XNUMX億條iptables規則。

我檢查了幾千個,最多十個。 問題是,已經達到這個閾值,對節點進行 ssh 就很成問題了。 因為數據包經過如此多的鏈條,開始感覺不太好。

而這一切也都是在 Kubernetes 的幫助下解決的。 有這樣一個資源配額對象。 設定集群中命名空間的可用資源和物件的數量。 我們可以在 Kubernetes 叢集的每個命名空間中建立一個 yaml 物件。 使用這個對象,我們可以說我們為這個命名空間分配了一定數量的請求和限制,然後我們可以說在這個命名空間中可以創建 10 個服務和 10 個 Pod。 一個開發人員至少在晚上會窒息。 Kubernetes 會告訴他:“你無法將 pod 擴展到這個數量,因為資源超出了配額。” 就這樣,問題解決了。 文件在這裡.

在這方面出現了一個問題。 你會感覺到在 Kubernetes 中創造命名空間變得多麼困難。 為了創建它,我們需要考慮很多事情。

資源配額+限制範圍+RBAC
• 建立命名空間
• 在內部建立一個限制範圍
• 建立內部資源配額
• 為 CI 建立服務帳戶
• 為 CI 和使用者建立角色綁定
• 選擇性地啟動必要的服務容器

因此,我想藉此機會分享我的進展。 有一個東西叫SDK操作符。 這是 Kubernetes 叢集為其編寫算子的一種方式。 您可以使用 Ansible 編寫語句。

一開始是用Ansible寫的,後來看到有SDK算子,就把Ansible角色改寫成了算子。 該語句可讓您在 Kubernetes 叢集中建立一個稱為命令的物件。 在命令內部,它允許您在 yaml 中描述該命令的環境。 在團隊環境中,它允許我們描述我們正在分配如此多的資源。

小一點 讓整個複雜的過程變得更容易.

並得出結論。 這一切該怎麼辦?
第一的。 Pod 安全策略很好。 儘管事實上到目前為止還沒有 Kubernetes 安裝程式使用它們,但您仍然需要在叢集中使用它們。

網路策略不僅僅是另一個不必要的功能。 這才是集群真正需要的。

LimitRange/ResourceQuota - 是時候使用它了。 我們很久以前就開始使用它,很長一段時間我確信每個人都在使用它。 事實證明,這種情況很少見。

除了我在報告中提到的內容之外,還有一些未記錄的功能可以讓您攻擊叢集。 最近發布 對 Kubernetes 漏洞的廣泛分析.

有些事情是那麼的悲傷和傷人。 例如,在某些情況下,Kubernetes 叢集中的 Cubelet 可以將 warlocks 目錄的內容提供給未經授權的使用者。

這裡 有關於如何重現我告訴你的一切的說明。 有一些檔案包含 ResourceQuota 和 Pod 安全策略的生產範例。 你可以觸摸這一切。

謝謝大家。

來源: www.habr.com

添加評論