在容器化技術日益普及的今天,許多開發者和運維人員都將應用部署在 Docker 或 Kubernetes 中。然而,一個普遍存在的誤解是:“容器是完全隔離的,所以它是安全的。”
如果你也有同樣的想法,那么你需要重新審視容器安全了。
本文將從一個簡單的命令切入,深入探討容器安全的核心機制,并為你提供 Kubernetes 中最實用的安全實踐。
一、你的 root
,到底是誰的 root
?
當你敲下 docker run my-image
這條命令時,如果沒有特別指定,你的容器會以 root 用戶身份運行。一個很自然的問題是:這個 root
,是宿主機的 root
嗎?
答案是:不完全是,但存在危險的關聯。
容器利用 Linux 內核的 Namespace(命名空間)技術來創建隔離。其中,User Namespace 提供了用戶身份的隔離。理論上,容器內的 root
用戶(UID 0
)可以被映射到宿主機上的一個普通用戶(例如 UID 1001
)。在這種情況下,容器內的 root
進程在宿主機上實際上是受限的。
然而,默認的 Docker 配置并沒有啟用這種隔離。這意味著,容器內的 root
用戶就是宿主機上的 root
用戶。如果你的容器被攻破,攻擊者將直接擁有宿主機的最高權限,這就像為惡意軟件打開了所有大門。
一個危險的例子:假設你通過 hostPath
將宿主機的 /etc
目錄掛載到容器中。由于容器內的 root
等同于宿主機的 root
,攻擊者可以輕易地訪問 /host/etc/shadow
文件,從而獲取所有用戶的加密密碼。
因此,我們的首要任務是打破這種危險的關聯,讓容器以非特權用戶身份運行。
二、核心原理:共享內核機制的“雙刃劍”
要理解容器安全的本質,我們必須回到它的底層:共享內核機制。
與虛擬機(VM)不同,容器沒有自己的獨立內核。所有容器都共享宿主機的操作系統內核。正是這種設計,讓容器變得輕量、高效。
但與此同時,共享內核也帶來了安全風險。容器的隔離是軟件層面的,而不是硬件層面的。 容器通過 Namespace(實現視圖隔離)和 Cgroups(實現資源限制)來獲得安全保障。
你可以將共享內核比喻為一棟公寓樓的核心結構,而 Namespace 和 Cgroups 則是隔離每個單元的墻壁和門。如果墻壁出現漏洞(即內核漏洞),或者房門沒有上鎖(即配置不當),那么一個單元內的惡意行為就可能蔓延到整個公寓樓。
這也是為什么我們不能盲目信任容器的默認設置,而必須主動加固其安全配置。
三、特權容器:比 root
用戶更危險的存在
除了默認的 root
用戶權限,容器中還有一個更具破壞力的概念:特權容器(Privileged Container)。
特權容器是指突破了所有命名空間隔離,擁有幾乎所有宿主機權限的容器。它可以通過以下配置開啟:
securityContext:privileged: true
當一個容器被賦予 privileged: true
時,它:
- 可以訪問所有設備:能夠直接訪問宿主機的硬件設備,比如
/dev/sda1
。 - 可以加載內核模塊:能夠為宿主機加載新的內核模塊。
- 幾乎沒有限制:可以執行任何系統調用,如同在宿主機上直接使用
root
權限。
為什么特權容器如此危險?
特權容器本質上等同于直接在宿主機上以 root
用戶運行進程。如果你的應用需要訪問底層硬件或執行復雜的系統管理任務,通常會選擇特權容器。然而,一旦特權容器被攻破,攻擊者就能完全控制宿主機,甚至影響到宿主機上的其他容器。這是一種極其嚴重的安全風險,在生產環境中應極力避免。
四、Kubernetes 的解決方案:內置的 Pod 安全策略
Kubernetes 不僅僅提供了 安全上下文(Security Context) 這個配置項,它還內置了一套強大的安全策略機制來強制執行這些配置,這套機制就是 PodSecurity Admission (PSA)。
你可以將 安全上下文 看作是 Pod 的安全配置清單,而 PSA 則是 Kubernetes 集群的“安全警衛”。PSA 會在 Pod 創建時進行檢查,確保其安全上下文符合預設的安全標準。
這套機制將 Kubernetes 的安全能力分為三個等級,方便你根據應用場景選擇不同的安全級別:
- 特權(
privileged
):最寬松的策略,幾乎不作任何限制。這個級別應只用于需要直接操作宿主機的特殊應用,如監控工具或網絡插件。 - 基線(
baseline
):一個適度寬松的策略,旨在防止已知的特權提升。它會強制應用非特權用戶運行、限制特權能力,但允許一些默認的非敏感配置。這是大多數生產應用的推薦起點。 - 受限(
restricted
):最嚴格的策略,遵循最新的容器安全最佳實踐。它會強制所有 Pod 以非特權用戶運行、使用只讀文件系統、并移除所有不必要的特權。此級別適用于對安全性要求極高的多租戶環境。
如何應用 PSA?
你可以在命名空間(Namespace)層面,通過一個簡單的標簽來強制應用這些策略:
apiVersion: v1
kind: Namespace
metadata:name: my-secure-namespacelabels:pod-security.kubernetes.io/enforce: restricted
通過這個標簽,my-secure-namespace
中的任何 Pod,如果其安全配置不符合 restricted
策略,都會被 Kubernetes 拒絕創建。
為什么要同時使用安全上下文和 PSA?
這是一種深度防御的策略:
- PSA 守住底線:它作為集群層面的“門禁”,確保沒有 Pod 能夠以極不安全的方式運行。
- 安全上下文進行精細化配置:它允許你為每個 Pod 定義更具體、更適合應用的權限,即使在
restricted
策略下,你仍然可以使用runAsUser
和fsGroup
等配置來優化應用的權限。
這種結合使用的方式,既能保證集群整體的安全性,又能為每個應用提供量身定做的安全配置。
五、落地實踐:分層推進的安全最佳實踐
在實際項目中,我們應該遵循分層推進的原則來配置 Pod 安全。這就像學習開車,首先要掌握的是踩油門、剎車和打方向盤。至于如何使用高級輔助駕駛系統,那是更高級的技能。
1. 基礎最佳實踐 (所有應用)
這三條配置是所有容器安全實踐的基礎。如果說容器安全是一棟大樓,那么這三條就是地基和核心框架。有了它們,你的 Pod 就已經具備了抵御絕大多數攻擊的能力。
- 實踐一:堅持最小權限原則,以非
root
用戶運行
這是最重要的實踐。通過在 Pod 級別設置 runAsUser
,你可以強制所有容器都以一個權限受限的非 root
用戶身份運行。
YAML 配置示例:
spec:securityContext:runAsUser: 1001runAsGroup: 1001fsGroup: 1001
一個常見的疑問:UID 1001 需要在宿主機上提前創建嗎?
不需要。 這是容器和 Kubernetes 運行的一個巧妙之處。當你使用 runAsUser: 1001
時,Kubernetes 不會去檢查宿主機上是否存在 UID 為 1001
的用戶。它只會確保容器內部的進程以 UID 1001
的身份運行。在 Linux 中,UID 和 GID 只是一個數字,內核只關心這個數字,而不關心它是否對應一個實際的用戶名。
一個重要的例外:文件權限
雖然你不需要提前創建用戶,但如果你的容器應用需要訪問宿主機上的文件,那么容器內的進程必須擁有對這個目錄的訪問權限。在這種情況下,你需要確保容器內的 UID 1001
擁有對宿主機上該目錄的讀寫權限。這通常通過在宿主機上設置權限或在 Pod 中使用 fsGroup
來實現。
- 實踐二:容器級別的補充配置
Pod 級別的配置提供了整體安全,但一些細節需要針對性地處理。以下是一個遵循最佳實踐的 Pod YAML 模板,你可以直接拿來使用:
apiVersion: v1
kind: Pod
metadata:name: my-secure-pod
spec:# Pod 級別的安全上下文:為整個 Pod 設定基線安全策略securityContext:# 1. 堅持最小權限原則:以非 root 用戶運行# 這個用戶 ID 應該在你的容器鏡像中定義,或者是一個在宿主機上# 權限受限的用戶 ID。通常選擇一個大于 1000 的 ID。runAsUser: 1001# 2. 限制文件系統權限:確保 Pod 訪問的數據卷權限受限# 當 Pod 掛載數據卷時,該卷的所有權會變為指定的組 ID,# 確保只有 Pod 內的進程可以讀寫。runAsGroup: 1001fsGroup: 1001containers:- name: my-app-containerimage: my-app-image:v1.0# 容器級別的安全上下文:針對特定容器進行調整securityContext:# 3. 限制特權:丟棄所有不必要的特權,只添加確實需要的# drop: [ALL] 是一個很好的起點,表示丟棄所有默認特權。capabilities:drop:- ALL# 4. 禁止權限提升:防止攻擊者從非特權提升到 root 權限allowPrivilegeEscalation: false# 5. 只讀文件系統:防止惡意篡改# 如果容器不需要向根文件系統寫入數據,將其設置為只讀。readOnlyRootFilesystem: truevolumeMounts:# 6. 掛載數據卷:為需要寫入數據的容器提供可寫空間# 與 readOnlyRootFilesystem 結合使用,確保只有特定目錄可寫。- name: data-volumemountPath: /datavolumes:- name: data-volumeemptyDir: {}
2. 高級安全實踐 (特定應用)
除了上述三條,Pod 級別的 securityContext
還有其他一些配置,但它們通常被認為是高級安全實踐,而不是基礎最佳實踐。
seLinuxOptions
/windowsOptions
:這些配置是特定于操作系統的,不具備普適性。seccompProfile
/sysctls
:這些配置非常精細,需要你對 Linux 內核、系統調用和應用程序行為有深入的了解。錯誤的配置可能會導致 Pod 無法啟動或應用崩潰。對于大多數應用來說,默認的配置已經足夠安全。
因此,最佳實踐是分層推進。首先,確保你理解并應用了這三條核心配置。當你的項目需要更高的安全級別或面臨特定安全挑戰時,再根據具體情況去探索和應用其他高級配置。
六、總結:安全上下文的意義
容器的共享內核機制是其性能的基石,但同時也帶來了潛在的安全風險。我們不能將安全寄托于默認配置,而必須主動出擊。
Pod 和容器的安全上下文就是我們最好的武器。
通過將通用策略(如 runAsUser
)放在 Pod 級別,將精細化策略(如 capabilities
和 readOnlyRootFilesystem
)放在容器級別,我們能夠構建一個多層次的深度防御體系。這不僅能確保應用在最安全的環境中運行,也能夠保證整個系統的健壯和穩定。
記住,容器安全并非高深莫測,它源于對底層原理的理解和對最佳實踐的堅持。