【Kubernetes安全系列第三篇】kubernetes叢集元件的安全
【Kubernetes安全系列第三篇】kubernetes叢集元件的安全
在《保護Kubernetes for Cloud Native Applications》系列的前一篇文章中,我們討論了在保護部署Kubernetes叢集的基礎設施時需要考慮的因素。 這一次,我們將注意力轉移到叢集本身。

Kubernetes是一個複雜的系統,上圖顯示了構成一個叢集的許多不同的組成部分。 為了保持叢集的整體完整性,需要仔細保護每一個元件中。
在本文中,我們無法涵蓋叢集級安全性的各個方面,但我們的目標是解決更重要的主題。 正如我們稍後將看到的,可以從更廣泛的社群獲得幫助,包括Kubernetes叢集的最佳實踐安全性,以及衡量對最佳實踐的遵守情況的工具。
叢集安裝
我們應該首先簡要觀察一下可以用來安裝叢集元件的許多不同工具。
從安全形度來看,Kubernetes叢集元件的某些預設配置引數是次優的,需要正確設定以確保安全叢集。 除非您選擇託管的Kubernetes叢集(例如Giant Swarm提供的叢集),代表您管理整個叢集,否則許多不同的叢集安裝工具會加劇這個問題,每個工具都會有一些微妙的不同配置。 雖然大多數安裝程式都提供了合理的預設設定,但我們絕不應該認為它們在安全性方面有所支援,我們的目標應該是確保我們選擇使用哪種安裝程式機制,並將其配置為匹配我們的要求。
讓我們來看一下控制平面安全性的一些重要方面。
API Server
API伺服器是群集中所有通訊的中心,它位於應用了大部分群集安全配置的API伺服器上。 API伺服器是群集控制平面的唯一元件,它能夠直接與群集的狀態儲存進行互動。 操作叢集,其他控制平面元件以及有時叢集工作負載的使用者都使用伺服器的基於HTTP的REST API與叢集互動。
由於其在叢集控制中的關鍵作用,因此就安全性而言,仔細管理對API伺服器的訪問至關重要。 如果某人或某些人獲得了對API的未經請求的訪問許可權,他們就有可能獲得各種敏感資訊,並獲得對叢集本身的控制權。 因此,客戶端對Kubernetes API的訪問應該進行加密,驗證和授權。
使用TLS進行安全通訊
為防止中間人攻擊,應使用TLS對每個客戶端和API伺服器之間的通訊進行加密。 為此,需要使用私鑰和X.509證書配置API伺服器。
頒發API伺服器證書的根證書頒發機構(CA)的X.509證書,必須可供在TLS握手期間需要向API伺服器進行身份驗證的任何客戶端使用,這導致我們遇到叢集的證書頒發機構的問題。 一般來說,正如我們稍後將看到的,客戶端可以通過多種方式對API伺服器進行身份驗證,其中一種方法是通過X.509證書。 如果採用這種客戶端身份驗證方法,在大多數情況下(至少對於群集元件)可能都是如此,每個叢集元件都應該獲得自己的證書,建立叢集範圍的PKI功能很有意義。
有許多方法可以為叢集實現PKI功能,沒有說一種方法比另一種更好。 它可以手動配置,可以由您選擇的安裝程式配置,或通過其他方式配置。 實際上,可以將叢集配置為具有自己的內建CA,該CA可以發出證書以響應通過API伺服器提交的證書籤名請求。 在Giant Swarm,我們使用名為cert-operator的運算子與Hashicorp的Vault一起使用。
雖然我們的主題是與API伺服器進行安全通訊,但請務必禁用其不安全的埠(在Kubernetes 1.13之前),該埠通過普通HTTP(--insecure-port = 0)提供API!
身份驗證,授權和准入控制
現在讓我們將注意力轉移到控制哪些客戶端可以對叢集中的哪些資源執行哪些操作。 我們在這裡不會詳細介紹,總的來說,這是下一篇文章的主題。 重要的是確保控制平面的元件配置為提供底層訪問控制。

