Kubernetes准入控制器指南

Kubernetes准入控制器指南

作者:Malte Isberner(StackRox)

Kubernetes極大地提高了當今生產中後端羣集的速度和可管理性。由於其靈活性、可擴展性和易用性,Kubernetes已成爲容器編排器的事實標準。Kubernetes也提供一系列保護生產工作負載的功能。安全功能的最新引入是一組稱爲“准入控制器”的插件。必須啓用准入控制器才能使用Kubernetes的一些更高級的安全功能,例如,在整個命名空間中強制實施安全配置基線的pod安全政策。以下必須知道的提示和技巧,將幫助你利用准入控制器,在Kubernetes中充分利用這些安全功能。

什麼是Kubernetes准入控制器?

簡而言之,Kubernetes准入控制器是管理和強制執行集羣使用方式的插件。可以將它們視爲攔截(經過身份驗證的)API請求的網守,並且可以更改請求對象,或完全拒絕請求。准入控制過程有兩個階段:首先執行改變(mutating)階段,然後是驗證(validating)階段。因此,准入控制器可以充當改變或驗證控制器,或兩者的組合。例如,LimitRanger准入控制器可以使用默認資源請求和限制(改變階段)擴充pod,並驗證具有設置資源要求的pod,不超過LimitRange對象中指定的每命名空間限制(驗證階段)。


准入控制器階段

值得注意的是,許多用戶認爲是內置的Kubernetes操作的某些方面,實際上由准入控制器管理。例如,當刪除命名空間並隨後進入Terminating狀態時,NamespaceLifecycle准入控制器將阻止在此命名空間中創建任何新對象。

在Kubernetes附帶的30多個准入控制器中,有兩個因其幾乎無限的靈活性而發揮特殊作用 - ValidatingAdmissionWebhooks和MutatingAdmissionWebhooks,兩者在Kubernetes 1.13都處於beta狀態。我們將仔細研究這兩個准入控制器,因爲它們本身並沒有實現任何政策決策邏輯。相反,相應的操作是從集羣內運行的服務的REST端點(webhook)獲得的。這種方法將准入控制器邏輯與Kubernetes API服務器分離,從而允許用戶在Kubernetes集羣中創建、更新或刪除資源時實現自定義邏輯。

兩種准入控制器webhooks之間的差異幾乎是不言自明的:改變(mutating)准入webhooks可能會改變對象,而驗證(validating)准入webhooks則不會。然而,即使是改變准入webhook也可以拒絕請求,從而以驗證的方式行事。驗證入場webhooks比改變webhooks有兩個主要優點:首先,出於安全原因,可能需要禁用MutatingAdmissionWebhook准入控制器(或對誰可能創建MutatingWebhookConfiguration對象應用更嚴格的RBAC限制),因爲它可能會產生混淆,甚至危險的副作用。其次,如上圖所示,驗證准入控制器(以及webhooks)在改變控制器之後運行。因此,驗證webhook看到的任何請求對象都是將持久保存到etcd的最終版本。

通過將標誌傳遞給Kubernetes API服務器來配置啓用的准入控制器集。請注意,舊的-admission-control標誌在1.10中已棄用,並替換爲-enable-admission-plugins。

--enable-admission-plugins=ValidatingAdmissionWebhook,MutatingAdmissionWebhook

Kubernetes建議默認啓用以下准入控制器。

--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,Priority,ResourceQuota,PodSecurityPolicy

可以在Kubernetes官方參考中找到完整的准入控制器列表及其說明。本討論將僅關注基於webhook的准入控制器。

