Kubernetes(k8s)部署安全最佳實踐_Kubernetes中文社群
編者按:本文作者是來自 Aqua Security 的 Amir Jerbi 和 Michael Cherny,他們以大量的案例和經驗為基礎,總結並描述了 Kubernetes 部署中的最佳安全實踐。
Kubernetes 提供了很多能夠提高應用安全的方法。要進行這些配置,就要掌握 Kubernetes 的相關知識,同時也要清楚的瞭解安全需求。這裡我們關注的安全內容集中在容器的生命週期上:構建、傳輸以及執行,並且針對 Kubernetes 進行了特別的裁剪。我們自己的 SaaS 就是執行在 Google Cloud Platform 上的 Kubernetes 中,已經採用了這些最佳實踐。
下面是我們對於安全部署 Kubernetes 應用的一些建議。
確保映象無漏洞
執行帶有漏洞的容器會讓你的環境身處險境。只要執行中的系統的所有元件都不存在已知漏洞,就能夠避免很多被攻擊的機會。
安全漏洞的持續掃描
容器中可能有一些過期元件,這些過期元件往往會包含已知漏洞(CVE)。新的漏洞層出不窮,因此對安全漏洞的掃描工作必須持續進行。
適時應用安全更新
一旦在執行的容器中發現了安全漏洞,就該對源映象進行更新並部署。為了避免破壞映象和容器的繼承性,儘量不要在容器中直接進行更新(例如 apt-update)。 Kubernetes 的滾動更新功能可以漸進式的為執行中的應用更新映象,這一功能讓應用更新變得簡單優雅。
只使用可靠的映象
要避免受到有漏洞甚至惡意的容器的威脅,映象的准入就需要受到有效管理。和隨意下載執行軟體一樣,下載執行不可靠的映象也是高危行為,必須杜絕。
使用私庫來儲存你的映象,並保證只向其推送可靠映象。這樣就縮小了戰場面積,避免大量不確認的公開映象湧入你的環境。另外建議在持續構建流程中加入漏洞掃描之類的安全環節。
持續整合管線要控制門檻,只允許使用受確認的程式碼進行映象構建。映象構建成功後,應該進行漏洞掃描,排除問題後才能推入私庫,進行下一步的部署。過程中發現問題,應該終端構建過程,阻止安全質量低下的映象進入私庫。
限制對 Kubernetes Node 的直接訪問
對 Kubernetes Node 的 SSH 訪問會降低主機的安全性。應該讓使用者儘量使用 kubectl exec,這一命令提供了對容器環境的直接訪問,而不需要接觸宿主機。
還可以使用 Kubernetes 的 Authorization Plugins 來對使用者的資源訪問進行進一步控制。這一外掛允許定義對名稱空間、容器以及操作的基於角色的訪問控制。
在資源之間建立管理邊界
限制使用者許可權能夠降低出錯和入侵造成的危害。Kubernetes 名稱空間讓你可以把資源分割為不同名稱的群組之中。一個名稱空間中建立的資源對其他名稱空間是不可見的。預設情況下,Kubernetes 使用者建立的資源都存在於 default 名稱空間中。可以建立其他的名稱空間,並把資源和使用者繫結上去。可以使用 Kubernetes Authorization 外掛來建立策略,讓不同使用者分別訪問各自的名稱空間和對應的資源。
例如下面的策略讓 “Alice” 能夠從名稱空間 “fronto” 中讀取 Pod:
{ "apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": { "user": "alice", "namespace": "fronto", "resource": "pods", "readonly": true } }
設定資源配額
容器執行中如果沒有資源限制,那麼系統就可能處於 DoS 或鄰里不和的情境之中。要降低或阻止這一風險,就需要設定資源配額。預設情況下,所有的 Kubernetes 叢集資源都可以不受限的訪問 CPU 和記憶體。可以為名稱空間建立配額策略,來限制 Pod 的 CPU 和記憶體消費。
下面的例子是一個名稱空間的資源配額定義,限制執行 Pod 數量為 4,CPU 的使用限制在 1-2 之間,記憶體使用在 1-2 G 之間:
apiVersion: v1 kind: ResourceQuota metadata: name: compute-resources spec: hard: pods: "4" requests.cpu: "1" requests.memory: 1Gi limits.cpu: "2" limits.memory: 2Gi
將資源配額指派給名稱空間:
kubectl create -f ./compute-resources.yaml --namespace=myspace
規劃網路分割槽
在同一個 Kubernetes 叢集上執行不同的應用,引入了一個風險就是應用之間的互相訪問。要確保容器只能訪問允許訪問的範圍,網路分割槽是很重要的。Kubernetes 中的一大挑戰就是在 Pod、Service 以及容器之間的網路劃分,造成這一問題的根本在於容器網路的動態分配過程,讓容器可以跨越 Node 進行網路互訪。
Google Cloud Platform 使用者收益於自動防火牆規則功能,能夠阻止跨叢集的通訊。使用 SDN 或者防火牆能夠達到類似的效果。Kuberntes Network SIG 正在進行這方面的努力,目的是增強 Pod 之間的通訊策略。新的網路策略 API 將會用於建立 Pod 之間的防火牆規則,限制容器應用的網路訪問。
下面的例子是一條網路策略,用於控制 “backend” Pod,只允許來自於 “frontend” Pod 的訪問。
POST /apis/net.alpha.kubernetes.io/v1alpha1/namespaces/tenant-a/networkpolicys { "kind": "NetworkPolicy", "metadata": { "name": "pol1" }, "spec": { "allowIncoming": { "from": [{ "pods": { "segment": "frontend" } }], "toPorts": [{ "port": 80, "protocol": "TCP" }] }, "podSelector": { "segment": "backend" } } }
Pod 和容器的安全上下文
設計容器和 Pod 的時候,一定要配置 Pod、容器以及卷的安全上下文。安全上下文是部署 Yaml 中的一個屬性,他控制了 pod/container/volume 的安全引數,下面列出一些重要的引數:
安全上下文設定 | 描述 |
---|---|
SecurityContext->runAsNonRoot | 容器應該用非 root 使用者執行 |
SecurityContext->Capabilities | 設定 Linux 分配給容器的效能 |
SecurityContext->readOnlyRootFilesystem | 容器是否可以寫入 root 檔案系統 |
PodSecurityContext->runAsNonRoot | 阻止 Pod 中的容器以 root 使用者執行 |
下面是一個帶有安全上下文的 Pod 定義:
apiVersion: v1 kind: Pod metadata: name: hello-world spec: containers: # specification of the pod’s containers # ... securityContext: readOnlyRootFilesystem: true runAsNonRoot: true
如果用特權形式(–privileged)執行容器,可以用 DenyEscalatingExec 控制。這一開關拒絕在特權容器上使用 Exec 和 Attach 命令。具體情況可以參考 Admission 文件
記錄日誌
Kubernetes 支援叢集級別的日誌,集中收集日誌到中央服務。當叢集建立之後,STDOUT 和 STDERR 就能夠被 Node 中的 Fluent 蒐集起來,並彙總到 Google Stackdriver Logging 或者 Elasticsearch,並用 Kibana 進行檢視。
總結
Kubernetes 為安全提供了很多特性。對這些特性進行學習和了解,才能夠制定出符合應用需求的安全方案。
我們建議實施文中提到的最佳實踐,使用 Kubernetes 的動態配置能力,結合持續整合,無縫提高安全保障能力。