當API請求落在API伺服器上時,它會執行一系列檢查以確定是否為請求提供服務,則是根據定義的策略驗證是為請求提供服務還是改變資源物件, 執行鏈如上圖所示。
Kubernetes支援許多不同的身份驗證方案,這些方案几乎總是在叢集外部實現,包括X.509證書,基本身份驗證,bearer令牌,用於通過可信身份提供商進行身份驗證的OpenID Connect(OIDC)等。 使用API伺服器上的相關配置選項可以啟用多種方案,因此請務必為計劃使用的身份驗證方案提供這些選項。 例如,X.509客戶端證書身份驗證需要包含一個或多個CA證書( -client-ca-file)檔案路徑。 需要記住的一點是,預設情況下,任何未通過其中一種身份驗證方案驗證的API請求都會被視為匿名請求。 雖然匿名請求獲得的訪問許可權可能受到授權的限制,但如果不需要,則應完全關閉它們(--anonymous-auth = false)。
驗證請求後,API伺服器會根據授權策略考慮請求。 同樣,授權模式是一個配置選項(--authorization-mode),至少應該從預設值AlwaysAllow進行更改。 理想情況下,授權模式列表應包括RBAC和Node,前者用於啟用RBAC API以進行細粒度訪問控制,後者用於授權kubelet API請求(見下文)。
一旦API請求經過身份驗證和授權,資源物件在使用admission controller持久儲存到叢集的狀態資料庫之前可能會經過驗證或改變。 建議使用最少的admission controller,除非有充分的理由,否則不應從列表中刪除。 額外的安全相關的admission controllers值得考慮:
- DenyEscalatingExec:如果必須允許您的pod以增強的許可權執行(例如使用主機的IPC/PID名稱空間),則此admission controller將阻止使用者在pod的特權容器中執行命令。
- PodSecurityPolicy:為所有建立的pod提供應用各種安全機制的方法。 我們將在本系列的下一篇文章中對此進行進一步討論,但是現在確保啟用此admission controller非常重要,否則我們的安全策略將無法應用。
- NodeRestriction:用於管理kubelet必須的叢集資源的訪問許可權,下面將詳細介紹。
- ImagePolicyWebhook - 允許為pod的容器定義的映象,通過外部“映象驗證器”(例如Image Enforcer)檢查漏洞。 Image Enforcer基於Open Policy Agent(OPA),與開源漏洞掃描程式Clair配合使用。
Dynamic admission control
是Kubernetes中相對較新的功能,相對靜態外掛化admission control機制提供更大的靈活性。 它是通過admission webhooks和基於控制器的初始化實現的,並且只要社群解決方案達到足夠成熟的水平,就可以為叢集安全做出很大貢獻。
Kubelet
kubelet是在叢集中的每個節點上執行的代理,負責它所在節點上的所有與pod相關的活動,包括啟動/停止和重新啟動pod容器,報告pod容器的執行狀況等等。 在API伺服器之後,當涉及到安全性時,kubelet是下一個要考慮的最重要的叢集元件。
訪問Kubelet REST API
kubelet在埠10250和10255上提供小型REST API。埠10250是讀/寫埠,而10255是具有API端點子集的只讀埠。
提供對埠10250的不受限制的訪問是危險的,因為可以在pod的容器內執行任意命令,以及啟動任意pod。 同樣,這兩個埠都提供對有關pod及其容器的潛在敏感資訊的讀訪問權,這可能會使工作負載容易受到攻擊。
為了防止潛在的危害,應通過設定kubelet的配置 --read-only-port=0
來禁用只讀埠。 但是,埠10250需要可用於度量收集和其他重要功能。 應仔細控制對此埠的訪問,因此我們將討論關鍵的安全配置。
客戶端認證
除非特別配置,否則kubelet API對來自客戶端的未經身份驗證的請求是開放的。因此,配置一種可用的身份驗證方法非常重要; X.509客戶端證書,或具有包含bearer令牌的授權頭的請求。
對於X.509客戶端證書,需要使CA bundle的內容對kubelet可用,以便它可以在TLS握手期間驗證客戶端提供的證書。這是作為kubelet配置(--client-ca-file)的一部分提供的。
在理想的世界中,唯一需要訪問kubelet API的客戶端是Kubernetes API伺服器。它需要訪問kubelet的API端點以獲取各種功能,例如收集日誌和指標,在容器中執行命令(想想kubectl exec),將埠轉發到容器等等。為了通過kubelet對其進行身份驗證,需要使用客戶端TLS憑據(--kubelet-client-certificate和--kubelet-client-key)配置API伺服器。
匿名認證
如果您已經注意並配置了API伺服器對kubelet API的訪問許可權,那麼您可能會認為已經“完成工作”了。 但事實並非如此,因為任何觸發kubelet的API的請求都不會嘗試使用kubelet進行身份驗證,這被視為匿名請求。 預設情況下,kubelet會傳遞匿名請求以進行授權,而不是將其作為未經身份驗證而拒絕。
如果在您的環境中允許匿名的kubelet API請求是必不可少的,那麼就有了授權門,它可以靈活地確定API能夠和不能提供什麼服務。 但是,通過將kubelet的--anonymous-auth配置設定為false,完全禁止匿名API請求會更安全。 通過這樣的配置,API向未經授權的客戶端返回401 Unauthorized響應。
授權
通過授權對kubelet API的請求,再一次可能會違反預設的Kubernetes設定。 對kubelet API的授權以兩種模式之一執行; AlwaysAllow(預設)或Webhook。 AlwaysAllow模式完全符合您的預期 - 它將允許所有通過身份驗證門的請求成功,這也包括匿名請求。
最好的方法是使用kubelet的--authorization-mode配置選項和webhook值,將授權決策轉移到Kubernetes API伺服器,而不是將其開啟。 使用此配置,kubelet呼叫SubjectAccessReview API(它是API伺服器的一部分)以確定是否允許主體發出請求。
限制Kubelet的力量
在較舊版本的Kubernetes(1.7之前)中,即使Node和Pod物件受另一個節點上執行的另一個kubelet的控制,該kubelet也具有對所有Node和Pod API物件的讀寫訪問許可權。 他們還可以讀取pod規範中包含的所有物件:Secret,ConfigMap,PersistentVolume和PersistentVolumeClaim物件。 換句話說,一個kubelet可以訪問和控制它不負責的眾多資源。 這非常強大,並且在叢集節點受損的情況下,損壞可能會迅速升級到相關節點之外。
節點授權
出於這個原因,專門為kubelet引入了節點授權模式,目的是控制其對Kubernetes API的訪問。 節點授權器限制kubelet讀取與kubelet相關的那些物件上的操作(例如pod,節點,服務),並對Secrets,Configmap,PersistentVolume和PersistentVolumeClaim物件應用進一步的只讀限制,這些物件與 pod繫結到執行kubelet的節點。
NodeRestriction Admission Controller
將kubelet限制為與其相關的物件的只讀訪問許可權是防止受損叢集或工作負載的重要一步。但是,kubelet需要對其Node和Pod物件進行寫訪問,以此作為其正常功能的一種方式。為了實現這一點,一旦kubelet的API請求通過節點授權,它就會受到NodeRestriction Admission Controller的約束,該控制器限制了kubelet可以修改的Node和Pod物件(它自己的)。為此,kubelet使用者必須是 system:node:<nodeName>
,它必須屬於 system:nodes
組。當然,它是kubelet使用者的nodeName元件,NodeRestriction Admission Controller使用它來允許或禁止修改Node和Pod物件的kubelet API請求。接下來,每個kubelet應具有唯一的X.509證書,用於向API伺服器進行身份驗證,subject的Common Name表示使用者,而組織則表示該組。
同樣,這些重要的配置不會自動發生,並且API伺服器需要以Node作為--authorization-mode配置選項的逗號分隔外掛列表之一啟動,而NodeRestriction需要在列表中由--enable-admission-plugins選項指定的Admission Controller。
最佳實踐
特別需要強調的是我們只覆蓋了叢集層的安全考慮因素的子集(儘管是重要的),如果你認為這聽起來非常令人生畏,那就不要擔心,因為有幫助在手。
與為基礎架構層的元件(如Docker)建立基準安全建議的方式相同,這個建議也適用於Kubernetes叢集。網際網路安全中心(CIS)已為叢集的每個元件編制了一套完整的配置設定和檔案系統檢查,並以CIS Kubernetes Benchmark的形式釋出。
您可能也有興趣知道Kubernetes社群已經開發了一個開源工具,用於稽核Kubernetes叢集與基準測試Kubernetes Bench for Security。它是一個Golang應用程式,支援許多不同的Kubernetes版本(1.6以上),以及不同版本的基準測試。
如果您認真對待叢集的正確保護,那麼必須使用基準作為合規性的衡量標準。
總結
顯而易見,採取預防措施來保護具有適當配置的叢集對於保護叢集中執行的工作負載至關重要。 雖然Kubernetes社群非常努力地提供所有必要的安全控制來實現其安全性,但由於歷史原因,一些預設配置忽略了最佳實踐。 如果我們忽視這些缺點,將會帶來危險,所以我們必須在建立叢集或者當升級到提供新功能的新版本時,由我們自己負責來處理這些缺點以縮小差距。
我們在這裡討論的一些內容為下一層鋪平了道路,我們利用配置的安全機制來定義和應用安全控制,以保護在叢集上執行的工作負載。 下一篇文章的名字叫做《Kubernetes叢集安全最佳實踐》。