爲什麼我需要准入控制器?

  • 安全性:准入控制器可以通過在整個命名空間或集羣中,強制使用合理的安全基準來提高安全性。內置的PodSecurityPolicy准入控制器可能是最突出的例子;例如,它可以用於禁止容器以root身份運行,或者確保容器的根文件系統始終以只讀方式掛載。可通過基於webhook的自定義准入控制器實現的其他用例包括:

    • 允許僅從企業已知的特定倉庫中提取鏡像,同時拒絕未知的鏡像倉庫。
    • 拒絕不符合安全標準的部署。例如,使用特權(privileged)標誌的容器可以規避許多安全檢查。基於webhook的准入控制器可以減輕此風險,該准入控制器拒絕此類部署(驗證)或覆蓋特權(privileged)標誌,將其設置爲false。
  • 治理:准入控制器允許你強制遵守某些做法,例如具有良好的標籤、註釋、資源限制或其他設置。一些常見的場景包括:

    • 對不同對象強制執行標籤驗證,以確保將正確的標籤用於各種對象,例如分配給團隊或項目的每個對象,或指定應用程序標籤的每個部署。
    • 自動向對象添加註釋,例如爲“dev”部署資源分配正確的成本中心。
  • 配置管理:准入控制器允許你驗證羣集中運行對象的配置,並防止羣集中任何明顯的錯誤配置。准入控制器可用於檢測和修復沒有語義標籤的部署鏡像,例如:

    • 自動添加資源限制或驗證資源限制,
    • 確保合理的標籤被添加到pod,或
    • 確保生產部署中使用的鏡像引用不使用最新的(latest)標記或帶有-dev後綴的標記。

通過這種方式,准入控制器和政策管理有助於確保應用程序在不斷變化的控制環境中保持合規。

示例:編寫和部署准入控制器Webhook

爲了說明如何利用准入控制器webhook來建立自定義安全政策,讓我們考慮一個解決Kubernetes缺點之一的例子:它的許多默認值都經過優化,易於使用並減少摩擦,有時以犧牲安全性爲代價。其中一個設置是默認允許容器以root身份運行(並且,如果沒有進一步的配置,Dockerfile中也沒有USER指令,也會這樣)。儘管容器在一定程度上與底層主機隔離,但以root身份運行容器確實會增加部署的風險級別 - 作爲許多安全性最佳實踐之一,這應該避免。例如,最近暴露的runC漏洞(CVE-2019-5736)只有在容器以root身份運行時才能被利用。

你可以使用自定義改變准入控制器webhook來應用更安全的默認值:除非明確請求,否則我們的webhook將確保pod作爲非root用戶運行(如果未進行明確分配,我們將分配用戶ID 1234)。請注意,此設置不會阻止你在羣集中部署任何工作負載,包括那些合法需要以root身份運行的工作負載。它只要求你在部署配置中,明確啓用此風險程序操作模式,而對所有其他工作負載默認爲非root模式。

完整的代碼以及部署說明可以在我們隨附的GitHub存儲庫中找到。在這裏,我們將重點介紹webhook如何工作的一些更微妙的方面。

改變(Mutating)Webhook配置

通過在Kubernetes中創建MutatingWebhookConfiguration對象來定義改變准入控制器webhook。在我們的示例中,我們使用以下配置:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
  name: demo-webhook
webhooks:
  - name: webhook-server.webhook-demo.svc
    clientConfig:
      service:
        name: webhook-server
        namespace: webhook-demo
        path: "/mutate"
      caBundle: ${CA_PEM_B64}
    rules:
      - operations: [ "CREATE" ]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]

此配置定義webhook webhook-server.webhook-demo.svc,並指示Kubernetes API服務器在通過向/mutate URL發出HTTP POST請求創建pod時,在命名空間webhook-demo中查詢服務webhook-server。要使此配置生效,必須滿足幾個先決條件。

Webhook REST API

Kubernetes API服務器向給定服務和URL路徑發出HTTPS POST請求,並在請求正文中使用JSON編碼的AdmissionReview(設置了Request字段)。響應應該是JSON編碼的AdmissionReview,這次設置了Response字段。

我們的演示存儲庫包含一個處理序列化/反序列化樣板代碼的函數,並允許你專注於實現在Kubernetes API對象上運行的邏輯。在我們的示例中,實現准入控制器邏輯的函數稱爲applySecurityDefaults,在/mutate URL下提供此功能的HTTPS服務器可以設置如下:

mux := http.NewServeMux()
mux.Handle("/mutate", admitFuncHandler(applySecurityDefaults))
server := &http.Server{
  Addr:    ":8443",
  Handler: mux,
}
log.Fatal(server.ListenAndServeTLS(certPath, keyPath))

請注意,要使服務器在沒有提升權限的情況下運行,我們讓HTTP服務器偵聽端口8443。Kubernetes不允許在webhook配置中指定端口;它始終採用HTTPS端口443。但是,由於無論如何都需要服務對象,我們可以輕鬆地將服務的端口443映射到容器上的端口8443:

apiVersion: v1
kind: Service
metadata:
  name: webhook-server
  namespace: webhook-demo
spec:
  selector:
    app: webhook-server  # specified by the deployment/pod
  ports:
    - port: 443
      targetPort: webhook-api  # name of port 8443 of the container

對象修改邏輯

在改變准入控制器webhook中,通過JSON補丁執行改變。雖然JSON補丁標準包含許多複雜性,遠遠超出了本討論的範圍,但我們的示例中的Go數據結構,及其用法應該爲用戶提供有關JSON補丁如何工作的良好初步概述:

type patchOperation struct {
  Op    string      `json:"op"`
  Path  string      `json:"path"`
  Value interface{} `json:"value,omitempty"`
}

要將pod的字段.spec.securityContext.runAsNonRoot設置爲true,我們構造以下patchOperation對象:

patches = append(patches, patchOperation{
  Op:    "add",
  Path:  "/spec/securityContext/runAsNonRoot",
  Value: true,
})

TLS證書

由於必須通過HTTPS提供webhook,因此我們需要適當的服務器證書。這些證書可以是自簽名的(由自簽名CA簽名),但我們需要Kubernetes在與webhook服務器通信時指示相應的CA證書。此外,證書的公用名(CN)必須與Kubernetes API服務器使用的服務器名稱匹配,內部服務的名稱是<service-name>.<namespace>.svc,在我們的案例中即webhook-server.webhook-demo.svc。由於自簽名TLS證書的生成在Internet上有詳細記錄,因此我們只需在示例中引用相應的shell腳本

之前顯示的webhook配置包含佔位符${CA_PEM_B64}。在我們創建此配置之前,我們需要將此部分替換爲CA的Base64編碼的PEM證書。openssl base64 -A命令可用於此目的。

測試Webhook

在部署webhook服務器並對其進行配置之後(可以通過從存儲庫調用./deploy.sh腳本來完成),現在是時候測試並驗證webhook是否確實完成它的工作。存儲庫包含三個示例:

  • 未指定安全上下文的pod(pod-with-defaults)。我們希望此pod以非root身份運行,用戶ID爲1234。
  • 一個指定安全上下文的pod,明確允許它以root身份運行(pod-with-override)。
  • 具有衝突配置的pod,指定它必須以非root用戶身份運行,但用戶ID爲0(pod-with-conflict)。爲了展示拒絕對象創建請求,我們增加了我們的准入控制器邏輯,以拒絕這些明顯的錯誤配置。

通過運行kubectl create -f examples/<name>.yaml創建其中一個pod。在前兩個示例中,你可以通過檢查日誌來驗證pod運行的用戶ID,例如:

$ kubectl create -f examples/pod-with-defaults.yaml
$ kubectl logs pod-with-defaults
I am running as user 1234

在第三個示例中,應該拒絕對象創建並提供適當的錯誤消息:

$ kubectl create -f examples/pod-with-conflict.yaml
Error from server (InternalError): error when creating "examples/pod-with-conflict.yaml": Internal error occurred: admission webhook "webhook-server.webhook-demo.svc" denied the request: runAsNonRoot specified, but runAsUser set to 0 (the root user)

你也可以使用自己的工作負載進行測試。當然,你還可以通過更改webhook的邏輯,並查看更改如何影響對象創建來進一步實驗。有關如何進行此類更改實驗的更多信息,請參閱存儲庫的自述文件

摘要

Kubernetes准入控制器爲安全性提供了顯着優勢。深入研究兩個功能強大的示例,並附帶可用的代碼,將幫助你開始利用這些強大的功能。

參考文檔:


KubeCon + CloudNativeCon + Open Source Summit大會日期:

  • 會議日程通告日期:2019 年 4 月 10 日
  • 會議活動舉辦日期:2019 年 6 月 24 至 26 日

KubeCon + CloudNativeCon + Open Source Summit贊助方案
KubeCon + CloudNativeCon + Open Source Summit多元化獎學金現正接受申請
KubeCon + CloudNativeCon和Open Source Summit即將首次合體落地中國
KubeCon + CloudNativeCon + Open Source Summit購票窗口,立即購票!
CNCF邀請你加入最終用戶社區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章